aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/conf/files5
-rw-r--r--sys/conf/newvers.sh12
-rw-r--r--sys/conf/param.c2
-rw-r--r--sys/doc/Changes (renamed from sys/i386/doc/Changes)96
-rw-r--r--sys/doc/Makefile (renamed from sys/i386/doc/Makefile)2
-rw-r--r--sys/doc/ata/ata-1 (renamed from sys/i386/doc/ata/ata-1)0
-rw-r--r--sys/doc/ata/ata-10 (renamed from sys/i386/doc/ata/ata-10)0
-rw-r--r--sys/doc/ata/ata-11 (renamed from sys/i386/doc/ata/ata-11)0
-rw-r--r--sys/doc/ata/ata-2 (renamed from sys/i386/doc/ata/ata-2)0
-rw-r--r--sys/doc/ata/ata-3 (renamed from sys/i386/doc/ata/ata-3)0
-rw-r--r--sys/doc/ata/ata-4 (renamed from sys/i386/doc/ata/ata-4)0
-rw-r--r--sys/doc/ata/ata-5 (renamed from sys/i386/doc/ata/ata-5)0
-rw-r--r--sys/doc/ata/ata-6 (renamed from sys/i386/doc/ata/ata-6)0
-rw-r--r--sys/doc/ata/ata-7 (renamed from sys/i386/doc/ata/ata-7)0
-rw-r--r--sys/doc/ata/ata-8 (renamed from sys/i386/doc/ata/ata-8)0
-rw-r--r--sys/doc/ata/ata-9 (renamed from sys/i386/doc/ata/ata-9)0
-rw-r--r--sys/doc/ata/ata-apx (renamed from sys/i386/doc/ata/ata-apx)0
-rw-r--r--sys/doc/ata/ata-prt (renamed from sys/i386/doc/ata/ata-prt)0
-rw-r--r--sys/doc/ata/ata-toc (renamed from sys/i386/doc/ata/ata-toc)0
-rw-r--r--sys/doc/ed.relnotes (renamed from sys/i386/doc/ed.relnotes)2
-rw-r--r--sys/doc/memory-test.doc40
-rw-r--r--sys/doc/options.doc682
-rw-r--r--sys/doc/options.texi (renamed from sys/i386/doc/options.texi)256
-rw-r--r--sys/doc/seagate.doc125
-rw-r--r--sys/doc/sound.doc80
-rw-r--r--sys/doc/vm_layout.doc (renamed from sys/i386/doc/vm_layout.doc)2
-rw-r--r--sys/doc/wt.doc (renamed from sys/i386/doc/wt.doc)0
-rw-r--r--sys/gnu/fpemul/Changelog36
-rw-r--r--sys/gnu/fpemul/README277
-rw-r--r--sys/gnu/fpemul/control_w.h95
-rw-r--r--sys/gnu/fpemul/div_small.s101
-rw-r--r--sys/gnu/fpemul/errors.c612
-rw-r--r--sys/gnu/fpemul/exception.h102
-rw-r--r--sys/gnu/fpemul/fpu_arith.c235
-rw-r--r--sys/gnu/fpemul/fpu_asm.h82
-rw-r--r--sys/gnu/fpemul/fpu_aux.c233
-rw-r--r--sys/gnu/fpemul/fpu_emu.h188
-rw-r--r--sys/gnu/fpemul/fpu_entry.c483
-rw-r--r--sys/gnu/fpemul/fpu_etc.c175
-rw-r--r--sys/gnu/fpemul/fpu_proto.h115
-rw-r--r--sys/gnu/fpemul/fpu_system.h97
-rw-r--r--sys/gnu/fpemul/fpu_trig.c1367
-rw-r--r--sys/gnu/fpemul/get_address.c203
-rw-r--r--sys/gnu/fpemul/load_store.c269
-rw-r--r--sys/gnu/fpemul/math_emu.h47
-rw-r--r--sys/gnu/fpemul/poly_2xm1.c141
-rw-r--r--sys/gnu/fpemul/poly_atan.c252
-rw-r--r--sys/gnu/fpemul/poly_div.s144
-rw-r--r--sys/gnu/fpemul/poly_l2.c318
-rw-r--r--sys/gnu/fpemul/poly_mul64.s124
-rw-r--r--sys/gnu/fpemul/poly_sin.c192
-rw-r--r--sys/gnu/fpemul/poly_tan.c229
-rw-r--r--sys/gnu/fpemul/polynomial.s192
-rw-r--r--sys/gnu/fpemul/reg_add_sub.c303
-rw-r--r--sys/gnu/fpemul/reg_compare.c384
-rw-r--r--sys/gnu/fpemul/reg_constant.c175
-rw-r--r--sys/gnu/fpemul/reg_constant.h82
-rw-r--r--sys/gnu/fpemul/reg_div.s295
-rw-r--r--sys/gnu/fpemul/reg_ld_str.c1387
-rw-r--r--sys/gnu/fpemul/reg_mul.c162
-rw-r--r--sys/gnu/fpemul/reg_norm.s182
-rw-r--r--sys/gnu/fpemul/reg_round.s653
-rw-r--r--sys/gnu/fpemul/reg_u_add.s244
-rw-r--r--sys/gnu/fpemul/reg_u_div.s506
-rw-r--r--sys/gnu/fpemul/reg_u_mul.s199
-rw-r--r--sys/gnu/fpemul/reg_u_sub.s361
-rw-r--r--sys/gnu/fpemul/status_w.h106
-rw-r--r--sys/gnu/fpemul/version.h61
-rw-r--r--sys/gnu/fpemul/wm_shrx.s261
-rw-r--r--sys/gnu/fpemul/wm_sqrt.s496
-rw-r--r--sys/i386/boot/Makefile45
-rw-r--r--sys/i386/boot/biosbootbin0 -> 512 bytes
-rw-r--r--sys/i386/boot/bootbin0 -> 7632 bytes
-rw-r--r--sys/i386/boot/boot.c12
-rwxr-xr-xsys/i386/boot/boot.symbin0 -> 10379 bytes
-rw-r--r--sys/i386/boot/boot2.S12
-rw-r--r--sys/i386/boot/bootbiosbin0 -> 7120 bytes
-rw-r--r--sys/i386/boot/disk.c45
-rw-r--r--sys/i386/boot/io.c20
-rw-r--r--sys/i386/boot/start.S3
-rw-r--r--sys/i386/conf/ECLIPSE66
-rw-r--r--sys/i386/conf/GENERICAH13
-rw-r--r--sys/i386/conf/GENERICBT12
-rw-r--r--sys/i386/conf/HAMSTER67
-rw-r--r--sys/i386/conf/LAPTOP68
-rw-r--r--sys/i386/conf/LINT79
-rw-r--r--sys/i386/conf/Makefile.i38618
-rw-r--r--sys/i386/conf/SYSCONS86
-rw-r--r--sys/i386/conf/TIME64
-rw-r--r--sys/i386/conf/WCARCH67
-rw-r--r--sys/i386/conf/WCARCHIVE73
-rw-r--r--sys/i386/conf/devices.i3863
-rw-r--r--sys/i386/conf/files.i38694
-rw-r--r--sys/i386/doc/sound.doc38
-rw-r--r--sys/i386/i386/autoconf.c7
-rw-r--r--sys/i386/i386/conf.c65
-rw-r--r--sys/i386/i386/db_interface.c21
-rw-r--r--sys/i386/i386/exception.s138
-rw-r--r--sys/i386/i386/in_cksum.c94
-rw-r--r--sys/i386/i386/locore.s117
-rw-r--r--sys/i386/i386/machdep.c133
-rw-r--r--sys/i386/i386/math_emulate.c144
-rw-r--r--sys/i386/i386/microtime.s87
-rw-r--r--sys/i386/i386/pmap.c879
-rw-r--r--sys/i386/i386/random.s60
-rw-r--r--sys/i386/i386/support.s323
-rw-r--r--sys/i386/i386/swtch.s43
-rw-r--r--sys/i386/i386/trap.c814
-rw-r--r--sys/i386/i386/vm_machdep.c889
-rw-r--r--sys/i386/include/ansi.h21
-rw-r--r--sys/i386/include/asmacros.h6
-rw-r--r--sys/i386/include/console.h31
-rw-r--r--sys/i386/include/cpu.h13
-rw-r--r--sys/i386/include/cpufunc.h13
-rw-r--r--sys/i386/include/ioctl_fd.h31
-rw-r--r--sys/i386/include/limits.h6
-rw-r--r--sys/i386/include/lpt.h24
-rw-r--r--sys/i386/include/mouse.h44
-rw-r--r--sys/i386/include/npx.h10
-rw-r--r--sys/i386/include/param.h6
-rw-r--r--sys/i386/include/pcaudioio.h75
-rw-r--r--sys/i386/include/pmap.h55
-rw-r--r--sys/i386/include/psl.h6
-rw-r--r--sys/i386/include/pte.h4
-rw-r--r--sys/i386/include/soundcard.h50
-rw-r--r--sys/i386/include/spl.h104
-rw-r--r--sys/i386/include/ultrasound.h9
-rw-r--r--sys/i386/include/vmparam.h2
-rw-r--r--sys/i386/isa/aha1542.c13
-rw-r--r--sys/i386/isa/bt742a.c111
-rw-r--r--sys/i386/isa/bt742a_32.c1694
-rw-r--r--sys/i386/isa/clock.c279
-rw-r--r--sys/i386/isa/com.c101
-rw-r--r--sys/i386/isa/debug.h85
-rw-r--r--sys/i386/isa/elink.c77
-rw-r--r--sys/i386/isa/elink.h39
-rw-r--r--sys/i386/isa/fd.c778
-rw-r--r--sys/i386/isa/fdc.h12
-rw-r--r--sys/i386/isa/fdreg.h20
-rw-r--r--sys/i386/isa/ft.c1188
-rw-r--r--sys/i386/isa/ftreg.h11
-rw-r--r--sys/i386/isa/ic/i82365.h190
-rw-r--r--sys/i386/isa/ic/nec765.h73
-rw-r--r--sys/i386/isa/icu.h12
-rw-r--r--sys/i386/isa/icu.s492
-rw-r--r--sys/i386/isa/if_ed.c1451
-rw-r--r--sys/i386/isa/if_edreg.h2
-rw-r--r--sys/i386/isa/if_el.c800
-rw-r--r--sys/i386/isa/if_elreg.h76
-rw-r--r--sys/i386/isa/if_ep.c164
-rw-r--r--sys/i386/isa/if_ie507.h19
-rw-r--r--sys/i386/isa/if_is.c10
-rw-r--r--sys/i386/isa/if_ze.c1951
-rw-r--r--sys/i386/isa/if_zereg.h859
-rw-r--r--sys/i386/isa/ipl.h7
-rw-r--r--sys/i386/isa/isa.c227
-rw-r--r--sys/i386/isa/isa.h9
-rw-r--r--sys/i386/isa/isa_device.h4
-rw-r--r--sys/i386/isa/kbdtables.h40
-rw-r--r--sys/i386/isa/lpa.c14
-rw-r--r--sys/i386/isa/lpt.c150
-rw-r--r--sys/i386/isa/mcd.c5
-rw-r--r--sys/i386/isa/npx.c22
-rw-r--r--sys/i386/isa/pcaudio.c430
-rw-r--r--sys/i386/isa/pccons.c101
-rw-r--r--sys/i386/isa/psm.c464
-rw-r--r--sys/i386/isa/seagate.c2036
-rw-r--r--sys/i386/isa/sio.c1242
-rw-r--r--sys/i386/isa/sound/CHANGELOG75
-rw-r--r--sys/i386/isa/sound/RELNOTES.Linux256
-rw-r--r--sys/i386/isa/sound/adlib_card.c13
-rw-r--r--sys/i386/isa/sound/audio.c106
-rw-r--r--sys/i386/isa/sound/debug.h29
-rw-r--r--sys/i386/isa/sound/dev_table.c148
-rw-r--r--sys/i386/isa/sound/dev_table.h46
-rw-r--r--sys/i386/isa/sound/dmabuf.c360
-rw-r--r--sys/i386/isa/sound/dsp.c270
-rw-r--r--sys/i386/isa/sound/gus_card.c87
-rw-r--r--sys/i386/isa/sound/gus_hw.h15
-rw-r--r--sys/i386/isa/sound/gus_linearvol.h18
-rw-r--r--sys/i386/isa/sound/gus_midi.c14
-rw-r--r--sys/i386/isa/sound/gus_vol.c70
-rw-r--r--sys/i386/isa/sound/gus_wave.c1834
-rw-r--r--sys/i386/isa/sound/ics2101.c265
-rw-r--r--sys/i386/isa/sound/local.h13
-rw-r--r--sys/i386/isa/sound/midi.c220
-rw-r--r--sys/i386/isa/sound/midibuf.c14
-rw-r--r--sys/i386/isa/sound/mpu401.c129
-rw-r--r--sys/i386/isa/sound/opl3.c68
-rw-r--r--sys/i386/isa/sound/os.h94
-rw-r--r--sys/i386/isa/sound/pas.h12
-rw-r--r--sys/i386/isa/sound/pas2_card.c135
-rw-r--r--sys/i386/isa/sound/pas2_midi.c16
-rw-r--r--sys/i386/isa/sound/pas2_mixer.c40
-rw-r--r--sys/i386/isa/sound/pas2_pcm.c91
-rw-r--r--sys/i386/isa/sound/patmgr.c41
-rw-r--r--sys/i386/isa/sound/pro_midi.c140
-rw-r--r--sys/i386/isa/sound/sb.h28
-rw-r--r--sys/i386/isa/sound/sb16_dsp.c627
-rw-r--r--sys/i386/isa/sound/sb16_midi.c287
-rw-r--r--sys/i386/isa/sound/sb_card.c13
-rw-r--r--sys/i386/isa/sound/sb_dsp.c1140
-rw-r--r--sys/i386/isa/sound/sb_midi.c224
-rw-r--r--sys/i386/isa/sound/sb_mixer.c422
-rw-r--r--sys/i386/isa/sound/sb_mixer.h212
-rw-r--r--sys/i386/isa/sound/sequencer.c311
-rw-r--r--sys/i386/isa/sound/sound_calls.h59
-rw-r--r--sys/i386/isa/sound/sound_config.h43
-rw-r--r--sys/i386/isa/sound/sound_switch.c445
-rw-r--r--sys/i386/isa/sound/soundcard.c307
-rw-r--r--sys/i386/isa/spkr.c23
-rw-r--r--sys/i386/isa/syscons.c623
-rw-r--r--sys/i386/isa/tw.c997
-rw-r--r--sys/i386/isa/ultra14f.c6
-rw-r--r--sys/i386/isa/vector.s196
-rw-r--r--sys/i386/isa/wd.c204
-rw-r--r--sys/i386/isa/wt.c3
-rw-r--r--sys/i386/isa/wt.c.orig902
-rw-r--r--sys/i386/netboot/Makefile9
-rw-r--r--sys/i386/netboot/bootmenu.c2
-rw-r--r--sys/i386/netboot/ether.c479
-rw-r--r--sys/i386/netboot/ether.h178
-rw-r--r--sys/i386/netboot/wd80x3.c240
-rw-r--r--sys/i386/pci/ncr.c5507
-rw-r--r--sys/i386/pci/ncr_reg.h489
-rw-r--r--sys/i386/pci/pci.c494
-rw-r--r--sys/i386/pci/pci.h58
-rw-r--r--sys/i386/pci/pci_config.c45
-rw-r--r--sys/i386/pci/pci_device.h88
-rw-r--r--sys/i386/pci/pcibios.c261
-rw-r--r--sys/i386/pci/pcibios.h53
-rw-r--r--sys/isofs/isofs_rrip.c7
-rw-r--r--sys/isofs/isofs_vfsops.c6
-rw-r--r--sys/isofs/isofs_vnops.c15
-rw-r--r--sys/kern/imgact_aout.c6
-rw-r--r--sys/kern/init_main.c34
-rw-r--r--sys/kern/init_sysent.c8
-rw-r--r--sys/kern/kern_acct.c2
-rw-r--r--sys/kern/kern_clock.c608
-rw-r--r--sys/kern/kern_descrip.c4
-rw-r--r--sys/kern/kern_execve.c45
-rw-r--r--sys/kern/kern_exit.c5
-rw-r--r--sys/kern/kern_fork.c10
-rw-r--r--sys/kern/kern_malloc.c74
-rw-r--r--sys/kern/kern_ntptime.c267
-rw-r--r--sys/kern/kern_physio.c105
-rw-r--r--sys/kern/kern_prot.c2
-rw-r--r--sys/kern/kern_resource.c2
-rw-r--r--sys/kern/kern_sig.c12
-rw-r--r--sys/kern/kern_subr.c2
-rw-r--r--sys/kern/kern_synch.c4
-rw-r--r--sys/kern/kern_time.c6
-rw-r--r--sys/kern/makesyscalls.sh4
-rw-r--r--sys/kern/spec_vnops.c4
-rw-r--r--sys/kern/subr_prf.c11
-rw-r--r--sys/kern/subr_rand.c100
-rw-r--r--sys/kern/sys_process.c2
-rw-r--r--sys/kern/syscalls.c6
-rw-r--r--sys/kern/syscalls.master6
-rw-r--r--sys/kern/sysv_msg.c6
-rw-r--r--sys/kern/sysv_sem.c12
-rw-r--r--sys/kern/sysv_shm.c14
-rw-r--r--sys/kern/tty.c545
-rw-r--r--sys/kern/tty_conf.c6
-rw-r--r--sys/kern/tty_pty.c155
-rw-r--r--sys/kern/uipc_mbuf.c17
-rw-r--r--sys/kern/vfs__bio.c95
-rw-r--r--sys/kern/vfs_lookup.c15
-rw-r--r--sys/kern/vfs_subr.c25
-rw-r--r--sys/kern/vfs_syscalls.c7
-rw-r--r--sys/kern/vfs_vnops.c3
-rw-r--r--sys/net/if.c7
-rw-r--r--sys/net/if.h12
-rw-r--r--sys/net/if_ethersubr.c226
-rw-r--r--sys/net/if_loop.c30
-rw-r--r--sys/net/if_ppp.c108
-rw-r--r--sys/net/if_sl.c54
-rw-r--r--sys/net/netisr.h15
-rw-r--r--sys/net/pppdefs.h (renamed from sys/net/ppp.h)8
-rw-r--r--sys/net/raw_cb.h3
-rw-r--r--sys/net/route.c2
-rw-r--r--sys/netinet/if_ether.c8
-rw-r--r--sys/netinet/if_ether.h100
-rw-r--r--sys/netinet/igmp.c321
-rw-r--r--sys/netinet/igmp.h60
-rw-r--r--sys/netinet/igmp_var.h80
-rw-r--r--sys/netinet/in.c132
-rw-r--r--sys/netinet/in.h37
-rw-r--r--sys/netinet/in_pcb.c93
-rw-r--r--sys/netinet/in_pcb.h7
-rw-r--r--sys/netinet/in_pcb.old.c503
-rw-r--r--sys/netinet/in_pcb.orig.c498
-rw-r--r--sys/netinet/in_proto.c12
-rw-r--r--sys/netinet/in_var.h117
-rw-r--r--sys/netinet/ip_icmp.c7
-rw-r--r--sys/netinet/ip_input.c53
-rw-r--r--sys/netinet/ip_mroute.c1056
-rw-r--r--sys/netinet/ip_mroute.h174
-rw-r--r--sys/netinet/ip_output.c494
-rw-r--r--sys/netinet/ip_var.h17
-rw-r--r--sys/netinet/raw_ip.c54
-rw-r--r--sys/netinet/tcp_usrreq.c7
-rw-r--r--sys/netinet/udp_usrreq.c104
-rw-r--r--sys/nfs/nfs_bio.c8
-rw-r--r--sys/nfs/nfs_serv.c16
-rw-r--r--sys/nfs/nfs_socket.c2
-rw-r--r--sys/nfs/nfs_subs.c60
-rw-r--r--sys/nfs/nfs_vnops.c16
-rw-r--r--sys/pcfs/pcfs_conv.c6
-rw-r--r--sys/pcfs/pcfs_fat.c18
-rw-r--r--sys/pcfs/pcfs_vfsops.c5
-rw-r--r--sys/pcfs/pcfs_vnops.c41
-rw-r--r--sys/procfs/procfs_subr.c174
-rw-r--r--sys/procfs/procfs_vnops.c10
-rw-r--r--sys/scsi/cd.c13
-rw-r--r--sys/scsi/scsi_base.c16
-rw-r--r--sys/scsi/scsi_ioctl.c2
-rw-r--r--sys/scsi/scsiconf.h3
-rw-r--r--sys/scsi/sd.c20
-rw-r--r--sys/scsi/st.c22
-rw-r--r--sys/sys/acct.h2
-rw-r--r--sys/sys/buf.h8
-rw-r--r--sys/sys/callout.h2
-rw-r--r--sys/sys/conf.h4
-rw-r--r--sys/sys/disklabel.h3
-rw-r--r--sys/sys/dkstat.h2
-rw-r--r--sys/sys/errno.h2
-rw-r--r--sys/sys/exec.h4
-rw-r--r--sys/sys/fcntl.h2
-rw-r--r--sys/sys/ftape.h5
-rw-r--r--sys/sys/ioctl.h9
-rw-r--r--sys/sys/ioctl_compat.h2
-rw-r--r--sys/sys/ipc.h2
-rw-r--r--sys/sys/kernel.h11
-rw-r--r--sys/sys/malloc.h17
-rw-r--r--sys/sys/map.h2
-rw-r--r--sys/sys/mbuf.h54
-rw-r--r--sys/sys/mount.h5
-rw-r--r--sys/sys/namei.h2
-rw-r--r--sys/sys/param.h2
-rw-r--r--sys/sys/proc.h2
-rw-r--r--sys/sys/procfs.h24
-rw-r--r--sys/sys/signal.h2
-rw-r--r--sys/sys/specdev.h4
-rw-r--r--sys/sys/stat.h2
-rw-r--r--sys/sys/syscall.h6
-rw-r--r--sys/sys/systm.h42
-rw-r--r--sys/sys/termios.h8
-rw-r--r--sys/sys/timeb.h2
-rw-r--r--sys/sys/times.h2
-rw-r--r--sys/sys/timex.h290
-rw-r--r--sys/sys/tty.h55
-rw-r--r--sys/sys/ttydefaults.h8
-rw-r--r--sys/sys/types.h2
-rw-r--r--sys/sys/unistd.h12
-rw-r--r--sys/ufs/dir.h2
-rw-r--r--sys/ufs/ufs_disksubr.c7
-rw-r--r--sys/ufs/ufs_lookup.c2
-rw-r--r--sys/ufs/ufs_vfsops.c2
-rw-r--r--sys/ufs/ufs_vnops.c7
-rw-r--r--sys/vm/device_pager.c1
-rw-r--r--sys/vm/queue.h26
-rw-r--r--sys/vm/swap_pager.c1012
-rw-r--r--sys/vm/swap_pager.h9
-rw-r--r--sys/vm/vm_fault.c28
-rw-r--r--sys/vm/vm_glue.c52
-rw-r--r--sys/vm/vm_mmap.c101
-rw-r--r--sys/vm/vm_object.c24
-rw-r--r--sys/vm/vm_object.h5
-rw-r--r--sys/vm/vm_page.c93
-rw-r--r--sys/vm/vm_page.h40
-rw-r--r--sys/vm/vm_pageout.c763
-rw-r--r--sys/vm/vm_pageout.h3
-rw-r--r--sys/vm/vm_pager.c22
-rw-r--r--sys/vm/vm_pager.h4
-rw-r--r--sys/vm/vm_swap.c4
-rw-r--r--sys/vm/vm_unix.c9
-rw-r--r--sys/vm/vnode_pager.c1408
378 files changed, 54616 insertions, 10228 deletions
diff --git a/sys/conf/files b/sys/conf/files
index 3fdbb5a24850..82cf5c8fd714 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $Id: files,v 1.20 1994/01/25 11:16:01 rgrimes Exp $
+# $Id: files,v 1.22 1994/05/17 22:30:25 jkh Exp $
#
ddb/db_access.c optional ddb
ddb/db_aout.c optional ddb
@@ -39,6 +39,7 @@ kern/kern_fork.c standard
kern/kern_kinfo.c standard
kern/kern_ktrace.c standard
kern/kern_malloc.c standard
+kern/kern_ntptime.c standard
kern/kern_proc.c standard
kern/kern_prot.c standard
kern/kern_resource.c standard
@@ -132,6 +133,8 @@ netinet/tcp_subr.c optional inet
netinet/tcp_timer.c optional inet
netinet/tcp_usrreq.c optional inet
netinet/udp_usrreq.c optional inet
+netinet/igmp.c optional multicast
+netinet/ip_mroute.c optional inet multicast mrouting
netiso/clnp_debug.c optional iso
netiso/clnp_er.c optional iso
netiso/clnp_frag.c optional iso
diff --git a/sys/conf/newvers.sh b/sys/conf/newvers.sh
index ab111aa33136..949e4cf49f08 100644
--- a/sys/conf/newvers.sh
+++ b/sys/conf/newvers.sh
@@ -32,7 +32,7 @@
# SUCH DAMAGE.
#
# from: @(#)newvers.sh 7.4 (Berkeley) 12/7/90
-# $Id: newvers.sh,v 1.7.2.3 1994/05/01 19:19:34 rgrimes Exp $
+# $Id: newvers.sh,v 1.10 1994/06/28 10:39:08 jkh Exp $
#
if [ ! -r version ]
@@ -44,12 +44,18 @@ fi
touch version
-kernvers="FreeBSD 1.1(Release)"
+ostype="FreeBSD"
+osrelease="1.1.5.1(RELEASE)"
+kernvers="${ostype} ${osrelease}"
v=`cat version` t=`date "+ %m/%d/%y %H:%M"`
t=`date`
user=${USER-root}
host=`hostname`
dir=`pwd`
(
- echo "char version[] = \"${kernvers} ($1) #${v}: ${t}\\n ${user}@${host}:${dir}\\n\";"
+ echo "const char version[] = \"${kernvers} ($1) #${v}: ${t}\\n ${user}@${host}:${dir}\\n\";"
+ echo "const char ostype[] = \"${ostype}\";"
+ echo "const char osrelease[] = \"${osrelease}\";"
+ echo "const int osbuild = ${v};"
+ echo "const char osconfig[] = \"$1\";"
) > vers.c
diff --git a/sys/conf/param.c b/sys/conf/param.c
index 115a3239c44e..1c4fcb161b33 100644
--- a/sys/conf/param.c
+++ b/sys/conf/param.c
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)param.c 7.20 (Berkeley) 6/27/91
- * $Id: param.c,v 1.9.2.1 1994/05/04 07:44:02 rgrimes Exp $
+ * $Id: param.c,v 1.10 1994/05/04 08:22:05 rgrimes Exp $
*/
#include "sys/param.h"
diff --git a/sys/i386/doc/Changes b/sys/doc/Changes
index 590f087f85bf..685f1f3173b5 100644
--- a/sys/i386/doc/Changes
+++ b/sys/doc/Changes
@@ -1,15 +1,90 @@
Hello, Emacs, this is an -*- Indented-Text -*- file!
-$Id: Changes,v 1.15 1994/02/21 23:03:09 rgrimes Exp $
+$Id: Changes,v 1.5 1994/04/20 19:22:38 wollman Exp $
This file is intended to keep track of important kernel and user
changes in FreeBSD between releases. Entries are in reverse
chronological order; userids can be decoded with the chart at the end
of this file.
+---------------
Since 1.1 BETA:
-
+---------------
+
+- I/O clustering is implemented for all block devices. This should
+ substantially improve the performance of disk drives which don't
+ have write-behind caches, especially on IDE drives. (dyson/davidg)
+
+- Allow mbufs to point to other ``external'' objects than just
+ clusters, and add in performance enhancements for NFS from Yuval
+ Yarom. (davidg)
+
+- Yet more VM system improvements. (dyson/davidg)
+ o 4-MB systems should now spend substantially less time in the VM
+ system.
+ o General cleanup and some portability fixes.
+ o A new system process is now responsible for keeping page
+ statistics, rather than making the pagedaemon do it, which was
+ getting impractical. Performance is improved on small-memory
+ machines.
+ o Process resource usage counters are now properly updated for
+ faults and swaps.
+
+- Fixed bugs in truncating mapped files. (dyson/davidg)
+
+- A new version of the interrupt-handler glue code has been added, which
+ is faster and architecturally cleaner than the previous version. The
+ beginnings of better profiling support have also been added. (bde/davidg)
+
+- This file moved from /sys/i386/doc to /sys/doc. (wollman)
+
+- Bounce buffers have been implemented for ISA devices which use
+ DMA (although not all devices can make use of it yet). (davidg/dyson)
+
+- The set*id() functions have been changed (wollman):
+ o A significant security hole involving the POSIX saved uid and gid
+ has been plugged.
+ o The setreuid() and setregid() functions now never change the real
+ ID, but rather only ensure that you will be able to change the
+ EFFECTIVE ID back to what you specified as a real ID. This change
+ breaks the setruid() and setrgid() functions, which were a bad
+ idea in a POSIX environment anyway. Logic taken from 4.4BSD.
+ o The SUGID process flag bit has been implemented as in 4.4, and the
+ `ps' program modified to print it out.
+
+- The scheduling algorithm has been changed to penalize
+ processes that fork a lot, like `make', which should enhance
+ interactive performance during such operations significantly.
+ (davidg/dyson)
+
+- MCLBYTES is now 4096, so each mbuf cluster is given a whole page, in
+ preparation for page flipping. (davidg/dyson)
+
+- More VM system improvements (dyson/davidg):
+ o Pre-faulting of initial pages on process startup and mmap (faster
+ than starting and immediately taking a fault).
+ o Even more efficient physical map (pmap) code.
+ o Pageouts are now clustered, similar to pagein clustering in 1.1.
+ o The pageout code is more efficient and keeps better statistics.
+ o The procfs can now provide more information from the VM system.
+ o Some pager bugs have been fixed.
+
+- Improved IP checksum and bzero routines. (bde/dyson)
+
+- The Mitsumi CD-ROM driver is more careful about recognizing Ethernet
+ cards as CD-ROMs. (jkh)
+
+- `struct tty's now allocated dynamically, and ring buffers can be
+ deallocated. (guido)
+
+
+---------------------------
Between 1.1 BETA and 1.0.2:
+---------------------------
+
+- QIC-40 and QIC-80 tapes are now supported, using the `ft' driver
+ from Jim Babb. (alm/nate)
+
- Improved lpt driver, should no longer lock up when lprm is done on
an active job. Fixed up the probe routine so that it works on most
if not all printers now. (csgr/rgrimes)
@@ -103,12 +178,12 @@ Between 1.1 BETA and 1.0.2:
the lower 640k is reclaimed for system use. This removes
the 640K kernel size limit.
o VM object cache size is now dynamic and a function of the
- kernel 'maxusers' parameter.
+ kernel `maxusers' parameter.
o Memory in the I/O hole is explicitly marked non-cacheable.
- Added 3C509 driver written by Herb Peyerl. (ats/nate)
-- Added a new 'wd' driver which does a much better job of probing, handles
+- Added a new `wd' driver which does a much better job of probing, handles
stray interrupts better, and supports multiple controllers. In addition
this driver supports DOS partitions much better and conforms to ATA specs
much better. NB: configuration lines for this driver are different
@@ -117,7 +192,7 @@ Between 1.1 BETA and 1.0.2:
- Added support in the if_ed driver for the Toshiba ethernet cards.The
support must be enabled with an "options TOSH_ETHER" in the config
- file. Done it this way, because i don't know how widespread the cards
+ file. Done it this way, because I don't know how widespread the cards
are. (ats)
- Added support in the if_ed driver for the SMC Ultra via patches from
@@ -174,17 +249,10 @@ Between 1.1 BETA and 1.0.2:
- Make it possible to mmap(2) /dev/mem. (julian)
-Between 1.0.2 and 1.0:
-
-Between 1.0 EPSILON and 1.0 GAMMA:
-
-Between 1.0 GAMMA and 1.0 BETA:
-
-Between 1.0 BETA and 1.0 ALPHA:
-
-Between 1.0 ALPHA and Patchkit 0.2.4:
+-----------------------
Userids map as follows:
+-----------------------
ache Andrew Chernov
alm Andrew Moore
ats Andreas Schulz
diff --git a/sys/i386/doc/Makefile b/sys/doc/Makefile
index 67fb36b0ceac..78e3021c3831 100644
--- a/sys/i386/doc/Makefile
+++ b/sys/doc/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.1 1993/11/06 00:07:42 wollman Exp $
+# $Id: Makefile,v 1.1 1994/03/30 20:36:32 wollman Exp $
#
# Makefile for /sys/i386/doc
# This creates options.info and options.doc from options.texi, if the
diff --git a/sys/i386/doc/ata/ata-1 b/sys/doc/ata/ata-1
index 5486217b026b..5486217b026b 100644
--- a/sys/i386/doc/ata/ata-1
+++ b/sys/doc/ata/ata-1
diff --git a/sys/i386/doc/ata/ata-10 b/sys/doc/ata/ata-10
index f4c0336944d4..f4c0336944d4 100644
--- a/sys/i386/doc/ata/ata-10
+++ b/sys/doc/ata/ata-10
diff --git a/sys/i386/doc/ata/ata-11 b/sys/doc/ata/ata-11
index bba6a1073cc8..bba6a1073cc8 100644
--- a/sys/i386/doc/ata/ata-11
+++ b/sys/doc/ata/ata-11
diff --git a/sys/i386/doc/ata/ata-2 b/sys/doc/ata/ata-2
index bad2d9eea869..bad2d9eea869 100644
--- a/sys/i386/doc/ata/ata-2
+++ b/sys/doc/ata/ata-2
diff --git a/sys/i386/doc/ata/ata-3 b/sys/doc/ata/ata-3
index 54645e040b7f..54645e040b7f 100644
--- a/sys/i386/doc/ata/ata-3
+++ b/sys/doc/ata/ata-3
diff --git a/sys/i386/doc/ata/ata-4 b/sys/doc/ata/ata-4
index 0c2fb638ce8d..0c2fb638ce8d 100644
--- a/sys/i386/doc/ata/ata-4
+++ b/sys/doc/ata/ata-4
diff --git a/sys/i386/doc/ata/ata-5 b/sys/doc/ata/ata-5
index c120a39b3736..c120a39b3736 100644
--- a/sys/i386/doc/ata/ata-5
+++ b/sys/doc/ata/ata-5
diff --git a/sys/i386/doc/ata/ata-6 b/sys/doc/ata/ata-6
index 1bce4866f5b5..1bce4866f5b5 100644
--- a/sys/i386/doc/ata/ata-6
+++ b/sys/doc/ata/ata-6
diff --git a/sys/i386/doc/ata/ata-7 b/sys/doc/ata/ata-7
index 240706912a85..240706912a85 100644
--- a/sys/i386/doc/ata/ata-7
+++ b/sys/doc/ata/ata-7
diff --git a/sys/i386/doc/ata/ata-8 b/sys/doc/ata/ata-8
index f651b6b49326..f651b6b49326 100644
--- a/sys/i386/doc/ata/ata-8
+++ b/sys/doc/ata/ata-8
diff --git a/sys/i386/doc/ata/ata-9 b/sys/doc/ata/ata-9
index 4cd3f6684989..4cd3f6684989 100644
--- a/sys/i386/doc/ata/ata-9
+++ b/sys/doc/ata/ata-9
diff --git a/sys/i386/doc/ata/ata-apx b/sys/doc/ata/ata-apx
index cd8981c58eab..cd8981c58eab 100644
--- a/sys/i386/doc/ata/ata-apx
+++ b/sys/doc/ata/ata-apx
diff --git a/sys/i386/doc/ata/ata-prt b/sys/doc/ata/ata-prt
index 546510bcf6a0..546510bcf6a0 100644
--- a/sys/i386/doc/ata/ata-prt
+++ b/sys/doc/ata/ata-prt
diff --git a/sys/i386/doc/ata/ata-toc b/sys/doc/ata/ata-toc
index 1866f7677596..1866f7677596 100644
--- a/sys/i386/doc/ata/ata-toc
+++ b/sys/doc/ata/ata-toc
diff --git a/sys/i386/doc/ed.relnotes b/sys/doc/ed.relnotes
index d01e21d4c427..c46c26124f8f 100644
--- a/sys/i386/doc/ed.relnotes
+++ b/sys/doc/ed.relnotes
@@ -171,4 +171,4 @@ KNOWN PROBLEMS
on the 16bit boards. This can lead to ring-buffer overruns resulting in
additional lost data during heavy network traffic.
-$Id: ed.relnotes,v 1.4 1993/11/22 11:15:16 davidg Exp $
+$Id: ed.relnotes,v 1.1 1994/03/30 20:36:33 wollman Exp $
diff --git a/sys/doc/memory-test.doc b/sys/doc/memory-test.doc
new file mode 100644
index 000000000000..cda44f7f1a48
--- /dev/null
+++ b/sys/doc/memory-test.doc
@@ -0,0 +1,40 @@
+Message-Id: <199403270118.RAA00584@corbin.Root.COM>
+From: David Greenman <davidg@root.com>
+To: "Jordan K. Hubbard" <jkh@whisker.hubbard.ie>
+Cc: csgr@alpha.ru.ac.za, freebsd-hackers@freefall.cdrom.com
+Subject: Re: Boune Buffers : Crash and Burn ^&%$&&^%
+Date: Sat, 26 Mar 1994 17:18:04 -0800
+
+>Ummm. I suppose it would be stupid to wonder whether or not this is good
+>memory you just stuck in there? :-) It does raise the point: David - what
+>does your memory test do when it finds memory errors?
+
+ The order of events is like this:
+
+1) Determine amount of memory by looking at CMOS.
+2) Do memory test of this memory, starting at the highest address and going
+backwards.
+3) If a bad page is found, TRUNCATE the physical memory to end just before
+this page. Continue until all memory is tested.
+
+ So all memory past the lowest bad page is ignored. The number of pages that
+were chopped are then reported as # of "bad pages" when the memory information
+is printed. If there aren't any, then nothing is printed.
+ The rationale for not constructing a bit mask and weeding out just the
+pages that test bad is a flimsy one - doing so would have required moving the
+memory test into the machine independant portion of the VM system, and would
+have required much trickier coding. Since the main purpose of this was to
+verify that what the CMOS was telling us was correct, and also to identify
+when there is bad memory, trying to make the best of it when there really is
+some bad memory didn't seem important to me. ..but hey, if somebody wants to
+recode it, I won't complain. :-)
+ The problem that Terry eluded to, but didn't state, is where you test memory
+for the purpose of determining how much you have. This is problematic because
+many memory controllers will generate a fatal NMI when you 'test' non-existant
+memory (i.e. the parity check fails, and the condition is handled less than
+gracefully), and can also happen with some memory controllers that do strange
+address wrapping in the non-existant memory area. Compaq machines are alledged
+to be suseptable to one of these two problems.
+
+-DG
+
diff --git a/sys/doc/options.doc b/sys/doc/options.doc
new file mode 100644
index 000000000000..178eaf0b3c59
--- /dev/null
+++ b/sys/doc/options.doc
@@ -0,0 +1,682 @@
+$Id: options.doc,v 1.10 1994/06/28 14:00:21 jkh Exp $
+
+ This file documents the configuration options available in the
+FreeBSD operating system.
+
+ Copyright (C) 1993, 1994, Garrett A. Wollman. All rights reserved.
+
+ Redistribution and use in source and printed 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 this list of conditions.
+ 2. Redistributions in printed form must reproduce the above copyright
+ notice, this list of conditions and the following notice of
+ authorship.
+
+ Trademarks are property of their respective owners.
+
+FreeBSD Configuration Options
+*****************************
+
+ This document describes kernel configuration options relevant to the
+FreeBSD operating system between versions 1.1 and 1.2. It is intended
+for readers who already have a general understanding of the process of
+configuring a BSD kernel and wish to get a general overview of the
+meaning of various configuration options. This document covers
+configurable options and pseudo-devices; it is intended that devices may
+be added at a later date.
+
+Options for Subsystems
+**********************
+
+ This chapter discusses options controlling the inclusion of various
+subsystems in FreeBSD. These include things like filesystems,
+networking modules, and whatnot. Remember that options containing
+underscores must be quoted.
+
+`pseudo-device bpfilter NUMBER'
+ The `bpfilter' pseudo-device is the Berkeley Packet Filter,
+ developed by Lawrence Berkeley Labs and based on an earlier packet
+ filter from Stanford. See the `bpf' manual page for more details.
+ The NUMBER given is the maximum number of simultaneous users
+ permitted. (NB: in previous version of BPF, the NUMBER had to be
+ greater than the number of interfaces; this space is now
+ dynamically allocated so this requirement is no longer present.)
+
+`options CCITT'
+ The `CCITT' option enables support for the ITU-T X.25(1980)
+ network-layer protocol. Nobody we know has a direct X.25
+ connection to anything, so this code has never been tested.
+
+ This option will likely be removed in a future release of FreeBSD.
+
+`options "COMPAT_42"'
+ This option is used to disable UDP checksumming. Under ordinary
+ circumstances it should never ever ever be defined; however, if
+ you are stuck trying to communicate with an old 4.2BSD machine, or
+ one running something derived from 4.2 like SunOS 3.5 or Ultrix
+ 2.0, this may be necessary in order to successfully receive UDP
+ packets.
+
+ This option will be replaced by run-time configuration in a future
+ release of FreeBSD.
+
+`options "COMPAT_43"'
+ This option controls a whole host of features, mostly relating to
+ system-call compatibilty with 4.3BSD. At the present time, it
+ should not be turned off, as many utilities and library routines
+ still depend on these obsolescent system calls being present. At
+ some future date, this will probably be split up into two separate
+ options, one for binary compatibility and one for the old but
+ useful system calls.
+
+`options "COMPAT_102"'
+ This option, which is not yet implemented, will control whether
+ certain entry points which were system calls in FreeBSD 1.0.2 but
+ have been replaced with library routines, are supported in the
+ kernel for backwards compatiblity.
+
+`options "DIRECTED_BROADCAST"'
+ If this option is enabled, the kernel will support sending IP
+ broadcast packets to subnets other than the one that the machine
+ is on, and when forwarding will accept such packets. That is to
+ say, if your host lives on subnets `132.198.3' and `132.198.4',
+ and the `132.198.3' side receives a packet addressed to
+ `132.198.4.255', it will forward the packet as a broadcast on that
+ subnet.
+
+ This option will likely be replaced by run-time configuration in a
+ future release of FreeBSD.
+
+`options "DISKLABEL_UNPROTECTED"'
+ This options disables the checks which normally protects the
+ disklabel from being overwritten. This allows dd of=/dev/rwd0d
+ if=file bs=8k to restore an diskimage.
+
+`pseudo-device ether'
+ This pseudo-device provides link-layer support for Ethernet device
+ drivers. It is mandatory for all systems which include Ethernet or
+ Ethernet-like devices, such as `ed', `ie', and `is'. This code is
+ due for a redesign.
+
+`options EON'
+`pseudo-device eon'
+ The `eon' network interface supports the ISO 8473
+ Connectionless-Mode Network Protocol, tunnelled through IP version
+ 4. `eon' interfaces are created automatically once initially
+ configured by adding ISO routes with IP destinations. At present,
+ both the pseudo-device and option declaration are necessary.
+
+ This option will likely be removed in a future release of FreeBSD.
+
+`options FIFO'
+ This option enables support for System V- and POSIX-style named
+ pipes or fifos.
+
+`options GATEWAY'
+`options IPFORWARDING=VALUE'
+`options IPSENDREDIRECTS=VALUE'
+ These three options control whether FreeBSD's IP forwarding
+ functions are enabled. Technically speaking, because FreeBSD does
+ not meet the standards set out in the "Router Requirements"
+ document (RFC 1009), these should not be enabled, but sometimes it
+ is necessary to enable this function. The `GATEWAY' option turns
+ on `IPFORWARDING', and also controls the sizing of certain system
+ tables. The `IPFORWARDING' option controls the initial value of
+ the `ipforwarding' kernel variable (default 1 if `GATEWAY'
+ defined, 0 otherwise), which controls whether packets are acutally
+ forwarded or not; VALUE should be either `0' or `1'.
+ `IPSENDREDIRECTS' controls the initial value of the
+ `ipsendredirects' variable (default is one, but should be changed
+ to zero); its VALUE should also be either `0' or `1'.
+
+ This option will be replaced by run-time configuration in a future
+ release of FreeBSD.
+
+`options INET'
+ This option controls the inclusion of the Internet protocol suite,
+ including IP version 4, TCP, UDP, and ICMP. Support for IP
+ multicast, IP next generation, and IGMP will be provided at a
+ future date. It is not recommended to even attempt to generate a
+ system with this option turned off, as many parts of the system
+ depend on Internet networking in important and subtle ways.
+
+`options ISO'
+`options TPIP'
+ These options control the inclusion of ISO OSI networking
+ protocols. The TPIP option includes just enough support to run
+ ISO Transport Protocol class 4 over IP, supporing the
+ `SOCK_SEQPACKET' abstraction. The ISO option includes support for
+ CLNP, TP class 0, ISO 9542 ESIS, and IEEE 802.2 logical link
+ control class 0 (for CLNP only).
+
+ This option will likely be removed in a future release of FreeBSD.
+
+`options ISOFS'
+ The `ISOFS' option enables kernel support for the ISO 9660 CD-ROM
+ filesystem, including RockRidge extensions.
+
+`options "ISO_X25ESIS"'
+ This option controls whether ISO 9542 ESIS is run over ITU-T X.25
+ link layers. This requires the `CCITT' option to be enabled as
+ well.
+
+ This option will likely be removed in a future release of FreeBSD.
+
+`options KTRACE'
+ This option enables the tracing of several classes of internal
+ kernel events. See the `ktrace' command for more details.
+ Recommended.
+
+`pseudo-device log'
+ The `log' pseudo-device provides kernel support to send kernel
+ messages to `syslog'. It is mandatory.
+
+`pseudo-device loop'
+ The `loop' pseudo-device provides the trivial network interface.
+ It is required when any networking options are enabled.
+
+`options MACHVMCOMPAT'
+ This option enables a Mach-compatible interface to the virtual
+ memory subsystem, supporting system calls `vm_allocate',
+ `vm_deallocate', `vm_inherit', and `vm_protect'. (Given the
+ nature of the VM system, it is impossible to support a Mach-style
+ `vm_region' call, and in every case the `map' argument is ignored
+ and replaced with the calling process's own map.)
+
+`options MFS'
+ This option enables support for the memory filesystem, an in-core
+ filesystem which lives in the swap area. Using MFS as a `/tmp'
+ filesystem can dramatically increase the speed of
+ temporary-space-intensive operations such as compilations. See the
+ `mount_mfs' manual page for more details.
+
+`options MULTICAST'
+ Enable multicast support for things like vat, nv, etc.
+
+`options MROUTING'
+ Enable multicast routing support (generally goes hand-in-hand with
+ the above). See also mrouted(1).
+
+`options NFS'
+ The `NFS' option enables support for Sun's Network File System.
+ (Also called "Nightmare" or "Not a"....) This presently includes
+ both client- and server-side kernelized NFS support; it may in the
+ future be broken into separate options. This NFS implmentation
+ comes to BSD courtesy of Rick Macklem of the University of Guelph,
+ and is not derived from Sun licensed source code. As a result,
+ there are sometimes interoperability problems where the published
+ specification is vague, and this option supports several new and
+ useful features compared to Sun's. See the `mount' manual page
+ for more details.
+
+`options NS'
+`options NSIP'
+ `NS' controls the inclusion of support for the Xerox Network
+ Service protocol family. At the present time, it is not known
+ whether this code even works; testers are welcome. The `NSIP'
+ option enables encapsulation of XNS IDP over IP.
+
+ These options will likely be removed in a future release of
+ FreeBSD.
+
+`options "PANIC_REBOOT_WAIT_TIME=TIME"'
+ This option controls how long the system waits after a panic
+ before it reboots. If a TIME of zero is specified, it reboots
+ immediately; otherwise, TIME is the number of seconds to wait
+ before rebooting. If, during the waiting period, a key is hit on
+ the console, the countdown stops and the system will wait for the
+ user to copy down the panic message and hit another key before
+ rebooting.
+
+`options PCFS'
+ This option controls support for mounting MS-DOS disks and disk
+ partitions under FreeBSD. The `pcfs' manual page is presently very
+ bogus.
+
+`pseudo-device ppp NUMBER'
+ The `ppp' pseudo-device provides support for the Internet
+ Point-to-Point protocol (RFC 1351 et seq), implemented as a line
+ discipline over standard serial links. NUMBER should be the
+ number of simultaneous PPP interfaces which will be configured.
+
+`pseudo-device pty NUMBER'
+ This pseudo-device provides support for pseudo-ttys, which are
+ required for `rlogin', `telnet', and `xterm' to operate correctly;
+ NUMBER should be set to the total number of these programs you
+ expect to have running at any given time. Because pty's are not
+ as yet dynamically allocated, and the underlying structures are
+ large, it is best to keep this value as small as feasible, until
+ this deficiency is remedied.
+
+`options QUOTA'
+ The `QUOTA' option enables support for disk quotas. Note that NFS
+ quota support is not available.
+
+`pseudo-device sl NUMBER'
+ This pseudo-device provides support for the Serial Line Internet
+ Protocol (RFC 1055), implemented as a line discipline over standard
+ serial links. It includes support for Van Jacobson header
+ compression. NUMBER should be the number of simultaneous SLIP
+ interfaces which will be configured. See also the `slattach'
+ manual page.
+
+`options SYSVSHM'
+`options SYSVSEM'
+`options SYSVMSG'
+`options SHMMAXPGS=VALUE'
+ The `SYSVSHM' option enables kernel-side emulation of System
+ V-compatible shared memory. The `SHMMAXPGS' option (default 64
+ pages or 256K) determines the maximum amount of shared memory
+ available under this mechanism. The `SYSVSEM' option provides
+ emulation of System V-compatible semaphores, and likewise
+ `SYSVMSG' for message queues.
+
+`options "TCP_COMPAT_42"'
+ This option controls the perpetuation of several bugs inherited
+ from the 4.2BSD implementation of TCP. It should only be defined
+ in the circumstances outlined for `COMPAT_42', above.
+
+ This option will likely be replaced by run-time configuration in a
+ future release of FreeBSD.
+
+`pseudo-device tun'
+ The `tun' driver provides a network interface which is attached to
+ a character device. In this way, a user-mode program can grab
+ packets out of the networking system, fiddle with them or move
+ them around, and pass stuff packets back up into the kernel. It
+ is not known if this device either compiles or operates correctly,
+ although it was believed to do both at some time in the past.
+
+`options UCONSOLE'
+ This option allows any old user to grab kernel output away from the
+ console and send it to the tty of their choice. It presents an
+ incredile security hole for some systems, but is necessary in
+ order to allow programs like `xconsole' to operate.
+
+`options XSERVER'
+ This obsolescent option enables support in the `pc' console driver
+ for certain operations required by the XFree86 server.
+
+Performace and Debugging Options
+********************************
+
+ The following options are provided for system performace
+optimization. Note that kernel profiling is supported via the `-p'
+option to the `config' command; for more information see the `config'
+manual page.
+
+`psuedo-device ddb'
+ This option enables the `ddb' debugger, taken from Mach. See the
+ `ddb' and `dbsym' manual pages for more information on the use of
+ this debugger.
+
+`options DIAGNOSTIC'
+`options NAMEI_DIAGNOSTIC'
+`options PARANOID'
+ These debugging options reduce performace. They are intended to
+ enable certain internal consistency checks which are not supposed
+ to fail during correct operation, and so are normally disabled for
+ performace reasons.
+
+`options FASTLINKS'
+ The `FASTLINKS' option enables the creation of symbolic links whose
+ target names reside entirely within the i-node of the link, when
+ possible. This results in faster access for those links which are
+ short enough (in practice, most of them). All kernels can read
+ such links, but only `FASTLINKS' kernels will create them, for
+ compatibility with older kernels lacking such support.
+
+`options ICMPPRINTFS'
+ This option is defined to allow debugging of ICMP ("Internet
+ Control Message Protocol") packets in the kernel. When defined
+ and the `icmpprintfs' kernel variable (default false) is set to
+ true, ICMP packets will be printed out to the console when
+ received. Note that it is probably better to use `tcpdump' for
+ this kind of debugging.
+
+`options KGDB'
+ The `KGDB' option enables certain bits of kernel code which will
+ eventually be able to talk to a remote copy of the `gdb' debugger
+ over a serial connection. The present code does not work, but
+ users are invited to hack on it and contribute the changes back to
+ the FreeBSD team.
+
+`options MAXMEM=SIZE'
+ The `MAXMEM' option controls how much memory the kernel will
+ recognize on bootup, specified in kilobytes. This may be useful
+ for dealing with certain broken attachment busses (or the adapters
+ thereon) which are unable to deal with memory beyond a certain
+ address.
+
+`options SUBNETSARELOCAL'
+ This option controls whether the TCP system believes that machines
+ on other subnets of your network are considered to be "local" to
+ your host. For most systems, this option should be on (the
+ default); if you are directly connected to a class A network,
+ however, then it may need to be turned off. (This is true of
+ networks like the MILNET.)
+
+`options "SYMTAB_SPACE=VALUE"'
+ This obsolescent option controls the amount of space that will be
+ statically allocated in the debugger source code to hold the kernel
+ symbol table that `dbsym' sticks there. Eventually this will be
+ dynamically allocated at load time. The default VALUE is 63000
+ bytes.
+
+`options "UPDATE_INTERVAL=VALUE"'
+ This option controls the wait time between successive `sync'
+ operations run by the `update' system process (pid 3). This option
+ will be replaced by run-time configuration in a future release of
+ FreeBSD.
+
+`options DUMMY_NOPS'
+ This option controls the use of real Nops for bus operations.
+ This might break on older systems so should be used with care.
+
+Device Options
+**************
+
+ There are different device selections available depending on the
+type of bus present in your computer. We will cover generic FreeBSD
+devices, ISA-bus devices, and EISA-bus devices. A separate section
+describes the devices available in the SCSI subsystem.
+
+Generic Devices and Options
+===========================
+
+ The following devices and options are available in all FreeBSD
+configurations. In addition to these devices, a selection of ISA
+devices (*note ISA::.) is required in order to generate a workable
+system.
+
+`machine "i386"'
+ This mandatory declaration informs the `config' program that you
+ are using an i386 or compatible CPU, and enables the selection of
+ all the other devices listed here.
+
+`cpu "I386_CPU"'
+`cpu "I486_CPU"'
+ These two options control which specific CPUs will be supported by
+ the generated kernel. If the kernel detects that it is not
+ running on a CPU for which support was enabled, it will panic
+ quickly upon startup. If you do not expect to need to run your
+ kernel on an i386 or similar CPU, leaving out that support can
+ increase virtual memory system performance.
+
+`options "MATH_EMULATE"'
+ When this option is defined, the math coprocessor emulator is
+ compiled into the kernel. When it is not defined and the
+ coprocessor is absent, programs which use floating-point
+ operations are automatically killed.
+
+`device npx0 at isa? port "IO_NPX" irq 13 vector npxintr'
+ The `npx' device provides support for the i387 numeric coprocessor
+ and the floating-point portions of the i486 CPU. This will
+ eventually be fixed to not require ISA to be configured.
+
+`pseudo-device speaker'
+ The `speaker' pseudo-device provides support for rudimentary access
+ to the PC's speaker via `/dev/spkr'. It provides a
+ character-device interface which interprets `PLAY' strings similar
+ to IBM PC Advanced BASIC, as well as an `ioctl' interface with more
+ fine-grained control. See the `spkr' manual page for more
+ information.
+
+ISA-bus Devices and Options
+===========================
+
+ The following options are specific to ISA-bus devices and systems.
+Since the EISA bus is backwards-compatible with the ISA bus, all these
+options also apply to EISA systems. The same goes for VESA Local Bus
+(VL-Bus) systems.
+
+`controller isa0'
+ This *mandatory* declaration must precede any other devices listed
+ in this section. It provides the basic support for the ISA-bus
+ glue logic, including DMA and autoconfiguration.
+
+`controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr'
+`options "TUNE_1542"'
+ The `aha' device supports the Adaptec 154x series of SCSI
+ controllers, and attempts to support other vendors' controllers
+ which claim compatibility with the Adaptec 1542, such as the
+ BusLogic 545. This device is included in the `GENERICAH'
+ distribution kernel. The `scbus' device (*note SCSI::.) is a
+ prerequisite for this device.
+
+ Some older versions of this code would attempt to set the
+ controller's bus access speed to the fastest possible without
+ losing data; we have found that this makes the driver unusable for
+ some users. If you wish to enable this optimization, or if you
+ suspect that your SCSI transfers are running slower than they
+ should, then you can use the `TUNE_1542' option to enable
+ bus-timing detection.
+
+`controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr'
+ This device supports the Bustek 742 SCSI controller. It is
+ included in the `GENERICBT' distribution kernel; the `scbus' device
+ (*note SCSI::.) is a prerequisite.
+
+`options ALLOW_CONFLICT_IOADDR'
+ Allow devices on the ISA bus to share conflicting IO address
+ spaces. This is generally an error, though things like PS/2 mouse
+ drivers which are implemented seperately from the keyboard driver
+ will require this option to be set. Note that this is almost
+ always sub-optimal, and the current PS/2 mouse driver will, in
+ fact, frequently fight with the keyboard if you try to use them
+ concurrently. Needing this option enabled is a sure sign that you
+ need to consider a different design for your driver.
+
+`options ALLOW_CONFLICT_IRQ'
+ Allow devices on the ISA bus to share conflicting IRQ's. This is
+ often necessary for multiport serial cards which have several
+ devices at the same IRQ. Enable this only with caution!
+
+`options COM_MULTIPORT'
+ This option enables support in the `sio' serial driver for certain
+ multi-port serial boards.
+
+`device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr'
+`device ed1 at isa? port 0x300 net irq 5 iomem 0xd8000 vector edintr'
+ The `ed' network interface driver provides support for the Western
+ Digital/SMC 80x3 series, the 3Com 3c503, and Novell NE1000 and
+ NE2000 series of Ethernet controllers. It automatically detects
+ differences among the various versions of these controllers and
+ adapts appropriately. The `ed1' line shown is for the Novell
+ boards; the `ed0' line is appropriate for all other supported
+ controllers. (The Novell controllers cannot be configured to use
+ port 0x280.)
+
+`controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr'
+`disk fd0 at fdc0 drive 0'
+`disk fd1 at fdc0 drive 1'
+`tape ft0 at fdc0 drive 2'
+ The `fdc' driver provides support for the standard PC floppy-disk
+ controller. The `fd' sub-driver supports 3.5- and 5.25-inch
+ floppy disks in the standard 360KB, 720KB, 1200KB, 1440KB, and
+ 2880KB formats, as well as a number of other formats not supported
+ by DOS. The `ft' driver is available for QIC-80 "floppy tape"
+ support. The drivers support formatting of both tapes and disks.
+ This driver is substantially improved from that shipped in
+ previous releases of FreeBSD.
+
+`device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr'
+ This network interface driver provides support for the AT&T
+ StarLAN 10 and EN100 family of controllers. Note that the
+ configuration specified here is not the default configuration, but
+ one which attempts to deal with the conflicts that arise in more
+ modern systems. (It is expected that this driver will be expanded
+ in the future to support other similar cards in the manner of
+ `ed'.)
+
+`device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr'
+ The `is' network interface driver supports the Isolan 4141-0 and
+ Isolink 4110 Ethernet controllers.
+
+`device lpt0 at isa? port "IO_LPT1" tty'
+`device lpt0 at isa? port "IO_LPT1" tty irq 7 vector lptintr'
+`device lpt0 at isa? port ? tty irq 7 vector lptintr'
+`device lpt0 at isa? port ? tty'
+ The `lpt' driver provides support for the parallel printer driver
+ accessed as `/dev/lptN' (N=0, 1, ...). The current version of
+ this driver provides support for either polled or interrupt-driven
+ ports, a unification of the `lpt' and `lpa' drivers from FreeBSD
+ 1.1.
+
+ The first and second examples show explicit selection of a port
+ address. If the port is not specified, as in the third and fourth
+ examples, the driver defaults to whatever address the BIOS printer
+ driver would have used. The second and third examples select
+ interrupt-driven I/O; if polled mode is specified, as in the first
+ and fourth examples, it is impossible to enable interrupt-driven
+ access at run time.
+
+ If you receive "ISA strayintr 7" messages correlated with the use
+ of the polled mode of `lpt', chances are that your controller
+ supports interrupt-driven operation, and you should switch to that
+ mode.
+
+`device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr'
+ This device provides support for the Mitsumi non-SCSI CD-ROM drive.
+ Performance is known to be quite slow.
+
+`device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint'
+`device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr'
+`options NCONS=VALUE'
+`options COMCONSOLE'
+ The `pc' and `sc' devices provide support for the system display
+ and keyboard, which is the default console. There might actually
+ be documentation somewhere for both of these. The `sc' device
+ requires the `NCONS' option to be defined to some value; it
+ represents the number of virtual consoles to be provided by the
+ driver; a reasonable value is 8. One of `pc' or `sc' is presently
+ required unless `COMCONSOLE' is enabled, in which case a serial
+ port is made into the console.
+
+`device psm0 at isa? port "IO_KBD" tty irq 12 vector psmintr'
+ This driver provides support for the IBM-style PS/2 mouse now
+ popular on many PCs. This driver shares an address with the
+ console driver and therefore requires that the option
+ `ALLOW_CONFLICT_IOADDR' also be set. It is also important that
+ the console driver (`pc' or `sc') *preceed* this driver in your
+ kernel configuration file in order to get priority. All in all,
+ this driver is a hack and should really be integrated into the
+ console driver itself, evidence of which can be easily seen when
+ trying to use the mouse and keyboard at the same time in X (try
+ it). Volunteers willing to clean this up and do it properly are
+ most welcome!
+
+`device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr'
+`device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr'
+`device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr'
+`device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr'
+ The `sio' driver provides support for high-speed serial
+ communications using the standard 8250, 16450, and 16550 UART
+ chips. It provides a standard tty interface for these devices as
+ `/dev/ttyUNIT', and, when enabled with the `comcontrol' program, a
+ call-out capability as `/dev/cuaUNIT' (UNIT is two digits,
+ zero-padded in both cases). Certain multi-port systems are also
+ supported.
+
+`device uha0 at isa? port "IO_UHA0" bio irq 14 drq 5 vector uhaintr'
+ This device supports the Ultrastor 14F and related SCSI
+ controllers. It is included in the `GENERICBT' distribution
+ kernel, and requires `scbus' (*note SCSI::.) as a prerequisite.
+ The Ultrastor 24F is not supported.
+
+`controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr'
+`disk wd0 at wdc0 drive 0'
+`disk wd1 at wdc0 drive 1'
+ The `wd' device supports standard ST-506, RLL, ESDI, and IDE hard
+ disks, as controlled by the Western Digital WD100x series of
+ controllers (and compatible hardware). This version is
+ substantially improved from that provided in FreeBSD 1.0.
+
+`device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr'
+ This driver supports Archive QIC-02 and Wangtek QIC-02 and QIC-36
+ cartridge tape controllers.
+
+`device ze0 at isa? port 0x300 net irq 5 iomem 0xd8000 vector zeintr'
+ This driver supports certain PCMCIA ethernet cards. It was
+ originally written for the IBM Credit Card Adapter and has also
+ been tested with the National Semi `InfoMover' PCMCIA card.
+
+EISA-bus Devices and Options
+============================
+
+ There is presently only one EISA-specific device driver.
+
+`controller ahb0 at isa? bio irq 11 vector ahbintr'
+ The `ahb' driver provides support for the Adaptec AHA-174x series
+ of SCSI controllers. This controller is included in the
+ `GENERICAH' distribution kernel, and requires the `scbus' driver
+ (*note SCSI::.) as a prerequisite.
+
+Micro Channel Devices and Options
+=================================
+
+ We don't support Micro Channel right now. Anyone interested in
+working on Micro Channel support should send mail to
+`FreeBSD-Questions@freefall.cdrom.com' for information on how to help.
+
+PCI Devices and Options
+=======================
+
+ We don't support PCI, either. Anyone interested in working on PCI
+support should send mail to `FreeBSD-Questions@freefall.cdrom.com' for
+information on how to help.
+
+The SCSI Subsystem
+==================
+
+ The SCSI subsystem consists of a set of adaptor-specific driver
+routines, which were described in the previous sections, and the generic
+SCSI device drivers, which handle the standardized interactions with
+devices on the SCSI bus.
+
+`device cd0'
+ The `cd' device provides support for CD-ROM drives. Only one `cd'
+ device need be configured, as the driver automatically allocates
+ units for each CD-ROM drive found. Playing of audio CDs is also
+ supported, on drives which support it, through `ioctl' calls.
+ Support for retrieval of CD audio over the SCSI bus is not
+ presently available.
+
+`device ch0'
+ The `ch' driver supports SCSI media changers; this may include
+ tape, removable disk, and CD changers. One `ch' device should be
+ configured for each changer you expect to support.
+
+`device scbus0'
+ This driver forms the core of the SCSI subsystem. It provides the
+ device-independent routines that manage SCSI transactions, keep
+ track of attached devices, and act as glue between
+ SCSI-device-specific drivers and system-specific host adaptors.
+ This device is *mandatory* for all SCSI systems.
+
+`device sd0'
+ The `sd' driver provides access to non-removable SCSI disks. One
+ `sd' device should be defined for each disk you expect to have
+ simultaneously connected to the system.
+
+`device st0'
+ The `st' driver supports generic SCSI tape drives. One `st'
+ device should be defined for each tape drive you wish to access.
+ See the `st' manual page for information about how to manipulate
+ the parameters of this device.
+
+`device uk0'
+ The `uk' driver provides an attachment point for all otherwise
+ unrecognized SCSI devices. You can't actually do anything with
+ such a device, except perhaps send it an inquiry command using the
+ `scsi' program (q.v.).
+
+Internal Use Only
+*****************
+
+ Eventually, this chapter will document some of the kernel manifest
+constants which are not defines, but which can be tweaked in various
+header files.
+
diff --git a/sys/i386/doc/options.texi b/sys/doc/options.texi
index 4313cf009459..7df30b8b442b 100644
--- a/sys/i386/doc/options.texi
+++ b/sys/doc/options.texi
@@ -1,4 +1,10 @@
\input texinfo @c -*- texinfo -*-
+@c -----------------------------------------------------------------------
+@c Thus spoke the elders in days long gone by:
+@c How to edit: use an existing entry as template, keep the file sorted
+@c using the most keyword-ish thing. When done, make sure /usr/gnu/bin
+@c is in your path, run "make" and THEN "cvs commit".
+@c -----------------------------------------------------------------------
@c %**start of header
@setfilename options.info
@settitle Configuration Options for FreeBSD
@@ -10,7 +16,7 @@
@c %**end of header
@ifinfo
-$Id: options.texi,v 1.4.2.1 1994/03/07 01:51:59 rgrimes Exp $
+$Id: options.texi,v 1.11 1994/06/28 06:10:40 jkh Exp $
This file documents the configuration options available in the FreeBSD
operating system.
@@ -33,7 +39,7 @@ Trademarks are property of their respective owners.
@titlepage
@title Configuration Options in FreeBSD
-@subtitle for FreeBSD 1.1
+@subtitle for FreeBSD 1.1.5
@author Garrett A. Wollman
@author FreeBSD Project
@@ -71,12 +77,12 @@ authorship.
@top FreeBSD Configuration Options
This document describes kernel configuration options relevant to the
-FreeBSD operating system version 1.1. It is intended for readers who
-already have a general understanding of the process of configuring a BSD
-kernel and wish to get a general overview of the meaning of various
-configuration options. This document covers configurable options and
-pseudo-devices; it is intended that devices may be added at a later
-date.
+FreeBSD operating system between versions 1.1 and 1.2. It is intended
+for readers who already have a general understanding of the process of
+configuring a BSD kernel and wish to get a general overview of the
+meaning of various configuration options. This document covers
+configurable options and pseudo-devices; it is intended that devices may
+be added at a later date.
@menu
* Subsystems:: Controlling subsystems.
@@ -118,6 +124,8 @@ The @samp{CCITT} option enables support for the ITU-T X.25(1980)
network-layer protocol. Nobody we know has a direct X.25 connection to
anything, so this code has never been tested.
+This option will likely be removed in a future release of FreeBSD.
+
@item options "COMPAT_42"
@findex COMPAT_42
@cindex UDP Checksums
@@ -130,6 +138,9 @@ stuck trying to communicate with an old 4.2BSD machine, or one running
something derived from 4.2 like SunOS 3.5 or Ultrix 2.0, this may be
necessary in order to successfully receive UDP packets.
+This option will be replaced by run-time configuration in a future
+release of FreeBSD.
+
@item options "COMPAT_43"
@findex COMPAT_43
@cindex 4.3 Compatibility
@@ -141,6 +152,16 @@ on these obsolescent system calls being present. At some future date,
this will probably be split up into two separate options, one for binary
compatibility and one for the old but useful system calls.
+@item options "COMPAT_102"
+@findex COMPAT_102
+@cindex FreeBSD 1.0.2 compatibility
+@cindex 1.0.2 Compatibility
+@cindex Compatibility options
+This option, which is not yet implemented, will control whether certain
+entry points which were system calls in FreeBSD 1.0.2 but have been
+replaced with library routines, are supported in the kernel for
+backwards compatiblity.
+
@item options "DIRECTED_BROADCAST"
@findex DIRECTED_BROADCAST
@cindex IP
@@ -153,6 +174,18 @@ on subnets @samp{132.198.3} and @samp{132.198.4}, and the
@samp{132.198.4.255}, it will forward the packet as a broadcast on that
subnet.
+This option will likely be replaced by run-time configuration in a
+future release of FreeBSD.
+
+@item options "DISKLABEL_UNPROTECTED"
+@findex DISKLABEL_UNPROTECTED
+@cindex disk
+@cindex disklabel
+@cindex rawdisk
+This options disables the checks which normally protects the disklabel from
+being overwritten. This allows dd of=/dev/rwd0d if=file bs=8k to restore
+an diskimage.
+
@item pseudo-device ether
@findex ether
@cindex Ethernet
@@ -173,6 +206,8 @@ Connectionless-Mode Network Protocol, tunnelled through IP version 4.
configured by adding ISO routes with IP destinations. At present, both
the pseudo-device and option declaration are necessary.
+This option will likely be removed in a future release of FreeBSD.
+
@item options FIFO
@findex FIFO
@cindex Named pipes
@@ -202,6 +237,9 @@ forwarded or not; @var{value} should be either @samp{0} or @samp{1}.
@samp{ipsendredirects} variable (default is one, but should be changed
to zero); its @var{value} should also be either @samp{0} or @samp{1}.
+This option will be replaced by run-time configuration in a future
+release of FreeBSD.
+
@item options INET
@findex INET
@cindex IP
@@ -230,7 +268,10 @@ These options control the inclusion of ISO OSI networking protocols.
The TPIP option includes just enough support to run ISO Transport
Protocol class 4 over IP, supporing the @samp{SOCK_SEQPACKET}
abstraction. The ISO option includes support for CLNP, TP class 0,
-ISO 9542 ESIS, and IEEE 802.2 logical link control class 0 (for CLNP only).
+ISO 9542 ESIS, and IEEE 802.2 logical link control class 0 (for CLNP
+only).
+
+This option will likely be removed in a future release of FreeBSD.
@item options ISOFS
@findex ISOFS
@@ -248,11 +289,13 @@ filesystem, including RockRidge extensions.
This option controls whether ISO 9542 ESIS is run over ITU-T X.25 link
layers. This requires the @samp{CCITT} option to be enabled as well.
+This option will likely be removed in a future release of FreeBSD.
+
@item options KTRACE
@findex KTRACE
@cindex Kernel tracing
This option enables the tracing of several classes of internal kernel
-events. See the @samp{ktrace} command for more details.
+events. See the @samp{ktrace} command for more details. Recommended.
@item pseudo-device log
@findex log
@@ -286,6 +329,20 @@ filesystem can dramatically increase the speed of
temporary-space-intensive operations such as compilations. See the
@samp{mount_mfs} manual page for more details.
+@item options MULTICAST
+@findex MULTICAST
+@cindex Multicast IP
+@cindex Networking
+Enable multicast support for things like vat, nv, etc.
+
+
+@item options MROUTING
+@findex MROUTING
+@cindex Multicast Routing
+@cindex Networking
+Enable multicast routing support (generally goes hand-in-hand with the
+above). See also mrouted(1).
+
@item options NFS
@findex NFS
@cindex Network File System
@@ -313,16 +370,17 @@ Service protocol family. At the present time, it is not known whether
this code even works; testers are welcome. The @samp{NSIP} option
enables encapsulation of XNS IDP over IP.
-@item options PANICDELAY
-@itemx options PANICWAIT
-@findex PANICDELAY
-@findex PANICWAIT
+These options will likely be removed in a future release of FreeBSD.
+
+@item options "PANIC_REBOOT_WAIT_TIME=@var{time}"
+@findex PANIC_REBOOT_WAIT_TIME
@cindex Kernel panics
-These options control whether the system waits after a panic. This is
-necessary on some systems which do not support crash dumps, so that the
-actual panic message can be read. The @samp{PANICDELAY} option inserts
-a delay of twenty seconds before self-destructing; the @samp{PANICWAIT}
-option instead waits for a key to be pressed on the console.
+This option controls how long the system waits after a panic before it
+reboots. If a @var{time} of zero is specified, it reboots immediately;
+otherwise, @var{time} is the number of seconds to wait before rebooting.
+If, during the waiting period, a key is hit on the console, the
+countdown stops and the system will wait for the user to copy down the
+panic message and hit another key before rebooting.
@item options PCFS
@findex PCFS
@@ -395,22 +453,6 @@ under this mechanism. The @samp{SYSVSEM} option provides emulation of
System V-compatible semaphores, and likewise @samp{SYSVMSG} for message
queues.
-@item options RMP
-@findex RMP
-@cindex Remote maintenance protocol
-@cindex Networking domains
-This option should control the inclusion of support for HP's remote
-maintenance protocol, but the source code is not included in FreeBSD at
-present, so enabling it will not result in any good.
-
-@item pseudo-device tb
-@findex tb
-@cindex Tablet line discipline
-The @samp{tb} pseudo-device provides support for the `tablet' line
-discipline. Nobody on the FreeBSD team actually has one of the tablets
-in question, so we have no idea if this actually works or not. It may
-not even compile.
-
@item options "TCP_COMPAT_42"
@findex TCP_COMPAT_42
@cindex 4.2 Compatibility
@@ -420,6 +462,9 @@ This option controls the perpetuation of several bugs inherited from the
4.2BSD implementation of TCP. It should only be defined in the
circumstances outlined for @samp{COMPAT_42}, above.
+This option will likely be replaced by run-time configuration in a
+future release of FreeBSD.
+
@item pseudo-device tun
@findex tun
@cindex Network interfaces
@@ -436,20 +481,7 @@ to do both at some time in the past.
This option allows any old user to grab kernel output away from the
console and send it to the tty of their choice. It presents an
incredile security hole for some systems, but is necessary in order to
-allow programs like @samp{xconsole} to operate. (The alternative,
-making @samp{xconsole} set-uid root, opens the exact same security
-hole.)
-
-@item options XE
-@findex XE
-@cindex X.25
-@cindex IEEE 802.2 LLC
-@cindex Network interfaces
-@cindex Ethernet
-This option should control the inclusion of support for running X.25
-over IEEE 802.2 LLC class 2, but that code was not included in the
-Networking/2 release, so enabling it will disable kernel compilation.
-Requires @samp{CCITT}.
+allow programs like @samp{xconsole} to operate.
@item options XSERVER
@findex XSERVER
@@ -521,16 +553,12 @@ over a serial connection. The present code does not work, but users are
invited to hack on it and contribute the changes back to the FreeBSD
team.
-@item options MCLSHIFT=@var{value}
-@findex MCLSHIFT
-@cindex Network parameters
-This option controls the number of bytes in an mbuf cluster, which is
-one of the basic units through which network data is managed. It is
-equal to the log base two of @samp{MCLBYTES}, the size of an mbuf
-cluster, and defaults to eleven (for an @samp{MCLBYTES} of 2048). It
-will likely eventually be bumped up to twelve (4096), so that the
-network code can take advantage of page flipping to reduce the numer of
-copies necessary.
+@item options MAXMEM=@var{size}
+@findex MAXMEM
+The @samp{MAXMEM} option controls how much memory the kernel will
+recognize on bootup, specified in kilobytes. This may be useful for
+dealing with certain broken attachment busses (or the adapters thereon)
+which are unable to deal with memory beyond a certain address.
@item options SUBNETSARELOCAL
@findex SUBNETSARELOCAL
@@ -551,6 +579,21 @@ statically allocated in the debugger source code to hold the kernel
symbol table that @samp{dbsym} sticks there. Eventually this will be
dynamically allocated at load time. The default @var{value} is 63000
bytes.
+
+@item options "UPDATE_INTERVAL=@var{value}"
+@findex UPDATE_INTERVAL
+@cindex Update process
+This option controls the wait time between successive @samp{sync}
+operations run by the @samp{update} system process (pid 3). This option
+will be replaced by run-time configuration in a future release of
+FreeBSD.
+
+@item options DUMMY_NOPS
+@findex DUMMY_NOPS
+@cindex Fast i/o bus operations
+This option controls the use of real Nops for bus operations.
+This might break on older systems so should be used with care.
+
@end table
@node Devices, Internals, Performance, Top
@@ -637,7 +680,9 @@ listed in this section. It provides the basic support for the ISA-bus
glue logic, including DMA and autoconfiguration.
@item controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
+@item options "TUNE_1542"
@findex aha
+@findex TUNE_1542
@cindex Adaptec 154x
@cindex SCSI host adaptors
The @samp{aha} device supports the Adaptec 154x series of SCSI
@@ -647,6 +692,13 @@ This device is included in the @samp{GENERICAH} distribution kernel.
The @samp{scbus} device (@pxref{SCSI}) is a prerequisite for this
device.
+Some older versions of this code would attempt to set the controller's
+bus access speed to the fastest possible without losing data; we have
+found that this makes the driver unusable for some users. If you wish
+to enable this optimization, or if you suspect that your SCSI transfers
+are running slower than they should, then you can use the @samp{TUNE_1542}
+option to enable bus-timing detection.
+
@item controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr
@findex bt
@cindex Bustek 742
@@ -655,14 +707,27 @@ This device supports the Bustek 742 SCSI controller. It is included in
the @samp{GENERICBT} distribution kernel; the @samp{scbus} device
(@pxref{SCSI}) is a prerequisite.
-@item options COM_BIDIR
-@findex COM_BIDIR
-@cindex Bi-directional serial ports
-@cindex Serial ports
-This option enables bi-directional support in the @samp{sio} serial
-driver. This option is slated for removal, at which time bi-directional
-support will always be enabled. See the @samp{comcontrol} manual page
-for more information.
+@item options ALLOW_CONFLICT_IOADDR
+@findex ALLOW_CONFLICT_IOADDR
+@cindex Allow IO Address Conflict
+@cindex ISA device management
+Allow devices on the ISA bus to share conflicting IO address spaces.
+This is generally an error, though things like PS/2 mouse drivers which
+are implemented seperately from the keyboard driver will require this
+option to be set. Note that this is almost always sub-optimal, and the
+current PS/2 mouse driver will, in fact, frequently fight with the
+keyboard if you try to use them concurrently. Needing this option
+enabled is a sure sign that you need to consider a different design for
+your driver.
+
+@item options ALLOW_CONFLICT_IRQ
+@findex ALLOW_CONFLICT_IRQ
+@cindex Allow IRQ Conflict
+@cindex ISA device management
+
+Allow devices on the ISA bus to share conflicting IRQ's. This is often
+necessary for multiport serial cards which have several devices at the
+same IRQ. Enable this only with caution!
@item options COM_MULTIPORT
@findex COM_MULTIPORT
@@ -742,21 +807,31 @@ Isolink 4110 Ethernet controllers.
@c source tree. When it is made available, this information will be
@c updated.
-@item device lpa0 at isa? port "IO_LPT1" tty
+@item device lpt0 at isa? port "IO_LPT1" tty
@itemx device lpt0 at isa? port "IO_LPT1" tty irq 7 vector lptintr
+@itemx device lpt0 at isa? port ? tty irq 7 vector lptintr
+@itemx device lpt0 at isa? port ? tty
@findex lpa
@findex lpt
@cindex Parallel printers
-The @samp{lpa} device provides support for the parallel printer driver
-accessed as @file{/dev/lp}. The @samp{lpt} driver provides the same
-functionality, but only works with those printer controllers which
-support interrupt-driven operations. If you receive @samp{ISA strayintr
-7} messages correlated with the use of the @samp{lpa} driver, chances
-are that your controller supports interrupt-driven operation, and you
-should switch to the @samp{lpt} driver.
-The @samp{lpa} driver is obsolete, and will be removed in release 1.2,
-to be replaced by special flags to the @samp{lpt} driver.
+The @samp{lpt} driver provides support for the parallel printer driver
+accessed as @file{/dev/lpt@var{N}} (@var{N}=0, 1, @dots{}). The current
+version of this driver provides support for either polled or
+interrupt-driven ports, a unification of the @samp{lpt} and @samp{lpa}
+drivers from FreeBSD 1.1.
+
+The first and second examples show explicit selection of a port address.
+If the port is not specified, as in the third and fourth examples, the
+driver defaults to whatever address the BIOS printer driver would have
+used. The second and third examples select interrupt-driven I/O; if
+polled mode is specified, as in the first and fourth examples, it is
+impossible to enable interrupt-driven access at run time.
+
+If you receive ``ISA strayintr 7'' messages correlated with the use of
+the polled mode of @samp{lpt}, chances are that your controller supports
+interrupt-driven operation, and you should switch to that mode.
+
@item device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr
@findex mcd
@@ -786,9 +861,24 @@ actually be documentation somewhere for both of these. The @samp{sc}
device requires the @samp{NCONS} option to be defined to some value; it
represents the number of virtual consoles to be provided by the driver;
a reasonable value is 8. One of @samp{pc} or @samp{sc} is presently
-required unless @samp{COMSONSOLE} is enabled, in which case a serial
+required unless @samp{COMCONSOLE} is enabled, in which case a serial
port is made into the console.
+@item device psm0 at isa? port "IO_KBD" tty irq 12 vector psmintr
+@findex psm
+@cindex PS/2 mouse
+@cindex Mouse
+This driver provides support for the IBM-style PS/2 mouse now popular
+on many PCs. This driver shares an address with the console driver
+and therefore requires that the option @samp{ALLOW_CONFLICT_IOADDR}
+also be set. It is also important that the console driver
+(@samp{pc} or @samp{sc}) *preceed* this driver in your kernel configuration
+file in order to get priority. All in all, this driver is a hack and
+should really be integrated into the console driver itself, evidence of
+which can be easily seen when trying to use the mouse and keyboard at the
+same time in X (try it). Volunteers willing to clean this up and do it
+properly are most welcome!
+
@c sb, anyone?
@item device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
@@ -844,6 +934,16 @@ that provided in FreeBSD 1.0.
@cindex Quarter-Inch-Cartridge (QIC) tape drives
This driver supports Archive QIC-02 and Wangtek QIC-02 and QIC-36
cartridge tape controllers.
+
+@item device ze0 at isa? port 0x300 net irq 5 iomem 0xd8000 vector zeintr
+@findex ze
+@cindex Ethernet
+@cindex Network Interfaces
+@cindex PCMCIA
+@cindex PCMCIA Ethernet Cards
+This driver supports certain PCMCIA ethernet cards. It was originally
+written for the IBM Credit Card Adapter and has also been tested
+with the National Semi `InfoMover' PCMCIA card.
@end table
@node EISA, MCA, ISA, Devices
diff --git a/sys/doc/seagate.doc b/sys/doc/seagate.doc
new file mode 100644
index 000000000000..b22f2c9bca4e
--- /dev/null
+++ b/sys/doc/seagate.doc
@@ -0,0 +1,125 @@
+This is a low level driver for Seagate ST01/02, Future Domain TMC-885, TMC-950
+SCSI host adapter that uses Julian Elishers SCSI code.
+
+This driver is the result of looking at code written by the following people:
+
+ Drew Eckhardt
+ Julian Elischer
+ Glen Overby
+ Gary Close
+
+Special thank to
+
+ Robert Knier
+
+that made the fast blind transfer routines, and also helped with debugging.
+
+I am very grateful to these people.
+
+
+Operating system requirements:
+
+This driver uses the latest version of Julian Elischers scsi code, available
+at FreeBSD.cdrom.com in the file called newscsi3.tar.gz. The driver has been
+tested on FreeBSD 1.1-BETA, FreeBSD 1.0.2, 386bsd 0.1. I don't know if will
+work with NetBSD. (I hope it will.)
+
+
+The hardware:
+
+The ST01/02, and Future Domain 950 are very simple SCSI controllers. They are
+not busmastering, so the processor must do all transfers a la IDE. They support
+blind transfer by adding wait states (up to a certain limit). Interrupt is
+generated for reconnect and parity errors (maybe also for some other events).
+
+The card consists of one command port that writes to scsi control lines, reads
+status lines, and a data port that read/writes to the 8 data lines. The address
+decoding gives both data and control ports large memory areas to a single
+port. This is used by the code.
+
+The ST01/02 differs from the FD950 in memory address location and SCSI id.
+
+Probing for the card:
+
+A card is recognized by comparing the BIOS signature with known signatures. A
+new card may not be recognized if the BIOS signature has changed. Please send
+new signatures to me.
+
+Driver function:
+
+A scsi command is sent to scsi_cmd function. The command is either placed in
+the queue or an retryable message is returned. The routine may wait for
+completion of the command depending on the supplied flags. A timer is started
+for every command placed in the queue. The commands are added in the order they
+are received. There is a possiblity to make all REQUEST SENSE commands be
+queued before all other commands, but I dont think it is a good thing (Linux
+do however use this).
+
+The card is mostly controlled by the sea_main function. It is called by
+scsi_cmd, the interrupt routine, and the timeout routine. The sea_main routine
+runs as long there are something to do (transfer data, issue queued commands,
+and handle reconnected commands).
+
+The data transfers may be done in two different ways: Blind and polled
+transfers. They differ in the way the driver does it handshaking with the
+target. During a blind transfer, the driver code blindly transfers a block
+of data without checking for changed phase. During polled transfers, the
+phase is checked between every character transfered. The polled transfers
+are always used for status information and command transfers.
+
+Because the card does not use dma in any way, there is no need to handle
+physical addresses. There is no problem with the isa-bus address limit of
+16MB, making bounce-buffers unnecessary.
+
+The data structures:
+
+Every card has a sea_data structure keeping the queues of commands waiting to
+be issued, and commands currently disconnected. The type of card (Seagate or
+Future Domain), data and control port addresses, scsi id, busy flags for all
+possible targets, and interrupt vector for the card.
+
+Every scsi command to be issued are stored in a sea_scb structure. It contains
+a flag describing status/error of the command, current data buffer position,
+and number of bytes remaining to be transfered.
+
+
+INSTALLATION
+
+1) Alter defines in /sys/i386/isa/seagate.c if you don't
+ want to use blind transfers and/or disconnects. Please note that
+ interrupts must be enabled on the board if disconnects are used.
+2) Create a new config file in /sys/i386/conf containing defines for Julian's
+ scsi code (see GENERICAH or GENERICBT). Replace the aha or bt controller
+ with:
+ controller sea0 at isa? bio irq 5 iomem 0xC8000 iosiz 0x2000 vector seaintr
+3) config, make depend, make, cp, shutdown -r
+
+You should now have a working kernel booted.
+
+I have tested the code on the following hardware: 386DX 24MHz 8MB AMI BIOS,
+Maxtor 7120A IDE, Seagate ST02, Maxtor LXT340S scsi disk, SONY CDU-8003 cdrom,
+(short test with) WANGTEK 6200HS DAT drive.
+
+
+PROBLEMS
+
+I have had problems getting the ST02 boot using FreeBSD boot floppies. I think
+is some problem with BIOS calls not working. It is unfortunately impossible to
+disconnect the ST02 floppy controller.
+
+I have had problems getting the driver to talk to a 40 MB Seagate disk. I
+don't have access to it any more, so I can't do any more checking on it.
+
+NOTE: The ST02 creates its own description of the disk attached. This is not
+the same as the disk says. This translation problem may cause problems when
+sharing a disk between both DOS and BSD. It is however not impossible.
+
+
+/Kent
+
+
+Kent Palmkvist
+kentp@isy.liu.se
+
+
+$Id: seagate.doc,v 1.1 1994/06/28 15:47:12 jkh Exp $
diff --git a/sys/doc/sound.doc b/sys/doc/sound.doc
new file mode 100644
index 000000000000..0c2f10034287
--- /dev/null
+++ b/sys/doc/sound.doc
@@ -0,0 +1,80 @@
+NOTE! Check that there is no #define EXCLUDE_<cardname> lines for
+ the cards you are configuring in the sound/local.h. Otherwise
+ the low level driver for the card is not compiled in the kernel.
+You may add one or more of the following depending on what you do NOT
+want compiled into your kernel. Only use the options for which you
+do NOT have a card to support it, or if you do not want a particular
+functionality.
+
+ options EXCLUDE_AUDIO # NO digital audio support
+ options EXCLUDE_SEQUENCER # NO sequencer support
+ options "EXCLUDE_MPU401" # NO MPU401 support
+ options EXCLUDE_GUS # NO GUS support
+ options EXCLUDE_GUS_IODETECT # NO GUS io detection
+ options EXCLUDE_SB # NO SB support
+ options EXCLUDE_SB_EMULATION # NO PAS SB emulation support
+ options EXCLUDE_SBPRO # NO SB Pro support
+ options "EXCLUDE_SB16" # NO SB 16 support
+ options "EXCLUDE_YM3812" # NO AdLib support
+ options "EXCLUDE_OPL3" # NO OPL3 chip support
+ options EXCLUDE_PAS # NO Pro Audio Studio support
+ options EXCLUDE_PRO_MIDI # NO PAS MIDI support
+ options EXCLUDE_CHIP_MIDI # NO MIDI chip support
+ options EXCLUDE_MIDI # NO MIDI support whatsoever
+
+To enable sound card support, you need to uncomment and add one or more of
+the following lines to your kernel configuration file according to the
+directions below:
+
+#device snd5 at isa? port 0x330 irq 6 vector mpuintr
+#device snd4 at isa? port 0x220 irq 15 drq 6 vector gusintr
+#device snd3 at isa? port 0x388 irq 10 drq 6 vector pasintr
+#device snd2 at isa? port 0x220 irq 7 drq 1 vector sbintr
+#device snd6 at isa? port 0x220 irq 7 drq 5 vector sbintr
+#device snd7 at isa? port 0x300
+#device snd1 at isa? port 0x388
+
+Note for PAS user: you should change snd1 line to
+#device snd1 at isa? port 0x38a
+(next stereo port) to avoid conflict with snd3
+
+ Unit numbers are:
+ 1 for Yamaha FM synth
+ 2 for SB/SB Pro DSP
+ 3 for PAS PCM and Midi
+ 4 for GUS
+ 5 for MPU-401 (there is separate driver for the SB16)
+ 6 for SB16 (DSP)
+ 7 for SB16 Midi (MPU-401 emulation)
+
+ If you have ProAudioSpectrum, uncomment units 3, 2 and 1
+ If you have SoundBlaster 1.0 to 2.0 or SB Pro, uncomment 2 and 1.
+ If you have SoundBlaster 16, uncomment 2, 1, 6 and 7.
+ (use the same IRQ for the cards 2, 6 and 7. The DMA of the
+ card 2 is the 8 bit one and the DMA of the card 6 is the 16 bit one.
+ the port address of the card 7 is the Midi I/O address of the SB16.
+ If you have GravisUltrasound, uncomment 4
+ If you have MPU-401, uncomment 5
+
+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: Some systems with the OPTI chipset will require you to #define
+BROKEN_BUS_CLOCK in /sys/i386/sound/pas2_card.c. Symptoms are that
+you will hear a lot of clicking and popping sounds, like a geiger counter,
+coming out of the PAS even when is not playing anything.
+
+Probing problems: Since the SB16 uses the same IRQ and addresses for
+the different drivers, some of the snd dirvers will not be probed because
+the kernel thinks there is a conflict. Until a real solution is implemented,
+to get all the snd drivers to work, immediately return(0) to the haveseen()
+call in /sys/i386/isa/isa.c on your local copy. (Warning: doing this
+will bypass checks for ALL drivers, so be careful)
+
+ - Jordan Hubbard (jkh@freefall.cdrom.com)
+ - Steven Wallace (swallace@freefall.cdrom.com)
diff --git a/sys/i386/doc/vm_layout.doc b/sys/doc/vm_layout.doc
index 3734fd8ab088..6b95bcaf85d6 100644
--- a/sys/i386/doc/vm_layout.doc
+++ b/sys/doc/vm_layout.doc
@@ -29,4 +29,4 @@ PTDI Address pmap.h/param.h Calculation to locate it in vm space
FDC00000 PTmap PTDPTDI << PDRSHIFT
3F7 FDC00000 PTDPTDI #define (KPTDI-1)
-$Id: vm_layout.doc,v 1.6 1993/10/16 19:25:07 rgrimes Exp $
+$Id: vm_layout.doc,v 1.1 1994/03/30 20:36:36 wollman Exp $
diff --git a/sys/i386/doc/wt.doc b/sys/doc/wt.doc
index 1e9f1bafec8a..1e9f1bafec8a 100644
--- a/sys/i386/doc/wt.doc
+++ b/sys/doc/wt.doc
diff --git a/sys/gnu/fpemul/Changelog b/sys/gnu/fpemul/Changelog
new file mode 100644
index 000000000000..a2fbccd15c2c
--- /dev/null
+++ b/sys/gnu/fpemul/Changelog
@@ -0,0 +1,36 @@
+This file contains the changes made to W. Metzenthem's 387 FPU
+emulator to make it work under NetBSD.
+
+a, Changes to make it compile:
+
+ 1 - Changed the #include's to get the appropriate .h files.
+ 2 - Renamed .S to .s, to satisfy the kernel Makefile.
+ 3 - Changed the C++ style // comments to /* */
+ 4 - Changed the FPU_ORIG_EIP macro. A letter from bde included
+ in the package suggested using tf_isp for using instead
+ of the linux __orig_eip. This later turned out to interfere
+ with the user stack, so i created a separate variable, stored
+ in the i387_union.
+ 5 - Changed the get_fs_.. put_fs_.. fns to fubyte,fuword,subyte,
+ suword.
+ 6 - Removed the verify_area fns. I don't really know what they do,
+ i suppose they verify access to memory. The sufu routines
+ should do this.
+
+b, Changes to make it work:
+
+ 1 - Made math_emulate() to return 0 when successful, so trap() won't
+ try to generate a signal.
+ 2 - Changed the size of the save87 struct in /sys/arch/i387/include/
+ npx.h to accomodate the i387_union.
+
+d, Other changes:
+
+ 1 - Removed obsolate and/or linux specific stuff.
+ 2 - Changed the RE_ENTRANT_CHECK_[ON|OFF] macro to
+ REENTRANT_CHECK([ON|OFF]) so indent can grok it.
+ 3 - Re-indented to Berkeley style.
+ 4 - Limited max no of lookaheads. LOOKAHEAD_LIMIT in fpu_entry.c
+
+
+ Szabolcs Szigeti (pink@fsz.bme.hu)
diff --git a/sys/gnu/fpemul/README b/sys/gnu/fpemul/README
new file mode 100644
index 000000000000..0cef6c456fc7
--- /dev/null
+++ b/sys/gnu/fpemul/README
@@ -0,0 +1,277 @@
+/*
+ * wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ */
+
+wm-FPU-emu is an FPU emulator for Linux. It is derived from wm-emu387
+which is my 80387 emulator for djgpp (gcc under msdos); wm-emu387 was
+in turn based upon emu387 which was written by DJ Delorie for djgpp.
+The interface to the Linux kernel is based upon the original Linux
+math emulator by Linus Torvalds.
+
+My target FPU for wm-FPU-emu is that described in the Intel486
+Programmer's Reference Manual (1992 edition). Numerous facets of the
+functioning of the FPU are not well covered in the Reference Manual;
+in the absence of clear details I have made guesses about the most
+reasonable behaviour. Recently, this situation has improved because
+I now have some access to the results produced by a real 80486 FPU.
+
+wm-FPU-emu does not implement all of the behaviour of the 80486 FPU.
+See "Limitations" later in this file for a partial list of some
+differences. I believe that the missing features are never used by
+normal C or FORTRAN programs.
+
+
+Please report bugs, etc to me at:
+ apm233m@vaxc.cc.monash.edu.au
+
+
+--Bill Metzenthen
+ May 1993
+
+
+----------------------- Internals of wm-FPU-emu -----------------------
+
+Numeric algorithms:
+(1) Add, subtract, and multiply. Nothing remarkable in these.
+(2) Divide has been tuned to get reasonable performance. The algorithm
+ is not the obvious one which most people seem to use, but is designed
+ to take advantage of the characteristics of the 80386. I expect that
+ it has been invented many times before I discovered it, but I have not
+ seen it. It is based upon one of those ideas which one carries around
+ for years without ever bothering to check it out.
+(3) The sqrt function has been tuned to get good performance. It is based
+ upon Newton's classic method. Performance was improved by capitalizing
+ upon the properties of Newton's method, and the code is once again
+ structured taking account of the 80386 characteristics.
+(4) The trig, log, and exp functions are based in each case upon quasi-
+ "optimal" polynomial approximations. My definition of "optimal" was
+ based upon getting good accuracy with reasonable speed.
+
+The code of the emulator is complicated slightly by the need to
+account for a limited form of re-entrancy. Normally, the emulator will
+emulate each FPU instruction to completion without interruption.
+However, it may happen that when the emulator is accessing the user
+memory space, swapping may be needed. In this case the emulator may be
+temporarily suspended while disk i/o takes place. During this time
+another process may use the emulator, thereby changing some static
+variables (eg FPU_st0_ptr, etc). The code which accesses user memory
+is confined to five files:
+ fpu_entry.c
+ reg_ld_str.c
+ load_store.c
+ get_address.c
+ errors.c
+
+----------------------- Limitations of wm-FPU-emu -----------------------
+
+There are a number of differences between the current wm-FPU-emu
+(version beta 1.4) and the 80486 FPU (apart from bugs). Some of the
+more important differences are listed below:
+
+All internal computations are performed at 64 bit or higher precision
+and rounded etc as required by the PC bits of the FPU control word.
+Under the crt0 version for Linux current at March 1993, the FPU PC
+bits specify 53 bits precision.
+
+The precision flag (PE of the FPU status word) and the Roundup flag
+(C1 of the status word) are now partially implemented. Does anyone
+write code which uses these features?
+
+The functions which load/store the FPU state are partially implemented,
+but the implementation should be sufficient for handling FPU errors etc
+in 32 bit protected mode.
+
+The implementation of the exception mechanism is flawed for unmasked
+interrupts.
+
+Detection of certain conditions, such as denormal operands, is not yet
+complete.
+
+----------------------- Performance of wm-FPU-emu -----------------------
+
+Speed.
+-----
+
+The speed of floating point computation with the emulator will depend
+upon instruction mix. Relative performance is best for the instructions
+which require most computation. The simple instructions are adversely
+affected by the fpu instruction trap overhead.
+
+
+Timing: Some simple timing tests have been made on the emulator functions.
+The times include load/store instructions. All times are in microseconds
+measured on a 33MHz 386 with 64k cache. The Turbo C tests were under
+ms-dos, the next two columns are for emulators running with the djgpp
+ms-dos extender. The final column is for wm-FPU-emu in Linux 0.97,
+using libm4.0 (hard).
+
+function Turbo C djgpp 1.06 WM-emu387 wm-FPU-emu
+
+ + 60.5 154.8 76.5 139.4
+ - 61.1-65.5 157.3-160.8 76.2-79.5 142.9-144.7
+ * 71.0 190.8 79.6 146.6
+ / 61.2-75.0 261.4-266.9 75.3-91.6 142.2-158.1
+
+ sin() 310.8 4692.0 319.0 398.5
+ cos() 284.4 4855.2 308.0 388.7
+ tan() 495.0 8807.1 394.9 504.7
+ atan() 328.9 4866.4 601.1 419.5-491.9
+
+ sqrt() 128.7 crashed 145.2 227.0
+ log() 413.1-419.1 5103.4-5354.21 254.7-282.2 409.4-437.1
+ exp() 479.1 6619.2 469.1 850.8
+
+
+The performance under Linux is improved by the use of look-ahead code.
+The following results show the improvement which is obtained under
+Linux due to the look-ahead code. Also given are the times for the
+original Linux emulator with the 4.1 'soft' lib.
+
+ [ Linus' note: I changed look-ahead to be the default under linux, as
+ there was no reason not to use it after I had edited it to be
+ disabled during tracing ]
+
+ wm-FPU-emu w original w
+ look-ahead 'soft' lib
+ + 106.4 190.2
+ - 108.6-111.6 192.4-216.2
+ * 113.4 193.1
+ / 108.8-124.4 700.1-706.2
+
+ sin() 390.5 2642.0
+ cos() 381.5 2767.4
+ tan() 496.5 3153.3
+ atan() 367.2-435.5 2439.4-3396.8
+
+ sqrt() 195.1 4732.5
+ log() 358.0-387.5 3359.2-3390.3
+ exp() 619.3 4046.4
+
+
+These figures are now somewhat out-of-date. The emulator has become
+progressively slower for most functions as more of the 80486 features
+have been implemented.
+
+
+----------------------- Accuracy of wm-FPU-emu -----------------------
+
+
+Accuracy: The following table gives the accuracy of the sqrt(), trig
+and log functions. Each function was tested at about 400 points. Ideal
+results would be 64 bits. The reduced accuracy of cos() and tan() for
+arguments greater than pi/4 can be thought of as being due to the
+precision of the argument x; e.g. an argument of pi/2-(1e-10) which is
+accurate to 64 bits can result in a relative accuracy in cos() of about
+64 + log2(cos(x)) = 31 bits. Results for the Turbo C emulator are given
+in the last column.
+
+
+Function Tested x range Worst result (bits) Turbo C
+
+sqrt(x) 1 .. 2 64.1 63.2
+atan(x) 1e-10 .. 200 62.6 62.8
+cos(x) 0 .. pi/2-(1e-10) 63.2 (x <= pi/4) 62.4
+ 35.2 (x = pi/2-(1e-10)) 31.9
+sin(x) 1e-10 .. pi/2 63.0 62.8
+tan(x) 1e-10 .. pi/2-(1e-10) 62.4 (x <= pi/4) 62.1
+ 35.2 (x = pi/2-(1e-10)) 31.9
+exp(x) 0 .. 1 63.1 62.9
+log(x) 1+1e-6 .. 2 62.4 62.1
+
+
+As of version 1.3 of the emulator, the accuracy of the basic
+arithmetic has been improved (by a small fraction of a bit). Care has
+been taken to ensure full accuracy of the rounding of the basic
+arithmetic functions (+,-,*,/,and fsqrt), and they all now produce
+results which are exact to the 64th bit (unless there are any bugs
+left). To ensure this, it was necessary to effectively get information
+of up to about 128 bits precision. The emulator now passes the
+"paranoia" tests (compiled with gcc 2.3.3) for 'float' variables (24
+bit precision numbers) when precision control is set to 24, 53 or 64
+bits, and for 'double' variables (53 bit precision numbers) when
+precision control is set to 53 bits (a properly performing FPU cannot
+pass the 'paranoia' tests for 'double' variables when precision
+control is set to 64 bits).
+
+------------------------- Contributors -------------------------------
+
+A number of people have contributed to the development of the
+emulator, often by just reporting bugs, sometimes with a suggested
+fix, and a few kind people have provided me with access in one way or
+another to an 80486 machine. Contributors include (to those people who
+I have forgotten, please excuse me):
+
+Linus Torvalds
+Tommy.Thorn@daimi.aau.dk
+Andrew.Tridgell@anu.edu.au
+Nick Holloway alfie@dcs.warwick.ac.uk
+Hermano Moura moura@dcs.gla.ac.uk
+Jon Jagger J.Jagger@scp.ac.uk
+Lennart Benschop
+Brian Gallew geek+@CMU.EDU
+Thomas Staniszewski ts3v+@andrew.cmu.edu
+Martin Howell mph@plasma.apana.org.au
+M Saggaf alsaggaf@athena.mit.edu
+Peter Barker PETER@socpsy.sci.fau.edu
+tom@vlsivie.tuwien.ac.at
+Dan Russel russed@rpi.edu
+Daniel Carosone danielce@ee.mu.oz.au
+cae@jpmorgan.com
+Hamish Coleman t933093@minyos.xx.rmit.oz.au
+
+...and numerous others who responded to my request for help with
+a real 80486.
+
diff --git a/sys/gnu/fpemul/control_w.h b/sys/gnu/fpemul/control_w.h
new file mode 100644
index 000000000000..2f3dc2ec854a
--- /dev/null
+++ b/sys/gnu/fpemul/control_w.h
@@ -0,0 +1,95 @@
+/*
+ * control_w.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: control_w.h,v 1.3 1994/06/10 07:44:07 rich Exp $
+ *
+ */
+
+#ifndef _CONTROLW_H_
+#define _CONTROLW_H_
+
+#ifdef LOCORE
+#define _Const_(x) $/**/x
+#else
+#define _Const_(x) x
+#endif
+
+#define CW_RC _Const_(0x0C00) /* rounding control */
+#define CW_PC _Const_(0x0300) /* precision control */
+
+#define CW_Precision Const_(0x0020) /* loss of precision mask */
+#define CW_Underflow Const_(0x0010) /* underflow mask */
+#define CW_Overflow Const_(0x0008) /* overflow mask */
+#define CW_ZeroDiv Const_(0x0004) /* divide by zero mask */
+#define CW_Denormal Const_(0x0002) /* denormalized operand mask */
+#define CW_Invalid Const_(0x0001) /* invalid operation mask */
+
+#define CW_Exceptions _Const_(0x003f) /* all masks */
+
+#define RC_RND _Const_(0x0000)
+#define RC_DOWN _Const_(0x0400)
+#define RC_UP _Const_(0x0800)
+#define RC_CHOP _Const_(0x0C00)
+
+/* p 15-5: Precision control bits affect only the following:
+ ADD, SUB(R), MUL, DIV(R), and SQRT */
+#define PR_24_BITS _Const_(0x000)
+#define PR_53_BITS _Const_(0x200)
+#define PR_64_BITS _Const_(0x300)
+/* FULL_PRECISION simulates all exceptions masked */
+#define FULL_PRECISION (PR_64_BITS | RC_RND | 0x3f)
+
+#endif /* _CONTROLW_H_ */
diff --git a/sys/gnu/fpemul/div_small.s b/sys/gnu/fpemul/div_small.s
new file mode 100644
index 000000000000..aff2738fe43f
--- /dev/null
+++ b/sys/gnu/fpemul/div_small.s
@@ -0,0 +1,101 @@
+ .file "div_small.S"
+/*
+ * div_small.S
+ *
+ * Divide a 64 bit integer by a 32 bit integer & return remainder.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: div_small.s,v 1.3 1994/06/10 07:44:08 rich Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | unsigned long div_small(unsigned long long *x, unsigned long y) |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_asm.h"
+
+.text
+ .align 2,144
+
+.globl _div_small
+
+_div_small:
+ pushl %ebp
+ movl %esp,%ebp
+
+ pushl %esi
+
+ movl PARAM1,%esi /* pointer to num */
+ movl PARAM2,%ecx /* The denominator */
+
+ movl 4(%esi),%eax /* Get the current num msw */
+ xorl %edx,%edx
+ divl %ecx
+
+ movl %eax,4(%esi)
+
+ movl (%esi),%eax /* Get the num lsw */
+ divl %ecx
+
+ movl %eax,(%esi)
+
+ movl %edx,%eax /* Return the remainder in eax */
+
+ popl %esi
+
+ leave
+ ret
+
diff --git a/sys/gnu/fpemul/errors.c b/sys/gnu/fpemul/errors.c
new file mode 100644
index 000000000000..dd8f1b2a1692
--- /dev/null
+++ b/sys/gnu/fpemul/errors.c
@@ -0,0 +1,612 @@
+/*
+ * errors.c
+ *
+ * The error handling functions for wm-FPU-emu
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: errors.c,v 1.3 1994/06/10 07:44:10 rich Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+
+
+
+
+
+#include "param.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "status_w.h"
+#include "control_w.h"
+#include "reg_constant.h"
+#include "version.h"
+
+/* */
+#undef PRINT_MESSAGES
+/* */
+
+
+void
+Un_impl(void)
+{
+ unsigned char byte1, FPU_modrm;
+
+ REENTRANT_CHECK(OFF);
+ byte1 = fubyte((unsigned char *) FPU_ORIG_EIP);
+ FPU_modrm = fubyte(1 + (unsigned char *) FPU_ORIG_EIP);
+
+ printf("Unimplemented FPU Opcode at eip=%p : %02x ",
+ FPU_ORIG_EIP, byte1);
+
+ if (FPU_modrm >= 0300)
+ printf("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
+ else
+ printf("/%d\n", (FPU_modrm >> 3) & 7);
+ REENTRANT_CHECK(ON);
+
+ EXCEPTION(EX_Invalid);
+
+}
+
+
+
+
+void
+emu_printall()
+{
+ int i;
+ static char *tag_desc[] = {"Valid", "Zero", "ERROR", "ERROR",
+ "DeNorm", "Inf", "NaN", "Empty"};
+ unsigned char byte1, FPU_modrm;
+
+ REENTRANT_CHECK(OFF);
+ byte1 = fubyte((unsigned char *) FPU_ORIG_EIP);
+ FPU_modrm = fubyte(1 + (unsigned char *) FPU_ORIG_EIP);
+
+#ifdef DEBUGGING
+ if (status_word & SW_Backward)
+ printf("SW: backward compatibility\n");
+ if (status_word & SW_C3)
+ printf("SW: condition bit 3\n");
+ if (status_word & SW_C2)
+ printf("SW: condition bit 2\n");
+ if (status_word & SW_C1)
+ printf("SW: condition bit 1\n");
+ if (status_word & SW_C0)
+ printf("SW: condition bit 0\n");
+ if (status_word & SW_Summary)
+ printf("SW: exception summary\n");
+ if (status_word & SW_Stack_Fault)
+ printf("SW: stack fault\n");
+ if (status_word & SW_Precision)
+ printf("SW: loss of precision\n");
+ if (status_word & SW_Underflow)
+ printf("SW: underflow\n");
+ if (status_word & SW_Overflow)
+ printf("SW: overflow\n");
+ if (status_word & SW_Zero_Div)
+ printf("SW: divide by zero\n");
+ if (status_word & SW_Denorm_Op)
+ printf("SW: denormalized operand\n");
+ if (status_word & SW_Invalid)
+ printf("SW: invalid operation\n");
+#endif /* DEBUGGING */
+
+ status_word = status_word & ~SW_Top;
+ status_word |= (top & 7) << SW_Top_Shift;
+
+ printf("At %p: %02x ", FPU_ORIG_EIP, byte1);
+ if (FPU_modrm >= 0300)
+ printf("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
+ else
+ printf("/%d, mod=%d rm=%d\n",
+ (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7);
+
+ printf(" SW: b=%d st=%d es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n",
+ status_word & 0x8000 ? 1 : 0, /* busy */
+ (status_word & 0x3800) >> 11, /* stack top pointer */
+ status_word & 0x80 ? 1 : 0, /* Error summary status */
+ status_word & 0x40 ? 1 : 0, /* Stack flag */
+ status_word & SW_C3 ? 1 : 0, status_word & SW_C2 ? 1 : 0, /* cc */
+ status_word & SW_C1 ? 1 : 0, status_word & SW_C0 ? 1 : 0, /* cc */
+ status_word & SW_Precision ? 1 : 0, status_word & SW_Underflow ? 1 : 0,
+ status_word & SW_Overflow ? 1 : 0, status_word & SW_Zero_Div ? 1 : 0,
+ status_word & SW_Denorm_Op ? 1 : 0, status_word & SW_Invalid ? 1 : 0);
+
+ printf(" CW: ic=%d rc=%d%d pc=%d%d iem=%d ef=%d%d%d%d%d%d\n",
+ control_word & 0x1000 ? 1 : 0,
+ (control_word & 0x800) >> 11, (control_word & 0x400) >> 10,
+ (control_word & 0x200) >> 9, (control_word & 0x100) >> 8,
+ control_word & 0x80 ? 1 : 0,
+ control_word & SW_Precision ? 1 : 0, control_word & SW_Underflow ? 1 : 0,
+ control_word & SW_Overflow ? 1 : 0, control_word & SW_Zero_Div ? 1 : 0,
+ control_word & SW_Denorm_Op ? 1 : 0, control_word & SW_Invalid ? 1 : 0);
+
+ for (i = 0; i < 8; i++) {
+ FPU_REG *r = &st(i);
+ switch (r->tag) {
+ case TW_Empty:
+ continue;
+ break;
+ case TW_Zero:
+ printf("st(%d) %c .0000 0000 0000 0000 ",
+ i, r->sign ? '-' : '+');
+ break;
+ case TW_Valid:
+ case TW_NaN:
+ case TW_Denormal:
+ case TW_Infinity:
+ printf("st(%d) %c .%04x %04x %04x %04x e%+-6d ", i,
+ r->sign ? '-' : '+',
+ (long) (r->sigh >> 16),
+ (long) (r->sigh & 0xFFFF),
+ (long) (r->sigl >> 16),
+ (long) (r->sigl & 0xFFFF),
+ r->exp - EXP_BIAS + 1);
+ break;
+ default:
+ printf("Whoops! Error in errors.c ");
+ break;
+ }
+ printf("%s\n", tag_desc[(int) (unsigned) r->tag]);
+ }
+
+ printf("[data] %c .%04x %04x %04x %04x e%+-6d ",
+ FPU_loaded_data.sign ? '-' : '+',
+ (long) (FPU_loaded_data.sigh >> 16),
+ (long) (FPU_loaded_data.sigh & 0xFFFF),
+ (long) (FPU_loaded_data.sigl >> 16),
+ (long) (FPU_loaded_data.sigl & 0xFFFF),
+ FPU_loaded_data.exp - EXP_BIAS + 1);
+ printf("%s\n", tag_desc[(int) (unsigned) FPU_loaded_data.tag]);
+ REENTRANT_CHECK(ON);
+
+}
+
+static struct {
+ int type;
+ char *name;
+} exception_names[] = {
+ {
+ EX_StackOver, "stack overflow"
+ },
+ {
+ EX_StackUnder, "stack underflow"
+ },
+ {
+ EX_Precision, "loss of precision"
+ },
+ {
+ EX_Underflow, "underflow"
+ },
+ {
+ EX_Overflow, "overflow"
+ },
+ {
+ EX_ZeroDiv, "divide by zero"
+ },
+ {
+ EX_Denormal, "denormalized operand"
+ },
+ {
+ EX_Invalid, "invalid operation"
+ },
+ {
+ EX_INTERNAL, "INTERNAL BUG in " FPU_VERSION
+ },
+ {
+ 0, NULL
+ }
+};
+/*
+ EX_INTERNAL is always given with a code which indicates where the
+ error was detected.
+
+ Internal error types:
+ 0x14 in e14.c
+ 0x1nn in a *.c file:
+ 0x101 in reg_add_sub.c
+ 0x102 in reg_mul.c
+ 0x103 in poly_sin.c
+ 0x104 in poly_tan.c
+ 0x105 in reg_mul.c
+ 0x106 in reg_mov.c
+ 0x107 in fpu_trig.c
+ 0x108 in reg_compare.c
+ 0x109 in reg_compare.c
+ 0x110 in reg_add_sub.c
+ 0x111 in interface.c
+ 0x112 in fpu_trig.c
+ 0x113 in reg_add_sub.c
+ 0x114 in reg_ld_str.c
+ 0x115 in fpu_trig.c
+ 0x116 in fpu_trig.c
+ 0x117 in fpu_trig.c
+ 0x118 in fpu_trig.c
+ 0x119 in fpu_trig.c
+ 0x120 in poly_atan.c
+ 0x121 in reg_compare.c
+ 0x122 in reg_compare.c
+ 0x123 in reg_compare.c
+ 0x2nn in an *.s file:
+ 0x201 in reg_u_add.S
+ 0x202 in reg_u_div.S
+ 0x203 in reg_u_div.S
+ 0x204 in reg_u_div.S
+ 0x205 in reg_u_mul.S
+ 0x206 in reg_u_sub.S
+ 0x207 in wm_sqrt.S
+ 0x208 in reg_div.S
+ 0x209 in reg_u_sub.S
+ 0x210 in reg_u_sub.S
+ 0x211 in reg_u_sub.S
+ 0x212 in reg_u_sub.S
+ 0x213 in wm_sqrt.S
+ 0x214 in wm_sqrt.S
+ 0x215 in wm_sqrt.S
+ 0x216 in reg_round.S
+ 0x217 in reg_round.S
+ 0x218 in reg_round.S
+ */
+
+void
+exception(int n)
+{
+ int i, int_type;
+
+ int_type = 0; /* Needed only to stop compiler warnings */
+ if (n & EX_INTERNAL) {
+ int_type = n - EX_INTERNAL;
+ n = EX_INTERNAL;
+ /* Set lots of exception bits! */
+ status_word |= (SW_Exc_Mask | SW_Summary | FPU_BUSY);
+ } else {
+ /* Extract only the bits which we use to set the status word */
+ n &= (SW_Exc_Mask);
+ /* Set the corresponding exception bit */
+ status_word |= n;
+ if (status_word & ~control_word & CW_Exceptions)
+ status_word |= SW_Summary;
+ if (n & (SW_Stack_Fault | EX_Precision)) {
+ if (!(n & SW_C1))
+ /* This bit distinguishes over- from underflow
+ * for a stack fault, and roundup from
+ * round-down for precision loss. */
+ status_word &= ~SW_C1;
+ }
+ }
+
+ REENTRANT_CHECK(OFF);
+ if ((~control_word & n & CW_Exceptions) || (n == EX_INTERNAL)) {
+#ifdef PRINT_MESSAGES
+ /* My message from the sponsor */
+ printf(FPU_VERSION " " __DATE__ " (C) W. Metzenthen.\n");
+#endif /* PRINT_MESSAGES */
+
+ /* Get a name string for error reporting */
+ for (i = 0; exception_names[i].type; i++)
+ if ((exception_names[i].type & n) == exception_names[i].type)
+ break;
+
+ if (exception_names[i].type) {
+#ifdef PRINT_MESSAGES
+ printf("FP Exception: %s!\n", exception_names[i].name);
+#endif /* PRINT_MESSAGES */
+ } else
+ printf("FP emulator: Unknown Exception: 0x%04x!\n", n);
+
+ if (n == EX_INTERNAL) {
+ printf("FP emulator: Internal error type 0x%04x\n", int_type);
+ emu_printall();
+ }
+#ifdef PRINT_MESSAGES
+ else
+ emu_printall();
+#endif /* PRINT_MESSAGES */
+
+ /* The 80486 generates an interrupt on the next non-control
+ * FPU instruction. So we need some means of flagging it. We
+ * use the ES (Error Summary) bit for this, assuming that this
+ * is the way a real FPU does it (until I can check it out),
+ * if not, then some method such as the following kludge might
+ * be needed. */
+/* regs[0].tag |= TW_FPU_Interrupt; */
+ }
+ REENTRANT_CHECK(ON);
+
+#ifdef __DEBUG__
+ math_abort(SIGFPE);
+#endif /* __DEBUG__ */
+
+}
+
+
+/* Real operation attempted on two operands, one a NaN */
+void
+real_2op_NaN(FPU_REG * a, FPU_REG * b, FPU_REG * dest)
+{
+ FPU_REG *x;
+ int signalling;
+
+ x = a;
+ if (a->tag == TW_NaN) {
+ if (b->tag == TW_NaN) {
+ signalling = !(a->sigh & b->sigh & 0x40000000);
+ /* find the "larger" */
+ if (*(long long *) &(a->sigl) < *(long long *) &(b->sigl))
+ x = b;
+ } else {
+ /* return the quiet version of the NaN in a */
+ signalling = !(a->sigh & 0x40000000);
+ }
+ } else
+#ifdef PARANOID
+ if (b->tag == TW_NaN)
+#endif /* PARANOID */
+ {
+ signalling = !(b->sigh & 0x40000000);
+ x = b;
+ }
+#ifdef PARANOID
+ else {
+ signalling = 0;
+ EXCEPTION(EX_INTERNAL | 0x113);
+ x = &CONST_QNaN;
+ }
+#endif /* PARANOID */
+
+ if (!signalling) {
+ if (!(x->sigh & 0x80000000)) /* pseudo-NaN ? */
+ x = &CONST_QNaN;
+ reg_move(x, dest);
+ return;
+ }
+ if (control_word & CW_Invalid) {
+ /* The masked response */
+ if (!(x->sigh & 0x80000000)) /* pseudo-NaN ? */
+ x = &CONST_QNaN;
+ reg_move(x, dest);
+ /* ensure a Quiet NaN */
+ dest->sigh |= 0x40000000;
+ }
+ EXCEPTION(EX_Invalid);
+
+ return;
+}
+/* Invalid arith operation on Valid registers */
+void
+arith_invalid(FPU_REG * dest)
+{
+
+ if (control_word & CW_Invalid) {
+ /* The masked response */
+ reg_move(&CONST_QNaN, dest);
+ }
+ EXCEPTION(EX_Invalid);
+
+ return;
+
+}
+
+
+/* Divide a finite number by zero */
+void
+divide_by_zero(int sign, FPU_REG * dest)
+{
+
+ if (control_word & CW_ZeroDiv) {
+ /* The masked response */
+ reg_move(&CONST_INF, dest);
+ dest->sign = (unsigned char) sign;
+ }
+ EXCEPTION(EX_ZeroDiv);
+
+ return;
+
+}
+
+
+/* This may be called often, so keep it lean */
+void
+set_precision_flag_up(void)
+{
+ if (control_word & CW_Precision)
+ status_word |= (SW_Precision | SW_C1); /* The masked response */
+ else
+ exception(EX_Precision | SW_C1);
+
+}
+
+
+/* This may be called often, so keep it lean */
+void
+set_precision_flag_down(void)
+{
+ if (control_word & CW_Precision) { /* The masked response */
+ status_word &= ~SW_C1;
+ status_word |= SW_Precision;
+ } else
+ exception(EX_Precision);
+}
+
+
+int
+denormal_operand(void)
+{
+ if (control_word & CW_Denormal) { /* The masked response */
+ status_word |= SW_Denorm_Op;
+ return 0;
+ } else {
+ exception(EX_Denormal);
+ return 1;
+ }
+}
+
+
+void
+arith_overflow(FPU_REG * dest)
+{
+
+ if (control_word & CW_Overflow) {
+ char sign;
+ /* The masked response */
+/* **** The response here depends upon the rounding mode */
+ sign = dest->sign;
+ reg_move(&CONST_INF, dest);
+ dest->sign = sign;
+ } else {
+ /* Subtract the magic number from the exponent */
+ dest->exp -= (3 * (1 << 13));
+ }
+
+ /* By definition, precision is lost. It appears that the roundup bit
+ * (C1) is also set by convention. */
+ EXCEPTION(EX_Overflow | EX_Precision | SW_C1);
+
+ return;
+
+}
+
+
+void
+arith_underflow(FPU_REG * dest)
+{
+
+ if (control_word & CW_Underflow) {
+ /* The masked response */
+ if (dest->exp <= EXP_UNDER - 63)
+ reg_move(&CONST_Z, dest);
+ } else {
+ /* Add the magic number to the exponent */
+ dest->exp += (3 * (1 << 13));
+ }
+
+ EXCEPTION(EX_Underflow);
+
+ return;
+}
+
+
+void
+stack_overflow(void)
+{
+
+ if (control_word & CW_Invalid) {
+ /* The masked response */
+ top--;
+ reg_move(&CONST_QNaN, FPU_st0_ptr = &st(0));
+ }
+ EXCEPTION(EX_StackOver);
+
+ return;
+
+}
+
+
+void
+stack_underflow(void)
+{
+
+ if (control_word & CW_Invalid) {
+ /* The masked response */
+ reg_move(&CONST_QNaN, FPU_st0_ptr);
+ }
+ EXCEPTION(EX_StackUnder);
+
+ return;
+
+}
+
+
+void
+stack_underflow_i(int i)
+{
+
+ if (control_word & CW_Invalid) {
+ /* The masked response */
+ reg_move(&CONST_QNaN, &(st(i)));
+ }
+ EXCEPTION(EX_StackUnder);
+
+ return;
+
+}
+
+
+void
+stack_underflow_pop(int i)
+{
+
+ if (control_word & CW_Invalid) {
+ /* The masked response */
+ reg_move(&CONST_QNaN, &(st(i)));
+ pop();
+ }
+ EXCEPTION(EX_StackUnder);
+
+ return;
+
+}
diff --git a/sys/gnu/fpemul/exception.h b/sys/gnu/fpemul/exception.h
new file mode 100644
index 000000000000..af814f1d5191
--- /dev/null
+++ b/sys/gnu/fpemul/exception.h
@@ -0,0 +1,102 @@
+/*
+ * exception.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: exception.h,v 1.3 1994/06/10 07:44:12 rich Exp $
+ *
+ *
+ */
+
+#ifndef _EXCEPTION_H_
+#define _EXCEPTION_H_
+
+
+#ifdef LOCORE
+#define Const_(x) $/**/x
+#else
+#define Const_(x) x
+#endif
+
+#ifndef SW_C1
+#include "fpu_emu.h"
+#endif /* SW_C1 */
+
+#define FPU_BUSY Const_(0x8000) /* FPU busy bit (8087 compatibility) */
+#define EX_ErrorSummary Const_(0x0080) /* Error summary status */
+/* Special exceptions: */
+#define EX_INTERNAL Const_(0x8000) /* Internal error in wm-FPU-emu */
+#define EX_StackOver Const_(0x0041|SW_C1) /* stack overflow */
+#define EX_StackUnder Const_(0x0041) /* stack underflow */
+/* Exception flags: */
+#define EX_Precision Const_(0x0020) /* loss of precision */
+#define EX_Underflow Const_(0x0010) /* underflow */
+#define EX_Overflow Const_(0x0008) /* overflow */
+#define EX_ZeroDiv Const_(0x0004) /* divide by zero */
+#define EX_Denormal Const_(0x0002) /* denormalized operand */
+#define EX_Invalid Const_(0x0001) /* invalid operation */
+
+
+#ifndef LOCORE
+
+#ifdef DEBUG
+#define EXCEPTION(x) { printf("exception in %s at line %d\n", \
+ __FILE__, __LINE__); exception(x); }
+#else
+#define EXCEPTION(x) exception(x)
+#endif
+
+#endif /* LOCORE */
+
+#endif /* _EXCEPTION_H_ */
diff --git a/sys/gnu/fpemul/fpu_arith.c b/sys/gnu/fpemul/fpu_arith.c
new file mode 100644
index 000000000000..0ffb6615e70b
--- /dev/null
+++ b/sys/gnu/fpemul/fpu_arith.c
@@ -0,0 +1,235 @@
+/*
+ * fpu_arith.c
+ *
+ * Code to implement the FPU register/register arithmetic instructions
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: fpu_arith.c,v 1.3 1994/06/10 07:44:14 rich Exp $
+ *
+ */
+
+
+
+
+#include "param.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "control_w.h"
+
+
+void
+fadd__()
+{
+ /* fadd st,st(i) */
+ reg_add(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
+}
+
+
+void
+fmul__()
+{
+ /* fmul st,st(i) */
+ reg_mul(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
+}
+
+
+
+void
+fsub__()
+{
+ /* fsub st,st(i) */
+ reg_sub(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
+}
+
+
+void
+fsubr_()
+{
+ /* fsubr st,st(i) */
+ reg_sub(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word);
+}
+
+
+void
+fdiv__()
+{
+ /* fdiv st,st(i) */
+ reg_div(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
+}
+
+
+void
+fdivr_()
+{
+ /* fdivr st,st(i) */
+ reg_div(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word);
+}
+
+
+
+void
+fadd_i()
+{
+ /* fadd st(i),st */
+ reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
+}
+
+
+void
+fmul_i()
+{
+ /* fmul st(i),st */
+ reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
+}
+
+
+void
+fsubri()
+{
+ /* fsubr st(i),st */
+ /* This is the sense of the 80486 manual reg_sub(&st(FPU_rm),
+ * FPU_st0_ptr, &st(FPU_rm), control_word); */
+ reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
+}
+
+
+void
+fsub_i()
+{
+ /* fsub st(i),st */
+ /* This is the sense of the 80486 manual reg_sub(FPU_st0_ptr,
+ * &st(FPU_rm), &st(FPU_rm), control_word); */
+ reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
+}
+
+
+void
+fdivri()
+{
+ /* fdivr st(i),st */
+ reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
+}
+
+
+void
+fdiv_i()
+{
+ /* fdiv st(i),st */
+ reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
+}
+
+
+
+void
+faddp_()
+{
+ /* faddp st(i),st */
+ reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
+ pop();
+}
+
+
+void
+fmulp_()
+{
+ /* fmulp st(i),st */
+ reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
+ pop();
+}
+
+
+
+void
+fsubrp()
+{
+ /* fsubrp st(i),st */
+ /* This is the sense of the 80486 manual reg_sub(&st(FPU_rm),
+ * FPU_st0_ptr, &st(FPU_rm), control_word); */
+ reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
+ pop();
+}
+
+
+void
+fsubp_()
+{
+ /* fsubp st(i),st */
+ /* This is the sense of the 80486 manual reg_sub(FPU_st0_ptr,
+ * &st(FPU_rm), &st(FPU_rm), control_word); */
+ reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
+ pop();
+}
+
+
+void
+fdivrp()
+{
+ /* fdivrp st(i),st */
+ reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
+ pop();
+}
+
+
+void
+fdivp_()
+{
+ /* fdivp st(i),st */
+ reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
+ pop();
+}
diff --git a/sys/gnu/fpemul/fpu_asm.h b/sys/gnu/fpemul/fpu_asm.h
new file mode 100644
index 000000000000..2f6e58a65410
--- /dev/null
+++ b/sys/gnu/fpemul/fpu_asm.h
@@ -0,0 +1,82 @@
+/*
+ * fpu_asm.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: fpu_asm.h,v 1.3 1994/06/10 07:44:16 rich Exp $
+ *
+ */
+
+#ifndef _FPU_ASM_H_
+#define _FPU_ASM_H_
+
+#include "fpu_emu.h"
+
+#define EXCEPTION _exception
+
+
+#define PARAM1 8(%ebp)
+#define PARAM2 12(%ebp)
+#define PARAM3 16(%ebp)
+#define PARAM4 20(%ebp)
+
+#define SIGL_OFFSET 8
+#define SIGN(x) (x)
+#define TAG(x) 1(x)
+#define EXP(x) 4(x)
+#define SIG(x) SIGL_OFFSET/**/(x)
+#define SIGL(x) SIGL_OFFSET/**/(x)
+#define SIGH(x) 12(x)
+
+#endif /* _FPU_ASM_H_ */
diff --git a/sys/gnu/fpemul/fpu_aux.c b/sys/gnu/fpemul/fpu_aux.c
new file mode 100644
index 000000000000..135fef9e105f
--- /dev/null
+++ b/sys/gnu/fpemul/fpu_aux.c
@@ -0,0 +1,233 @@
+/*
+ * fpu_aux.c
+ *
+ * Code to implement some of the FPU auxiliary instructions.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: fpu_aux.c,v 1.3 1994/06/10 07:44:17 rich Exp $
+ *
+ */
+
+
+#include "param.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "status_w.h"
+
+
+
+void
+fclex(void)
+{
+ status_word &= ~(SW_Backward | SW_Summary | SW_Stack_Fault | SW_Precision |
+ SW_Underflow | SW_Overflow | SW_Zero_Div | SW_Denorm_Op |
+ SW_Invalid);
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+}
+/* Needs to be externally visible */
+void
+finit()
+{
+ int r;
+ control_word = 0x037f;
+ status_word = 0;
+ top = 0; /* We don't keep top in the status word
+ * internally. */
+ for (r = 0; r < 8; r++) {
+ regs[r].tag = TW_Empty;
+ }
+ FPU_entry_eip = ip_offset = 0;
+}
+
+static FUNC finit_table[] = {
+ Un_impl, Un_impl, fclex, finit, Un_impl, Un_impl, Un_impl, Un_impl
+};
+
+void
+finit_()
+{
+ (finit_table[FPU_rm]) ();
+}
+
+
+static void
+fstsw_ax(void)
+{
+
+ status_word &= ~SW_Top;
+ status_word |= (top & 7) << SW_Top_Shift;
+
+ *(short *) &FPU_EAX = status_word;
+
+}
+
+static FUNC fstsw_table[] = {
+ fstsw_ax, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl
+};
+
+void
+fstsw_()
+{
+ (fstsw_table[FPU_rm]) ();
+}
+
+
+
+static void
+fnop(void)
+{
+}
+
+FUNC fp_nop_table[] = {
+ fnop, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl
+};
+
+void
+fp_nop()
+{
+ (fp_nop_table[FPU_rm]) ();
+}
+
+
+void
+fld_i_()
+{
+ FPU_REG *st_new_ptr;
+
+ if (STACK_OVERFLOW) {
+ stack_overflow();
+ return;
+ }
+ /* fld st(i) */
+ if (NOT_EMPTY(FPU_rm)) {
+ reg_move(&st(FPU_rm), st_new_ptr);
+ push();
+ } else {
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ push();
+ stack_underflow();
+ } else
+ EXCEPTION(EX_StackUnder);
+ }
+
+}
+
+
+void
+fxch_i()
+{
+ /* fxch st(i) */
+ FPU_REG t;
+ register FPU_REG *sti_ptr = &st(FPU_rm);
+
+ if (FPU_st0_tag == TW_Empty) {
+ if (sti_ptr->tag == TW_Empty) {
+ stack_underflow();
+ stack_underflow_i(FPU_rm);
+ return;
+ }
+ reg_move(sti_ptr, FPU_st0_ptr);
+ stack_underflow_i(FPU_rm);
+ return;
+ }
+ if (sti_ptr->tag == TW_Empty) {
+ reg_move(FPU_st0_ptr, sti_ptr);
+ stack_underflow();
+ return;
+ }
+ reg_move(FPU_st0_ptr, &t);
+ reg_move(sti_ptr, FPU_st0_ptr);
+ reg_move(&t, sti_ptr);
+}
+
+
+void
+ffree_()
+{
+ /* ffree st(i) */
+ st(FPU_rm).tag = TW_Empty;
+}
+
+
+void
+ffreep()
+{
+ /* ffree st(i) + pop - unofficial code */
+ st(FPU_rm).tag = TW_Empty;
+ pop();
+}
+
+
+void
+fst_i_()
+{
+ /* fst st(i) */
+ reg_move(FPU_st0_ptr, &st(FPU_rm));
+}
+
+
+void
+fstp_i()
+{
+ /* fstp st(i) */
+ reg_move(FPU_st0_ptr, &st(FPU_rm));
+ pop();
+}
diff --git a/sys/gnu/fpemul/fpu_emu.h b/sys/gnu/fpemul/fpu_emu.h
new file mode 100644
index 000000000000..b81a85127fec
--- /dev/null
+++ b/sys/gnu/fpemul/fpu_emu.h
@@ -0,0 +1,188 @@
+/*
+ * fpu_emu.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: fpu_emu.h,v 1.3 1994/06/10 07:44:19 rich Exp $
+ *
+ */
+
+
+#ifndef _FPU_EMU_H_
+#define _FPU_EMU_H_
+
+/*
+ * Define DENORM_OPERAND to make the emulator detect denormals
+ * and use the denormal flag of the status word. Note: this only
+ * affects the flag and corresponding interrupt, the emulator
+ * will always generate denormals and operate upon them as required.
+ */
+#define DENORM_OPERAND
+
+/*
+ * Define PECULIAR_486 to get a closer approximation to 80486 behaviour,
+ * rather than behaviour which appears to be cleaner.
+ * This is a matter of opinion: for all I know, the 80486 may simply
+ * be complying with the IEEE spec. Maybe one day I'll get to see the
+ * spec...
+ */
+#define PECULIAR_486
+
+#ifdef LOCORE
+#include "fpu_asm.h"
+#define Const(x) $/**/x
+#else
+#define Const(x) x
+#endif
+
+#define EXP_BIAS Const(0)
+#define EXP_OVER Const(0x4000) /* smallest invalid large exponent */
+#define EXP_UNDER Const(-0x3fff) /* largest invalid small exponent */
+
+#define SIGN_POS Const(0)
+#define SIGN_NEG Const(1)
+
+/* Keep the order TW_Valid, TW_Zero, TW_Denormal */
+#define TW_Valid Const(0)/* valid */
+#define TW_Zero Const(1)/* zero */
+/* The following fold to 2 (Special) in the Tag Word */
+#define TW_Denormal Const(4)/* De-normal */
+#define TW_Infinity Const(5)/* + or - infinity */
+#define TW_NaN Const(6)/* Not a Number */
+
+#define TW_Empty Const(7)/* empty */
+
+ /* #define TW_FPU_Interrupt Const(0x80) *//* Signals an interrupt */
+
+
+#ifndef LOCORE
+
+#include "types.h"
+#include "math_emu.h"
+
+#ifdef PARANOID
+extern char emulating;
+#define REENTRANT_CHECK(state) emulating = (state)
+#define ON 1
+#define OFF 0
+#else
+#define REENTRANT_CHECK(state)
+#endif /* PARANOID */
+
+typedef void (*FUNC) (void);
+typedef struct fpu_reg FPU_REG;
+
+#define st(x) ( regs[((top+x) &7 )] )
+
+#define STACK_OVERFLOW (st_new_ptr = &st(-1), st_new_ptr->tag != TW_Empty)
+#define NOT_EMPTY(i) (st(i).tag != TW_Empty)
+#define NOT_EMPTY_0 (FPU_st0_tag ^ TW_Empty)
+
+extern unsigned char FPU_rm;
+
+extern char FPU_st0_tag;
+extern FPU_REG *FPU_st0_ptr;
+
+extern void *FPU_data_address;
+
+extern FPU_REG FPU_loaded_data;
+
+#define pop() { FPU_st0_ptr->tag = TW_Empty; top++; }
+
+/* push() does not affect the tags */
+#define push() { top--; FPU_st0_ptr = st_new_ptr; }
+
+
+#define reg_move(x, y) { \
+ *(short *)&((y)->sign) = *(short *)&((x)->sign); \
+ *(long *)&((y)->exp) = *(long *)&((x)->exp); \
+ *(long long *)&((y)->sigl) = *(long long *)&((x)->sigl); }
+
+
+/*----- Prototypes for functions written in assembler -----*/
+/* extern void reg_move(FPU_REG *a, FPU_REG *b); */
+
+extern void mul64(long long *a, long long *b, long long *result);
+extern void poly_div2(long long *x);
+extern void poly_div4(long long *x);
+extern void poly_div16(long long *x);
+extern void
+polynomial(unsigned accum[], unsigned x[],
+ unsigned short terms[][4], int n);
+ extern void normalize(FPU_REG * x);
+ extern void normalize_nuo(FPU_REG * x);
+ extern void reg_div(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ,
+ unsigned int control_w);
+ extern void reg_u_sub(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ,
+ unsigned int control_w);
+ extern void reg_u_mul(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ,
+ unsigned int control_w);
+ extern void reg_u_div(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ,
+ unsigned int control_w);
+ extern void reg_u_add(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ,
+ unsigned int control_w);
+ extern void wm_sqrt(FPU_REG * n, unsigned int control_w);
+ extern unsigned shrx(void *l, unsigned x);
+ extern unsigned shrxs(void *v, unsigned x);
+ extern unsigned long div_small(unsigned long long *x, unsigned long y);
+ extern void round_reg(FPU_REG * arg, unsigned int extent,
+ unsigned int control_w);
+
+#ifndef MAKING_PROTO
+#include "fpu_proto.h"
+#endif
+
+#endif /* LOCORE */
+
+#endif /* _FPU_EMU_H_ */
diff --git a/sys/gnu/fpemul/fpu_entry.c b/sys/gnu/fpemul/fpu_entry.c
new file mode 100644
index 000000000000..7a882227bcdb
--- /dev/null
+++ b/sys/gnu/fpemul/fpu_entry.c
@@ -0,0 +1,483 @@
+/*
+ * fpu_entry.c
+ *
+ * The entry function for wm-FPU-emu
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ * $Id: fpu_entry.c,v 1.4 1994/06/22 05:52:14 jkh Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | math_emulate() is the sole entry point for wm-FPU-emu |
+ +---------------------------------------------------------------------------*/
+
+
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "control_w.h"
+#include "status_w.h"
+
+
+#define __BAD__ Un_impl /* Not implemented */
+
+#define FPU_LOOKAHEAD 1 /* For performance boost */
+
+#if FPU_LOOKAHEAD != 0 /* I think thet we have to limit the */
+#define LOOKAHEAD_LIMIT 7 /* Max number of lookahead instructions*/
+#endif /* Or else a prog consisting of a million */
+ /* fnops will spend all its time in kernel*/
+
+#ifndef NO_UNDOC_CODE /* Un-documented FPU op-codes supported by
+ * default. */
+
+/* WARNING: These codes are not documented by Intel in their 80486 manual
+ and may not work on FPU clones or later Intel FPUs. */
+
+/* Changes to support the un-doc codes provided by Linus Torvalds. */
+
+#define _d9_d8_ fstp_i /* unofficial code (19) */
+#define _dc_d0_ fcom_st /* unofficial code (14) */
+#define _dc_d8_ fcompst /* unofficial code (1c) */
+#define _dd_c8_ fxch_i /* unofficial code (0d) */
+#define _de_d0_ fcompst /* unofficial code (16) */
+#define _df_c0_ ffreep /* unofficial code (07) ffree + pop */
+#define _df_c8_ fxch_i /* unofficial code (0f) */
+#define _df_d0_ fstp_i /* unofficial code (17) */
+#define _df_d8_ fstp_i /* unofficial code (1f) */
+
+static FUNC st_instr_table[64] = {
+ fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_,
+ fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_,
+ fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_,
+ fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_,
+ fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
+ fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
+ fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
+ fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
+};
+#else /* Support only documented FPU op-codes */
+
+static FUNC st_instr_table[64] = {
+ fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__,
+ fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__,
+ fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__,
+ fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__,
+ fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
+ fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
+ fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
+ fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
+};
+#endif /* NO_UNDOC_CODE */
+
+
+#define _NONE_ 0 /* Take no special action */
+#define _REG0_ 1 /* Need to check for not empty st(0) */
+#define _REGI_ 2 /* Need to check for not empty st(0) and
+ * st(rm) */
+#define _REGi_ 0 /* Uses st(rm) */
+#define _PUSH_ 3 /* Need to check for space to push onto stack */
+#define _null_ 4 /* Function illegal or not implemented */
+#define _REGIi 5 /* Uses st(0) and st(rm), result to st(rm) */
+#define _REGIp 6 /* Uses st(0) and st(rm), result to st(rm)
+ * then pop */
+#define _REGIc 0 /* Compare st(0) and st(rm) */
+#define _REGIn 0 /* Uses st(0) and st(rm), but handle checks
+ * later */
+
+#ifndef NO_UNDOC_CODE
+
+/* Un-documented FPU op-codes supported by default. (see above) */
+
+static unsigned char type_table[64] = {
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
+ _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
+ _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
+ _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
+ _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
+ _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
+};
+#else /* Support only documented FPU op-codes */
+
+static unsigned char type_table[64] = {
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_,
+ _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
+ _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
+ _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_,
+ _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
+ _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
+};
+#endif /* NO_UNDOC_CODE */
+
+/* Be careful when using any of these global variables...
+ they might change if swapping is triggered */
+unsigned char FPU_rm;
+char FPU_st0_tag;
+FPU_REG *FPU_st0_ptr;
+
+#ifdef PARANOID
+char emulating = 0;
+#endif /* PARANOID */
+
+#define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
+#define math_abort(signo) \
+ FPU_EIP = FPU_ORIG_EIP;REENTRANT_CHECK(OFF);return(signo);
+
+int
+math_emulate(struct trapframe * tframe)
+{
+
+ unsigned char FPU_modrm;
+ unsigned short code;
+#ifdef LOOKAHEAD_LIMIT
+ int lookahead_limit = LOOKAHEAD_LIMIT;
+#endif
+#ifdef PARANOID
+ if (emulating) {
+ printf("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
+ }
+ REENTRANT_CHECK(ON);
+#endif /* PARANOID */
+
+ if ((((struct pcb *) curproc->p_addr)->pcb_flags & FP_SOFTFP) == 0) {
+ finit();
+ control_word = __INITIAL_NPXCW__;
+ ((struct pcb *) curproc->p_addr)->pcb_flags |= FP_SOFTFP;
+ }
+ FPU_info = tframe;
+ FPU_ORIG_EIP = FPU_EIP; /* --pink-- */
+
+ if (FPU_CS != 0x001f) {
+ printf("math_emulate: %x : %x\n", FPU_CS, FPU_EIP);
+ panic("FPU emulation in kernel");
+ }
+#ifdef notyet
+ /* We cannot handle emulation in v86-mode */
+ if (FPU_EFLAGS & 0x00020000) {
+ FPU_ORIG_EIP = FPU_EIP;
+ math_abort(FPU_info, SIGILL);
+ }
+#endif
+
+ FPU_lookahead = FPU_LOOKAHEAD;
+ if (curproc->p_flag & STRC)
+ FPU_lookahead = 0;
+
+do_another_FPU_instruction:
+
+ REENTRANT_CHECK(OFF);
+ code = fuword((u_int *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ if ((code & 0xff) == 0x9b) { /* fwait */
+ if (status_word & SW_Summary)
+ goto do_the_FPU_interrupt;
+ else {
+ FPU_EIP++;
+ goto FPU_instruction_done;
+ }
+ }
+ if (status_word & SW_Summary) {
+ /* Ignore the error for now if the current instruction is a
+ * no-wait control instruction */
+ /* The 80486 manual contradicts itself on this topic, so I use
+ * the following list of such instructions until I can check
+ * on a real 80486: fninit, fnstenv, fnsave, fnstsw, fnstenv,
+ * fnclex. */
+ if (!((((code & 0xf803) == 0xe003) || /* fnclex, fninit,
+ * fnstsw */
+ (((code & 0x3003) == 0x3001) && /* fnsave, fnstcw,
+ * fnstenv, fnstsw */
+ ((code & 0xc000) != 0xc000))))) {
+ /* This is a guess about what a real FPU might do to
+ * this bit: */
+/* status_word &= ~SW_Summary; ****/
+
+ /* We need to simulate the action of the kernel to FPU
+ * interrupts here. Currently, the "real FPU" part of
+ * the kernel (0.99.10) clears the exception flags,
+ * sets the registers to empty, and passes information
+ * back to the interrupted process via the cs selector
+ * and operand selector, so we do the same. */
+ do_the_FPU_interrupt:
+ cs_selector &= 0xffff0000;
+ cs_selector |= (status_word & ~SW_Top) | ((top & 7) << SW_Top_Shift);
+ operand_selector = tag_word();
+ status_word = 0;
+ top = 0;
+ {
+ int r;
+ for (r = 0; r < 8; r++) {
+ regs[r].tag = TW_Empty;
+ }
+ }
+ REENTRANT_CHECK(OFF);
+ math_abort(SIGFPE);
+ }
+ }
+ FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP;
+
+ if ((code & 0xff) == 0x66) { /* size prefix */
+ FPU_EIP++;
+ REENTRANT_CHECK(OFF);
+ code = fuword((u_int *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ }
+ FPU_EIP += 2;
+
+ FPU_modrm = code >> 8;
+ FPU_rm = FPU_modrm & 7;
+
+ if (FPU_modrm < 0300) {
+ /* All of these instructions use the mod/rm byte to get a data
+ * address */
+ get_address(FPU_modrm);
+ if (!(code & 1)) {
+ unsigned short status1 = status_word;
+ FPU_st0_ptr = &st(0);
+ FPU_st0_tag = FPU_st0_ptr->tag;
+
+ /* Stack underflow has priority */
+ if (NOT_EMPTY_0) {
+ switch ((code >> 1) & 3) {
+ case 0:
+ reg_load_single();
+ break;
+ case 1:
+ reg_load_int32();
+ break;
+ case 2:
+ reg_load_double();
+ break;
+ case 3:
+ reg_load_int16();
+ break;
+ }
+
+ /* No more access to user memory, it is safe
+ * to use static data now */
+ FPU_st0_ptr = &st(0);
+ FPU_st0_tag = FPU_st0_ptr->tag;
+
+ /* NaN operands have the next priority. */
+ /* We have to delay looking at st(0) until
+ * after loading the data, because that data
+ * might contain an SNaN */
+ if ((FPU_st0_tag == TW_NaN) ||
+ (FPU_loaded_data.tag == TW_NaN)) {
+ /* Restore the status word; we might
+ * have loaded a denormal. */
+ status_word = status1;
+ if ((FPU_modrm & 0x30) == 0x10) {
+ /* fcom or fcomp */
+ EXCEPTION(EX_Invalid);
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ if (FPU_modrm & 0x08)
+ pop(); /* fcomp, so we pop. */
+ } else
+ real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
+ goto reg_mem_instr_done;
+ }
+ switch ((FPU_modrm >> 3) & 7) {
+ case 0: /* fadd */
+ reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
+ break;
+ case 1: /* fmul */
+ reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
+ break;
+ case 2: /* fcom */
+ compare_st_data();
+ break;
+ case 3: /* fcomp */
+ compare_st_data();
+ pop();
+ break;
+ case 4: /* fsub */
+ reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
+ break;
+ case 5: /* fsubr */
+ reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, control_word);
+ break;
+ case 6: /* fdiv */
+ reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
+ break;
+ case 7: /* fdivr */
+ if (FPU_st0_tag == TW_Zero)
+ status_word = status1; /* Undo any denorm tag,
+ * zero-divide has
+ * priority. */
+ reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, control_word);
+ break;
+ }
+ } else {
+ if ((FPU_modrm & 0x30) == 0x10) {
+ /* The instruction is fcom or fcomp */
+ EXCEPTION(EX_StackUnder);
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ if (FPU_modrm & 0x08)
+ pop(); /* fcomp, Empty or not,
+ * we pop. */
+ } else
+ stack_underflow();
+ }
+ } else {
+ load_store_instr(((FPU_modrm & 0x38) | (code & 6)) >> 1);
+ }
+
+reg_mem_instr_done:
+
+ data_operand_offset = (unsigned long) FPU_data_address;
+ } else {
+ /* None of these instructions access user memory */
+ unsigned char instr_index = (FPU_modrm & 0x38) | (code & 7);
+
+ FPU_st0_ptr = &st(0);
+ FPU_st0_tag = FPU_st0_ptr->tag;
+ switch (type_table[(int) instr_index]) {
+ case _NONE_: /* also _REGIc: _REGIn */
+ break;
+ case _REG0_:
+ if (!NOT_EMPTY_0) {
+ stack_underflow();
+ goto FPU_instruction_done;
+ }
+ break;
+ case _REGIi:
+ if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) {
+ stack_underflow_i(FPU_rm);
+ goto FPU_instruction_done;
+ }
+ break;
+ case _REGIp:
+ if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) {
+ stack_underflow_i(FPU_rm);
+ pop();
+ goto FPU_instruction_done;
+ }
+ break;
+ case _REGI_:
+ if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) {
+ stack_underflow();
+ goto FPU_instruction_done;
+ }
+ break;
+ case _PUSH_: /* Only used by the fld st(i) instruction */
+ break;
+ case _null_:
+ Un_impl();
+ goto FPU_instruction_done;
+ default:
+ EXCEPTION(EX_INTERNAL | 0x111);
+ goto FPU_instruction_done;
+ }
+ (*st_instr_table[(int) instr_index]) ();
+ }
+
+FPU_instruction_done:
+
+ ip_offset = FPU_entry_eip;
+ bswapw(code);
+ *(1 + (unsigned short *) &cs_selector) = code & 0x7ff;
+
+#ifdef DEBUG
+ REENTRANT_CHECK(OFF);
+ emu_printall();
+ REENTRANT_CHECK(ON);
+#endif /* DEBUG */
+#ifdef LOOKAHEAD_LIMIT
+if (--lookahead_limit)
+#endif
+ if (FPU_lookahead) {
+ unsigned char next;
+
+ /* (This test should generate no machine code) */
+ while (1) {
+ REENTRANT_CHECK(OFF);
+ next = fubyte((u_char *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ if (((next & 0xf8) == 0xd8) || (next == 0x9b)) { /* fwait */
+ goto do_another_FPU_instruction;
+ } else
+ if (next == 0x66) { /* size prefix */
+ REENTRANT_CHECK(OFF);
+ next = fubyte((u_char *) (FPU_EIP + 1));
+ REENTRANT_CHECK(ON);
+ if ((next & 0xf8) == 0xd8) {
+ FPU_EIP++;
+ goto do_another_FPU_instruction;
+ }
+ }
+ break;
+ }
+ }
+ REENTRANT_CHECK(OFF);
+ return (0); /* --pink-- */
+}
diff --git a/sys/gnu/fpemul/fpu_etc.c b/sys/gnu/fpemul/fpu_etc.c
new file mode 100644
index 000000000000..9993d3bddfd4
--- /dev/null
+++ b/sys/gnu/fpemul/fpu_etc.c
@@ -0,0 +1,175 @@
+/*
+ * fpu_etc.c
+ *
+ * Implement a few FPU instructions.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: fpu_etc.c,v 1.3 1994/06/10 07:44:24 rich Exp $
+ *
+ */
+
+#include "param.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "status_w.h"
+#include "reg_constant.h"
+
+
+static void
+fchs(void)
+{
+ if (NOT_EMPTY_0) {
+ FPU_st0_ptr->sign ^= SIGN_POS ^ SIGN_NEG;
+ status_word &= ~SW_C1;
+ } else
+ stack_underflow();
+}
+
+static void
+fabs(void)
+{
+ if (FPU_st0_tag ^ TW_Empty) {
+ FPU_st0_ptr->sign = SIGN_POS;
+ status_word &= ~SW_C1;
+ } else
+ stack_underflow();
+}
+
+
+static void
+ftst_(void)
+{
+ switch (FPU_st0_tag) {
+ case TW_Zero:
+ setcc(SW_C3);
+ break;
+ case TW_Valid:
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if (FPU_st0_ptr->sign == SIGN_POS)
+ setcc(0);
+ else
+ setcc(SW_C0);
+ break;
+ case TW_NaN:
+ setcc(SW_C0 | SW_C2 | SW_C3); /* Operand is not comparable */
+ EXCEPTION(EX_Invalid);
+ break;
+ case TW_Infinity:
+ if (FPU_st0_ptr->sign == SIGN_POS)
+ setcc(0);
+ else
+ setcc(SW_C0);
+ EXCEPTION(EX_Invalid);
+ break;
+ case TW_Empty:
+ setcc(SW_C0 | SW_C2 | SW_C3);
+ EXCEPTION(EX_StackUnder);
+ break;
+ default:
+ setcc(SW_C0 | SW_C2 | SW_C3); /* Operand is not comparable */
+ EXCEPTION(EX_INTERNAL | 0x14);
+ break;
+ }
+}
+
+static void
+fxam(void)
+{
+ int c = 0;
+ switch (FPU_st0_tag) {
+ case TW_Empty:
+ c = SW_C3 | SW_C0;
+ break;
+ case TW_Zero:
+ c = SW_C3;
+ break;
+ case TW_Valid:
+ /* This will need to be changed if TW_Denormal is ever used. */
+ if (FPU_st0_ptr->exp <= EXP_UNDER)
+ c = SW_C2 | SW_C3; /* Denormal */
+ else
+ c = SW_C3;
+ break;
+ case TW_NaN:
+ c = SW_C0;
+ break;
+ case TW_Infinity:
+ c = SW_C2 | SW_C0;
+ break;
+ }
+ if (FPU_st0_ptr->sign == SIGN_NEG)
+ c |= SW_C1;
+ setcc(c);
+}
+
+static FUNC fp_etc_table[] = {
+ fchs, fabs, Un_impl, Un_impl, ftst_, fxam, Un_impl, Un_impl
+};
+
+void
+fp_etc()
+{
+ (fp_etc_table[FPU_rm]) ();
+}
diff --git a/sys/gnu/fpemul/fpu_proto.h b/sys/gnu/fpemul/fpu_proto.h
new file mode 100644
index 000000000000..463f31b7d7be
--- /dev/null
+++ b/sys/gnu/fpemul/fpu_proto.h
@@ -0,0 +1,115 @@
+/*
+ *
+ * $Id: fpu_proto.h,v 1.2 1994/04/29 21:16:23 gclarkii Exp $
+ *
+ */
+
+
+/* errors.c */
+extern void Un_impl(void);
+extern void emu_printall(void);
+extern void exception(int n);
+extern void real_2op_NaN(FPU_REG * a, FPU_REG * b, FPU_REG * dest);
+extern void arith_invalid(FPU_REG * dest);
+extern void divide_by_zero(int sign, FPU_REG * dest);
+extern void set_precision_flag_up(void);
+extern void set_precision_flag_down(void);
+extern int denormal_operand(void);
+extern void arith_overflow(FPU_REG * dest);
+extern void arith_underflow(FPU_REG * dest);
+extern void stack_overflow(void);
+extern void stack_underflow(void);
+extern void stack_underflow_i(int i);
+extern void stack_underflow_pop(int i);
+/* fpu_arith.c */
+extern void fadd__(void);
+extern void fmul__(void);
+extern void fsub__(void);
+extern void fsubr_(void);
+extern void fdiv__(void);
+extern void fdivr_(void);
+extern void fadd_i(void);
+extern void fmul_i(void);
+extern void fsubri(void);
+extern void fsub_i(void);
+extern void fdivri(void);
+extern void fdiv_i(void);
+extern void faddp_(void);
+extern void fmulp_(void);
+extern void fsubrp(void);
+extern void fsubp_(void);
+extern void fdivrp(void);
+extern void fdivp_(void);
+/* fpu_aux.c */
+extern void fclex(void);
+extern void finit(void);
+extern void finit_(void);
+extern void fstsw_(void);
+extern void fp_nop(void);
+extern void fld_i_(void);
+extern void fxch_i(void);
+extern void ffree_(void);
+extern void ffreep(void);
+extern void fst_i_(void);
+extern void fstp_i(void);
+/* fpu_entry.c */
+extern int math_emulate(struct trapframe * info);
+/* fpu_etc.c */
+extern void fp_etc(void);
+/* fpu_trig.c */
+extern void convert_l2reg(long *arg, FPU_REG * dest);
+extern void trig_a(void);
+extern void trig_b(void);
+/* get_address.c */
+extern void get_address(unsigned char FPU_modrm);
+/* load_store.c */
+extern void load_store_instr(char type);
+/* poly_2xm1.c */
+extern int poly_2xm1(FPU_REG * arg, FPU_REG * result);
+/* poly_atan.c */
+extern void poly_atan(FPU_REG * arg);
+extern void poly_add_1(FPU_REG * src);
+/* poly_l2.c */
+extern void poly_l2(FPU_REG * arg, FPU_REG * result);
+extern int poly_l2p1(FPU_REG * arg, FPU_REG * result);
+/* poly_sin.c */
+extern void poly_sine(FPU_REG * arg, FPU_REG * result);
+/* poly_tan.c */
+extern void poly_tan(FPU_REG * arg, FPU_REG * y_reg);
+/* reg_add_sub.c */
+extern void reg_add(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w);
+extern void reg_sub(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w);
+/* reg_compare.c */
+extern int compare(FPU_REG * b);
+extern int compare_st_data(void);
+extern void fcom_st(void);
+extern void fcompst(void);
+extern void fcompp(void);
+extern void fucom_(void);
+extern void fucomp(void);
+extern void fucompp(void);
+/* reg_constant.c */
+extern void fconst(void);
+/* reg_ld_str.c */
+extern void reg_load_extended(void);
+extern void reg_load_double(void);
+extern void reg_load_single(void);
+extern void reg_load_int64(void);
+extern void reg_load_int32(void);
+extern void reg_load_int16(void);
+extern void reg_load_bcd(void);
+extern int reg_store_extended(void);
+extern int reg_store_double(void);
+extern int reg_store_single(void);
+extern int reg_store_int64(void);
+extern int reg_store_int32(void);
+extern int reg_store_int16(void);
+extern int reg_store_bcd(void);
+extern int round_to_int(FPU_REG * r);
+extern char *fldenv(void);
+extern void frstor(void);
+extern unsigned short tag_word(void);
+extern char *fstenv(void);
+extern void fsave(void);
+/* reg_mul.c */
+extern void reg_mul(FPU_REG * a, FPU_REG * b, FPU_REG * dest, unsigned int control_w);
diff --git a/sys/gnu/fpemul/fpu_system.h b/sys/gnu/fpemul/fpu_system.h
new file mode 100644
index 000000000000..ac5263615b5f
--- /dev/null
+++ b/sys/gnu/fpemul/fpu_system.h
@@ -0,0 +1,97 @@
+/*
+ * fpu_system.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: fpu_system.h,v 1.3 1994/06/10 07:44:25 rich Exp $
+ *
+ */
+
+
+#ifndef _FPU_SYSTEM_H
+#define _FPU_SYSTEM_H
+
+/* system dependent definitions */
+
+/*
+#include <linux/sched.h>
+#include <linux/kernel.h>
+*/
+
+#define I387 (*(union i387_union *)&(((struct pcb *)curproc->p_addr)->pcb_savefpu))
+#define FPU_info (I387.soft.frame)
+
+#define FPU_CS (*(unsigned short *) &(FPU_info->tf_cs))
+#define FPU_DS (*(unsigned short *) &(FPU_info->tf_ds))
+#define FPU_EAX (FPU_info->tf_eax)
+#define FPU_EFLAGS (FPU_info->tf_eflags)
+#define FPU_EIP (FPU_info->tf_eip)
+/*#define FPU_ORIG_EIP (FPU_info->___orig_eip) */
+/*#define FPU_ORIG_EIP (FPU_info->tf_isp)*/
+#define FPU_ORIG_EIP (I387.soft.orig_eip)
+
+#define FPU_lookahead (I387.soft.lookahead)
+#define FPU_entry_eip (I387.soft.entry_eip)
+
+#define status_word (I387.soft.swd)
+#define control_word (I387.soft.cwd)
+#define regs (I387.soft.regs)
+#define top (I387.soft.top)
+
+#define ip_offset (I387.soft.fip)
+#define cs_selector (I387.soft.fcs)
+#define data_operand_offset (I387.soft.foo)
+#define operand_selector (I387.soft.fos)
+
+#endif
diff --git a/sys/gnu/fpemul/fpu_trig.c b/sys/gnu/fpemul/fpu_trig.c
new file mode 100644
index 000000000000..af2bd0553ce9
--- /dev/null
+++ b/sys/gnu/fpemul/fpu_trig.c
@@ -0,0 +1,1367 @@
+/*
+ * fpu_trig.c
+ *
+ * Implementation of the FPU "transcendental" functions.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: fpu_trig.c,v 1.4 1994/06/10 07:44:27 rich Exp $
+ *
+ */
+
+
+#include "param.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "status_w.h"
+#include "reg_constant.h"
+#include "control_w.h"
+
+static int
+trig_arg(FPU_REG * X)
+{
+ FPU_REG tmp, quot;
+ int rv;
+ long long q;
+ int old_cw = control_word;
+
+ control_word &= ~CW_RC;
+ control_word |= RC_CHOP;
+
+ reg_move(X, &quot);
+ reg_div(&quot, &CONST_PI2, &quot, FULL_PRECISION);
+
+ reg_move(&quot, &tmp);
+ round_to_int(&tmp);
+ if (tmp.sigh & 0x80000000)
+ return -1; /* |Arg| is >= 2^63 */
+ tmp.exp = EXP_BIAS + 63;
+ q = *(long long *) &(tmp.sigl);
+ normalize(&tmp);
+
+ reg_sub(&quot, &tmp, X, FULL_PRECISION);
+ rv = q & 7;
+
+ control_word = old_cw;
+ return rv;;
+}
+
+
+/* Convert a long to register */
+void
+convert_l2reg(long *arg, FPU_REG * dest)
+{
+ long num = *arg;
+
+ if (num == 0) {
+ reg_move(&CONST_Z, dest);
+ return;
+ }
+ if (num > 0)
+ dest->sign = SIGN_POS;
+ else {
+ num = -num;
+ dest->sign = SIGN_NEG;
+ }
+
+ dest->sigh = num;
+ dest->sigl = 0;
+ dest->exp = EXP_BIAS + 31;
+ dest->tag = TW_Valid;
+ normalize(dest);
+}
+
+
+static void
+single_arg_error(void)
+{
+ switch (FPU_st0_tag) {
+ case TW_NaN:
+ if (!(FPU_st0_ptr->sigh & 0x40000000)) { /* Signaling ? */
+ EXCEPTION(EX_Invalid);
+ /* Convert to a QNaN */
+ FPU_st0_ptr->sigh |= 0x40000000;
+ }
+ break; /* return with a NaN in st(0) */
+ case TW_Empty:
+ stack_underflow(); /* Puts a QNaN in st(0) */
+ break;
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL | 0x0112);
+#endif /* PARANOID */
+ }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+static void
+f2xm1(void)
+{
+ switch (FPU_st0_tag) {
+ case TW_Valid:
+ {
+ FPU_REG rv, tmp;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if (FPU_st0_ptr->sign == SIGN_POS) {
+ /* poly_2xm1(x) requires 0 < x < 1. */
+ if (poly_2xm1(FPU_st0_ptr, &rv))
+ return; /* error */
+ reg_mul(&rv, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
+ } else {
+/* **** Should change poly_2xm1() to at least handle numbers near 0 */
+ /* poly_2xm1(x) doesn't handle negative
+ * numbers. */
+ /* So we compute (poly_2xm1(x+1)-1)/2, for -1
+ * < x < 0 */
+ reg_add(FPU_st0_ptr, &CONST_1, &tmp, FULL_PRECISION);
+ poly_2xm1(&tmp, &rv);
+ reg_mul(&rv, &tmp, &tmp, FULL_PRECISION);
+ reg_sub(&tmp, &CONST_1, FPU_st0_ptr, FULL_PRECISION);
+ FPU_st0_ptr->exp--;
+ if (FPU_st0_ptr->exp <= EXP_UNDER)
+ arith_underflow(FPU_st0_ptr);
+ }
+ return;
+ }
+ case TW_Zero:
+ return;
+ case TW_Infinity:
+ if (FPU_st0_ptr->sign == SIGN_NEG) {
+ /* -infinity gives -1 (p16-10) */
+ reg_move(&CONST_1, FPU_st0_ptr);
+ FPU_st0_ptr->sign = SIGN_NEG;
+ }
+ return;
+ default:
+ single_arg_error();
+ }
+}
+
+static void
+fptan(void)
+{
+ FPU_REG *st_new_ptr;
+ int q;
+ char arg_sign = FPU_st0_ptr->sign;
+
+ if (STACK_OVERFLOW) {
+ stack_overflow();
+ return;
+ }
+ switch (FPU_st0_tag) {
+ case TW_Valid:
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ FPU_st0_ptr->sign = SIGN_POS;
+ if ((q = trig_arg(FPU_st0_ptr)) != -1) {
+ if (q & 1)
+ reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
+
+ poly_tan(FPU_st0_ptr, FPU_st0_ptr);
+
+ FPU_st0_ptr->sign = (q & 1) ^ arg_sign;
+
+ if (FPU_st0_ptr->exp <= EXP_UNDER)
+ arith_underflow(FPU_st0_ptr);
+
+ push();
+ reg_move(&CONST_1, FPU_st0_ptr);
+ setcc(0);
+ } else {
+ /* Operand is out of range */
+ setcc(SW_C2);
+ FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
+ return;
+ }
+ break;
+ case TW_Infinity:
+ /* Operand is out of range */
+ setcc(SW_C2);
+ FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
+ return;
+ case TW_Zero:
+ push();
+ reg_move(&CONST_1, FPU_st0_ptr);
+ setcc(0);
+ break;
+ default:
+ single_arg_error();
+ break;
+ }
+}
+
+
+static void
+fxtract(void)
+{
+ FPU_REG *st_new_ptr;
+ register FPU_REG *st1_ptr = FPU_st0_ptr; /* anticipate */
+
+ if (STACK_OVERFLOW) {
+ stack_overflow();
+ return;
+ }
+ if (!(FPU_st0_tag ^ TW_Valid)) {
+ long e;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ push();
+ reg_move(st1_ptr, FPU_st0_ptr);
+ FPU_st0_ptr->exp = EXP_BIAS;
+ e = st1_ptr->exp - EXP_BIAS;
+ convert_l2reg(&e, st1_ptr);
+ return;
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ char sign = FPU_st0_ptr->sign;
+ divide_by_zero(SIGN_NEG, FPU_st0_ptr);
+ push();
+ reg_move(&CONST_Z, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ return;
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ char sign = FPU_st0_ptr->sign;
+ FPU_st0_ptr->sign = SIGN_POS;
+ push();
+ reg_move(&CONST_INF, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ return;
+ } else
+ if (FPU_st0_tag == TW_NaN) {
+ if (!(FPU_st0_ptr->sigh & 0x40000000)) { /* Signaling ? */
+ EXCEPTION(EX_Invalid);
+ /* Convert to a QNaN */
+ FPU_st0_ptr->sigh |= 0x40000000;
+ }
+ push();
+ reg_move(st1_ptr, FPU_st0_ptr);
+ return;
+ } else
+ if (FPU_st0_tag == TW_Empty) {
+ /* Is this the correct
+ * behaviour? */
+ if (control_word & EX_Invalid) {
+ stack_underflow();
+ push();
+ stack_underflow();
+ } else
+ EXCEPTION(EX_StackUnder);
+ }
+#ifdef PARANOID
+ else
+ EXCEPTION(EX_INTERNAL | 0x119);
+#endif /* PARANOID */
+}
+
+
+static void
+fdecstp(void)
+{
+ top--; /* FPU_st0_ptr will be fixed in math_emulate()
+ * before the next instr */
+}
+
+static void
+fincstp(void)
+{
+ top++; /* FPU_st0_ptr will be fixed in math_emulate()
+ * before the next instr */
+}
+
+
+static void
+fsqrt_(void)
+{
+ if (!(FPU_st0_tag ^ TW_Valid)) {
+ int expon;
+
+ if (FPU_st0_ptr->sign == SIGN_NEG) {
+ arith_invalid(FPU_st0_ptr); /* sqrt(negative) is
+ * invalid */
+ return;
+ }
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ expon = FPU_st0_ptr->exp - EXP_BIAS;
+ FPU_st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0
+ * .. 4.0) */
+
+ wm_sqrt(FPU_st0_ptr, control_word); /* Do the computation */
+
+ FPU_st0_ptr->exp += expon >> 1;
+ FPU_st0_ptr->sign = SIGN_POS;
+ } else
+ if (FPU_st0_tag == TW_Zero)
+ return;
+ else
+ if (FPU_st0_tag == TW_Infinity) {
+ if (FPU_st0_ptr->sign == SIGN_NEG)
+ arith_invalid(FPU_st0_ptr); /* sqrt(-Infinity) is
+ * invalid */
+ return;
+ } else {
+ single_arg_error();
+ return;
+ }
+
+}
+
+
+static void
+frndint_(void)
+{
+ if (!(FPU_st0_tag ^ TW_Valid)) {
+ if (FPU_st0_ptr->exp > EXP_BIAS + 63)
+ return;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ round_to_int(FPU_st0_ptr); /* Fortunately, this can't
+ * overflow to 2^64 */
+ FPU_st0_ptr->exp = EXP_BIAS + 63;
+ normalize(FPU_st0_ptr);
+ return;
+ } else
+ if ((FPU_st0_tag == TW_Zero) || (FPU_st0_tag == TW_Infinity))
+ return;
+ else
+ single_arg_error();
+}
+
+
+static void
+fsin(void)
+{
+ char arg_sign = FPU_st0_ptr->sign;
+
+ if (FPU_st0_tag == TW_Valid) {
+ int q;
+ FPU_st0_ptr->sign = SIGN_POS;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if ((q = trig_arg(FPU_st0_ptr)) != -1) {
+ FPU_REG rv;
+
+ if (q & 1)
+ reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
+
+ poly_sine(FPU_st0_ptr, &rv);
+
+ setcc(0);
+ if (q & 2)
+ rv.sign ^= SIGN_POS ^ SIGN_NEG;
+ rv.sign ^= arg_sign;
+ reg_move(&rv, FPU_st0_ptr);
+
+ if (FPU_st0_ptr->exp <= EXP_UNDER)
+ arith_underflow(FPU_st0_ptr);
+
+ set_precision_flag_up(); /* We do not really know
+ * if up or down */
+
+ return;
+ } else {
+ /* Operand is out of range */
+ setcc(SW_C2);
+ FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
+ return;
+ }
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ setcc(0);
+ return;
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ /* Operand is out of range */
+ setcc(SW_C2);
+ FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
+ return;
+ } else
+ single_arg_error();
+}
+
+
+static int
+f_cos(FPU_REG * arg)
+{
+ char arg_sign = arg->sign;
+
+ if (arg->tag == TW_Valid) {
+ int q;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return 1;
+#endif /* DENORM_OPERAND */
+
+ arg->sign = SIGN_POS;
+ if ((q = trig_arg(arg)) != -1) {
+ FPU_REG rv;
+
+ if (!(q & 1))
+ reg_sub(&CONST_1, arg, arg, FULL_PRECISION);
+
+ poly_sine(arg, &rv);
+
+ setcc(0);
+ if ((q + 1) & 2)
+ rv.sign ^= SIGN_POS ^ SIGN_NEG;
+ reg_move(&rv, arg);
+
+ set_precision_flag_up(); /* We do not really know
+ * if up or down */
+
+ return 0;
+ } else {
+ /* Operand is out of range */
+ setcc(SW_C2);
+ arg->sign = arg_sign; /* restore st(0) */
+ return 1;
+ }
+ } else
+ if (arg->tag == TW_Zero) {
+ reg_move(&CONST_1, arg);
+ setcc(0);
+ return 0;
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ /* Operand is out of range */
+ setcc(SW_C2);
+ arg->sign = arg_sign; /* restore st(0) */
+ return 1;
+ } else {
+ single_arg_error(); /* requires arg ==
+ * &st(0) */
+ return 1;
+ }
+}
+
+
+static void
+fcos(void)
+{
+ f_cos(FPU_st0_ptr);
+}
+
+
+static void
+fsincos(void)
+{
+ FPU_REG *st_new_ptr;
+ FPU_REG arg;
+
+ if (STACK_OVERFLOW) {
+ stack_overflow();
+ return;
+ }
+ reg_move(FPU_st0_ptr, &arg);
+ if (!f_cos(&arg)) {
+ fsin();
+ push();
+ reg_move(&arg, FPU_st0_ptr);
+ }
+}
+
+
+/*---------------------------------------------------------------------------*/
+/* The following all require two arguments: st(0) and st(1) */
+
+/* remainder of st(0) / st(1) */
+/* Assumes that st(0) and st(1) are both TW_Valid */
+static void
+fprem_kernel(int round)
+{
+ FPU_REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) {
+ FPU_REG tmp;
+ int old_cw = control_word;
+ int expdif = FPU_st0_ptr->exp - (st1_ptr)->exp;
+
+#ifdef DENORM_OPERAND
+ if (((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ control_word &= ~CW_RC;
+ control_word |= round;
+
+ if (expdif < 64) {
+ /* This should be the most common case */
+ long long q;
+ int c = 0;
+
+ reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION);
+
+ round_to_int(&tmp); /* Fortunately, this can't
+ * overflow to 2^64 */
+ tmp.exp = EXP_BIAS + 63;
+ q = *(long long *) &(tmp.sigl);
+ normalize(&tmp);
+
+ reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION);
+ reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION);
+
+ if (q & 4)
+ c |= SW_C3;
+ if (q & 2)
+ c |= SW_C1;
+ if (q & 1)
+ c |= SW_C0;
+
+ setcc(c);
+ } else {
+ /* There is a large exponent difference ( >= 64 ) */
+ int N_exp;
+
+ reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION);
+ /* N is 'a number between 32 and 63' (p26-113) */
+ N_exp = (tmp.exp & 31) + 32;
+ tmp.exp = EXP_BIAS + N_exp;
+
+ round_to_int(&tmp); /* Fortunately, this can't
+ * overflow to 2^64 */
+ tmp.exp = EXP_BIAS + 63;
+ normalize(&tmp);
+
+ tmp.exp = EXP_BIAS + expdif - N_exp;
+
+ reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION);
+ reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION);
+
+ setcc(SW_C2);
+ }
+ control_word = old_cw;
+
+ if (FPU_st0_ptr->exp <= EXP_UNDER)
+ arith_underflow(FPU_st0_ptr);
+ return;
+ } else
+ if ((FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty)) {
+ stack_underflow();
+ return;
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ if (st1_tag == TW_Valid) {
+
+#ifdef DENORM_OPERAND
+ if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ setcc(0);
+ return;
+ } else
+ if (st1_tag == TW_Zero) {
+ arith_invalid(FPU_st0_ptr);
+ return;
+ }
+ /* fprem(?,0) always invalid */
+ else
+ if (st1_tag == TW_Infinity) {
+ setcc(0);
+ return;
+ }
+ } else
+ if (FPU_st0_tag == TW_Valid) {
+ if (st1_tag == TW_Zero) {
+ arith_invalid(FPU_st0_ptr); /* fprem(Valid,Zero) is
+ * invalid */
+ return;
+ } else
+ if (st1_tag != TW_NaN) {
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if (st1_tag == TW_Infinity) {
+ /* fprem(Valid,
+ * Infinity)
+ * is o.k. */
+ setcc(0);
+ return;
+ }
+ }
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ if (st1_tag != TW_NaN) {
+ arith_invalid(FPU_st0_ptr); /* fprem(Infinity,?) is
+ * invalid */
+ return;
+ }
+ }
+ /* One of the registers must contain a NaN is we got here. */
+
+#ifdef PARANOID
+ if ((FPU_st0_tag != TW_NaN) && (st1_tag != TW_NaN))
+ EXCEPTION(EX_INTERNAL | 0x118);
+#endif /* PARANOID */
+
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr);
+
+}
+
+
+/* ST(1) <- ST(1) * log ST; pop ST */
+static void
+fyl2x(void)
+{
+ FPU_REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) {
+ if (FPU_st0_ptr->sign == SIGN_POS) {
+ int saved_control, saved_status;
+
+#ifdef DENORM_OPERAND
+ if (((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ /* We use the general purpose arithmetic, so we need
+ * to save these. */
+ saved_status = status_word;
+ saved_control = control_word;
+ control_word = FULL_PRECISION;
+
+ poly_l2(FPU_st0_ptr, FPU_st0_ptr);
+
+ /* Enough of the basic arithmetic is done now */
+ control_word = saved_control;
+ status_word = saved_status;
+
+ /* Let the multiply set the flags */
+ reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION);
+
+ pop();
+ FPU_st0_ptr = &st(0);
+ } else {
+ /* negative */
+ pop();
+ FPU_st0_ptr = &st(0);
+ arith_invalid(FPU_st0_ptr); /* st(0) cannot be
+ * negative */
+ return;
+ }
+ } else
+ if ((FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty)) {
+ stack_underflow_pop(1);
+ return;
+ } else
+ if ((FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN)) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ } else
+ if ((FPU_st0_tag <= TW_Zero) && (st1_tag <= TW_Zero)) {
+ /* one of the args is zero, the other
+ * valid, or both zero */
+ if (FPU_st0_tag == TW_Zero) {
+ pop();
+ FPU_st0_ptr = &st(0);
+ if (FPU_st0_ptr->tag == TW_Zero)
+ arith_invalid(FPU_st0_ptr); /* Both args zero is
+ * invalid */
+#ifdef PECULIAR_486
+ /* This case is not
+ * specifically covered in the
+ * manual, but divide-by-zero
+ * would seem to be the best
+ * response. However, a real
+ * 80486 does it this way... */
+ else
+ if (FPU_st0_ptr->tag == TW_Infinity) {
+ reg_move(&CONST_INF, FPU_st0_ptr);
+ return;
+ }
+#endif /* PECULIAR_486 */
+ else
+ divide_by_zero(st1_ptr->sign ^ SIGN_NEG ^ SIGN_POS, FPU_st0_ptr);
+ return;
+ } else {
+ /* st(1) contains zero, st(0)
+ * valid <> 0 */
+ /* Zero is the valid answer */
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+ if (FPU_st0_ptr->sign == SIGN_NEG) {
+ pop();
+ FPU_st0_ptr = &st(0);
+ arith_invalid(FPU_st0_ptr); /* log(negative) */
+ return;
+ }
+ if (FPU_st0_ptr->exp < EXP_BIAS)
+ sign ^= SIGN_NEG ^ SIGN_POS;
+ pop();
+ FPU_st0_ptr = &st(0);
+ reg_move(&CONST_Z, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ return;
+ }
+ }
+ /* One or both arg must be an infinity */
+ else
+ if (FPU_st0_tag == TW_Infinity) {
+ if ((FPU_st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero)) {
+ pop();
+ FPU_st0_ptr = &st(0);
+ arith_invalid(FPU_st0_ptr); /* log(-infinity) or
+ * 0*log(infinity) */
+ return;
+ } else {
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ pop();
+ FPU_st0_ptr = &st(0);
+ reg_move(&CONST_INF, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ return;
+ }
+ }
+ /* st(1) must be infinity here */
+ else
+ if ((FPU_st0_tag == TW_Valid) && (FPU_st0_ptr->sign == SIGN_POS)) {
+ if (FPU_st0_ptr->exp >= EXP_BIAS) {
+ if ((FPU_st0_ptr->exp == EXP_BIAS) &&
+ (FPU_st0_ptr->sigh == 0x80000000) &&
+ (FPU_st0_ptr->sigl == 0)) {
+ /* st(0
+ * )
+ * hold
+ * s
+ * 1.0 */
+ pop();
+ FPU_st0_ptr = &st(0);
+ arith_invalid(FPU_st0_ptr); /* infinity*log(1) */
+ return;
+ }
+ /* st(0) is
+ * positive
+ * and > 1.0 */
+ pop();
+ } else {
+ /* st(0) is
+ * positive
+ * and < 1.0 */
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ st1_ptr->sign ^= SIGN_NEG;
+ pop();
+ }
+ return;
+ } else {
+ /* st(0) must be zero
+ * or negative */
+ if (FPU_st0_ptr->tag == TW_Zero) {
+ pop();
+ FPU_st0_ptr = st1_ptr;
+ st1_ptr->sign ^= SIGN_NEG ^ SIGN_POS;
+ /* This should
+ * be invalid,
+ * but a real
+ * 80486 is
+ * happy with
+ * it. */
+#ifndef PECULIAR_486
+ divide_by_zero(st1_ptr->sign, FPU_st0_ptr);
+#endif /* PECULIAR_486 */
+ } else {
+ pop();
+ FPU_st0_ptr = st1_ptr;
+ arith_invalid(FPU_st0_ptr); /* log(negative) */
+ }
+ return;
+ }
+}
+
+
+static void
+fpatan(void)
+{
+ FPU_REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) {
+ int saved_control, saved_status;
+ FPU_REG sum;
+ int quadrant = st1_ptr->sign | ((FPU_st0_ptr->sign) << 1);
+
+#ifdef DENORM_OPERAND
+ if (((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ /* We use the general purpose arithmetic so we need to save
+ * these. */
+ saved_status = status_word;
+ saved_control = control_word;
+ control_word = FULL_PRECISION;
+
+ st1_ptr->sign = FPU_st0_ptr->sign = SIGN_POS;
+ if (compare(st1_ptr) == COMP_A_lt_B) {
+ quadrant |= 4;
+ reg_div(FPU_st0_ptr, st1_ptr, &sum, FULL_PRECISION);
+ } else
+ reg_div(st1_ptr, FPU_st0_ptr, &sum, FULL_PRECISION);
+
+ poly_atan(&sum);
+
+ if (quadrant & 4) {
+ reg_sub(&CONST_PI2, &sum, &sum, FULL_PRECISION);
+ }
+ if (quadrant & 2) {
+ reg_sub(&CONST_PI, &sum, &sum, FULL_PRECISION);
+ }
+ if (quadrant & 1)
+ sum.sign ^= SIGN_POS ^ SIGN_NEG;
+
+ /* All of the basic arithmetic is done now */
+ control_word = saved_control;
+ status_word = saved_status;
+
+ reg_move(&sum, st1_ptr);
+ } else
+ if ((FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty)) {
+ stack_underflow_pop(1);
+ return;
+ } else
+ if ((FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN)) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ } else
+ if ((FPU_st0_tag == TW_Infinity) || (st1_tag == TW_Infinity)) {
+ char sign = st1_ptr->sign;
+ if (FPU_st0_tag == TW_Infinity) {
+ if (st1_tag == TW_Infinity) {
+ if (FPU_st0_ptr->sign == SIGN_POS) {
+ reg_move(&CONST_PI4, st1_ptr);
+ } else
+ reg_add(&CONST_PI4, &CONST_PI2, st1_ptr, FULL_PRECISION);
+ } else {
+
+#ifdef DENORM_OPERAND
+ if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if (FPU_st0_ptr->sign == SIGN_POS) {
+ reg_move(&CONST_Z, st1_ptr);
+ pop();
+ return;
+ } else
+ reg_move(&CONST_PI, st1_ptr);
+ }
+ } else {
+ /* st(1) is infinity, st(0)
+ * not infinity */
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ reg_move(&CONST_PI2, st1_ptr);
+ }
+ st1_ptr->sign = sign;
+ } else
+ if (st1_tag == TW_Zero) {
+ /* st(0) must be valid or zero */
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if (FPU_st0_ptr->sign == SIGN_POS) {
+ reg_move(&CONST_Z, st1_ptr);
+ pop();
+ return;
+ } else
+ reg_move(&CONST_PI, st1_ptr);
+ st1_ptr->sign = sign;
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ /* st(1) must be
+ * TW_Valid here */
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ reg_move(&CONST_PI2, st1_ptr);
+ st1_ptr->sign = sign;
+ }
+#ifdef PARANOID
+ else
+ EXCEPTION(EX_INTERNAL | 0x220);
+#endif /* PARANOID */
+
+ pop();
+ set_precision_flag_up();/* We do not really know if up or down */
+}
+
+
+static void
+fprem(void)
+{
+ fprem_kernel(RC_CHOP);
+}
+
+
+static void
+fprem1(void)
+{
+ fprem_kernel(RC_RND);
+}
+
+
+static void
+fyl2xp1(void)
+{
+ FPU_REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) {
+ int saved_control, saved_status;
+
+#ifdef DENORM_OPERAND
+ if (((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ /* We use the general purpose arithmetic so we need to save
+ * these. */
+ saved_status = status_word;
+ saved_control = control_word;
+ control_word = FULL_PRECISION;
+
+ if (poly_l2p1(FPU_st0_ptr, FPU_st0_ptr)) {
+ arith_invalid(st1_ptr); /* poly_l2p1() returned
+ * invalid */
+ pop();
+ return;
+ }
+ /* Enough of the basic arithmetic is done now */
+ control_word = saved_control;
+ status_word = saved_status;
+
+ /* Let the multiply set the flags */
+ reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION);
+
+ pop();
+ } else
+ if ((FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty)) {
+ stack_underflow_pop(1);
+ return;
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ if (st1_tag <= TW_Zero) {
+
+#ifdef DENORM_OPERAND
+ if ((st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) &&
+ (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ st1_ptr->sign ^= FPU_st0_ptr->sign;
+ reg_move(FPU_st0_ptr, st1_ptr);
+ } else
+ if (st1_tag == TW_Infinity) {
+ arith_invalid(st1_ptr); /* Infinity*log(1) */
+ pop();
+ return;
+ } else
+ if (st1_tag == TW_NaN) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ }
+#ifdef PARANOID
+ else {
+ EXCEPTION(EX_INTERNAL | 0x116);
+ return;
+ }
+#endif /* PARANOID */
+ pop();
+ return;
+ } else
+ if (FPU_st0_tag == TW_Valid) {
+ if (st1_tag == TW_Zero) {
+ if (FPU_st0_ptr->sign == SIGN_NEG) {
+ if (FPU_st0_ptr->exp >= EXP_BIAS) {
+ /* st(0) holds
+ * <= -1.0 */
+ arith_invalid(st1_ptr); /* infinity*log(1) */
+ pop();
+ return;
+ }
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+ st1_ptr->sign ^= SIGN_POS ^ SIGN_NEG;
+ pop();
+ return;
+ }
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+ pop();
+ return;
+ }
+ if (st1_tag == TW_Infinity) {
+ if (FPU_st0_ptr->sign == SIGN_NEG) {
+ if ((FPU_st0_ptr->exp >= EXP_BIAS) &&
+ !((FPU_st0_ptr->sigh == 0x80000000) &&
+ (FPU_st0_ptr->sigl == 0))) {
+ /* st(0) holds
+ * < -1.0 */
+ arith_invalid(st1_ptr);
+ pop();
+ return;
+ }
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+ st1_ptr->sign ^= SIGN_POS ^ SIGN_NEG;
+ pop();
+ return;
+ }
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+ pop();
+ return;
+ }
+ if (st1_tag == TW_NaN) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ }
+ } else
+ if (FPU_st0_tag == TW_NaN) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ if (st1_tag == TW_NaN) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ } else
+ if ((FPU_st0_ptr->sign == SIGN_NEG) ||
+ (st1_tag == TW_Zero)) {
+ arith_invalid(st1_ptr); /* log(infinity) */
+ pop();
+ return;
+ }
+ /* st(1) must be valid
+ * here. */
+
+#ifdef DENORM_OPERAND
+ if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ /* The Manual says
+ * that log(Infinity)
+ * is invalid, but a
+ * real 80486 sensibly
+ * says that it is
+ * o.k. */
+ {
+ char sign = st1_ptr->sign;
+ reg_move(&CONST_INF, st1_ptr);
+ st1_ptr->sign = sign;
+ }
+ pop();
+ return;
+ }
+#ifdef PARANOID
+ else {
+ EXCEPTION(EX_INTERNAL | 0x117);
+ }
+#endif /* PARANOID */
+}
+
+
+static void
+emu_fscale(void)
+{
+ FPU_REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+ int old_cw = control_word;
+
+ if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) {
+ long scale;
+ FPU_REG tmp;
+
+#ifdef DENORM_OPERAND
+ if (((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if (st1_ptr->exp > EXP_BIAS + 30) {
+ /* 2^31 is far too large, would require 2^(2^30) or
+ * 2^(-2^30) */
+ char sign;
+
+ if (st1_ptr->sign == SIGN_POS) {
+ EXCEPTION(EX_Overflow);
+ sign = FPU_st0_ptr->sign;
+ reg_move(&CONST_INF, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ } else {
+ EXCEPTION(EX_Underflow);
+ sign = FPU_st0_ptr->sign;
+ reg_move(&CONST_Z, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ }
+ return;
+ }
+ control_word &= ~CW_RC;
+ control_word |= RC_CHOP;
+ reg_move(st1_ptr, &tmp);
+ round_to_int(&tmp); /* This can never overflow here */
+ control_word = old_cw;
+ scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl;
+ scale += FPU_st0_ptr->exp;
+ FPU_st0_ptr->exp = scale;
+
+ /* Use round_reg() to properly detect under/overflow etc */
+ round_reg(FPU_st0_ptr, 0, control_word);
+
+ return;
+ } else
+ if (FPU_st0_tag == TW_Valid) {
+ if (st1_tag == TW_Zero) {
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ return;
+ }
+ if (st1_tag == TW_Infinity) {
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ if (sign == SIGN_POS) {
+ reg_move(&CONST_INF, FPU_st0_ptr);
+ } else
+ reg_move(&CONST_Z, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ return;
+ }
+ if (st1_tag == TW_NaN) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr);
+ return;
+ }
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ if (st1_tag == TW_Valid) {
+
+#ifdef DENORM_OPERAND
+ if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ return;
+ } else
+ if (st1_tag == TW_Zero) {
+ return;
+ } else
+ if (st1_tag == TW_Infinity) {
+ if (st1_ptr->sign == SIGN_NEG)
+ return;
+ else {
+ arith_invalid(FPU_st0_ptr); /* Zero scaled by
+ * +Infinity */
+ return;
+ }
+ } else
+ if (st1_tag == TW_NaN) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr);
+ return;
+ }
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ if (st1_tag == TW_Valid) {
+
+#ifdef DENORM_OPERAND
+ if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return;
+#endif /* DENORM_OPERAND */
+
+ return;
+ }
+ if (((st1_tag == TW_Infinity) && (st1_ptr->sign == SIGN_POS))
+ || (st1_tag == TW_Zero))
+ return;
+ else
+ if (st1_tag == TW_Infinity) {
+ arith_invalid(FPU_st0_ptr); /* Infinity scaled by
+ * -Infinity */
+ return;
+ } else
+ if (st1_tag == TW_NaN) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr);
+ return;
+ }
+ } else
+ if (FPU_st0_tag == TW_NaN) {
+ if (st1_tag != TW_Empty) {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr);
+ return;
+ }
+ }
+#ifdef PARANOID
+ if (!((FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty))) {
+ EXCEPTION(EX_INTERNAL | 0x115);
+ return;
+ }
+#endif
+
+ /* At least one of st(0), st(1) must be empty */
+ stack_underflow();
+
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+static FUNC trig_table_a[] = {
+ f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp
+};
+
+void
+trig_a(void)
+{
+ (trig_table_a[FPU_rm]) ();
+}
+
+
+static FUNC trig_table_b[] =
+{
+ fprem, fyl2xp1, fsqrt_, fsincos, frndint_, emu_fscale, fsin, fcos
+};
+
+void
+trig_b(void)
+{
+ (trig_table_b[FPU_rm]) ();
+}
diff --git a/sys/gnu/fpemul/get_address.c b/sys/gnu/fpemul/get_address.c
new file mode 100644
index 000000000000..957ea4e2eeba
--- /dev/null
+++ b/sys/gnu/fpemul/get_address.c
@@ -0,0 +1,203 @@
+/*
+ * get_address.c
+ *
+ * Get the effective address from an FPU instruction.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: get_address.c,v 1.3 1994/06/10 07:44:29 rich Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+
+#include "param.h"
+#include "proc.h"
+#include "systm.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+#include "machine/reg.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+
+static int reg_offset[] = {
+tEAX, tECX, tEDX, tEBX, tESP, tEBP, tESI, tEDI};
+#define REG_(x) (*(((int*)FPU_info) + reg_offset[(x)]))
+
+void *FPU_data_address;
+
+
+/* Decode the SIB byte. This function assumes mod != 0 */
+static void *
+sib(int mod)
+{
+ unsigned char ss, index, base;
+ long offset;
+
+ REENTRANT_CHECK(OFF);
+ base = fubyte((char *) FPU_EIP); /* The SIB byte */
+ REENTRANT_CHECK(ON);
+ FPU_EIP++;
+ ss = base >> 6;
+ index = (base >> 3) & 7;
+ base &= 7;
+
+ if ((mod == 0) && (base == 5))
+ offset = 0; /* No base register */
+ else
+ offset = REG_(base);
+
+ if (index == 4) {
+ /* No index register */
+ /* A non-zero ss is illegal */
+ if (ss)
+ EXCEPTION(EX_Invalid);
+ } else {
+ offset += (REG_(index)) << ss;
+ }
+
+ if (mod == 1) {
+ /* 8 bit signed displacement */
+ REENTRANT_CHECK(OFF);
+ offset += (signed char) fubyte((char *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ FPU_EIP++;
+ } else
+ if (mod == 2 || base == 5) { /* The second condition also
+ * has mod==0 */
+ /* 32 bit displacment */
+ REENTRANT_CHECK(OFF);
+ offset += (signed) fuword((unsigned long *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ FPU_EIP += 4;
+ }
+ return (void *) offset;
+}
+
+
+/*
+ MOD R/M byte: MOD == 3 has a special use for the FPU
+ SIB byte used iff R/M = 100b
+
+ 7 6 5 4 3 2 1 0
+ ..... ......... .........
+ MOD OPCODE(2) R/M
+
+
+ SIB byte
+
+ 7 6 5 4 3 2 1 0
+ ..... ......... .........
+ SS INDEX BASE
+
+*/
+
+void
+get_address(unsigned char FPU_modrm)
+{
+ unsigned char mod;
+ long *cpu_reg_ptr;
+ int offset = 0; /* Initialized just to stop compiler warnings. */
+
+ mod = (FPU_modrm >> 6) & 3;
+
+ if (FPU_rm == 4 && mod != 3) {
+ FPU_data_address = sib(mod);
+ return;
+ }
+ cpu_reg_ptr = (long *) &REG_(FPU_rm);
+ switch (mod) {
+ case 0:
+ if (FPU_rm == 5) {
+ /* Special case: disp32 */
+ REENTRANT_CHECK(OFF);
+ offset = fuword((unsigned long *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ FPU_EIP += 4;
+ FPU_data_address = (void *) offset;
+ return;
+ } else {
+ FPU_data_address = (void *) *cpu_reg_ptr; /* Just return the
+ * contents of the cpu
+ * register */
+ return;
+ }
+ case 1:
+ /* 8 bit signed displacement */
+ REENTRANT_CHECK(OFF);
+ offset = (signed char) fubyte((char *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ FPU_EIP++;
+ break;
+ case 2:
+ /* 32 bit displacement */
+ REENTRANT_CHECK(OFF);
+ offset = (signed) fuword((unsigned long *) FPU_EIP);
+ REENTRANT_CHECK(ON);
+ FPU_EIP += 4;
+ break;
+ case 3:
+ /* Not legal for the FPU */
+ EXCEPTION(EX_Invalid);
+ }
+
+ FPU_data_address = offset + (char *) *cpu_reg_ptr;
+}
diff --git a/sys/gnu/fpemul/load_store.c b/sys/gnu/fpemul/load_store.c
new file mode 100644
index 000000000000..a86a2fd3ec82
--- /dev/null
+++ b/sys/gnu/fpemul/load_store.c
@@ -0,0 +1,269 @@
+/*
+ * load_store.c
+ *
+ * This file contains most of the code to interpret the FPU instructions
+ * which load and store from user memory.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: load_store.c,v 1.3 1994/06/10 07:44:30 rich Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+
+#include "param.h"
+#include "proc.h"
+#include "systm.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "status_w.h"
+
+
+#define _NONE_ 0 /* FPU_st0_ptr etc not needed */
+#define _REG0_ 1 /* Will be storing st(0) */
+#define _PUSH_ 3 /* Need to check for space to push onto stack */
+#define _null_ 4 /* Function illegal or not implemented */
+
+#define pop_0() { pop_ptr->tag = TW_Empty; top++; }
+
+
+static unsigned char type_table[32] = {
+ _PUSH_, _PUSH_, _PUSH_, _PUSH_,
+ _null_, _null_, _null_, _null_,
+ _REG0_, _REG0_, _REG0_, _REG0_,
+ _REG0_, _REG0_, _REG0_, _REG0_,
+ _NONE_, _null_, _NONE_, _PUSH_,
+ _NONE_, _PUSH_, _null_, _PUSH_,
+ _NONE_, _null_, _NONE_, _REG0_,
+ _NONE_, _REG0_, _NONE_, _REG0_
+};
+
+void
+load_store_instr(char type)
+{
+ FPU_REG *pop_ptr; /* We need a version of FPU_st0_ptr which
+ * won't change. */
+
+ pop_ptr = NULL; /* Initialized just to stop compiler warnings. */
+
+
+ switch (type_table[(int) (unsigned) type]) {
+ case _NONE_:
+ break;
+ case _REG0_:
+ pop_ptr = &st(0); /* Some of these instructions pop
+ * after storing */
+
+ FPU_st0_ptr = pop_ptr; /* Set the global variables. */
+ FPU_st0_tag = FPU_st0_ptr->tag;
+ break;
+ case _PUSH_:
+ {
+ pop_ptr = &st(-1);
+ if (pop_ptr->tag != TW_Empty) {
+ stack_overflow();
+ return;
+ }
+ top--;
+ }
+ break;
+ case _null_:
+ return Un_impl();
+#ifdef PARANOID
+ default:
+ return EXCEPTION(EX_INTERNAL);
+#endif /* PARANOID */
+ }
+
+ switch (type) {
+ case 000: /* fld m32real */
+ reg_load_single();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ reg_move(&FPU_loaded_data, pop_ptr);
+ break;
+ case 001: /* fild m32int */
+ reg_load_int32();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ reg_move(&FPU_loaded_data, pop_ptr);
+ break;
+ case 002: /* fld m64real */
+ reg_load_double();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ reg_move(&FPU_loaded_data, pop_ptr);
+ break;
+ case 003: /* fild m16int */
+ reg_load_int16();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ reg_move(&FPU_loaded_data, pop_ptr);
+ break;
+ case 010: /* fst m32real */
+ reg_store_single();
+ break;
+ case 011: /* fist m32int */
+ reg_store_int32();
+ break;
+ case 012: /* fst m64real */
+ reg_store_double();
+ break;
+ case 013: /* fist m16int */
+ reg_store_int16();
+ break;
+ case 014: /* fstp m32real */
+ if (reg_store_single())
+ pop_0();/* pop only if the number was actually stored
+ * (see the 80486 manual p16-28) */
+ break;
+ case 015: /* fistp m32int */
+ if (reg_store_int32())
+ pop_0();/* pop only if the number was actually stored
+ * (see the 80486 manual p16-28) */
+ break;
+ case 016: /* fstp m64real */
+ if (reg_store_double())
+ pop_0();/* pop only if the number was actually stored
+ * (see the 80486 manual p16-28) */
+ break;
+ case 017: /* fistp m16int */
+ if (reg_store_int16())
+ pop_0();/* pop only if the number was actually stored
+ * (see the 80486 manual p16-28) */
+ break;
+ case 020: /* fldenv m14/28byte */
+ fldenv();
+ break;
+ case 022: /* frstor m94/108byte */
+ frstor();
+ break;
+ case 023: /* fbld m80dec */
+ reg_load_bcd();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ reg_move(&FPU_loaded_data, pop_ptr);
+ break;
+ case 024: /* fldcw */
+ REENTRANT_CHECK(OFF);
+ control_word = fuword((unsigned short *) FPU_data_address);
+ REENTRANT_CHECK(ON);
+#ifdef NO_UNDERFLOW_TRAP
+ if (!(control_word & EX_Underflow)) {
+ control_word |= EX_Underflow;
+ }
+#endif
+ FPU_data_address = (void *) data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 025: /* fld m80real */
+ reg_load_extended();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ reg_move(&FPU_loaded_data, pop_ptr);
+ break;
+ case 027: /* fild m64int */
+ reg_load_int64();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ reg_move(&FPU_loaded_data, pop_ptr);
+ break;
+ case 030: /* fstenv m14/28byte */
+ fstenv();
+ FPU_data_address = (void *) data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 032: /* fsave */
+ fsave();
+ FPU_data_address = (void *) data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 033: /* fbstp m80dec */
+ if (reg_store_bcd())
+ pop_0();/* pop only if the number was actually stored
+ * (see the 80486 manual p16-28) */
+ break;
+ case 034: /* fstcw m16int */
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, FPU_data_address, 2);*/
+ suword( (short *) FPU_data_address,control_word);
+ REENTRANT_CHECK(ON);
+ FPU_data_address = (void *) data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 035: /* fstp m80real */
+ if (reg_store_extended())
+ pop_0();/* pop only if the number was actually stored
+ * (see the 80486 manual p16-28) */
+ break;
+ case 036: /* fstsw m2byte */
+ status_word &= ~SW_Top;
+ status_word |= (top & 7) << SW_Top_Shift;
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, FPU_data_address, 2);*/
+ suword( (short *) FPU_data_address,status_word);
+ REENTRANT_CHECK(ON);
+ FPU_data_address = (void *) data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 037: /* fistp m64int */
+ if (reg_store_int64())
+ pop_0();/* pop only if the number was actually stored
+ * (see the 80486 manual p16-28) */
+ break;
+ }
+}
diff --git a/sys/gnu/fpemul/math_emu.h b/sys/gnu/fpemul/math_emu.h
new file mode 100644
index 000000000000..031d391595b5
--- /dev/null
+++ b/sys/gnu/fpemul/math_emu.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * $Id: math_emu.h,v 1.2 1994/04/29 21:23:24 gclarkii Exp $
+ *
+ */
+
+#ifndef _MATH_EMU_H
+#define _MATH_EMU_H
+
+struct fpu_reg {
+ char sign;
+ char tag;
+ long exp;
+ u_long sigl;
+ u_long sigh;
+};
+
+union i387_union {
+ struct i387_hard_struct {
+ long cwd;
+ long swd;
+ long twd;
+ long fip;
+ long fcs;
+ long foo;
+ long fos;
+ long st_space[20]; /* 8*10 bytes for each FP-reg = 80
+ * bytes */
+ } hard;
+ struct i387_soft_struct {
+ long cwd;
+ long swd;
+ long twd;
+ long fip;
+ long fcs;
+ long foo;
+ long fos;
+ long top;
+ struct fpu_reg regs[8]; /* 8*16 bytes for each FP-reg = 128
+ * bytes */
+ unsigned char lookahead;
+ struct trapframe *frame;
+ unsigned long entry_eip;
+ int orig_eip;
+ } soft;
+};
+#endif
diff --git a/sys/gnu/fpemul/poly_2xm1.c b/sys/gnu/fpemul/poly_2xm1.c
new file mode 100644
index 000000000000..d4da1bb81527
--- /dev/null
+++ b/sys/gnu/fpemul/poly_2xm1.c
@@ -0,0 +1,141 @@
+/*
+ * poly_2xm1.c
+ *
+ * Function to compute 2^x-1 by a polynomial approximation.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: poly_2xm1.c,v 1.3 1994/06/10 07:44:32 rich Exp $
+ *
+ */
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+
+
+
+#define HIPOWER 13
+static unsigned short lterms[HIPOWER][4] =
+{
+ {0x79b5, 0xd1cf, 0x17f7, 0xb172},
+ {0x1b56, 0x058b, 0x7bff, 0x3d7f},
+ {0x8bb0, 0x8250, 0x846b, 0x0e35},
+ {0xbc65, 0xf747, 0x556d, 0x0276},
+ {0x17cb, 0x9e39, 0x61ff, 0x0057},
+ {0xe018, 0x9776, 0x1848, 0x000a},
+ {0x66f2, 0xff30, 0xffe5, 0x0000},
+ {0x682f, 0xffb6, 0x162b, 0x0000},
+ {0xb7ca, 0x2956, 0x01b5, 0x0000},
+ {0xcd3e, 0x4817, 0x001e, 0x0000},
+ {0xb7e2, 0xecbe, 0x0001, 0x0000},
+ {0x0ed5, 0x1a27, 0x0000, 0x0000},
+ {0x101d, 0x0222, 0x0000, 0x0000},
+};
+
+
+/*--- poly_2xm1() -----------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+int
+poly_2xm1(FPU_REG * arg, FPU_REG * result)
+{
+ short exponent;
+ long long Xll;
+ FPU_REG accum;
+
+
+ exponent = arg->exp - EXP_BIAS;
+
+ if (arg->tag == TW_Zero) {
+ /* Return 0.0 */
+ reg_move(&CONST_Z, result);
+ return 0;
+ }
+ if (exponent >= 0) { /* Can't hack a number >= 1.0 */
+ arith_invalid(result); /* Number too large */
+ return 1;
+ }
+ if (arg->sign != SIGN_POS) { /* Can't hack a number < 0.0 */
+ arith_invalid(result); /* Number negative */
+ return 1;
+ }
+ if (exponent < -64) {
+ reg_move(&CONST_LN2, result);
+ return 0;
+ }
+ *(unsigned *) &Xll = arg->sigl;
+ *(((unsigned *) &Xll) + 1) = arg->sigh;
+ if (exponent < -1) {
+ /* shift the argument right by the required places */
+ if (shrx(&Xll, -1 - exponent) >= (unsigned)0x80000000)
+ Xll++; /* round up */
+ }
+ *(short *) &(accum.sign) = 0; /* will be a valid positive nr with
+ * expon = 0 */
+ accum.exp = 0;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((unsigned *) &accum.sigl, (unsigned *) &Xll, lterms, HIPOWER - 1);
+
+ /* Convert to 64 bit signed-compatible */
+ accum.exp += EXP_BIAS - 1;
+
+ reg_move(&accum, result);
+
+ normalize(result);
+
+ return 0;
+
+}
diff --git a/sys/gnu/fpemul/poly_atan.c b/sys/gnu/fpemul/poly_atan.c
new file mode 100644
index 000000000000..f597ed9af43e
--- /dev/null
+++ b/sys/gnu/fpemul/poly_atan.c
@@ -0,0 +1,252 @@
+/*
+ * p_atan.c
+ *
+ * Compute the tan of a FPU_REG, using a polynomial approximation.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: poly_atan.c,v 1.4 1994/06/10 07:44:34 rich Exp $
+ *
+ */
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+
+
+#define HIPOWERon 6 /* odd poly, negative terms */
+static unsigned oddnegterms[HIPOWERon][2] =
+{
+ {0x00000000, 0x00000000}, /* for + 1.0 */
+ {0x763b6f3d, 0x1adc4428},
+ {0x20f0630b, 0x0502909d},
+ {0x4e825578, 0x0198ce38},
+ {0x22b7cb87, 0x008da6e3},
+ {0x9b30ca03, 0x00239c79}
+};
+#define HIPOWERop 6 /* odd poly, positive terms */
+static unsigned oddplterms[HIPOWERop][2] =
+{
+ {0xa6f67cb8, 0x94d910bd},
+ {0xa02ffab4, 0x0a43cb45},
+ {0x04265e6b, 0x02bf5655},
+ {0x0a728914, 0x00f280f7},
+ {0x6d640e01, 0x004d6556},
+ {0xf1dd2dbf, 0x000a530a}
+};
+
+
+static unsigned denomterm[2] =
+{0xfc4bd208, 0xea2e6612};
+
+
+
+/*--- poly_atan() -----------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+void
+poly_atan(FPU_REG * arg)
+{
+ char recursions = 0;
+ short exponent;
+ FPU_REG odd_poly, even_poly, pos_poly, neg_poly;
+ FPU_REG argSq;
+ long long arg_signif, argSqSq;
+
+
+#ifdef PARANOID
+ if (arg->sign != 0) { /* Can't hack a number < 0.0 */
+ arith_invalid(arg);
+ return;
+ } /* Need a positive number */
+#endif /* PARANOID */
+
+ exponent = arg->exp - EXP_BIAS;
+
+ if (arg->tag == TW_Zero) {
+ /* Return 0.0 */
+ reg_move(&CONST_Z, arg);
+ return;
+ }
+ if (exponent >= -2) {
+ /* argument is in the range [0.25 .. 1.0] */
+ if (exponent >= 0) {
+#ifdef PARANOID
+ if ((exponent == 0) &&
+ (arg->sigl == 0) && (arg->sigh == 0x80000000))
+#endif /* PARANOID */
+ {
+ reg_move(&CONST_PI4, arg);
+ return;
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL | 0x104); /* There must be a logic
+ * error */
+#endif /* PARANOID */
+ }
+ /* If the argument is greater than sqrt(2)-1 (=0.414213562...) */
+ /* convert the argument by an identity for atan */
+ if ((exponent >= -1) || (arg->sigh > 0xd413ccd0)) {
+ FPU_REG numerator, denom;
+
+ recursions++;
+
+ arg_signif = *(long long *) &(arg->sigl);
+ if (exponent < -1) {
+ if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000)
+ arg_signif++; /* round up */
+ }
+ *(long long *) &(numerator.sigl) = -arg_signif;
+ numerator.exp = EXP_BIAS - 1;
+ normalize(&numerator); /* 1 - arg */
+
+ arg_signif = *(long long *) &(arg->sigl);
+ if (shrx(&arg_signif, -exponent) >= (unsigned)0x80000000)
+ arg_signif++; /* round up */
+ *(long long *) &(denom.sigl) = arg_signif;
+ denom.sigh |= 0x80000000; /* 1 + arg */
+
+ arg->exp = numerator.exp;
+ reg_u_div(&numerator, &denom, arg, FULL_PRECISION);
+
+ exponent = arg->exp - EXP_BIAS;
+ }
+ }
+ *(long long *) &arg_signif = *(long long *) &(arg->sigl);
+
+#ifdef PARANOID
+ /* This must always be true */
+ if (exponent >= -1) {
+ EXCEPTION(EX_INTERNAL | 0x120); /* There must be a logic error */
+ }
+#endif /* PARANOID */
+
+ /* shift the argument right by the required places */
+ if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000)
+ arg_signif++; /* round up */
+
+ /* Now have arg_signif with binary point at the left .1xxxxxxxx */
+ mul64(&arg_signif, &arg_signif, (long long *) (&argSq.sigl));
+ mul64((long long *) (&argSq.sigl), (long long *) (&argSq.sigl), &argSqSq);
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(pos_poly.sign) = 0;
+ pos_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &pos_poly.sigl, (unsigned *) &argSqSq,
+ (unsigned short (*)[4]) oddplterms, HIPOWERop - 1);
+ mul64((long long *) (&argSq.sigl), (long long *) (&pos_poly.sigl),
+ (long long *) (&pos_poly.sigl));
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(neg_poly.sign) = 0;
+ neg_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &neg_poly.sigl, (unsigned *) &argSqSq,
+ (unsigned short (*)[4]) oddnegterms, HIPOWERon - 1);
+
+ /* Subtract the mantissas */
+ *((long long *) (&pos_poly.sigl)) -= *((long long *) (&neg_poly.sigl));
+
+ reg_move(&pos_poly, &odd_poly);
+ poly_add_1(&odd_poly);
+
+ /* The complete odd polynomial */
+ reg_u_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION);
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(even_poly.sign) = 0;
+
+ mul64((long long *) (&argSq.sigl),
+ (long long *) (&denomterm), (long long *) (&even_poly.sigl));
+
+ poly_add_1(&even_poly);
+
+ reg_div(&odd_poly, &even_poly, arg, FULL_PRECISION);
+
+ if (recursions)
+ reg_sub(&CONST_PI4, arg, arg, FULL_PRECISION);
+}
+
+
+/* The argument to this function must be polynomial() compatible,
+ i.e. have an exponent (not checked) of EXP_BIAS-1 but need not
+ be normalized.
+ This function adds 1.0 to the (assumed positive) argument. */
+void
+poly_add_1(FPU_REG * src)
+{
+/* Rounding in a consistent direction produces better results
+ for the use of this function in poly_atan. Simple truncation
+ is used here instead of round-to-nearest. */
+
+#ifdef OBSOLETE
+ char round = (src->sigl & 3) == 3;
+#endif /* OBSOLETE */
+
+ shrx(&src->sigl, 1);
+
+#ifdef OBSOLETE
+ if (round)
+ (*(long long *) &src->sigl)++; /* Round to even */
+#endif /* OBSOLETE */
+
+ src->sigh |= 0x80000000;
+
+ src->exp = EXP_BIAS;
+
+}
diff --git a/sys/gnu/fpemul/poly_div.s b/sys/gnu/fpemul/poly_div.s
new file mode 100644
index 000000000000..dce95ed82aae
--- /dev/null
+++ b/sys/gnu/fpemul/poly_div.s
@@ -0,0 +1,144 @@
+ .file "poly_div.S"
+/*
+ * poly_div.S
+ *
+ * A set of functions to divide 64 bit integers by fixed numbers.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: poly_div.s,v 1.3 1994/06/10 07:44:36 rich Exp $
+ *
+ */
+
+#include "fpu_asm.h"
+
+.text
+
+/*---------------------------------------------------------------------------*/
+ .align 2,144
+.globl _poly_div2
+_poly_div2:
+ pushl %ebp
+ movl %esp,%ebp
+
+ movl PARAM1,%ecx
+ movw (%ecx),%ax
+
+ shrl $1,4(%ecx)
+ rcrl $1,(%ecx)
+
+ testw $1,%ax
+ je poly_div2_exit
+
+ addl $1,(%ecx)
+ adcl $0,4(%ecx)
+poly_div2_exit:
+
+ leave
+ ret
+/*---------------------------------------------------------------------------*/
+ .align 2,144
+.globl _poly_div4
+_poly_div4:
+ pushl %ebp
+ movl %esp,%ebp
+
+ movl PARAM1,%ecx
+ movw (%ecx),%ax
+
+ movl 4(%ecx),%edx
+ shll $30,%edx
+
+ shrl $2,4(%ecx)
+ shrl $2,(%ecx)
+
+ orl %edx,(%ecx)
+
+ testw $2,%ax
+ je poly_div4_exit
+
+ addl $1,(%ecx)
+ adcl $0,4(%ecx)
+poly_div4_exit:
+
+ leave
+ ret
+/*---------------------------------------------------------------------------*/
+ .align 2,144
+.globl _poly_div16
+_poly_div16:
+ pushl %ebp
+ movl %esp,%ebp
+
+ movl PARAM1,%ecx
+ movw (%ecx),%ax
+
+ movl 4(%ecx),%edx
+ shll $28,%edx
+
+ shrl $4,4(%ecx)
+ shrl $4,(%ecx)
+
+ orl %edx,(%ecx)
+
+ testw $8,%ax
+ je poly_div16_exit
+
+ addl $1,(%ecx)
+ adcl $0,4(%ecx)
+poly_div16_exit:
+
+ leave
+ ret
+/*---------------------------------------------------------------------------*/
diff --git a/sys/gnu/fpemul/poly_l2.c b/sys/gnu/fpemul/poly_l2.c
new file mode 100644
index 000000000000..7c44103fd049
--- /dev/null
+++ b/sys/gnu/fpemul/poly_l2.c
@@ -0,0 +1,318 @@
+/*
+ * poly_l2.c
+ *
+ * Compute the base 2 log of a FPU_REG, using a polynomial approximation.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: poly_l2.c,v 1.5 1994/06/10 07:44:38 rich Exp $
+ *
+ */
+
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+
+
+
+#define HIPOWER 9
+static unsigned short lterms[HIPOWER][4] =
+{
+ /* Ideal computation with these coeffs gives about 64.6 bit rel
+ * accuracy. */
+ {0xe177, 0xb82f, 0x7652, 0x7154},
+ {0xee0f, 0xe80f, 0x2770, 0x7b1c},
+ {0x0fc0, 0xbe87, 0xb143, 0x49dd},
+ {0x78b9, 0xdadd, 0xec54, 0x34c2},
+ {0x003a, 0x5de9, 0x628b, 0x2909},
+ {0x5588, 0xed16, 0x4abf, 0x2193},
+ {0xb461, 0x85f7, 0x347a, 0x1c6a},
+ {0x0975, 0x87b3, 0xd5bf, 0x1876},
+ {0xe85c, 0xcec9, 0x84e7, 0x187d}
+};
+
+
+
+
+/*--- poly_l2() -------------------------------------------------------------+
+ | Base 2 logarithm by a polynomial approximation. |
+ +---------------------------------------------------------------------------*/
+void
+poly_l2(FPU_REG * arg, FPU_REG * result)
+{
+ short exponent;
+ char zero; /* flag for an Xx == 0 */
+ unsigned short bits, shift;
+ long long Xsq;
+ FPU_REG accum, denom, num, Xx;
+
+
+ exponent = arg->exp - EXP_BIAS;
+
+ accum.tag = TW_Valid; /* set the tags to Valid */
+
+ if (arg->sigh > (unsigned) 0xb504f334) {
+ /* This is good enough for the computation of the polynomial
+ * sum, but actually results in a loss of precision for the
+ * computation of Xx. This will matter only if exponent
+ * becomes zero. */
+ exponent++;
+ accum.sign = 1; /* sign to negative */
+ num.exp = EXP_BIAS; /* needed to prevent errors in div
+ * routine */
+ reg_u_div(&CONST_1, arg, &num, FULL_PRECISION);
+ } else {
+ accum.sign = 0; /* set the sign to positive */
+ num.sigl = arg->sigl; /* copy the mantissa */
+ num.sigh = arg->sigh;
+ }
+
+
+ /* shift num left, lose the ms bit */
+ num.sigh <<= 1;
+ if (num.sigl & 0x80000000)
+ num.sigh |= 1;
+ num.sigl <<= 1;
+
+ denom.sigl = num.sigl;
+ denom.sigh = num.sigh;
+ poly_div4((long long *) &(denom.sigl));
+ denom.sigh += 0x80000000; /* set the msb */
+ Xx.exp = EXP_BIAS; /* needed to prevent errors in div routine */
+ reg_u_div(&num, &denom, &Xx, FULL_PRECISION);
+
+ zero = !(Xx.sigh | Xx.sigl);
+
+ mul64((long long *) &Xx.sigl, (long long *) &Xx.sigl, &Xsq);
+ poly_div16(&Xsq);
+
+ accum.exp = -1; /* exponent of accum */
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((unsigned *) &accum.sigl, (unsigned *) &Xsq, lterms, HIPOWER - 1);
+
+ if (!exponent) {
+ /* If the exponent is zero, then we would lose precision by
+ * sticking to fixed point computation here */
+ /* We need to re-compute Xx because of loss of precision. */
+ FPU_REG lXx;
+ char sign;
+
+ sign = accum.sign;
+ accum.sign = 0;
+
+ /* make accum compatible and normalize */
+ accum.exp = EXP_BIAS + accum.exp;
+ normalize(&accum);
+
+ if (zero) {
+ reg_move(&CONST_Z, result);
+ } else {
+ /* we need to re-compute lXx to better accuracy */
+ num.tag = TW_Valid; /* set the tags to Vaild */
+ num.sign = 0; /* set the sign to positive */
+ num.exp = EXP_BIAS - 1;
+ if (sign) {
+ /* The argument is of the form 1-x */
+ /* Use 1-1/(1-x) = x/(1-x) */
+ *((long long *) &num.sigl) = -*((long long *) &(arg->sigl));
+ normalize(&num);
+ reg_div(&num, arg, &num, FULL_PRECISION);
+ } else {
+ normalize(&num);
+ }
+
+ denom.tag = TW_Valid; /* set the tags to Valid */
+ denom.sign = SIGN_POS; /* set the sign to positive */
+ denom.exp = EXP_BIAS;
+
+ reg_div(&num, &denom, &lXx, FULL_PRECISION);
+
+ reg_u_mul(&lXx, &accum, &accum, FULL_PRECISION);
+
+ reg_u_add(&lXx, &accum, result, FULL_PRECISION);
+
+ normalize(result);
+ }
+
+ result->sign = sign;
+ return;
+ }
+ mul64((long long *) &accum.sigl,
+ (long long *) &Xx.sigl, (long long *) &accum.sigl);
+
+ *((long long *) (&accum.sigl)) += *((long long *) (&Xx.sigl));
+
+ if (Xx.sigh > accum.sigh) {
+ /* There was an overflow */
+
+ poly_div2((long long *) &accum.sigl);
+ accum.sigh |= 0x80000000;
+ accum.exp++;
+ }
+ /* When we add the exponent to the accum result later, we will require
+ * that their signs are the same. Here we ensure that this is so. */
+ if (exponent && ((exponent < 0) ^ (accum.sign))) {
+ /* signs are different */
+
+ accum.sign = !accum.sign;
+
+ /* An exceptional case is when accum is zero */
+ if (accum.sigl | accum.sigh) {
+ /* find 1-accum */
+ /* Shift to get exponent == 0 */
+ if (accum.exp < 0) {
+ poly_div2((long long *) &accum.sigl);
+ accum.exp++;
+ }
+ /* Just negate, but throw away the sign */
+ *((long long *) &(accum.sigl)) = -*((long long *) &(accum.sigl));
+ if (exponent < 0)
+ exponent++;
+ else
+ exponent--;
+ }
+ }
+ shift = exponent >= 0 ? exponent : -exponent;
+ bits = 0;
+ if (shift) {
+ if (accum.exp) {
+ accum.exp++;
+ poly_div2((long long *) &accum.sigl);
+ }
+ while (shift) {
+ poly_div2((long long *) &accum.sigl);
+ if (shift & 1)
+ accum.sigh |= 0x80000000;
+ shift >>= 1;
+ bits++;
+ }
+ }
+ /* Convert to 64 bit signed-compatible */
+ accum.exp += bits + EXP_BIAS - 1;
+
+ reg_move(&accum, result);
+ normalize(result);
+
+ return;
+}
+
+
+/*--- poly_l2p1() -----------------------------------------------------------+
+ | Base 2 logarithm by a polynomial approximation. |
+ | log2(x+1) |
+ +---------------------------------------------------------------------------*/
+int
+poly_l2p1(FPU_REG * arg, FPU_REG * result)
+{
+ char sign = 0;
+ long long Xsq;
+ FPU_REG arg_pl1, denom, accum, local_arg, poly_arg;
+
+
+ sign = arg->sign;
+
+ reg_add(arg, &CONST_1, &arg_pl1, FULL_PRECISION);
+
+ if ((arg_pl1.sign) | (arg_pl1.tag)) { /* We need a valid positive
+ * number! */
+ return 1;
+ }
+ reg_add(&CONST_1, &arg_pl1, &denom, FULL_PRECISION);
+ reg_div(arg, &denom, &local_arg, FULL_PRECISION);
+ local_arg.sign = 0; /* Make the sign positive */
+
+ /* Now we need to check that |local_arg| is less than 3-2*sqrt(2) =
+ * 0.17157.. = .0xafb0ccc0 * 2^-2 */
+
+ if (local_arg.exp >= EXP_BIAS - 3) {
+ if ((local_arg.exp > EXP_BIAS - 3) ||
+ (local_arg.sigh > (unsigned) 0xafb0ccc0)) {
+ /* The argument is large */
+ poly_l2(&arg_pl1, result);
+ return 0;
+ }
+ }
+ /* Make a copy of local_arg */
+ reg_move(&local_arg, &poly_arg);
+
+ /* Get poly_arg bits aligned as required */
+ shrx((unsigned *) &(poly_arg.sigl), -(poly_arg.exp - EXP_BIAS + 3));
+
+ mul64((long long *) &(poly_arg.sigl), (long long *) &(poly_arg.sigl), &Xsq);
+ poly_div16(&Xsq);
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &accum.sigl, (unsigned *) &Xsq, lterms, HIPOWER - 1);
+
+ accum.tag = TW_Valid; /* set the tags to Valid */
+ accum.sign = SIGN_POS; /* and make accum positive */
+
+ /* make accum compatible and normalize */
+ accum.exp = EXP_BIAS - 1;
+ normalize(&accum);
+
+ reg_u_mul(&local_arg, &accum, &accum, FULL_PRECISION);
+
+ reg_u_add(&local_arg, &accum, result, FULL_PRECISION);
+
+ /* Multiply the result by 2 */
+ result->exp++;
+
+ result->sign = sign;
+
+ return 0;
+}
diff --git a/sys/gnu/fpemul/poly_mul64.s b/sys/gnu/fpemul/poly_mul64.s
new file mode 100644
index 000000000000..be5a136a1299
--- /dev/null
+++ b/sys/gnu/fpemul/poly_mul64.s
@@ -0,0 +1,124 @@
+/*
+ * poly_mul64.S
+ *
+ * Multiply two 64 bit integers.
+ *
+ * Call from C as:
+ * void mul64(long long *a, long long *b, long long *result)
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: poly_mul64.s,v 1.3 1994/06/10 07:44:39 rich Exp $
+ *
+ */
+
+
+#include "fpu_asm.h"
+
+.text
+ .align 2,144
+.globl _mul64
+_mul64:
+ pushl %ebp
+ movl %esp,%ebp
+ subl $16,%esp
+ pushl %esi
+ pushl %ebx
+
+ movl PARAM1,%esi
+ movl PARAM2,%ecx
+ movl PARAM3,%ebx
+
+ xor %eax,%eax
+ movl %eax,-4(%ebp)
+ movl %eax,-8(%ebp)
+
+ movl (%esi),%eax
+ mull (%ecx)
+ movl %eax,-16(%ebp) /* Not used */
+ movl %edx,-12(%ebp)
+
+ movl (%esi),%eax
+ mull 4(%ecx)
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl 4(%esi),%eax
+ mull (%ecx)
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl 4(%esi),%eax
+ mull 4(%ecx)
+ addl %eax,-8(%ebp)
+ adcl %edx,-4(%ebp)
+
+ testb $128,-9(%ebp)
+ je L_no_round
+
+ addl $1,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+L_no_round:
+ movl -8(%ebp),%esi
+ movl %esi,(%ebx)
+ movl -4(%ebp),%esi
+ movl %esi,4(%ebx)
+
+ popl %ebx
+ popl %esi
+ leave
+ ret
diff --git a/sys/gnu/fpemul/poly_sin.c b/sys/gnu/fpemul/poly_sin.c
new file mode 100644
index 000000000000..c48cacee24dc
--- /dev/null
+++ b/sys/gnu/fpemul/poly_sin.c
@@ -0,0 +1,192 @@
+/*
+ * poly_sin.c
+ *
+ * Computation of an approximation of the sin function by a polynomial
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: poly_sin.c,v 1.4 1994/06/10 07:44:41 rich Exp $
+ *
+ */
+
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+
+
+#define HIPOWER 5
+static unsigned short lterms[HIPOWER][4] =
+{
+ {0x846a, 0x42d1, 0xb544, 0x921f},
+ {0xe110, 0x75aa, 0xbc67, 0x1466},
+ {0x503d, 0xa43f, 0x83c1, 0x000a},
+ {0x8f9d, 0x7a19, 0x00f4, 0x0000},
+ {0xda03, 0x06aa, 0x0000, 0x0000},
+};
+
+static unsigned short negterms[HIPOWER][4] =
+{
+ {0x95ed, 0x2df2, 0xe731, 0xa55d},
+ {0xd159, 0xe62b, 0xd2cc, 0x0132},
+ {0x6342, 0xe9fb, 0x3c60, 0x0000},
+ {0x6256, 0xdf5a, 0x0002, 0x0000},
+ {0xf279, 0x000b, 0x0000, 0x0000},
+};
+
+
+/*--- poly_sine() -----------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+void
+poly_sine(FPU_REG * arg, FPU_REG * result)
+{
+ short exponent;
+ FPU_REG Xx, Xx2, Xx4, accum, negaccum;
+
+
+ exponent = arg->exp - EXP_BIAS;
+
+ if (arg->tag == TW_Zero) {
+ /* Return 0.0 */
+ reg_move(&CONST_Z, result);
+ return;
+ }
+#ifdef PARANOID
+ if (arg->sign != 0) { /* Can't hack a number < 0.0 */
+ EXCEPTION(EX_Invalid);
+ reg_move(&CONST_QNaN, result);
+ return;
+ }
+ if (exponent >= 0) { /* Can't hack a number > 1.0 */
+ if ((exponent == 0) && (arg->sigl == 0) && (arg->sigh == 0x80000000)) {
+ reg_move(&CONST_1, result);
+ return;
+ }
+ EXCEPTION(EX_Invalid);
+ reg_move(&CONST_QNaN, result);
+ return;
+ }
+#endif /* PARANOID */
+
+ Xx.sigl = arg->sigl;
+ Xx.sigh = arg->sigh;
+ if (exponent < -1) {
+ /* shift the argument right by the required places */
+ if (shrx(&(Xx.sigl), -1 - exponent) >= (unsigned)0x80000000)
+ (*((long long *) (&(Xx.sigl))))++; /* round up */
+ }
+ mul64((long long *) &(Xx.sigl), (long long *) &(Xx.sigl),
+ (long long *) &(Xx2.sigl));
+ mul64((long long *) &(Xx2.sigl), (long long *) &(Xx2.sigl),
+ (long long *) &(Xx4.sigl));
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(accum.sign) = 0;
+ accum.exp = 0;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &(accum.sigl), &(Xx4.sigl), lterms, HIPOWER - 1);
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(negaccum.sign) = 0;
+ negaccum.exp = 0;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &(negaccum.sigl), &(Xx4.sigl), negterms, HIPOWER - 1);
+ mul64((long long *) &(Xx2.sigl), (long long *) &(negaccum.sigl),
+ (long long *) &(negaccum.sigl));
+
+ /* Subtract the mantissas */
+ *((long long *) (&(accum.sigl))) -= *((long long *) (&(negaccum.sigl)));
+
+ /* Convert to 64 bit signed-compatible */
+ accum.exp = EXP_BIAS - 1 + accum.exp;
+
+ *(short *) &(result->sign) = *(short *) &(accum.sign);
+ result->exp = accum.exp;
+ result->sigl = accum.sigl;
+ result->sigh = accum.sigh;
+
+ normalize(result);
+
+ reg_mul(result, arg, result, FULL_PRECISION);
+ reg_u_add(result, arg, result, FULL_PRECISION);
+
+ /* A small overflow may be possible... but an illegal result. */
+ if (result->exp >= EXP_BIAS) {
+ if ((result->exp > EXP_BIAS) /* Larger or equal 2.0 */
+ ||(result->sigl > 1) /* Larger than 1.0+msb */
+ ||(result->sigh != 0x80000000) /* Much > 1.0 */
+ ) {
+#ifdef DEBUGGING
+ RE_ENTRANT_CHECK_OFF
+ printk("\nEXP=%d, MS=%08x, LS=%08x\n", result->exp,
+ result->sigh, result->sigl);
+ RE_ENTRANT_CHECK_ON
+#endif /* DEBUGGING */
+ EXCEPTION(EX_INTERNAL | 0x103);
+ }
+#ifdef DEBUGGING
+ RE_ENTRANT_CHECK_OFF
+ printk("\n***CORRECTING ILLEGAL RESULT*** in poly_sin() computation\n");
+ printk("EXP=%d, MS=%08x, LS=%08x\n", result->exp,
+ result->sigh, result->sigl);
+ RE_ENTRANT_CHECK_ON
+#endif /* DEBUGGING */
+
+ result->sigl = 0; /* Truncate the result to 1.00 */
+ }
+}
diff --git a/sys/gnu/fpemul/poly_tan.c b/sys/gnu/fpemul/poly_tan.c
new file mode 100644
index 000000000000..b167bd51a904
--- /dev/null
+++ b/sys/gnu/fpemul/poly_tan.c
@@ -0,0 +1,229 @@
+/*
+ * poly_tan.c
+ *
+ * Compute the tan of a FPU_REG, using a polynomial approximation.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: poly_tan.c,v 1.4 1994/06/10 07:44:42 rich Exp $
+ *
+ */
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+
+
+#define HIPOWERop 3 /* odd poly, positive terms */
+static unsigned short oddplterms[HIPOWERop][4] =
+{
+ {0x846a, 0x42d1, 0xb544, 0x921f},
+ {0x6fb2, 0x0215, 0x95c0, 0x099c},
+ {0xfce6, 0x0cc8, 0x1c9a, 0x0000}
+};
+#define HIPOWERon 2 /* odd poly, negative terms */
+static unsigned short oddnegterms[HIPOWERon][4] =
+{
+ {0x6906, 0xe205, 0x25c8, 0x8838},
+ {0x1dd7, 0x3fe3, 0x944e, 0x002c}
+};
+#define HIPOWERep 2 /* even poly, positive terms */
+static unsigned short evenplterms[HIPOWERep][4] =
+{
+ {0xdb8f, 0x3761, 0x1432, 0x2acf},
+ {0x16eb, 0x13c1, 0x3099, 0x0003}
+};
+#define HIPOWERen 2 /* even poly, negative terms */
+static unsigned short evennegterms[HIPOWERen][4] =
+{
+ {0x3a7c, 0xe4c5, 0x7f87, 0x2945},
+ {0x572b, 0x664c, 0xc543, 0x018c}
+};
+
+
+/*--- poly_tan() ------------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+void
+poly_tan(FPU_REG * arg, FPU_REG * y_reg)
+{
+ char invert = 0;
+ short exponent;
+ FPU_REG odd_poly, even_poly, pos_poly, neg_poly;
+ FPU_REG argSq;
+ long long arg_signif, argSqSq;
+
+
+ exponent = arg->exp - EXP_BIAS;
+
+ if (arg->tag == TW_Zero) {
+ /* Return 0.0 */
+ reg_move(&CONST_Z, y_reg);
+ return;
+ }
+ if (exponent >= -1) {
+ /* argument is in the range [0.5 .. 1.0] */
+ if (exponent >= 0) {
+#ifdef PARANOID
+ if ((exponent == 0) &&
+ (arg->sigl == 0) && (arg->sigh == 0x80000000))
+#endif /* PARANOID */
+ {
+ arith_overflow(y_reg);
+ return;
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL | 0x104); /* There must be a logic
+ * error */
+ return;
+#endif /* PARANOID */
+ }
+ /* The argument is in the range [0.5 .. 1.0) */
+ /* Convert the argument to a number in the range (0.0 .. 0.5] */
+ *((long long *) (&arg->sigl)) = -*((long long *) (&arg->sigl));
+ normalize(arg); /* Needed later */
+ exponent = arg->exp - EXP_BIAS;
+ invert = 1;
+ }
+#ifdef PARANOID
+ if (arg->sign != 0) { /* Can't hack a number < 0.0 */
+ arith_invalid(y_reg);
+ return;
+ } /* Need a positive number */
+#endif /* PARANOID */
+
+ *(long long *) &arg_signif = *(long long *) &(arg->sigl);
+ if (exponent < -1) {
+ /* shift the argument right by the required places */
+ if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000)
+ arg_signif++; /* round up */
+ }
+ mul64(&arg_signif, &arg_signif, (long long *) (&argSq.sigl));
+ mul64((long long *) (&argSq.sigl), (long long *) (&argSq.sigl), &argSqSq);
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(pos_poly.sign) = 0;
+ pos_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &pos_poly.sigl, (unsigned *) &argSqSq, oddplterms, HIPOWERop - 1);
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(neg_poly.sign) = 0;
+ neg_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &neg_poly.sigl, (unsigned *) &argSqSq, oddnegterms, HIPOWERon - 1);
+ mul64((long long *) (&argSq.sigl), (long long *) (&neg_poly.sigl),
+ (long long *) (&neg_poly.sigl));
+
+ /* Subtract the mantissas */
+ *((long long *) (&pos_poly.sigl)) -= *((long long *) (&neg_poly.sigl));
+
+ /* Convert to 64 bit signed-compatible */
+ pos_poly.exp -= 1;
+
+ reg_move(&pos_poly, &odd_poly);
+ normalize(&odd_poly);
+
+ reg_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION);
+ reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION); /* This is just the odd
+ * polynomial */
+
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(pos_poly.sign) = 0;
+ pos_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &pos_poly.sigl, (unsigned *) &argSqSq, evenplterms, HIPOWERep - 1);
+ mul64((long long *) (&argSq.sigl),
+ (long long *) (&pos_poly.sigl), (long long *) (&pos_poly.sigl));
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *) &(neg_poly.sign) = 0;
+ neg_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((u_int *) &neg_poly.sigl, (unsigned *) &argSqSq, evennegterms, HIPOWERen - 1);
+
+ /* Subtract the mantissas */
+ *((long long *) (&neg_poly.sigl)) -= *((long long *) (&pos_poly.sigl));
+ /* and multiply by argSq */
+
+ /* Convert argSq to a valid reg number */
+ *(short *) &(argSq.sign) = 0;
+ argSq.exp = EXP_BIAS - 1;
+ normalize(&argSq);
+
+ /* Convert to 64 bit signed-compatible */
+ neg_poly.exp -= 1;
+
+ reg_move(&neg_poly, &even_poly);
+ normalize(&even_poly);
+
+ reg_mul(&even_poly, &argSq, &even_poly, FULL_PRECISION);
+ reg_add(&even_poly, &argSq, &even_poly, FULL_PRECISION);
+ reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION); /* This is just the even
+ * polynomial */
+
+ /* Now ready to copy the results */
+ if (invert) {
+ reg_div(&even_poly, &odd_poly, y_reg, FULL_PRECISION);
+ } else {
+ reg_div(&odd_poly, &even_poly, y_reg, FULL_PRECISION);
+ }
+
+}
diff --git a/sys/gnu/fpemul/polynomial.s b/sys/gnu/fpemul/polynomial.s
new file mode 100644
index 000000000000..8f48ff4183ae
--- /dev/null
+++ b/sys/gnu/fpemul/polynomial.s
@@ -0,0 +1,192 @@
+/*
+ * polynomial.S
+ *
+ * Fixed point arithmetic polynomial evaluation.
+ *
+ * Call from C as:
+ * void polynomial(unsigned accum[], unsigned x[], unsigned terms[][2],
+ * int n)
+ *
+ * Computes:
+ * terms[0] + (terms[1] + (terms[2] + ... + (terms[n-1]*x)*x)*x)*x) ... )*x
+ * The result is returned in accum.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: polynomial.s,v 1.3 1994/06/10 07:44:43 rich Exp $
+ *
+ */
+
+ .file "fpolynom.s"
+
+#include "fpu_asm.h"
+
+
+/* #define EXTRA_PRECISE*/
+
+#define TERM_SIZE $8
+
+
+.text
+ .align 2,144
+.globl _polynomial
+_polynomial:
+ pushl %ebp
+ movl %esp,%ebp
+ subl $32,%esp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi /* accum */
+ movl PARAM2,%edi /* x */
+ movl PARAM3,%ebx /* terms */
+ movl PARAM4,%ecx /* n */
+
+ movl TERM_SIZE,%eax
+ mull %ecx
+ movl %eax,%ecx
+
+ movl 4(%ebx,%ecx,1),%edx /* terms[n] */
+ movl %edx,-20(%ebp)
+ movl (%ebx,%ecx,1),%edx /* terms[n] */
+ movl %edx,-24(%ebp)
+ xor %eax,%eax
+ movl %eax,-28(%ebp)
+
+ subl TERM_SIZE,%ecx
+ js L_accum_done
+
+L_accum_loop:
+ xor %eax,%eax
+ movl %eax,-4(%ebp)
+ movl %eax,-8(%ebp)
+
+#ifdef EXTRA_PRECISE
+ movl -28(%ebp),%eax
+ mull 4(%edi) /* x ms long */
+ movl %edx,-12(%ebp)
+#endif EXTRA_PRECISE
+
+ movl -24(%ebp),%eax
+ mull (%edi) /* x ls long */
+/* movl %eax,-16(%ebp) */ /* Not needed */
+ addl %edx,-12(%ebp)
+ adcl $0,-8(%ebp)
+
+ movl -24(%ebp),%eax
+ mull 4(%edi) /* x ms long */
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl -20(%ebp),%eax
+ mull (%edi)
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl -20(%ebp),%eax
+ mull 4(%edi)
+ addl %eax,-8(%ebp)
+ adcl %edx,-4(%ebp)
+
+/* Now add the next term */
+ movl (%ebx,%ecx,1),%eax
+ addl %eax,-8(%ebp)
+ movl 4(%ebx,%ecx,1),%eax
+ adcl %eax,-4(%ebp)
+
+/* And put into the second register */
+ movl -4(%ebp),%eax
+ movl %eax,-20(%ebp)
+ movl -8(%ebp),%eax
+ movl %eax,-24(%ebp)
+
+#ifdef EXTRA_PRECISE
+ movl -12(%ebp),%eax
+ movl %eax,-28(%ebp)
+#else
+ testb $128,-25(%ebp)
+ je L_no_poly_round
+
+ addl $1,-24(%ebp)
+ adcl $0,-20(%ebp)
+L_no_poly_round:
+#endif EXTRA_PRECISE
+
+ subl TERM_SIZE,%ecx
+ jns L_accum_loop
+
+L_accum_done:
+#ifdef EXTRA_PRECISE
+/* And round the result */
+ testb $128,-25(%ebp)
+ je L_poly_done
+
+ addl $1,-24(%ebp)
+ adcl $0,-20(%ebp)
+#endif EXTRA_PRECISE
+
+L_poly_done:
+ movl -24(%ebp),%eax
+ movl %eax,(%esi)
+ movl -20(%ebp),%eax
+ movl %eax,4(%esi)
+
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
diff --git a/sys/gnu/fpemul/reg_add_sub.c b/sys/gnu/fpemul/reg_add_sub.c
new file mode 100644
index 000000000000..ff8b0ea37c2e
--- /dev/null
+++ b/sys/gnu/fpemul/reg_add_sub.c
@@ -0,0 +1,303 @@
+/*
+ * reg_add_sub.c
+ *
+ * Functions to add or subtract two registers and put the result in a third.
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_add_sub.c,v 1.3 1994/06/10 07:44:44 rich Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | For each function, the destination may be any FPU_REG, including one of |
+ | the source FPU_REGs. |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+#include "fpu_system.h"
+
+
+void
+reg_add(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w)
+{
+ int diff;
+
+ if (!(a->tag | b->tag)) {
+ /* Both registers are valid */
+ if (!(a->sign ^ b->sign)) {
+ /* signs are the same */
+ reg_u_add(a, b, dest, control_w);
+ dest->sign = a->sign;
+ return;
+ }
+ /* The signs are different, so do a subtraction */
+ diff = a->exp - b->exp;
+ if (!diff) {
+ diff = a->sigh - b->sigh; /* Works only if ms bits
+ * are identical */
+ if (!diff) {
+ diff = a->sigl > b->sigl;
+ if (!diff)
+ diff = -(a->sigl < b->sigl);
+ }
+ }
+ if (diff > 0) {
+ reg_u_sub(a, b, dest, control_w);
+ dest->sign = a->sign;
+ } else
+ if (diff == 0) {
+ reg_move(&CONST_Z, dest);
+ /* sign depends upon rounding mode */
+ dest->sign = ((control_w & CW_RC) != RC_DOWN)
+ ? SIGN_POS : SIGN_NEG;
+ } else {
+ reg_u_sub(b, a, dest, control_w);
+ dest->sign = b->sign;
+ }
+ return;
+ } else {
+ if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) {
+ real_2op_NaN(a, b, dest);
+ return;
+ } else
+ if (a->tag == TW_Zero) {
+ if (b->tag == TW_Zero) {
+ char different_signs = a->sign ^ b->sign;
+ /* Both are zero, result will be zero. */
+ reg_move(a, dest);
+ if (different_signs) {
+ /* Signs are different. */
+ /* Sign of answer depends upon
+ * rounding mode. */
+ dest->sign = ((control_w & CW_RC) != RC_DOWN)
+ ? SIGN_POS : SIGN_NEG;
+ }
+ } else {
+#ifdef DENORM_OPERAND
+ if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(b, dest);
+ }
+ return;
+ } else
+ if (b->tag == TW_Zero) {
+#ifdef DENORM_OPERAND
+ if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(a, dest);
+ return;
+ } else
+ if (a->tag == TW_Infinity) {
+ if (b->tag != TW_Infinity) {
+#ifdef DENORM_OPERAND
+ if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(a, dest);
+ return;
+ }
+ if (a->sign == b->sign) {
+ /* They are both + or
+ * - infinity */
+ reg_move(a, dest);
+ return;
+ }
+ arith_invalid(dest); /* Infinity-Infinity is
+ * undefined. */
+ return;
+ } else
+ if (b->tag == TW_Infinity) {
+#ifdef DENORM_OPERAND
+ if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(b, dest);
+ return;
+ }
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL | 0x101);
+#endif
+}
+
+
+/* Subtract b from a. (a-b) -> dest */
+void
+reg_sub(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w)
+{
+ int diff;
+
+ if (!(a->tag | b->tag)) {
+ /* Both registers are valid */
+ diff = a->exp - b->exp;
+ if (!diff) {
+ diff = a->sigh - b->sigh; /* Works only if ms bits
+ * are identical */
+ if (!diff) {
+ diff = a->sigl > b->sigl;
+ if (!diff)
+ diff = -(a->sigl < b->sigl);
+ }
+ }
+ switch (a->sign * 2 + b->sign) {
+ case 0: /* P - P */
+ case 3: /* N - N */
+ if (diff > 0) {
+ reg_u_sub(a, b, dest, control_w);
+ dest->sign = a->sign;
+ } else
+ if (diff == 0) {
+#ifdef DENORM_OPERAND
+ if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(&CONST_Z, dest);
+ /* sign depends upon rounding mode */
+ dest->sign = ((control_w & CW_RC) != RC_DOWN)
+ ? SIGN_POS : SIGN_NEG;
+ } else {
+ reg_u_sub(b, a, dest, control_w);
+ dest->sign = a->sign ^ SIGN_POS ^ SIGN_NEG;
+ }
+ return;
+ case 1: /* P - N */
+ reg_u_add(a, b, dest, control_w);
+ dest->sign = SIGN_POS;
+ return;
+ case 2: /* N - P */
+ reg_u_add(a, b, dest, control_w);
+ dest->sign = SIGN_NEG;
+ return;
+ }
+ } else {
+ if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) {
+ real_2op_NaN(a, b, dest);
+ return;
+ } else
+ if (b->tag == TW_Zero) {
+ if (a->tag == TW_Zero) {
+ char same_signs = !(a->sign ^ b->sign);
+ /* Both are zero, result will be zero. */
+ reg_move(a, dest); /* Answer for different
+ * signs. */
+ if (same_signs) {
+ /* Sign depends upon rounding
+ * mode */
+ dest->sign = ((control_w & CW_RC) != RC_DOWN)
+ ? SIGN_POS : SIGN_NEG;
+ }
+ } else {
+#ifdef DENORM_OPERAND
+ if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(a, dest);
+ }
+ return;
+ } else
+ if (a->tag == TW_Zero) {
+#ifdef DENORM_OPERAND
+ if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(b, dest);
+ dest->sign ^= SIGN_POS ^ SIGN_NEG;
+ return;
+ } else
+ if (a->tag == TW_Infinity) {
+ if (b->tag != TW_Infinity) {
+#ifdef DENORM_OPERAND
+ if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(a, dest);
+ return;
+ }
+ /* Both args are Infinity */
+ if (a->sign == b->sign) {
+ arith_invalid(dest); /* Infinity-Infinity is
+ * undefined. */
+ return;
+ }
+ reg_move(a, dest);
+ return;
+ } else
+ if (b->tag == TW_Infinity) {
+#ifdef DENORM_OPERAND
+ if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(b, dest);
+ dest->sign ^= SIGN_POS ^ SIGN_NEG;
+ return;
+ }
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL | 0x110);
+#endif
+}
diff --git a/sys/gnu/fpemul/reg_compare.c b/sys/gnu/fpemul/reg_compare.c
new file mode 100644
index 000000000000..692cdc3910f8
--- /dev/null
+++ b/sys/gnu/fpemul/reg_compare.c
@@ -0,0 +1,384 @@
+/*
+ * reg_compare.c
+ *
+ * Compare two floating point registers
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_compare.c,v 1.3 1994/06/10 07:44:47 rich Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | compare() is the core FPU_REG comparison function |
+ +---------------------------------------------------------------------------*/
+#include "param.h"
+#include "proc.h"
+#include "systm.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "control_w.h"
+#include "status_w.h"
+
+
+int
+compare(FPU_REG * b)
+{
+ int diff;
+
+ if (FPU_st0_ptr->tag | b->tag) {
+ if (FPU_st0_ptr->tag == TW_Zero) {
+ if (b->tag == TW_Zero)
+ return COMP_A_eq_B;
+ if (b->tag == TW_Valid) {
+#ifdef DENORM_OPERAND
+ if ((b->exp <= EXP_UNDER) && (denormal_operand()))
+ return COMP_Denormal;
+#endif /* DENORM_OPERAND */
+ return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B;
+ }
+ } else
+ if (b->tag == TW_Zero) {
+ if (FPU_st0_ptr->tag == TW_Valid) {
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
+ return COMP_Denormal;
+#endif /* DENORM_OPERAND */
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
+ }
+ }
+ if (FPU_st0_ptr->tag == TW_Infinity) {
+ if ((b->tag == TW_Valid) || (b->tag == TW_Zero)) {
+#ifdef DENORM_OPERAND
+ if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)
+ && (denormal_operand()))
+ return COMP_Denormal;
+#endif /* DENORM_OPERAND */
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
+ } else
+ if (b->tag == TW_Infinity) {
+ /* The 80486 book says that infinities
+ * can be equal! */
+ return (FPU_st0_ptr->sign == b->sign) ? COMP_A_eq_B :
+ ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
+ }
+ /* Fall through to the NaN code */
+ } else
+ if (b->tag == TW_Infinity) {
+ if ((FPU_st0_ptr->tag == TW_Valid) || (FPU_st0_ptr->tag == TW_Zero)) {
+#ifdef DENORM_OPERAND
+ if ((FPU_st0_ptr->tag == TW_Valid)
+ && (FPU_st0_ptr->exp <= EXP_UNDER)
+ && (denormal_operand()))
+ return COMP_Denormal;
+#endif /* DENORM_OPERAND */
+ return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B;
+ }
+ /* Fall through to the NaN code */
+ }
+ /* The only possibility now should be that one of the
+ * arguments is a NaN */
+ if ((FPU_st0_ptr->tag == TW_NaN) || (b->tag == TW_NaN)) {
+ if (((FPU_st0_ptr->tag == TW_NaN) && !(FPU_st0_ptr->sigh & 0x40000000))
+ || ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)))
+ /* At least one arg is a signaling NaN */
+ return COMP_No_Comp | COMP_SNaN | COMP_NaN;
+ else
+ /* Neither is a signaling NaN */
+ return COMP_No_Comp | COMP_NaN;
+ }
+ EXCEPTION(EX_Invalid);
+ }
+#ifdef PARANOID
+ if (!(FPU_st0_ptr->sigh & 0x80000000))
+ EXCEPTION(EX_Invalid);
+ if (!(b->sigh & 0x80000000))
+ EXCEPTION(EX_Invalid);
+#endif /* PARANOID */
+
+#ifdef DENORM_OPERAND
+ if (((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (b->exp <= EXP_UNDER)) && (denormal_operand()))
+ return COMP_Denormal;
+#endif /* DENORM_OPERAND */
+
+ if (FPU_st0_ptr->sign != b->sign)
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
+
+ diff = FPU_st0_ptr->exp - b->exp;
+ if (diff == 0) {
+ diff = FPU_st0_ptr->sigh - b->sigh; /* Works only if ms bits
+ * are identical */
+ if (diff == 0) {
+ diff = FPU_st0_ptr->sigl > b->sigl;
+ if (diff == 0)
+ diff = -(FPU_st0_ptr->sigl < b->sigl);
+ }
+ }
+ if (diff > 0)
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
+ if (diff < 0)
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B;
+ return COMP_A_eq_B;
+
+}
+
+
+/* This function requires that st(0) is not empty */
+int
+compare_st_data(void)
+{
+ int f, c;
+
+ c = compare(&FPU_loaded_data);
+
+ if (c & (COMP_NaN | COMP_Denormal)) {
+ if (c & COMP_NaN) {
+ EXCEPTION(EX_Invalid);
+ f = SW_C3 | SW_C2 | SW_C0;
+ } else {
+ /* One of the operands is a de-normal */
+ return 0;
+ }
+ } else
+ switch (c) {
+ case COMP_A_lt_B:
+ f = SW_C0;
+ break;
+ case COMP_A_eq_B:
+ f = SW_C3;
+ break;
+ case COMP_A_gt_B:
+ f = 0;
+ break;
+ case COMP_No_Comp:
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL | 0x121);
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#endif /* PARANOID */
+ }
+ setcc(f);
+ return 1;
+}
+
+
+static int
+compare_st_st(int nr)
+{
+ int f, c;
+
+ if (!NOT_EMPTY_0 || !NOT_EMPTY(nr)) {
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ /* Stack fault */
+ EXCEPTION(EX_StackUnder);
+ return control_word & CW_Invalid;
+ }
+ c = compare(&st(nr));
+ if (c & (COMP_NaN | COMP_Denormal)) {
+ if (c & COMP_NaN) {
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ EXCEPTION(EX_Invalid);
+ return control_word & CW_Invalid;
+ } else {
+ /* One of the operands is a de-normal */
+ return control_word & CW_Denormal;
+ }
+ } else
+ switch (c) {
+ case COMP_A_lt_B:
+ f = SW_C0;
+ break;
+ case COMP_A_eq_B:
+ f = SW_C3;
+ break;
+ case COMP_A_gt_B:
+ f = 0;
+ break;
+ case COMP_No_Comp:
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL | 0x122);
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#endif /* PARANOID */
+ }
+ setcc(f);
+ return 1;
+}
+
+
+static int
+compare_u_st_st(int nr)
+{
+ int f, c;
+
+ if (!NOT_EMPTY_0 || !NOT_EMPTY(nr)) {
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ /* Stack fault */
+ EXCEPTION(EX_StackUnder);
+ return control_word & CW_Invalid;
+ }
+ c = compare(&st(nr));
+ if (c & (COMP_NaN | COMP_Denormal)) {
+ if (c & COMP_NaN) {
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ if (c & COMP_SNaN) { /* This is the only difference
+ * between un-ordered and
+ * ordinary comparisons */
+ EXCEPTION(EX_Invalid);
+ return control_word & CW_Invalid;
+ }
+ return 1;
+ } else {
+ /* One of the operands is a de-normal */
+ return control_word & CW_Denormal;
+ }
+ } else
+ switch (c) {
+ case COMP_A_lt_B:
+ f = SW_C0;
+ break;
+ case COMP_A_eq_B:
+ f = SW_C3;
+ break;
+ case COMP_A_gt_B:
+ f = 0;
+ break;
+ case COMP_No_Comp:
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL | 0x123);
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#endif /* PARANOID */
+ }
+ setcc(f);
+ return 1;
+}
+/*---------------------------------------------------------------------------*/
+
+void
+fcom_st()
+{
+ /* fcom st(i) */
+ compare_st_st(FPU_rm);
+}
+
+
+void
+fcompst()
+{
+ /* fcomp st(i) */
+ if (compare_st_st(FPU_rm))
+ pop();
+}
+
+
+void
+fcompp()
+{
+ /* fcompp */
+ if (FPU_rm != 1)
+ return Un_impl();
+ if (compare_st_st(1)) {
+ pop();
+ FPU_st0_ptr = &st(0);
+ pop();
+ }
+}
+
+
+void
+fucom_()
+{
+ /* fucom st(i) */
+ compare_u_st_st(FPU_rm);
+
+}
+
+
+void
+fucomp()
+{
+ /* fucomp st(i) */
+ if (compare_u_st_st(FPU_rm))
+ pop();
+}
+
+
+void
+fucompp()
+{
+ /* fucompp */
+ if (FPU_rm == 1) {
+ if (compare_u_st_st(1)) {
+ pop();
+ FPU_st0_ptr = &st(0);
+ pop();
+ }
+ } else
+ Un_impl();
+}
diff --git a/sys/gnu/fpemul/reg_constant.c b/sys/gnu/fpemul/reg_constant.c
new file mode 100644
index 000000000000..f33427358e1c
--- /dev/null
+++ b/sys/gnu/fpemul/reg_constant.c
@@ -0,0 +1,175 @@
+/*
+ * reg_constant.c
+ *
+ * All of the constant FPU_REGs
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $id:$
+ *
+ */
+
+
+
+#include "param.h"
+#include "proc.h"
+#include "systm.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "status_w.h"
+#include "reg_constant.h"
+
+
+FPU_REG CONST_1 = {SIGN_POS, TW_Valid, EXP_BIAS,
+0x00000000, 0x80000000};
+FPU_REG CONST_2 = {SIGN_POS, TW_Valid, EXP_BIAS + 1,
+0x00000000, 0x80000000};
+FPU_REG CONST_HALF = {SIGN_POS, TW_Valid, EXP_BIAS - 1,
+0x00000000, 0x80000000};
+FPU_REG CONST_L2T = {SIGN_POS, TW_Valid, EXP_BIAS + 1,
+0xcd1b8afe, 0xd49a784b};
+FPU_REG CONST_L2E = {SIGN_POS, TW_Valid, EXP_BIAS,
+0x5c17f0bc, 0xb8aa3b29};
+FPU_REG CONST_PI = {SIGN_POS, TW_Valid, EXP_BIAS + 1,
+0x2168c235, 0xc90fdaa2};
+FPU_REG CONST_PI2 = {SIGN_POS, TW_Valid, EXP_BIAS,
+0x2168c235, 0xc90fdaa2};
+FPU_REG CONST_PI4 = {SIGN_POS, TW_Valid, EXP_BIAS - 1,
+0x2168c235, 0xc90fdaa2};
+FPU_REG CONST_LG2 = {SIGN_POS, TW_Valid, EXP_BIAS - 2,
+0xfbcff799, 0x9a209a84};
+FPU_REG CONST_LN2 = {SIGN_POS, TW_Valid, EXP_BIAS - 1,
+0xd1cf79ac, 0xb17217f7};
+/* Only the sign (and tag) is used in internal zeroes */
+FPU_REG CONST_Z = {SIGN_POS, TW_Zero, 0, 0x0, 0x0};
+/* Only the sign and significand (and tag) are used in internal NaNs */
+/* The 80486 never generates one of these
+FPU_REG CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 };
+ */
+/* This is the real indefinite QNaN */
+FPU_REG CONST_QNaN = {SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000};
+/* Only the sign (and tag) is used in internal infinities */
+FPU_REG CONST_INF = {SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000};
+
+
+
+static void
+fld_const(FPU_REG * c)
+{
+ FPU_REG *st_new_ptr;
+
+ if (STACK_OVERFLOW) {
+ stack_overflow();
+ return;
+ }
+ push();
+ reg_move(c, FPU_st0_ptr);
+ status_word &= ~SW_C1;
+}
+
+
+static void
+fld1(void)
+{
+ fld_const(&CONST_1);
+}
+
+static void
+fldl2t(void)
+{
+ fld_const(&CONST_L2T);
+}
+
+static void
+fldl2e(void)
+{
+ fld_const(&CONST_L2E);
+}
+
+static void
+fldpi(void)
+{
+ fld_const(&CONST_PI);
+}
+
+static void
+fldlg2(void)
+{
+ fld_const(&CONST_LG2);
+}
+
+static void
+fldln2(void)
+{
+ fld_const(&CONST_LN2);
+}
+
+static void
+fldz(void)
+{
+ fld_const(&CONST_Z);
+}
+
+static FUNC constants_table[] = {
+ fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, Un_impl
+};
+
+void
+fconst(void)
+{
+ (constants_table[FPU_rm]) ();
+}
diff --git a/sys/gnu/fpemul/reg_constant.h b/sys/gnu/fpemul/reg_constant.h
new file mode 100644
index 000000000000..d64a3d55f91b
--- /dev/null
+++ b/sys/gnu/fpemul/reg_constant.h
@@ -0,0 +1,82 @@
+/*
+ * reg_constant.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_constant.h,v 1.3 1994/06/10 07:44:49 rich Exp $
+ *
+ */
+
+#ifndef _REG_CONSTANT_H_
+#define _REG_CONSTANT_H_
+
+#include "fpu_emu.h"
+
+extern FPU_REG CONST_1;
+extern FPU_REG CONST_2;
+extern FPU_REG CONST_HALF;
+extern FPU_REG CONST_L2T;
+extern FPU_REG CONST_L2E;
+extern FPU_REG CONST_PI;
+extern FPU_REG CONST_PI2;
+extern FPU_REG CONST_PI4;
+extern FPU_REG CONST_LG2;
+extern FPU_REG CONST_LN2;
+extern FPU_REG CONST_Z;
+extern FPU_REG CONST_PINF;
+extern FPU_REG CONST_INF;
+extern FPU_REG CONST_MINF;
+extern FPU_REG CONST_QNaN;
+
+#endif /* _REG_CONSTANT_H_ */
diff --git a/sys/gnu/fpemul/reg_div.s b/sys/gnu/fpemul/reg_div.s
new file mode 100644
index 000000000000..8f7357530b7e
--- /dev/null
+++ b/sys/gnu/fpemul/reg_div.s
@@ -0,0 +1,295 @@
+ .file "reg_div.S"
+/*
+ * reg_div.S
+ *
+ * Divide one FPU_REG by another and put the result in a destination FPU_REG.
+ *
+ * Call from C as:
+ * void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest,
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_div.s,v 1.3 1994/06/10 07:44:50 rich Exp $
+ *
+ */
+
+#include "exception.h"
+#include "fpu_asm.h"
+#include "control_w.h"
+
+.text
+ .align 2
+
+.globl _reg_div
+_reg_div:
+ pushl %ebp
+ movl %esp,%ebp
+
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi
+ movl PARAM2,%ebx
+ movl PARAM3,%edi
+
+ movb TAG(%esi),%al
+ orb TAG(%ebx),%al
+
+ jne L_div_special /* Not (both numbers TW_Valid) */
+
+#ifdef DENORM_OPERAND
+/* Check for denormals */
+ cmpl EXP_UNDER,EXP(%esi)
+ jg xL_arg1_not_denormal
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xL_arg1_not_denormal:
+ cmpl EXP_UNDER,EXP(%ebx)
+ jg xL_arg2_not_denormal
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xL_arg2_not_denormal:
+#endif DENORM_OPERAND
+
+/* Both arguments are TW_Valid */
+ movb TW_Valid,TAG(%edi)
+
+ movb SIGN(%esi),%cl
+ cmpb %cl,SIGN(%ebx)
+ setne (%edi) /* Set the sign, requires SIGN_NEG=1, SIGN_POS=0 */
+
+ movl EXP(%esi),%edx
+ movl EXP(%ebx),%eax
+ subl %eax,%edx
+ addl EXP_BIAS,%edx
+ movl %edx,EXP(%edi)
+
+ jmp _divide_kernel
+
+
+/*-----------------------------------------------------------------------*/
+L_div_special:
+ cmpb TW_NaN,TAG(%esi) /* A NaN with anything to give NaN */
+ je L_arg1_NaN
+
+ cmpb TW_NaN,TAG(%ebx) /* A NaN with anything to give NaN */
+ jne L_no_NaN_arg
+
+/* Operations on NaNs */
+L_arg1_NaN:
+L_arg2_NaN:
+ pushl %edi /* Destination */
+ pushl %ebx
+ pushl %esi
+ call _real_2op_NaN
+ jmp LDiv_exit
+
+/* Invalid operations */
+L_zero_zero:
+L_inf_inf:
+ pushl %edi /* Destination */
+ call _arith_invalid /* 0/0 or Infinity/Infinity */
+ jmp LDiv_exit
+
+L_no_NaN_arg:
+ cmpb TW_Infinity,TAG(%esi)
+ jne L_arg1_not_inf
+
+ cmpb TW_Infinity,TAG(%ebx)
+ je L_inf_inf /* invalid operation */
+
+ cmpb TW_Valid,TAG(%ebx)
+ je L_inf_valid
+
+#ifdef PARANOID
+ /* arg2 must be zero or valid */
+ cmpb TW_Zero,TAG(%ebx)
+ ja L_unknown_tags
+#endif PARANOID
+
+ /* Note that p16-9 says that infinity/0 returns infinity */
+ jmp L_copy_arg1 /* Answer is Inf */
+
+L_inf_valid:
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%ebx)
+ jg L_copy_arg1 /* Answer is Inf */
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+#endif DENORM_OPERAND
+
+ jmp L_copy_arg1 /* Answer is Inf */
+
+L_arg1_not_inf:
+ cmpb TW_Zero,TAG(%ebx) /* Priority to div-by-zero error */
+ jne L_arg2_not_zero
+
+ cmpb TW_Zero,TAG(%esi)
+ je L_zero_zero /* invalid operation */
+
+#ifdef PARANOID
+ /* arg1 must be valid */
+ cmpb TW_Valid,TAG(%esi)
+ ja L_unknown_tags
+#endif PARANOID
+
+/* Division by zero error */
+ pushl %edi /* destination */
+ movb SIGN(%esi),%al
+ xorb SIGN(%ebx),%al
+ pushl %eax /* lower 8 bits have the sign */
+ call _divide_by_zero
+ jmp LDiv_exit
+
+L_arg2_not_zero:
+ cmpb TW_Infinity,TAG(%ebx)
+ jne L_arg2_not_inf
+
+#ifdef DENORM_OPERAND
+ cmpb TW_Valid,TAG(%esi)
+ jne L_return_zero
+
+ cmpl EXP_UNDER,EXP(%esi)
+ jg L_return_zero /* Answer is zero */
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+#endif DENORM_OPERAND
+
+ jmp L_return_zero /* Answer is zero */
+
+L_arg2_not_inf:
+
+#ifdef PARANOID
+ cmpb TW_Zero,TAG(%esi)
+ jne L_unknown_tags
+#endif PARANOID
+
+ /* arg1 is zero, arg2 is not Infinity or a NaN */
+
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%ebx)
+ jg L_copy_arg1 /* Answer is zero */
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+#endif DENORM_OPERAND
+
+L_copy_arg1:
+ movb TAG(%esi),%ax
+ movb %ax,TAG(%edi)
+ movl EXP(%esi),%eax
+ movl %eax,EXP(%edi)
+ movl SIGL(%esi),%eax
+ movl %eax,SIGL(%edi)
+ movl SIGH(%esi),%eax
+ movl %eax,SIGH(%edi)
+
+ movb SIGN(%esi),%cl
+ cmpb %cl,SIGN(%ebx)
+ jne LDiv_negative_result
+
+ movb SIGN_POS,SIGN(%edi)
+ jmp LDiv_exit
+
+LDiv_set_result_sign:
+ movb SIGN(%esi),%cl
+ cmpb %cl,SIGN(%edi)
+ jne LDiv_negative_result
+
+ movb SIGN_POS,SIGN(%ebx)
+ jmp LDiv_exit
+
+LDiv_negative_result:
+ movb SIGN_NEG,SIGN(%edi)
+
+LDiv_exit:
+ leal -12(%ebp),%esp
+
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+
+
+L_return_zero:
+ movb TW_Zero,TAG(%edi)
+ jmp LDiv_set_result_sign
+
+#ifdef PARANOID
+L_unknown_tags:
+ push EX_INTERNAL | 0x208
+ call EXCEPTION
+
+ /* Generate a NaN for unknown tags */
+ movl _CONST_QNaN,%eax
+ movl %eax,(%edi)
+ movl _CONST_QNaN+4,%eax
+ movl %eax,SIGL(%edi)
+ movl _CONST_QNaN+8,%eax
+ movl %eax,SIGH(%edi)
+ jmp LDiv_exit
+#endif PARANOID
diff --git a/sys/gnu/fpemul/reg_ld_str.c b/sys/gnu/fpemul/reg_ld_str.c
new file mode 100644
index 000000000000..37b69a6bbfe2
--- /dev/null
+++ b/sys/gnu/fpemul/reg_ld_str.c
@@ -0,0 +1,1387 @@
+/*
+ * reg_ld_str.c
+ *
+ * All of the functions which transfer data between user memory and FPU_REGs.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_ld_str.c,v 1.3 1994/06/10 07:44:52 rich Exp $
+ *
+ */
+
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+#include "param.h"
+#include "proc.h"
+#include "systm.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+#include "reg_constant.h"
+#include "control_w.h"
+#include "status_w.h"
+
+
+#define EXTENDED_Emax 0x3fff /* largest valid exponent */
+#define EXTENDED_Ebias 0x3fff
+#define EXTENDED_Emin (-0x3ffe) /* smallest valid exponent */
+
+#define DOUBLE_Emax 1023 /* largest valid exponent */
+#define DOUBLE_Ebias 1023
+#define DOUBLE_Emin (-1022) /* smallest valid exponent */
+
+#define SINGLE_Emax 127 /* largest valid exponent */
+#define SINGLE_Ebias 127
+#define SINGLE_Emin (-126) /* smallest valid exponent */
+
+#define LOST_UP (EX_Precision | SW_C1)
+#define LOST_DOWN EX_Precision
+
+FPU_REG FPU_loaded_data;
+
+
+/* Get a long double from user memory */
+void
+reg_load_extended(void)
+{
+ long double *s = (long double *) FPU_data_address;
+ unsigned long sigl, sigh, exp;
+
+ REENTRANT_CHECK(OFF);
+ /* Use temporary variables here because FPU_loaded data is static and
+ * hence re-entrancy problems can arise */
+ sigl = fuword((unsigned long *) s);
+ sigh = fuword(1 + (unsigned long *) s);
+ exp = fuword(4 + (unsigned short *) s);
+ REENTRANT_CHECK(ON);
+
+ FPU_loaded_data.sigl = sigl;
+ FPU_loaded_data.sigh = sigh;
+ FPU_loaded_data.exp = exp;
+
+ if (FPU_loaded_data.exp & 0x8000)
+ FPU_loaded_data.sign = SIGN_NEG;
+ else
+ FPU_loaded_data.sign = SIGN_POS;
+ if ((FPU_loaded_data.exp &= 0x7fff) == 0) {
+ if (!(FPU_loaded_data.sigl | FPU_loaded_data.sigh)) {
+ FPU_loaded_data.tag = TW_Zero;
+ return;
+ }
+ /* The number is a de-normal or pseudodenormal. */
+ /* The 80486 doesn't regard pseudodenormals as denormals here. */
+ if (!(FPU_loaded_data.sigh & 0x80000000))
+ EXCEPTION(EX_Denormal);
+ FPU_loaded_data.exp++;
+
+ /* The default behaviour will now take care of it. */
+ } else
+ if (FPU_loaded_data.exp == 0x7fff) {
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ if ((FPU_loaded_data.sigh == 0x80000000)
+ && (FPU_loaded_data.sigl == 0)) {
+ FPU_loaded_data.tag = TW_Infinity;
+ return;
+ } else
+ if (!(FPU_loaded_data.sigh & 0x80000000)) {
+ /* Unsupported NaN data type */
+ EXCEPTION(EX_Invalid);
+ FPU_loaded_data.tag = TW_NaN;
+ return;
+ }
+ FPU_loaded_data.tag = TW_NaN;
+ return;
+ }
+ FPU_loaded_data.exp = (FPU_loaded_data.exp & 0x7fff) - EXTENDED_Ebias
+ + EXP_BIAS;
+ FPU_loaded_data.tag = TW_Valid;
+
+ if (!(sigh & 0x80000000)) {
+ /* Unsupported data type */
+ EXCEPTION(EX_Invalid);
+ normalize_nuo(&FPU_loaded_data);
+ }
+}
+
+
+/* Get a double from user memory */
+void
+reg_load_double(void)
+{
+ double *dfloat = (double *) FPU_data_address;
+ int exp;
+ unsigned m64, l64;
+
+ REENTRANT_CHECK(OFF);
+ m64 = fuword(1 + (unsigned long *) dfloat);
+ l64 = fuword((unsigned long *) dfloat);
+ REENTRANT_CHECK(ON);
+
+ if (m64 & 0x80000000)
+ FPU_loaded_data.sign = SIGN_NEG;
+ else
+ FPU_loaded_data.sign = SIGN_POS;
+ exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias;
+ m64 &= 0xfffff;
+ if (exp > DOUBLE_Emax) {
+ /* Infinity or NaN */
+ if ((m64 == 0) && (l64 == 0)) {
+ /* +- infinity */
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.tag = TW_Infinity;
+ return;
+ } else {
+ /* Must be a signaling or quiet NaN */
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.tag = TW_NaN;
+ FPU_loaded_data.sigh = (m64 << 11) | 0x80000000;
+ FPU_loaded_data.sigh |= l64 >> 21;
+ FPU_loaded_data.sigl = l64 << 11;
+ return;
+ }
+ } else
+ if (exp < DOUBLE_Emin) {
+ /* Zero or de-normal */
+ if ((m64 == 0) && (l64 == 0)) {
+ /* Zero */
+ int c = FPU_loaded_data.sign;
+ reg_move(&CONST_Z, &FPU_loaded_data);
+ FPU_loaded_data.sign = c;
+ return;
+ } else {
+ /* De-normal */
+ EXCEPTION(EX_Denormal);
+ FPU_loaded_data.exp = DOUBLE_Emin + EXP_BIAS;
+ FPU_loaded_data.tag = TW_Valid;
+ FPU_loaded_data.sigh = m64 << 11;
+ FPU_loaded_data.sigh |= l64 >> 21;
+ FPU_loaded_data.sigl = l64 << 11;
+ normalize_nuo(&FPU_loaded_data);
+ return;
+ }
+ } else {
+ FPU_loaded_data.exp = exp + EXP_BIAS;
+ FPU_loaded_data.tag = TW_Valid;
+ FPU_loaded_data.sigh = (m64 << 11) | 0x80000000;
+ FPU_loaded_data.sigh |= l64 >> 21;
+ FPU_loaded_data.sigl = l64 << 11;
+
+ return;
+ }
+}
+
+
+/* Get a float from user memory */
+void
+reg_load_single(void)
+{
+ float *single = (float *) FPU_data_address;
+ unsigned m32;
+ int exp;
+
+ REENTRANT_CHECK(OFF);
+ m32 = fuword((unsigned long *) single);
+ REENTRANT_CHECK(ON);
+
+ if (m32 & 0x80000000)
+ FPU_loaded_data.sign = SIGN_NEG;
+ else
+ FPU_loaded_data.sign = SIGN_POS;
+ if (!(m32 & 0x7fffffff)) {
+ /* Zero */
+ int c = FPU_loaded_data.sign;
+ reg_move(&CONST_Z, &FPU_loaded_data);
+ FPU_loaded_data.sign = c;
+ return;
+ }
+ exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias;
+ m32 = (m32 & 0x7fffff) << 8;
+ if (exp < SINGLE_Emin) {
+ /* De-normals */
+ EXCEPTION(EX_Denormal);
+ FPU_loaded_data.exp = SINGLE_Emin + EXP_BIAS;
+ FPU_loaded_data.tag = TW_Valid;
+ FPU_loaded_data.sigh = m32;
+ FPU_loaded_data.sigl = 0;
+ normalize_nuo(&FPU_loaded_data);
+ return;
+ } else
+ if (exp > SINGLE_Emax) {
+ /* Infinity or NaN */
+ if (m32 == 0) {
+ /* +- infinity */
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.tag = TW_Infinity;
+ return;
+ } else {
+ /* Must be a signaling or quiet NaN */
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.tag = TW_NaN;
+ FPU_loaded_data.sigh = m32 | 0x80000000;
+ FPU_loaded_data.sigl = 0;
+ return;
+ }
+ } else {
+ FPU_loaded_data.exp = exp + EXP_BIAS;
+ FPU_loaded_data.sigh = m32 | 0x80000000;
+ FPU_loaded_data.sigl = 0;
+ FPU_loaded_data.tag = TW_Valid;
+ }
+}
+
+
+/* Get a long long from user memory */
+void
+reg_load_int64(void)
+{
+ long long *_s = (long long *) FPU_data_address;
+ int e;
+ long long s;
+
+ REENTRANT_CHECK(OFF);
+ ((unsigned long *) &s)[0] = fuword((unsigned long *) _s);
+ ((unsigned long *) &s)[1] = fuword(1 + (unsigned long *) _s);
+ REENTRANT_CHECK(ON);
+
+ if (s == 0) {
+ reg_move(&CONST_Z, &FPU_loaded_data);
+ return;
+ }
+ if (s > 0)
+ FPU_loaded_data.sign = SIGN_POS;
+ else {
+ s = -s;
+ FPU_loaded_data.sign = SIGN_NEG;
+ }
+
+ e = EXP_BIAS + 63;
+ *((long long *) &FPU_loaded_data.sigl) = s;
+ FPU_loaded_data.exp = e;
+ FPU_loaded_data.tag = TW_Valid;
+ normalize_nuo(&FPU_loaded_data);
+}
+
+
+/* Get a long from user memory */
+void
+reg_load_int32(void)
+{
+ long *_s = (long *) FPU_data_address;
+ long s;
+ int e;
+
+ REENTRANT_CHECK(OFF);
+ s = (long) fuword((unsigned long *) _s);
+ REENTRANT_CHECK(ON);
+
+ if (s == 0) {
+ reg_move(&CONST_Z, &FPU_loaded_data);
+ return;
+ }
+ if (s > 0)
+ FPU_loaded_data.sign = SIGN_POS;
+ else {
+ s = -s;
+ FPU_loaded_data.sign = SIGN_NEG;
+ }
+
+ e = EXP_BIAS + 31;
+ FPU_loaded_data.sigh = s;
+ FPU_loaded_data.sigl = 0;
+ FPU_loaded_data.exp = e;
+ FPU_loaded_data.tag = TW_Valid;
+ normalize_nuo(&FPU_loaded_data);
+}
+
+
+/* Get a short from user memory */
+void
+reg_load_int16(void)
+{
+ short *_s = (short *) FPU_data_address;
+ int s, e;
+
+ REENTRANT_CHECK(OFF);
+ /* Cast as short to get the sign extended. */
+ s = (short) fuword((unsigned short *) _s);
+ REENTRANT_CHECK(ON);
+
+ if (s == 0) {
+ reg_move(&CONST_Z, &FPU_loaded_data);
+ return;
+ }
+ if (s > 0)
+ FPU_loaded_data.sign = SIGN_POS;
+ else {
+ s = -s;
+ FPU_loaded_data.sign = SIGN_NEG;
+ }
+
+ e = EXP_BIAS + 15;
+ FPU_loaded_data.sigh = s << 16;
+
+ FPU_loaded_data.sigl = 0;
+ FPU_loaded_data.exp = e;
+ FPU_loaded_data.tag = TW_Valid;
+ normalize_nuo(&FPU_loaded_data);
+}
+
+
+/* Get a packed bcd array from user memory */
+void
+reg_load_bcd(void)
+{
+ char *s = (char *) FPU_data_address;
+ int pos;
+ unsigned char bcd;
+ long long l = 0;
+
+ for (pos = 8; pos >= 0; pos--) {
+ l *= 10;
+ REENTRANT_CHECK(OFF);
+ bcd = (unsigned char) fubyte((unsigned char *) s + pos);
+ REENTRANT_CHECK(ON);
+ l += bcd >> 4;
+ l *= 10;
+ l += bcd & 0x0f;
+ }
+
+ /* Finish all access to user memory before putting stuff into the
+ * static FPU_loaded_data */
+ REENTRANT_CHECK(OFF);
+ FPU_loaded_data.sign =
+ ((unsigned char) fubyte((unsigned char *) s + 9)) & 0x80 ?
+ SIGN_NEG : SIGN_POS;
+ REENTRANT_CHECK(ON);
+
+ if (l == 0) {
+ char sign = FPU_loaded_data.sign;
+ reg_move(&CONST_Z, &FPU_loaded_data);
+ FPU_loaded_data.sign = sign;
+ } else {
+ *((long long *) &FPU_loaded_data.sigl) = l;
+ FPU_loaded_data.exp = EXP_BIAS + 63;
+ FPU_loaded_data.tag = TW_Valid;
+ normalize_nuo(&FPU_loaded_data);
+ }
+}
+/*===========================================================================*/
+
+/* Put a long double into user memory */
+int
+reg_store_extended(void)
+{
+ long double *d = (long double *) FPU_data_address;
+ long e = FPU_st0_ptr->exp - EXP_BIAS + EXTENDED_Ebias;
+ unsigned short sign = FPU_st0_ptr->sign * 0x8000;
+ unsigned long ls, ms;
+
+
+ if (FPU_st0_tag == TW_Valid) {
+ if (e >= 0x7fff) {
+ EXCEPTION(EX_Overflow); /* Overflow */
+ /* This is a special case: see sec 16.2.5.1 of the
+ * 80486 book */
+ if (control_word & EX_Overflow) {
+ /* Overflow to infinity */
+ ls = 0;
+ ms = 0x80000000;
+ e = 0x7fff;
+ } else
+ return 0;
+ } else
+ if (e <= 0) {
+ if (e > -63) {
+ /* Correctly format the de-normal */
+ int precision_loss;
+ FPU_REG tmp;
+
+ EXCEPTION(EX_Denormal);
+ reg_move(FPU_st0_ptr, &tmp);
+ tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 62 */
+ if ((precision_loss = round_to_int(&tmp))) {
+ EXCEPTION(EX_Underflow | precision_loss);
+ /* This is a special case: see
+ * sec 16.2.5.1 of the 80486
+ * book */
+ if (!(control_word & EX_Underflow))
+ return 0;
+ }
+ e = 0;
+ ls = tmp.sigl;
+ ms = tmp.sigh;
+ } else {
+ /* ****** ??? This should not be
+ * possible */
+ EXCEPTION(EX_Underflow); /* Underflow */
+ /* This is a special case: see sec
+ * 16.2.5.1 of the 80486 book */
+ if (control_word & EX_Underflow) {
+ /* Underflow to zero */
+ ls = 0;
+ ms = 0;
+ e = FPU_st0_ptr->sign == SIGN_POS ? 0x7fff : 0xffff;
+ } else
+ return 0;
+ }
+ } else {
+ ls = FPU_st0_ptr->sigl;
+ ms = FPU_st0_ptr->sigh;
+ }
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ ls = ms = 0;
+ e = 0;
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ ls = 0;
+ ms = 0x80000000;
+ e = 0x7fff;
+ } else
+ if (FPU_st0_tag == TW_NaN) {
+ ls = FPU_st0_ptr->sigl;
+ ms = FPU_st0_ptr->sigh;
+ e = 0x7fff;
+ } else
+ if (FPU_st0_tag == TW_Empty) {
+ /* Empty register (stack
+ * underflow) */
+ EXCEPTION(EX_StackUnder);
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ /* Put out the QNaN
+ * indefinite */
+ ls = 0;
+ ms = 0xc0000000;
+ e = 0xffff;
+ } else
+ return 0;
+ } else {
+ /* We don't use TW_Denormal
+ * yet ... perhaps never! */
+ EXCEPTION(EX_Invalid);
+ /* Store a NaN */
+ e = 0x7fff;
+ ls = 1;
+ ms = 0x80000000;
+ }
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, d, 10); */
+ suword((unsigned long *) d, ls);
+ suword(1 + (unsigned long *) d, ms);
+ suword(4 + (short *) d, (unsigned short) e | sign);
+ REENTRANT_CHECK(ON);
+
+ return 1;
+
+}
+
+
+/* Put a double into user memory */
+int
+reg_store_double(void)
+{
+ double *dfloat = (double *) FPU_data_address;
+ unsigned long l[2];
+ if (FPU_st0_tag == TW_Valid) {
+ int exp;
+ FPU_REG tmp;
+
+ reg_move(FPU_st0_ptr, &tmp);
+ exp = tmp.exp - EXP_BIAS;
+
+ if (exp < DOUBLE_Emin) { /* It may be a denormal */
+ /* Make a de-normal */
+ int precision_loss;
+
+ if (exp <= -EXTENDED_Ebias)
+ EXCEPTION(EX_Denormal);
+
+ tmp.exp += -DOUBLE_Emin + 52; /* largest exp to be 51 */
+
+ if ((precision_loss = round_to_int(&tmp))) {
+#ifdef PECULIAR_486
+ /* Did it round to a non-denormal ? */
+ /* This behaviour might be regarded as
+ * peculiar, it appears that the 80486 rounds
+ * to the dest precision, then converts to
+ * decide underflow. */
+ if ((tmp.sigh == 0x00100000) && (tmp.sigl == 0) &&
+ (FPU_st0_ptr->sigl & 0x000007ff))
+ EXCEPTION(precision_loss);
+ else
+#endif /* PECULIAR_486 */
+ {
+ EXCEPTION(EX_Underflow | precision_loss);
+ /* This is a special case: see sec
+ * 16.2.5.1 of the 80486 book */
+ if (!(control_word & EX_Underflow))
+ return 0;
+ }
+ }
+ l[0] = tmp.sigl;
+ l[1] = tmp.sigh;
+ } else {
+ if (tmp.sigl & 0x000007ff) {
+ unsigned long increment = 0; /* avoid gcc warnings */
+
+ switch (control_word & CW_RC) {
+ case RC_RND:
+ /* Rounding can get a little messy.. */
+ increment = ((tmp.sigl & 0x7ff) > 0x400) | /* nearest */
+ ((tmp.sigl & 0xc00) == 0xc00); /* odd -> even */
+ break;
+ case RC_DOWN: /* towards -infinity */
+ increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff;
+ break;
+ case RC_UP: /* towards +infinity */
+ increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0;
+ break;
+ case RC_CHOP:
+ increment = 0;
+ break;
+ }
+
+ /* Truncate the mantissa */
+ tmp.sigl &= 0xfffff800;
+
+ if (increment) {
+ set_precision_flag_up();
+
+ if (tmp.sigl >= 0xfffff800) {
+ /* the sigl part overflows */
+ if (tmp.sigh == 0xffffffff) {
+ /* The sigh part
+ * overflows */
+ tmp.sigh = 0x80000000;
+ exp++;
+ if (exp >= EXP_OVER)
+ goto overflow;
+ } else {
+ tmp.sigh++;
+ }
+ tmp.sigl = 0x00000000;
+ } else {
+ /* We only need to increment
+ * sigl */
+ tmp.sigl += 0x00000800;
+ }
+ } else
+ set_precision_flag_down();
+ }
+ l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
+ l[1] = ((tmp.sigh >> 11) & 0xfffff);
+
+ if (exp > DOUBLE_Emax) {
+ overflow:
+ EXCEPTION(EX_Overflow);
+ /* This is a special case: see sec 16.2.5.1 of
+ * the 80486 book */
+ if (control_word & EX_Overflow) {
+ /* Overflow to infinity */
+ l[0] = 0x00000000; /* Set to */
+ l[1] = 0x7ff00000; /* + INF */
+ } else
+ return 0;
+ } else {
+ /* Add the exponent */
+ l[1] |= (((exp + DOUBLE_Ebias) & 0x7ff) << 20);
+ }
+ }
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ /* Number is zero */
+ l[0] = 0;
+ l[1] = 0;
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ l[0] = 0;
+ l[1] = 0x7ff00000;
+ } else
+ if (FPU_st0_tag == TW_NaN) {
+ /* See if we can get a valid NaN from
+ * the FPU_REG */
+ l[0] = (FPU_st0_ptr->sigl >> 11) | (FPU_st0_ptr->sigh << 21);
+ l[1] = ((FPU_st0_ptr->sigh >> 11) & 0xfffff);
+ if (!(l[0] | l[1])) {
+ /* This case does not seem to
+ * be handled by the 80486
+ * specs */
+ EXCEPTION(EX_Invalid);
+ /* Make the quiet NaN "real
+ * indefinite" */
+ goto put_indefinite;
+ }
+ l[1] |= 0x7ff00000;
+ } else
+ if (FPU_st0_tag == TW_Empty) {
+ /* Empty register (stack
+ * underflow) */
+ EXCEPTION(EX_StackUnder);
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ /* Put out the QNaN
+ * indefinite */
+ put_indefinite:
+ REENTRANT_CHECK(OFF);
+ /* verify_area(VERIFY_W
+ * RITE, (void *)
+ * dfloat, 8); */
+ suword((unsigned long *) dfloat, 0);
+ suword(1 + (unsigned long *) dfloat, 0xfff80000);
+ REENTRANT_CHECK(ON);
+ return 1;
+ } else
+ return 0;
+ }
+#if 0 /* TW_Denormal is not used yet, and probably
+ * won't be */
+ else
+ if (FPU_st0_tag == TW_Denormal) {
+ /* Extended real ->
+ * double real will
+ * always underflow */
+ l[0] = l[1] = 0;
+ EXCEPTION(EX_Underflow);
+ }
+#endif
+ if (FPU_st0_ptr->sign)
+ l[1] |= 0x80000000;
+
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, (void *) dfloat, 8);*/
+ suword((u_long *) dfloat, l[0]);
+ suword((u_long *) dfloat + 1, l[1]);
+/*
+ suword(l[0], (unsigned long *) dfloat);
+ suword(l[1], 1 + (unsigned long *) dfloat);*/
+ REENTRANT_CHECK(ON);
+
+ return 1;
+}
+
+
+/* Put a float into user memory */
+int
+reg_store_single(void)
+{
+ float *single = (float *) FPU_data_address;
+ long templ;
+
+ if (FPU_st0_tag == TW_Valid) {
+ int exp;
+ FPU_REG tmp;
+
+ reg_move(FPU_st0_ptr, &tmp);
+ exp = tmp.exp - EXP_BIAS;
+
+ if (exp < SINGLE_Emin) {
+ /* Make a de-normal */
+ int precision_loss;
+
+ if (exp <= -EXTENDED_Ebias)
+ EXCEPTION(EX_Denormal);
+
+ tmp.exp += -SINGLE_Emin + 23; /* largest exp to be 22 */
+
+ if ((precision_loss = round_to_int(&tmp))) {
+#ifdef PECULIAR_486
+ /* Did it round to a non-denormal ? */
+ /* This behaviour might be regarded as
+ * peculiar, it appears that the 80486 rounds
+ * to the dest precision, then converts to
+ * decide underflow. */
+ if ((tmp.sigl == 0x00800000) &&
+ ((FPU_st0_ptr->sigh & 0x000000ff) || FPU_st0_ptr->sigl))
+ EXCEPTION(precision_loss);
+ else
+#endif /* PECULIAR_486 */
+ {
+ EXCEPTION(EX_Underflow | precision_loss);
+ /* This is a special case: see sec
+ * 16.2.5.1 of the 80486 book */
+ if (!(control_word & EX_Underflow))
+ return 0;
+ }
+ }
+ templ = tmp.sigl;
+ } else {
+ if (tmp.sigl | (tmp.sigh & 0x000000ff)) {
+ unsigned long increment = 0; /* avoid gcc warnings */
+ unsigned long sigh = tmp.sigh;
+ unsigned long sigl = tmp.sigl;
+
+ switch (control_word & CW_RC) {
+ case RC_RND:
+ increment = ((sigh & 0xff) > 0x80) /* more than half */
+ ||(((sigh & 0xff) == 0x80) && sigl) /* more than half */
+ ||((sigh & 0x180) == 0x180); /* round to even */
+ break;
+ case RC_DOWN: /* towards -infinity */
+ increment = (tmp.sign == SIGN_POS)
+ ? 0 : (sigl | (sigh & 0xff));
+ break;
+ case RC_UP: /* towards +infinity */
+ increment = (tmp.sign == SIGN_POS)
+ ? (sigl | (sigh & 0xff)) : 0;
+ break;
+ case RC_CHOP:
+ increment = 0;
+ break;
+ }
+
+ /* Truncate part of the mantissa */
+ tmp.sigl = 0;
+
+ if (increment) {
+ set_precision_flag_up();
+
+ if (sigh >= 0xffffff00) {
+ /* The sigh part overflows */
+ tmp.sigh = 0x80000000;
+ exp++;
+ if (exp >= EXP_OVER)
+ goto overflow;
+ } else {
+ tmp.sigh &= 0xffffff00;
+ tmp.sigh += 0x100;
+ }
+ } else {
+ set_precision_flag_down();
+ tmp.sigh &= 0xffffff00; /* Finish the truncation */
+ }
+ }
+ templ = (tmp.sigh >> 8) & 0x007fffff;
+
+ if (exp > SINGLE_Emax) {
+ overflow:
+ EXCEPTION(EX_Overflow);
+ /* This is a special case: see sec 16.2.5.1 of
+ * the 80486 book */
+ if (control_word & EX_Overflow) {
+ /* Overflow to infinity */
+ templ = 0x7f800000;
+ } else
+ return 0;
+ } else
+ templ |= ((exp + SINGLE_Ebias) & 0xff) << 23;
+ }
+ } else
+ if (FPU_st0_tag == TW_Zero) {
+ templ = 0;
+ } else
+ if (FPU_st0_tag == TW_Infinity) {
+ templ = 0x7f800000;
+ } else
+ if (FPU_st0_tag == TW_NaN) {
+ /* See if we can get a valid NaN from
+ * the FPU_REG */
+ templ = FPU_st0_ptr->sigh >> 8;
+ if (!(templ & 0x3fffff)) {
+ /* This case does not seem to
+ * be handled by the 80486
+ * specs */
+ EXCEPTION(EX_Invalid);
+ /* Make the quiet NaN "real
+ * indefinite" */
+ goto put_indefinite;
+ }
+ templ |= 0x7f800000;
+ } else
+ if (FPU_st0_tag == TW_Empty) {
+ /* Empty register (stack
+ * underflow) */
+ EXCEPTION(EX_StackUnder);
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ /* Put out the QNaN
+ * indefinite */
+ put_indefinite:
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, (void *) single, 4); */
+ suword((unsigned long *) single, 0xffc00000);
+ REENTRANT_CHECK(ON);
+ return 1;
+ } else
+ return 0;
+ }
+#if 0 /* TW_Denormal is not used yet, and probably
+ * won't be */
+ else
+ if (FPU_st0_tag == TW_Denormal) {
+ /* Extended real ->
+ * real will always
+ * underflow */
+ templ = 0;
+ EXCEPTION(EX_Underflow);
+ }
+#endif
+#ifdef PARANOID
+ else {
+ EXCEPTION(EX_INTERNAL | 0x106);
+ return 0;
+ }
+#endif
+ if (FPU_st0_ptr->sign)
+ templ |= 0x80000000;
+
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, (void *) single, 4); */
+ suword((unsigned long *) single, templ);
+ REENTRANT_CHECK(ON);
+
+ return 1;
+}
+
+
+/* Put a long long into user memory */
+int
+reg_store_int64(void)
+{
+ long long *d = (long long *) FPU_data_address;
+ FPU_REG t;
+ long long tll;
+
+ if (FPU_st0_tag == TW_Empty) {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ goto put_indefinite;
+ } else
+ return 0;
+ }
+ reg_move(FPU_st0_ptr, &t);
+ round_to_int(&t);
+ ((long *) &tll)[0] = t.sigl;
+ ((long *) &tll)[1] = t.sigh;
+ if ((t.sigh & 0x80000000) &&
+ !((t.sigh == 0x80000000) && (t.sigl == 0) && (t.sign == SIGN_NEG))) {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if (control_word & EX_Invalid) {
+ /* Produce "indefinite" */
+ put_indefinite:
+ ((long *) &tll)[1] = 0x80000000;
+ ((long *) &tll)[0] = 0;
+ } else
+ return 0;
+ } else
+ if (t.sign)
+ tll = -tll;
+
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, (void *) d, 8); */
+ suword((unsigned long *) d, ((long *) &tll)[0]);
+ suword(1 + (unsigned long *) d, ((long *) &tll)[1]);
+ REENTRANT_CHECK(ON);
+
+ return 1;
+}
+
+
+/* Put a long into user memory */
+int
+reg_store_int32(void)
+{
+ long *d = (long *) FPU_data_address;
+ FPU_REG t;
+
+ if (FPU_st0_tag == TW_Empty) {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, d, 4);*/
+ suword((unsigned long *) d, 0x80000000);
+ REENTRANT_CHECK(ON);
+ return 1;
+ } else
+ return 0;
+ }
+ reg_move(FPU_st0_ptr, &t);
+ round_to_int(&t);
+ if (t.sigh ||
+ ((t.sigl & 0x80000000) &&
+ !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG)))) {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if (control_word & EX_Invalid) {
+ /* Produce "indefinite" */
+ t.sigl = 0x80000000;
+ } else
+ return 0;
+ } else
+ if (t.sign)
+ t.sigl = -(long) t.sigl;
+
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, d, 4); */
+ suword((unsigned long *) d, t.sigl);
+ REENTRANT_CHECK(ON);
+
+ return 1;
+}
+
+
+/* Put a short into user memory */
+int
+reg_store_int16(void)
+{
+ short *d = (short *) FPU_data_address;
+ FPU_REG t;
+ short ts;
+
+ if (FPU_st0_tag == TW_Empty) {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, d, 2);*/
+ suword((unsigned short *) d, 0x8000);
+ REENTRANT_CHECK(ON);
+ return 1;
+ } else
+ return 0;
+ }
+ reg_move(FPU_st0_ptr, &t);
+ round_to_int(&t);
+ if (t.sigh ||
+ ((t.sigl & 0xffff8000) &&
+ !((t.sigl == 0x8000) && (t.sign == SIGN_NEG)))) {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if (control_word & EX_Invalid) {
+ /* Produce "indefinite" */
+ ts = 0x8000;
+ } else
+ return 0;
+ } else
+ if (t.sign)
+ t.sigl = -t.sigl;
+
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, d, 2); */
+ suword((short *) d, (short) t.sigl);
+ REENTRANT_CHECK(ON);
+
+ return 1;
+}
+
+
+/* Put a packed bcd array into user memory */
+int
+reg_store_bcd(void)
+{
+ char *d = (char *) FPU_data_address;
+ FPU_REG t;
+ long long ll;
+ unsigned char b;
+ int i;
+ unsigned char sign = (FPU_st0_ptr->sign == SIGN_NEG) ? 0x80 : 0;
+
+ if (FPU_st0_tag == TW_Empty) {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if (control_word & EX_Invalid) {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ goto put_indefinite;
+ } else
+ return 0;
+ }
+ reg_move(FPU_st0_ptr, &t);
+ round_to_int(&t);
+ ll = *(long long *) (&t.sigl);
+
+ /* Check for overflow, by comparing with 999999999999999999 decimal. */
+ if ((t.sigh > 0x0de0b6b3) ||
+ ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff))) {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if (control_word & EX_Invalid) {
+ put_indefinite:
+ /* Produce "indefinite" */
+ REENTRANT_CHECK(OFF);
+/* verify_area(VERIFY_WRITE, d, 10);*/
+ subyte((unsigned char *) d + 7, 0xff);
+ subyte((unsigned char *) d + 8, 0xff);
+ subyte((unsigned char *) d + 9, 0xff);
+ REENTRANT_CHECK(ON);
+ return 1;
+ } else
+ return 0;
+ }
+/* verify_area(VERIFY_WRITE, d, 10);*/
+ for (i = 0; i < 9; i++) {
+ b = div_small(&ll, 10);
+ b |= (div_small(&ll, 10)) << 4;
+ REENTRANT_CHECK(OFF);
+ subyte((unsigned char *) d + i, b);
+ REENTRANT_CHECK(ON);
+ }
+ REENTRANT_CHECK(OFF);
+ subyte((unsigned char *) d + 9, sign);
+ REENTRANT_CHECK(ON);
+
+ return 1;
+}
+/*===========================================================================*/
+
+/* r gets mangled such that sig is int, sign:
+ it is NOT normalized */
+/* The return value (in eax) is zero if the result is exact,
+ if bits are changed due to rounding, truncation, etc, then
+ a non-zero value is returned */
+/* Overflow is signalled by a non-zero return value (in eax).
+ In the case of overflow, the returned significand always has the
+ the largest possible value */
+/* The value returned in eax is never actually needed :-) */
+int
+round_to_int(FPU_REG * r)
+{
+ char very_big;
+ unsigned eax;
+
+ if (r->tag == TW_Zero) {
+ /* Make sure that zero is returned */
+ *(long long *) &r->sigl = 0;
+ return 0; /* o.k. */
+ }
+ if (r->exp > EXP_BIAS + 63) {
+ r->sigl = r->sigh = ~0; /* The largest representable number */
+ return 1; /* overflow */
+ }
+ eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp);
+ very_big = !(~(r->sigh) | ~(r->sigl)); /* test for 0xfff...fff */
+#define half_or_more (eax & 0x80000000)
+#define frac_part (eax)
+#define more_than_half ((eax & 0x80000001) == 0x80000001)
+ switch (control_word & CW_RC) {
+ case RC_RND:
+ if (more_than_half /* nearest */
+ || (half_or_more && (r->sigl & 1))) { /* odd -> even */
+ if (very_big)
+ return 1; /* overflow */
+ (*(long long *) (&r->sigl))++;
+ return LOST_UP;
+ }
+ break;
+ case RC_DOWN:
+ if (frac_part && r->sign) {
+ if (very_big)
+ return 1; /* overflow */
+ (*(long long *) (&r->sigl))++;
+ return LOST_UP;
+ }
+ break;
+ case RC_UP:
+ if (frac_part && !r->sign) {
+ if (very_big)
+ return 1; /* overflow */
+ (*(long long *) (&r->sigl))++;
+ return LOST_UP;
+ }
+ break;
+ case RC_CHOP:
+ break;
+ }
+
+ return eax ? LOST_DOWN : 0;
+
+}
+/*===========================================================================*/
+
+char *
+fldenv(void)
+{
+ char *s = (char *) FPU_data_address;
+ unsigned short tag_word = 0;
+ unsigned char tag;
+ int i;
+
+ REENTRANT_CHECK(OFF);
+ control_word = fuword((unsigned short *) s);
+ status_word = fuword((unsigned short *) (s + 4));
+ tag_word = fuword((unsigned short *) (s + 8));
+ ip_offset = fuword((unsigned long *) (s + 0x0c));
+ cs_selector = fuword((unsigned long *) (s + 0x10));
+ data_operand_offset = fuword((unsigned long *) (s + 0x14));
+ operand_selector = fuword((unsigned long *) (s + 0x18));
+ REENTRANT_CHECK(ON);
+
+ top = (status_word >> SW_Top_Shift) & 7;
+
+ for (i = 0; i < 8; i++) {
+ tag = tag_word & 3;
+ tag_word >>= 2;
+
+ switch (tag) {
+ case 0:
+ regs[i].tag = TW_Valid;
+ break;
+ case 1:
+ regs[i].tag = TW_Zero;
+ break;
+ case 2:
+ regs[i].tag = TW_NaN;
+ break;
+ case 3:
+ regs[i].tag = TW_Empty;
+ break;
+ }
+ }
+
+ FPU_data_address = (void *) data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+
+ return s + 0x1c;
+}
+
+
+void
+frstor(void)
+{
+ int i, stnr;
+ unsigned char tag;
+ unsigned short saved_status, saved_control;
+ char *s = (char *) fldenv();
+
+ saved_status = status_word;
+ saved_control = control_word;
+ control_word = 0x037f; /* Mask all interrupts while we load. */
+ for (i = 0; i < 8; i++) {
+ /* load each register */
+ FPU_data_address = (void *) (s + i * 10);
+ reg_load_extended();
+ stnr = (i + top) & 7;
+ tag = regs[stnr].tag; /* derived from the loaded tag word */
+ reg_move(&FPU_loaded_data, &regs[stnr]);
+ if (tag == TW_NaN) {
+ /* The current data is a special, i.e. NaN,
+ * unsupported, infinity, or denormal */
+ unsigned char t = regs[stnr].tag; /* derived from the new
+ * data */
+ if ( /* (t == TW_Valid) || *** */ (t == TW_Zero))
+ regs[stnr].tag = TW_NaN;
+ } else
+ regs[stnr].tag = tag;
+ }
+ control_word = saved_control;
+ status_word = saved_status;
+
+ FPU_data_address = (void *) data_operand_offset; /* We want no net effect */
+}
+
+
+unsigned short
+tag_word(void)
+{
+ unsigned short word = 0;
+ unsigned char tag;
+ int i;
+
+ for (i = 7; i >= 0; i--) {
+ switch (tag = regs[i].tag) {
+#if 0 /* TW_Denormal is not used yet, and probably
+ * won't be */
+ case TW_Denormal:
+#endif
+ case TW_Valid:
+ if (regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias))
+ tag = 2;
+ break;
+ case TW_Infinity:
+ case TW_NaN:
+ tag = 2;
+ break;
+ case TW_Empty:
+ tag = 3;
+ break;
+ /* TW_Valid and TW_Zero already have the correct value */
+ }
+ word <<= 2;
+ word |= tag;
+ }
+ return word;
+}
+
+
+char *
+fstenv(void)
+{
+ char *d = (char *) FPU_data_address;
+
+/* verify_area(VERIFY_WRITE, d, 28);*/
+
+#if 0 /****/
+ *(unsigned short *) &cs_selector = fpu_cs;
+ *(unsigned short *) &operand_selector = fpu_os;
+#endif /****/
+
+ REENTRANT_CHECK(OFF);
+ suword((unsigned short *) d, control_word);
+ suword((unsigned short *) (d + 4), (status_word & ~SW_Top) | ((top & 7) << SW_Top_Shift));
+ suword((unsigned short *) (d + 8), tag_word());
+ suword((unsigned long *) (d + 0x0c), ip_offset);
+ suword((unsigned long *) (d + 0x10), cs_selector);
+ suword((unsigned long *) (d + 0x14), data_operand_offset);
+ suword((unsigned long *) (d + 0x18), operand_selector);
+ REENTRANT_CHECK(ON);
+
+ return d + 0x1c;
+}
+
+
+void
+fsave(void)
+{
+ char *d;
+ FPU_REG tmp, *rp;
+ int i;
+ short e;
+
+ d = fstenv();
+/* verify_area(VERIFY_WRITE, d, 80);*/
+ for (i = 0; i < 8; i++) {
+ /* Store each register in the order: st(0), st(1), ... */
+ rp = &regs[(top + i) & 7];
+
+ e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
+
+ if (rp->tag == TW_Valid) {
+ if (e >= 0x7fff) {
+ /* Overflow to infinity */
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), 0);
+ suword((unsigned long *) (d + i * 10 + 4), 0);
+ REENTRANT_CHECK(ON);
+ e = 0x7fff;
+ } else
+ if (e <= 0) {
+ if (e > -63) {
+ /* Make a de-normal */
+ reg_move(rp, &tmp);
+ tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 62 */
+ round_to_int(&tmp);
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), tmp.sigl);
+ suword((unsigned long *) (d + i * 10 + 4), tmp.sigh);
+ REENTRANT_CHECK(ON);
+ } else {
+ /* Underflow to zero */
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), 0);
+ suword((unsigned long *) (d + i * 10 + 4), 0);
+ REENTRANT_CHECK(ON);
+ }
+ e = 0;
+ } else {
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), rp->sigl);
+ suword((unsigned long *) (d + i * 10 + 4), rp->sigh);
+ REENTRANT_CHECK(ON);
+ }
+ } else
+ if (rp->tag == TW_Zero) {
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), 0);
+ suword((unsigned long *) (d + i * 10 + 4), 0);
+ REENTRANT_CHECK(ON);
+ e = 0;
+ } else
+ if (rp->tag == TW_Infinity) {
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), 0);
+ suword((unsigned long *) (d + i * 10 + 4), 0x80000000);
+ REENTRANT_CHECK(ON);
+ e = 0x7fff;
+ } else
+ if (rp->tag == TW_NaN) {
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), rp->sigl);
+ suword((unsigned long *) (d + i * 10 + 4), rp->sigh);
+ REENTRANT_CHECK(ON);
+ e = 0x7fff;
+ } else
+ if (rp->tag == TW_Empty) {
+ /* just copy the reg */
+ REENTRANT_CHECK(OFF);
+ suword((unsigned long *) (d + i * 10), rp->sigl);
+ suword((unsigned long *) (d + i * 10 + 4), rp->sigh);
+ REENTRANT_CHECK(ON);
+ }
+ e |= rp->sign == SIGN_POS ? 0 : 0x8000;
+ REENTRANT_CHECK(OFF);
+ suword((unsigned short *) (d + i * 10 + 8), e);
+ REENTRANT_CHECK(ON);
+ }
+
+ finit();
+
+}
+/*===========================================================================*/
diff --git a/sys/gnu/fpemul/reg_mul.c b/sys/gnu/fpemul/reg_mul.c
new file mode 100644
index 000000000000..5924e5e75df2
--- /dev/null
+++ b/sys/gnu/fpemul/reg_mul.c
@@ -0,0 +1,162 @@
+/*
+ * reg_mul.c
+ *
+ * Multiply one FPU_REG by another, put the result in a destination FPU_REG.
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_mul.c,v 1.3 1994/06/10 07:44:53 rich Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | The destination may be any FPU_REG, including one of the source FPU_REGs. |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "fpu_system.h"
+
+
+/* This routine must be called with non-empty source registers */
+void
+reg_mul(FPU_REG * a, FPU_REG * b, FPU_REG * dest, unsigned int control_w)
+{
+ char sign = (a->sign ^ b->sign);
+
+ if (!(a->tag | b->tag)) {
+ /* This should be the most common case */
+ reg_u_mul(a, b, dest, control_w);
+ dest->sign = sign;
+ return;
+ } else
+ if ((a->tag <= TW_Zero) && (b->tag <= TW_Zero)) {
+#ifdef DENORM_OPERAND
+ if (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ||
+ ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER))) {
+ if (denormal_operand())
+ return;
+ }
+#endif /* DENORM_OPERAND */
+ /* Must have either both arguments == zero, or one
+ * valid and the other zero. The result is therefore
+ * zero. */
+ reg_move(&CONST_Z, dest);
+#ifdef PECULIAR_486
+ /* The 80486 book says that the answer is +0, but a
+ * real 80486 appears to behave this way... */
+ dest->sign = sign;
+#endif /* PECULIAR_486 */
+ return;
+ }
+#if 0 /* TW_Denormal is not used yet... perhaps
+ * never will be. */
+ else
+ if ((a->tag <= TW_Denormal) && (b->tag <= TW_Denormal)) {
+ /* One or both arguments are de-normalized */
+ /* Internal de-normalized numbers are not
+ * supported yet */
+ EXCEPTION(EX_INTERNAL | 0x105);
+ reg_move(&CONST_Z, dest);
+ }
+#endif
+ else {
+ /* Must have infinities, NaNs, etc */
+ if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) {
+ real_2op_NaN(a, b, dest);
+ return;
+ } else
+ if (a->tag == TW_Infinity) {
+ if (b->tag == TW_Zero) {
+ arith_invalid(dest);
+ return;
+ }
+ /* Zero*Infinity is invalid */
+ else {
+#ifdef DENORM_OPERAND
+ if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(a, dest);
+ dest->sign = sign;
+ }
+ return;
+ } else
+ if (b->tag == TW_Infinity) {
+ if (a->tag == TW_Zero) {
+ arith_invalid(dest);
+ return;
+ }
+ /* Zero*Infinity is
+ * invalid */
+ else {
+#ifdef DENORM_OPERAND
+ if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand())
+ return;
+#endif /* DENORM_OPERAND */
+ reg_move(b, dest);
+ dest->sign = sign;
+ }
+ return;
+ }
+#ifdef PARANOID
+ else {
+ EXCEPTION(EX_INTERNAL | 0x102);
+ }
+#endif /* PARANOID */
+ }
+}
diff --git a/sys/gnu/fpemul/reg_norm.s b/sys/gnu/fpemul/reg_norm.s
new file mode 100644
index 000000000000..37b080e0271b
--- /dev/null
+++ b/sys/gnu/fpemul/reg_norm.s
@@ -0,0 +1,182 @@
+/*
+ * reg_norm.s
+ *
+ * Normalize the value in a FPU_REG.
+ *
+ * Call from C as:
+ * void normalize(FPU_REG *n)
+ *
+ * void normalize_nuo(FPU_REG *n)
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_norm.s,v 1.3 1994/06/10 07:44:54 rich Exp $
+ *
+ */
+
+
+#include "fpu_asm.h"
+
+
+.text
+
+ .align 2,144
+.globl _normalize
+
+_normalize:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %ebx
+
+ movl PARAM1,%ebx
+
+ movl SIGH(%ebx),%edx
+ movl SIGL(%ebx),%eax
+
+ orl %edx,%edx /* ms bits */
+ js L_done /* Already normalized */
+ jnz L_shift_1 /* Shift left 1 - 31 bits */
+
+ orl %eax,%eax
+ jz L_zero /* The contents are zero */
+
+/* L_shift_32: */
+ movl %eax,%edx
+ xorl %eax,%eax
+ subl $32,EXP(%ebx) /* This can cause an underflow */
+
+/* We need to shift left by 1 - 31 bits */
+L_shift_1:
+ bsrl %edx,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ shld %cl,%eax,%edx
+ shl %cl,%eax
+ subl %ecx,EXP(%ebx) /* This can cause an underflow */
+
+ movl %edx,SIGH(%ebx)
+ movl %eax,SIGL(%ebx)
+
+L_done:
+ cmpl EXP_OVER,EXP(%ebx)
+ jge L_overflow
+
+ cmpl EXP_UNDER,EXP(%ebx)
+ jle L_underflow
+
+L_exit:
+ popl %ebx
+ leave
+ ret
+
+
+L_zero:
+ movl EXP_UNDER,EXP(%ebx)
+ movb TW_Zero,TAG(%ebx)
+ jmp L_exit
+
+L_underflow:
+ push %ebx
+ call _arith_underflow
+ pop %ebx
+ jmp L_exit
+
+L_overflow:
+ push %ebx
+ call _arith_overflow
+ pop %ebx
+ jmp L_exit
+
+
+
+/* Normalise without reporting underflow or overflow */
+ .align 2,144
+.globl _normalize_nuo
+
+_normalize_nuo:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %ebx
+
+ movl PARAM1,%ebx
+
+ movl SIGH(%ebx),%edx
+ movl SIGL(%ebx),%eax
+
+ orl %edx,%edx /* ms bits */
+ js L_exit /* Already normalized */
+ jnz L_nuo_shift_1 /* Shift left 1 - 31 bits */
+
+ orl %eax,%eax
+ jz L_zero /* The contents are zero */
+
+/* L_nuo_shift_32: */
+ movl %eax,%edx
+ xorl %eax,%eax
+ subl $32,EXP(%ebx) /* This can cause an underflow */
+
+/* We need to shift left by 1 - 31 bits */
+L_nuo_shift_1:
+ bsrl %edx,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ shld %cl,%eax,%edx
+ shl %cl,%eax
+ subl %ecx,EXP(%ebx) /* This can cause an underflow */
+
+ movl %edx,SIGH(%ebx)
+ movl %eax,SIGL(%ebx)
+ jmp L_exit
+
+
diff --git a/sys/gnu/fpemul/reg_round.s b/sys/gnu/fpemul/reg_round.s
new file mode 100644
index 000000000000..7490bbb31d7e
--- /dev/null
+++ b/sys/gnu/fpemul/reg_round.s
@@ -0,0 +1,653 @@
+ .file "reg_round.S"
+/*
+ * reg_round.S
+ *
+ * Rounding/truncation/etc for FPU basic arithmetic functions.
+ *
+ * This code has four possible entry points.
+ * The following must be entered by a jmp intruction:
+ * FPU_round, FPU_round_sqrt, and FPU_Arith_exit.
+ *
+ * The _round_reg entry point is intended to be used by C code.
+ * From C, call as:
+ * void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w)
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_round.s,v 1.3 1994/06/10 07:44:55 rich Exp $
+ *
+ */
+
+
+/*---------------------------------------------------------------------------+
+ | Four entry points. |
+ | |
+ | Needed by both the FPU_round and FPU_round_sqrt entry points: |
+ | %eax:%ebx 64 bit significand |
+ | %edx 32 bit extension of the significand |
+ | %edi pointer to an FPU_REG for the result to be stored |
+ | stack calling function must have set up a C stack frame and |
+ | pushed %esi, %edi, and %ebx |
+ | |
+ | Needed just for the FPU_round_sqrt entry point: |
+ | %cx A control word in the same format as the FPU control word. |
+ | Otherwise, PARAM4 must give such a value. |
+ | |
+ | |
+ | The significand and its extension are assumed to be exact in the |
+ | following sense: |
+ | If the significand by itself is the exact result then the significand |
+ | extension (%edx) must contain 0, otherwise the significand extension |
+ | must be non-zero. |
+ | If the significand extension is non-zero then the significand is |
+ | smaller than the magnitude of the correct exact result by an amount |
+ | greater than zero and less than one ls bit of the significand. |
+ | The significand extension is only required to have three possible |
+ | non-zero values: |
+ | less than 0x80000000 <=> the significand is less than 1/2 an ls |
+ | bit smaller than the magnitude of the |
+ | true exact result. |
+ | exactly 0x80000000 <=> the significand is exactly 1/2 an ls bit |
+ | smaller than the magnitude of the true |
+ | exact result. |
+ | greater than 0x80000000 <=> the significand is more than 1/2 an ls |
+ | bit smaller than the magnitude of the |
+ | true exact result. |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | The code in this module has become quite complex, but it should handle |
+ | all of the FPU flags which are set at this stage of the basic arithmetic |
+ | computations. |
+ | There are a few rare cases where the results are not set identically to |
+ | a real FPU. These require a bit more thought because at this stage the |
+ | results of the code here appear to be more consistent... |
+ | This may be changed in a future version. |
+ +---------------------------------------------------------------------------*/
+
+
+#include "fpu_asm.h"
+#include "exception.h"
+#include "control_w.h"
+
+#define LOST_DOWN $1
+#define LOST_UP $2
+#define DENORMAL $1
+#define UNMASKED_UNDERFLOW $2
+
+.data
+ .align 2,0
+FPU_bits_lost:
+ .byte 0
+FPU_denormal:
+ .byte 0
+
+.text
+ .align 2,144
+.globl FPU_round
+.globl FPU_round_sqrt
+.globl FPU_Arith_exit
+.globl _round_reg
+
+/* Entry point when called from C */
+_round_reg:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%edi
+ movl SIGH(%edi),%eax
+ movl SIGL(%edi),%ebx
+ movl PARAM2,%edx
+ movl PARAM3,%ecx
+ jmp FPU_round_sqrt
+
+FPU_round: /* Normal entry point */
+ movl PARAM4,%ecx
+
+FPU_round_sqrt: /* Entry point from wm_sqrt.S */
+
+#ifdef PARANOID
+/* Cannot use this here yet */
+/* orl %eax,%eax */
+/* jns L_entry_bugged */
+#endif PARANOID
+
+ cmpl EXP_UNDER,EXP(%edi)
+ jle xMake_denorm /* The number is a de-normal*/
+
+ movb $0,FPU_denormal /* 0 -> not a de-normal*/
+
+xDenorm_done:
+ movb $0,FPU_bits_lost /*No bits yet lost in rounding*/
+
+ movl %ecx,%esi
+ andl CW_PC,%ecx
+ cmpl PR_64_BITS,%ecx
+ je LRound_To_64
+
+ cmpl PR_53_BITS,%ecx
+ je LRound_To_53
+
+ cmpl PR_24_BITS,%ecx
+ je LRound_To_24
+
+#ifdef PARANOID
+ jmp L_bugged /* There is no bug, just a bad control word */
+#endif PARANOID
+
+
+/* Round etc to 24 bit precision */
+LRound_To_24:
+ movl %esi,%ecx
+ andl CW_RC,%ecx
+ cmpl RC_RND,%ecx
+ je LRound_nearest_24
+
+ cmpl RC_CHOP,%ecx
+ je LCheck_truncate_24
+
+ cmpl RC_UP,%ecx /* Towards +infinity */
+ je LUp_24
+
+ cmpl RC_DOWN,%ecx /* Towards -infinity */
+ je LDown_24
+
+#ifdef PARANOID
+ jmp L_bugged
+#endif PARANOID
+
+LUp_24:
+ cmpb SIGN_POS,SIGN(%edi)
+ jne LCheck_truncate_24 /* If negative then up==truncate */
+
+ jmp LCheck_24_round_up
+
+LDown_24:
+ cmpb SIGN_POS,SIGN(%edi)
+ je LCheck_truncate_24 /* If positive then down==truncate */
+
+LCheck_24_round_up:
+ movl %eax,%ecx
+ andl $0x000000ff,%ecx
+ orl %ebx,%ecx
+ orl %edx,%ecx
+ jnz LDo_24_round_up
+ jmp LRe_normalise
+
+LRound_nearest_24:
+ /* Do rounding of the 24th bit if needed (nearest or even) */
+ movl %eax,%ecx
+ andl $0x000000ff,%ecx
+ cmpl $0x00000080,%ecx
+ jc LCheck_truncate_24 /*less than half, no increment needed*/
+
+ jne LGreater_Half_24 /* greater than half, increment needed*/
+
+ /* Possibly half, we need to check the ls bits */
+ orl %ebx,%ebx
+ jnz LGreater_Half_24 /* greater than half, increment needed*/
+
+ orl %edx,%edx
+ jnz LGreater_Half_24 /* greater than half, increment needed*/
+
+ /* Exactly half, increment only if 24th bit is 1 (round to even)*/
+ testl $0x00000100,%eax
+ jz LDo_truncate_24
+
+LGreater_Half_24: /*Rounding: increment at the 24th bit*/
+LDo_24_round_up:
+ andl $0xffffff00,%eax /*Truncate to 24 bits*/
+ xorl %ebx,%ebx
+ movb LOST_UP,FPU_bits_lost
+ addl $0x00000100,%eax
+ jmp LCheck_Round_Overflow
+
+LCheck_truncate_24:
+ movl %eax,%ecx
+ andl $0x000000ff,%ecx
+ orl %ebx,%ecx
+ orl %edx,%ecx
+ jz LRe_normalise /* No truncation needed*/
+
+LDo_truncate_24:
+ andl $0xffffff00,%eax /* Truncate to 24 bits*/
+ xorl %ebx,%ebx
+ movb LOST_DOWN,FPU_bits_lost
+ jmp LRe_normalise
+
+
+/* Round etc to 53 bit precision */
+LRound_To_53:
+ movl %esi,%ecx
+ andl CW_RC,%ecx
+ cmpl RC_RND,%ecx
+ je LRound_nearest_53
+
+ cmpl RC_CHOP,%ecx
+ je LCheck_truncate_53
+
+ cmpl RC_UP,%ecx /* Towards +infinity*/
+ je LUp_53
+
+ cmpl RC_DOWN,%ecx /* Towards -infinity*/
+ je LDown_53
+
+#ifdef PARANOID
+ jmp L_bugged
+#endif PARANOID
+
+LUp_53:
+ cmpb SIGN_POS,SIGN(%edi)
+ jne LCheck_truncate_53 /* If negative then up==truncate*/
+
+ jmp LCheck_53_round_up
+
+LDown_53:
+ cmpb SIGN_POS,SIGN(%edi)
+ je LCheck_truncate_53 /* If positive then down==truncate*/
+
+LCheck_53_round_up:
+ movl %ebx,%ecx
+ andl $0x000007ff,%ecx
+ orl %edx,%ecx
+ jnz LDo_53_round_up
+ jmp LRe_normalise
+
+LRound_nearest_53:
+ /*Do rounding of the 53rd bit if needed (nearest or even)*/
+ movl %ebx,%ecx
+ andl $0x000007ff,%ecx
+ cmpl $0x00000400,%ecx
+ jc LCheck_truncate_53 /* less than half, no increment needed*/
+
+ jnz LGreater_Half_53 /* greater than half, increment needed*/
+
+ /*Possibly half, we need to check the ls bits*/
+ orl %edx,%edx
+ jnz LGreater_Half_53 /* greater than half, increment needed*/
+
+ /* Exactly half, increment only if 53rd bit is 1 (round to even)*/
+ testl $0x00000800,%ebx
+ jz LTruncate_53
+
+LGreater_Half_53: /*Rounding: increment at the 53rd bit*/
+LDo_53_round_up:
+ movb LOST_UP,FPU_bits_lost
+ andl $0xfffff800,%ebx /* Truncate to 53 bits*/
+ addl $0x00000800,%ebx
+ adcl $0,%eax
+ jmp LCheck_Round_Overflow
+
+LCheck_truncate_53:
+ movl %ebx,%ecx
+ andl $0x000007ff,%ecx
+ orl %edx,%ecx
+ jz LRe_normalise
+
+LTruncate_53:
+ movb LOST_DOWN,FPU_bits_lost
+ andl $0xfffff800,%ebx /* Truncate to 53 bits*/
+ jmp LRe_normalise
+
+
+/* Round etc to 64 bit precision*/
+LRound_To_64:
+ movl %esi,%ecx
+ andl CW_RC,%ecx
+ cmpl RC_RND,%ecx
+ je LRound_nearest_64
+
+ cmpl RC_CHOP,%ecx
+ je LCheck_truncate_64
+
+ cmpl RC_UP,%ecx /* Towards +infinity*/
+ je LUp_64
+
+ cmpl RC_DOWN,%ecx /* Towards -infinity*/
+ je LDown_64
+
+#ifdef PARANOID
+ jmp L_bugged
+#endif PARANOID
+
+LUp_64:
+ cmpb SIGN_POS,SIGN(%edi)
+ jne LCheck_truncate_64 /* If negative then up==truncate*/
+
+ orl %edx,%edx
+ jnz LDo_64_round_up
+ jmp LRe_normalise
+
+LDown_64:
+ cmpb SIGN_POS,SIGN(%edi)
+ je LCheck_truncate_64 /*If positive then down==truncate*/
+
+ orl %edx,%edx
+ jnz LDo_64_round_up
+ jmp LRe_normalise
+
+LRound_nearest_64:
+ cmpl $0x80000000,%edx
+ jc LCheck_truncate_64
+
+ jne LDo_64_round_up
+
+ /* Now test for round-to-even */
+ testb $1,%ebx
+ jz LCheck_truncate_64
+
+LDo_64_round_up:
+ movb LOST_UP,FPU_bits_lost
+ addl $1,%ebx
+ adcl $0,%eax
+
+LCheck_Round_Overflow:
+ jnc LRe_normalise /* Rounding done, no overflow */
+
+ /* Overflow, adjust the result (to 1.0) */
+ rcrl $1,%eax
+ rcrl $1,%ebx
+ incl EXP(%edi)
+ jmp LRe_normalise
+
+LCheck_truncate_64:
+ orl %edx,%edx
+ jz LRe_normalise
+
+LTruncate_64:
+ movb LOST_DOWN,FPU_bits_lost
+
+LRe_normalise:
+ testb $0xff,FPU_denormal
+ jnz xNormalise_result
+
+xL_Normalised:
+ cmpb LOST_UP,FPU_bits_lost
+ je xL_precision_lost_up
+
+ cmpb LOST_DOWN,FPU_bits_lost
+ je xL_precision_lost_down
+
+xL_no_precision_loss:
+ cmpl EXP_OVER,EXP(%edi)
+ jge L_overflow
+
+ /* store the result */
+ movb TW_Valid,TAG(%edi)
+
+xL_Store_significand:
+ movl %eax,SIGH(%edi)
+ movl %ebx,SIGL(%edi)
+
+FPU_Arith_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+
+
+/* Set the FPU status flags to represent precision loss due to*/
+/* round-up.*/
+xL_precision_lost_up:
+ push %eax
+ call _set_precision_flag_up
+ popl %eax
+ jmp xL_no_precision_loss
+
+/* Set the FPU status flags to represent precision loss due to*/
+/* truncation.*/
+xL_precision_lost_down:
+ push %eax
+ call _set_precision_flag_down
+ popl %eax
+ jmp xL_no_precision_loss
+
+
+/* The number is a denormal (which might get rounded up to a normal)
+// Shift the number right the required number of bits, which will
+// have to be undone later...*/
+xMake_denorm:
+ /* The action to be taken depends upon whether the underflow
+ // exception is masked*/
+ testb CW_Underflow,%cl /* Underflow mask.*/
+ jz xUnmasked_underflow /* Do not make a denormal.*/
+
+ movb DENORMAL,FPU_denormal
+
+ pushl %ecx /* Save*/
+ movl EXP(%edi),%ecx
+ subl EXP_UNDER+1,%ecx
+ negl %ecx
+
+ cmpl $64,%ecx /* shrd only works for 0..31 bits */
+ jnc xDenorm_shift_more_than_63
+
+ cmpl $32,%ecx /* shrd only works for 0..31 bits */
+ jnc xDenorm_shift_more_than_32
+
+/* We got here without jumps by assuming that the most common requirement
+// is for a small de-normalising shift.
+// Shift by [1..31] bits */
+ addl %ecx,EXP(%edi)
+ orl %edx,%edx /* extension*/
+ setne %ch
+ xorl %edx,%edx
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ orb %ch,%dl
+ popl %ecx
+ jmp xDenorm_done
+
+/* Shift by [32..63] bits*/
+xDenorm_shift_more_than_32:
+ addl %ecx,EXP(%edi)
+ subb $32,%cl
+ orl %edx,%edx
+ setne %ch
+ orb %ch,%bl
+ xorl %edx,%edx
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ orl %edx,%edx /*test these 32 bits*/
+ setne %cl
+ orb %ch,%bl
+ orb %cl,%bl
+ movl %ebx,%edx
+ movl %eax,%ebx
+ xorl %eax,%eax
+ popl %ecx
+ jmp xDenorm_done
+
+/* Shift by [64..) bits*/
+xDenorm_shift_more_than_63:
+ cmpl $64,%ecx
+ jne xDenorm_shift_more_than_64
+
+/* Exactly 64 bit shift*/
+ addl %ecx,EXP(%edi)
+ xorl %ecx,%ecx
+ orl %edx,%edx
+ setne %cl
+ orl %ebx,%ebx
+ setne %ch
+ orb %ch,%cl
+ orb %cl,%al
+ movl %eax,%edx
+ xorl %eax,%eax
+ xorl %ebx,%ebx
+ popl %ecx
+ jmp xDenorm_done
+
+xDenorm_shift_more_than_64:
+ movl EXP_UNDER+1,EXP(%edi)
+/* This is easy, %eax must be non-zero, so..*/
+ movl $1,%edx
+ xorl %eax,%eax
+ xorl %ebx,%ebx
+ popl %ecx
+ jmp xDenorm_done
+
+
+xUnmasked_underflow:
+ /* Increase the exponent by the magic number*/
+ addl $(3*(1<<13)),EXP(%edi)
+ movb UNMASKED_UNDERFLOW,FPU_denormal
+ jmp xDenorm_done
+
+
+/* Undo the de-normalisation.*/
+xNormalise_result:
+ cmpb UNMASKED_UNDERFLOW,FPU_denormal
+ je xSignal_underflow
+
+/* The number must be a denormal if we got here.*/
+#ifdef PARANOID
+ /* But check it... just in case.*/
+ cmpl EXP_UNDER+1,EXP(%edi)
+ jne L_norm_bugged
+#endif PARANOID
+
+ orl %eax,%eax /* ms bits*/
+ jnz LNormalise_shift_up_to_31 /* Shift left 0 - 31 bits*/
+
+ orl %ebx,%ebx
+ jz L_underflow_to_zero /* The contents are zero*/
+
+/* Shift left 32 - 63 bits*/
+ movl %ebx,%eax
+ xorl %ebx,%ebx
+ subl $32,EXP(%edi)
+
+LNormalise_shift_up_to_31:
+ bsrl %eax,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ shld %cl,%ebx,%eax
+ shl %cl,%ebx
+ subl %ecx,EXP(%edi)
+
+LNormalise_shift_done:
+ testb $0xff,FPU_bits_lost /* bits lost == underflow*/
+ jz xL_Normalised
+
+ /* There must be a masked underflow*/
+ push %eax
+ pushl EX_Underflow
+ call _exception
+ popl %eax
+ popl %eax
+ jmp xL_Normalised
+
+
+/* The operations resulted in a number too small to represent.
+// Masked response.*/
+L_underflow_to_zero:
+ push %eax
+ call _set_precision_flag_down
+ popl %eax
+
+ push %eax
+ pushl EX_Underflow
+ call _exception
+ popl %eax
+ popl %eax
+
+ movb TW_Zero,TAG(%edi)
+ jmp xL_Store_significand
+
+
+/* The operations resulted in a number too large to represent.*/
+L_overflow:
+ push %edi
+ call _arith_overflow
+ pop %edi
+ jmp FPU_Arith_exit
+
+
+xSignal_underflow:
+ push %eax
+ pushl EX_Underflow
+ call EXCEPTION
+ popl %eax
+ popl %eax
+ jmp xL_Normalised
+
+
+#ifdef PARANOID
+/* If we ever get here then we have problems! */
+L_bugged:
+ pushl EX_INTERNAL|0x201
+ call EXCEPTION
+ popl %ebx
+ jmp FPU_Arith_exit
+
+L_norm_bugged:
+ pushl EX_INTERNAL|0x216
+ call EXCEPTION
+ popl %ebx
+ jmp FPU_Arith_exit
+
+L_entry_bugged:
+ pushl EX_INTERNAL|0x217
+ call EXCEPTION
+ popl %ebx
+ jmp FPU_Arith_exit
+#endif PARANOID
diff --git a/sys/gnu/fpemul/reg_u_add.s b/sys/gnu/fpemul/reg_u_add.s
new file mode 100644
index 000000000000..35b5025643d7
--- /dev/null
+++ b/sys/gnu/fpemul/reg_u_add.s
@@ -0,0 +1,244 @@
+ .file "reg_u_add.S"
+/*
+ * reg_u_add.S
+ *
+ * Add two valid (TW_Valid) FPU_REG numbers, of the same sign, and put the
+ * result in a destination FPU_REG.
+ *
+ * Call from C as:
+ * void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
+ * int control_w)
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_u_add.s,v 1.3 1994/06/10 07:44:56 rich Exp $
+ *
+ */
+
+
+/*
+ | Kernel addition routine reg_u_add(reg *arg1, reg *arg2, reg *answ).
+ | Takes two valid reg f.p. numbers (TW_Valid), which are
+ | treated as unsigned numbers,
+ | and returns their sum as a TW_Valid or TW_S f.p. number.
+ | The returned number is normalized.
+ | Basic checks are performed if PARANOID is defined.
+ */
+
+#include "exception.h"
+#include "fpu_asm.h"
+#include "control_w.h"
+
+.text
+ .align 2,144
+.globl _reg_u_add
+_reg_u_add:
+ pushl %ebp
+ movl %esp,%ebp
+/* subl $16,%esp*/
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi /* source 1 */
+ movl PARAM2,%edi /* source 2 */
+
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%esi)
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp1_not_denorm:
+ cmpl EXP_UNDER,EXP(%edi)
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
+/* xorl %ecx,%ecx*/
+ movl EXP(%esi),%ecx
+ subl EXP(%edi),%ecx /* exp1 - exp2 */
+/* jnc L_arg1_larger*/
+ jge L_arg1_larger
+
+ /* num1 is smaller */
+ movl SIGL(%esi),%ebx
+ movl SIGH(%esi),%eax
+
+ movl %edi,%esi
+ negw %cx
+ jmp L_accum_loaded
+
+L_arg1_larger:
+ /* num1 has larger or equal exponent */
+ movl SIGL(%edi),%ebx
+ movl SIGH(%edi),%eax
+
+L_accum_loaded:
+ movl PARAM3,%edi /* destination */
+ movb SIGN(%esi),%dl
+ movb %dl,SIGN(%edi) /* Copy the sign from the first arg */
+
+
+ movl EXP(%esi),%edx
+ movl %edx,EXP(%edi) /* Copy exponent to destination */
+
+ xorl %edx,%edx /* clear the extension */
+
+#ifdef PARANOID
+ testl $0x80000000,%eax
+ je L_bugged
+
+ testl $0x80000000,SIGH(%esi)
+ je L_bugged
+#endif PARANOID
+
+/* The number to be shifted is in %eax:%ebx:%edx*/
+ cmpw $32,%cx /* shrd only works for 0..31 bits */
+ jnc L_more_than_31
+
+/* less than 32 bits */
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ jmp L_shift_done
+
+L_more_than_31:
+ cmpw $64,%cx
+ jnc L_more_than_63
+
+ subb $32,%cl
+ jz L_exactly_32
+
+ shrd %cl,%eax,%edx
+ shr %cl,%eax
+ orl %ebx,%ebx
+ jz L_more_31_no_low /* none of the lowest bits is set*/
+
+ orl $1,%edx /* record the fact in the extension*/
+
+L_more_31_no_low:
+ movl %eax,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_exactly_32:
+ movl %ebx,%edx
+ movl %eax,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_more_than_63:
+ cmpw $65,%cx
+ jnc L_more_than_64
+
+ movl %eax,%edx
+ orl %ebx,%ebx
+ jz L_more_63_no_low
+
+ orl $1,%edx
+ jmp L_more_63_no_low
+
+L_more_than_64:
+ movl $1,%edx /* The shifted nr always at least one '1'*/
+
+L_more_63_no_low:
+ xorl %ebx,%ebx
+ xorl %eax,%eax
+
+L_shift_done:
+ /* Now do the addition */
+ addl SIGL(%esi),%ebx
+ adcl SIGH(%esi),%eax
+ jnc L_round_the_result
+
+ /* Overflow, adjust the result */
+ rcrl $1,%eax
+ rcrl $1,%ebx
+ rcrl $1,%edx
+ jnc L_no_bit_lost
+
+ orl $1,%edx
+
+L_no_bit_lost:
+ incl EXP(%edi)
+
+L_round_the_result:
+ jmp FPU_round /* Round the result*/
+
+
+
+#ifdef PARANOID
+/* If we ever get here then we have problems! */
+L_bugged:
+ pushl EX_INTERNAL|0x201
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+#endif PARANOID
+
+
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
diff --git a/sys/gnu/fpemul/reg_u_div.s b/sys/gnu/fpemul/reg_u_div.s
new file mode 100644
index 000000000000..7f61b057fd78
--- /dev/null
+++ b/sys/gnu/fpemul/reg_u_div.s
@@ -0,0 +1,506 @@
+ .file "reg_u_div.S"
+/*
+ * reg_u_div.S
+ *
+ * Core division routines
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_u_div.s,v 1.3 1994/06/10 07:44:57 rich Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | Kernel for the division routines. |
+ | |
+ | void reg_u_div(FPU_REG *a, FPU_REG *a, |
+ | FPU_REG *dest, unsigned int control_word) |
+ | |
+ | Does not compute the destination exponent, but does adjust it. |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+#include "control_w.h"
+
+
+/* #define dSIGL(x) (x) */
+/* #define dSIGH(x) 4(x) */
+
+
+.data
+/*
+ Local storage:
+ Result: accum_3:accum_2:accum_1:accum_0
+ Overflow flag: ovfl_flag
+ */
+ .align 2,0
+accum_3:
+ .long 0
+accum_2:
+ .long 0
+accum_1:
+ .long 0
+accum_0:
+ .long 0
+result_1:
+ .long 0
+result_2:
+ .long 0
+ovfl_flag:
+ .byte 0
+
+
+.text
+ .align 2,144
+
+.globl _reg_u_div
+
+.globl _divide_kernel
+
+_reg_u_div:
+ pushl %ebp
+ movl %esp,%ebp
+
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi /* pointer to num */
+ movl PARAM2,%ebx /* pointer to denom */
+ movl PARAM3,%edi /* pointer to answer */
+
+#ifdef DENORM_OPERAND
+ movl EXP(%esi),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp1_not_denorm:
+ movl EXP(%ebx),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
+_divide_kernel:
+#ifdef PARANOID
+/* testl $0x80000000, SIGH(%esi) *//* Dividend */
+/* je L_bugged */
+ testl $0x80000000, SIGH(%ebx) /* Divisor*/
+ je L_bugged
+#endif PARANOID
+
+/* Check if the divisor can be treated as having just 32 bits */
+ cmpl $0,SIGL(%ebx)
+ jnz L_Full_Division /* Can't do a quick divide */
+
+/* We should be able to zip through the division here */
+ movl SIGH(%ebx),%ecx /* The divisor */
+ movl SIGH(%esi),%edx /* Dividend */
+ movl SIGL(%esi),%eax /* Dividend */
+
+ cmpl %ecx,%edx
+ setaeb ovfl_flag /* Keep a record */
+ jb L_no_adjust
+
+ subl %ecx,%edx /* Prevent the overflow */
+
+L_no_adjust:
+ /* Divide the 64 bit number by the 32 bit denominator */
+ divl %ecx
+ movl %eax,result_2
+
+ /* Work on the remainder of the first division */
+ xorl %eax,%eax
+ divl %ecx
+ movl %eax,result_1
+
+ /* Work on the remainder of the 64 bit division */
+ xorl %eax,%eax
+ divl %ecx
+
+ testb $255,ovfl_flag /* was the num > denom ? */
+ je L_no_overflow
+
+ /* Do the shifting here */
+ /* increase the exponent */
+ incl EXP(%edi)
+
+ /* shift the mantissa right one bit */
+ stc /* To set the ms bit */
+ rcrl result_2
+ rcrl result_1
+ rcrl %eax
+
+L_no_overflow:
+ jmp LRound_precision /* Do the rounding as required*/
+
+
+/*---------------------------------------------------------------------------+
+ | Divide: Return arg1/arg2 to arg3. |
+ | |
+ | This routine does not use the exponents of arg1 and arg2, but does |
+ | adjust the exponent of arg3. |
+ | |
+ | The maximum returned value is (ignoring exponents) |
+ | .ffffffff ffffffff |
+ | ------------------ = 1.ffffffff fffffffe |
+ | .80000000 00000000 |
+ | and the minimum is |
+ | .80000000 00000000 |
+ | ------------------ = .80000000 00000001 (rounded) |
+ | .ffffffff ffffffff |
+ | |
+ +---------------------------------------------------------------------------*/
+
+
+L_Full_Division:
+ /* Save extended dividend in local register*/
+ movl SIGL(%esi),%eax
+ movl %eax,accum_2
+ movl SIGH(%esi),%eax
+ movl %eax,accum_3
+ xorl %eax,%eax
+ movl %eax,accum_1 /* zero the extension */
+ movl %eax,accum_0 /* zero the extension */
+
+ movl SIGL(%esi),%eax /* Get the current num */
+ movl SIGH(%esi),%edx
+
+/*----------------------------------------------------------------------*/
+/* Initialization done */
+/* Do the first 32 bits */
+
+ movb $0,ovfl_flag
+ cmpl SIGH(%ebx),%edx /* Test for imminent overflow */
+ jb LLess_than_1
+ ja LGreater_than_1
+
+ cmpl SIGL(%ebx),%eax
+ jb LLess_than_1
+
+LGreater_than_1:
+/* The dividend is greater or equal, would cause overflow */
+ setaeb ovfl_flag /* Keep a record */
+
+ subl SIGL(%ebx),%eax
+ sbbl SIGH(%ebx),%edx /* Prevent the overflow */
+ movl %eax,accum_2
+ movl %edx,accum_3
+
+LLess_than_1:
+/* At this point, we have a dividend < divisor, with a record of
+ adjustment in ovfl_flag */
+
+ /* We will divide by a number which is too large */
+ movl SIGH(%ebx),%ecx
+ addl $1,%ecx
+ jnc LFirst_div_not_1
+
+ /* here we need to divide by 100000000h,
+ i.e., no division at all.. */
+ mov %edx,%eax
+ jmp LFirst_div_done
+
+LFirst_div_not_1:
+ divl %ecx /* Divide the numerator by the augmented
+ denom ms dw */
+
+LFirst_div_done:
+ movl %eax,result_2 /* Put the result in the answer */
+
+ mull SIGH(%ebx) /* mul by the ms dw of the denom */
+
+ subl %eax,accum_2 /* Subtract from the num local reg */
+ sbbl %edx,accum_3
+
+ movl result_2,%eax /* Get the result back */
+ mull SIGL(%ebx) /* now mul the ls dw of the denom */
+
+ subl %eax,accum_1 /* Subtract from the num local reg */
+ sbbl %edx,accum_2
+ sbbl $0,accum_3
+ je LDo_2nd_32_bits /* Must check for non-zero result here */
+
+#ifdef PARANOID
+ jb L_bugged_1
+#endif PARANOID
+
+ /* need to subtract another once of the denom */
+ incl result_2 /* Correct the answer */
+
+ movl SIGL(%ebx),%eax
+ movl SIGH(%ebx),%edx
+ subl %eax,accum_1 /* Subtract from the num local reg */
+ sbbl %edx,accum_2
+
+#ifdef PARANOID
+ sbbl $0,accum_3
+ jne L_bugged_1 /* Must check for non-zero result here */
+#endif PARANOID
+
+/*----------------------------------------------------------------------*/
+/* Half of the main problem is done, there is just a reduced numerator
+ to handle now */
+/* Work with the second 32 bits, accum_0 not used from now on */
+LDo_2nd_32_bits:
+ movl accum_2,%edx /* get the reduced num */
+ movl accum_1,%eax
+
+ /* need to check for possible subsequent overflow */
+ cmpl SIGH(%ebx),%edx
+ jb LDo_2nd_div
+ ja LPrevent_2nd_overflow
+
+ cmpl SIGL(%ebx),%eax
+ jb LDo_2nd_div
+
+LPrevent_2nd_overflow:
+/* The numerator is greater or equal, would cause overflow */
+ /* prevent overflow */
+ subl SIGL(%ebx),%eax
+ sbbl SIGH(%ebx),%edx
+ movl %edx,accum_2
+ movl %eax,accum_1
+
+ incl result_2 /* Reflect the subtraction in the answer */
+
+#ifdef PARANOID
+ je L_bugged_2 /* Can't bump the result to 1.0 */
+#endif PARANOID
+
+LDo_2nd_div:
+ cmpl $0,%ecx /* augmented denom msw*/
+ jnz LSecond_div_not_1
+
+ /* %ecx == 0, we are dividing by 1.0 */
+ mov %edx,%eax
+ jmp LSecond_div_done
+
+LSecond_div_not_1:
+ divl %ecx /* Divide the numerator by the denom ms dw */
+
+LSecond_div_done:
+ movl %eax,result_1 /* Put the result in the answer */
+
+ mull SIGH(%ebx) /* mul by the ms dw of the denom */
+
+ subl %eax,accum_1 /* Subtract from the num local reg */
+ sbbl %edx,accum_2
+
+#ifdef PARANOID
+ jc L_bugged_2
+#endif PARANOID
+
+ movl result_1,%eax /* Get the result back */
+ mull SIGL(%ebx) /* now mul the ls dw of the denom */
+
+ subl %eax,accum_0 /* Subtract from the num local reg */
+ sbbl %edx,accum_1 /* Subtract from the num local reg */
+ sbbl $0,accum_2
+
+#ifdef PARANOID
+ jc L_bugged_2
+#endif PARANOID
+
+ jz LDo_3rd_32_bits
+
+#ifdef PARANOID
+ cmpl $1,accum_2
+ jne L_bugged_2
+#endif PARANOID
+
+ /* need to subtract another once of the denom */
+ movl SIGL(%ebx),%eax
+ movl SIGH(%ebx),%edx
+ subl %eax,accum_0 /* Subtract from the num local reg */
+ sbbl %edx,accum_1
+ sbbl $0,accum_2
+
+#ifdef PARANOID
+ jc L_bugged_2
+ jne L_bugged_2
+#endif PARANOID
+
+ addl $1,result_1 /* Correct the answer */
+ adcl $0,result_2
+
+#ifdef PARANOID
+ jc L_bugged_2 /* Must check for non-zero result here */
+#endif PARANOID
+
+/*----------------------------------------------------------------------*/
+/* The division is essentially finished here, we just need to perform
+ tidying operations. */
+/* deal with the 3rd 32 bits */
+LDo_3rd_32_bits:
+ movl accum_1,%edx /* get the reduced num */
+ movl accum_0,%eax
+
+ /* need to check for possible subsequent overflow */
+ cmpl SIGH(%ebx),%edx /* denom*/
+ jb LRound_prep
+ ja LPrevent_3rd_overflow
+
+ cmpl SIGL(%ebx),%eax /* denom */
+ jb LRound_prep
+
+LPrevent_3rd_overflow:
+ /* prevent overflow */
+ subl SIGL(%ebx),%eax
+ sbbl SIGH(%ebx),%edx
+ movl %edx,accum_1
+ movl %eax,accum_0
+
+ addl $1,result_1 /* Reflect the subtraction in the answer */
+ adcl $0,result_2
+ jne LRound_prep
+ jnc LRound_prep
+
+ /* This is a tricky spot, there is an overflow of the answer */
+ movb $255,ovfl_flag /* Overflow -> 1.000 */
+
+LRound_prep:
+/* Prepare for rounding.
+// To test for rounding, we just need to compare 2*accum with the
+// denom. */
+ movl accum_0,%ecx
+ movl accum_1,%edx
+ movl %ecx,%eax
+ orl %edx,%eax
+ jz LRound_ovfl /* The accumulator contains zero.*/
+
+ /* Multiply by 2 */
+ clc
+ rcll $1,%ecx
+ rcll $1,%edx
+ jc LRound_large /* No need to compare, denom smaller */
+
+ subl SIGL(%ebx),%ecx
+ sbbl SIGH(%ebx),%edx
+ jnc LRound_not_small
+
+ movl $0x70000000,%eax /* Denom was larger */
+ jmp LRound_ovfl
+
+LRound_not_small:
+ jnz LRound_large
+
+ movl $0x80000000,%eax /* Remainder was exactly 1/2 denom */
+ jmp LRound_ovfl
+
+LRound_large:
+ movl $0xff000000,%eax /* Denom was smaller */
+
+LRound_ovfl:
+/* We are now ready to deal with rounding, but first we must get
+ the bits properly aligned */
+ testb $255,ovfl_flag /* was the num > denom ? */
+ je LRound_precision
+
+ incl EXP(%edi)
+
+ /* shift the mantissa right one bit */
+ stc /* Will set the ms bit */
+ rcrl result_2
+ rcrl result_1
+ rcrl %eax
+
+/* Round the result as required */
+LRound_precision:
+ decl EXP(%edi) /* binary point between 1st & 2nd bits */
+
+ movl %eax,%edx
+ movl result_1,%ebx
+ movl result_2,%eax
+ jmp FPU_round
+
+
+#ifdef PARANOID
+/* The logic is wrong if we got here */
+L_bugged:
+ pushl EX_INTERNAL|0x202
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_1:
+ pushl EX_INTERNAL|0x203
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_2:
+ pushl EX_INTERNAL|0x204
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+
+ leave
+ ret
+#endif PARANOID
diff --git a/sys/gnu/fpemul/reg_u_mul.s b/sys/gnu/fpemul/reg_u_mul.s
new file mode 100644
index 000000000000..328d2b07da56
--- /dev/null
+++ b/sys/gnu/fpemul/reg_u_mul.s
@@ -0,0 +1,199 @@
+ .file "reg_u_mul.S"
+/*
+ * reg_u_mul.S
+ *
+ * Core multiplication routine
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_u_mul.s,v 1.3 1994/06/10 07:44:58 rich Exp $
+ *
+ */
+
+/*---------------------------------------------------------------------------+
+ | Basic multiplication routine. |
+ | Does not check the resulting exponent for overflow/underflow |
+ | |
+ | reg_u_mul(FPU_REG *a, FPU_REG *b, FPU_REG *c, unsigned int cw); |
+ | |
+ | Internal working is at approx 128 bits. |
+ | Result is rounded to nearest 53 or 64 bits, using "nearest or even". |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+#include "control_w.h"
+
+
+.data
+ .align 2,0
+accum_0:
+ .long 0
+accum_1:
+ .long 0
+
+
+.text
+ .align 2,144
+
+.globl _reg_u_mul
+_reg_u_mul:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi
+ movl PARAM2,%edi
+
+#ifdef PARANOID
+ testl $0x80000000,SIGH(%esi)
+ jz L_bugged
+ testl $0x80000000,SIGH(%edi)
+ jz L_bugged
+#endif PARANOID
+
+#ifdef DENORM_OPERAND
+ movl EXP(%esi),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp1_not_denorm:
+ movl EXP(%edi),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
+ xorl %ecx,%ecx
+ xorl %ebx,%ebx
+
+ movl SIGL(%esi),%eax
+ mull SIGL(%edi)
+ movl %eax,accum_0
+ movl %edx,accum_1
+
+ movl SIGL(%esi),%eax
+ mull SIGH(%edi)
+ addl %eax,accum_1
+ adcl %edx,%ebx
+/* adcl $0,%ecx *//* overflow here is not possible */
+
+ movl SIGH(%esi),%eax
+ mull SIGL(%edi)
+ addl %eax,accum_1
+ adcl %edx,%ebx
+ adcl $0,%ecx
+
+ movl SIGH(%esi),%eax
+ mull SIGH(%edi)
+ addl %eax,%ebx
+ adcl %edx,%ecx
+
+ movl EXP(%esi),%eax /* Compute the exponent */
+ addl EXP(%edi),%eax
+ subl EXP_BIAS-1,%eax
+/* Have now finished with the sources */
+ movl PARAM3,%edi /* Point to the destination */
+ movl %eax,EXP(%edi)
+
+/* Now make sure that the result is normalized */
+ testl $0x80000000,%ecx
+ jnz LResult_Normalised
+
+ /* Normalize by shifting left one bit */
+ shll $1,accum_0
+ rcll $1,accum_1
+ rcll $1,%ebx
+ rcll $1,%ecx
+ decl EXP(%edi)
+
+LResult_Normalised:
+ movl accum_0,%eax
+ movl accum_1,%edx
+ orl %eax,%eax
+ jz L_extent_zero
+
+ orl $1,%edx
+
+L_extent_zero:
+ movl %ecx,%eax
+ jmp FPU_round
+
+
+#ifdef PARANOID
+L_bugged:
+ pushl EX_INTERNAL|0x205
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+#endif PARANOID
+
diff --git a/sys/gnu/fpemul/reg_u_sub.s b/sys/gnu/fpemul/reg_u_sub.s
new file mode 100644
index 000000000000..7d6d8961669e
--- /dev/null
+++ b/sys/gnu/fpemul/reg_u_sub.s
@@ -0,0 +1,361 @@
+ .file "reg_u_sub.S"
+/*
+ * reg_u_sub.S
+ *
+ * Core floating point subtraction routine.
+ *
+ * Call from C as:
+ * void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
+ * int control_w)
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: reg_u_sub.s,v 1.3 1994/06/10 07:44:59 rich Exp $
+ *
+ */
+
+/*
+ | Kernel subtraction routine reg_u_sub(reg *arg1, reg *arg2, reg *answ).
+ | Takes two valid reg f.p. numbers (TW_Valid), which are
+ | treated as unsigned numbers,
+ | and returns their difference as a TW_Valid or TW_Zero f.p.
+ | number.
+ | The first number (arg1) must be the larger.
+ | The returned number is normalized.
+ | Basic checks are performed if PARANOID is defined.
+ */
+
+#include "exception.h"
+#include "fpu_asm.h"
+#include "control_w.h"
+
+.text
+ .align 2,144
+.globl _reg_u_sub
+_reg_u_sub:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi /* source 1 */
+ movl PARAM2,%edi /* source 2 */
+
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%esi)
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp1_not_denorm:
+ cmpl EXP_UNDER,EXP(%edi)
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
+/* xorl %ecx,%ecx */
+ movl EXP(%esi),%ecx
+ subl EXP(%edi),%ecx /* exp1 - exp2 */
+
+#ifdef PARANOID
+ /* source 2 is always smaller than source 1 */
+/* jc L_bugged */
+ js L_bugged_1
+
+ testl $0x80000000,SIGH(%edi) /* The args are assumed to be be normalized */
+ je L_bugged_2
+
+ testl $0x80000000,SIGH(%esi)
+ je L_bugged_2
+#endif PARANOID
+
+/*--------------------------------------+
+ | Form a register holding the |
+ | smaller number |
+ +--------------------------------------*/
+ movl SIGH(%edi),%eax /* register ms word */
+ movl SIGL(%edi),%ebx /* register ls word */
+
+ movl PARAM3,%edi /* destination */
+ movl EXP(%esi),%edx
+ movl %edx,EXP(%edi) /* Copy exponent to destination */
+ movb SIGN(%esi),%dl
+ movb %dl,SIGN(%edi) /* Copy the sign from the first arg */
+
+ xorl %edx,%edx /* register extension */
+
+/*--------------------------------------+
+ | Shift the temporary register |
+ | right the required number of |
+ | places. |
+ +--------------------------------------*/
+L_shift_r:
+ cmpl $32,%ecx /* shrd only works for 0..31 bits */
+ jnc L_more_than_31
+
+/* less than 32 bits */
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ jmp L_shift_done
+
+L_more_than_31:
+ cmpl $64,%ecx
+ jnc L_more_than_63
+
+ subb $32,%cl
+ jz L_exactly_32
+
+ shrd %cl,%eax,%edx
+ shr %cl,%eax
+ orl %ebx,%ebx
+ jz L_more_31_no_low /* none of the lowest bits is set */
+
+ orl $1,%edx /* record the fact in the extension */
+
+L_more_31_no_low:
+ movl %eax,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_exactly_32:
+ movl %ebx,%edx
+ movl %eax,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_more_than_63:
+ cmpw $65,%cx
+ jnc L_more_than_64
+
+ /* Shift right by 64 bits */
+ movl %eax,%edx
+ orl %ebx,%ebx
+ jz L_more_63_no_low
+
+ orl $1,%edx
+ jmp L_more_63_no_low
+
+L_more_than_64:
+ jne L_more_than_65
+
+ /* Shift right by 65 bits */
+ /* Carry is clear if we get here */
+ movl %eax,%edx
+ rcrl %edx
+ jnc L_shift_65_nc
+
+ orl $1,%edx
+ jmp L_more_63_no_low
+
+L_shift_65_nc:
+ orl %ebx,%ebx
+ jz L_more_63_no_low
+
+ orl $1,%edx
+ jmp L_more_63_no_low
+
+L_more_than_65:
+ movl $1,%edx /* The shifted nr always at least one '1' */
+
+L_more_63_no_low:
+ xorl %ebx,%ebx
+ xorl %eax,%eax
+
+L_shift_done:
+L_subtr:
+/*------------------------------+
+ | Do the subtraction |
+ +------------------------------*/
+ xorl %ecx,%ecx
+ subl %edx,%ecx
+ movl %ecx,%edx
+ movl SIGL(%esi),%ecx
+ sbbl %ebx,%ecx
+ movl %ecx,%ebx
+ movl SIGH(%esi),%ecx
+ sbbl %eax,%ecx
+ movl %ecx,%eax
+
+#ifdef PARANOID
+ /* We can never get a borrow */
+ jc L_bugged
+#endif PARANOID
+
+/*--------------------------------------+
+ | Normalize the result |
+ +--------------------------------------*/
+ testl $0x80000000,%eax
+ jnz L_round /* no shifting needed */
+
+ orl %eax,%eax
+ jnz L_shift_1 /* shift left 1 - 31 bits */
+
+ orl %ebx,%ebx
+ jnz L_shift_32 /* shift left 32 - 63 bits */
+
+/* A rare case, the only one which is non-zero if we got here
+// is: 1000000 .... 0000
+// -0111111 .... 1111 1
+// --------------------
+// 0000000 .... 0000 1 */
+
+ cmpl $0x80000000,%edx
+ jnz L_must_be_zero
+
+ /* Shift left 64 bits */
+ subl $64,EXP(%edi)
+ movl %edx,%eax
+ jmp L_store
+
+L_must_be_zero:
+#ifdef PARANOID
+ orl %edx,%edx
+ jnz L_bugged_3
+#endif PARANOID
+
+ /* The result is zero */
+ movb TW_Zero,TAG(%edi)
+ movl $0,EXP(%edi) /* exponent */
+ movl $0,SIGL(%edi)
+ movl $0,SIGH(%edi)
+ jmp L_exit /* Does not underflow */
+
+L_shift_32:
+ movl %ebx,%eax
+ movl %edx,%ebx
+ movl $0,%edx
+ subl $32,EXP(%edi) /* Can get underflow here */
+
+/* We need to shift left by 1 - 31 bits */
+L_shift_1:
+ bsrl %eax,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ shld %cl,%ebx,%eax
+ shld %cl,%edx,%ebx
+ shl %cl,%edx
+ subl %ecx,EXP(%edi) /* Can get underflow here */
+
+L_round:
+ jmp FPU_round /* Round the result */
+
+
+#ifdef PARANOID
+L_bugged_1:
+ pushl EX_INTERNAL|0x206
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_2:
+ pushl EX_INTERNAL|0x209
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_3:
+ pushl EX_INTERNAL|0x210
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_4:
+ pushl EX_INTERNAL|0x211
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged:
+ pushl EX_INTERNAL|0x212
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+#endif PARANOID
+
+
+L_store:
+/*------------------------------+
+ | Store the result |
+ +------------------------------*/
+ movl %eax,SIGH(%edi)
+ movl %ebx,SIGL(%edi)
+
+ movb TW_Valid,TAG(%edi) /* Set the tags to TW_Valid */
+
+ cmpl EXP_UNDER,EXP(%edi)
+ jle L_underflow
+
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+
+
+L_underflow:
+ push %edi
+ call _arith_underflow
+ pop %ebx
+ jmp L_exit
+
diff --git a/sys/gnu/fpemul/status_w.h b/sys/gnu/fpemul/status_w.h
new file mode 100644
index 000000000000..e54b36415c9d
--- /dev/null
+++ b/sys/gnu/fpemul/status_w.h
@@ -0,0 +1,106 @@
+/*
+ * status_w.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: status_w.h,v 1.3 1994/06/10 07:45:01 rich Exp $
+ *
+ */
+
+
+#ifndef _STATUS_H_
+#define _STATUS_H_
+
+
+#ifdef LOCORE
+#define Const__(x) $/**/x
+#else
+#define Const__(x) x
+#endif
+
+#define SW_Backward Const__(0x8000) /* backward compatibility */
+#define SW_C3 Const__(0x4000) /* condition bit 3 */
+#define SW_Top Const__(0x3800) /* top of stack */
+#define SW_Top_Shift Const__(11) /* shift for top of stack bits */
+#define SW_C2 Const__(0x0400) /* condition bit 2 */
+#define SW_C1 Const__(0x0200) /* condition bit 1 */
+#define SW_C0 Const__(0x0100) /* condition bit 0 */
+#define SW_Summary Const__(0x0080) /* exception summary */
+#define SW_Stack_Fault Const__(0x0040) /* stack fault */
+#define SW_Precision Const__(0x0020) /* loss of precision */
+#define SW_Underflow Const__(0x0010) /* underflow */
+#define SW_Overflow Const__(0x0008) /* overflow */
+#define SW_Zero_Div Const__(0x0004) /* divide by zero */
+#define SW_Denorm_Op Const__(0x0002) /* denormalized operand */
+#define SW_Invalid Const__(0x0001) /* invalid operation */
+
+#define SW_Exc_Mask Const__(0x27f) /* Status word exception bit mask */
+
+#ifndef LOCORE
+
+#define COMP_A_gt_B 1
+#define COMP_A_eq_B 2
+#define COMP_A_lt_B 3
+#define COMP_No_Comp 4
+#define COMP_Denormal 0x20
+#define COMP_NaN 0x40
+#define COMP_SNaN 0x80
+
+#define setcc(cc) ({ \
+ status_word &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \
+ status_word |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); })
+
+#endif /* LOCORE */
+
+#endif /* _STATUS_H_ */
diff --git a/sys/gnu/fpemul/version.h b/sys/gnu/fpemul/version.h
new file mode 100644
index 000000000000..40c4513175a3
--- /dev/null
+++ b/sys/gnu/fpemul/version.h
@@ -0,0 +1,61 @@
+/*
+ * version.h
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: version.h,v 1.3 1994/06/10 07:45:02 rich Exp $
+ *
+ */
+
+#define FPU_VERSION "wm-FPU-emu version BETA 1.4"
diff --git a/sys/gnu/fpemul/wm_shrx.s b/sys/gnu/fpemul/wm_shrx.s
new file mode 100644
index 000000000000..2044a682141e
--- /dev/null
+++ b/sys/gnu/fpemul/wm_shrx.s
@@ -0,0 +1,261 @@
+ .file "wm_shrx.S"
+/*
+ * wm_shrx.S
+ *
+ * 64 bit right shift functions
+ *
+ * Call from C as:
+ * unsigned shrx(void *arg1, unsigned arg2)
+ * and
+ * unsigned shrxs(void *arg1, unsigned arg2)
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: wm_shrx.s,v 1.3 1994/06/10 07:45:03 rich Exp $
+ *
+ */
+
+
+#include "fpu_asm.h"
+
+.text
+ .align 2,144
+
+/*---------------------------------------------------------------------------+
+ | unsigned shrx(void *arg1, unsigned arg2) |
+ | |
+ | Extended shift right function. |
+ | Fastest for small shifts. |
+ | Shifts the 64 bit quantity pointed to by the first arg (arg1) |
+ | right by the number of bits specified by the second arg (arg2). |
+ | Forms a 96 bit quantity from the 64 bit arg and eax: |
+ | [ 64 bit arg ][ eax ] |
+ | shift right ---------> |
+ | The eax register is initialized to 0 before the shifting. |
+ | Results returned in the 64 bit arg and eax. |
+ +---------------------------------------------------------------------------*/
+
+ .globl _shrx
+
+_shrx:
+ push %ebp
+ movl %esp,%ebp
+ pushl %esi
+ movl PARAM2,%ecx
+ movl PARAM1,%esi
+ cmpl $32,%ecx /* shrd only works for 0..31 bits */
+ jnc L_more_than_31
+
+/* less than 32 bits */
+ pushl %ebx
+ movl (%esi),%ebx /* lsl */
+ movl 4(%esi),%edx /* msl */
+ xorl %eax,%eax /* extension */
+ shrd %cl,%ebx,%eax
+ shrd %cl,%edx,%ebx
+ shr %cl,%edx
+ movl %ebx,(%esi)
+ movl %edx,4(%esi)
+ popl %ebx
+ popl %esi
+ leave
+ ret
+
+L_more_than_31:
+ cmpl $64,%ecx
+ jnc L_more_than_63
+
+ subb $32,%cl
+ movl (%esi),%eax /* lsl */
+ movl 4(%esi),%edx /* msl */
+ shrd %cl,%edx,%eax
+ shr %cl,%edx
+ movl %edx,(%esi)
+ movl $0,4(%esi)
+ popl %esi
+ leave
+ ret
+
+L_more_than_63:
+ cmpl $96,%ecx
+ jnc L_more_than_95
+
+ subb $64,%cl
+ movl 4(%esi),%eax /* msl */
+ shr %cl,%eax
+ xorl %edx,%edx
+ movl %edx,(%esi)
+ movl %edx,4(%esi)
+ popl %esi
+ leave
+ ret
+
+L_more_than_95:
+ xorl %eax,%eax
+ movl %eax,(%esi)
+ movl %eax,4(%esi)
+ popl %esi
+ leave
+ ret
+
+
+/*---------------------------------------------------------------------------+
+ | unsigned shrxs(void *arg1, unsigned arg2) |
+ | |
+ | Extended shift right function (optimized for small floating point |
+ | integers). |
+ | Shifts the 64 bit quantity pointed to by the first arg (arg1) |
+ | right by the number of bits specified by the second arg (arg2). |
+ | Forms a 96 bit quantity from the 64 bit arg and eax: |
+ | [ 64 bit arg ][ eax ] |
+ | shift right ---------> |
+ | The eax register is initialized to 0 before the shifting. |
+ | The lower 8 bits of eax are lost and replaced by a flag which is |
+ | set (to 0x01) if any bit, apart from the first one, is set in the |
+ | part which has been shifted out of the arg. |
+ | Results returned in the 64 bit arg and eax. |
+ +---------------------------------------------------------------------------*/
+ .globl _shrxs
+_shrxs:
+ push %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %ebx
+ movl PARAM2,%ecx
+ movl PARAM1,%esi
+ cmpl $64,%ecx /* shrd only works for 0..31 bits */
+ jnc Ls_more_than_63
+
+ cmpl $32,%ecx /* shrd only works for 0..31 bits */
+ jc Ls_less_than_32
+
+/* We got here without jumps by assuming that the most common requirement
+ is for small integers */
+/* Shift by [32..63] bits */
+ subb $32,%cl
+ movl (%esi),%eax /* lsl */
+ movl 4(%esi),%edx /* msl */
+ xorl %ebx,%ebx
+ shrd %cl,%eax,%ebx
+ shrd %cl,%edx,%eax
+ shr %cl,%edx
+ orl %ebx,%ebx /* test these 32 bits */
+ setne %bl
+ test $0x7fffffff,%eax /* and 31 bits here */
+ setne %bh
+ orw %bx,%bx /* Any of the 63 bit set ? */
+ setne %al
+ movl %edx,(%esi)
+ movl $0,4(%esi)
+ popl %ebx
+ popl %esi
+ leave
+ ret
+
+/* Shift by [0..31] bits */
+Ls_less_than_32:
+ movl (%esi),%ebx /* lsl */
+ movl 4(%esi),%edx /* msl */
+ xorl %eax,%eax /* extension */
+ shrd %cl,%ebx,%eax
+ shrd %cl,%edx,%ebx
+ shr %cl,%edx
+ test $0x7fffffff,%eax /* only need to look at eax here */
+ setne %al
+ movl %ebx,(%esi)
+ movl %edx,4(%esi)
+ popl %ebx
+ popl %esi
+ leave
+ ret
+
+/* Shift by [64..95] bits */
+Ls_more_than_63:
+ cmpl $96,%ecx
+ jnc Ls_more_than_95
+
+ subb $64,%cl
+ movl (%esi),%ebx /* lsl */
+ movl 4(%esi),%eax /* msl */
+ xorl %edx,%edx /* extension */
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ orl %ebx,%edx
+ setne %bl
+ test $0x7fffffff,%eax /* only need to look at eax here */
+ setne %bh
+ orw %bx,%bx
+ setne %al
+ xorl %edx,%edx
+ movl %edx,(%esi) /* set to zero */
+ movl %edx,4(%esi) /* set to zero */
+ popl %ebx
+ popl %esi
+ leave
+ ret
+
+Ls_more_than_95:
+/* Shift by [96..inf) bits */
+ xorl %eax,%eax
+ movl (%esi),%ebx
+ orl 4(%esi),%ebx
+ setne %al
+ xorl %ebx,%ebx
+ movl %ebx,(%esi)
+ movl %ebx,4(%esi)
+ popl %ebx
+ popl %esi
+ leave
+ ret
diff --git a/sys/gnu/fpemul/wm_sqrt.s b/sys/gnu/fpemul/wm_sqrt.s
new file mode 100644
index 000000000000..cbe0451f5f46
--- /dev/null
+++ b/sys/gnu/fpemul/wm_sqrt.s
@@ -0,0 +1,496 @@
+ .file "wm_sqrt.S"
+/*
+ * wm_sqrt.S
+ *
+ * Fixed point arithmetic square root evaluation.
+ *
+ * Call from C as:
+ * void wm_sqrt(FPU_REG *n, unsigned int control_word)
+ *
+ *
+ * Copyright (C) 1992,1993,1994
+ * W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
+ * Australia. E-mail billm@vaxc.cc.monash.edu.au
+ * All rights reserved.
+ *
+ * This copyright notice covers the redistribution and use of the
+ * FPU emulator developed by W. Metzenthen. It covers only its use
+ * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
+ * use is not permitted under this copyright.
+ *
+ * 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 include information specifying
+ * that source code for the emulator is freely available and include
+ * either:
+ * a) an offer to provide the source code for a nominal distribution
+ * fee, or
+ * b) list at least two alternative methods whereby the source
+ * can be obtained, e.g. a publically accessible bulletin board
+ * and an anonymous ftp site from which the software can be
+ * downloaded.
+ * 3. All advertising materials specifically mentioning features or use of
+ * this emulator must acknowledge that it was developed by W. Metzenthen.
+ * 4. The name of W. Metzenthen may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * 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
+ * W. METZENTHEN 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 purpose of this copyright, based upon the Berkeley copyright, is to
+ * ensure that the covered software remains freely available to everyone.
+ *
+ * The software (with necessary differences) is also available, but under
+ * the terms of the GNU copyleft, for the Linux operating system and for
+ * the djgpp ms-dos extender.
+ *
+ * W. Metzenthen June 1994.
+ *
+ *
+ * $Id: wm_sqrt.s,v 1.3 1994/06/10 07:45:04 rich Exp $
+ *
+ */
+
+
+/*---------------------------------------------------------------------------+
+ | wm_sqrt(FPU_REG *n, unsigned int control_word) |
+ | returns the square root of n in n. |
+ | |
+ | Use Newton's method to compute the square root of a number, which must |
+ | be in the range [1.0 .. 4.0), to 64 bits accuracy. |
+ | Does not check the sign or tag of the argument. |
+ | Sets the exponent, but not the sign or tag of the result. |
+ | |
+ | The guess is kept in %esi:%edi |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+
+
+.data
+/*
+ Local storage:
+ */
+ .align 4,0
+accum_3:
+ .long 0 /* ms word */
+accum_2:
+ .long 0
+accum_1:
+ .long 0
+accum_0:
+ .long 0
+
+/* The de-normalised argument:
+// sq_2 sq_1 sq_0
+// b b b b b b b ... b b b b b b .... b b b b 0 0 0 ... 0
+// ^ binary point here */
+fsqrt_arg_2:
+ .long 0 /* ms word */
+fsqrt_arg_1:
+ .long 0
+fsqrt_arg_0:
+ .long 0 /* ls word, at most the ms bit is set */
+
+.text
+ .align 2,144
+
+.globl _wm_sqrt
+
+_wm_sqrt:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi
+
+ movl SIGH(%esi),%eax
+ movl SIGL(%esi),%ecx
+ xorl %edx,%edx
+
+/* We use a rough linear estimate for the first guess.. */
+
+ cmpl EXP_BIAS,EXP(%esi)
+ jnz sqrt_arg_ge_2
+
+ shrl $1,%eax /* arg is in the range [1.0 .. 2.0) */
+ rcrl $1,%ecx
+ rcrl $1,%edx
+
+sqrt_arg_ge_2:
+/* From here on, n is never accessed directly again until it is
+// replaced by the answer. */
+
+ movl %eax,fsqrt_arg_2 /* ms word of n */
+ movl %ecx,fsqrt_arg_1
+ movl %edx,fsqrt_arg_0
+
+/* Make a linear first estimate */
+ shrl $1,%eax
+ addl $0x40000000,%eax
+ movl $0xaaaaaaaa,%ecx
+ mull %ecx
+ shll %edx /* max result was 7fff... */
+ testl $0x80000000,%edx /* but min was 3fff... */
+ jnz sqrt_prelim_no_adjust
+
+ movl $0x80000000,%edx /* round up */
+
+sqrt_prelim_no_adjust:
+ movl %edx,%esi /* Our first guess */
+
+/* We have now computed (approx) (2 + x) / 3, which forms the basis
+ for a few iterations of Newton's method */
+
+ movl fsqrt_arg_2,%ecx /* ms word */
+
+/* From our initial estimate, three iterations are enough to get us
+// to 30 bits or so. This will then allow two iterations at better
+// precision to complete the process.
+
+// Compute (g + n/g)/2 at each iteration (g is the guess). */
+ shrl %ecx /* Doing this first will prevent a divide */
+ /* overflow later. */
+
+ movl %ecx,%edx /* msw of the arg / 2 */
+ divl %esi /* current estimate */
+ shrl %esi /* divide by 2 */
+ addl %eax,%esi /* the new estimate */
+
+ movl %ecx,%edx
+ divl %esi
+ shrl %esi
+ addl %eax,%esi
+
+ movl %ecx,%edx
+ divl %esi
+ shrl %esi
+ addl %eax,%esi
+
+/* Now that an estimate accurate to about 30 bits has been obtained (in %esi),
+// we improve it to 60 bits or so.
+
+// The strategy from now on is to compute new estimates from
+// guess := guess + (n - guess^2) / (2 * guess) */
+
+/* First, find the square of the guess */
+ movl %esi,%eax
+ mull %esi
+/* guess^2 now in %edx:%eax */
+
+ movl fsqrt_arg_1,%ecx
+ subl %ecx,%eax
+ movl fsqrt_arg_2,%ecx /* ms word of normalized n */
+ sbbl %ecx,%edx
+ jnc sqrt_stage_2_positive
+/* subtraction gives a negative result
+// negate the result before division */
+ notl %edx
+ notl %eax
+ addl $1,%eax
+ adcl $0,%edx
+
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+ jmp sqrt_stage_2_finish
+
+sqrt_stage_2_positive:
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+
+ notl %ecx
+ notl %eax
+ addl $1,%eax
+ adcl $0,%ecx
+
+sqrt_stage_2_finish:
+ sarl $1,%ecx /* divide by 2 */
+ rcrl $1,%eax
+
+ /* Form the new estimate in %esi:%edi */
+ movl %eax,%edi
+ addl %ecx,%esi
+
+ jnz sqrt_stage_2_done /* result should be [1..2) */
+
+#ifdef PARANOID
+/* It should be possible to get here only if the arg is ffff....ffff*/
+ cmp $0xffffffff,fsqrt_arg_1
+ jnz sqrt_stage_2_error
+#endif PARANOID
+
+/* The best rounded result.*/
+ xorl %eax,%eax
+ decl %eax
+ movl %eax,%edi
+ movl %eax,%esi
+ movl $0x7fffffff,%eax
+ jmp sqrt_round_result
+
+#ifdef PARANOID
+sqrt_stage_2_error:
+ pushl EX_INTERNAL|0x213
+ call EXCEPTION
+#endif PARANOID
+
+sqrt_stage_2_done:
+
+/* Now the square root has been computed to better than 60 bits */
+
+/* Find the square of the guess*/
+ movl %edi,%eax /* ls word of guess*/
+ mull %edi
+ movl %edx,accum_1
+
+ movl %esi,%eax
+ mull %esi
+ movl %edx,accum_3
+ movl %eax,accum_2
+
+ movl %edi,%eax
+ mull %esi
+ addl %eax,accum_1
+ adcl %edx,accum_2
+ adcl $0,accum_3
+
+/* movl %esi,%eax*/
+/* mull %edi*/
+ addl %eax,accum_1
+ adcl %edx,accum_2
+ adcl $0,accum_3
+
+/* guess^2 now in accum_3:accum_2:accum_1*/
+
+ movl fsqrt_arg_0,%eax /* get normalized n*/
+ subl %eax,accum_1
+ movl fsqrt_arg_1,%eax
+ sbbl %eax,accum_2
+ movl fsqrt_arg_2,%eax /* ms word of normalized n*/
+ sbbl %eax,accum_3
+ jnc sqrt_stage_3_positive
+
+/* subtraction gives a negative result*/
+/* negate the result before division */
+ notl accum_1
+ notl accum_2
+ notl accum_3
+ addl $1,accum_1
+ adcl $0,accum_2
+
+#ifdef PARANOID
+ adcl $0,accum_3 /* This must be zero */
+ jz sqrt_stage_3_no_error
+
+sqrt_stage_3_error:
+ pushl EX_INTERNAL|0x207
+ call EXCEPTION
+
+sqrt_stage_3_no_error:
+#endif PARANOID
+
+ movl accum_2,%edx
+ movl accum_1,%eax
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+
+ sarl $1,%ecx / divide by 2*/
+ rcrl $1,%eax
+
+ /* prepare to round the result*/
+
+ addl %ecx,%edi
+ adcl $0,%esi
+
+ jmp sqrt_stage_3_finished
+
+sqrt_stage_3_positive:
+ movl accum_2,%edx
+ movl accum_1,%eax
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+
+ sarl $1,%ecx /* divide by 2*/
+ rcrl $1,%eax
+
+ /* prepare to round the result*/
+
+ notl %eax /* Negate the correction term*/
+ notl %ecx
+ addl $1,%eax
+ adcl $0,%ecx /* carry here ==> correction == 0*/
+ adcl $0xffffffff,%esi
+
+ addl %ecx,%edi
+ adcl $0,%esi
+
+sqrt_stage_3_finished:
+
+/* The result in %esi:%edi:%esi should be good to about 90 bits here,
+// and the rounding information here does not have sufficient accuracy
+// in a few rare cases. */
+ cmpl $0xffffffe0,%eax
+ ja sqrt_near_exact_x
+
+ cmpl $0x00000020,%eax
+ jb sqrt_near_exact
+
+ cmpl $0x7fffffe0,%eax
+ jb sqrt_round_result
+
+ cmpl $0x80000020,%eax
+ jb sqrt_get_more_precision
+
+sqrt_round_result:
+/* Set up for rounding operations*/
+ movl %eax,%edx
+ movl %esi,%eax
+ movl %edi,%ebx
+ movl PARAM1,%edi
+ movl EXP_BIAS,EXP(%edi) /* Result is in [1.0 .. 2.0)*/
+ movl PARAM2,%ecx
+ jmp FPU_round_sqrt
+
+
+sqrt_near_exact_x:
+/* First, the estimate must be rounded up.*/
+ addl $1,%edi
+ adcl $0,%esi
+
+sqrt_near_exact:
+/* This is an easy case because x^1/2 is monotonic.
+// We need just find the square of our estimate, compare it
+// with the argument, and deduce whether our estimate is
+// above, below, or exact. We use the fact that the estimate
+// is known to be accurate to about 90 bits. */
+ movl %edi,%eax /* ls word of guess*/
+ mull %edi
+ movl %edx,%ebx /* 2nd ls word of square*/
+ movl %eax,%ecx /* ls word of square*/
+
+ movl %edi,%eax
+ mull %esi
+ addl %eax,%ebx
+ addl %eax,%ebx
+
+#ifdef PARANOID
+ cmp $0xffffffb0,%ebx
+ jb sqrt_near_exact_ok
+
+ cmp $0x00000050,%ebx
+ ja sqrt_near_exact_ok
+
+ pushl EX_INTERNAL|0x214
+ call EXCEPTION
+
+sqrt_near_exact_ok:
+#endif PARANOID
+
+ or %ebx,%ebx
+ js sqrt_near_exact_small
+
+ jnz sqrt_near_exact_large
+
+ or %ebx,%edx
+ jnz sqrt_near_exact_large
+
+/* Our estimate is exactly the right answer*/
+ xorl %eax,%eax
+ jmp sqrt_round_result
+
+sqrt_near_exact_small:
+/* Our estimate is too small*/
+ movl $0x000000ff,%eax
+ jmp sqrt_round_result
+
+sqrt_near_exact_large:
+/* Our estimate is too large, we need to decrement it*/
+ subl $1,%edi
+ sbbl $0,%esi
+ movl $0xffffff00,%eax
+ jmp sqrt_round_result
+
+
+sqrt_get_more_precision:
+/* This case is almost the same as the above, except we start*/
+/* with an extra bit of precision in the estimate.*/
+ stc /* The extra bit.*/
+ rcll $1,%edi /* Shift the estimate left one bit*/
+ rcll $1,%esi
+
+ movl %edi,%eax /* ls word of guess*/
+ mull %edi
+ movl %edx,%ebx /* 2nd ls word of square*/
+ movl %eax,%ecx /* ls word of square*/
+
+ movl %edi,%eax
+ mull %esi
+ addl %eax,%ebx
+ addl %eax,%ebx
+
+/* Put our estimate back to its original value*/
+ stc /* The ms bit.*/
+ rcrl $1,%esi /* Shift the estimate left one bit*/
+ rcrl $1,%edi
+
+#ifdef PARANOID
+ cmp $0xffffff60,%ebx
+ jb sqrt_more_prec_ok
+
+ cmp $0x000000a0,%ebx
+ ja sqrt_more_prec_ok
+
+ pushl EX_INTERNAL|0x215
+ call EXCEPTION
+
+sqrt_more_prec_ok:
+#endif PARANOID
+
+ or %ebx,%ebx
+ js sqrt_more_prec_small
+
+ jnz sqrt_more_prec_large
+
+ or %ebx,%ecx
+ jnz sqrt_more_prec_large
+
+/* Our estimate is exactly the right answer*/
+ movl $0x80000000,%eax
+ jmp sqrt_round_result
+
+sqrt_more_prec_small:
+/* Our estimate is too small*/
+ movl $0x800000ff,%eax
+ jmp sqrt_round_result
+
+sqrt_more_prec_large:
+/* Our estimate is too large*/
+ movl $0x7fffff00,%eax
+ jmp sqrt_round_result
diff --git a/sys/i386/boot/Makefile b/sys/i386/boot/Makefile
index 3f402426ee49..3c7418f7ddda 100644
--- a/sys/i386/boot/Makefile
+++ b/sys/i386/boot/Makefile
@@ -20,7 +20,7 @@
# the rights to redistribute these changes.
#
# from: Mach, Revision 2.2 92/04/04 11:33:46 rpd
-# $Id: Makefile,v 1.5 1993/12/11 20:35:15 ats Exp $
+# $Id: Makefile,v 1.10 1994/06/20 04:32:40 jkh Exp $
#
wd0:
@@ -32,7 +32,10 @@ wd0:
NOPROG= noprog
NOMAN= noman
-CFLAGS = -O -DDO_BAD144 -I${.CURDIR}
+# tunable loopcount parameter, waiting for keypress
+BOOTWAIT?= 2400
+
+CFLAGS = -O2 -DDO_BAD144 -DBOOTWAIT=${BOOTWAIT} -I${.CURDIR}
LIBS= -lc
INC= -I${.CURDIR}/../..
@@ -61,31 +64,31 @@ biosboot: boot
bootbios: boot
dd if=boot of=bootbios skip=1
-/usr/mdec/bootsd: bootbios
- cp bootbios /usr/mdec/bootsd
+${DESTDIR}/usr/mdec/bootsd: bootbios
+ cp bootbios ${DESTDIR}/usr/mdec/bootsd
-/usr/mdec/sdboot: biosboot
- cp biosboot /usr/mdec/sdboot
+${DESTDIR}/usr/mdec/sdboot: biosboot
+ cp biosboot ${DESTDIR}/usr/mdec/sdboot
-/usr/mdec/bootwd: /usr/mdec/bootsd
- rm -f /usr/mdec/bootwd
- ln /usr/mdec/bootsd /usr/mdec/bootwd
+${DESTDIR}/usr/mdec/bootwd: ${DESTDIR}/usr/mdec/bootsd
+ rm -f ${DESTDIR}/usr/mdec/bootwd
+ ln ${DESTDIR}/usr/mdec/bootsd ${DESTDIR}/usr/mdec/bootwd
-/usr/mdec/wdboot: /usr/mdec/sdboot
- rm -f /usr/mdec/wdboot
- ln /usr/mdec/sdboot /usr/mdec/wdboot
+${DESTDIR}/usr/mdec/wdboot: ${DESTDIR}/usr/mdec/sdboot
+ rm -f ${DESTDIR}/usr/mdec/wdboot
+ ln ${DESTDIR}/usr/mdec/sdboot ${DESTDIR}/usr/mdec/wdboot
-/usr/mdec/bootfd: /usr/mdec/bootsd
- rm -f /usr/mdec/bootfd
- ln /usr/mdec/bootsd /usr/mdec/bootfd
+${DESTDIR}/usr/mdec/bootfd: ${DESTDIR}/usr/mdec/bootsd
+ rm -f ${DESTDIR}/usr/mdec/bootfd
+ ln ${DESTDIR}/usr/mdec/bootsd ${DESTDIR}/usr/mdec/bootfd
-/usr/mdec/fdboot: /usr/mdec/sdboot
- rm -f /usr/mdec/fdboot
- ln /usr/mdec/sdboot /usr/mdec/fdboot
+${DESTDIR}/usr/mdec/fdboot: ${DESTDIR}/usr/mdec/sdboot
+ rm -f ${DESTDIR}/usr/mdec/fdboot
+ ln ${DESTDIR}/usr/mdec/sdboot ${DESTDIR}/usr/mdec/fdboot
-sd: /usr/mdec/bootsd /usr/mdec/sdboot
-wd: /usr/mdec/bootwd /usr/mdec/wdboot
-fd: /usr/mdec/bootfd /usr/mdec/fdboot
+sd: ${DESTDIR}/usr/mdec/bootsd ${DESTDIR}/usr/mdec/sdboot
+wd: ${DESTDIR}/usr/mdec/bootwd ${DESTDIR}/usr/mdec/wdboot
+fd: ${DESTDIR}/usr/mdec/bootfd ${DESTDIR}/usr/mdec/fdboot
all: biosboot bootbios
diff --git a/sys/i386/boot/biosboot b/sys/i386/boot/biosboot
new file mode 100644
index 000000000000..9c572c5d2422
--- /dev/null
+++ b/sys/i386/boot/biosboot
Binary files differ
diff --git a/sys/i386/boot/boot b/sys/i386/boot/boot
new file mode 100644
index 000000000000..bba109001d81
--- /dev/null
+++ b/sys/i386/boot/boot
Binary files differ
diff --git a/sys/i386/boot/boot.c b/sys/i386/boot/boot.c
index 19de113b0c51..ad3c7b2ee258 100644
--- a/sys/i386/boot/boot.c
+++ b/sys/i386/boot/boot.c
@@ -24,7 +24,7 @@
* the rights to redistribute these changes.
*
* from: Mach, [92/04/03 16:51:14 rvb]
- * $Id: boot.c,v 1.9.2.1 1994/05/01 05:14:49 rgrimes Exp $
+ * $Id: boot.c,v 1.14 1994/06/16 03:53:27 adam Exp $
*/
@@ -60,8 +60,7 @@ struct exec head;
int argv[10], esym;
char *name;
char *names[] = {
- "/386bsd", "/o386bsd", "/386bsd.old",
- "/vmunix", "/ovmunix", "/vmunix.old"
+ "/386bsd", "/o386bsd", "/386bsd.old"
};
#define NUMNAMES (sizeof(names)/sizeof(char *))
@@ -76,7 +75,7 @@ int drive;
ouraddr,
argv[7] = memsize(0),
argv[8] = memsize(1),
- "$Revision: 1.9.2.1 $");
+ "$Revision: 1.14 $");
printf("use hd(1,a)/386bsd to boot sd0 when wd0 is also installed\n");
gateA20();
loadstart:
@@ -138,13 +137,12 @@ loadprog(howto)
{
if((addr + head.a_text + head.a_data) > ouraddr)
{
- printf("kernel will not fit below loader\n");
+ printf("kernel overlaps loader\n");
return;
}
if((addr + head.a_text + head.a_data + head.a_bss) > 0xa0000)
{
- printf("kernel too big, won't fit in 640K with bss\n");
- printf("Only hope is to link the kernel for > 1MB\n");
+ printf("bss exceeds 640k limit\n");
return;
}
}
diff --git a/sys/i386/boot/boot.sym b/sys/i386/boot/boot.sym
new file mode 100755
index 000000000000..8249abc812dc
--- /dev/null
+++ b/sys/i386/boot/boot.sym
Binary files differ
diff --git a/sys/i386/boot/boot2.S b/sys/i386/boot/boot2.S
index 9270d4de0f3c..c49bde8d377e 100644
--- a/sys/i386/boot/boot2.S
+++ b/sys/i386/boot/boot2.S
@@ -24,7 +24,7 @@
* the rights to redistribute these changes.
*
* from: Mach, Revision 2.2 92/04/04 11:35:26 rpd
- * $Id: boot2.S,v 1.3 1993/11/13 04:43:25 rgrimes Exp $
+ * $Id: boot2.S,v 1.4 1994/06/22 05:52:25 jkh Exp $
*/
#include "asm.h"
@@ -128,14 +128,22 @@ ENTRY(boot2)
mov %ax, %es
/* fix up IDT entries for bdb */
- subl $2, %ebx
+ 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
diff --git a/sys/i386/boot/bootbios b/sys/i386/boot/bootbios
new file mode 100644
index 000000000000..a0ed09db1e0c
--- /dev/null
+++ b/sys/i386/boot/bootbios
Binary files differ
diff --git a/sys/i386/boot/disk.c b/sys/i386/boot/disk.c
index 1c7712f10517..8db41bbb6f4b 100644
--- a/sys/i386/boot/disk.c
+++ b/sys/i386/boot/disk.c
@@ -24,7 +24,17 @@
* the rights to redistribute these changes.
*
* from: Mach, Revision 2.2 92/04/04 11:35:49 rpd
- * $Id: disk.c,v 1.4 1994/02/22 22:59:40 rgrimes Exp $
+ * $Id: disk.c,v 1.5 1994/05/16 03:06:00 ache Exp $
+ */
+
+/*
+ * 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"
@@ -80,10 +90,12 @@ devopen()
#else EMBEDDED_DISKLABEL
Bread(dosdev, 0);
dptr = (struct dos_partition *)(((char *)0)+DOSPARTOFF);
+ sector = LABELSECTOR;
for (i = 0; i < NDOSPART; i++, dptr++)
- if (dptr->dp_typ == DOSPTYP_386BSD)
+ if (dptr->dp_typ == DOSPTYP_386BSD) {
+ sector = dptr->dp_start + LABELSECTOR;
break;
- sector = dptr->dp_start + LABELSECTOR;
+ }
Bread(dosdev, sector++);
dl=((struct disklabel *)0);
disklabel = *dl; /* structure copy (maybe useful later)*/
@@ -113,10 +125,20 @@ devopen()
int dkbbnum;
struct dkbad *dkbptr;
- /* find the first readable bad144 sector */
- /* some of this code is copied from ufs/disk_subr.c */
+ /* 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 */
- dkbbnum = dl->d_secperunit - dl->d_nsectors;
+
+#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
@@ -138,9 +160,9 @@ devopen()
i += 2;
} while (i < 10 && i < dl->d_nsectors);
if (!do_bad144)
- printf("Bad badsect table\n");
+ printf("Bad bad sector table\n");
else
- printf("Using bad144 bad sector at %d\n", dkbbnum+i);
+ printf("Using bad sector table at %d\n", dkbbnum+i);
}
#endif DO_BAD144
}
@@ -245,7 +267,12 @@ badsect(dosdev, sector)
goto no_remap;
}
/* otherwise find replacement sector */
- newsec = dl->d_secperunit - dl->d_nsectors - i -1;
+ 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
diff --git a/sys/i386/boot/io.c b/sys/i386/boot/io.c
index 7cb6d02ba93a..29fd128c9ce7 100644
--- a/sys/i386/boot/io.c
+++ b/sys/i386/boot/io.c
@@ -1,3 +1,4 @@
+
/*
* Mach Operating System
* Copyright (c) 1992, 1991 Carnegie Mellon University
@@ -24,7 +25,7 @@
* the rights to redistribute these changes.
*
* from: Mach, Revision 2.2 92/04/04 11:35:57 rpd
- * $Id: io.c,v 1.3 1993/10/16 19:11:36 rgrimes Exp $
+ * $Id: io.c,v 1.6 1994/06/16 03:53:29 adam Exp $
*/
#include <i386/include/pio.h>
@@ -38,10 +39,10 @@
#define KC_CMD_WIN 0xd0 /* read output port */
#define KC_CMD_WOUT 0xd1 /* write output port */
-#define KB_A20 0x9f /* enable A20,
+#define KB_A20 0xdf /* enable A20,
enable output buffer full interrupt
enable data line
- disable clock line */
+ enable clock line */
/*
* Gate A20 for high memory
@@ -137,13 +138,24 @@ getchar()
return(c);
}
+#if BOOTWAIT
+spinwait(i)
+int i;
+{
+ while (--i >= 0)
+ (void)inb(0x84);
+}
+#endif
+
gets(buf)
char *buf;
{
int i;
char *ptr=buf;
- for (i = 240000; i>0; i--)
+#if BOOTWAIT
+ for (i = BOOTWAIT; i>0; spinwait(10000),i--)
+#endif
if (ischar())
for (;;)
switch(*ptr = getchar() & 0xff) {
diff --git a/sys/i386/boot/start.S b/sys/i386/boot/start.S
index bdf387667780..44de86eb89f4 100644
--- a/sys/i386/boot/start.S
+++ b/sys/i386/boot/start.S
@@ -24,7 +24,7 @@
* the rights to redistribute these changes.
*
* from: Mach, Revision 2.2 92/04/04 11:36:29 rpd
- * $Id: start.S,v 1.2 1993/10/16 19:11:38 rgrimes Exp $
+ * $Id: start.S,v 1.3 1994/06/13 19:27:52 jkh Exp $
*/
/*
@@ -99,6 +99,7 @@ start:
jae hd
fd:
+ mov $0x0, %dl
# reset the disk system
#ifdef DEBUG
data32
diff --git a/sys/i386/conf/ECLIPSE b/sys/i386/conf/ECLIPSE
new file mode 100644
index 000000000000..31eede546035
--- /dev/null
+++ b/sys/i386/conf/ECLIPSE
@@ -0,0 +1,66 @@
+#
+# GENERICAH -- Generic machine with WD/AHx family disks
+#
+# $Id: GENERICAH,v 1.36 1994/06/17 06:56:59 sean Exp $
+#
+
+machine "i386"
+cpu "I386_CPU"
+cpu "I486_CPU"
+cpu "I586_CPU"
+ident ECLIPSE
+timezone 8 dst
+maxusers 10
+maxfdescs 2048 #Max file descriptors per process
+options INET #InterNETworking
+options ISOFS #ISO File System
+options NFS #Network File System
+options PCFS #MSDOS File System
+options "COMPAT_43" #Compatible with BSD 4.3
+options "TCP_COMPAT_42" #TCP/IP compatible with 4.2
+options XSERVER #Xserver
+options UCONSOLE #X Console support
+options "FAT_CURSOR" #block cursor in syscons or pccons
+options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device
+options "NCONS=4" #4 virtual consoles
+
+config "386bsd" root on sd0 swap on sd0 dumps on sd0
+
+controller isa0
+
+device pci0 at isa? bio irq 9 vector pciintr
+device ncr0
+
+controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk fd0 at fdc0 drive 0
+
+controller scbus0
+
+device sd0
+device sd1
+
+device st0
+
+device cd0 #Only need one of these, the code dynamically grows
+
+device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
+device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
+
+device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
+device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr
+
+device lpt0 at isa? port? tty irq 7 vector lptintr
+
+device ix0 at isa? port 0x320 net irq 10 iomem 0xd0000 iosiz 32768 vector ixintr
+
+pseudo-device loop
+pseudo-device ether
+pseudo-device log
+pseudo-device sl 1
+pseudo-device ppp 1
+pseudo-device pty 32
+pseudo-device speaker
+
+pseudo-device swappager
+pseudo-device vnodepager
+pseudo-device devpager
diff --git a/sys/i386/conf/GENERICAH b/sys/i386/conf/GENERICAH
index 4bba699be512..2c10cd45daa1 100644
--- a/sys/i386/conf/GENERICAH
+++ b/sys/i386/conf/GENERICAH
@@ -1,12 +1,13 @@
#
# GENERICAH -- Generic machine with WD/AHx family disks
#
-# $Id: GENERICAH,v 1.25.2.2 1994/04/12 16:37:30 csgr Exp $
+# $Id: GENERICAH,v 1.36 1994/06/17 06:56:59 sean Exp $
#
machine "i386"
cpu "I386_CPU"
cpu "I486_CPU"
+cpu "I586_CPU"
ident GENERICAH
timezone 8 dst
maxusers 10
@@ -22,14 +23,17 @@ options XSERVER #Xserver
options UCONSOLE #X Console support
options "FAT_CURSOR" #block cursor in syscons or pccons
#options GATEWAY #Host is a Gateway (forwards packets)
+options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device
+options "NCONS=4" #4 virtual consoles
-config "386bsd" root on wd0 swap on wd0 and sd0 dumps on wd0
+config "386bsd" root on wd0 swap on wd0 and wd1 and sd0 and sd1 dumps on wd0
controller isa0
controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
disk fd0 at fdc0 drive 0
disk fd1 at fdc0 drive 1
+#tape ft0 at fdc0 drive 2
controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr
disk wd0 at wdc0 drive 0
@@ -41,6 +45,7 @@ disk wd3 at wdc1 drive 1
controller ahb0 at isa? bio irq 11 vector ahbintr
controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
+controller sea0 at isa? bio irq 5 iomem 0xc8000 iosiz 0x2000 vector seaintr
controller scbus0
device sd0
@@ -57,7 +62,7 @@ device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr
device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr
device mcd1 at isa? port 0x340 bio irq 11 vector mcdintr
-device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint
+device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
@@ -78,7 +83,7 @@ pseudo-device loop
pseudo-device ether
pseudo-device log
pseudo-device sl 2
-pseudo-device pty 12
+pseudo-device pty 16
pseudo-device speaker
pseudo-device swappager
diff --git a/sys/i386/conf/GENERICBT b/sys/i386/conf/GENERICBT
index 5311d76f7388..fae078940fa7 100644
--- a/sys/i386/conf/GENERICBT
+++ b/sys/i386/conf/GENERICBT
@@ -1,12 +1,13 @@
#
# GENERICBT -- Generic machine with WD/BTx family disks
#
-# $Id: GENERICBT,v 1.25.2.2 1994/04/12 16:37:32 csgr Exp $
+# $Id: GENERICBT,v 1.35 1994/06/08 00:30:32 phk Exp $
#
machine "i386"
cpu "I386_CPU"
cpu "I486_CPU"
+cpu "I586_CPU"
ident GENERICBT
timezone 8 dst
maxusers 10
@@ -22,14 +23,17 @@ options XSERVER #Xserver
options UCONSOLE #X Console support
options "FAT_CURSOR" #block cursor in syscons or pccons
#options GATEWAY #Host is a Gateway (forwards packets)
+options "NCONS=4" #4 virtual consoles
+options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device
-config "386bsd" root on wd0 swap on wd0 and sd0 dumps on wd0
+config "386bsd" root on wd0 swap on wd0 and wd1 and sd0 and sd1 dumps on wd0
controller isa0
controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
disk fd0 at fdc0 drive 0
disk fd1 at fdc0 drive 1
+#tape ft0 at fdc0 drive 2
controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr
disk wd0 at wdc0 drive 0
@@ -57,7 +61,7 @@ device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr
device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr
device mcd1 at isa? port 0x340 bio irq 11 vector mcdintr
-device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint
+device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
@@ -78,7 +82,7 @@ pseudo-device loop
pseudo-device ether
pseudo-device log
pseudo-device sl 2
-pseudo-device pty 12
+pseudo-device pty 16
pseudo-device speaker
pseudo-device swappager
diff --git a/sys/i386/conf/HAMSTER b/sys/i386/conf/HAMSTER
new file mode 100644
index 000000000000..a3da520cd05e
--- /dev/null
+++ b/sys/i386/conf/HAMSTER
@@ -0,0 +1,67 @@
+#
+# GENERICAH -- Generic machine with WD/AHx family disks
+#
+# $Id: GENERICAH,v 1.36 1994/06/17 06:56:59 sean Exp $
+#
+
+machine "i386"
+cpu "I386_CPU"
+cpu "I486_CPU"
+cpu "I586_CPU"
+ident GENERICAH
+timezone 8 dst
+maxusers 10
+maxfdescs 2048 #Max file descriptors per process
+options INET #InterNETworking
+options ISOFS #ISO File System
+options NFS #Network File System
+options PCFS #MSDOS File System
+options "COMPAT_43" #Compatible with BSD 4.3
+options "TCP_COMPAT_42" #TCP/IP compatible with 4.2
+options XSERVER #Xserver
+options UCONSOLE #X Console support
+options "FAT_CURSOR" #block cursor in syscons or pccons
+#options GATEWAY #Host is a Gateway (forwards packets)
+options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device
+options "NCONS=8" #4 virtual consoles
+
+config "386bsd" root on sd0 swap on sd0 dumps on sd0
+
+controller isa0
+
+controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk fd0 at fdc0 drive 0
+
+controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
+controller scbus0
+
+device sd0
+device sd1
+device sd2
+device sd3
+
+device st0
+device st1
+
+device cd0 #Only need one of these, the code dynamically grows
+
+device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
+device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
+
+device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
+device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr
+
+device lpt0 at isa? port? tty irq 7 vector lptintr
+
+device ix0 at isa? port 0x300 net irq 10 iomem 0xd0000 iosiz 32768 vector ixintr
+
+pseudo-device loop
+pseudo-device ether
+pseudo-device log
+pseudo-device sl 2
+pseudo-device pty 32
+pseudo-device speaker
+
+pseudo-device swappager
+pseudo-device vnodepager
+pseudo-device devpager
diff --git a/sys/i386/conf/LAPTOP b/sys/i386/conf/LAPTOP
new file mode 100644
index 000000000000..4c7ce3b2fbd7
--- /dev/null
+++ b/sys/i386/conf/LAPTOP
@@ -0,0 +1,68 @@
+#
+# LAPTOP -- Minimal machine with IDE drives.
+#
+# $Id: LAPTOP,v 1.3 1994/06/22 05:58:53 jkh Exp $
+#
+
+machine "i386"
+cpu "I386_CPU"
+cpu "I486_CPU"
+cpu "I586_CPU"
+ident LAPTOP
+timezone 8 dst
+maxusers 8
+maxfdescs 256 #Max file descriptors per process
+options INET #InterNETworking
+options NFS #Network File System
+options PCFS #MSDOS File System
+options "COMPAT_43" #Compatible with BSD 4.3
+options "TCP_COMPAT_42" #TCP/IP compatible with 4.2
+options XSERVER #Xserver
+options UCONSOLE #X Console support
+options "FAT_CURSOR" #block cursor in syscons or pccons
+options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device
+options "NCONS=8" #8 virtual consoles
+options LAPTOP
+
+# Do not use in binary distributions; put here because most laptops lack 387
+# support, and LAPTOP is not one of the kernels we build by default.
+options GPL_MATH_EMULATE #Support for x87 emualtion via GPL'd emu
+
+# Most laptops have PS/2 style trackball mice.
+options ALLOW_CONFLICT_IOADDR #no IO addr conflict checks (PS/2 mice)
+
+config "386bsd" root on wd0 swap on wd0 and wd1 and sd0 and sd1 dumps on wd0
+
+controller isa0
+
+controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk fd0 at fdc0 drive 0
+
+controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr
+disk wd0 at wdc0 drive 0
+
+device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
+device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
+
+device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
+device lpt0 at isa? port? tty irq 7 vector lptintr
+device psm0 at isa? port "IO_KBD" tty irq 12 vector psmintr
+
+# IBM/National PCMCIA ethernet cards
+device ze0 at isa? port 0x300 net irq 5 iomem 0xd8000 vector zeintr
+
+# The digital speaker driver (/dev/pcaudio). Might as well since we almost
+# certainly won't have a sound card!
+device pca0 at isa? tty
+
+pseudo-device loop
+pseudo-device ether
+pseudo-device log
+pseudo-device sl 1
+pseudo-device ppp 1
+pseudo-device pty 16
+pseudo-device speaker
+
+pseudo-device swappager
+pseudo-device vnodepager
+pseudo-device devpager
diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT
index 129d2c5d6ea0..9235212a9920 100644
--- a/sys/i386/conf/LINT
+++ b/sys/i386/conf/LINT
@@ -4,38 +4,40 @@
#
# This kernel is NOT MEANT to be runnable!
#
-# $Id: LINT,v 1.53 1994/02/09 05:35:57 nate Exp $
+# $Id: LINT,v 1.79 1994/06/16 05:31:16 jkh Exp $
#
machine "i386"
cpu "I386_CPU"
cpu "I486_CPU"
+cpu "I586_CPU"
ident LINT
timezone 8 dst
maxusers 10
maxfdescs 2048 #Max file descriptors per process
options MATH_EMULATE #Support for x87 emulation
+# Do not use in binary distributions
+#options GPL_MATH_EMULATE #Support for x87 emualtion via
+ #new math emulator
+
config "386bsd" root on wd0 swap on wd0 and sd0 dumps on wd0
#
# options that appear as inline #ifdef's
#
-options "COM_BIDIR" #Bidirectional support in sys/isa/sio.c
options "COM_MULTIPORT" #Multiport support in sys/isa/sio.c
-options "FIFO_TRIGGER=FIFO_TRIGGER_1" #Use this fifo value in sio.c
options "COMPAT_43" #compatible with BSD 4.3
-options "SYMTAB_SPACE=104705" #This kernel needs LOTS of symtable
+options "SYMTAB_SPACE=119000" #This kernel needs LOTS of symtable
options GATEWAY #internetwork gateway
options KTRACE #kernel tracing
options "NCONS=8" #number of syscons virtual consoles
options "FAT_CURSOR" #block cursor in syscons or pccons
-options "STAR_SAVER" #syscons "stars" screen saver
-options "FADE_SAVER" #syscons "fade" screen saver
-options "SNAKE_SAVER" #syscons "snake" screen saver
-options "BLANK_SAVER" #syscons "blank" screen saver
+
+#options ALLOW_CONFLICT_IOADDR #no IO addr conflict checks (PS/2 mice)
+#options ALLOW_CONFLICT_IRQ #no IRQ conflict checks (mport serial)
options "TCP_COMPAT_42" #tcp/ip compatible with 4.2
# ^^^ NOT RECOMMENDED FOR NORMAL USE
@@ -53,13 +55,14 @@ options MACHVMCOMPAT #support for Mach-style vm calls
options IPBROADCASTECHO=1 #send reply to broadcast pings
options IPMASKAGENT=1 #send reply to icmp mask requests
options TPCONS #support X.25 network-layer service
+options USER_LDT #allow user-level control of i386 ldt
-options EXCLUDE_CHIP_MIDI # \ sound driver options
-options "EXCLUDE_MPU401" # \ exclude specified
-options EXCLUDE_GUS # / device or chip
-options EXCLUDE_SBPRO # / from driver
+# See /sys/i386/doc/sound.doc for information about EXCLUDE options for
+# the sound drivers.
-options USER_LDT #allow user-level control of i386 ldt
+# Multicast support.
+options MULTICAST # Multicast code
+options MROUTING # Multicast routing
#
# options that are in sys/conf/files
@@ -120,13 +123,15 @@ controller ahb0 at isa? bio irq 11 vector ahbintr
controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr
# driver for the Seagate ST01/ST02 card, not yet finished.
#controller sg0 at isa? bio irq 5 iomem 0xc8000 iosiz 0x2000 vector sgintr
-#dcfclk device-driver
+# driver for the Seagate ST01/ST02 or Future Domain 950 card, works
+controller sea0 at isa? bio irq 5 iomem 0xc8000 iosiz 0x2000 vector seaintr
controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
disk fd0 at fdc0 drive 0
disk fd1 at fdc0 drive 1
tape ft0 at fdc0 drive 2
+
# driver for the Western Digital and SMCC WD80xx cards, for the Novell
-# NE1000/200 card and the 3COM 3C503 card.
+# NE1000/2000 card and the 3COM 3C503 card.
device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr
# driver for the AT&T Starlan card.
device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr
@@ -135,27 +140,38 @@ device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr
#device ix0 at isa? port 0x320 net irq 10 iomem 0xd0000 iosiz 32768 vector ixintr
# driver for the Etherlink III ( 3C509 ) card, beta version.
device ep0 at isa? port 0x300 net irq 10 vector epintr
+#driver for the 3c501
+device el0 at isa? port 0x300 net irq 9 vector elintr
+# IBM/National PCMCIA ethernet cards
+device ze0 at isa? port 0x300 net irq 5 iomem 0xd8000 vector zeintr
+
#special cased above:
#controller isa0
-# interruptless parallel printer port driver
-device lpa0 at isa? port "IO_LPT1" tty
-device lpa1 at isa? port "IO_LPT2" tty
+
+# interruptless parallel printer port driver. NOW OBSOLETE, DON'T USE.
+#device lpa0 at isa? port "IO_LPT1" tty
+#device lpa1 at isa? port "IO_LPT2" tty
# interrupt driven parallel printer port driver
device lpt0 at isa? port "IO_LPT3" tty irq 7 vector lptintr
-# Driver for Mutsumi CD-ROM players
-device mcd0 at isa? port 0x300 bio irq 10 vector mcdint
+# Driver for Mitsumi CD-ROM players
+device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr
# Driver for Logitech and ATI inport bus mice
device mse0 at isa? port 0x23c tty irq 5 vector mseintr
device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
-device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint
#only one of pc0 or sc0 allowed
-#device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
+#device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint
+device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr
device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr
device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr
+#PS/2 mouse driver (must follow pc0 or sc0 if enabled). Also enable
+#ALLOW_CONFLICT_IOADDR option (see above) if you want to use this.
+#device psm0 at isa? port "IO_KBD" tty irq 12 vector psmintr
+
pseudo-device speaker
-#tw device-driver
+device tw0 at isa? port 0x278 tty irq 5 vector twintr
+
controller uha0 at isa? port "IO_UHA0" bio irq 14 drq 5 vector uhaintr
controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr
disk wd0 at wdc0 drive 0
@@ -166,14 +182,17 @@ disk wd3 at wdc1 drive 1
device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr
# Various sound card drivers.
-# See /sys/i386/doc/sound.doc for more information.
-device snd5 at isa? port 0x330 irq 6 drq 0 vector mpuintr
+# See /sys/doc/sound.doc for more information.
+device snd5 at isa? port 0x330 irq 6 vector mpuintr
device snd4 at isa? port 0x220 irq 15 drq 6 vector gusintr
-device snd3 at isa? port 0x388 irq 12 drq 3 vector pasintr
+device snd3 at isa? port 0x388 irq 10 drq 6 vector pasintr
device snd2 at isa? port 0x220 irq 7 drq 1 vector sbintr
-device snd1 at isa? port 0x388 irq 0 drq 0 vector sbintr
-#
-#
+device snd6 at isa? port 0x220 irq 7 drq 5 vector sbintr
+device snd7 at isa? port 0x300
+device snd1 at isa? port 0x388
+
+# The digital speaker driver (/dev/pcaudio).
+device pca0 at isa? tty
+
# options that have not been resolved yet
-#
pseudo-device log
diff --git a/sys/i386/conf/Makefile.i386 b/sys/i386/conf/Makefile.i386
index df5fcf18c66c..0ecf4c5e14e5 100644
--- a/sys/i386/conf/Makefile.i386
+++ b/sys/i386/conf/Makefile.i386
@@ -1,6 +1,6 @@
# Copyright 1990 W. Jolitz
# from: @(#)Makefile.i386 7.1 5/10/91
-# $Id: Makefile.i386,v 1.22 1994/02/17 06:51:15 rgrimes Exp $
+# $Id: Makefile.i386,v 1.27 1994/06/16 00:45:02 adam Exp $
#
# Makefile for FreeBSD
#
@@ -24,7 +24,15 @@ TOUCH= touch -f -c
LD= /usr/bin/ld
CC= cc
CPP= cpp
+.if defined(DEBUG)
+.if defined(NOSTRIP)
+STRIP= echo '(skipping) strip'
+.else
+STRIP= cp $@ $@.sym; strip
+.endif
+.else
STRIP= strip
+.endif
DBSYM= /usr/sbin/dbsym
S= ../..
@@ -51,11 +59,11 @@ NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $<
NORMAL_S= ${CPP} -I. -DLOCORE ${COPTS} $< | ${AS} ${ASFLAGS} -o $*.o
DRIVER_C= ${CC} -c ${CFLAGS} ${PROF} $<
DRIVER_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $<
-SYSTEM_OBJS=locore.o exception.o swtch.o support.o ${OBJS} param.o \
+SYSTEM_OBJS=locore.o config.o exception.o swtch.o support.o ${OBJS} param.o \
ioconf.o conf.o machdep.o
SYSTEM_DEP=Makefile symbols.sort ${SYSTEM_OBJS}
SYSTEM_LD_HEAD= @echo loading $@; rm -f $@
-SYSTEM_LD= @${LD} -Bstatic -Z -T ${LOAD_ADDRESS} -o $@ -X vers.o ${SYSTEM_OBJS}
+SYSTEM_LD= @${LD} -Bstatic -Z -T ${LOAD_ADDRESS} -o $@ -X ${SYSTEM_OBJS} vers.o
SYSTEM_LD_TAIL= @echo rearranging symbols; symorder symbols.sort $@; \
${DBSYM} -fT ${LOAD_ADDRESS} $@; ${STRIP} -x $@; size $@; chmod 755 $@
@@ -90,7 +98,7 @@ symbols.sort: ${I386}/i386/symbols.raw
locore.o: assym.s ${I386}/i386/locore.s machine/trap.h machine/psl.h \
machine/pte.h ${I386}/isa/vector.s ${I386}/isa/icu.s \
- $S/sys/errno.h machine/specialreg.h ${I386}/isa/debug.h \
+ $S/sys/errno.h machine/specialreg.h \
${I386}/isa/icu.h ${I386}/isa/isa.h vector.h $S/net/netisr.h \
machine/asmacros.h
${CPP} -I. -DLOCORE ${COPTS} ${I386}/i386/locore.s | \
@@ -104,7 +112,7 @@ exception.o: assym.s ${I386}/i386/exception.s machine/trap.h \
${AS} ${ASFLAGS} -o exception.o
swtch.o: assym.s ${I386}/i386/swtch.s \
- $S/sys/errno.h ${I386}/isa/debug.h machine/asmacros.h
+ $S/sys/errno.h machine/asmacros.h
${CPP} -I. ${COPTS} ${I386}/i386/swtch.s | \
${AS} ${ASFLAGS} -o swtch.o
diff --git a/sys/i386/conf/SYSCONS b/sys/i386/conf/SYSCONS
deleted file mode 100644
index 2143ec61c60d..000000000000
--- a/sys/i386/conf/SYSCONS
+++ /dev/null
@@ -1,86 +0,0 @@
-#
-# SYSCONS -- Generic machine with WD/AHx family disks and syscons
-#
-# $Id: SYSCONS,v 1.18 1994/02/07 10:42:04 rgrimes Exp $
-#
-
-machine "i386"
-cpu "I386_CPU"
-cpu "I486_CPU"
-ident SYSCONS
-timezone 8 dst
-maxusers 10
-maxfdescs 2048 #Max file descriptors per process
-options MATH_EMULATE #Support for x87 emulation
-options INET #InterNETworking
-options ISOFS #ISO File System
-options NFS #Network File System
-options PCFS #MSDOS File System
-options "COMPAT_43" #Compatible with BSD 4.3
-options "TCP_COMPAT_42" #TCP/IP compatible with 4.2
-options XSERVER #Xserver
-options UCONSOLE #X Console support
-options "NCONS=8" #8 virtual consoles
-options "FAT_CURSOR" #block cursor in syscons
-options "STAR_SAVER" #syscons "stars" screen saver
-#options GATEWAY #Host is a Gateway (forwards packets)
-
-config "386bsd" root on wd0 swap on wd0 and sd0 dumps on wd0
-
-controller isa0
-
-controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
-disk fd0 at fdc0 drive 0
-disk fd1 at fdc0 drive 1
-
-controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr
-disk wd0 at wdc0 drive 0
-disk wd1 at wdc0 drive 1
-
-controller wdc1 at isa? port "IO_WD2" bio irq 15 vector wdintr
-disk wd2 at wdc1 drive 0
-disk wd3 at wdc1 drive 1
-
-controller ahb0 at isa? bio irq 11 vector ahbintr
-controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
-controller scbus0
-
-device sd0
-device sd1
-device sd2
-device sd3
-
-device st0
-device st1
-
-device cd0 #Only need one of these, the code dynamically grows
-
-device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
-device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
-
-device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
-device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr
-device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr
-device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr
-
-device lpt0 at isa? port "IO_LPT3" tty irq 7 vector lptintr
-device lpa0 at isa? port "IO_LPT1" tty
-device lpa1 at isa? port "IO_LPT2" tty
-
-device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr
-device ed1 at isa? port 0x300 net irq 5 iomem 0xd8000 vector edintr
-device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr
-device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr
-
-device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr
-
-pseudo-device loop
-pseudo-device ether
-pseudo-device log
-pseudo-device sl 2
-pseudo-device pty 4
-pseudo-device speaker
-
-pseudo-device swappager
-pseudo-device vnodepager
-pseudo-device devpager
diff --git a/sys/i386/conf/TIME b/sys/i386/conf/TIME
new file mode 100644
index 000000000000..bc9c0d8e8752
--- /dev/null
+++ b/sys/i386/conf/TIME
@@ -0,0 +1,64 @@
+#
+# GENERICBT -- Generic machine with WD/BTx family disks
+#
+# $Id: GENERICBT,v 1.35 1994/06/08 00:30:32 phk Exp $
+#
+
+machine "i386"
+cpu "I386_CPU"
+cpu "I486_CPU"
+cpu "I586_CPU"
+ident TIME
+timezone 8 dst
+maxusers 10
+maxfdescs 1024 #Max file descriptors per process
+options INET #InterNETworking
+options ISOFS #ISO File System
+options NFS #Network File System
+options PCFS #MSDOS File System
+options "COMPAT_43" #Compatible with BSD 4.3
+options "TCP_COMPAT_42" #TCP/IP compatible with 4.2
+options XSERVER #Xserver
+options UCONSOLE #X Console support
+options "FAT_CURSOR" #block cursor in syscons or pccons
+options "NCONS=4" #4 virtual consoles
+options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device
+
+config "386bsd" root on sd0 swap on sd0 dumps on sd0
+
+controller isa0
+
+controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk fd0 at fdc0 drive 0
+
+controller bt0 at isa? port "IO_BT0" bio irq 11 vector btintr
+controller scbus0
+
+device sd0
+device sd1
+
+device st0
+
+device cd0 #Only need one of these, the code dynamically grows
+
+device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
+device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
+
+device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
+device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr
+
+device lpt0 at isa? port? tty irq 7 vector lptintr
+
+device ed0 at isa? port 0x360 net irq 10 iomem 0xd8000 iosiz 16384 vector edintr
+#device ix0 at isa? port 0x320 net irq 10 iomem 0xd8000 iosiz 32768 vector ixintr
+
+pseudo-device loop
+pseudo-device ether
+pseudo-device log
+pseudo-device sl 2
+pseudo-device pty 32
+pseudo-device speaker
+
+pseudo-device swappager
+pseudo-device vnodepager
+pseudo-device devpager
diff --git a/sys/i386/conf/WCARCH b/sys/i386/conf/WCARCH
new file mode 100644
index 000000000000..f228ec5752d8
--- /dev/null
+++ b/sys/i386/conf/WCARCH
@@ -0,0 +1,67 @@
+#
+# GENERICBT -- Generic machine with WD/BTx family disks
+#
+# $Id: GENERICBT,v 1.35 1994/06/08 00:30:32 phk Exp $
+#
+
+machine "i386"
+cpu "I386_CPU"
+cpu "I486_CPU"
+cpu "I586_CPU"
+ident TIME
+timezone 8 dst
+maxusers 10
+maxfdescs 1024 #Max file descriptors per process
+options INET #InterNETworking
+options ISOFS #ISO File System
+options NFS #Network File System
+options PCFS #MSDOS File System
+options "COMPAT_43" #Compatible with BSD 4.3
+options "TCP_COMPAT_42" #TCP/IP compatible with 4.2
+options XSERVER #Xserver
+options UCONSOLE #X Console support
+options "FAT_CURSOR" #block cursor in syscons or pccons
+options "NCONS=4" #4 virtual consoles
+options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device
+
+config "386bsd" root on sd0 swap on sd0 dumps on sd0
+
+controller isa0
+
+controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk fd0 at fdc0 drive 0
+
+# driver for the Adaptec 154x SCSI cards.
+controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
+
+#controller bt0 at isa? port "IO_BT0" bio irq 11 vector btintr
+#controller scbus0
+
+device sd0
+device sd1
+
+device st0
+
+device cd0 #Only need one of these, the code dynamically grows
+
+device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
+device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
+
+device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
+device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr
+
+device lpt0 at isa? port? tty irq 7 vector lptintr
+
+device ed0 at isa? port 0x360 net irq 10 iomem 0xd8000 iosiz 16384 vector edintr
+#device ix0 at isa? port 0x320 net irq 10 iomem 0xd8000 iosiz 32768 vector ixintr
+
+pseudo-device loop
+pseudo-device ether
+pseudo-device log
+pseudo-device sl 2
+pseudo-device pty 32
+pseudo-device speaker
+
+pseudo-device swappager
+pseudo-device vnodepager
+pseudo-device devpager
diff --git a/sys/i386/conf/WCARCHIVE b/sys/i386/conf/WCARCHIVE
new file mode 100644
index 000000000000..0ce90784f3fd
--- /dev/null
+++ b/sys/i386/conf/WCARCHIVE
@@ -0,0 +1,73 @@
+#
+# GENERICAH -- Generic machine with WD/AHx family disks
+#
+# $Id: GENERICAH,v 1.36 1994/06/17 06:56:59 sean Exp $
+#
+
+machine "i386"
+cpu "I386_CPU"
+cpu "I486_CPU"
+cpu "I586_CPU"
+ident WCARCHIVE
+timezone 8 dst
+maxusers 40
+maxfdescs 1024 #Max file descriptors per process
+options INET #InterNETworking
+options ISOFS #ISO File System
+options NFS #Network File System
+options PCFS #MSDOS File System
+options "COMPAT_43" #Compatible with BSD 4.3
+options "TCP_COMPAT_42" #TCP/IP compatible with 4.2
+options XSERVER #Xserver
+options UCONSOLE #X Console support
+options "FAT_CURSOR" #block cursor in syscons or pccons
+options GATEWAY #Host is a Gateway (forwards packets)
+options "SCSI_DELAY=10" #Be pessimistic about Joe SCSI device
+options "NCONS=4" #4 virtual consoles
+options "NMBCLUSTERS=1024" #Needed space for many ftp connections
+
+config "386bsd" root on sd0 swap on sd0 and sd1 and sd2 dumps on sd0
+
+controller isa0
+
+controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk fd0 at fdc0 drive 0
+
+controller ahb0 at isa? bio irq 11 vector ahbintr
+controller ahb1 at isa? bio irq 12 vector ahbintr
+
+#controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
+#controller aha1 at isa? port 0x230 bio irq 12 drq 6 vector ahaintr
+
+controller scbus0
+controller scbus1
+
+device sd0
+device sd1
+device sd2
+device sd3
+device sd4
+device sd5
+device sd6
+device sd7
+
+device cd0 #Only need one of these, the code dynamically grows
+
+device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
+device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
+
+device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
+
+device lpt0 at isa? port? tty irq 7 vector lptintr
+
+device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 flags 0x4 vector edintr
+
+pseudo-device loop
+pseudo-device ether
+pseudo-device log
+pseudo-device pty 32
+pseudo-device speaker
+
+pseudo-device swappager
+pseudo-device vnodepager
+pseudo-device devpager
diff --git a/sys/i386/conf/devices.i386 b/sys/i386/conf/devices.i386
index c388159afee3..3abc0605aac1 100644
--- a/sys/i386/conf/devices.i386
+++ b/sys/i386/conf/devices.i386
@@ -1,6 +1,6 @@
# This file tells what major numbers the various possible swap devices have.
#
-# $Id: devices.i386,v 1.5 1994/01/04 20:09:28 nate Exp $
+# $Id: devices.i386,v 1.6 1994/03/21 20:48:49 ats Exp $
#
wd 0
dk 1
@@ -10,3 +10,4 @@ sd 4
st 5
cd 6
mcd 7
+scd 8
diff --git a/sys/i386/conf/files.i386 b/sys/i386/conf/files.i386
index 1b84847c7713..92a79684af05 100644
--- a/sys/i386/conf/files.i386
+++ b/sys/i386/conf/files.i386
@@ -1,7 +1,7 @@
# This file tells config what files go into building a kernel,
# files marked standard are always included.
#
-# $Id: files.i386,v 1.25 1994/02/07 04:27:59 alm Exp $
+# $Id: files.i386,v 1.39 1994/06/16 05:31:20 jkh Exp $
#
i386/i386/autoconf.c standard device-driver
i386/i386/cons.c standard
@@ -17,53 +17,61 @@ i386/i386/pmap.c standard
i386/i386/sys_machdep.c standard
i386/i386/trap.c standard
i386/i386/vm_machdep.c standard
+i386/i386/random.s optional multicast
i386/isa/aha1542.c optional aha device-driver
i386/isa/aha1742.c optional ahb device-driver
i386/isa/bt742a.c optional bt device-driver
i386/isa/clock.c standard
i386/isa/com.c optional com device-driver
-i386/isa/dcfclk.c optional dcfclk device-driver
i386/isa/fd.c optional fd device-driver
i386/isa/ft.c optional ft device-driver
i386/isa/if_ed.c optional ed device-driver
+i386/isa/if_el.c optional el device-driver
i386/isa/if_ep.c optional ep device-driver
i386/isa/if_ie.c optional ie device-driver
i386/isa/if_is.c optional is device-driver
i386/isa/if_ix.c optional ix device-driver
i386/isa/isa.c optional isa device-driver
-i386/isa/lpa.c optional lpa device-driver
i386/isa/lpt.c optional lpt device-driver
i386/isa/mcd.c optional mcd device-driver
i386/isa/mse.c optional mse device-driver
i386/isa/npx.c optional npx device-driver
i386/isa/syscons.c optional sc device-driver
i386/isa/pccons.c optional pc device-driver
+i386/isa/pcaudio.c optional pca device-driver
i386/isa/psm.c optional psm device-driver
i386/isa/sb.c optional sb device-driver
+i386/isa/scd.c optional scd device-driver
+i386/isa/seagate.c optional sea device-driver
i386/isa/sg.c optional sg device-driver
i386/isa/sio.c optional sio device-driver
-i386/isa/sound/adlib_card.c optional snd device-driver
-i386/isa/sound/audio.c optional snd device-driver
-i386/isa/sound/dev_table.c optional snd device-driver
-i386/isa/sound/dmabuf.c optional snd device-driver
-i386/isa/sound/dsp.c optional snd device-driver
-i386/isa/sound/gus_card.c optional snd device-driver
-i386/isa/sound/gus_midi.c optional snd device-driver
-i386/isa/sound/gus_vol.c optional snd device-driver
-i386/isa/sound/gus_wave.c optional snd device-driver
-i386/isa/sound/midi.c optional snd device-driver
-i386/isa/sound/midibuf.c optional snd device-driver
-i386/isa/sound/mpu401.c optional snd device-driver
-i386/isa/sound/opl3.c optional snd device-driver
-i386/isa/sound/pas2_card.c optional snd device-driver
-i386/isa/sound/pas2_midi.c optional snd device-driver
-i386/isa/sound/pas2_mixer.c optional snd device-driver
-i386/isa/sound/pas2_pcm.c optional snd device-driver
-i386/isa/sound/patmgr.c optional snd device-driver
-i386/isa/sound/pro_midi.c optional snd device-driver
-i386/isa/sound/sb_card.c optional snd device-driver
-i386/isa/sound/sb_dsp.c optional snd device-driver
-i386/isa/sound/sequencer.c optional snd device-driver
+i386/isa/sound/adlib_card.c optional snd device-driver
+i386/isa/sound/audio.c optional snd device-driver
+i386/isa/sound/dev_table.c optional snd device-driver
+i386/isa/sound/dmabuf.c optional snd device-driver
+i386/isa/sound/gus_card.c optional snd device-driver
+i386/isa/sound/gus_midi.c optional snd device-driver
+i386/isa/sound/gus_vol.c optional snd device-driver
+i386/isa/sound/gus_wave.c optional snd device-driver
+i386/isa/sound/ics2101.c optional snd device-driver
+i386/isa/sound/midi.c optional snd device-driver
+i386/isa/sound/midibuf.c optional snd device-driver
+i386/isa/sound/mpu401.c optional snd device-driver
+i386/isa/sound/opl3.c optional snd device-driver
+i386/isa/sound/pas2_card.c optional snd device-driver
+i386/isa/sound/pas2_midi.c optional snd device-driver
+i386/isa/sound/pas2_mixer.c optional snd device-driver
+i386/isa/sound/pas2_pcm.c optional snd device-driver
+i386/isa/sound/patmgr.c optional snd device-driver
+i386/isa/sound/pro_midi.c optional snd device-driver
+i386/isa/sound/sb16_dsp.c optional snd device-driver
+i386/isa/sound/sb16_midi.c optional snd device-driver
+i386/isa/sound/sb_card.c optional snd device-driver
+i386/isa/sound/sb_dsp.c optional snd device-driver
+i386/isa/sound/sb_midi.c optional snd device-driver
+i386/isa/sound/sb_mixer.c optional snd device-driver
+i386/isa/sound/sequencer.c optional snd device-driver
+i386/isa/sound/sound_switch.c optional snd device-driver
i386/isa/sound/soundcard.c optional snd device-driver
i386/isa/spkr.c optional speaker
i386/isa/tw.c optional tw device-driver
@@ -76,3 +84,39 @@ i386/isa/pcvt/pcvt_out.c optional vt device-driver
i386/isa/pcvt/pcvt_kbd.c optional vt device-driver
i386/isa/pcvt/pcvt_vtf.c optional vt device-driver
i386/isa/pcvt/pcvt_ext.c optional vt device-driver
+i386/isa/if_ze.c optional ze device-driver
+gnu/fpemul/div_small.s optional gpl_math_emulate
+gnu/fpemul/errors.c optional gpl_math_emulate
+gnu/fpemul/fpu_arith.c optional gpl_math_emulate
+gnu/fpemul/fpu_aux.c optional gpl_math_emulate
+gnu/fpemul/fpu_entry.c optional gpl_math_emulate
+gnu/fpemul/fpu_etc.c optional gpl_math_emulate
+gnu/fpemul/fpu_trig.c optional gpl_math_emulate
+gnu/fpemul/get_address.c optional gpl_math_emulate
+gnu/fpemul/load_store.c optional gpl_math_emulate
+gnu/fpemul/poly_2xm1.c optional gpl_math_emulate
+gnu/fpemul/poly_atan.c optional gpl_math_emulate
+gnu/fpemul/poly_div.s optional gpl_math_emulate
+gnu/fpemul/poly_l2.c optional gpl_math_emulate
+gnu/fpemul/poly_mul64.s optional gpl_math_emulate
+gnu/fpemul/poly_sin.c optional gpl_math_emulate
+gnu/fpemul/poly_tan.c optional gpl_math_emulate
+gnu/fpemul/polynomial.s optional gpl_math_emulate
+gnu/fpemul/reg_add_sub.c optional gpl_math_emulate
+gnu/fpemul/reg_compare.c optional gpl_math_emulate
+gnu/fpemul/reg_constant.c optional gpl_math_emulate
+gnu/fpemul/reg_div.s optional gpl_math_emulate
+gnu/fpemul/reg_ld_str.c optional gpl_math_emulate
+gnu/fpemul/reg_mul.c optional gpl_math_emulate
+gnu/fpemul/reg_norm.s optional gpl_math_emulate
+gnu/fpemul/reg_round.s optional gpl_math_emulate
+gnu/fpemul/reg_u_add.s optional gpl_math_emulate
+gnu/fpemul/reg_u_div.s optional gpl_math_emulate
+gnu/fpemul/reg_u_mul.s optional gpl_math_emulate
+gnu/fpemul/reg_u_sub.s optional gpl_math_emulate
+gnu/fpemul/wm_shrx.s optional gpl_math_emulate
+gnu/fpemul/wm_sqrt.s optional gpl_math_emulate
+i386/pci/ncr.c optional ncr device-driver
+i386/pci/pci.c optional pci device-driver
+i386/pci/pcibios.c optional pci device-driver
+i386/pci/pci_config.c optional pci device-driver
diff --git a/sys/i386/doc/sound.doc b/sys/i386/doc/sound.doc
deleted file mode 100644
index 7f5b9da64e6d..000000000000
--- a/sys/i386/doc/sound.doc
+++ /dev/null
@@ -1,38 +0,0 @@
-To enable sound card support, you need to add one or more of the following
-lines to your kernel configuration file:
-
-device snd5 at isa? port 0x330 irq 6 drq 0 vector mpuintr
-device snd4 at isa? port 0x220 irq 15 drq 6 vector gusintr
-device snd3 at isa? port 0x388 irq 12 drq 3 vector pasintr
-device snd2 at isa? port 0x220 irq 7 drq 1 vector sbintr
-device snd1 at isa? port 0x388 irq 0 drq 0 vector sbintr
-
- Unit numbers are:
- 1 for Yamaha FM synth
- 2 for SB/SB Pro DSP
- 3 for PAS PCM and Midi
- 4 for GUS
- 5 for MPU-401
-
- If you have ProAudioSpectrum, uncomment units 3 and 2.
- If you have SoundBlaster, uncomment 2 and 1.
- If you have GravisUltrasound, uncomment 4
- If you have MPU-401, uncomment 5
-
-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: Some systems with the OPTI chipset will require you to #define
-BROKEN_BUS_CLOCK in /sys/i386/sound/pas2_card.c. Symptoms are that
-you will hear a lot of clicking and popping sounds, like a geiger counter,
-coming out of the PAS even when is not playing anything.
-
-
-
- - Jordan Hubbard (jkh@freefall.cdrom.com)
diff --git a/sys/i386/i386/autoconf.c b/sys/i386/i386/autoconf.c
index 7584c02b353e..5b571a04432c 100644
--- a/sys/i386/i386/autoconf.c
+++ b/sys/i386/i386/autoconf.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)autoconf.c 7.1 (Berkeley) 5/9/91
- * $Id: autoconf.c,v 1.8.2.1 1994/03/23 23:45:08 rgrimes Exp $
+ * $Id: autoconf.c,v 1.11 1994/03/21 15:02:47 ache Exp $
*/
/*
@@ -79,6 +79,11 @@ configure()
isa_configure();
#endif
+#include "pci.h"
+#if NPCI > 0
+ pci_configure();
+#endif
+
#if GENERICxxx && !defined(DISKLESS)
if ((boothowto & RB_ASKNAME) == 0)
setroot();
diff --git a/sys/i386/i386/conf.c b/sys/i386/i386/conf.c
index b1ed3063a3b1..8e0bf2673c3a 100644
--- a/sys/i386/i386/conf.c
+++ b/sys/i386/i386/conf.c
@@ -41,7 +41,7 @@
* SUCH DAMAGE.
*
* from: @(#)conf.c 5.8 (Berkeley) 5/12/91
- * $Id: conf.c,v 1.20.2.1 1994/05/04 07:45:12 rgrimes Exp $
+ * $Id: conf.c,v 1.28 1994/05/30 03:35:53 ache Exp $
*/
#include "param.h"
@@ -213,6 +213,7 @@ struct bdevsw bdevsw[] =
cddump, cdsize, 0 },
{ mcdopen, mcdclose, mcdstrategy, mcdioctl, /*7*/
mcddump, mcdsize, 0 },
+ { 0, } /* block major 8 is reserved for local use */
/*
* If you need a bdev major number, please contact the FreeBSD team
* by sending mail to "FreeBSD-hackers@freefall.cdrom.com".
@@ -236,7 +237,7 @@ d_close_t pcclose;
d_rdwr_t pcread, pcwrite;
d_ioctl_t pcioctl;
d_mmap_t pcmmap;
-extern struct tty pccons;
+extern struct tty *pccons;
/* controlling TTY */
d_open_t cttyopen;
@@ -262,7 +263,7 @@ d_close_t ptcclose;
d_rdwr_t ptcread, ptcwrite;
d_select_t ptcselect;
d_ioctl_t ptyioctl;
-extern struct tty pt_tty[];
+extern struct tty *pt_tty[];
#else
#define ptsopen (d_open_t *)enxio
#define ptsclose (d_close_t *)enxio
@@ -285,9 +286,8 @@ d_close_t comclose;
d_rdwr_t comread;
d_rdwr_t comwrite;
d_ioctl_t comioctl;
-d_select_t comselect;
#define comreset (d_reset_t *)enxio
-extern struct tty com_tty[];
+extern struct tty *com_tty[];
#else
#define comopen (d_open_t *)enxio
#define comclose (d_close_t *)enxio
@@ -295,7 +295,6 @@ extern struct tty com_tty[];
#define comwrite (d_rdwr_t *)enxio
#define comioctl (d_ioctl_t *)enxio
#define comreset (d_reset_t *)enxio
-#define comselect (d_select_t *)enxio
#define com_tty NULL
#endif
@@ -401,33 +400,15 @@ d_ioctl_t bpfioctl;
#define bpfioctl (d_ioctl_t *)enxio
#endif
-#include "dcfclk.h"
-#if NDCFCLK > 0
-d_open_t dcfclkopen;
-d_close_t dcfclkclose;
-d_rdwr_t dcfclkread;
-d_ioctl_t dcfclkioctl;
-d_select_t dcfclkselect;
-#else
-#define dcfclkopen (d_open_t *)enxio
-#define dcfclkclose (d_close_t *)enxio
-#define dcfclkread (d_rdwr_t *)enxio
-#define dcfclkioctl (d_ioctl_t *)enxio
-#define dcfclkselect (d_select_t *)enxio
-#endif
-
-#include "lpa.h"
-#if NLPA > 0
-d_open_t lpaopen;
-d_close_t lpaclose;
-d_rdwr_t lpawrite;
-d_ioctl_t lpaioctl;
-#else
+/*
+ * lpa has been removed from the tree,
+ * but the major dev no for it still remains.
+ * (lpt now provides all the functionality which lpa used to.)
+ */
#define lpaopen (d_open_t *)enxio
#define lpaclose (d_close_t *)enxio
#define lpawrite (d_rdwr_t *)enxio
#define lpaioctl (d_ioctl_t *)enxio
-#endif
#include "speaker.h"
#if NSPEAKER > 0
@@ -442,6 +423,21 @@ d_ioctl_t spkrioctl;
#define spkrioctl (d_ioctl_t *)enxio
#endif
+#include "pca.h"
+#if NPCA > 0
+d_open_t pcaopen;
+d_close_t pcaclose;
+d_rdwr_t pcawrite;
+d_ioctl_t pcaioctl;
+d_select_t pcaselect;
+#else
+#define pcaopen (d_open_t *)enxio
+#define pcaclose (d_close_t *)enxio
+#define pcawrite (d_rdwr_t *)enxio
+#define pcaioctl (d_ioctl_t *)enxio
+#define pcaselect (d_select_t *)enxio
+#endif
+
#include "mse.h"
#if NMSE > 0
d_open_t mseopen;
@@ -464,7 +460,7 @@ d_ioctl_t sioioctl;
d_select_t sioselect;
d_stop_t siostop;
#define sioreset (d_reset_t *)enxio
-extern struct tty sio_tty[];
+extern struct tty *sio_tty[];
#else
#define sioopen (d_open_t *)enxio
#define sioclose (d_close_t *)enxio
@@ -544,7 +540,7 @@ struct cdevsw cdevsw[] =
logselect, nommap, NULL },
{ comopen, comclose, comread, comwrite, /*8*/
comioctl, nostop, comreset, com_tty, /* com */
- comselect, nommap, NULL },
+ ttselect, nommap, NULL },
{ Fdopen, fdclose, rawread, rawwrite, /*9*/
fdioctl, nostop, nullreset, NULL, /* Fd (!=fd) */
seltrue, nommap, fdstrategy },
@@ -590,9 +586,9 @@ struct cdevsw cdevsw[] =
{ bpfopen, bpfclose, bpfread, bpfwrite, /*23*/
bpfioctl, nostop, nullreset, NULL, /* bpf */
bpfselect, nommap, NULL },
- { dcfclkopen, dcfclkclose, dcfclkread, nowrite, /*24*/
- dcfclkioctl, nostop, nullreset, NULL, /* dcfclk */
- dcfclkselect, nommap, NULL },
+ { pcaopen, pcaclose, noread, pcawrite, /*24*/
+ pcaioctl, nostop, nullreset, NULL, /* pcaudio */
+ pcaselect, nommap, NULL },
{ lpaopen, lpaclose, noread, lpawrite, /*25*/
lpaioctl, nullstop, nullreset, NULL, /* lpa */
seltrue, nommap, NULL },
@@ -614,6 +610,7 @@ struct cdevsw cdevsw[] =
{ ukopen, ukclose, noread, nowrite, /*31*/
ukioctl, nostop, nullreset, NULL, /* unknown */
seltrue, nommap, NULL }, /* scsi */
+ { 0, } /* character device 32 is reserved for local use */
/*
* If you need a cdev major number, please contact the FreeBSD team
* by sending mail to `freebsd-hackers@freefall.cdrom.com'.
diff --git a/sys/i386/i386/db_interface.c b/sys/i386/i386/db_interface.c
index 7e68844e4ade..aeeedbecdac1 100644
--- a/sys/i386/i386/db_interface.c
+++ b/sys/i386/i386/db_interface.c
@@ -23,7 +23,7 @@
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*
- * $Id: db_interface.c,v 1.5 1993/12/19 00:50:00 wollman Exp $
+ * $Id: db_interface.c,v 1.7 1994/06/22 05:52:28 jkh Exp $
*/
/*
@@ -178,8 +178,6 @@ db_read_bytes(addr, size, data)
db_nofault = 0;
}
-struct pte *pmap_pte(pmap_t, vm_offset_t);
-
/*
* Write bytes to kernel address space for debugger.
*/
@@ -234,9 +232,24 @@ db_write_bytes(addr, size, data)
}
}
+/*
+ * XXX move this to machdep.c and allow it to be called iff any debugger is
+ * installed.
+ * XXX msg is not printed.
+ */
void
Debugger (msg)
const char *msg;
{
- asm ("int $3");
+ static volatile u_char in_Debugger;
+
+ if (!in_Debugger) {
+ in_Debugger = 1;
+#ifdef __GNUC__
+ asm("int $3");
+#else
+ int3();
+#endif
+ in_Debugger = 0;
+ }
}
diff --git a/sys/i386/i386/exception.s b/sys/i386/i386/exception.s
index aa2e3d940fd1..f247c0a233af 100644
--- a/sys/i386/i386/exception.s
+++ b/sys/i386/i386/exception.s
@@ -30,7 +30,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: exception.s,v 1.2 1994/01/03 07:55:20 davidg Exp $
+ * $Id: exception.s,v 1.3 1994/04/02 07:00:23 davidg Exp $
*/
#include "npx.h" /* NNPX */
@@ -39,7 +39,9 @@
#include "errno.h" /* error return codes */
-#include "i386/isa/debug.h" /* BDE debugging macros */
+#include "machine/spl.h" /* SWI_AST_MASK ... */
+
+#include "machine/psl.h" /* PSL_I */
#include "machine/trap.h" /* trap codes */
#include "syscall.h" /* syscall numbers */
@@ -57,31 +59,49 @@
/*****************************************************************************/
/*
* Trap and fault vector routines
- *
+ */
+#define IDTVEC(name) ALIGN_TEXT ; .globl _X/**/name ; _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.
*/
-#define IDTVEC(name) ALIGN_TEXT; .globl _X/**/name; _X/**/name:
-#define TRAP(a) pushl $(a) ; jmp alltraps
+#ifdef BDE_DEBUGGER
+#define BDBTRAP(name) \
+ ss ; \
+ cmpb $0,_bdb_exists ; \
+ je 1f ; \
+ testb $SEL_RPL_MASK,4(%esp) ; \
+ jne 1f ; \
+ ss ; \
+ .globl bdb_/**/name/**/_ljmp ; \
+bdb_/**/name/**/_ljmp: ; \
+ ljmp $0,$0 ; \
+1:
+#else
+#define BDBTRAP(name)
+#endif
+
#ifdef KGDB
-# define BPTTRAP(a) sti; pushl $(a) ; jmp bpttraps
+# define BPTTRAP(a) testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; \
+ pushl $(a) ; jmp _bpttraps
#else
-# define BPTTRAP(a) sti; TRAP(a)
+# define BPTTRAP(a) testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; TRAP(a)
#endif
+MCOUNT_LABEL(user)
+MCOUNT_LABEL(btrap)
+
IDTVEC(div)
pushl $0; TRAP(T_DIVIDE)
IDTVEC(dbg)
-#if defined(BDE_DEBUGGER) && defined(BDBTRAP)
BDBTRAP(dbg)
-#endif
pushl $0; BPTTRAP(T_TRCTRAP)
IDTVEC(nmi)
pushl $0; TRAP(T_NMI)
IDTVEC(bpt)
-#if defined(BDE_DEBUGGER) && defined(BDBTRAP)
BDBTRAP(bpt)
-#endif
pushl $0; BPTTRAP(T_BPTFLT)
IDTVEC(ofl)
pushl $0; TRAP(T_OFLOW)
@@ -114,22 +134,24 @@ IDTVEC(fpu)
* error. It would be better to handle npx interrupts as traps but
* this is difficult for nested interrupts.
*/
- pushl $0 /* dummy error code */
- pushl $T_ASTFLT
+ pushl $0 /* dumby error code */
+ pushl $0 /* dumby trap type */
pushal
- nop /* silly, the bug is for popal and it only
- * bites when the next instruction has a
- * complicated address mode */
pushl %ds
pushl %es /* now the stack frame is a trap frame */
movl $KDSEL,%eax
movl %ax,%ds
movl %ax,%es
- pushl _cpl
+ 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
- jmp doreti
+ MEXITCOUNT
+ jmp _doreti
#else /* NNPX > 0 */
pushl $0; TRAP(T_ARITHTRAP)
#endif /* NNPX > 0 */
@@ -166,25 +188,37 @@ IDTVEC(rsvd14)
pushl $0; TRAP(31)
SUPERALIGN_TEXT
-alltraps:
+_alltraps:
pushal
- nop
pushl %ds
pushl %es
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
/*
- * Return through doreti to handle ASTs. Have to change trap frame
+ * 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.
*/
- movl $T_ASTFLT,TF_TRAPNO(%esp) /* new trap type (err code not used) */
- pushl _cpl
- pushl $0 /* dummy unit */
- jmp doreti
+ pushl %eax
+ subl $4,%esp
+ MEXITCOUNT
+ jmp _doreti
#ifdef KGDB
/*
@@ -192,17 +226,18 @@ calltrap:
* to the regular trap code.
*/
SUPERALIGN_TEXT
-bpttraps:
+_bpttraps:
pushal
- nop
pushl %ds
pushl %es
movl $KDSEL,%eax
movl %ax,%ds
movl %ax,%es
+ FAKE_MCOUNT(12*4(%esp))
testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp) /* non-kernel mode? */
jne calltrap /* yes */
call _kgdb_trap_glue
+ MEXITCOUNT
jmp calltrap
#endif
@@ -214,7 +249,6 @@ IDTVEC(syscall)
pushfl /* Room for tf_err */
pushfl /* Room for tf_trapno */
pushal
- nop
pushl %ds
pushl %es
movl $KDSEL,%eax /* switch to kernel segments */
@@ -222,51 +256,17 @@ IDTVEC(syscall)
movl %ax,%es
movl TF_ERR(%esp),%eax /* copy eflags from tf_err to fs_eflags */
movl %eax,TF_EFLAGS(%esp)
- movl $0,TF_ERR(%esp) /* zero tf_err */
+ FAKE_MCOUNT(12*4(%esp))
incl _cnt+V_SYSCALL
+ movl $SWI_AST_MASK,_cpl
call _syscall
/*
- * Return through doreti to handle ASTs.
+ * Return via _doreti to handle ASTs.
*/
- movl $T_ASTFLT,TF_TRAPNO(%esp) /* new trap type (err code not used) */
- pushl _cpl
- pushl $0
- jmp doreti
-
-#ifdef SHOW_A_LOT
-/*
- * 'show_bits' was too big when defined as a macro. The line length for some
- * enclosing macro was too big for gas. Perhaps the code would have blown
- * the cache anyway.
- */
- ALIGN_TEXT
-show_bits:
- pushl %eax
- SHOW_BIT(0)
- SHOW_BIT(1)
- SHOW_BIT(2)
- SHOW_BIT(3)
- SHOW_BIT(4)
- SHOW_BIT(5)
- SHOW_BIT(6)
- SHOW_BIT(7)
- SHOW_BIT(8)
- SHOW_BIT(9)
- SHOW_BIT(10)
- SHOW_BIT(11)
- SHOW_BIT(12)
- SHOW_BIT(13)
- SHOW_BIT(14)
- SHOW_BIT(15)
- popl %eax
- ret
-
- .data
-bit_colors:
- .byte GREEN,RED,0,0
- .text
-
-#endif /* SHOW_A_LOT */
+ pushl $0 /* cpl to restore */
+ subl $4,%esp
+ MEXITCOUNT
+ jmp _doreti
/*
* include generated interrupt vectors and ISA intr code
diff --git a/sys/i386/i386/in_cksum.c b/sys/i386/i386/in_cksum.c
index 0dec1d67056d..0b3d3e5d9637 100644
--- a/sys/i386/i386/in_cksum.c
+++ b/sys/i386/i386/in_cksum.c
@@ -32,7 +32,7 @@
*
* from tahoe: in_cksum.c 1.2 86/01/05
* from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91
- * $Id: in_cksum.c,v 1.4 1993/12/19 00:50:02 wollman Exp $
+ * $Id: in_cksum.c,v 1.5 1994/03/07 11:47:30 davidg Exp $
*/
#include "param.h"
@@ -56,9 +56,10 @@
* Thanks to gcc we don't have to guess
* which registers contain sum & w.
*/
-#define CLC asm("clc")
-#define ADD(n) asm("adcl " #n "(%2), %0": "=r"(sum): "0"(sum), "r"(w))
-#define MOP asm("adcl $0, %0": "=r"(sum): "0"(sum))
+#define ADD(n) asm("addl " #n "(%2), %0" : "=r" (sum) : "0" (sum), "r" (w))
+#define ADDC(n) asm("adcl " #n "(%2), %0" : "=r" (sum) : "0" (sum), "r" (w))
+#define LOAD(n) asm volatile("movb " #n "(%1), %0" : "=r" (junk) : "r" (w))
+#define MOP asm("adcl $0, %0" : "=r" (sum) : "0" (sum))
int
in_cksum(m, len)
@@ -114,28 +115,90 @@ in_cksum(m, len)
}
}
/*
+ * Advance to a 486 cache line boundary.
+ */
+ if (4 & (int) w && mlen >= 4) {
+ ADD(0);
+ MOP;
+ w += 2;
+ mlen -= 4;
+ }
+ if (8 & (int) w && mlen >= 8) {
+ ADD(0);
+ ADDC(4);
+ MOP;
+ w += 4;
+ mlen -= 8;
+ }
+ /*
* Do as much of the checksum as possible 32 bits at at time.
* In fact, this loop is unrolled to make overhead from
* branches &c small.
*/
+ mlen -= 1;
while ((mlen -= 32) >= 0) {
+ u_char junk;
/*
- * Clear the carry flag, add with carry 16 words
- * and fold-in last carry by adding a 0 with carry.
+ * Add with carry 16 words and fold in the last
+ * carry by adding a 0 with carry.
+ *
+ * The early ADD(16) and the LOAD(32) are to load
+ * the next 2 cache lines in advance on 486's. The
+ * 486 has a penalty of 2 clock cycles for loading
+ * a cache line, plus whatever time the external
+ * memory takes to load the first word(s) addressed.
+ * These penalties are unavoidable. Subsequent
+ * accesses to a cache line being loaded (and to
+ * other external memory?) are delayed until the
+ * whole load finishes. These penalties are mostly
+ * avoided by not accessing external memory for
+ * 8 cycles after the ADD(16) and 12 cycles after
+ * the LOAD(32). The loop terminates when mlen
+ * is initially 33 (not 32) to guaranteed that
+ * the LOAD(32) is within bounds.
*/
- CLC;
- ADD(0); ADD(4); ADD(8); ADD(12);
- ADD(16); ADD(20); ADD(24); ADD(28);
- MOP; w += 16;
+ ADD(16);
+ ADDC(0);
+ ADDC(4);
+ ADDC(8);
+ ADDC(12);
+ LOAD(32);
+ ADDC(20);
+ ADDC(24);
+ ADDC(28);
+ MOP;
+ w += 16;
+ }
+ mlen += 32 + 1;
+ if (mlen >= 32) {
+ ADD(16);
+ ADDC(0);
+ ADDC(4);
+ ADDC(8);
+ ADDC(12);
+ ADDC(20);
+ ADDC(24);
+ ADDC(28);
+ MOP;
+ w += 16;
+ mlen -= 32;
}
- mlen += 32;
- while ((mlen -= 8) >= 0) {
- CLC;
- ADD(0); ADD(4);
+ if (mlen >= 16) {
+ ADD(0);
+ ADDC(4);
+ ADDC(8);
+ ADDC(12);
+ MOP;
+ w += 8;
+ mlen -= 16;
+ }
+ if (mlen >= 8) {
+ ADD(0);
+ ADDC(4);
MOP;
w += 4;
+ mlen -= 8;
}
- mlen += 8;
if (mlen == 0 && byte_swapped == 0)
continue; /* worth 1% maybe ?? */
REDUCE;
@@ -172,4 +235,3 @@ in_cksum(m, len)
REDUCE;
return (~sum & 0xffff);
}
-
diff --git a/sys/i386/i386/locore.s b/sys/i386/i386/locore.s
index 57ee6a93832d..d0317103d43e 100644
--- a/sys/i386/i386/locore.s
+++ b/sys/i386/i386/locore.s
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)locore.s 7.3 (Berkeley) 5/13/91
- * $Id: locore.s,v 1.15 1994/02/01 04:08:54 davidg Exp $
+ * $Id: locore.s,v 1.20 1994/06/14 17:49:42 davidg Exp $
*/
/*
@@ -51,7 +51,6 @@
#include "machine/pte.h" /* page table entry definitions */
#include "errno.h" /* error return codes */
#include "machine/specialreg.h" /* x86 special registers */
-#include "i386/isa/debug.h" /* BDE debugging macros */
#include "machine/cputypes.h" /* x86 cpu type definitions */
#include "syscall.h" /* system call numbers */
#include "machine/asmacros.h" /* miscellaneous asm macros */
@@ -102,8 +101,10 @@ _esym: .long 0 /* ptr to end of syms */
.globl _boothowto,_bootdev,_curpcb
- .globl _cpu,_cold,_atdevbase
+ .globl _cpu,_cold,_atdevbase,_cpu_vendor,_cpu_id
_cpu: .long 0 /* are we 386, 386sx, or 486 */
+_cpu_id: .long 0
+_cpu_vendor: .space 17
_cold: .long 1 /* cold till we are not */
_atdevbase: .long 0 /* location of start of iomem in virtual */
_atdevphys: .long 0 /* location of device mapping ptes (phys) */
@@ -123,7 +124,7 @@ _proc0paddr: .long 0 /* address of proc 0 address space */
#ifdef BDE_DEBUGGER
.globl _bdb_exists /* flag to indicate BDE debugger is available */
-_bde_exists: .long 0
+_bdb_exists: .long 0
#endif
.globl tmpstk
@@ -140,10 +141,10 @@ tmpstk:
* btext: beginning of text section.
* Also the entry point (jumped to directly from the boot blocks).
*/
-ENTRY(btext)
+NON_GPROF_ENTRY(btext)
movw $0x1234,0x472 /* warm boot */
jmp 1f
- .space 0x500 /* skip over warm boot shit */
+ .org 0x500 /* space for BIOS variables */
/*
* pass parameters on stack (howto, bootdev, unit, cyloffset, esym)
@@ -151,7 +152,8 @@ ENTRY(btext)
* ( if we want to hold onto /boot, it's physical %esp up to _end)
*/
- 1: movl 4(%esp),%eax
+ 1:
+ movl 4(%esp),%eax
movl %eax,_boothowto-KERNBASE
movl 8(%esp),%eax
movl %eax,_bootdev-KERNBASE
@@ -164,30 +166,76 @@ ENTRY(btext)
movl _nfs_diskless_size-KERNBASE,%ecx
movl 20(%esp),%esi
movl $(_nfs_diskless-KERNBASE),%edi
+ cld
rep
movsb
#endif
- /* find out our CPU type. */
- pushfl
- popl %eax
- movl %eax,%ecx
- xorl $0x40000,%eax
- pushl %eax
- popfl
- pushfl
- popl %eax
- xorl %ecx,%eax
- shrl $18,%eax
- andl $1,%eax
- push %ecx
- popfl
-
- cmpl $0,%eax
- jne 1f
- movl $CPU_386,_cpu-KERNBASE
+ /* don't trust what the BIOS gives for eflags */
+ pushl $PSL_MBO
+ popfl
+
+ /* Find out our CPU type. */
+
+ /* 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,_cpu-KERNBASE
+ jmp 2f
+
+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,_cpu-KERNBASE
+ jmp 2f
+
+1: /* Use the `cpuid' instruction. */
+ xorl %eax,%eax
+ .byte 0x0f,0xa2 # cpuid 0
+ movl %ebx,_cpu_vendor-KERNBASE # store vendor string
+ movl %edx,_cpu_vendor+4-KERNBASE
+ movl %ecx,_cpu_vendor+8-KERNBASE
+ movb $0,_cpu_vendor+12-KERNBASE
+
+ movl $1,%eax
+ .byte 0x0f,0xa2 # cpuid 1
+ movl %eax,_cpu_id-KERNBASE # store cpu_id
+ rorl $8,%eax # extract family type
+ andl $15,%eax
+ cmpl $5,%eax
+ jae 1f
+
+ /* less than Pentium; must be 486 */
+ movl $CPU_486,_cpu-KERNBASE
jmp 2f
-1: movl $CPU_486,_cpu-KERNBASE
+
+1: movl $CPU_586,_cpu-KERNBASE
2:
/*
@@ -217,7 +265,7 @@ ENTRY(btext)
movl $_end-KERNBASE,%ecx
addl $NBPG-1,%ecx /* page align up */
andl $~(NBPG-1),%ecx
- movl %ecx,%esi /* esi=start of tables */
+ movl %ecx,%esi /* esi = start of free memory */
movl %ecx,_KERNend-KERNBASE /* save end of kernel */
/* clear bss */
@@ -296,7 +344,7 @@ ENTRY(btext)
shrl $PGSHIFT,%ecx
orl $PG_V|PG_KW,%eax /* valid, kernel read/write */
fillkpt
-#endif
+#endif /* KGDB || BDE_DEBUGGER */
/* now initialize the page dir, upages, p0stack PT, and page tables */
@@ -309,7 +357,7 @@ ENTRY(btext)
addl %esi,%ebx /* address of page directory */
addl $((1+UPAGES+1)*NBPG),%ebx /* offset to kernel page tables */
fillkpt
-
+
/* map I/O memory map */
movl _KPTphys-KERNBASE,%ebx /* base of kernel page tables */
@@ -368,6 +416,7 @@ ENTRY(btext)
movl $_gdt-KERNBASE,%edi
movl %edi,2(%esp)
movl $8*18/4,%ecx
+ cld
rep /* copy gdt */
movsl
movl $_gdt-KERNBASE,-8+2(%edi) /* adjust gdt self-ptr */
@@ -389,6 +438,7 @@ ENTRY(btext)
movl $_idt-KERNBASE,%edi
movl %edi,6+2(%esp)
movl $8*4/4,%ecx
+ cld
rep /* copy idt */
movsl
@@ -397,7 +447,7 @@ ENTRY(btext)
addl $2*6,%esp
popal
-#endif
+#endif /* BDE_DEBUGGER */
/* load base of page directory and enable mapping */
movl %esi,%eax /* phys address of ptd in proc 0 */
@@ -436,7 +486,7 @@ begin: /* now running relocated at KERNBASE where the system is linked to run */
movl $_gdt+8*9,%eax /* adjust slots 9-17 */
movl $9,%ecx
reloc_gdt:
- movb $0xfe,7(%eax) /* top byte of base addresses, was 0, */
+ movb $KERNBASE>>24,7(%eax) /* top byte of base addresses, was 0, */
addl $8,%eax /* now KERNBASE>>24 */
loop reloc_gdt
@@ -444,7 +494,7 @@ reloc_gdt:
je 1f
int $3
1:
-#endif
+#endif /* BDE_DEBUGGER */
/*
* Skip over the page tables and the kernel stack
@@ -494,7 +544,7 @@ lretmsg1:
.asciz "lret: toinit\n"
-#define LCALL(x,y) .byte 0x9a ; .long y; .word x
+#define LCALL(x,y) .byte 0x9a ; .long y ; .word x
/*
* Icode is copied out to process 1 and executed in user mode:
* execve("/sbin/init", argv, envp); exit(0);
@@ -551,4 +601,3 @@ NON_GPROF_ENTRY(sigcode)
.globl _szsigcode
_szsigcode:
.long _szsigcode-_sigcode
-
diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c
index 3e0a368b4b44..05546058fbd0 100644
--- a/sys/i386/i386/machdep.c
+++ b/sys/i386/i386/machdep.c
@@ -35,13 +35,12 @@
* SUCH DAMAGE.
*
* from: @(#)machdep.c 7.4 (Berkeley) 6/3/91
- * $Id: machdep.c,v 1.36.2.5 1994/04/18 04:56:56 rgrimes Exp $
+ * $Id: machdep.c,v 1.47 1994/06/17 13:32:07 davidg Exp $
*/
#include "npx.h"
#include "isa.h"
-#include <stddef.h>
#include "param.h"
#include "systm.h"
#include "signalvar.h"
@@ -58,7 +57,6 @@
#include "malloc.h"
#include "mbuf.h"
#include "msgbuf.h"
-#include "net/netisr.h"
#ifdef SYSVSHM
#include "sys/shm.h"
@@ -96,6 +94,8 @@ static void initcpu(void);
static int test_page(int *, int);
extern int grow(struct proc *,int);
+const char machine[] = "PC-Class";
+const char *cpu_model;
#ifndef PANIC_REBOOT_WAIT_TIME
#define PANIC_REBOOT_WAIT_TIME 15 /* default to 15 seconds */
@@ -115,22 +115,22 @@ int bufpages = BUFPAGES;
#else
int bufpages = 0;
#endif
+#ifdef BOUNCEPAGES
+int bouncepages = BOUNCEPAGES;
+#else
+int bouncepages = 0;
+#endif
extern int freebufspace;
+extern char *bouncememory;
int _udatasel, _ucodesel;
/*
* Machine-dependent startup code
*/
-int boothowto = 0, Maxmem = 0, maxmem = 0, badpages = 0, physmem = 0;
+int boothowto = 0, Maxmem = 0, badpages = 0, physmem = 0;
long dumplo;
extern int bootdev;
-#ifdef SMALL
-extern int forcemaxmem;
-#endif
-#if defined(GENERICAH) || defined(GENERICBT)
-int generic_hack = 1;
-#endif
int biosmem;
vm_offset_t phys_avail[6];
@@ -141,6 +141,8 @@ int cpu_class;
void dumpsys __P((void));
+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
+
void
cpu_startup()
{
@@ -255,6 +257,7 @@ again:
panic("startup: no room for tables");
goto again;
}
+
/*
* End of second pass, addresses have been assigned
*/
@@ -295,6 +298,22 @@ again:
printf("using %d buffers containing %d bytes of memory\n",
nbuf, bufpages * CLBYTES);
+#ifndef NOBOUNCE
+ /*
+ * If there is more than 16MB of memory, allocate some bounce buffers
+ */
+ if (Maxmem > 4096) {
+ if (bouncepages == 0)
+ bouncepages = 96; /* largest physio size + extra */
+ bouncememory = (char *)kmem_alloc(kernel_map, bouncepages * PAGE_SIZE);
+ }
+
+ /*
+ * init bounce buffers
+ */
+ vm_bounce_init();
+#endif
+
/*
* Set up CPU-specific registers, cache, etc.
*/
@@ -324,10 +343,13 @@ struct cpu_nameclass i386_cpus[] = {
static void
identifycpu()
{
+ extern unsigned long cpu_id;
+ extern char cpu_vendor[];
printf("CPU: ");
if (cpu >= 0 && cpu < (sizeof i386_cpus/sizeof(struct cpu_nameclass))) {
printf("%s", i386_cpus[cpu].cpu_name);
cpu_class = i386_cpus[cpu].cpu_class;
+ cpu_model = i386_cpus[cpu].cpu_name;
} else {
printf("unknown cpu type %d\n", cpu);
panic("startup: bad cpu id");
@@ -350,6 +372,10 @@ identifycpu()
printf("unknown"); /* will panic below... */
}
printf("-class CPU)");
+ if(cpu_id)
+ printf(" Id = 0x%x",cpu_id);
+ if(*cpu_vendor)
+ printf(" Origin = \"%s\"",cpu_vendor);
printf("\n"); /* cpu speed would be nice, but how? */
/*
@@ -503,7 +529,7 @@ sendsig(catcher, sig, mask, code)
* 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
- * psl to gain improper priviledges or to cause
+ * psl to gain improper privileges or to cause
* a machine fault.
*/
struct sigreturn_args {
@@ -558,11 +584,11 @@ sigreturn(p, uap, retval)
#define null_sel(sel) \
(!ISLDT(sel) && IDXSEL(sel) == 0)
- if ((scp->sc_cs&0xffff != _ucodesel && !valid_ldt_sel(scp->sc_cs)) ||
- (scp->sc_ss&0xffff != _udatasel && !valid_ldt_sel(scp->sc_ss)) ||
- (scp->sc_ds&0xffff != _udatasel && !valid_ldt_sel(scp->sc_ds) &&
+ if (((scp->sc_cs&0xffff) != _ucodesel && !valid_ldt_sel(scp->sc_cs)) ||
+ ((scp->sc_ss&0xffff) != _udatasel && !valid_ldt_sel(scp->sc_ss)) ||
+ ((scp->sc_ds&0xffff) != _udatasel && !valid_ldt_sel(scp->sc_ds) &&
!null_sel(scp->sc_ds)) ||
- (scp->sc_es&0xffff != _udatasel && !valid_ldt_sel(scp->sc_es) &&
+ ((scp->sc_es&0xffff) != _udatasel && !valid_ldt_sel(scp->sc_es) &&
!null_sel(scp->sc_es))) {
#ifdef DEBUG
printf("sigreturn: cs=0x%x ss=0x%x ds=0x%x es=0x%x\n",
@@ -709,10 +735,10 @@ boot(arghowto)
#endif
die:
printf("Rebooting...\n");
- DELAY (100000); /* wait 100ms for printf's to complete */
+ DELAY(1000000); /* wait 1 sec for printf's to complete and be read */
cpu_reset();
for(;;) ;
- /*NOTREACHED*/
+ /* NOTREACHED */
}
unsigned long dumpmag = 0x8fca0101UL; /* magic number for savecore */
@@ -782,36 +808,6 @@ microtime(tvp)
}
#endif /* HZ */
-void
-physstratdone(bp)
- struct buf *bp;
-{
- wakeup((caddr_t) bp);
- bp->b_flags &= ~B_CALL;
-}
-
-void
-physstrat(bp, strat, prio)
- struct buf *bp;
- int (*strat)(), prio;
-{
- register int s;
- caddr_t baddr;
-
- bp->b_flags |= B_CALL;
- bp->b_iodone = physstratdone;
- vmapbuf(bp);
- (*strat)(bp);
- /* pageout daemon doesn't wait for pushed pages */
- if (bp->b_flags & B_DIRTY)
- return;
- s = splbio();
- while ((bp->b_flags & B_DONE) == 0)
- tsleep((caddr_t)bp, prio, "physstr", 0);
- splx(s);
- vunmapbuf(bp);
-}
-
static void
initcpu()
{
@@ -1001,7 +997,7 @@ setidt(idx, func, typ, dpl)
ip->gd_hioffset = ((int)func)>>16 ;
}
-#define IDTVEC(name) __CONCAT(X, name)
+#define IDTVEC(name) __CONCAT(X,name)
typedef void idtvec_t();
extern idtvec_t
@@ -1012,7 +1008,7 @@ extern idtvec_t
IDTVEC(rsvd1), IDTVEC(rsvd2), IDTVEC(rsvd3), IDTVEC(rsvd4),
IDTVEC(rsvd5), IDTVEC(rsvd6), IDTVEC(rsvd7), IDTVEC(rsvd8),
IDTVEC(rsvd9), IDTVEC(rsvd10), IDTVEC(rsvd11), IDTVEC(rsvd12),
- IDTVEC(rsvd13), IDTVEC(rsvd14), IDTVEC(rsvd14), IDTVEC(syscall);
+ IDTVEC(rsvd13), IDTVEC(rsvd14), IDTVEC(syscall);
int _gsel_tss;
@@ -1044,8 +1040,9 @@ init386(first)
* the address space
*/
gdt_segs[GCODE_SEL].ssd_limit = i386_btop(i386_round_page(&etext)) - 1;
- gdt_segs[GDATA_SEL].ssd_limit = 0xffffffffUL; /* XXX constant? */
+ gdt_segs[GDATA_SEL].ssd_limit = i386_btop(0) - 1;
for (x=0; x < NGDT; x++) ssdtosd(gdt_segs+x, gdt+x);
+
/* make ldt memory segments */
/*
* The data segment limit must not cover the user area because we
@@ -1146,7 +1143,7 @@ init386(first)
#ifndef LARGEMEM
if (biosextmem > 65536) {
panic("extended memory beyond limit of 64MB");
- /* NOT REACHED */
+ /* NOTREACHED */
}
#endif
@@ -1169,25 +1166,6 @@ init386(first)
if ((pagesinext > 3840) && (pagesinext < 4096))
pagesinext = 3840;
-#if defined(GENERICAH) || defined(GENERICBT)
- /* XXX This is an ugle hack so that machines with >16MB of memory
- * can be booted using the GENERIC* kernels and not have to worry
- * about bus mastered DMA on the ISA bus. It is ONLY compiled into
- * the GENERIC* kernels and can be disabled by tweaking the global
- * generic_hack to be zero using gdb.
- */
- if (generic_hack) {
- if (pagesinext > 3840) {
- printf("WARNING WARNING WARNING WARNING WARNING WARNING\n");
- printf("GENERIC* kernels only USE the first 16MB of your ");
- printf("%dMB.\n", (pagesinext + 256) / 256);
- printf("Read the RELNOTES.FreeBSD file for the reason.\n");
- printf("WARNING WARNING WARNING WARNING WARNING WARNING\n");
- pagesinext = 3840;
- }
- }
-#endif /* defined (GENERICAH) || defiend(GENERICBT) */
-
/*
* Maxmem isn't the "maximum memory", it's the highest page of
* of the physical address space. It should be "Maxphyspage".
@@ -1266,9 +1244,9 @@ init386(first)
}
}
printf("done.\n");
-
- maxmem = Maxmem - 1; /* highest page of usable memory */
- avail_end = (maxmem << PAGE_SHIFT) - i386_round_page(sizeof(struct msgbuf));
+
+ avail_end = (Maxmem << PAGE_SHIFT)
+ - i386_round_page(sizeof(struct msgbuf));
/*
* Initialize pointers to the two chunks of memory; for use
@@ -1334,15 +1312,6 @@ test_page(address, pattern)
return(0);
}
-/*aston() {
- schednetisr(NETISR_AST);
-}*/
-
-void
-setsoftclock() {
- schednetisr(NETISR_SCLK);
-}
-
/*
* insert an element into a queue
*/
diff --git a/sys/i386/i386/math_emulate.c b/sys/i386/i386/math_emulate.c
index 256f8fb1033d..c6df09e9dddb 100644
--- a/sys/i386/i386/math_emulate.c
+++ b/sys/i386/i386/math_emulate.c
@@ -6,7 +6,7 @@
* [expediant "port" of linux 8087 emulator to 386BSD, with apologies -wfj]
*
* from: 386BSD 0.1
- * $Id: math_emulate.c,v 1.7 1994/01/29 22:07:16 nate Exp $
+ * $Id: math_emulate.c,v 1.9 1994/06/05 19:31:45 ats Exp $
*/
/*
@@ -107,243 +107,255 @@ math_emulate(struct trapframe * info)
switch (code) {
case 0x1d0: /* fnop */
return(0);
- case 0x1d1: case 0x1d2: case 0x1d3:
+ case 0x1d1: case 0x1d2: case 0x1d3: /* fst to 32-bit mem */
case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7:
math_abort(info,SIGILL);
- case 0x1e0:
+ case 0x1e0: /* fchs */
ST(0).exponent ^= 0x8000;
return(0);
- case 0x1e1:
+ case 0x1e1: /* fabs */
ST(0).exponent &= 0x7fff;
return(0);
case 0x1e2: case 0x1e3:
math_abort(info,SIGILL);
- case 0x1e4:
+ case 0x1e4: /* ftst */
ftst(PST(0));
return(0);
- case 0x1e5:
+ case 0x1e5: /* fxam */
printf("fxam not implemented\n\r");
math_abort(info,SIGILL);
- case 0x1e6: case 0x1e7:
+ case 0x1e6: case 0x1e7: /* fldenv */
math_abort(info,SIGILL);
- case 0x1e8:
+ case 0x1e8: /* fld1 */
fpush();
ST(0) = CONST1;
return(0);
- case 0x1e9:
+ case 0x1e9: /* fld2t */
fpush();
ST(0) = CONSTL2T;
return(0);
- case 0x1ea:
+ case 0x1ea: /* fld2e */
fpush();
ST(0) = CONSTL2E;
return(0);
- case 0x1eb:
+ case 0x1eb: /* fldpi */
fpush();
ST(0) = CONSTPI;
return(0);
- case 0x1ec:
+ case 0x1ec: /* fldlg2 */
fpush();
ST(0) = CONSTLG2;
return(0);
- case 0x1ed:
+ case 0x1ed: /* fldln2 */
fpush();
ST(0) = CONSTLN2;
return(0);
- case 0x1ee:
+ case 0x1ee: /* fldz */
fpush();
ST(0) = CONSTZ;
return(0);
case 0x1ef:
math_abort(info,SIGILL);
- case 0x1f0: case 0x1f1: case 0x1f2: case 0x1f3:
- case 0x1f4: case 0x1f5: case 0x1f6: case 0x1f7:
- case 0x1f8: case 0x1f9: case 0x1fa: case 0x1fb:
- case 0x1fe: case 0x1ff:
+ case 0x1f0: /* f2xm1 */
+ case 0x1f1: /* fyl2x */
+ case 0x1f2: /* fptan */
+ case 0x1f3: /* fpatan */
+ case 0x1f4: /* fxtract */
+ case 0x1f5: /* fprem1 */
+ case 0x1f6: /* fdecstp */
+ case 0x1f7: /* fincstp */
+ case 0x1f8: /* fprem */
+ case 0x1f9: /* fyl2xp1 */
+ case 0x1fa: /* fsqrt */
+ case 0x1fb: /* fsincos */
+ case 0x1fe: /* fsin */
+ case 0x1ff: /* fcos */
uprintf(
"math_emulate: instruction %04x not implemented\n",
code + 0xd800);
math_abort(info,SIGILL);
- case 0x1fd:
+ case 0x1fc: /* frndint */
+ frndint(PST(0),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return(0);
+ case 0x1fd: /* fscale */
/* incomplete and totally inadequate -wfj */
Fscale(PST(0), PST(1), &tmp);
real_to_real(&tmp,&ST(0));
return(0); /* 19 Sep 92*/
- case 0x1fc:
- frndint(PST(0),&tmp);
- real_to_real(&tmp,&ST(0));
- return(0);
- case 0x2e9:
+ case 0x2e9: /* ????? */
+/* if this should be a fucomp ST(0),ST(1) , it must be a 0x3e9 ATS */
fucom(PST(1),PST(0));
fpop(); fpop();
return(0);
- case 0x3d0: case 0x3d1:
+ case 0x3d0: case 0x3d1: /* fist ?? */
return(0);
- case 0x3e2:
+ case 0x3e2: /* fclex */
I387.swd &= 0x7f00;
return(0);
- case 0x3e3:
+ case 0x3e3: /* fninit */
I387.cwd = 0x037f;
I387.swd = 0x0000;
I387.twd = 0x0000;
return(0);
case 0x3e4:
return(0);
- case 0x6d9:
+ case 0x6d9: /* fcompp */
fcom(PST(1),PST(0));
fpop(); fpop();
return(0);
- case 0x7e0:
+ case 0x7e0: /* fstsw ax */
*(short *) &info->tf_eax = I387.swd;
return(0);
}
switch (code >> 3) {
- case 0x18:
+ case 0x18: /* fadd */
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
return(0);
- case 0x19:
+ case 0x19: /* fmul */
fmul(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
return(0);
- case 0x1a:
+ case 0x1a: /* fcom */
fcom(PST(code & 7),PST(0));
return(0);
- case 0x1b:
+ case 0x1b: /* fcomp */
fcom(PST(code & 7),PST(0));
fpop();
return(0);
- case 0x1c:
+ case 0x1c: /* fsubr */
real_to_real(&ST(code & 7),&tmp);
tmp.exponent ^= 0x8000;
fadd(PST(0),&tmp,&tmp);
real_to_real(&tmp,&ST(0));
return(0);
- case 0x1d:
+ case 0x1d: /* fsub */
ST(0).exponent ^= 0x8000;
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
return(0);
- case 0x1e:
+ case 0x1e: /* fdivr */
fdiv(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
return(0);
- case 0x1f:
+ case 0x1f: /* fdiv */
fdiv(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(0));
return(0);
- case 0x38:
+ case 0x38: /* fld */
fpush();
- ST(0) = ST((code & 7)+1);
+ ST(0) = ST((code & 7)+1); /* why plus 1 ????? ATS */
return(0);
- case 0x39:
+ case 0x39: /* fxch */
fxchg(&ST(0),&ST(code & 7));
return(0);
- case 0x3b:
+ case 0x3b: /* ??? ??? wrong ???? ATS */
ST(code & 7) = ST(0);
fpop();
return(0);
- case 0x98:
+ case 0x98: /* fadd */
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
return(0);
- case 0x99:
+ case 0x99: /* fmul */
fmul(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
return(0);
- case 0x9a:
+ case 0x9a: /* ???? , my manual don't list a direction bit
+for fcom , ??? ATS */
fcom(PST(code & 7),PST(0));
return(0);
- case 0x9b:
+ case 0x9b: /* same as above , ATS */
fcom(PST(code & 7),PST(0));
fpop();
return(0);
- case 0x9c:
+ case 0x9c: /* fsubr */
ST(code & 7).exponent ^= 0x8000;
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
return(0);
- case 0x9d:
+ case 0x9d: /* fsub */
real_to_real(&ST(0),&tmp);
tmp.exponent ^= 0x8000;
fadd(PST(code & 7),&tmp,&tmp);
real_to_real(&tmp,&ST(code & 7));
return(0);
- case 0x9e:
+ case 0x9e: /* fdivr */
fdiv(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
return(0);
- case 0x9f:
+ case 0x9f: /* fdiv */
fdiv(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(code & 7));
return(0);
- case 0xb8:
+ case 0xb8: /* ffree */
printf("ffree not implemented\n\r");
math_abort(info,SIGILL);
- case 0xb9:
+ case 0xb9: /* fstp ???? where is the pop ? ATS */
fxchg(&ST(0),&ST(code & 7));
return(0);
- case 0xba:
+ case 0xba: /* fst */
ST(code & 7) = ST(0);
return(0);
- case 0xbb:
+ case 0xbb: /* ????? encoding of fstp to mem ? ATS */
ST(code & 7) = ST(0);
fpop();
return(0);
- case 0xbc:
+ case 0xbc: /* fucom */
fucom(PST(code & 7),PST(0));
return(0);
- case 0xbd:
+ case 0xbd: /* fucomp */
fucom(PST(code & 7),PST(0));
fpop();
return(0);
- case 0xd8:
+ case 0xd8: /* faddp */
fadd(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return(0);
- case 0xd9:
+ case 0xd9: /* fmulp */
fmul(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return(0);
- case 0xda:
+ case 0xda: /* ??? encoding of ficom with 16 bit mem ? ATS */
fcom(PST(code & 7),PST(0));
fpop();
return(0);
- case 0xdc:
+ case 0xdc: /* fsubrp */
ST(code & 7).exponent ^= 0x8000;
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return(0);
- case 0xdd:
+ case 0xdd: /* fsubp */
real_to_real(&ST(0),&tmp);
tmp.exponent ^= 0x8000;
fadd(PST(code & 7),&tmp,&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return(0);
- case 0xde:
+ case 0xde: /* fdivrp */
fdiv(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return(0);
- case 0xdf:
+ case 0xdf: /* fdivp */
fdiv(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return(0);
- case 0xf8:
+ case 0xf8: /* fild 16-bit mem ???? ATS */
printf("ffree not implemented\n\r");
math_abort(info,SIGILL);
fpop();
return(0);
- case 0xf9:
+ case 0xf9: /* ????? ATS */
fxchg(&ST(0),&ST(code & 7));
return(0);
- case 0xfa:
- case 0xfb:
+ case 0xfa: /* fist 16-bit mem ? ATS */
+ case 0xfb: /* fistp 16-bit mem ? ATS */
ST(code & 7) = ST(0);
fpop();
return(0);
diff --git a/sys/i386/i386/microtime.s b/sys/i386/i386/microtime.s
index d167b6ee556a..4c77fd08117d 100644
--- a/sys/i386/i386/microtime.s
+++ b/sys/i386/i386/microtime.s
@@ -31,10 +31,10 @@
* SUCH DAMAGE.
*
* from: Steve McCanne's microtime code
- * $Id: microtime.s,v 1.2 1993/10/16 14:15:08 rgrimes Exp $
+ * $Id: microtime.s,v 1.4 1994/05/02 09:44:20 sos Exp $
*/
-#include "asm.h"
+#include "machine/asmacros.h"
#include "../isa/isa.h"
#include "../isa/timerreg.h"
@@ -44,31 +44,25 @@
*/
#ifndef HZ
ENTRY(microtime)
- pushl %edi
+ pushl %edi # save registers
pushl %esi
pushl %ebx
- movl $_time,%ebx
+ movl $_time, %ebx # get timeval ptr
+ movl (%ebx), %edi # sec = time.tv_sec
+ movl 4(%ebx), %esi # usec = time.tv_usec
cli # disable interrupts
- movl (%ebx),%edi # sec = time.tv_sec
- movl 4(%ebx),%esi # usec = time.tv_usec
+ movl $(TIMER_SEL0|TIMER_LATCH), %eax
+ outb %al, $TIMER_MODE # latch timer 0's counter
- movl $(TIMER_SEL0|TIMER_LATCH),%eax
- outb %al,$TIMER_MODE # latch timer 0's counter
+ xorl %ebx, %ebx # clear ebx
+ inb $TIMER_CNTR0, %al # Read counter value, LSB first
+ movb %al, %bl
+ inb $TIMER_CNTR0, %al
+ movb %al, %bh
- #
- # Read counter value into ebx, LSB first
- #
- inb $TIMER_CNTR0,%al
- movzbl %al,%ebx
- inb $TIMER_CNTR0,%al
- movzbl %al,%eax
- sall $8,%eax
- orl %eax,%ebx
-
- #
# 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
@@ -96,38 +90,45 @@ ENTRY(microtime)
# we're called from an ipl less than the clock. Otherwise,
# it might not work. Currently, only gettimeofday and bpf
# call microtime so it's not a problem.
- #
- cmpl $11890,%ebx
- jle 2f
- movl $0x0a,%eax # tell ICU we want IRR
- outb %al,$IO_ICU1
- inb $IO_ICU1,%al # read IRR in ICU
- testb $1,%al # is a timer interrupt pending?
+ movl _timer0_prescale, %eax # adjust value if timer is
+ addl _timer0_divisor, %eax # reprogrammed
+ addl $-11932, %eax
+ subl %eax, %ebx
+
+ cmpl $11890, %ebx # do we have a possible overflow condition
+ jle 1f
+
+ inb $IO_ICU1, %al # read IRR in ICU
+ testb $1, %al # is a timer interrupt pending?
je 1f
- addl $-11932,%ebx # yes, subtract one clock period
+ addl $-11932, %ebx # yes, subtract one clock period
1:
- movl $0x0b,%eax # tell ICU we want ISR
- outb %al,$IO_ICU1 # (rest of kernel expects this)
-2:
sti # enable interrupts
- movl $11932,%eax # subtract counter value from 11932 since
- subl %ebx,%eax # it is a count-down value
- imull $1000,%eax,%eax
- movl $0,%edx # zero extend eax for div
- movl $1193,%ecx
+ movl $11932, %eax # subtract counter value from 11932 since
+ subl %ebx, %eax # it is a count-down value
+
+ movl %eax, %ebx # this really is a "imull $1000, %eax, %eax"
+ sall $10, %eax # instruction, but this saves us
+ sall $3, %ebx # 33/23 clocks on a 486/386 CPU
+ subl %ebx, %eax #
+ sall $1, %ebx # /sos
+ subl %ebx, %eax #
+
+ movl $0, %edx # zero extend eax into edx for div
+ movl $1193, %ecx
idivl %ecx # convert to usecs: mult by 1000/1193
- addl %eax,%esi # add counter usecs to time.tv_usec
- cmpl $1000000,%esi # carry in timeval?
- jl 3f
- subl $1000000,%esi # adjust usec
+ addl %eax, %esi # add counter usecs to time.tv_usec
+ cmpl $1000000, %esi # carry in timeval?
+ jl 2f
+ subl $1000000, %esi # adjust usec
incl %edi # bump sec
-3:
- movl 16(%esp),%ecx # load timeval pointer arg
- movl %edi,(%ecx) # tvp->tv_sec = sec
- movl %esi,4(%ecx) # tvp->tv_usec = usec
+2:
+ movl 16(%esp), %ecx # load timeval pointer arg
+ movl %edi, (%ecx) # tvp->tv_sec = sec
+ movl %esi, 4(%ecx) # tvp->tv_usec = usec
popl %ebx # restore regs
popl %esi
diff --git a/sys/i386/i386/pmap.c b/sys/i386/i386/pmap.c
index da74b5f6455b..a1c04662cb8b 100644
--- a/sys/i386/i386/pmap.c
+++ b/sys/i386/i386/pmap.c
@@ -1,6 +1,10 @@
/*
* 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
@@ -35,7 +39,7 @@
* SUCH DAMAGE.
*
* from: @(#)pmap.c 7.7 (Berkeley) 5/12/91
- * $Id: pmap.c,v 1.19.2.1 1994/04/18 04:56:59 rgrimes Exp $
+ * $Id: pmap.c,v 1.26 1994/06/11 17:09:39 paul Exp $
*/
/*
@@ -51,7 +55,8 @@
* Major modifications by John S. Dyson primarily to support
* pageable page tables, eliminating pmap_attributes,
* discontiguous memory pages, and using more efficient string
- * instructions. Jan 13, 1994.
+ * instructions. Jan 13, 1994. Further modifications on Mar 2, 1994,
+ * general clean-up and efficiency mods.
*/
/*
@@ -86,6 +91,7 @@
#include "malloc.h"
#include "user.h"
#include "i386/include/cpufunc.h"
+#include "i386/include/cputypes.h"
#include "vm/vm.h"
#include "vm/vm_kern.h"
@@ -99,55 +105,6 @@
*/
#define BSDVM_COMPAT 1
-#ifdef DEBUG
-struct {
- int kernel; /* entering kernel mapping */
- int user; /* entering user mapping */
- int ptpneeded; /* needed to allocate a PT page */
- int pwchange; /* no mapping change, just wiring or protection */
- int wchange; /* no mapping change, just wiring */
- int mchange; /* was mapped but mapping to different page */
- int managed; /* a managed page */
- int firstpv; /* first mapping for this PA */
- int secondpv; /* second mapping for this PA */
- int ci; /* cache inhibited */
- int unmanaged; /* not a managed page */
- int flushes; /* cache flushes */
-} enter_stats;
-struct {
- int calls;
- int removes;
- int pvfirst;
- int pvsearch;
- int ptinvalid;
- int uflushes;
- int sflushes;
-} remove_stats;
-
-int debugmap = 0;
-int pmapdebug = 0 /* 0xffff */;
-#define PDB_FOLLOW 0x0001
-#define PDB_INIT 0x0002
-#define PDB_ENTER 0x0004
-#define PDB_REMOVE 0x0008
-#define PDB_CREATE 0x0010
-#define PDB_PTPAGE 0x0020
-#define PDB_CACHE 0x0040
-#define PDB_BITS 0x0080
-#define PDB_COLLECT 0x0100
-#define PDB_PROTECT 0x0200
-#define PDB_PDRTAB 0x0400
-#define PDB_PARANOIA 0x2000
-#define PDB_WIRING 0x4000
-#define PDB_PVDUMP 0x8000
-
-int pmapvacflush = 0;
-#define PVF_ENTER 0x01
-#define PVF_REMOVE 0x02
-#define PVF_PROTECT 0x04
-#define PVF_TOTAL 0x80
-#endif
-
/*
* Get PDEs and PTEs for user/kernel address space
*/
@@ -156,14 +113,14 @@ int pmapvacflush = 0;
#define pmap_pte_pa(pte) (*(int *)(pte) & PG_FRAME)
-#define pmap_pde_v(pte) ((pte)->pd_v)
-#define pmap_pte_w(pte) ((pte)->pg_w)
-/* #define pmap_pte_ci(pte) ((pte)->pg_ci) */
-#define pmap_pte_m(pte) ((pte)->pg_m)
-#define pmap_pte_u(pte) ((pte)->pg_u)
-#define pmap_pte_v(pte) ((pte)->pg_v)
-#define pmap_pte_set_w(pte, v) ((pte)->pg_w = (v))
-#define pmap_pte_set_prot(pte, v) ((pte)->pg_prot = (v))
+#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_U) != 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,
@@ -184,19 +141,21 @@ vm_offset_t virtual_end; /* VA of last avail page (end of kernel AS) */
int i386pagesperpage; /* PAGE_SIZE / I386_PAGE_SIZE */
boolean_t pmap_initialized = FALSE; /* Has pmap_init completed? */
vm_offset_t vm_first_phys, vm_last_phys;
-vm_offset_t pager_sva, pager_eva;
-
-static inline boolean_t pmap_testbit();
-static inline void pmap_changebit();
-static inline int pmap_is_managed();
-static inline void *vm_get_pmap();
-static inline void vm_put_pmap();
-static inline void pmap_use_pt();
-inline struct pte *pmap_pte();
-static inline pv_entry_t get_pv_entry();
-void pmap_alloc_pv_entry();
-void pmap_clear_modify();
-void i386_protection_init();
+
+static inline boolean_t pmap_testbit();
+static inline void pmap_changebit();
+static inline int pmap_is_managed();
+static inline void *vm_get_pmap();
+static inline void vm_put_pmap();
+inline void pmap_use_pt();
+inline void pmap_unuse_pt();
+inline pt_entry_t * const pmap_pte();
+static inline pv_entry_t get_pv_entry();
+void pmap_alloc_pv_entry();
+void pmap_clear_modify();
+void i386_protection_init();
+extern vm_offset_t pager_sva, pager_eva;
+extern int cpu_class;
#if BSDVM_COMPAT
#include "msgbuf.h"
@@ -204,13 +163,12 @@ void i386_protection_init();
/*
* All those kernel PT submaps that BSD is so fond of
*/
-struct pte *CMAP1, *CMAP2, *mmap;
+pt_entry_t *CMAP1, *CMAP2, *mmap;
caddr_t CADDR1, CADDR2, vmmap;
-struct pte *msgbufmap;
+pt_entry_t *msgbufmap;
struct msgbuf *msgbufp;
#endif
-struct vm_map * pmap_fmap(pmap_t pmap) ;
void init_pv_entries(int) ;
/*
@@ -221,26 +179,24 @@ void init_pv_entries(int) ;
* [ what about induced faults -wfj]
*/
-inline struct pte *
-pmap_pte(pmap, va)
+inline pt_entry_t *
+const pmap_pte(pmap, va)
register pmap_t pmap;
vm_offset_t va;
{
- if (pmap && pmap_pde_v(pmap_pde(pmap, va))) {
+ if (pmap && *pmap_pde(pmap, va)) {
+ vm_offset_t frame = (int) pmap->pm_pdir[PTDPTDI] & PG_FRAME;
/* are we current address space or kernel? */
- if (pmap->pm_pdir[PTDPTDI].pd_pfnum == PTDpde.pd_pfnum
- || pmap == kernel_pmap)
- return ((struct pte *) vtopte(va));
-
+ if ( (pmap == kernel_pmap) || (frame == ((int) PTDpde & PG_FRAME)))
+ return ((pt_entry_t *) vtopte(va));
/* otherwise, we are alternate address space */
else {
- if (pmap->pm_pdir[PTDPTDI].pd_pfnum
- != APTDpde.pd_pfnum) {
+ if ( frame != ((int) APTDpde & PG_FRAME) ) {
APTDpde = pmap->pm_pdir[PTDPTDI];
tlbflush();
}
- return((struct pte *) avtopte(va));
+ return((pt_entry_t *) avtopte(va));
}
}
return(0);
@@ -258,40 +214,34 @@ pmap_extract(pmap, va)
register pmap_t pmap;
vm_offset_t va;
{
- struct pde save;
+ pd_entry_t save;
vm_offset_t pa;
int s;
- s = splhigh();
- if (pmap && pmap_pde_v(pmap_pde(pmap, va))) {
+ if (pmap && *pmap_pde(pmap, va)) {
+ vm_offset_t frame = (int) pmap->pm_pdir[PTDPTDI] & PG_FRAME;
/* are we current address space or kernel? */
- if (pmap->pm_pdir[PTDPTDI].pd_pfnum == PTDpde.pd_pfnum
- || pmap == kernel_pmap) {
+ if ( (pmap == kernel_pmap)
+ || (frame == ((int) PTDpde & PG_FRAME)) ) {
pa = *(int *) vtopte(va);
/* otherwise, we are alternate address space */
} else {
- if (pmap->pm_pdir[PTDPTDI].pd_pfnum
- != APTDpde.pd_pfnum) {
- save = APTDpde;
+ if ( frame != ((int) APTDpde & PG_FRAME)) {
APTDpde = pmap->pm_pdir[PTDPTDI];
tlbflush();
- pa = *(int *) avtopte(va);
- APTDpde = save;
- tlbflush();
- } else {
- tlbflush();
- pa = *(int *) avtopte(va);
}
+ pa = *(int *) avtopte(va);
}
pa = (pa & PG_FRAME) | (va & ~PG_FRAME);
- splx(s);
return pa;
}
- splx(s);
return 0;
}
+/*
+ * determine if a page is managed (memory vs. device)
+ */
static inline int
pmap_is_managed(pa)
vm_offset_t pa;
@@ -309,36 +259,51 @@ pmap_is_managed(pa)
}
/*
- * increment/decrement pmap wiring count
+ * find the vm_page_t of a pte (only) given va of pte and pmap
*/
-static inline void
-pmap_use_pt(pmap, va, use)
+inline vm_page_t
+pmap_pte_vm_page(pmap, pt)
+ pmap_t pmap;
+ vm_offset_t pt;
+{
+ pt = i386_trunc_page( pt);
+ pt = (pt - UPT_MIN_ADDRESS) / NBPG;
+ pt = ((vm_offset_t) pmap->pm_pdir[pt]) & PG_FRAME;
+ return PHYS_TO_VM_PAGE(pt);
+}
+
+/*
+ * Wire a page table page
+ */
+inline void
+pmap_use_pt(pmap, va)
pmap_t pmap;
vm_offset_t va;
- int use;
{
- vm_offset_t pt, pa;
- pv_entry_t pv;
- vm_page_t m;
+ vm_offset_t pt;
- if (va >= VM_MAX_ADDRESS)
+ if (va >= VM_MAX_ADDRESS || !pmap_initialized)
return;
- pt = i386_trunc_page(vtopte(va));
- pa = pmap_extract(pmap, pt);
- if (pa == 0) {
- printf("Warning pmap_use_pt pte paging failure\n");
- }
- if (!pa || !pmap_is_managed(pa))
- return;
- pv = pa_to_pvh(pa);
-
- m = PHYS_TO_VM_PAGE(pa);
- if (use) {
- vm_page_wire(m);
- } else {
- vm_page_unwire(m);
- }
+ pt = (vm_offset_t) vtopte(va);
+ vm_page_hold( pmap_pte_vm_page(pmap, pt));
+}
+
+/*
+ * Unwire a page table page
+ */
+inline void
+pmap_unuse_pt(pmap, va)
+ pmap_t pmap;
+ vm_offset_t va;
+{
+ vm_offset_t pt;
+
+ if (va >= VM_MAX_ADDRESS || !pmap_initialized)
+ return;
+
+ pt = (vm_offset_t) vtopte(va);
+ vm_page_unhold( pmap_pte_vm_page(pmap, pt));
}
/* [ macro again?, should I force kstack into user map here? -wfj ] */
@@ -370,7 +335,7 @@ pmap_bootstrap(firstaddr, loadaddr)
{
#if BSDVM_COMPAT
vm_offset_t va;
- struct pte *pte;
+ pt_entry_t *pte;
#endif
extern int IdlePTD;
@@ -436,6 +401,8 @@ pmap_bootstrap(firstaddr, loadaddr)
* 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)
@@ -538,12 +505,7 @@ pmap_create(size)
if (size)
return(NULL);
- /* XXX: is it ok to wait here? */
pmap = (pmap_t) malloc(sizeof *pmap, M_VMPMAP, M_WAITOK);
-#ifdef notifwewait
- if (pmap == NULL)
- panic("pmap_create: cannot allocate a pmap");
-#endif
bzero(pmap, sizeof(*pmap));
pmap_pinit(pmap);
return (pmap);
@@ -590,7 +552,7 @@ pmap_pinit(pmap)
/* install self-referential address mapping entry */
*(int *)(pmap->pm_pdir+PTDPTDI) =
- ((int)pmap_extract(kernel_pmap, (vm_offset_t)pmap->pm_pdir)) | PG_V | PG_KW;
+ ((int)pmap_kextract((vm_offset_t)pmap->pm_pdir)) | PG_V | PG_KW;
pmap->pm_count = 1;
simple_lock_init(&pmap->pm_lock);
@@ -698,6 +660,11 @@ get_pv_entry()
return tmp;
}
+/*
+ * this *strange* allocation routine *statistically* 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...
+ */
void
pmap_alloc_pv_entry()
{
@@ -755,7 +722,7 @@ pmap_alloc_pv_entry()
/*
* init the pv_entry allocation system
*/
-#define PVSPERPAGE 16
+#define PVSPERPAGE 64
void
init_pv_entries(npg)
int npg;
@@ -778,15 +745,13 @@ get_pt_entry(pmap)
pmap_t pmap;
{
pt_entry_t *ptp;
+ vm_offset_t frame = (int) pmap->pm_pdir[PTDPTDI] & PG_FRAME;
/* are we current address space or kernel? */
- if (pmap->pm_pdir[PTDPTDI].pd_pfnum == PTDpde.pd_pfnum
- || pmap == kernel_pmap)
+ if (pmap == kernel_pmap || frame == ((int) PTDpde & PG_FRAME)) {
ptp=PTmap;
-
/* otherwise, we are alternate address space */
- else {
- if (pmap->pm_pdir[PTDPTDI].pd_pfnum
- != APTDpde.pd_pfnum) {
+ } else {
+ if ( frame != ((int) APTDpde & PG_FRAME)) {
APTDpde = pmap->pm_pdir[PTDPTDI];
tlbflush();
}
@@ -808,9 +773,9 @@ pmap_remove_entry(pmap, pv, va)
vm_offset_t va;
{
pv_entry_t npv;
- int s;
int wired;
- s = splhigh();
+ int s;
+ s = splimp();
if (pmap == pv->pv_pmap && va == pv->pv_va) {
npv = pv->pv_next;
if (npv) {
@@ -849,121 +814,144 @@ pmap_remove(pmap, sva, eva)
register pt_entry_t *ptp,*ptq;
vm_offset_t pa;
register pv_entry_t pv;
- vm_offset_t asva, va;
+ vm_offset_t va;
vm_page_t m;
- int oldpte;
+ pt_entry_t oldpte;
if (pmap == NULL)
return;
ptp = get_pt_entry(pmap);
+/*
+ * special handling of removing one page. a very
+ * common operation and easy to short circuit some
+ * code.
+ */
+ if( (sva + NBPG) == eva) {
+
+ if( *pmap_pde( pmap, sva) == 0)
+ return;
+
+ ptq = ptp + i386_btop(sva);
+
+ if( !*ptq)
+ return;
+ /*
+ * Update statistics
+ */
+ if (pmap_pte_w(ptq))
+ pmap->pm_stats.wired_count--;
+ pmap->pm_stats.resident_count--;
+
+ pa = pmap_pte_pa(ptq);
+ oldpte = *ptq;
+ *ptq = 0;
+
+ if (pmap_is_managed(pa)) {
+ if ((((int) oldpte & PG_M) && (sva < USRSTACK || sva > UPT_MAX_ADDRESS))
+ || (sva >= USRSTACK && sva < USRSTACK+(UPAGES*NBPG))) {
+ if (sva < pager_sva || sva >= pager_eva) {
+ m = PHYS_TO_VM_PAGE(pa);
+ m->flags &= ~PG_CLEAN;
+ }
+ }
+
+ pv = pa_to_pvh(pa);
+ pmap_remove_entry(pmap, pv, sva);
+ pmap_unuse_pt(pmap, sva);
+ }
+ tlbflush();
+ return;
+ }
- /* this is essential since we must check the PDE(sva) for precense */
- while (sva <= eva && !pmap_pde_v(pmap_pde(pmap, sva)))
- sva = (sva & PD_MASK) + (1<<PD_SHIFT);
sva = i386_btop(sva);
eva = i386_btop(eva);
- for (; sva < eva; sva++) {
+ while (sva < eva) {
/*
* Weed out invalid mappings.
* Note: we assume that the page directory table is
* always allocated, and in kernel virtual.
*/
- if (!pmap_pde_v(pmap_pde(pmap, i386_ptob(sva))))
- {
+ if ( *pmap_pde(pmap, i386_ptob(sva)) == 0 ) {
/* We can race ahead here, straight to next pde.. */
- sva = sva & ~((NBPG/PTESIZE) - 1);
- sva = sva + NBPG/PTESIZE - 1;
+ nextpde:
+ sva = ((sva + NPTEPG) & ~(NPTEPG - 1));
continue;
- }
+ }
- ptq=ptp+sva;
-
+ ptq = ptp + sva;
/*
- * search for page table entries
+ * search for page table entries, use string operations
+ * that are much faster than
+ * explicitly scanning when page tables are not fully
+ * populated.
*/
- if (!pmap_pte_v(ptq)) {
- vm_offset_t nscan = ((sva + (NBPG/PTESIZE)) & ~((NBPG/PTESIZE) - 1)) - sva;
+ if ( *ptq == 0) {
+ vm_offset_t pdnxt = ((sva + NPTEPG) & ~(NPTEPG - 1));
+ vm_offset_t nscan = pdnxt - sva;
+ int found = 0;
+
if ((nscan + sva) > eva)
nscan = eva - sva;
- if (nscan) {
- int found;
-
- asm("xorl %%eax,%%eax;cld;repe;scasl;jz 1f;incl %%eax;1:;"
- :"=D"(ptq),"=a"(found)
- :"c"(nscan),"0"(ptq)
- :"cx");
- if (found)
- ptq -= 1;
+ asm("xorl %%eax,%%eax;cld;repe;scasl;jz 1f;incl %%eax;1:;"
+ :"=D"(ptq),"=a"(found)
+ :"c"(nscan),"0"(ptq)
+ :"cx");
- sva = ptq - ptp;
+ if( !found) {
+ sva = pdnxt;
+ continue;
}
- if (sva >= eva)
- goto endofloop;
- }
-
-
- if (!(sva & 0x3ff)) /* Only check once in a while */
- {
- if (!pmap_pde_v(pmap_pde(pmap, i386_ptob(sva)))) {
- /* We can race ahead here, straight to next pde.. */
- sva = sva & ~((NBPG/PTESIZE) - 1);
- sva = sva + NBPG/PTESIZE - 1;
- continue;
- }
- }
+ ptq -= 1;
- if (!pmap_pte_v(ptq))
- continue;
+ sva = ptq - ptp;
+ }
/*
* Update statistics
*/
- if (pmap_pte_w(ptq))
+ oldpte = *ptq;
+ if (((int)oldpte) & PG_W)
pmap->pm_stats.wired_count--;
pmap->pm_stats.resident_count--;
- pa = pmap_pte_pa(ptq);
- oldpte = *(int *) ptq;
-
/*
* Invalidate the PTEs.
* XXX: should cluster them up and invalidate as many
* as possible at once.
*/
- *(int *)ptq = 0;
+ *ptq = 0;
+
+ va = i386_ptob(sva);
/*
* Remove from the PV table (raise IPL since we
* may be called at interrupt time).
*/
- if (!pmap_is_managed(pa))
+ pa = ((int)oldpte) & PG_FRAME;
+ if (!pmap_is_managed(pa)) {
+ ++sva;
continue;
+ }
- va = i386_ptob(sva);
-
- if (((oldpte & PG_M) && (va < USRSTACK || va > UPT_MAX_ADDRESS))
+ if ((((int) oldpte & PG_M) && (va < USRSTACK || va > UPT_MAX_ADDRESS))
|| (va >= USRSTACK && va < USRSTACK+(UPAGES*NBPG))) {
- /*
- * don't update the dirty bit for pager mappings
- */
- if( va < pager_sva || va >= pager_eva) {
+ if (va < pager_sva || va >= pager_eva) {
m = PHYS_TO_VM_PAGE(pa);
m->flags &= ~PG_CLEAN;
}
}
pv = pa_to_pvh(pa);
- asva = i386_ptob(sva);
- pmap_remove_entry(pmap, pv, asva);
- pmap_use_pt(pmap, asva, 0);
+ pmap_remove_entry(pmap, pv, va);
+ pmap_unuse_pt(pmap, va);
+ ++sva;
}
-endofloop:
tlbflush();
}
@@ -973,6 +961,11 @@ endofloop:
* 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...)
*/
void
pmap_remove_all(pa)
@@ -1004,21 +997,22 @@ pmap_remove_all(pa)
pte = ptp + va;
if (pmap_pte_w(pte))
pmap->pm_stats.wired_count--;
- if (pmap_pte_v(pte))
+ if ( *pte)
pmap->pm_stats.resident_count--;
- if (((*(int *)pte & PG_M) && (pv->pv_va < USRSTACK || pv->pv_va > UPT_MAX_ADDRESS))
+ /*
+ * update the vm_page_t clean bit
+ */
+ if ( (m->flags & PG_CLEAN) &&
+ ((((int) *pte) & PG_M) && (pv->pv_va < USRSTACK || pv->pv_va > UPT_MAX_ADDRESS))
|| (pv->pv_va >= USRSTACK && pv->pv_va < USRSTACK+(UPAGES*NBPG))) {
- /*
- * don't update the dirty bit for pager mappings
- */
- if( pv->pv_va < pager_sva || pv->pv_va >= pager_eva) {
+ if (pv->pv_va < pager_sva || pv->pv_va >= pager_eva) {
m->flags &= ~PG_CLEAN;
}
}
- *(int *)pte = 0;
- pmap_use_pt(pmap, pv->pv_va, 0);
+ *pte = 0;
+ pmap_unuse_pt(pmap, pv->pv_va);
npv = pv->pv_next;
if (npv) {
@@ -1027,10 +1021,8 @@ pmap_remove_all(pa)
} else {
pv->pv_pmap = NULL;
}
-
}
splx(s);
-
tlbflush();
}
@@ -1049,7 +1041,6 @@ pmap_protect(pmap, sva, eva, prot)
register vm_offset_t va;
int i386prot;
register pt_entry_t *ptp;
- int reqactivate = 0;
int evap = i386_btop(eva);
int s;
@@ -1065,70 +1056,62 @@ pmap_protect(pmap, sva, eva, prot)
ptp = get_pt_entry(pmap);
- for (va = sva; va < eva; va += PAGE_SIZE) {
+ va = sva;
+ while (va < eva) {
+ int found=0;
+ int svap;
+ vm_offset_t nscan;
/*
* Page table page is not allocated.
* Skip it, we don't want to force allocation
* of unnecessary PTE pages just to set the protection.
*/
- if (!pmap_pde_v(pmap_pde(pmap, va))) {
+ if (! *pmap_pde(pmap, va)) {
/* XXX: avoid address wrap around */
+nextpde:
if (va >= i386_trunc_pdr((vm_offset_t)-1))
break;
- va = i386_round_pdr(va + PAGE_SIZE) - PAGE_SIZE;
+ va = i386_round_pdr(va + PAGE_SIZE);
continue;
}
pte = ptp + i386_btop(va);
+ if( *pte == 0) {
/*
* scan for a non-empty pte
*/
- {
- int found=0;
- int svap = pte - ptp;
- vm_offset_t nscan =
- ((svap + (NBPG/PTESIZE)) & ~((NBPG/PTESIZE) - 1)) - svap;
+ svap = pte - ptp;
+ nscan = ((svap + NPTEPG) & ~(NPTEPG - 1)) - svap;
+
if (nscan + svap > evap)
nscan = evap - svap;
- if (nscan) {
+
+ found = 0;
+ if (nscan)
asm("xorl %%eax,%%eax;cld;repe;scasl;jz 1f;incl %%eax;1:;"
:"=D"(pte),"=a"(found)
:"c"(nscan),"0"(pte):"cx");
- pte -= 1;
- svap = pte - ptp;
+ if( !found)
+ goto nextpde;
+
+ pte -= 1;
+ svap = pte - ptp;
- }
- if (svap >= evap)
- goto endofloop;
va = i386_ptob(svap);
- if (!found)
- continue;
}
- /*
- * Page not valid. Again, skip it.
- * Should we do this? Or set protection anyway?
- */
- if (!pmap_pte_v(pte))
- continue;
-
i386prot = pte_prot(pmap, prot);
-
- if (va < UPT_MIN_ADDRESS)
+ if (va < UPT_MAX_ADDRESS) {
i386prot |= PG_u;
- else if (va < UPT_MAX_ADDRESS)
- i386prot |= PG_u | PG_RW;
-
- if (i386prot != pte->pg_prot) {
- reqactivate = 1;
- pmap_pte_set_prot(pte, i386prot);
+ if( va >= UPT_MIN_ADDRESS)
+ i386prot |= PG_RW;
}
+ pmap_pte_set_prot(pte, i386prot);
+ va += PAGE_SIZE;
}
-endofloop:
- if (reqactivate)
- tlbflush();
+ tlbflush();
}
/*
@@ -1152,10 +1135,9 @@ pmap_enter(pmap, va, pa, prot, wired)
boolean_t wired;
{
register pt_entry_t *pte;
- register int npte;
+ register pt_entry_t npte;
vm_offset_t opa;
- boolean_t cacheable = TRUE;
- boolean_t checkpv = TRUE;
+ int cacheable=1;
if (pmap == NULL)
return;
@@ -1167,7 +1149,7 @@ pmap_enter(pmap, va, pa, prot, wired)
/*
* Page Directory table entry not valid, we need a new PT page
*/
- if (!pmap_pde_v(pmap_pde(pmap, va))) {
+ if ( *pmap_pde(pmap, va) == 0) {
pg("ptdi %x, va %x", pmap->pm_pdir[PTDPTDI], va);
}
@@ -1211,7 +1193,7 @@ pmap_enter(pmap, va, pa, prot, wired)
int s;
pv = pa_to_pvh(pa);
- s = splhigh();
+ s = splimp();
/*
* No entries yet, use header as the first entry
*/
@@ -1232,11 +1214,12 @@ pmap_enter(pmap, va, pa, prot, wired)
pv->pv_next = npv;
}
splx(s);
+ cacheable = 1;
} else {
- cacheable = FALSE;
+ cacheable = 0;
}
- pmap_use_pt(pmap, va, 1);
+ pmap_use_pt(pmap, va);
/*
* Increment counters
@@ -1249,11 +1232,12 @@ validate:
/*
* Now validate mapping with desired protection/wiring.
*/
- npte = (pa & PG_FRAME) | pte_prot(pmap, prot) | PG_V;
-
- if (!cacheable) {
- npte |= PG_N;
- }
+ npte = (pt_entry_t) ( (int) (pa | pte_prot(pmap, prot) | PG_V));
+ /*
+ * for correctness:
+ */
+ if( !cacheable)
+ (int) npte |= PG_N;
/*
* When forking (copy-on-write, etc):
@@ -1269,43 +1253,244 @@ validate:
* used (referenced) bits.
*/
if (pa == opa)
- npte |= *(int *)pte & (PG_M|PG_U);
+ (int) npte |= (int) *pte & (PG_M|PG_U);
+
if (wired)
- npte |= PG_W;
+ (int) npte |= PG_W;
if (va < UPT_MIN_ADDRESS)
- npte |= PG_u;
+ (int) npte |= PG_u;
else if (va < UPT_MAX_ADDRESS)
- npte |= PG_u | PG_RW;
+ (int) npte |= PG_u | PG_RW;
- if (npte != *(int *)pte) {
- *(int *)pte = npte;
+ if( *pte != npte) {
+ *pte = npte;
tlbflush();
}
}
/*
- * pmap_page_protect:
- *
- * Lower the permission for all mappings to a given page.
+ * add a wired page to the kva
*/
void
-pmap_page_protect(phys, prot)
- vm_offset_t phys;
- vm_prot_t prot;
+pmap_kenter(va, pa)
+ vm_offset_t va;
+ register vm_offset_t pa;
{
- void pmap_copy_on_write();
- switch (prot) {
- case VM_PROT_READ:
- case VM_PROT_READ|VM_PROT_EXECUTE:
- pmap_copy_on_write(phys);
- break;
- case VM_PROT_ALL:
- break;
- default:
- pmap_remove_all(phys);
- break;
- }
+ register pt_entry_t *pte;
+ register pv_entry_t pv, npv;
+ vm_offset_t opa;
+ int s;
+
+ /*
+ * 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.
+ */
+
+ pte = vtopte(va);
+
+ opa = pmap_pte_pa(pte);
+ /*
+ * 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 (!pmap_pte_w(pte)) {
+ kernel_pmap->pm_stats.wired_count++;
+ }
+ goto validate;
+ }
+
+ if (opa) {
+ pmap_remove(kernel_pmap, va, va + PAGE_SIZE);
+ }
+
+ pv = pa_to_pvh(pa);
+ s = splimp();
+ /*
+ * No entries yet, use header as the first entry
+ */
+ if (pv->pv_pmap == NULL) {
+ pv->pv_va = va;
+ pv->pv_pmap = kernel_pmap;
+ pv->pv_next = NULL;
+ }
+ /*
+ * There is at least one other VA mapping this page.
+ * Place this entry after the header.
+ */
+ else {
+ npv = get_pv_entry();
+ npv->pv_va = va;
+ npv->pv_pmap = kernel_pmap;
+ npv->pv_next = pv->pv_next;
+ pv->pv_next = npv;
+ }
+ splx(s);
+
+ /*
+ * Increment counters
+ */
+ kernel_pmap->pm_stats.resident_count++;
+
+validate:
+
+ /*
+ * Now validate mapping with desired protection/wiring.
+ */
+ *pte = (pt_entry_t) ( (int) (pa | PG_RW | PG_V | PG_W));
+}
+
+/*
+ * 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 inline void
+pmap_enter_quick(pmap, va, pa)
+ register pmap_t pmap;
+ vm_offset_t va;
+ register vm_offset_t pa;
+{
+ register pt_entry_t *pte;
+ register pv_entry_t pv, npv;
+ int s;
+
+ /*
+ * 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.
+ */
+
+ pte = vtopte(va);
+ if (pmap_pte_pa(pte)) {
+ pmap_remove(pmap, va, va + PAGE_SIZE);
+ }
+
+ pv = pa_to_pvh(pa);
+ s = splimp();
+ /*
+ * No entries yet, use header as the first entry
+ */
+ if (pv->pv_pmap == NULL) {
+ pv->pv_va = va;
+ pv->pv_pmap = pmap;
+ pv->pv_next = NULL;
+ }
+ /*
+ * There is at least one other VA mapping this page.
+ * Place this entry after the header.
+ */
+ else {
+ npv = get_pv_entry();
+ npv->pv_va = va;
+ npv->pv_pmap = pmap;
+ npv->pv_next = pv->pv_next;
+ pv->pv_next = npv;
+ }
+ splx(s);
+
+ pmap_use_pt(pmap, va);
+
+ /*
+ * Increment counters
+ */
+ pmap->pm_stats.resident_count++;
+
+validate:
+
+ /*
+ * Now validate mapping with desired protection/wiring.
+ */
+ *pte = (pt_entry_t) ( (int) (pa | PG_RO | PG_V | PG_u));
+}
+
+/*
+ * 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, offset, size)
+ pmap_t pmap;
+ vm_offset_t addr;
+ vm_object_t object;
+ vm_offset_t offset;
+ vm_offset_t size;
+{
+
+ vm_offset_t tmpoff;
+ vm_page_t p;
+ int s;
+ vm_offset_t v, lastv=0;
+ pt_entry_t pte;
+ extern vm_map_t kernel_map;
+ vm_offset_t objbytes;
+
+ if (!pmap)
+ return;
+
+ /*
+ * if we are processing a major portion of the object, then
+ * scan the entire thing.
+ */
+ if( size > object->size / 2) {
+ objbytes = size;
+ p = (vm_page_t) queue_first(&object->memq);
+ while (!queue_end(&object->memq, (queue_entry_t) p) && objbytes != 0) {
+ tmpoff = p->offset;
+ if( tmpoff < offset) {
+ p = (vm_page_t) queue_next(&p->listq);
+ continue;
+ }
+ tmpoff -= offset;
+ if( tmpoff >= size) {
+ p = (vm_page_t) queue_next(&p->listq);
+ continue;
+ }
+
+ if ((p->flags & (PG_BUSY|PG_FICTITIOUS)) == 0 ) {
+ vm_page_hold(p);
+ v = i386_trunc_page(((vm_offset_t)vtopte( addr+tmpoff)));
+ /* a fault might occur here */
+ *(volatile char *)v += 0;
+ vm_page_unhold(p);
+ pmap_enter_quick(pmap, addr+tmpoff, VM_PAGE_TO_PHYS(p));
+ }
+ p = (vm_page_t) queue_next(&p->listq);
+ objbytes -= NBPG;
+ }
+ } else {
+ /*
+ * else lookup the pages one-by-one.
+ */
+ for(tmpoff = 0; tmpoff < size; tmpoff += NBPG) {
+ if( p = vm_page_lookup(object, tmpoff + offset)) {
+ if( (p->flags & (PG_BUSY|PG_FICTITIOUS)) == 0) {
+ vm_page_hold(p);
+ v = i386_trunc_page(((vm_offset_t)vtopte( addr+tmpoff)));
+ /* a fault might occur here */
+ *(volatile char *)v += 0;
+ vm_page_unhold(p);
+ pmap_enter_quick(pmap, addr+tmpoff, VM_PAGE_TO_PHYS(p));
+ }
+ }
+ }
+ }
+
+ tlbflush();
}
/*
@@ -1343,7 +1528,7 @@ pmap_change_wiring(pmap, va, wired)
* been changed by the kernel
*/
if (!wired)
- pmap_pte_m(pte) = 1;
+ (int) *pte |= PG_M;
}
@@ -1357,14 +1542,12 @@ pmap_change_wiring(pmap, va, wired)
*/
void
pmap_copy(dst_pmap, src_pmap, dst_addr, len, src_addr)
- pmap_t dst_pmap;
- pmap_t src_pmap;
+ pmap_t dst_pmap, src_pmap;
vm_offset_t dst_addr;
vm_size_t len;
vm_offset_t src_addr;
{
}
-
/*
* Require that all active physical maps contain no
* incorrect entries NOW. [This update includes
@@ -1450,20 +1633,23 @@ pmap_pageable(pmap, sva, eva, 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 pv;
- register int *pte;
int s;
if (!pmap_is_managed(pa))
return FALSE;
pv = pa_to_pvh(pa);
- s = splhigh();
+ s = splimp();
/*
* Not found, check current mappings returning
@@ -1481,20 +1667,25 @@ pmap_page_exists(pmap, pa)
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 pv;
- register int *pte;
+ pt_entry_t *pte;
int s;
if (!pmap_is_managed(pa))
return FALSE;
pv = pa_to_pvh(pa);
- s = splhigh();
+ s = splimp();
/*
* Not found, check current mappings returning
@@ -1502,8 +1693,21 @@ pmap_testbit(pa, bit)
*/
if (pv->pv_pmap != NULL) {
for (; 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_U ) {
+ if ((pv->pv_va >= pager_sva) && (pv->pv_va < pager_eva)) {
+ continue;
+ }
+ }
if (bit & PG_M ) {
if (pv->pv_va >= USRSTACK) {
+ if (pv->pv_va >= pager_sva && pv->pv_va < pager_eva) {
+ continue;
+ }
if (pv->pv_va < USRSTACK+(UPAGES*NBPG)) {
splx(s);
return TRUE;
@@ -1514,8 +1718,8 @@ pmap_testbit(pa, bit)
}
}
}
- pte = (int *) pmap_pte(pv->pv_pmap, pv->pv_va);
- if (*pte & bit) {
+ pte = pmap_pte(pv->pv_pmap, pv->pv_va);
+ if ((int) *pte & bit) {
splx(s);
return TRUE;
}
@@ -1525,6 +1729,9 @@ pmap_testbit(pa, bit)
return(FALSE);
}
+/*
+ * this routine is used to modify bits in ptes
+ */
static inline void
pmap_changebit(pa, bit, setem)
vm_offset_t pa;
@@ -1532,16 +1739,15 @@ pmap_changebit(pa, bit, setem)
boolean_t setem;
{
register pv_entry_t pv;
- register int *pte, npte;
+ register pt_entry_t *pte, npte;
vm_offset_t va;
int s;
- int reqactivate = 0;
if (!pmap_is_managed(pa))
return;
pv = pa_to_pvh(pa);
- s = splhigh();
+ s = splimp();
/*
* Loop over all current mappings setting/clearing as appropos
@@ -1551,34 +1757,47 @@ pmap_changebit(pa, bit, setem)
for (; pv; pv = pv->pv_next) {
va = pv->pv_va;
- /*
- * XXX don't write protect pager mappings
- */
- if (!setem && (bit == PG_RW)) {
- extern vm_offset_t pager_sva, pager_eva;
-
- if (va >= pager_sva && va < pager_eva)
- continue;
- }
+ /*
+ * don't write protect pager mappings
+ */
+ if (!setem && (bit == PG_RW)) {
+ if (va >= pager_sva && va < pager_eva)
+ continue;
+ }
- pte = (int *) pmap_pte(pv->pv_pmap, va);
+ pte = pmap_pte(pv->pv_pmap, va);
if (setem)
- npte = *pte | bit;
+ (int) npte = (int) *pte | bit;
else
- npte = *pte & ~bit;
- if (*pte != npte) {
- *pte = npte;
- tlbflush();
- }
+ (int) npte = (int) *pte & ~bit;
+ *pte = npte;
}
}
splx(s);
+ tlbflush();
}
/*
- * Clear the modify bits on the specified physical page.
+ * 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);
+ }
+}
+/*
+ * Clear the modify bits on the specified physical page.
+ */
void
pmap_clear_modify(pa)
vm_offset_t pa;
@@ -1591,7 +1810,6 @@ pmap_clear_modify(pa)
*
* Clear the reference bit on the specified physical page.
*/
-
void
pmap_clear_reference(pa)
vm_offset_t pa;
@@ -1624,7 +1842,6 @@ boolean_t
pmap_is_modified(pa)
vm_offset_t pa;
{
-
return(pmap_testbit(pa, PG_M));
}
@@ -1680,32 +1897,17 @@ i386_protection_init()
}
#ifdef DEBUG
-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) {
- printf(" -> pmap %x, va %x, flags %x",
- pv->pv_pmap, pv->pv_va, pv->pv_flags);
- pads(pv->pv_pmap);
- }
- printf(" ");
-}
-
/* print address space of pmap*/
void
pads(pm)
pmap_t pm;
{
unsigned va, i, j;
- struct pte *ptep;
+ pt_entry_t *ptep;
if (pm == kernel_pmap) return;
for (i = 0; i < 1024; i++)
- if (pm->pm_pdir[i].pd_v)
+ if (pm->pm_pdir[i])
for (j = 0; j < 1024 ; j++) {
va = (i<<PD_SHIFT)+(j<<PG_SHIFT);
if (pm == kernel_pmap && va < KERNBASE)
@@ -1718,4 +1920,23 @@ pads(pm)
} ;
}
+
+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/i386/i386/random.s b/sys/i386/i386/random.s
new file mode 100644
index 000000000000..eab2f74475f5
--- /dev/null
+++ b/sys/i386/i386/random.s
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1990,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: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Here is a very good random number generator. This implementation is
+ * based on ``Two Fast Implementations of the "Minimal Standard" Random
+ * Number Generator'', David G. Carta, Communications of the ACM, Jan 1990,
+ * Vol 33 No 1. Do NOT modify this code unless you have a very thorough
+ * understanding of the algorithm. It's trickier than you think. If
+ * you do change it, make sure that its 10,000'th invocation returns
+ * 1043618065.
+ *
+ * Here is easier-to-decipher pseudocode:
+ *
+ * p = (16807*seed)<30:0> # e.g., the low 31 bits of the product
+ * q = (16807*seed)<62:31> # e.g., the high 31 bits starting at bit 32
+ * if (p + q < 2^31)
+ * seed = p + q
+ * else
+ * seed = ((p + q) & (2^31 - 1)) + 1
+ * return (seed);
+ *
+ * The result is in (0,2^31), e.g., it's always positive.
+ */
+#include "asm.h"
+
+ .data
+randseed:
+ .word 1
+ .text
+ENTRY(random)
+ movl $16807,%eax
+ imull randseed
+ shll $1,%edx
+ movl %eax,%ecx
+ and $0x7fffffff,%eax
+ shrl $31,%ecx
+ orl %ecx,%edx
+ addl %edx,%eax
+ jge 1f
+ incl %eax
+1:
+ movl %eax,randseed
+ ret
diff --git a/sys/i386/i386/support.s b/sys/i386/i386/support.s
index 14808109213f..2f3aa9a10410 100644
--- a/sys/i386/i386/support.s
+++ b/sys/i386/i386/support.s
@@ -30,7 +30,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: support.s,v 1.4 1994/02/01 04:09:07 davidg Exp $
+ * $Id: support.s,v 1.11 1994/06/11 02:30:05 davidg Exp $
*/
#include "assym.s" /* system definitions */
@@ -75,7 +75,7 @@ ENTRY(inw) /* val = inw(port) */
ENTRY(insb) /* insb(port, addr, cnt) */
pushl %edi
- movw 8(%esp),%dx
+ movl 8(%esp),%edx
movl 12(%esp),%edi
movl 16(%esp),%ecx
cld
@@ -88,7 +88,7 @@ ENTRY(insb) /* insb(port, addr, cnt) */
ENTRY(insw) /* insw(port, addr, cnt) */
pushl %edi
- movw 8(%esp),%dx
+ movl 8(%esp),%edx
movl 12(%esp),%edi
movl 16(%esp),%ecx
cld
@@ -99,6 +99,19 @@ ENTRY(insw) /* insw(port, addr, cnt) */
popl %edi
ret
+ENTRY(insl) /* insl(port, addr, cnt) */
+ pushl %edi
+ movl 8(%esp),%edx
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ insl
+ NOP
+ movl %edi,%eax
+ popl %edi
+ ret
+
ENTRY(rtcin) /* rtcin(val) */
movl 4(%esp),%eax
outb %al,$0x70
@@ -124,7 +137,7 @@ ENTRY(outw) /* outw(port, val) */
ENTRY(outsb) /* outsb(port, addr, cnt) */
pushl %esi
- movw 8(%esp),%dx
+ movl 8(%esp),%edx
movl 12(%esp),%esi
movl 16(%esp),%ecx
cld
@@ -137,7 +150,7 @@ ENTRY(outsb) /* outsb(port, addr, cnt) */
ENTRY(outsw) /* outsw(port, addr, cnt) */
pushl %esi
- movw 8(%esp),%dx
+ movl 8(%esp),%edx
movl 12(%esp),%esi
movl 16(%esp),%ecx
cld
@@ -148,11 +161,36 @@ ENTRY(outsw) /* outsw(port, addr, cnt) */
popl %esi
ret
+ENTRY(outsl) /* outsl(port, addr, cnt) */
+ pushl %esi
+ movl 8(%esp),%edx
+ movl 12(%esp),%esi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ outsl
+ NOP
+ movl %esi,%eax
+ popl %esi
+ ret
+
/*
* bcopy family
*/
-/* void bzero(void *base, u_int cnt) */
+
+/*
+ * void bzero(void *base, u_int cnt)
+ * Special code for I486 because stosl uses lots
+ * of clocks. Makes little or no difference on DX2 type
+ * machines, but stosl is about 1/2 as fast as
+ * memory moves on a standard DX !!!!!
+ */
ENTRY(bzero)
+#if defined(I486_CPU)
+ cmpl $CPUCLASS_486,_cpu_class
+ jz 1f
+#endif
+
pushl %edi
movl 8(%esp),%edi
movl 12(%esp),%ecx
@@ -168,6 +206,107 @@ ENTRY(bzero)
popl %edi
ret
+#if defined(I486_CPU)
+ SUPERALIGN_TEXT
+1:
+ 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 /* I486_CPU */
+
/* fillw(pat, base, cnt) */
ENTRY(fillw)
pushl %edi
@@ -182,7 +321,6 @@ ENTRY(fillw)
/* filli(pat, base, cnt) */
ENTRY(filli)
-filli:
pushl %edi
movl 8(%esp),%eax
movl 12(%esp),%edi
@@ -232,8 +370,8 @@ bcopyw:
movl 20(%esp),%ecx
cmpl %esi,%edi /* potentially overlapping? */
jnb 1f
- cld /* nope, copy forwards */
shrl $1,%ecx /* copy by 16-bit words */
+ cld /* nope, copy forwards */
rep
movsw
adc %ecx,%ecx /* any bytes left? */
@@ -247,10 +385,10 @@ bcopyw:
1:
addl %ecx,%edi /* copy backwards */
addl %ecx,%esi
- std
andl $1,%ecx /* any fractional bytes? */
decl %edi
decl %esi
+ std
rep
movsb
movl 20(%esp),%ecx /* copy remainder by 16-bit words */
@@ -269,7 +407,7 @@ ENTRY(bcopyx)
cmpl $2,%eax
je bcopyw /* not _bcopyw, to avoid multiple mcounts */
cmpl $4,%eax
- je bcopy
+ je bcopy /* XXX the shared ret's break mexitcount */
jmp bcopyb
/*
@@ -286,8 +424,8 @@ bcopy:
movl 20(%esp),%ecx
cmpl %esi,%edi /* potentially overlapping? */
jnb 1f
- cld /* nope, copy forwards */
shrl $2,%ecx /* copy by 32-bit words */
+ cld /* nope, copy forwards */
rep
movsl
movl 20(%esp),%ecx
@@ -302,10 +440,10 @@ bcopy:
1:
addl %ecx,%edi /* copy backwards */
addl %ecx,%esi
- std
andl $3,%ecx /* any fractional bytes? */
decl %edi
decl %esi
+ std
rep
movsb
movl 20(%esp),%ecx /* copy remainder by 32-bit words */
@@ -395,6 +533,12 @@ ENTRY(copyout) /* copyout(from_kernel, to_user, len) */
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
@@ -449,13 +593,13 @@ ENTRY(copyout) /* copyout(from_kernel, to_user, len) */
/* bcopy(%esi, %edi, %ebx) */
3:
- cld
movl %ebx,%ecx
shrl $2,%ecx
+ cld
rep
movsl
movb %bl,%cl
- andb $3,%cl /* XXX can we trust the rest of %ecx on clones? */
+ andb $3,%cl
rep
movsb
@@ -488,15 +632,22 @@ ENTRY(copyin)
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
- gs
rep
movsl
movb %al,%cl
andb $3,%cl /* copy remaining bytes */
- gs
rep
movsb
@@ -517,39 +668,42 @@ copyin_fault:
ret
/*
- * fu{byte,sword,word} : fetch a byte(sword, word) from user memory
+ * fu{byte,sword,word} : fetch a byte (sword, word) from user memory
*/
ALTENTRY(fuiword)
ENTRY(fuword)
- movl __udatasel,%ax
- movl %ax,%gs
movl _curpcb,%ecx
movl $fusufault,PCB_ONFAULT(%ecx)
- movl 4(%esp),%edx
- gs
+ 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
ENTRY(fusword)
- movl __udatasel,%ax
- movl %ax,%gs
movl _curpcb,%ecx
movl $fusufault,PCB_ONFAULT(%ecx)
movl 4(%esp),%edx
- gs
+
+ cmpl $VM_MAXUSER_ADDRESS-2,%edx
+ ja fusufault
+
movzwl (%edx),%eax
movl $0,PCB_ONFAULT(%ecx)
ret
ALTENTRY(fuibyte)
ENTRY(fubyte)
- movl __udatasel,%ax
- movl %ax,%gs
movl _curpcb,%ecx
movl $fusufault,PCB_ONFAULT(%ecx)
movl 4(%esp),%edx
- gs
+
+ cmpl $VM_MAXUSER_ADDRESS-1,%edx
+ ja fusufault
+
movzbl (%edx),%eax
movl $0,PCB_ONFAULT(%ecx)
ret
@@ -563,15 +717,10 @@ fusufault:
ret
/*
- * su{byte,sword,word}: write a byte(word, longword) to user memory
- */
-/*
- * we only have to set the right segment selector.
+ * su{byte,sword,word}: write a byte (word, longword) to user memory
*/
ALTENTRY(suiword)
ENTRY(suword)
- movl __udatasel,%ax
- movl %ax,%gs
movl _curpcb,%ecx
movl $fusufault,PCB_ONFAULT(%ecx)
movl 4(%esp),%edx
@@ -580,9 +729,10 @@ ENTRY(suword)
#if defined(I486_CPU) || defined(I586_CPU)
cmpl $CPUCLASS_386,_cpu_class
- jne 2f
+ jne 2f /* we only have to set the right segment selector */
#endif /* I486_CPU || I586_CPU */
+ /* XXX - page boundary crossing is still not handled */
movl %edx,%eax
shrl $IDXSHIFT,%edx
andb $0xfc,%dl
@@ -603,16 +753,16 @@ ENTRY(suword)
#endif
2:
+ cmpl $VM_MAXUSER_ADDRESS-4,%edx /* verify address validity */
+ ja fusufault
+
movl 8(%esp),%eax
- gs
movl %eax,(%edx)
xorl %eax,%eax
movl %eax,PCB_ONFAULT(%ecx)
ret
ENTRY(susword)
- movl __udatasel,%eax
- movl %ax,%gs
movl _curpcb,%ecx
movl $fusufault,PCB_ONFAULT(%ecx)
movl 4(%esp),%edx
@@ -624,6 +774,7 @@ ENTRY(susword)
jne 2f
#endif /* I486_CPU || I586_CPU */
+ /* XXX - page boundary crossing is still not handled */
movl %edx,%eax
shrl $IDXSHIFT,%edx
andb $0xfc,%dl
@@ -644,8 +795,10 @@ ENTRY(susword)
#endif
2:
+ cmpl $VM_MAXUSER_ADDRESS-2,%edx /* verify address validity */
+ ja fusufault
+
movw 8(%esp),%ax
- gs
movw %ax,(%edx)
xorl %eax,%eax
movl %eax,PCB_ONFAULT(%ecx)
@@ -684,8 +837,10 @@ ENTRY(subyte)
#endif
2:
+ cmpl $VM_MAXUSER_ADDRESS-1,%edx /* verify address validity */
+ ja fusufault
+
movb 8(%esp),%al
- gs
movb %al,(%edx)
xorl %eax,%eax
movl %eax,PCB_ONFAULT(%ecx)
@@ -702,11 +857,12 @@ ENTRY(copyoutstr)
pushl %esi
pushl %edi
movl _curpcb,%ecx
- movl $cpystrflt,PCB_ONFAULT(%ecx)
+ movl $cpystrflt,PCB_ONFAULT(%ecx) /* XXX rename copyoutstr_fault */
movl 12(%esp),%esi /* %esi = from */
movl 16(%esp),%edi /* %edi = to */
movl 20(%esp),%edx /* %edx = maxlen */
+ cld
#if defined(I386_CPU)
@@ -721,8 +877,8 @@ ENTRY(copyoutstr)
* we look at a page at a time and the end address is on a page
* boundary.
*/
- cmpl $VM_MAXUSER_ADDRESS,%edi
- jae cpystrflt
+ cmpl $VM_MAXUSER_ADDRESS-1,%edi
+ ja cpystrflt
movl %edi,%eax
shrl $IDXSHIFT,%eax
@@ -736,6 +892,7 @@ ENTRY(copyoutstr)
pushl %edx
pushl %edi
call _trapwrite
+ cld
popl %edi
popl %edx
orl %eax,%eax
@@ -747,7 +904,7 @@ ENTRY(copyoutstr)
movl $NBPG,%ecx
subl %eax,%ecx /* ecx = NBPG - (src % NBPG) */
cmpl %ecx,%edx
- jge 3f
+ jae 3f
movl %edx,%ecx /* ecx = min(ecx, edx) */
3:
orl %ecx,%ecx
@@ -780,13 +937,11 @@ ENTRY(copyoutstr)
decl %edx
jz 2f
/*
- * gs override doesn't work for stosb. Use the same explicit check
- * as in copyout(). It's much slower now because it is per-char.
- * XXX - however, it would be faster to rewrite this function to use
+ * XXX - would be faster to rewrite this function to use
* strlen() and copyout().
*/
- cmpl $VM_MAXUSER_ADDRESS,%edi
- jae cpystrflt
+ cmpl $VM_MAXUSER_ADDRESS-1,%edi
+ ja cpystrflt
lodsb
stosb
@@ -805,6 +960,28 @@ ENTRY(copyoutstr)
#endif /* I486_CPU || I586_CPU */
/*
+ * This was split from copyinstr_fault mainly because pushing gs changes the
+ * stack offsets. It's better to have it separate for mcounting too.
+ */
+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
+ orl %edx,%edx
+ jz 1f
+ movl %ecx,(%edx)
+1:
+ popl %edi
+ popl %esi
+ 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
@@ -815,18 +992,24 @@ ENTRY(copyinstr)
pushl %esi
pushl %edi
movl _curpcb,%ecx
- movl $cpystrflt,PCB_ONFAULT(%ecx)
+ movl $copyinstr_fault,PCB_ONFAULT(%ecx)
movl 12(%esp),%esi /* %esi = from */
movl 16(%esp),%edi /* %edi = to */
movl 20(%esp),%edx /* %edx = maxlen */
+ /*
+ * XXX should avoid touching gs. Either copy the string in and
+ * check the bounds later or get its length and check the bounds
+ * and then use copyin().
+ */
+ pushl %gs
movl __udatasel,%eax
movl %ax,%gs
incl %edx
-
+ cld
1:
decl %edx
- jz 4f
+ jz 2f
gs
lodsb
stosb
@@ -836,26 +1019,27 @@ ENTRY(copyinstr)
/* Success -- 0 byte reached */
decl %edx
xorl %eax,%eax
- jmp 6f
-4:
+ jmp 3f
+2:
/* edx is zero -- return ENAMETOOLONG */
movl $ENAMETOOLONG,%eax
- jmp 6f
+ jmp 3f
-cpystrflt:
+ ALIGN_TEXT
+copyinstr_fault:
movl $EFAULT,%eax
-cpystrflt_x:
-6:
+3:
/* set *lencopied and return %eax */
movl _curpcb,%ecx
movl $0,PCB_ONFAULT(%ecx)
- movl 20(%esp),%ecx
+ movl 24(%esp),%ecx
subl %edx,%ecx
- movl 24(%esp),%edx
+ movl 28(%esp),%edx
orl %edx,%edx
- jz 7f
+ jz 4f
movl %ecx,(%edx)
-7:
+4:
+ popl %gs
popl %edi
popl %esi
ret
@@ -872,7 +1056,7 @@ ENTRY(copystr)
movl 16(%esp),%edi /* %edi = to */
movl 20(%esp),%edx /* %edx = maxlen */
incl %edx
-
+ cld
1:
decl %edx
jz 4f
@@ -971,15 +1155,6 @@ ENTRY(ssdtosd)
popl %ebx
ret
-#if 0
-/* tlbflush() */
-ENTRY(tlbflush)
- movl %cr3,%eax
- orl $I386_CR3PAT,%eax
- movl %eax,%cr3
- ret
-#endif
-
/* load_cr0(cr0) */
ENTRY(load_cr0)
movl 4(%esp),%eax
@@ -991,11 +1166,6 @@ ENTRY(rcr0)
movl %cr0,%eax
ret
-/* rcr2() */
-ENTRY(rcr2)
- movl %cr2,%eax
- ret
-
/* rcr3() */
ENTRY(rcr3)
movl %cr3,%eax
@@ -1037,4 +1207,3 @@ ENTRY(longjmp)
xorl %eax,%eax /* return(1); */
incl %eax
ret
-
diff --git a/sys/i386/i386/swtch.s b/sys/i386/i386/swtch.s
index 0851bae3a001..f9d4f91c082c 100644
--- a/sys/i386/i386/swtch.s
+++ b/sys/i386/i386/swtch.s
@@ -33,15 +33,17 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: swtch.s,v 1.4 1994/01/31 10:26:59 davidg Exp $
+ * $Id: swtch.s,v 1.6 1994/04/20 07:06:18 davidg Exp $
*/
#include "npx.h" /* for NNPX */
#include "assym.s" /* for preprocessor defines */
#include "errno.h" /* for error codes */
-#include "i386/isa/debug.h" /* for SHOW macros */
#include "machine/asmacros.h" /* for miscellaneous assembly macros */
+#define LOCORE /* XXX inhibit C declarations */
+#include "machine/spl.h" /* for SWI_AST_MASK ... */
+
/*****************************************************************************/
/* Scheduling */
@@ -132,23 +134,31 @@ rem3: .asciz "remrq"
sw0: .asciz "swtch"
/*
- * When no processes are on the runq, Swtch branches to idle
+ * When no processes are on the runq, swtch() branches to _idle
* to wait for something to come ready.
*/
ALIGN_TEXT
-Idle:
+_idle:
+ MCOUNT
movl _IdlePTD,%ecx
movl %ecx,%cr3
movl $tmpstk-4,%esp
sti
- SHOW_STI
+
+ /*
+ * XXX callers of swtch() do a bogus splclock(). Locking should
+ * be left to swtch().
+ */
+ movl $SWI_AST_MASK,_cpl
+ testl $~SWI_AST_MASK,_ipending
+ je idle_loop
+ call _splz
ALIGN_TEXT
idle_loop:
- call _spl0
cli
cmpl $0,_whichqs
- jne sw1
+ jne sw1a
sti
hlt /* wait for interrupt */
jmp idle_loop
@@ -161,9 +171,7 @@ badsw:
/*
* Swtch()
*/
- SUPERALIGN_TEXT /* so profiling doesn't lump Idle with swtch().. */
ENTRY(swtch)
-
incl _cnt+V_SWTCH
/* switch to new process. first, save context as needed */
@@ -208,14 +216,14 @@ ENTRY(swtch)
/* save is done, now choose a new process or idle */
sw1:
cli
- SHOW_CLI
+sw1a:
movl _whichqs,%edi
2:
/* XXX - bsf is sloow */
bsfl %edi,%eax /* find a full q */
- je Idle /* if none, idle */
+ je _idle /* if none, idle */
+
/* XX update whichqs? */
-swfnd:
btrl %eax,%edi /* clear q full status */
jnb 2b /* if it was clear, look for another */
movl %eax,%ebx /* save which one we are using */
@@ -296,7 +304,6 @@ swfnd:
*/
pushl PCB_IML(%edx)
sti
- SHOW_STI
#if 0
call _splx
#endif
@@ -312,7 +319,7 @@ ENTRY(mvesp)
movl %esp,%eax
ret
/*
- * struct proc *swtch_to_inactive(p) ; struct proc *p;
+ * struct proc *swtch_to_inactive(struct proc *p);
*
* At exit of a process, move off the address space of the
* process and onto a "safe" one. Then, on a temporary stack
@@ -327,6 +334,7 @@ ENTRY(swtch_to_inactive)
movl %ecx,%cr3 /* good bye address space */
#write buffer?
movl $tmpstk-4,%esp /* temporary stack, compensated for call */
+ MEXITCOUNT
jmp %edx /* return, execute remainder of cleanup */
/*
@@ -418,7 +426,7 @@ ENTRY(addupc)
movl 8(%ebp),%eax /* pc */
subl PR_OFF(%edx),%eax /* pc -= up->pr_off */
- jl L1 /* if (pc < 0) return */
+ jb L1 /* if (pc was < off) return */
shrl $1,%eax /* praddr = pc >> 1 */
imull PR_SCALE(%edx),%eax /* praddr *= up->pr_scale */
@@ -448,8 +456,3 @@ proffault:
movl $0,PR_SCALE(%ecx) /* up->pr_scale = 0 */
leave
ret
-
-/* To be done: */
-ENTRY(astoff)
- ret
-
diff --git a/sys/i386/i386/trap.c b/sys/i386/i386/trap.c
index 34a2993ba1e7..c21693c85990 100644
--- a/sys/i386/i386/trap.c
+++ b/sys/i386/i386/trap.c
@@ -1,4 +1,5 @@
/*-
+ * Copyright (C) 1994, David Greenman
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
@@ -34,11 +35,11 @@
* SUCH DAMAGE.
*
* from: @(#)trap.c 7.4 (Berkeley) 5/13/91
- * $Id: trap.c,v 1.17 1994/02/08 09:26:01 davidg Exp $
+ * $Id: trap.c,v 1.27 1994/06/22 05:52:30 jkh Exp $
*/
/*
- * 386 Trap and System call handleing
+ * 386 Trap and System call handling
*/
#include "isa.h"
@@ -68,28 +69,13 @@
#include "machine/trap.h"
-#ifdef __GNUC__
-
-/*
- * The "r" contraint could be "rm" except for fatal bugs in gas. As usual,
- * we omit the size from the mov instruction to avoid nonfatal bugs in gas.
- */
-#define read_gs() ({ u_short gs; __asm("mov %%gs,%0" : "=r" (gs)); gs; })
-#define write_gs(newgs) __asm("mov %0,%%gs" : : "r" ((u_short) newgs))
-
-#else /* not __GNUC__ */
-
-u_short read_gs __P((void));
-void write_gs __P((/* promoted u_short */ int gs));
-
-#endif /* __GNUC__ */
+int trap_pfault __P((struct trapframe *, int));
+void trap_fatal __P((struct trapframe *));
extern int grow(struct proc *,int);
struct sysent sysent[];
int nsysent;
-extern short cpl;
-extern short netmask, ttymask, biomask;
#define MAX_TRAP_MSG 27
char *trap_msg[] = {
@@ -102,7 +88,7 @@ char *trap_msg[] = {
"arithmetic trap", /* 6 T_ARITHTRAP */
"system forced exception", /* 7 T_ASTFLT */
"segmentation (limit) fault", /* 8 T_SEGFLT */
- "protection fault", /* 9 T_PROTFLT */
+ "general protection fault", /* 9 T_PROTFLT */
"trace trap", /* 10 T_TRCTRAP */
"", /* 11 unused */
"page fault", /* 12 T_PAGEFLT */
@@ -123,15 +109,59 @@ char *trap_msg[] = {
"stack fault", /* 27 T_STKFLT */
};
-#define pde_v(v) (PTD[((v)>>PD_SHIFT)&1023].pd_v)
+static inline void
+userret(p, frame, osyst)
+ struct proc *p;
+ struct trapframe *frame;
+ struct timeval *osyst;
+{
+ int sig, s;
+
+ while (sig = CURSIG(p))
+ psig(sig);
+ p->p_pri = 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 happend after we put ourselves on the run queue
+ * but before swtch()'ed, we might not be on the queue
+ * indicated by our priority.
+ */
+ s = splclock();
+ setrq(p);
+ p->p_stats->p_ru.ru_nivcsw++;
+ swtch();
+ splx(s);
+ while (sig = CURSIG(p))
+ psig(sig);
+ }
+ if (p->p_stats->p_prof.pr_scale) {
+ int ticks;
+ struct timeval *tv = &p->p_stime;
+
+ ticks = ((tv->tv_sec - osyst->tv_sec) * 1000 +
+ (tv->tv_usec - osyst->tv_usec) / 1000) / (tick / 1000);
+ 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
+ }
+ }
+ curpri = p->p_pri;
+}
/*
* trap(frame):
- * Exception, fault, and trap interface to BSD kernel. This
- * common code is called from assembly language IDT gate entry
+ * 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. Note that the
- * effect is as if the arguments were passed call by reference.
+ * frame after the exception has been processed.
*/
/*ARGSUSED*/
@@ -139,361 +169,369 @@ void
trap(frame)
struct trapframe frame;
{
- register int i;
- register struct proc *p = curproc;
+ struct proc *p = curproc;
struct timeval syst;
- int ucode, type, code, eva, fault_type;
+ int i = 0, ucode = 0, type, code, eva, fault_type;
frame.tf_eflags &= ~PSL_NT; /* clear nested trap XXX */
type = frame.tf_trapno;
+ code = frame.tf_err;
+
+ if (ISPL(frame.tf_cs) == SEL_UPL) {
+ /* user trap */
+
+ syst = p->p_stime;
+ p->p_regs = (int *)&frame;
+
+ switch (type) {
+ case T_RESADFLT: /* reserved addressing fault */
+ case T_PRIVINFLT: /* privileged instruction fault */
+ case T_RESOPFLT: /* reserved operand 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 & SOWEUPC) && p->p_stats->p_prof.pr_scale) {
+ addupc(frame.tf_eip, &p->p_stats->p_prof, 1);
+ p->p_flag &= ~SOWEUPC;
+ }
+ goto out;
+
+ case T_PROTFLT: /* general protection fault */
+ case T_SEGNPFLT: /* segment not present fault */
+ case T_STKFLT: /* stack fault */
+ ucode = code + BUS_SEGM_FAULT ;
+ i = SIGBUS;
+ break;
+
+ case T_PAGEFLT: /* page fault */
+ i = trap_pfault(&frame, TRUE);
+
+ if (i == 0)
+ goto out;
+
+ ucode = T_PAGEFLT;
+ break;
+
+ case T_DIVIDE: /* integer divide fault */
+ ucode = FPE_INTDIV_TRAP;
+ i = SIGFPE;
+ break;
+
+#if NISA > 0
+ case T_NMI:
#if NDDB > 0
- if (curpcb && curpcb->pcb_onfault) {
- if (frame.tf_trapno == T_BPTFLT
- || frame.tf_trapno == T_TRCTRAP)
+ /* NMI can be hooked up to a pushbutton for debugging */
+ printf ("NMI ... going to debugger\n");
if (kdb_trap (type, 0, &frame))
return;
- }
#endif
-
- if (curpcb == 0 || curproc == 0)
- goto skiptoswitch;
- if (curpcb->pcb_onfault && frame.tf_trapno != T_PAGEFLT) {
- extern int _udatasel;
-
- if (read_gs() != (u_short) _udatasel)
- /*
- * Some user has corrupted %gs but we depend on it in
- * copyout() etc. Fix it up and retry.
- *
- * (We don't preserve %fs or %gs, so users can change
- * them to either _ucodesel, _udatasel or a not-present
- * selector, possibly ORed with 0 to 3, making them
- * volatile for other users. Not preserving them saves
- * time and doesn't lose functionality or open security
- * holes.)
- */
- write_gs(_udatasel);
- else
-copyfault:
- frame.tf_eip = (int)curpcb->pcb_onfault;
- return;
- }
+ /* machine/parity/power fail/"kitchen sink" faults */
+ if (isa_nmi(code) == 0) return;
+ /* FALL THROUGH */
+#endif
- syst = p->p_stime;
- if (ISPL(frame.tf_cs) == SEL_UPL) {
- type |= T_USER;
- p->p_regs = (int *)&frame;
- }
+ case T_OFLOW: /* integer overflow fault */
+ ucode = FPE_INTOVF_TRAP;
+ i = SIGFPE;
+ break;
-skiptoswitch:
- ucode=0;
- eva = rcr2();
- code = frame.tf_err;
+ case T_BOUND: /* bounds check fault */
+ ucode = FPE_SUBRNG_TRAP;
+ i = SIGFPE;
+ break;
- if ((type & ~T_USER) == T_PAGEFLT)
- goto pfault;
+ case T_DNA:
+#if NNPX > 0
+ /* if a transparent fault (due to context switch "late") */
+ if (npxdna())
+ return;
+#endif /* NNPX > 0 */
- switch (type) {
- case T_SEGNPFLT|T_USER:
- case T_STKFLT|T_USER:
- case T_PROTFLT|T_USER: /* protection fault */
- ucode = code + BUS_SEGM_FAULT ;
- i = SIGBUS;
- break;
+#if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE)
+ i = math_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 */
+#else /* MATH_EMULATE || GPL_MATH_EMULATE */
+ i = SIGFPE;
+ ucode = FPE_FPU_NP_TRAP;
+#endif /* MATH_EMULATE || GPL_MATH_EMULATE */
+ break;
+
+ case T_FPOPFLT: /* FPU operand fetch fault */
+ ucode = T_FPOPFLT;
+ i = SIGILL;
+ break;
+
+ default:
+ trap_fatal(&frame);
+ }
+ } else {
+ /* kernel trap */
- case T_PRIVINFLT|T_USER: /* privileged instruction fault */
- case T_RESADFLT|T_USER: /* reserved addressing fault */
- case T_RESOPFLT|T_USER: /* reserved operand fault */
- case T_FPOPFLT|T_USER: /* coprocessor operand fault */
- ucode = type &~ T_USER;
- i = SIGILL;
- break;
+ switch (type) {
+ case T_PAGEFLT: /* page fault */
+ (void) trap_pfault(&frame, FALSE);
+ return;
- case T_ASTFLT|T_USER: /* Allow process switch */
- astoff();
- cnt.v_soft++;
- if ((p->p_flag & SOWEUPC) && p->p_stats->p_prof.pr_scale) {
- addupc(frame.tf_eip, &p->p_stats->p_prof, 1);
- p->p_flag &= ~SOWEUPC;
+ case T_PROTFLT: /* general protection fault */
+ case T_SEGNPFLT: /* segment not present fault */
+ if (curpcb && curpcb->pcb_onfault) {
+ frame.tf_eip = (int)curpcb->pcb_onfault;
+ return;
+ }
+ break;
+
+#if NDDB > 0
+ case T_BPTFLT:
+ case T_TRCTRAP:
+ if (kdb_trap (type, 0, &frame))
+ return;
+ break;
+#else
+ case T_TRCTRAP: /* trace trap -- someone single stepping lcall's */
+ /* Q: how do we turn it on again? */
+ frame.tf_eflags &= ~PSL_T;
+ return;
+#endif
+
+#if NISA > 0
+ case T_NMI:
+#if NDDB > 0
+ /* NMI can be hooked up to a pushbutton for debugging */
+ printf ("NMI ... going to debugger\n");
+ if (kdb_trap (type, 0, &frame))
+ return;
+#endif
+ /* machine/parity/power fail/"kitchen sink" faults */
+ if (isa_nmi(code) == 0) return;
+ /* FALL THROUGH */
+#endif
}
- goto out;
- case T_DNA|T_USER:
-#if NNPX > 0
- /* if a transparent fault (due to context switch "late") */
- if (npxdna()) return;
-#endif /* NNPX > 0 */
-#ifdef MATH_EMULATE
- i = math_emulate(&frame);
- if (i == 0) return;
-#else /* MATH_EMULTATE */
- panic("trap: math emulation necessary!");
-#endif /* MATH_EMULTATE */
- ucode = FPE_FPU_NP_TRAP;
- break;
+ trap_fatal(&frame);
+ }
- case T_BOUND|T_USER:
- ucode = FPE_SUBRNG_TRAP;
- i = SIGFPE;
- break;
+ trapsignal(p, i, ucode);
- case T_OFLOW|T_USER:
- ucode = FPE_INTOVF_TRAP;
- i = SIGFPE;
- break;
+#ifdef DIAGNOSTIC
+ 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
- case T_DIVIDE|T_USER:
- ucode = FPE_INTDIV_TRAP;
- i = SIGFPE;
- break;
+out:
+ userret(p, &frame, &syst);
+}
- case T_ARITHTRAP|T_USER:
- ucode = code;
- i = SIGFPE;
- break;
+int
+trap_pfault(frame, usermode)
+ struct trapframe *frame;
+ int usermode;
+{
+ vm_offset_t va;
+ struct vmspace *vm;
+ vm_map_t map = 0;
+ int rv = 0, oldflags;
+ vm_prot_t ftype;
+ extern vm_map_t kernel_map;
+ int eva;
+ struct proc *p = curproc;
+
+ eva = rcr2();
+ va = trunc_page((vm_offset_t)eva);
- pfault:
- case T_PAGEFLT: /* allow page faults in kernel mode */
- case T_PAGEFLT|T_USER: /* page fault */
- {
- vm_offset_t va;
- struct vmspace *vm;
- vm_map_t map = 0;
- int rv = 0, oldflags;
- vm_prot_t ftype;
- unsigned nss, v;
- extern vm_map_t kernel_map;
+ /*
+ * Don't allow user-mode faults in kernel address space
+ */
+ if (usermode && (va >= KERNBASE)) {
+ goto nogo;
+ }
+
+ if ((p == 0) || (va >= KERNBASE)) {
+ vm = 0;
+ map = kernel_map;
+ } else {
+ vm = p->p_vmspace;
+ map = &vm->vm_map;
+ }
+
+ if (frame->tf_err & PGEX_W)
+ ftype = VM_PROT_READ | VM_PROT_WRITE;
+ else
+ ftype = VM_PROT_READ;
- va = trunc_page((vm_offset_t)eva);
+ oldflags = p->p_flag;
+ if (map != kernel_map) {
+ vm_offset_t pa;
+ vm_offset_t v = (vm_offset_t) vtopte(va);
+ vm_page_t ptepg;
/*
- * Don't allow user-mode faults in kernel address space
+ * Keep swapout from messing with us during this
+ * critical time.
*/
- if ((type == (T_PAGEFLT|T_USER)) && (va >= KERNBASE)) {
- goto nogo;
- }
+ p->p_flag |= SLOCK;
- if ((p == 0) || (type == T_PAGEFLT && va >= KERNBASE)) {
- vm = 0;
- map = kernel_map;
- } else {
- vm = p->p_vmspace;
- map = &vm->vm_map;
+ /*
+ * 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_flag &= ~SLOCK;
+ p->p_flag |= (oldflags & SLOCK);
+ goto nogo;
+ }
}
- if (code & PGEX_W)
- ftype = VM_PROT_READ | VM_PROT_WRITE;
- else
- ftype = VM_PROT_READ;
-
- oldflags = p->p_flag;
- if (map != kernel_map) {
- vm_offset_t pa;
- vm_offset_t v = (vm_offset_t) vtopte(va);
-
- /*
- * Keep swapout from messing with us during this
- * critical time.
- */
- p->p_flag |= SLOCK;
-
- /*
- * 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_flag &= ~SLOCK;
- p->p_flag |= (oldflags & SLOCK);
- goto nogo;
- }
- }
+ /*
+ * Check if page table is mapped, if not,
+ * fault it first
+ */
- /*
- * Check if page table is mapped, if not,
- * fault it first
- */
+ /* Fault the pte only if needed: */
+ *(volatile char *)v += 0;
- /* Fault the pte only if needed: */
- *(volatile char *)v += 0;
+ ptepg = (vm_page_t) pmap_pte_vm_page(vm_map_pmap(map), v);
+ if( ptepg->hold_count == 0)
+ ptepg->act_count += 3;
+ vm_page_hold(ptepg);
- /* Get the physical address: */
- pa = pmap_extract(vm_map_pmap(map), v);
+ /* Fault in the user page: */
+ rv = vm_fault(map, va, ftype, FALSE);
- /* And wire the pte page at system vm level: */
- vm_page_wire(PHYS_TO_VM_PAGE(pa));
+ vm_page_unhold(ptepg);
- /* Fault in the user page: */
- rv = vm_fault(map, va, ftype, FALSE);
+ /*
+ * page table pages don't need to be kept if they
+ * are not held
+ */
+ if( ptepg->hold_count == 0 && ptepg->wire_count == 0) {
+ pmap_page_protect( VM_PAGE_TO_PHYS(ptepg),
+ VM_PROT_NONE);
+ vm_page_free(ptepg);
+ }
- /* Unwire the pte page: */
- vm_page_unwire(PHYS_TO_VM_PAGE(pa));
- p->p_flag &= ~SLOCK;
- p->p_flag |= (oldflags & SLOCK);
- } 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);
- }
+ p->p_flag &= ~SLOCK;
+ p->p_flag |= (oldflags & SLOCK);
+ } 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) {
- if (type == T_PAGEFLT)
- return;
- goto out;
- }
+ if (rv == KERN_SUCCESS)
+ return (0);
nogo:
- if (type == T_PAGEFLT) {
- if (curpcb->pcb_onfault)
- goto copyfault;
-
- goto we_re_toast;
+ if (!usermode) {
+ if (curpcb->pcb_onfault) {
+ frame->tf_eip = (int)curpcb->pcb_onfault;
+ return (0);
}
- i = (rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV;
+ trap_fatal(frame);
+ }
- /* kludge to pass faulting virtual address to sendsig */
- ucode = type &~ T_USER;
- frame.tf_err = eva;
+ /* kludge to pass faulting virtual address to sendsig */
+ frame->tf_err = eva;
- break;
- }
+ return((rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV);
+}
-#if NDDB == 0
- case T_TRCTRAP: /* trace trap -- someone single stepping lcall's */
- frame.tf_eflags &= ~PSL_T;
+void
+trap_fatal(frame)
+ struct trapframe *frame;
+{
+ int code, type, eva;
- /* Q: how do we turn it on again? */
- return;
-#endif
-
- case T_BPTFLT|T_USER: /* bpt instruction fault */
- case T_TRCTRAP|T_USER: /* trace trap */
- frame.tf_eflags &= ~PSL_T;
- i = SIGTRAP;
- break;
+ code = frame->tf_err;
+ type = frame->tf_trapno;
+ eva = rcr2();
-#if NISA > 0
- case T_NMI:
- case T_NMI|T_USER:
-#if NDDB > 0
- /* NMI can be hooked up to a pushbutton for debugging */
- printf ("NMI ... going to debugger\n");
- if (kdb_trap (type, 0, &frame))
- return;
-#endif
- /* machine/parity/power fail/"kitchen sink" faults */
- if (isa_nmi(code) == 0) return;
- /* FALL THROUGH */
-#endif
- default:
- we_re_toast:
-
- fault_type = type & ~T_USER;
- if (fault_type <= MAX_TRAP_MSG)
- printf("\n\nFatal trap %d: %s while in %s mode\n",
- fault_type, trap_msg[fault_type],
- ISPL(frame.tf_cs) == SEL_UPL ? "user" : "kernel");
- if (fault_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\n", frame.tf_eip);
- printf("processor eflags = ");
- if (frame.tf_eflags & EFL_TF)
- printf("trace/trap, ");
- if (frame.tf_eflags & EFL_IF)
- printf("interrupt enabled, ");
- if (frame.tf_eflags & EFL_NT)
- printf("nested task, ");
- if (frame.tf_eflags & EFL_RF)
- printf("resume, ");
- if (frame.tf_eflags & EFL_VM)
- printf("vm86, ");
- printf("IOPL = %d\n", (frame.tf_eflags & EFL_IOPL) >> 12);
- printf("current process = ");
- if (curproc) {
- printf("%d (%s)\n",
- curproc->p_pid, curproc->p_comm ?
- curproc->p_comm : "");
- } else {
- printf("Idle\n");
- }
- printf("interrupt mask = ");
- if ((cpl & netmask) == netmask)
- printf("net ");
- if ((cpl & ttymask) == ttymask)
- printf("tty ");
- if ((cpl & biomask) == biomask)
- printf("bio ");
- if (cpl == 0)
- printf("none");
- printf("\n");
+ 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\n", frame->tf_eip);
+ printf("processor eflags = ");
+ if (frame->tf_eflags & EFL_TF)
+ printf("trace/trap, ");
+ if (frame->tf_eflags & EFL_IF)
+ printf("interrupt enabled, ");
+ if (frame->tf_eflags & EFL_NT)
+ printf("nested task, ");
+ if (frame->tf_eflags & EFL_RF)
+ printf("resume, ");
+ if (frame->tf_eflags & EFL_VM)
+ printf("vm86, ");
+ printf("IOPL = %d\n", (frame->tf_eflags & EFL_IOPL) >> 12);
+ printf("current process = ");
+ if (curproc) {
+ printf("%d (%s)\n",
+ 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;
+ if (kdb_trap(&psl))
+ return;
#endif
#if NDDB > 0
- if (kdb_trap (type, 0, &frame))
- return;
-#endif
- if (fault_type <= MAX_TRAP_MSG)
- panic(trap_msg[fault_type]);
- else
- panic("unknown/reserved trap");
-
- /* NOT REACHED */
- }
-
- trapsignal(p, i, ucode);
- if ((type & T_USER) == 0)
+ if (kdb_trap (type, 0, frame))
return;
-out:
- while (i = CURSIG(p))
- psig(i);
- p->p_pri = p->p_usrpri;
- if (want_resched) {
- int s;
- /*
- * 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 setrq ourselves but before we
- * swtch()'ed, we might not be on the queue indicated by
- * our priority.
- */
- s = splclock();
- setrq(p);
- p->p_stats->p_ru.ru_nivcsw++;
- swtch();
- splx(s);
- while (i = CURSIG(p))
- psig(i);
- }
- if (p->p_stats->p_prof.pr_scale) {
- int ticks;
- struct timeval *tv = &p->p_stime;
-
- ticks = ((tv->tv_sec - syst.tv_sec) * 1000 +
- (tv->tv_usec - syst.tv_usec) / 1000) / (tick / 1000);
- 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
- }
- }
- curpri = p->p_pri;
+ if (type <= MAX_TRAP_MSG)
+ panic(trap_msg[type]);
+ else
+ panic("unknown/reserved trap");
}
/*
@@ -505,7 +543,6 @@ out:
int trapwrite(addr)
unsigned addr;
{
- unsigned nss;
struct proc *p;
vm_offset_t va, v;
struct vmspace *vm;
@@ -572,21 +609,17 @@ int trapwrite(addr)
/*ARGSUSED*/
void
syscall(frame)
- volatile struct trapframe frame;
+ struct trapframe frame;
{
- register int *locr0 = ((int *)&frame);
- register caddr_t params;
- register int i;
- register struct sysent *callp;
- register struct proc *p = curproc;
+ caddr_t params;
+ int i;
+ struct sysent *callp;
+ struct proc *p = curproc;
struct timeval syst;
int error, opc;
int args[8], rval[2];
int code;
-#ifdef lint
- r0 = 0; r0 = r0; r1 = 0; r1 = r1;
-#endif
syst = p->p_stime;
if (ISPL(frame.tf_cs) != SEL_UPL)
panic("syscall");
@@ -610,13 +643,11 @@ syscall(frame)
if ((i = callp->sy_narg * sizeof (int)) &&
(error = copyin(params, (caddr_t)args, (u_int)i))) {
- frame.tf_eax = error;
- frame.tf_eflags |= PSL_C; /* carry bit */
#ifdef KTRACE
if (KTRPOINT(p, KTR_SYSCALL))
ktrsyscall(p->p_tracep, code, callp->sy_narg, args);
#endif
- goto done;
+ goto bad;
}
#ifdef KTRACE
if (KTRPOINT(p, KTR_SYSCALL))
@@ -624,81 +655,40 @@ syscall(frame)
#endif
rval[0] = 0;
rval[1] = frame.tf_edx;
-/*pg("%d. s %d\n", p->p_pid, code);*/
+
error = (*callp->sy_call)(p, args, rval);
- if (error == ERESTART)
- frame.tf_eip = opc;
- else if (error != EJUSTRETURN) {
- if (error) {
-/*pg("error %d", error);*/
- frame.tf_eax = error;
- frame.tf_eflags |= PSL_C; /* carry bit */
- } else {
- frame.tf_eax = rval[0];
- frame.tf_edx = rval[1];
- frame.tf_eflags &= ~PSL_C; /* carry bit */
- }
- }
- /* else if (error == EJUSTRETURN) */
- /* nothing to do */
-done:
- /*
- * Reinitialize proc pointer `p' as it may be different
- * if this is a child returning from fork syscall.
- */
- p = curproc;
- while (i = CURSIG(p))
- psig(i);
- p->p_pri = p->p_usrpri;
- if (want_resched) {
- int s;
+
+ switch (error) {
+
+ case 0:
/*
- * 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 setrq ourselves but before we
- * swtch()'ed, we might not be on the queue indicated by
- * our priority.
+ * Reinitialize proc pointer `p' as it may be different
+ * if this is a child returning from fork syscall.
*/
- s = splclock();
- setrq(p);
- p->p_stats->p_ru.ru_nivcsw++;
- swtch();
- splx(s);
- while (i = CURSIG(p))
- psig(i);
- }
- if (p->p_stats->p_prof.pr_scale) {
- int ticks;
- struct timeval *tv = &p->p_stime;
+ p = curproc;
+ frame.tf_eax = rval[0];
+ frame.tf_edx = rval[1];
+ frame.tf_eflags &= ~PSL_C; /* carry bit */
+ break;
- ticks = ((tv->tv_sec - syst.tv_sec) * 1000 +
- (tv->tv_usec - syst.tv_usec) / 1000) / (tick / 1000);
- 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
- }
+ case ERESTART:
+ frame.tf_eip = opc;
+ break;
+
+ case EJUSTRETURN:
+ break;
+
+ default:
+ bad:
+ frame.tf_eax = error;
+ frame.tf_eflags |= PSL_C; /* carry bit */
+ break;
}
- curpri = p->p_pri;
+
+ userret(p, &frame, &syst);
+
#ifdef KTRACE
if (KTRPOINT(p, KTR_SYSRET))
ktrsysret(p->p_tracep, code, error, rval[0]);
#endif
-#ifdef DIAGNOSTICx
-{ extern int _udatasel, _ucodesel;
- if (frame.tf_ss != _udatasel)
- printf("ss %x call %d\n", frame.tf_ss, code);
- if ((frame.tf_cs&0xffff) != _ucodesel)
- printf("cs %x call %d\n", frame.tf_cs, code);
- if (frame.tf_eip > VM_MAXUSER_ADDRESS) {
- printf("eip %x call %d\n", frame.tf_eip, code);
- frame.tf_eip = 0;
- }
-}
-#endif
}
diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c
index ac7174eb8b53..8b7e625cf41e 100644
--- a/sys/i386/i386/vm_machdep.c
+++ b/sys/i386/i386/vm_machdep.c
@@ -1,6 +1,7 @@
/*-
* 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
@@ -37,7 +38,7 @@
*
* 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.11.2.1 1994/03/24 08:56:56 rgrimes Exp $
+ * $Id: vm_machdep.c,v 1.28 1994/06/06 12:08:16 davidg Exp $
*/
#include "npx.h"
@@ -53,6 +54,837 @@
#include "vm/vm.h"
#include "vm/vm_kern.h"
+#define b_cylin b_resid
+
+#define MAXCLSTATS 256
+int clstats[MAXCLSTATS];
+int rqstats[MAXCLSTATS];
+
+
+#ifndef NOBOUNCE
+vm_map_t io_map;
+volatile int kvasfreecnt;
+
+
+caddr_t bouncememory;
+int bouncepages, bpwait;
+vm_offset_t *bouncepa;
+int bmwait, bmfreeing;
+
+#define BITS_IN_UNSIGNED (8*sizeof(unsigned))
+int bounceallocarraysize;
+unsigned *bounceallocarray;
+int bouncefree;
+
+#define SIXTEENMEG (4096*4096)
+#define MAXBKVA 1024
+
+/* special list that can be used at interrupt time for eventual kva free */
+struct kvasfree {
+ vm_offset_t addr;
+ vm_offset_t size;
+} kvaf[MAXBKVA];
+
+
+vm_offset_t vm_bounce_kva();
+/*
+ * get bounce buffer pages (count physically contiguous)
+ * (only 1 inplemented now)
+ */
+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) {
+ if (bit = ffs(~bounceallocarray[i])) {
+ 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;
+}
+
+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
+ */
+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!!!\n");
+
+ 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
+ */
+vm_offset_t
+vm_bounce_kva(size, waitok)
+ int size;
+ int waitok;
+{
+ int i;
+ int startfree;
+ vm_offset_t kva = 0;
+ int s = splbio();
+more:
+ if (!bmfreeing && kvasfreecnt) {
+ bmfreeing = 1;
+ for (i = 0; i < kvasfreecnt; i++) {
+ pmap_remove(kernel_pmap,
+ kvaf[i].addr, kvaf[i].addr + kvaf[i].size);
+ 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*NBPG, M_TEMP, M_WAITOK);
+ return kva;
+ }
+ kva = vm_bounce_kva(count*NBPG, 1);
+ for(i=0;i<count;i++) {
+ pa = vm_bounce_page_find(1);
+ pmap_kenter(kva + i * NBPG, pa);
+ }
+ pmap_update();
+ 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 * NBPG);
+ vm_bounce_page_free(pa, 1);
+ }
+ vm_bounce_kva_free(kva, count*NBPG, 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 bounceindex;
+ int i;
+ int s;
+
+ 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%x) < b_bcount(0x%x) !!!!\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_un.b_addr;
+ vaend = (vm_offset_t) bp->b_un.b_addr + bp->b_bufsize;
+
+ vapstart = i386_trunc_page(vastart);
+ vapend = i386_round_page(vaend);
+ countvmpg = (vapend - vapstart) / NBPG;
+
+/*
+ * 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;
+ va += NBPG;
+ }
+ if (dobounceflag == 0)
+ return;
+
+ if (bouncepages < dobounceflag)
+ panic("Not enough bounce buffers!!!");
+
+/*
+ * allocate a replacement kva for b_addr
+ */
+ kva = vm_bounce_kva(countvmpg*NBPG, 1);
+ 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 + (NBPG * i), bpa);
+ /*
+ * if we are writing, the copy the data into the page
+ */
+ if ((bp->b_flags & B_READ) == 0) {
+ pmap_update();
+ bcopy((caddr_t) va, (caddr_t) kva + (NBPG * i), NBPG);
+ }
+ } else {
+ /*
+ * use original page
+ */
+ pmap_kenter(kva + (NBPG * i), pa);
+ }
+ va += NBPG;
+ }
+ pmap_update();
+
+/*
+ * flag the buffer as being bounced
+ */
+ bp->b_flags |= B_BOUNCE;
+/*
+ * save the original buffer kva
+ */
+ bp->b_savekva = bp->b_un.b_addr;
+/*
+ * put our new kva into the buffer (offset by original offset)
+ */
+ bp->b_un.b_addr = (caddr_t) (((vm_offset_t) kva) |
+ ((vm_offset_t) bp->b_savekva & (NBPG - 1)));
+ return;
+}
+
+/*
+ * hook into biodone to free bounce buffer
+ */
+void
+vm_bounce_free(bp)
+ struct buf *bp;
+{
+ int i;
+ vm_offset_t origkva, bouncekva, bouncekvaend;
+ int countvmpg;
+ int s;
+
+/*
+ * 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_un.b_addr;
+
+/*
+ * 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 = i386_round_page(bouncekva + 1) - bouncekva;
+ mybouncepa = pmap_kextract(i386_trunc_page(bouncekva));
+
+/*
+ * if this is a bounced pa, then process as one
+ */
+ if ( mybouncepa != pmap_kextract( i386_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
+ */
+ vm_bounce_page_free(mybouncepa, 1);
+ }
+
+ origkva += copycount;
+ bouncekva += copycount;
+ i += copycount;
+ }
+
+/*
+ * add the old kva into the "to free" list
+ */
+
+ bouncekva= i386_trunc_page((vm_offset_t) bp->b_un.b_addr);
+ bouncekvaend= i386_round_page((vm_offset_t)bp->b_un.b_addr + bp->b_bufsize);
+
+ vm_bounce_kva_free( bouncekva, (bouncekvaend - bouncekva), 0);
+ bp->b_un.b_addr = bp->b_savekva;
+ bp->b_savekva = 0;
+ bp->b_flags &= ~B_BOUNCE;
+
+ return;
+}
+
+
+/*
+ * init the bounce buffer system
+ */
+void
+vm_bounce_init()
+{
+ vm_offset_t minaddr, maxaddr;
+ int i;
+
+ io_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, MAXBKVA * NBPG, FALSE);
+ 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\n");
+
+ bzero(bounceallocarray, bounceallocarraysize * sizeof(unsigned));
+ bouncepa = malloc(bouncepages * sizeof(vm_offset_t), M_TEMP, M_NOWAIT);
+ if (!bouncepa)
+ panic("Cannot allocate physical memory array\n");
+
+ for(i=0;i<bouncepages;i++) {
+ vm_offset_t pa;
+ if( (pa = pmap_kextract((vm_offset_t) bouncememory + i * NBPG)) >= SIXTEENMEG)
+ panic("bounce memory out of range");
+ if( pa == 0)
+ panic("bounce memory not resident");
+ bouncepa[i] = pa;
+ }
+ bouncefree = bouncepages;
+
+}
+#endif /* NOBOUNCE */
+
+
+static void
+cldiskvamerge( kvanew, orig1, orig1cnt, orig2, orig2cnt)
+ vm_offset_t kvanew;
+ vm_offset_t orig1, orig1cnt;
+ vm_offset_t orig2, orig2cnt;
+{
+ int i;
+ vm_offset_t pa;
+/*
+ * enter the transfer physical addresses into the new kva
+ */
+ for(i=0;i<orig1cnt;i++) {
+ vm_offset_t pa;
+ pa = pmap_kextract((caddr_t) orig1 + i * PAGE_SIZE);
+ pmap_kenter(kvanew + i * PAGE_SIZE, pa);
+ }
+
+ for(i=0;i<orig2cnt;i++) {
+ vm_offset_t pa;
+ pa = pmap_kextract((caddr_t) orig2 + i * PAGE_SIZE);
+ pmap_kenter(kvanew + (i + orig1cnt) * PAGE_SIZE, pa);
+ }
+ pmap_update();
+}
+
+void
+cldisksort(struct buf *dp, struct buf *bp, vm_offset_t maxio)
+{
+ register struct buf *ap, *newbp;
+ int i, trycount=0;
+ vm_offset_t orig1pages, orig2pages;
+ vm_offset_t orig1begin, orig2begin;
+ vm_offset_t kvanew, kvaorig;
+
+ if( bp->b_bcount < MAXCLSTATS*PAGE_SIZE)
+ ++rqstats[bp->b_bcount/PAGE_SIZE];
+ /*
+ * If nothing on the activity queue, then
+ * we become the only thing.
+ */
+ ap = dp->b_actf;
+ if(ap == NULL) {
+ dp->b_actf = bp;
+ dp->b_actl = bp;
+ bp->av_forw = NULL;
+ return;
+ }
+
+
+ if (bp->b_flags & B_READ) {
+ while( ap->av_forw && (ap->av_forw->b_flags & B_READ))
+ ap = ap->av_forw;
+ goto insert;
+ }
+
+ /*
+ * If we lie after the first (currently active)
+ * request, then we must locate the second request list
+ * and add ourselves to it.
+ */
+
+ if (bp->b_pblkno < ap->b_pblkno) {
+ while (ap->av_forw) {
+ /*
+ * Check for an ``inversion'' in the
+ * normally ascending block numbers,
+ * indicating the start of the second request list.
+ */
+ if (ap->av_forw->b_pblkno < ap->b_pblkno) {
+ /*
+ * Search the second request list
+ * for the first request at a larger
+ * block number. We go before that;
+ * if there is no such request, we go at end.
+ */
+ do {
+ if (bp->b_pblkno < ap->av_forw->b_pblkno)
+ goto insert;
+ ap = ap->av_forw;
+ } while (ap->av_forw);
+ goto insert; /* after last */
+ }
+ ap = ap->av_forw;
+ }
+ /*
+ * No inversions... we will go after the last, and
+ * be the first request in the second request list.
+ */
+ goto insert;
+ }
+ /*
+ * Request is at/after the current request...
+ * sort in the first request list.
+ */
+ while (ap->av_forw) {
+ /*
+ * We want to go after the current request
+ * if there is an inversion after it (i.e. it is
+ * the end of the first request list), or if
+ * the next request is a larger block than our request.
+ */
+ if (ap->av_forw->b_pblkno < ap->b_pblkno ||
+ bp->b_pblkno < ap->av_forw->b_pblkno )
+ goto insert;
+ ap = ap->av_forw;
+ }
+
+insert:
+
+#ifndef NOBOUNCE
+ /*
+ * read clustering with new read-ahead disk drives hurts mostly, so
+ * we don't bother...
+ */
+ if( bp->b_flags & (B_READ|B_SYNC))
+ goto nocluster;
+ if( bp->b_bcount != bp->b_bufsize) {
+ goto nocluster;
+ }
+ /*
+ * we currently only cluster I/O transfers that are at page-aligned
+ * kvas and transfers that are multiples of page lengths.
+ */
+ if ((bp->b_flags & B_BAD) == 0 &&
+ ((bp->b_bcount & PAGE_MASK) == 0) &&
+ (((vm_offset_t) bp->b_un.b_addr & PAGE_MASK) == 0)) {
+ if( maxio > MAXCLSTATS*PAGE_SIZE)
+ maxio = MAXCLSTATS*PAGE_SIZE;
+ /*
+ * merge with previous?
+ * conditions:
+ * 1) We reside physically immediately after the previous block.
+ * 2) The previous block is not first on the device queue because
+ * such a block might be active.
+ * 3) The mode of the two I/Os is identical.
+ * 4) The previous kva is page aligned and the previous transfer
+ * is a multiple of a page in length.
+ * 5) And the total I/O size would be below the maximum.
+ */
+ if( (ap->b_pblkno + (ap->b_bcount / DEV_BSIZE) == bp->b_pblkno) &&
+ (dp->b_actf != ap) &&
+ ((ap->b_flags & ~(B_CLUSTER|B_BOUNCE)) == (bp->b_flags & ~B_BOUNCE)) &&
+ ((ap->b_flags & B_BAD) == 0) &&
+ ((ap->b_bcount & PAGE_MASK) == 0) &&
+ (((vm_offset_t) ap->b_un.b_addr & PAGE_MASK) == 0) &&
+ (ap->b_bcount + bp->b_bcount < maxio)) {
+
+ /*
+ * something is majorly broken in the upper level
+ * fs code... blocks can overlap!!! this detects
+ * the overlap and does the right thing.
+ */
+ if( ap->av_forw &&
+ bp->b_pblkno + ((bp->b_bcount / DEV_BSIZE) > ap->av_forw->b_pblkno)) {
+ goto nocluster;
+ }
+
+ orig1begin = (vm_offset_t) ap->b_un.b_addr;
+ orig1pages = ap->b_bcount / PAGE_SIZE;
+
+ orig2begin = (vm_offset_t) bp->b_un.b_addr;
+ orig2pages = bp->b_bcount / PAGE_SIZE;
+
+ /*
+ * see if we can allocate a kva, if we cannot, the don't
+ * cluster.
+ */
+ kvanew = vm_bounce_kva( PAGE_SIZE * (orig1pages + orig2pages), 0);
+ if( !kvanew) {
+ goto nocluster;
+ }
+
+ if( (ap->b_flags & B_CLUSTER) == 0) {
+
+ /*
+ * get a physical buf pointer
+ */
+ newbp = (struct buf *)trypbuf();
+ if( !newbp) {
+ vm_bounce_kva_free( kvanew, PAGE_SIZE * (orig1pages + orig2pages), 1);
+ goto nocluster;
+ }
+
+ cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages);
+
+ /*
+ * build the new bp to be handed off to the device
+ */
+
+ --clstats[ap->b_bcount/PAGE_SIZE];
+ *newbp = *ap;
+ newbp->b_flags |= B_CLUSTER;
+ newbp->b_un.b_addr = (caddr_t) kvanew;
+ newbp->b_bcount += bp->b_bcount;
+ newbp->b_bufsize = newbp->b_bcount;
+ newbp->b_clusterf = ap;
+ newbp->b_clusterl = bp;
+ ++clstats[newbp->b_bcount/PAGE_SIZE];
+
+ /*
+ * enter the new bp onto the device queue
+ */
+ if( ap->av_forw)
+ ap->av_forw->av_back = newbp;
+ else
+ dp->b_actl = newbp;
+
+ if( dp->b_actf != ap )
+ ap->av_back->av_forw = newbp;
+ else
+ dp->b_actf = newbp;
+
+ /*
+ * enter the previous bps onto the cluster queue
+ */
+ ap->av_forw = bp;
+ bp->av_back = ap;
+
+ ap->av_back = NULL;
+ bp->av_forw = NULL;
+
+ } else {
+ vm_offset_t addr;
+
+ cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages);
+ /*
+ * free the old kva
+ */
+ vm_bounce_kva_free( orig1begin, ap->b_bufsize, 0);
+ --clstats[ap->b_bcount/PAGE_SIZE];
+
+ ap->b_un.b_addr = (caddr_t) kvanew;
+
+ ap->b_clusterl->av_forw = bp;
+ bp->av_forw = NULL;
+ bp->av_back = ap->b_clusterl;
+ ap->b_clusterl = bp;
+
+ ap->b_bcount += bp->b_bcount;
+ ap->b_bufsize = ap->b_bcount;
+ ++clstats[ap->b_bcount/PAGE_SIZE];
+ }
+ return;
+ /*
+ * merge with next?
+ * conditions:
+ * 1) We reside physically before the next block.
+ * 3) The mode of the two I/Os is identical.
+ * 4) The next kva is page aligned and the next transfer
+ * is a multiple of a page in length.
+ * 5) And the total I/O size would be below the maximum.
+ */
+ } else if( ap->av_forw &&
+ (bp->b_pblkno + (bp->b_bcount / DEV_BSIZE) == ap->av_forw->b_pblkno) &&
+ ((bp->b_flags & ~B_BOUNCE) == (ap->av_forw->b_flags & ~(B_CLUSTER|B_BOUNCE))) &&
+ ((ap->av_forw->b_flags & B_BAD) == 0) &&
+ ((ap->av_forw->b_bcount & PAGE_MASK) == 0) &&
+ (((vm_offset_t) ap->av_forw->b_un.b_addr & PAGE_MASK) == 0) &&
+ (ap->av_forw->b_bcount + bp->b_bcount < maxio)) {
+
+ /*
+ * something is majorly broken in the upper level
+ * fs code... blocks can overlap!!! this detects
+ * the overlap and does the right thing.
+ */
+ if( (ap->b_pblkno + (ap->b_bcount / DEV_BSIZE)) > bp->b_pblkno) {
+ goto nocluster;
+ }
+
+ orig1begin = (vm_offset_t) bp->b_un.b_addr;
+ orig1pages = bp->b_bcount / PAGE_SIZE;
+
+ orig2begin = (vm_offset_t) ap->av_forw->b_un.b_addr;
+ orig2pages = ap->av_forw->b_bcount / PAGE_SIZE;
+
+ /*
+ * see if we can allocate a kva, if we cannot, the don't
+ * cluster.
+ */
+ kvanew = vm_bounce_kva( PAGE_SIZE * (orig1pages + orig2pages), 0);
+ if( !kvanew) {
+ goto nocluster;
+ }
+
+ /*
+ * if next isn't a cluster we need to create one
+ */
+ if( (ap->av_forw->b_flags & B_CLUSTER) == 0) {
+
+ /*
+ * get a physical buf pointer
+ */
+ newbp = (struct buf *)trypbuf();
+ if( !newbp) {
+ vm_bounce_kva_free( kvanew, PAGE_SIZE * (orig1pages + orig2pages), 1);
+ goto nocluster;
+ }
+
+ cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages);
+ ap = ap->av_forw;
+ --clstats[ap->b_bcount/PAGE_SIZE];
+ *newbp = *ap;
+ newbp->b_flags |= B_CLUSTER;
+ newbp->b_un.b_addr = (caddr_t) kvanew;
+ newbp->b_blkno = bp->b_blkno;
+ newbp->b_pblkno = bp->b_pblkno;
+ newbp->b_bcount += bp->b_bcount;
+ newbp->b_bufsize = newbp->b_bcount;
+ newbp->b_clusterf = bp;
+ newbp->b_clusterl = ap;
+ ++clstats[newbp->b_bcount/PAGE_SIZE];
+
+ if( ap->av_forw)
+ ap->av_forw->av_back = newbp;
+ else
+ dp->b_actl = newbp;
+
+ if( dp->b_actf != ap )
+ ap->av_back->av_forw = newbp;
+ else
+ dp->b_actf = newbp;
+
+ bp->av_forw = ap;
+ ap->av_back = bp;
+
+ bp->av_back = NULL;
+ ap->av_forw = NULL;
+ } else {
+ vm_offset_t addr;
+
+ cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages);
+ ap = ap->av_forw;
+ vm_bounce_kva_free( orig2begin, ap->b_bufsize, 0);
+
+ ap->b_un.b_addr = (caddr_t) kvanew;
+ bp->av_forw = ap->b_clusterf;
+ ap->b_clusterf->av_back = bp;
+ ap->b_clusterf = bp;
+ bp->av_back = NULL;
+ --clstats[ap->b_bcount/PAGE_SIZE];
+
+ ap->b_blkno = bp->b_blkno;
+ ap->b_pblkno = bp->b_pblkno;
+ ap->b_bcount += bp->b_bcount;
+ ap->b_bufsize = ap->b_bcount;
+ ++clstats[ap->b_bcount/PAGE_SIZE];
+
+ }
+ return;
+ }
+ }
+#endif
+ /*
+ * don't merge
+ */
+nocluster:
+ ++clstats[bp->b_bcount/PAGE_SIZE];
+ bp->av_forw = ap->av_forw;
+ if( bp->av_forw)
+ bp->av_forw->av_back = bp;
+ else
+ dp->b_actl = bp;
+
+ ap->av_forw = bp;
+ bp->av_back = ap;
+}
+
+/*
+ * quick version of vm_fault
+ */
+
+void
+vm_fault_quick( v, prot)
+ vm_offset_t v;
+ int prot;
+{
+ if( (cpu_class == CPUCLASS_386) &&
+ (prot & VM_PROT_WRITE))
+ vm_fault(&curproc->p_vmspace->vm_map, v,
+ VM_PROT_READ|VM_PROT_WRITE, FALSE);
+ else if( prot & VM_PROT_WRITE)
+ *(volatile char *)v += 0;
+ else
+ *(volatile char *)v;
+}
+
+
/*
* Finish a fork operation, with process p2 nearly set up.
* Copy and update the kernel stack and pcb, making the child
@@ -213,32 +1045,6 @@ setredzone(pte, vaddr)
}
/*
- * Move pages from one kernel virtual address to another.
- * Both addresses are assumed to reside in the Sysmap,
- * and size must be a multiple of CLSIZE.
- */
-void
-pagemove(from, to, size)
- register caddr_t from, to;
- int size;
-{
- register struct pte *fpte, *tpte;
-
- if (size % CLBYTES)
- panic("pagemove");
- fpte = kvtopte(from);
- tpte = kvtopte(to);
- while (size > 0) {
- *tpte++ = *fpte;
- *(int *)fpte++ = 0;
- from += NBPG;
- to += NBPG;
- size -= NBPG;
- }
- tlbflush();
-}
-
-/*
* Convert kernel VA to physical address
*/
u_long
@@ -246,7 +1052,7 @@ kvtop(void *addr)
{
vm_offset_t va;
- va = pmap_extract(kernel_pmap, (vm_offset_t)addr);
+ va = pmap_kextract((vm_offset_t)addr);
if (va == 0)
panic("kvtop: zero page frame");
return((int)va);
@@ -255,22 +1061,11 @@ kvtop(void *addr)
extern vm_map_t phys_map;
/*
- * Map an IO request into kernel virtual address space. Requests fall into
- * one of five catagories:
- *
- * B_PHYS|B_UAREA: User u-area swap.
- * Address is relative to start of u-area (p_addr).
- * B_PHYS|B_PAGET: User page table swap.
- * Address is a kernel VA in usrpt (Usrptmap).
- * B_PHYS|B_DIRTY: Dirty page push.
- * Address is a VA in proc2's address space.
- * B_PHYS|B_PGIN: Kernel pagein of user pages.
- * Address is VA in user's address space.
- * B_PHYS: User "raw" IO request.
- * Address is VA in user's address space.
+ * Map an IO request into kernel virtual address space.
*
- * All requests are (re)mapped into kernel VA space via the useriomap
- * (a name with only slightly more meaning than "kernelmap")
+ * 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)
@@ -289,18 +1084,18 @@ vmapbuf(bp)
addr = bp->b_saveaddr = bp->b_un.b_addr;
off = (int)addr & PGOFSET;
p = bp->b_proc;
- npf = btoc(round_page(bp->b_bcount + off));
+ npf = btoc(round_page(bp->b_bufsize + off));
kva = kmem_alloc_wait(phys_map, ctob(npf));
bp->b_un.b_addr = (caddr_t) (kva + off);
while (npf--) {
pa = pmap_extract(&p->p_vmspace->vm_pmap, (vm_offset_t)addr);
if (pa == 0)
panic("vmapbuf: null page frame");
- pmap_enter(vm_map_pmap(phys_map), kva, trunc_page(pa),
- VM_PROT_READ|VM_PROT_WRITE, TRUE);
+ pmap_kenter(kva, trunc_page(pa));
addr += PAGE_SIZE;
kva += PAGE_SIZE;
}
+ pmap_update();
}
/*
@@ -317,7 +1112,7 @@ vunmapbuf(bp)
if ((bp->b_flags & B_PHYS) == 0)
panic("vunmapbuf");
- npf = btoc(round_page(bp->b_bcount + ((int)addr & PGOFSET)));
+ npf = btoc(round_page(bp->b_bufsize + ((int)addr & PGOFSET)));
kva = (vm_offset_t)((int)addr & ~PGOFSET);
kmem_free_wakeup(phys_map, kva, ctob(npf));
bp->b_un.b_addr = bp->b_saveaddr;
diff --git a/sys/i386/include/ansi.h b/sys/i386/include/ansi.h
index 3a54968b1595..c932ba7a598b 100644
--- a/sys/i386/include/ansi.h
+++ b/sys/i386/include/ansi.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)ansi.h 7.1 (Berkeley) 3/9/91
- * $Id: ansi.h,v 1.2 1993/10/16 14:39:05 rgrimes Exp $
+ * $Id: ansi.h,v 1.3 1994/04/04 21:11:11 wollman Exp $
*/
#ifndef _ANSI_H_
@@ -53,6 +53,23 @@
#define _SIZE_T_ unsigned int /* sizeof() */
#define _TIME_T_ long /* time() */
#define _VA_LIST_ char * /* va_list */
-#define _WCHAR_T_ unsigned short /* wchar_t */
+
+/*
+ * Runes (wchar_t) is declared to be an ``int'' instead of the more natural
+ * ``unsigned long'' or ``long''. Two things are happening here. It is not
+ * unsigned so that EOF (-1) can be naturally assigned to it and used. Also,
+ * it looks like 10646 will be a 31 bit standard. This means that if your
+ * ints cannot hold 32 bits, you will be in trouble. The reason an int was
+ * chosen over a long is that the is*() and to*() routines take ints (says
+ * ANSI C), but they use _RUNE_T_ instead of int. By changing it here, you
+ * lose a bit of ANSI conformance, but your programs will still work.
+ *
+ * Note that _WCHAR_T_ and _RUNE_T_ must be of the same type. When wchar_t
+ * and rune_t are typedef'd, _WCHAR_T_ will be undef'd, but _RUNE_T remains
+ * defined for ctype.h.
+ */
+#define _BSD_WCHAR_T_ int /* wchar_t */
+#define _BSD_RUNE_T_ int /* rune_t */
+
#endif /* _ANSI_H_ */
diff --git a/sys/i386/include/asmacros.h b/sys/i386/include/asmacros.h
index f0f2c014178c..4af0b97a8abf 100644
--- a/sys/i386/include/asmacros.h
+++ b/sys/i386/include/asmacros.h
@@ -5,6 +5,11 @@
#define GEN_ENTRY(name) ALIGN_TEXT; .globl name; name:
#define NON_GPROF_ENTRY(name) GEN_ENTRY(_/**/name)
+/* These three are place holders for future changes to the profiling code */
+#define MCOUNT_LABEL(name)
+#define MEXITCOUNT
+#define FAKE_MCOUNT(caller)
+
#ifdef GPROF
/*
* ALTENTRY() must be before a corresponding ENTRY() so that it can jump
@@ -30,6 +35,7 @@
*/
#define ALTENTRY(name) GEN_ENTRY(_/**/name)
#define ENTRY(name) GEN_ENTRY(_/**/name)
+#define MCOUNT
#endif
diff --git a/sys/i386/include/console.h b/sys/i386/include/console.h
index 011889032ecc..c52a3943c764 100644
--- a/sys/i386/include/console.h
+++ b/sys/i386/include/console.h
@@ -14,7 +14,7 @@
* DK9210 Aalborg SO Phone: +45 9814 8076
*
* from:@(#)console.h 1.1 940105
- * $Id: console.h,v 1.7 1994/02/04 10:35:29 chmr Exp $
+ * $Id: console.h,v 1.9 1994/05/20 12:21:49 sos Exp $
*/
#ifndef _CONSOLE_H_
@@ -55,7 +55,7 @@
#define GIO_FONT8x14 _IOR('c', 67, fnt14_t)
#define PIO_FONT8x16 _IOW('c', 68, fnt16_t)
#define GIO_FONT8x16 _IOR('c', 69, fnt16_t)
-#define CONS_GETINFO _IOR('c', 73, vid_info_t)
+#define CONS_GETINFO _IOWR('c', 73, vid_info_t)
#define CONS_GETVERS _IOR('c', 74, long)
#define CONS_80x25TEXT _IO('c', 102)
#define CONS_80x50TEXT _IO('c', 103)
@@ -124,16 +124,19 @@ struct vt_mode {
#define NUM_STATES 8 /* states per key */
#define ALTGR_OFFSET 128 /* offset for altlock keys */
+struct key_t {
+ u_char map[NUM_STATES];
+ u_char spcl;
+ u_char flgs;
+};
+
struct keymap {
u_short n_keys;
- struct key_t {
- u_char map[NUM_STATES];
- u_char spcl;
- u_char flgs;
- } key[NUM_KEYS];
+ struct key_t key[NUM_KEYS];
};
#define MAXFK 16
+#define NUM_FKEYS 60
struct fkeytab {
u_char str[MAXFK];
@@ -190,6 +193,7 @@ typedef struct ssaver ssaver_t;
#define NLK 0x05 /* num lock key */
#define SLK 0x06 /* scroll lock key */
#define LALT 0x07 /* left alt key */
+#define BTAB 0x08 /* backwards tab */
#define LCTR 0x09 /* left control key */
#define NEXT 0x0a /* switch to next screen */
#define F_SCR 0x0b /* switch to first screen */
@@ -214,11 +218,14 @@ typedef struct ssaver ssaver_t;
#define KB_STAT 0x64 /* kbd status port */
#define KB_BUF_FULL 0x01 /* kbd has char pending */
#define KB_READY 0x02 /* kbd ready for command */
+#define KB_MODE 0x4D /* kbd mode (trans, ints enable)*/
#define KB_WRITE 0x60 /* kbd write command */
-#define KB_SETLEDS 0xed /* kbd set leds */
-#define KB_SETRAD 0xf3 /* kbd set repeat&delay command */
-#define KB_ACK 0xfa /* kbd acknowledge answer */
-#define KB_RESET_CPU 0xfe /* kbd reset main cpu command */
-#define KB_RESET 0xff /* kbd reset */
+#define KB_RESET_DONE 0xAA /* kbd reset command completed */
+#define KB_SETLEDS 0xED /* kbd set leds */
+#define KB_ECHO 0xEE /* kbd set leds */
+#define KB_SETRAD 0xF3 /* kbd set repeat&delay command */
+#define KB_ACK 0xFA /* kbd acknowledge answer */
+#define KB_RESEND 0xFE /* kbd resend cmd answer */
+#define KB_RESET 0xFF /* kbd reset */
#endif
diff --git a/sys/i386/include/cpu.h b/sys/i386/include/cpu.h
index 184f5b86151d..82ab4c3734dc 100644
--- a/sys/i386/include/cpu.h
+++ b/sys/i386/include/cpu.h
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)cpu.h 5.4 (Berkeley) 5/9/91
- * $Id: cpu.h,v 1.4 1993/11/07 17:42:46 wollman Exp $
+ * $Id: cpu.h,v 1.5 1994/04/02 07:00:35 davidg Exp $
*/
#ifndef _MACHINE_CPU_H_
@@ -58,18 +58,21 @@
* Arguments to hardclock, softclock and gatherstats
* encapsulate the previous machine state in an opaque
* clockframe; for now, use generic intrframe.
+ * XXX softclock() has been fixed. It never needed a
+ * whole frame, only a usermode flag, at least on this
+ * machine. Fix the rest.
*/
typedef struct intrframe clockframe;
#define CLKF_USERMODE(framep) (ISPL((framep)->if_cs) == SEL_UPL)
-#define CLKF_BASEPRI(framep) ((framep)->if_ppl == 0)
+#define CLKF_BASEPRI(framep) (((framep)->if_ppl & ~SWI_AST_MASK) == 0)
#define CLKF_PC(framep) ((framep)->if_eip)
/*
* Preempt the current process if in interrupt from user mode,
* or after the current trap/syscall if in system mode.
*/
-#define need_resched() { want_resched++; aston(); }
+#define need_resched() { want_resched = 1; aston(); }
/*
* Give a profiling tick to the current process from the softclock
@@ -84,7 +87,8 @@ typedef struct intrframe clockframe;
*/
#define signotify(p) aston()
-#define aston() (astpending++)
+#define aston() setsoftast()
+#define astoff()
/*
* pull in #defines for kinds of processors
@@ -97,7 +101,6 @@ struct cpu_nameclass {
};
#ifdef KERNEL
-extern int astpending; /* want a trap before returning to user mode */
extern int want_resched; /* resched was called */
extern int cpu;
diff --git a/sys/i386/include/cpufunc.h b/sys/i386/include/cpufunc.h
index 65cc855793f0..16bb63b86a17 100644
--- a/sys/i386/include/cpufunc.h
+++ b/sys/i386/include/cpufunc.h
@@ -2,7 +2,7 @@
* Functions to provide access to special i386 instructions.
* XXX - bezillions more are defined in locore.s but are not declared anywhere.
*
- * $Id: cpufunc.h,v 1.9 1994/01/31 23:48:23 davidg Exp $
+ * $Id: cpufunc.h,v 1.11 1994/06/01 03:09:51 davidg Exp $
*/
#ifndef _MACHINE_CPUFUNC_H_
@@ -11,6 +11,8 @@
#include <sys/cdefs.h>
#include <sys/types.h>
+#include "machine/spl.h"
+
#ifdef __GNUC__
static inline int bdb(void)
@@ -69,6 +71,14 @@ tlbflush()
__asm __volatile("movl %%cr3, %%eax; movl %%eax, %%cr3" : : : "ax");
}
+static inline u_long
+rcr2()
+{
+ u_long data;
+ __asm __volatile("movl %%cr2,%%eax" : "=a" (data));
+ return data;
+}
+
static inline
int
imin(a, b)
@@ -224,7 +234,6 @@ void load_cr0 __P((u_int cr0));
u_int rcr0 __P((void));
void load_cr3(u_long);
u_long rcr3(void);
-u_long rcr2(void);
void setidt __P((int, void (*)(), int, int));
extern u_long kvtop(void *);
diff --git a/sys/i386/include/ioctl_fd.h b/sys/i386/include/ioctl_fd.h
index 2e3ac3104726..a8c17f69ac4a 100644
--- a/sys/i386/include/ioctl_fd.h
+++ b/sys/i386/include/ioctl_fd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 1992-1993 by Joerg Wunsch, Dresden
+ * Copyright (C) 1992-1994 by Joerg Wunsch, Dresden
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,17 +11,18 @@
* 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 ``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.
+ * 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.
*/
#ifndef _IOCTL_FD_H
@@ -94,5 +95,11 @@ struct fd_type {
#define FD_FORM _IOW('F', 61, struct fd_formb) /* format a track */
#define FD_GTYPE _IOR('F', 62, struct fd_type) /* get drive type */
+#define FD_STYPE _IOW('F', 63, struct fd_type) /* set drive type */
+
+#define FD_GOPTS _IOR('F', 64, int) /* drive options, see below */
+#define FD_SOPTS _IOW('F', 65, int)
+
+#define FDOPT_NORETRY 0x0001 /* no retries on failure (cleared on close) */
#endif /* !def _IOCTL_FD_H */
diff --git a/sys/i386/include/limits.h b/sys/i386/include/limits.h
index 568ad40848a3..586d6e2f5a69 100644
--- a/sys/i386/include/limits.h
+++ b/sys/i386/include/limits.h
@@ -31,15 +31,14 @@
* SUCH DAMAGE.
*
* from: @(#)limits.h 7.2 (Berkeley) 6/28/90
- * $Id: limits.h,v 1.4 1993/12/19 05:14:46 alm Exp $
+ * $Id: limits.h,v 1.6 1994/04/04 21:11:12 wollman Exp $
*/
#ifndef _MACHINE_LIMITS_H_
#define _MACHINE_LIMITS_H_ 1
#define CHAR_BIT 8 /* number of bits in a char */
-#define CLK_TCK 60 /* ticks per second */
-#define MB_LEN_MAX 1 /* no multibyte characters */
+#define MB_LEN_MAX 6 /* allow 21-bit UTF2 */
#define SCHAR_MIN (-0x7f-1) /* max value for a signed char */
#define SCHAR_MAX 0x7f /* min value for a signed char */
@@ -61,6 +60,7 @@
#define LONG_MIN (-0x7fffffff-1) /* min value for a long */
#if !defined(_ANSI_SOURCE) && !defined(_POSIX_SOURCE)
+#define CLK_TCK 128 /* ticks per second */
#define UQUAD_MAX 0xffffffffffffffffLL /* max unsigned quad */
#define QUAD_MAX 0x7fffffffffffffffLL /* max signed quad */
#define QUAD_MIN (-0x7fffffffffffffffLL-1) /* min signed quad */
diff --git a/sys/i386/include/lpt.h b/sys/i386/include/lpt.h
new file mode 100644
index 000000000000..87af5bcc5875
--- /dev/null
+++ b/sys/i386/include/lpt.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 1994 Geoffrey M. Rehmet
+ *
+ * This program is free software; you may redistribute it and/or
+ * modify it, provided that it retain the above copyright notice
+ * and the following disclaimer.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Geoff Rehmet, Rhodes University, South Africa <csgr@cs.ru.ac.za>
+ *
+ */
+
+#ifndef _LPT_PRINTER_H_
+#define _LPT_PRINTER_H_
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#define LPT_IRQ _IOW('p', 1, long) /* set interrupt status */
+
+#endif
diff --git a/sys/i386/include/mouse.h b/sys/i386/include/mouse.h
new file mode 100644
index 000000000000..95a66e474c6d
--- /dev/null
+++ b/sys/i386/include/mouse.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1992, 1993 Erik Forsberg.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ``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 I 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: mouse.h,v 1.1 1994/05/17 14:05:31 jkh Exp $
+ */
+
+struct mouseinfo {
+ unsigned char status;
+ char xmotion, ymotion;
+};
+
+#define BUTSTATMASK 0x07 /* Any mouse button down if any bit set */
+#define BUTCHNGMASK 0x38 /* Any mouse button changed if any bit set */
+
+#define BUT3STAT 0x01 /* Button 3 down if set */
+#define BUT2STAT 0x02 /* Button 2 down if set */
+#define BUT1STAT 0x04 /* Button 1 down if set */
+#define BUT3CHNG 0x08 /* Button 3 changed if set */
+#define BUT2CHNG 0x10 /* Button 2 changed if set */
+#define BUT1CHNG 0x20 /* Button 1 changed if set */
+#define MOVEMENT 0x40 /* Mouse movement detected */
+
+/* Ioctl definitions */
+
+#define MOUSEIOC ('M'<<8)
+#define MOUSEIOCREAD (MOUSEIOC|60)
diff --git a/sys/i386/include/npx.h b/sys/i386/include/npx.h
index ec29bf4fa024..168ef19fadd2 100644
--- a/sys/i386/include/npx.h
+++ b/sys/i386/include/npx.h
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)npx.h 5.3 (Berkeley) 1/18/91
- * $Id: npx.h,v 1.2 1993/10/16 14:39:22 rgrimes Exp $
+ * $Id: npx.h,v 1.3 1994/04/29 21:44:23 gclarkii Exp $
*/
/*
@@ -73,11 +73,13 @@ struct fpacc87 {
struct save87 {
struct env87 sv_env; /* floating point control/status */
struct fpacc87 sv_ac[8]; /* accumulator contents, 0-7 */
-#ifndef dontdef
u_long sv_ex_sw; /* status word for last exception (was pad) */
u_long sv_ex_tw; /* tag word for last exception (was pad) */
- u_char sv_pad[8 * 2 - 2 * 4]; /* bogus historical padding */
-#endif
+#ifdef GPL_MATH_EMULATE
+ u_char sv_pad[60];
+#else
+ u_char sv_pad[8 * 2 - 2 * 4]; /* bogus historical padding */
+#endif /* GPL_MATH_EMULATE */
};
/* Cyrix EMC memory - mapped coprocessor context switch information */
diff --git a/sys/i386/include/param.h b/sys/i386/include/param.h
index fe97ad809b25..953ebb9d36c4 100644
--- a/sys/i386/include/param.h
+++ b/sys/i386/include/param.h
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)param.h 5.8 (Berkeley) 6/28/91
- * $Id: param.h,v 1.13 1994/01/31 04:18:54 davidg Exp $
+ * $Id: param.h,v 1.14 1994/03/07 11:38:47 davidg Exp $
*/
#ifndef _MACHINE_PARAM_H_
@@ -62,7 +62,7 @@
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PAGE_MASK (PAGE_SIZE-1)
#define PGOFSET (NBPG-1) /* byte offset into page */
-#define NPTEPG (NBPG/(sizeof (struct pte)))
+#define NPTEPG (NBPG/(sizeof (pt_entry_t)))
/* XXX PDRSHIFT and PD_SHIFT are two names for the same thing */
#define PDRSHIFT 22 /* LOG2(NBPDR) */
@@ -103,7 +103,7 @@
#endif /* MSIZE */
#ifndef MCLSHIFT
-#define MCLSHIFT 11 /* convert bytes to m_buf clusters */
+#define MCLSHIFT 12 /* convert bytes to m_buf clusters */
#endif /* MCLSHIFT */
#define MCLBYTES (1 << MCLSHIFT) /* size of an m_buf cluster */
#define MCLOFSET (MCLBYTES - 1) /* offset within an m_buf cluster */
diff --git a/sys/i386/include/pcaudioio.h b/sys/i386/include/pcaudioio.h
new file mode 100644
index 000000000000..71c7ebb4c226
--- /dev/null
+++ b/sys/i386/include/pcaudioio.h
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1994 Søren 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.
+ * 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: pcaudioio.h,v 1.2 1994/05/20 12:22:40 sos Exp $
+ */
+
+#ifndef _PCAUDIOIO_H_
+#define _PCAUDIOIO_H_
+
+typedef struct audio_prinfo {
+ unsigned sample_rate; /* samples per second */
+ unsigned channels; /* # of channels (interleaved) */
+ unsigned precision; /* sample size in bits */
+ unsigned encoding; /* encoding method used */
+
+ unsigned gain; /* volume level: 0 - 255 */
+ unsigned port; /* input/output device */
+ unsigned _fill1[4];
+
+ unsigned samples; /* samples played */
+ unsigned eof; /* ?!? */
+ unsigned char pause; /* !=0 pause, ==0 continue */
+ unsigned char error; /* !=0 if overflow/underflow */
+ unsigned char waiting; /* !=0 if others wants access */
+ unsigned char _fill2[3];
+
+ unsigned char open; /* is device open */
+ unsigned char active; /* !=0 if sound hardware is active */
+} audio_prinfo_t;
+
+typedef struct audio_info {
+ audio_prinfo_t play;
+ audio_prinfo_t record;
+ unsigned monitor_gain;
+ unsigned _fill[4];
+} audio_info_t;
+
+#define AUDIO_ENCODING_ULAW (1) /* u-law encoding */
+#define AUDIO_ENCODING_ALAW (2) /* A-law encoding */
+#define AUDIO_ENCODING_RAW (3) /* linear encoding */
+
+#define AUDIO_MIN_GAIN (0) /* minimum volume value */
+#define AUDIO_MAX_GAIN (255) /* maximum volume value */
+
+#define AUDIO_INITINFO(i) memset((void*)i, 0xff, sizeof(audio_info_t))
+
+#define AUDIO_GETINFO _IOR('A', 1, audio_info_t)
+#define AUDIO_SETINFO _IOWR('A', 2, audio_info_t)
+#define AUDIO_DRAIN _IO('A', 3)
+#define AUDIO_FLUSH _IO('A', 4)
+
+#endif /*!_PCAUDIOIO_H*/
diff --git a/sys/i386/include/pmap.h b/sys/i386/include/pmap.h
index ba27f793735d..507baf94b356 100644
--- a/sys/i386/include/pmap.h
+++ b/sys/i386/include/pmap.h
@@ -42,7 +42,7 @@
*
* from: hp300: @(#)pmap.h 7.2 (Berkeley) 12/16/90
* from: @(#)pmap.h 7.4 (Berkeley) 5/12/91
- * $Id: pmap.h,v 1.10 1994/01/31 04:19:00 davidg Exp $
+ * $Id: pmap.h,v 1.16 1994/06/07 17:48:46 davidg Exp $
*/
#ifndef _PMAP_MACHINE_
@@ -58,7 +58,8 @@ struct pde
unsigned int
pd_v:1, /* valid bit */
pd_prot:2, /* access control */
- pd_mbz1:2, /* reserved, must be zero */
+ pd_ncpwt:1, /* page cache write through */
+ pd_ncpcd:1, /* page cache disable */
pd_u:1, /* hardware maintained 'used' bit */
:1, /* not used */
pd_mbz2:2, /* reserved, must be zero */
@@ -76,7 +77,8 @@ struct pte
unsigned int
pg_v:1, /* valid bit */
pg_prot:2, /* access control */
- pg_mbz1:2, /* reserved, must be zero */
+ pg_ncpwt:1, /* page cache write through */
+ pg_ncpcd:1, /* page cache disable */
pg_u:1, /* hardware maintained 'used' bit */
pg_m:1, /* hardware maintained modified bit */
pg_mbz2:2, /* reserved, must be zero */
@@ -91,10 +93,12 @@ unsigned int
#define PG_RW 0x00000002
#define PG_u 0x00000004
#define PG_PROT 0x00000006 /* all protection bits . */
-#define PG_W 0x00000200
-#define PG_N 0x00000800 /* Non-cacheable */
-#define PG_M 0x00000040
+#define PG_NC_PWT 0x00000008 /* page cache write through */
+#define PG_NC_PCD 0x00000010 /* page cache disable */
+#define PG_N 0x00000018 /* Non-cacheable */
#define PG_U 0x00000020
+#define PG_M 0x00000040
+#define PG_W 0x00000200
#define PG_FRAME 0xfffff000UL
#define PG_NOACC 0
@@ -115,8 +119,10 @@ unsigned int
#define PGEX_W 0x02 /* during a Write cycle */
#define PGEX_U 0x04 /* access from User mode (UPL) */
-typedef struct pde pd_entry_t; /* page directory entry */
-typedef struct pte pt_entry_t; /* Mach page table entry */
+/* typedef struct pde pd_entry_t; */ /* page directory entry */
+/* typedef struct pte pt_entry_t; */ /* Mach page table entry */
+typedef unsigned int *pd_entry_t;
+typedef unsigned int *pt_entry_t;
/*
* NKPDE controls the virtual space of the kernel, what ever is left, minus
@@ -145,18 +151,18 @@ typedef struct pte pt_entry_t; /* Mach page table entry */
#define KPTDI (APTDPTDI-NKPDE)/* start of kernel virtual pde's */
#define PTDPTDI (KPTDI-1) /* ptd entry that points to ptd! */
#define KSTKPTDI (PTDPTDI-1) /* ptd entry for u./kernel&user stack */
-#define KSTKPTEOFF (NBPG/sizeof(struct pde)-UPAGES) /* pte entry for kernel stack */
+#define KSTKPTEOFF (NBPG/sizeof(pd_entry_t)-UPAGES) /* pte entry for kernel stack */
-#define PDESIZE sizeof(struct pde) /* for assembly files */
-#define PTESIZE sizeof(struct pte) /* for assembly files */
+#define PDESIZE sizeof(pd_entry_t) /* for assembly files */
+#define PTESIZE sizeof(pt_entry_t) /* for assembly files */
/*
* Address of current and alternate address space page table maps
* and directories.
*/
#ifdef KERNEL
-extern struct pte PTmap[], APTmap[], Upte;
-extern struct pde PTD[], APTD[], PTDpde, APTDpde, Upde;
+extern pt_entry_t PTmap[], APTmap[], Upte;
+extern pd_entry_t PTD[], APTD[], PTDpde, APTDpde, Upde;
extern pt_entry_t *Sysmap;
extern int IdlePTD; /* physical address of "Idle" state directory */
@@ -171,12 +177,29 @@ extern int IdlePTD; /* physical address of "Idle" state directory */
#define vtopte(va) (PTmap + i386_btop(va))
#define kvtopte(va) vtopte(va)
#define ptetov(pt) (i386_ptob(pt - PTmap))
-#define vtophys(va) (i386_ptob(vtopte(va)->pg_pfnum) | ((int)(va) & PGOFSET))
+#define vtophys(va) (((int) (*vtopte(va))&PG_FRAME) | ((int)(va) & PGOFSET))
#define ispt(va) ((va) >= UPT_MIN_ADDRESS && (va) <= KPT_MAX_ADDRESS)
#define avtopte(va) (APTmap + i386_btop(va))
#define ptetoav(pt) (i386_ptob(pt - APTmap))
-#define avtophys(va) (i386_ptob(avtopte(va)->pg_pfnum) | ((int)(va) & PGOFSET))
+#define avtophys(va) (((int) (*avtopte(va))&PG_FRAME) | ((int)(va) & PGOFSET))
+
+#ifdef KERNEL
+/*
+ * Routine: pmap_kextract
+ * Function:
+ * Extract the physical page address associated
+ * kernel virtual address.
+ */
+static inline vm_offset_t
+pmap_kextract(va)
+ vm_offset_t va;
+{
+ vm_offset_t pa = *(int *)vtopte(va);
+ pa = (pa & PG_FRAME) | (va & ~PG_FRAME);
+ return pa;
+}
+#endif
/*
* macros to generate page directory/table indicies
@@ -252,7 +275,7 @@ extern void pmap_remove(struct pmap *, vm_offset_t, vm_offset_t);
extern void pmap_protect(struct pmap *, vm_offset_t, vm_offset_t, vm_prot_t);
extern void pmap_enter(pmap_t, vm_offset_t, vm_offset_t, vm_prot_t, boolean_t);
extern void pmap_change_wiring(pmap_t, vm_offset_t, boolean_t);
-extern inline struct pte *pmap_pte(pmap_t, vm_offset_t);
+extern inline pt_entry_t * const pmap_pte(pmap_t, vm_offset_t);
extern vm_offset_t pmap_extract(pmap_t, vm_offset_t);
extern void pmap_copy(pmap_t, pmap_t, vm_offset_t, vm_size_t, vm_offset_t);
extern void pmap_collect(pmap_t);
diff --git a/sys/i386/include/psl.h b/sys/i386/include/psl.h
index eb15d181b464..c877648ee796 100644
--- a/sys/i386/include/psl.h
+++ b/sys/i386/include/psl.h
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)psl.h 5.2 (Berkeley) 1/18/91
- * $Id: psl.h,v 1.3.2.1 1994/03/07 01:23:56 rgrimes Exp $
+ * $Id: psl.h,v 1.5 1994/06/07 23:53:52 phk Exp $
*/
#ifndef _MACHINE_PSL_H_
@@ -56,6 +56,10 @@
#define PSL_NT 0x00004000 /* nested task bit */
#define PSL_RF 0x00010000 /* restart flag bit */
#define PSL_VM 0x00020000 /* virtual 8086 mode bit */
+#define PSL_AC 0x00040000 /* alignment checking */
+#define PSL_VIF 0x00080000 /* virtual interrupt enable */
+#define PSL_VIP 0x00100000 /* virtual interrupt pending */
+#define PSL_ID 0x00200000 /* identification bit */
#define PSL_MBZ 0xffc08028 /* must be zero bits */
#define PSL_MBO 0x00000002 /* must be one bits */
diff --git a/sys/i386/include/pte.h b/sys/i386/include/pte.h
index ac0cb541de95..4b1f71091e2b 100644
--- a/sys/i386/include/pte.h
+++ b/sys/i386/include/pte.h
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)pte.h 5.5 (Berkeley) 5/9/91
- * $Id: pte.h,v 1.4 1994/01/31 06:52:41 davidg Exp $
+ * $Id: pte.h,v 1.5 1994/03/07 11:38:49 davidg Exp $
*/
#ifndef _MACHINE_PTE_H_
@@ -114,7 +114,7 @@ unsigned int
/*
* Pte related macros
*/
-#define dirty(pte) ((pte)->pg_m)
+#define dirty(pte) ((pte) & PG_M)
#ifndef LOCORE
#ifdef KERNEL
diff --git a/sys/i386/include/soundcard.h b/sys/i386/include/soundcard.h
index 23f20313d98a..ce28a144242d 100644
--- a/sys/i386/include/soundcard.h
+++ b/sys/i386/include/soundcard.h
@@ -1,5 +1,5 @@
#ifndef _SOUNDCARD_H_
-#define _SOUNDCARD_H_ 1
+#define _SOUNDCARD_H_
/*
* Copyright by Hannu Savolainen 1993
*
@@ -30,14 +30,15 @@
/*
* If you make modifications to this file, please contact me before
* distributing the modified version. There is already enough
- * diversity in the world.
+ * divercity in the world.
*
* Regards,
* Hannu Savolainen
- * hsavolai@cs.helsinki.fi
+ * hannu@voxware.pp.fi, Hannu.Savolainen@helsinki.fi
*/
-#define SOUND_VERSION 200
+#define SOUND_VERSION 205
+#define VOXWARE
#include <sys/ioctl.h>
@@ -50,6 +51,8 @@
#define SNDCARD_PAS 3
#define SNDCARD_GUS 4
#define SNDCARD_MPU401 5
+#define SNDCARD_SB16 6
+#define SNDCARD_SB16MIDI 7
/***********************************
* IOCTL Commands for /dev/sequencer
@@ -66,9 +69,9 @@
*/
/* #define IOCTYPE (0xff<<8) */
#define IOCPARM_MASK 0x7f /* parameters must be < 128 bytes */
-#define IOC_VOID 0x20000000 /* no parameters */
-#define IOC_OUT 0x40000000 /* copy out parameters */
-#define IOC_IN 0x80000000 /* copy in parameters */
+#define IOC_VOID 0x00000000 /* no parameters */
+#define IOC_OUT 0x20000000 /* copy out parameters */
+#define IOC_IN 0x40000000 /* copy in parameters */
#define IOC_INOUT (IOC_IN|IOC_OUT)
/* the 0x20000000 is so we can distinguish new ioctl's from old */
#define _IO(x,y) ((int)(IOC_VOID|(x<<8)|y))
@@ -180,7 +183,7 @@ struct patch_info {
int volume;
int spare[4];
- char data[0]; /* The waveform data starts here */
+ char data[1]; /* The waveform data starts here */
};
@@ -307,6 +310,14 @@ struct patmgr_info { /* Note! size must be < 4k since kmalloc() is used */
#define CTRL_EXPRESSION 253
#define CTRL_MAIN_VOLUME 252
#define SEQ_BALANCE 11
+#define SEQ_VOLMODE 12
+
+/*
+ * Volume mode decides how volumes are used
+ */
+
+#define VOL_METHOD_ADAGIO 1
+#define VOL_METHOD_LINEAR 2
/*
* Note! SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO are used also as
@@ -402,7 +413,8 @@ struct midi_info {
char name[30];
int device; /* 0-N. INITIALIZE BEFORE CALLING */
unsigned long capabilities; /* To be defined later */
- int dummies[19]; /* Reserve space */
+ int dev_type;
+ int dummies[18]; /* Reserve space */
};
/********************************************
@@ -418,6 +430,7 @@ struct midi_info {
#define SOUND_PCM_WRITE_CHANNELS _IOWR('P', 6, int)
#define SOUND_PCM_WRITE_FILTER _IOWR('P', 7, int)
#define SNDCTL_DSP_POST _IO ('P', 8)
+#define SNDCTL_DSP_SUBDIVIDE _IOWR('P', 9, int)
#define SOUND_PCM_READ_RATE _IOR ('P', 2, int)
#define SOUND_PCM_READ_CHANNELS _IOR ('P', 6, int)
@@ -430,6 +443,7 @@ struct midi_info {
#define SOUND_PCM_POST SNDCTL_DSP_POST
#define SOUND_PCM_RESET SNDCTL_DSP_RESET
#define SOUND_PCM_SYNC SNDCTL_DSP_SYNC
+#define SOUND_PCM_SUBDIVIDE SNDCTL_DSP_SUBDIVIDE
/*********************************************
* IOCTL commands for /dev/mixer
@@ -624,7 +638,8 @@ void seqbuf_dump(void); /* This function must be provided by programs */
* }
*/
-#define SEQ_DEFINEBUF(len) unsigned char _seqbuf[len]; int _seqbuflen = len, _seqbufptr = 0
+#define SEQ_DEFINEBUF(len) unsigned char _seqbuf[len]; int _seqbuflen = len; int _seqbufptr = 0
+#define SEQ_DECLAREBUF() extern unsigned char _seqbuf[]; extern int _seqbuflen;extern int _seqbufptr
#define SEQ_PM_DEFINES struct patmgr_info _pm_info
#define _SEQ_NEEDBUF(len) if ((_seqbufptr+(len)) > _seqbuflen) seqbuf_dump()
#define _SEQ_ADVBUF(len) _seqbufptr += len
@@ -638,6 +653,17 @@ void seqbuf_dump(void); /* This function must be provided by programs */
_pm_info.parm1 = bank, _pm_info.parm2 = 128, \
ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info))
+#define SEQ_VOLUME_MODE(dev, mode) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_VOLMODE;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (mode);\
+ _seqbuf[_seqbufptr+4] = 0;\
+ _seqbuf[_seqbufptr+5] = 0;\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
#define SEQ_START_NOTE(dev, voice, note, vol) {_SEQ_NEEDBUF(8);\
_seqbuf[_seqbufptr] = SEQ_EXTENDED;\
_seqbuf[_seqbufptr+1] = SEQ_NOTEON;\
@@ -728,8 +754,8 @@ void seqbuf_dump(void); /* This function must be provided by programs */
_seqbuf[_seqbufptr+2] = (device);\
_seqbuf[_seqbufptr+3] = 0;\
_SEQ_ADVBUF(4);}
-#define SEQ_WRPATCH(patch, len) {if (_seqbufptr) seqbuf_dump();\
- if (write(seqfd, (char*)(patch), len)==-1) \
+#define SEQ_WRPATCH(patchx, len) {if (_seqbufptr) seqbuf_dump();\
+ if (write(seqfd, (char*)(patchx), len)==-1) \
perror("Write patch: /dev/sequencer");}
#endif
diff --git a/sys/i386/include/spl.h b/sys/i386/include/spl.h
new file mode 100644
index 000000000000..0be93644a463
--- /dev/null
+++ b/sys/i386/include/spl.h
@@ -0,0 +1,104 @@
+#ifndef _MACHINE_IPL_H_
+#define _MACHINE_IPL_H_
+
+#include "machine/../isa/ipl.h" /* XXX "machine" means cpu for i386 */
+
+/*
+ * Software interrupt bit numbers in priority order. The priority only
+ * determines which swi will be dispatched next; a higher priority swi
+ * may be dispatched when a nested h/w interrupt handler returns.
+ */
+#define SWI_TTY (NHWI + 0)
+#define SWI_NET (NHWI + 1)
+#define SWI_CLOCK 30
+#define SWI_AST 31
+
+/*
+ * Corresponding interrupt-pending bits for ipending.
+ */
+#define SWI_TTY_PENDING (1 << SWI_TTY)
+#define SWI_NET_PENDING (1 << SWI_NET)
+#define SWI_CLOCK_PENDING (1 << SWI_CLOCK)
+#define SWI_AST_PENDING (1 << SWI_AST)
+
+/*
+ * Corresponding interrupt-disable masks for cpl. The ordering is now by
+ * inclusion (where each mask is considered as a set of bits). Everything
+ * except SWI_AST_MASK includes SWI_CLOCK_MASK so that softclock() doesn't
+ * run while other swi handlers are running and timeout routines can call
+ * swi handlers. Everything includes SWI_AST_MASK so that AST's are masked
+ * until just before return to user mode.
+ */
+#define SWI_TTY_MASK (SWI_TTY_PENDING | SWI_CLOCK_MASK)
+#define SWI_NET_MASK (SWI_NET_PENDING | SWI_CLOCK_MASK)
+#define SWI_CLOCK_MASK (SWI_CLOCK_PENDING | SWI_AST_MASK)
+#define SWI_AST_MASK SWI_AST_PENDING
+#define SWI_MASK (~HWI_MASK)
+
+#ifndef LOCORE
+
+extern unsigned bio_imask; /* group of interrupts masked with splbio() */
+extern unsigned cpl; /* current priority level mask */
+extern unsigned high_imask; /* group of interrupts masked with splhigh() */
+extern unsigned net_imask; /* group of interrupts masked with splimp() */
+extern volatile unsigned ipending; /* active interrupts masked by cpl */
+extern volatile unsigned netisr;
+extern unsigned tty_imask; /* group of interrupts masked with spltty() */
+
+/*
+ * ipending has to be volatile so that it is read every time it is accessed
+ * in splx() and spl0(), but we don't want it to be read nonatomically when
+ * it is changed. Pretending that ipending is a plain int happens to give
+ * suitable atomic code for "ipending |= constant;".
+ */
+#define setsoftast() (*(unsigned *)&ipending |= SWI_AST_PENDING)
+#define setsoftclock() (*(unsigned *)&ipending |= SWI_CLOCK_PENDING)
+#define setsoftnet() (*(unsigned *)&ipending |= SWI_NET_PENDING)
+#define setsofttty() (*(unsigned *)&ipending |= SWI_TTY_PENDING)
+
+void unpend_V __P((void));
+
+#ifdef __GNUC__
+
+void splz __P((void));
+
+#define GENSPL(name, set_cpl) \
+static __inline int name(void) \
+{ \
+ unsigned x; \
+ \
+ x = cpl; \
+ set_cpl; \
+ return (x); \
+}
+
+GENSPL(splbio, cpl |= bio_imask)
+GENSPL(splclock, cpl = HWI_MASK | SWI_MASK)
+GENSPL(splhigh, cpl = HWI_MASK | SWI_MASK)
+GENSPL(splimp, cpl |= net_imask)
+GENSPL(splnet, cpl |= SWI_NET_MASK)
+GENSPL(splsoftclock, cpl = SWI_CLOCK_MASK)
+GENSPL(splsofttty, cpl |= SWI_TTY_MASK)
+GENSPL(spltty, cpl |= tty_imask)
+
+static __inline void
+spl0(void)
+{
+ cpl = SWI_AST_MASK;
+ if (ipending & ~SWI_AST_MASK)
+ splz();
+}
+
+static __inline void
+splx(int ipl)
+{
+ cpl = ipl;
+ if (ipending & ~ipl)
+ splz();
+}
+
+#endif /* __GNUC__ */
+
+#endif /* LOCORE */
+
+#endif /* _MACHINE_IPL_H_ */
diff --git a/sys/i386/include/ultrasound.h b/sys/i386/include/ultrasound.h
index 9b6e78fbadce..40e2443e6f65 100644
--- a/sys/i386/include/ultrasound.h
+++ b/sys/i386/include/ultrasound.h
@@ -32,12 +32,6 @@
* and not portable.
*/
-#ifdef linux
-#include <linux/soundcard.h>
-#else
-#include <machine/soundcard.h>
-#endif
-
/*
* Private events for Gravis Ultrasound (GUS)
*
@@ -90,6 +84,7 @@
#define _GUS_VOICEFADE 0x0d
#define _GUS_VOLUME_SCALE 0x0e
#define _GUS_VOICEVOL2 0x0f
+#define _GUS_VOICE_POS 0x10
/*
* GUS API macros
@@ -120,5 +115,7 @@
#define GUS_RAMPON(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_RAMPON, (p1), 0)
#define GUS_RAMPOFF(chn, voice) _GUS_CMD(chn, voice, _GUS_RAMPOFF, 0, 0)
#define GUS_VOLUME_SCALE(chn, voice, p1, p2) _GUS_CMD(chn, voice, _GUS_VOLUME_SCALE, (p1), (p2))
+#define GUS_VOICE_POS(chn, voice, p) _GUS_CMD(chn, voice, _GUS_VOICE_POS, \
+ (p) & 0xffff, ((p) >> 16) & 0xffff)
#endif
diff --git a/sys/i386/include/vmparam.h b/sys/i386/include/vmparam.h
index a5b657cd82c2..c5ed04dde346 100644
--- a/sys/i386/include/vmparam.h
+++ b/sys/i386/include/vmparam.h
@@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* from: @(#)vmparam.h 5.9 (Berkeley) 5/12/91
- * $Id: vmparam.h,v 1.11.2.1 1994/03/24 08:57:03 rgrimes Exp $
+ * $Id: vmparam.h,v 1.12 1994/03/21 09:35:24 davidg Exp $
*/
diff --git a/sys/i386/isa/aha1542.c b/sys/i386/isa/aha1542.c
index efeee385ba51..df1e3cada643 100644
--- a/sys/i386/isa/aha1542.c
+++ b/sys/i386/isa/aha1542.c
@@ -12,7 +12,7 @@
* on the understanding that TFS is not responsible for the correct
* functioning of this software in any circumstances.
*
- * $Id: aha1542.c,v 1.20.2.2 1994/05/03 05:16:50 rgrimes Exp $
+ * $Id: aha1542.c,v 1.27 1994/06/05 19:18:10 ats Exp $
*/
/*
@@ -299,8 +299,8 @@ struct aha_data {
struct aha_ccb *aha_ccb_free; /* the next free ccb */
struct aha_ccb aha_ccb[AHA_MBX_SIZE]; /* all the CCBs */
int aha_int; /* our irq level */
- int aha_dma; /* out DMA req channel */
- int aha_scsi_dev; /* ourscsi bus address */
+ int aha_dma; /* our DMA req channel */
+ int aha_scsi_dev; /* our scsi bus address */
struct scsi_link sc_link; /* prototype for subdevs */
} *ahadata[NAHA];
@@ -589,6 +589,7 @@ ahaattach(dev)
aha->sc_link.adapter_targ = aha->aha_scsi_dev;
aha->sc_link.adapter = &aha_switch;
aha->sc_link.device = &aha_dev;
+ aha->sc_link.flags = SDEV_BOUNCE;
/*
* ask the adapter what subunits are present
@@ -741,7 +742,7 @@ aha_get_ccb(unit, flags)
* to come free
*/
while ((!(rc = aha->aha_ccb_free)) && (!(flags & SCSI_NOSLEEP))) {
- sleep(&aha->aha_ccb_free, PRIBIO);
+ tsleep((caddr_t)&aha->aha_ccb_free, PRIBIO, "ahaccb", 0);
}
if (rc) {
aha->aha_ccb_free = aha->aha_ccb_free->next;
@@ -916,7 +917,7 @@ aha_init(unit)
#ifdef AHADEBUG
printf("aha%d: extended bios flags %x\n", unit, extbios.flags);
#endif /* AHADEBUG */
- printf("aha%d: 1542C/CF detected, unlocking mailbox\n");
+ printf("aha%d: 1542C/CF detected, unlocking mailbox\n", unit);
aha_cmd(unit, 2, 0, 0, 0, AHA_MBX_ENABLE,
0, extbios.mailboxlock);
}
@@ -1006,7 +1007,7 @@ aha_init(unit)
return (EIO);
}
#else
- printf ("\n");
+ printf (" (bus speed defaulted)\n");
#endif /*TUNE_1542*/
/*
* Initialize mail box
diff --git a/sys/i386/isa/bt742a.c b/sys/i386/isa/bt742a.c
index e160e9167cb1..990344f28ea6 100644
--- a/sys/i386/isa/bt742a.c
+++ b/sys/i386/isa/bt742a.c
@@ -12,7 +12,7 @@
* on the understanding that TFS is not responsible for the correct
* functioning of this software in any circumstances.
*
- * $Id: bt742a.c,v 1.12 1993/12/19 00:50:29 wollman Exp $
+ * $Id: bt742a.c,v 1.22 1994/06/15 04:19:23 jkh Exp $
*/
/*
@@ -126,7 +126,7 @@ struct bt_cmd_buf {
* these could be bigger but we need the bt_data to fit on a single page..
*/
-#define BT_MBX_SIZE 16 /* mail box size (MAX 255 MBxs) */
+#define BT_MBX_SIZE 32 /* mail box size (MAX 255 MBxs) */
/* don't need that many really */
#define BT_CCB_MAX 32 /* store up to 32CCBs at any one time */
/* in bt742a H/W ( Not MAX ? ) */
@@ -299,6 +299,21 @@ struct bt_config {
u_char :5;
};
+/*
+ * Determin 32bit address/Data firmware functionality from Bus type
+ * Note: bt742a/747[s|d]/757/946/445s will return 'E'
+ * bt542b/545s/545d will be return 'A'
+ * 94/05/18 amurai@spec.co.jp
+ */
+#define BT_BUS_TYPE_24bit 'A' /* PC/AT 24 bit address bus type */
+#define BT_BUS_TYPE_32bit 'E' /* EISA/VLB/PCI 32 bit address bus type */
+#define BT_BUS_TYPE_MCA 'M' /* Micro chanel is ? forget it right now */
+struct bt_ext_info {
+ u_char bus_type; /* Host adapter bus type */
+ u_char bios_addr; /* Bios Address-Not use*/
+ u_short max_seg; /* Max segment List */
+};
+
#define INT9 0x01
#define INT10 0x02
#define INT11 0x04
@@ -568,7 +583,8 @@ btprobe(dev)
}
/*
* If it's there, put in it's interrupt vectors
- */ dev->id_unit = unit;
+ */
+ dev->id_unit = unit;
dev->id_irq = (1 << bt->bt_int);
dev->id_drq = bt->bt_dma;
@@ -593,6 +609,7 @@ btattach(dev)
bt->sc_link.adapter_targ = bt->bt_scsi_dev;
bt->sc_link.adapter = &bt_switch;
bt->sc_link.device = &bt_dev;
+ bt->sc_link.flags = SDEV_BOUNCE;
/*
* ask the adapter what subunits are present
@@ -818,7 +835,8 @@ bt_get_ccb(unit, flags)
goto gottit;
} else {
if (!(flags & SCSI_NOSLEEP)) {
- sleep(&bt->bt_ccb_free, PRIBIO);
+ tsleep((caddr_t)&bt->bt_ccb_free, PRIBIO,
+ "btccb", 0);
}
}
}
@@ -987,6 +1005,7 @@ bt_init(unit)
unsigned char ad[4];
volatile int i, sts;
struct bt_config conf;
+ struct bt_ext_info info;
/*
* reset board, If it doesn't respond, assume
@@ -1008,6 +1027,32 @@ bt_init(unit)
return (ENXIO);
}
/*
+ * Make sure board has a capability of 32bit addressing.
+ * and Firmware also need a capability of 32bit addressing pointer
+ * in Extended mailbox and ccb structure.
+ * 94/05/18 amurai@spec.co.jp
+ */
+ bt_cmd(unit, 1, sizeof(info),0,&info, BT_INQUIRE_EXTENDED,sizeof(info));
+ switch (info.bus_type) {
+ case BT_BUS_TYPE_24bit: /* PC/AT 24 bit address bus */
+ printf("bt%d: bt54x-ISA(24bit) bus detected\n", unit);
+ break;
+ case BT_BUS_TYPE_32bit: /* EISA/VLB/PCI 32 bit bus */
+ printf("bt%d: PCI/EISA/VLB(32bit) bus detected\n",unit);
+ break;
+ case BT_BUS_TYPE_MCA: /* forget it right now */
+ printf("bt%d: MCA bus architecture detected..", unit);
+ printf("[giving up]\n");
+ return (ENXIO);
+ break;
+ default:
+ printf("bt%d: Unknown state detected...", unit);
+ printf("[giving up]\n");
+ return (ENXIO);
+ break;
+ }
+
+ /*
* Assume we have a board at this stage
* setup dma channel from jumpers and save int
* level
@@ -1107,8 +1152,6 @@ bt_init(unit)
bt->bt_mbx.tmbi = &bt->bt_mbx.mbi[0];
bt_inquire_setup_information(unit);
- /* Enable round-robin scheme - appeared at firmware rev. 3.31 */
- bt_cmd(unit, 1, 0, 0, 0, BT_ROUND_ROBIN, BT_ENABLE);
/*
* Note that we are going and return (to probe)
@@ -1122,9 +1165,14 @@ bt_inquire_setup_information(unit)
{
struct bt_data *bt = btdata[unit];
struct bt_setup setup;
+ char dummy[8];
struct bt_boardID bID;
int i;
+ /* Inquire Installed Devices */
+ bzero( &dummy[0], sizeof(dummy) );
+ bt_cmd(unit, 0, sizeof(dummy), 10, &dummy[0], BT_DEV_GET);
+
/* Inquire Board ID to Bt742 for firmware version */
bt_cmd(unit, 0, sizeof(bID), 0, &bID, BT_INQUIRE);
printf("bt%d: version %c.%c, ",
@@ -1143,19 +1191,37 @@ bt_inquire_setup_information(unit)
} else {
printf("no parity, ");
}
- printf("%d mbxs, %d ccbs\n", setup.num_mbx, bt->numccbs);
+ printf("%d mbxs, %d ccbs\n", setup.num_mbx, BT_CCB_MAX);
+
+ /*
+ * Displaying SCSI negotiation value by each target.
+ * How can I determin FAST scsi value? XXX amurai@spec.co.jp
+ */
for (i = 0; i < 8; i++) {
- if (!setup.sync[i].offset &&
- !setup.sync[i].period &&
- !setup.sync[i].valid)
+ if (!setup.sync[i].valid)
continue;
+ if (!setup.sync[i].offset && !setup.sync[i].period )
+ printf("bt%d: targ %d async\n", unit, i);
+ else
+ printf("bt%d: targ %d offset=%02d, period=%dnsec\n",
+ unit, i,
+ setup.sync[i].offset,
+ 200 + setup.sync[i].period * 50 );
+ }
- printf("bt%d: dev%02d Offset=%d,Transfer period=%d, Synchronous? %s",
- unit, i,
- setup.sync[i].offset, setup.sync[i].period,
- setup.sync[i].valid ? "Yes" : "No");
+ /*
+ * Enable round-robin scheme - appeared at firmware rev. 3.31
+ * Below rev 2.XX firmware has a problem for issuing
+ * BT_ROUND_ROBIN command amurai@spec.co.jp
+ */
+ if ( bID.firm_revision != '2' ) {
+ printf("bt%d: Enabling Round robin scheme\n", unit);
+ bt_cmd(unit, 1, 0, 0, 0, BT_ROUND_ROBIN, BT_ENABLE);
+ } else {
+ printf("bt%d: Not Enabling Round robin scheme\n", unit);
}
+
}
#ifndef min
@@ -1434,13 +1500,15 @@ bt_timeout(caddr_t arg1, int arg2)
struct bt_data *bt;
int s = splbio();
+ /*
+ * A timeout routine in kernel DONOT unlink
+ * Entry chains when time outed....So infinity Loop..
+ * 94/04/20 amurai@spec.co.jp
+ */
+ untimeout(bt_timeout, (caddr_t)ccb);
+
unit = ccb->xfer->sc_link->adapter_unit;
bt = btdata[unit];
- printf("bt%d:%d:%d (%s%d) timed out ", unit
- ,ccb->xfer->sc_link->target
- ,ccb->xfer->sc_link->lun
- ,ccb->xfer->sc_link->device->name
- ,ccb->xfer->sc_link->dev_unit);
#ifdef UTEST
bt_print_active_ccbs(unit);
@@ -1467,13 +1535,14 @@ bt_timeout(caddr_t arg1, int arg2)
ccb->xfer->retries = 0; /* I MEAN IT ! */
ccb->host_stat = BT_ABORTED;
bt_done(unit, ccb);
- } else { /* abort the operation that has timed out */
+ } else {
+ /* abort the operation that has timed out */
printf("bt%d: Try to abort\n", unit);
bt_send_mbo(unit, ~SCSI_NOMASK,
BT_MBO_ABORT, ccb);
/* 2 secs for the abort */
- timeout(bt_timeout, (caddr_t)ccb, 2 * hz);
ccb->flags = CCB_ABORTED;
+ timeout(bt_timeout, (caddr_t)ccb, 2 * hz);
}
splx(s);
}
diff --git a/sys/i386/isa/bt742a_32.c b/sys/i386/isa/bt742a_32.c
new file mode 100644
index 000000000000..f8eabf4de61f
--- /dev/null
+++ b/sys/i386/isa/bt742a_32.c
@@ -0,0 +1,1694 @@
+/*
+ * Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * $Id: bt742a_32.c,v 1.1 1994/06/17 19:05:57 jkh Exp $
+ */
+
+/*
+ * Bulogic/Bustek 32 bit Addressing Mode SCSI driver (Was bt742a.c)
+ *
+ * THIS DRIVER IS EXPERIMENTAL AND NOT ENABLED BY DEFAULT. It is
+ * provided here merely for reference purposes.
+ *
+ * NOTE: 1. Some bt5xx card can NOT handling 32 bit addressing mode.
+ * 2. OLD bt445s Revision A,B,C,D(nowired) + any firmware version
+ * has broken busmaster for handling 32 bit addressing on H/W bus side.
+ * 3. Extend probing still need to confirm by user base due to
+ * several H/W and firmware dependency. If you have a problem with
+ * extend probing, please contact to 'amurai@spec.co.jp'
+ *
+ * amurai@spec.co.jp 94/6/16
+ */
+
+#include <sys/types.h>
+
+#ifdef KERNEL /* don't laugh.. it compiles to a program too.. look */
+#include <bt.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 <sys/user.h>
+#endif /* KERNEL */
+
+#include <i386/isa/isa_device.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#ifdef KERNEL
+#include "ddb.h"
+#include "kernel.h"
+#else /*KERNEL */
+#define NBT 1
+#endif /*KERNEL */
+
+typedef unsigned long int physaddr;
+
+/*
+ * I/O Port Interface
+ */
+
+#define BT_BASE bt->bt_base
+#define BT_CTRL_STAT_PORT (BT_BASE + 0x0) /* control & status */
+#define BT_CMD_DATA_PORT (BT_BASE + 0x1) /* cmds and datas */
+#define BT_INTR_PORT (BT_BASE + 0x2) /* Intr. stat */
+
+/*
+ * BT_CTRL_STAT bits (write)
+ */
+
+#define BT_HRST 0x80 /* Hardware reset */
+#define BT_SRST 0x40 /* Software reset */
+#define BT_IRST 0x20 /* Interrupt reset */
+#define BT_SCRST 0x10 /* SCSI bus reset */
+
+/*
+ * BT_CTRL_STAT bits (read)
+ */
+
+#define BT_STST 0x80 /* Self test in Progress */
+#define BT_DIAGF 0x40 /* Diagnostic Failure */
+#define BT_INIT 0x20 /* Mbx Init required */
+#define BT_IDLE 0x10 /* Host Adapter Idle */
+#define BT_CDF 0x08 /* cmd/data out port full */
+#define BT_DF 0x04 /* Data in port full */
+#define BT_INVDCMD 0x01 /* Invalid command */
+
+/*
+ * BT_CMD_DATA bits (write)
+ */
+
+#define BT_NOP 0x00 /* No operation */
+#define BT_MBX_INIT 0x01 /* Mbx initialization */
+#define BT_START_SCSI 0x02 /* start scsi command */
+#define BT_START_BIOS 0x03 /* start bios command */
+#define BT_INQUIRE 0x04 /* Adapter Inquiry */
+#define BT_MBO_INTR_EN 0x05 /* Enable MBO available interrupt */
+#define BT_SEL_TIMEOUT_SET 0x06 /* set selection time-out */
+#define BT_BUS_ON_TIME_SET 0x07 /* set bus-on time */
+#define BT_BUS_OFF_TIME_SET 0x08 /* set bus-off time */
+#define BT_SPEED_SET 0x09 /* set transfer speed */
+#define BT_DEV_GET 0x0a /* return installed devices */
+#define BT_CONF_GET 0x0b /* return configuration data */
+#define BT_TARGET_EN 0x0c /* enable target mode */
+#define BT_SETUP_GET 0x0d /* return setup data */
+#define BT_WRITE_CH2 0x1a /* write channel 2 buffer */
+#define BT_READ_CH2 0x1b /* read channel 2 buffer */
+#define BT_WRITE_FIFO 0x1c /* write fifo buffer */
+#define BT_READ_FIFO 0x1d /* read fifo buffer */
+#define BT_ECHO 0x1e /* Echo command data */
+#define BT_MBX_INIT_EXTENDED 0x81 /* Mbx initialization */
+#define BT_INQUIRE_EXTENDED 0x8D /* Adapter Setup Inquiry */
+
+/* The following command appeared at FirmWare 3.31 */
+#define BT_ROUND_ROBIN 0x8f /* Enable/Disable(default) round robin */
+#define BT_DISABLE 0x00 /* Parameter value for Disable */
+#define BT_ENABLE 0x01 /* Parameter value for Enable */
+
+struct bt_cmd_buf {
+ u_char byte[16];
+};
+
+/*
+ * BT_INTR_PORT bits (read)
+ */
+
+#define BT_ANY_INTR 0x80 /* Any interrupt */
+#define BT_SCRD 0x08 /* SCSI reset detected */
+#define BT_HACC 0x04 /* Command complete */
+#define BT_MBOA 0x02 /* MBX out empty */
+#define BT_MBIF 0x01 /* MBX in full */
+
+/*
+ * Mail box defs etc.
+ * these could be bigger but we need the bt_data to fit on a single page..
+ */
+
+#define BT_MBX_SIZE 32 /* mail box size (MAX 255 MBxs) */
+ /* don't need that many really */
+#define BT_CCB_MAX 32 /* store up to 32CCBs at any one time */
+ /* in bt742a H/W ( Not MAX ? ) */
+#define CCB_HASH_SIZE 32 /* when we have a physical addr. for */
+ /* a ccb and need to find the ccb in */
+ /* space, look it up in the hash table */
+#define CCB_HASH_SHIFT 9 /* only hash on multiples of 512 */
+#define CCB_HASH(x) ((((long int)(x))>>CCB_HASH_SHIFT) % CCB_HASH_SIZE)
+
+#define bt_nextmbx( wmb, mbx, mbio ) \
+ if ( (wmb) == &((mbx)->mbio[BT_MBX_SIZE - 1 ]) ) \
+ (wmb) = &((mbx)->mbio[0]); \
+ else \
+ (wmb)++;
+
+typedef struct bt_mbx_out {
+ physaddr ccb_addr;
+ unsigned char dummy[3];
+ unsigned char cmd;
+} BT_MBO;
+
+typedef struct bt_mbx_in {
+ physaddr ccb_addr;
+ unsigned char btstat;
+ unsigned char sdstat;
+ unsigned char dummy;
+ unsigned char stat;
+} BT_MBI;
+
+struct bt_mbx {
+ BT_MBO mbo[BT_MBX_SIZE];
+ BT_MBI mbi[BT_MBX_SIZE];
+ BT_MBO *tmbo; /* Target Mail Box out */
+ BT_MBI *tmbi; /* Target Mail Box in */
+};
+
+/*
+ * mbo.cmd values
+ */
+
+#define BT_MBO_FREE 0x0 /* MBO entry is free */
+#define BT_MBO_START 0x1 /* MBO activate entry */
+#define BT_MBO_ABORT 0x2 /* MBO abort entry */
+
+/*
+ * mbi.stat values
+ */
+
+#define BT_MBI_FREE 0x0 /* MBI entry is free */
+#define BT_MBI_OK 0x1 /* completed without error */
+#define BT_MBI_ABORT 0x2 /* aborted ccb */
+#define BT_MBI_UNKNOWN 0x3 /* Tried to abort invalid CCB */
+#define BT_MBI_ERROR 0x4 /* Completed with error */
+
+#if defined(BIG_DMA)
+WARNING...THIS WON'T WORK(won't fit on 1 page)
+/* #define BT_NSEG 2048*/ /* Number of scatter gather segments - to much vm */
+#define BT_NSEG 128
+#else
+#define BT_NSEG 33
+#endif /* BIG_DMA */
+
+struct bt_scat_gath {
+ unsigned long seg_len;
+ physaddr seg_addr;
+};
+
+struct bt_ccb {
+ unsigned char opcode;
+ unsigned char:3, data_in:1, data_out:1,:3;
+ unsigned char scsi_cmd_length;
+ unsigned char req_sense_length;
+ /*------------------------------------longword boundary */
+ unsigned long data_length;
+ /*------------------------------------longword boundary */
+ physaddr data_addr;
+ /*------------------------------------longword boundary */
+ unsigned char dummy[2];
+ unsigned char host_stat;
+ unsigned char target_stat;
+ /*------------------------------------longword boundary */
+ unsigned char target;
+ unsigned char lun;
+ unsigned char scsi_cmd[12]; /* 12 bytes (bytes only) */
+ unsigned char dummy2[1];
+ unsigned char link_id;
+ /*------------------------------------4 longword boundary */
+ physaddr link_addr;
+ /*------------------------------------longword boundary */
+ physaddr sense_ptr;
+/*-----end of HW fields-------------------------------longword boundary */
+ struct scsi_sense_data scsi_sense;
+ /*------------------------------------longword boundary */
+ struct bt_scat_gath scat_gath[BT_NSEG];
+ /*------------------------------------longword boundary */
+ struct bt_ccb *next;
+ /*------------------------------------longword boundary */
+ struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */
+ /*------------------------------------longword boundary */
+ struct bt_mbx_out *mbx; /* pointer to mail box */
+ /*------------------------------------longword boundary */
+ int flags;
+#define CCB_FREE 0
+#define CCB_ACTIVE 1
+#define CCB_ABORTED 2
+ /*------------------------------------longword boundary */
+ struct bt_ccb *nexthash; /* if two hash the same */
+ /*------------------------------------longword boundary */
+ physaddr hashkey; /*physaddr of this ccb */
+ /*------------------------------------longword boundary */
+};
+
+/*
+ * opcode fields
+ */
+
+#define BT_INITIATOR_CCB 0x00 /* SCSI Initiator CCB */
+#define BT_TARGET_CCB 0x01 /* SCSI Target CCB */
+#define BT_INIT_SCAT_GATH_CCB 0x02 /* SCSI Initiator with scattter gather */
+#define BT_RESET_CCB 0x81 /* SCSI Bus reset */
+
+/*
+ * bt_ccb.host_stat values
+ */
+
+#define BT_OK 0x00 /* cmd ok */
+#define BT_LINK_OK 0x0a /* Link cmd ok */
+#define BT_LINK_IT 0x0b /* Link cmd ok + int */
+#define BT_SEL_TIMEOUT 0x11 /* Selection time out */
+#define BT_OVER_UNDER 0x12 /* Data over/under run */
+#define BT_BUS_FREE 0x13 /* Bus dropped at unexpected time */
+#define BT_INV_BUS 0x14 /* Invalid bus phase/sequence */
+#define BT_BAD_MBO 0x15 /* Incorrect MBO cmd */
+#define BT_BAD_CCB 0x16 /* Incorrect ccb opcode */
+#define BT_BAD_LINK 0x17 /* Not same values of LUN for links */
+#define BT_INV_TARGET 0x18 /* Invalid target direction */
+#define BT_CCB_DUP 0x19 /* Duplicate CCB received */
+#define BT_INV_CCB 0x1a /* Invalid CCB or segment list */
+#define BT_ABORTED 42 /* pseudo value from driver */
+
+struct bt_boardID {
+ u_char board_type;
+ u_char custom_feture;
+ char firm_revision;
+ u_char firm_version;
+};
+
+struct bt_setup {
+ u_char sync_neg:1;
+ u_char parity:1;
+ u_char :6;
+ u_char speed;
+ u_char bus_on;
+ u_char bus_off;
+ u_char num_mbx;
+ u_char mbx[3]; /* make a sense with back-word compatibility*/
+ struct {
+ u_char offset:4;
+ u_char period:3;
+ u_char valid:1;
+ } sync[8];
+ u_char disc_sts;
+};
+
+struct bt_config {
+ u_char chan;
+ u_char intr;
+ u_char scsi_dev:3;
+ u_char :5;
+};
+
+#define BT_INQUIRE_REV_THIRD 0x84 /* Get Adapter FirmWare version #3 */
+#define BT_INQUIRE_REV_FOURTH 0x85 /* Get Adapter FirmWare version #4 */
+
+/*
+ * Determine 32bit address/Data firmware functionality from the bus type
+ * Note: bt742a/747[s|d]/757/946/445s will return 'E'
+ * bt542b/545s/545d will return 'A'
+ * 94/05/18 amurai@spec.co.jp
+ */
+#define BT_BUS_TYPE_24bit 'A' /* PC/AT 24 bit address bus type */
+#define BT_BUS_TYPE_32bit 'E' /* EISA/VLB/PCI 32 bit address bus type */
+#define BT_BUS_TYPE_MCA 'M' /* Micro chanel is ? forget it right now */
+struct bt_ext_info {
+ u_char bus_type; /* Host adapter bus type */
+ u_char bios_addr; /* Bios Address-Not used */
+ u_short max_seg; /* Max segment List */
+ u_char num_mbx; /* Number of mailbox */
+ int32 mbx_base; /* mailbox base address */
+ struct {
+ u_char resv1:2; /* ??? */
+ u_char maxsync:1; /* ON: 10MB/s , OFF: 5MB/s */
+ u_char resv2:2; /* ??? */
+ u_char sync:1; /* ON: Sync, OFF: async ONLY!! */
+ u_char resv3:2; /* ??? */
+ } s;
+ u_char firmid[3]; /* Firmware ver. & rev. w/o last char */
+};
+
+#define BT_GET_BOARD_INFO 0x8b /* Get H/W ID and Revision */
+struct bt_board_info {
+ u_char id[4]; /* i.e bt742a -> '7','4','2','A' */
+ u_char ver[2]; /* i.e Board Revision 'H' -> 'H', 0x00 */
+};
+
+#define BT_GET_SYNC_VALUE 0x8c /* Get Synchronous Value */
+struct bt_sync_value {
+ u_char value[8]; /* Synchrnous value (value * 10 nsec) */
+};
+
+#define INT9 0x01
+#define INT10 0x02
+#define INT11 0x04
+#define INT12 0x08
+#define INT14 0x20
+#define INT15 0x40
+
+#define EISADMA 0x00
+#define CHAN0 0x01
+#define CHAN5 0x20
+#define CHAN6 0x40
+#define CHAN7 0x80
+
+#define KVTOPHYS(x) vtophys(x)
+#define PAGESIZ 4096
+#define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); }
+
+u_char bt_scratch_buf[256];
+
+struct bt_data {
+ short bt_base; /* base port for each board */
+ struct bt_mbx bt_mbx; /* all our mailboxes */
+ struct bt_ccb *bt_ccb_free; /* list of free CCBs */
+ struct bt_ccb *ccbhash[CCB_HASH_SIZE]; /* phys to kv hash */
+ int bt_int; /* int. read off board */
+ int bt_dma; /* DMA channel read of board */
+ int bt_scsi_dev; /* adapters scsi id */
+ int numccbs; /* how many we have malloc'd */
+ struct scsi_link sc_link; /* prototype for devs */
+} *btdata[NBT];
+
+/***********debug values *************/
+#define BT_SHOWCCBS 0x01
+#define BT_SHOWINTS 0x02
+#define BT_SHOWCMDS 0x04
+#define BT_SHOWMISC 0x08
+int bt_debug = 0;
+
+#ifdef KERNEL
+int btprobe();
+int btattach();
+int btintr();
+int32 bt_scsi_cmd();
+void bt_timeout(caddr_t, int);
+void bt_inquire_setup_information();
+void bt_done();
+void btminphys();
+u_int32 bt_adapter_info();
+struct bt_ccb *bt_get_ccb();
+struct bt_ccb *bt_ccb_phys_kv();
+
+static int btunit = 0;
+
+struct isa_driver btdriver =
+{
+ btprobe,
+ btattach,
+ "bt"
+};
+
+struct scsi_adapter bt_switch =
+{
+ bt_scsi_cmd,
+ btminphys,
+ 0,
+ 0,
+ bt_adapter_info,
+ "bt",
+ 0, 0
+};
+
+/* the below structure is so we have a default dev struct for out link struct */
+struct scsi_device bt_dev =
+{
+ NULL, /* Use default error handler */
+ NULL, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
+ "bt",
+ 0,
+ 0, 0
+};
+
+#endif /*KERNEL */
+
+#define BT_RESET_TIMEOUT 1000
+#ifndef KERNEL
+main()
+{
+ printf("bt_data is %d bytes\n", sizeof(struct bt_data));
+ printf("bt_ccb is %d bytes\n", sizeof(struct bt_ccb));
+ printf("bt_mbx is %d bytes\n", sizeof(struct bt_mbx));
+}
+
+#else /*KERNEL */
+
+/*
+ * bt_cmd(unit,icnt, ocnt,wait, retval, opcode, args)
+ *
+ * Activate Adapter command
+ * icnt: number of args (outbound bytes written after opcode)
+ * ocnt: number of expected returned bytes
+ * wait: number of seconds to wait for response
+ * retval: buffer where to place returned bytes
+ * opcode: opcode BT_NOP, BT_MBX_INIT, BT_START_SCSI ...
+ * args: parameters
+ *
+ * Performs an adapter command through the ports. Not to be confused with a
+ * scsi command, which is read in via the dma; one of the adapter commands
+ * tells it to read in a scsi command.
+ */
+int
+bt_cmd(unit, icnt, ocnt, wait, retval, opcode, args)
+ int unit;
+ int icnt;
+ int ocnt;
+ int wait;
+ u_char *retval;
+ unsigned opcode;
+ u_char args;
+{
+ struct bt_data *bt = btdata[unit];
+ unsigned *ic = &opcode;
+ u_char oc;
+ register i;
+ int sts;
+
+ /*
+ * multiply the wait argument by a big constant
+ * zero defaults to 1
+ */
+ if (wait)
+ wait *= 100000;
+ else
+ wait = 100000;
+ /*
+ * Wait for the adapter to go idle, unless it's one of
+ * the commands which don't need this
+ */
+ if (opcode != BT_MBX_INIT && opcode != BT_START_SCSI) {
+ i = 100000; /* 1 sec? */
+ while (--i) {
+ sts = inb(BT_CTRL_STAT_PORT);
+ if (sts & BT_IDLE) {
+ break;
+ }
+ DELAY(10);
+ }
+ if (i == 0) {
+ printf("bt%d: bt_cmd, host not idle(0x%x)\n", unit, sts);
+ return (ENXIO);
+ }
+ }
+ /*
+ * Now that it is idle, if we expect output, preflush the
+ * queue feeding to us.
+ */
+ if (ocnt) {
+ while ((inb(BT_CTRL_STAT_PORT)) & BT_DF)
+ inb(BT_CMD_DATA_PORT);
+ }
+ /*
+ * Output the command and the number of arguments given
+ * for each byte, first check the port is empty.
+ */
+ icnt++;
+ /* include the command */
+ while (icnt--) {
+ sts = inb(BT_CTRL_STAT_PORT);
+ for (i = wait; i; i--) {
+ sts = inb(BT_CTRL_STAT_PORT);
+ if (!(sts & BT_CDF))
+ break;
+ DELAY(10);
+ }
+ if (i == 0) {
+ printf("bt%d: bt_cmd, cmd/data port full\n", unit);
+ outb(BT_CTRL_STAT_PORT, BT_SRST);
+ return (ENXIO);
+ }
+ outb(BT_CMD_DATA_PORT, (u_char) (*ic++));
+ }
+ /*
+ * If we expect input, loop that many times, each time,
+ * looking for the data register to have valid data
+ */
+ while (ocnt--) {
+ sts = inb(BT_CTRL_STAT_PORT);
+ for (i = wait; i; i--) {
+ sts = inb(BT_CTRL_STAT_PORT);
+ if (sts & BT_DF)
+ break;
+ DELAY(10);
+ }
+ if (i == 0) {
+ printf("bt%d: bt_cmd, cmd/data port empty %d\n",
+ unit, ocnt);
+ return (ENXIO);
+ }
+ oc = inb(BT_CMD_DATA_PORT);
+ if (retval)
+ *retval++ = oc;
+ }
+ /*
+ * Wait for the board to report a finised instruction
+ */
+ i = 100000; /* 1 sec? */
+ while (--i) {
+ sts = inb(BT_INTR_PORT);
+ if (sts & BT_HACC) {
+ break;
+ }
+ DELAY(10);
+ }
+ if (i == 0) {
+ printf("bt%d: bt_cmd, host not finished(0x%x)\n", unit, sts);
+ return (ENXIO);
+ }
+ outb(BT_CTRL_STAT_PORT, BT_IRST);
+ return (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 isa_device structure from
+ * autoconf.c
+ */
+int
+btprobe(dev)
+ struct isa_device *dev;
+{
+ /*
+ * find unit and check we have that many defined
+ */
+ int unit = btunit;
+ struct bt_data *bt;
+
+ if (unit >= NBT) {
+ printf("bt%d: unit number too high\n", unit);
+ return 0;
+ }
+ /*
+ * Allocate a storage area for us
+ */
+ if (btdata[unit]) {
+ printf("bt%d: memory already allocated\n", unit);
+ return 0;
+ }
+ bt = malloc(sizeof(struct bt_data), M_TEMP, M_NOWAIT);
+ if (!bt) {
+ printf("bt%d: cannot malloc!\n", unit);
+ return 0;
+ }
+ bzero(bt, sizeof(struct bt_data));
+ btdata[unit] = bt;
+ bt->bt_base = dev->id_iobase;
+
+ /*
+ * Try initialise a unit at this location
+ * sets up dma and bus speed, loads bt->bt_int
+ */
+ if (bt_init(unit) != 0) {
+ btdata[unit] = NULL;
+ free(bt, M_TEMP);
+ return 0;
+ }
+ /*
+ * If it's there, put in it's interrupt vectors
+ */
+ dev->id_unit = unit;
+ dev->id_irq = (1 << bt->bt_int);
+ dev->id_drq = bt->bt_dma;
+
+ btunit++;
+ return 1;
+}
+
+/*
+ * Attach all the sub-devices we can find
+ */
+int
+btattach(dev)
+ struct isa_device *dev;
+{
+ int unit = dev->id_unit;
+ struct bt_data *bt = btdata[unit];
+
+ /*
+ * fill in the prototype scsi_link.
+ */
+ bt->sc_link.adapter_unit = unit;
+ bt->sc_link.adapter_targ = bt->bt_scsi_dev;
+ bt->sc_link.adapter = &bt_switch;
+ bt->sc_link.device = &bt_dev;
+
+ /*
+ * Forcely Bounce buffer mechanizum is ON for some broken busmaster
+ * chip with over 16Mbytes boundary for while...
+ * amurai@spec.co.jp 94/06/16
+ */
+ bt->sc_link.flags = SDEV_BOUNCE; /*XXX*/
+
+ /*
+ * ask the adapter what subunits are present
+ */
+ scsi_attachdevs(&(bt->sc_link));
+ return 1;
+}
+
+/*
+ * Return some information to the caller about the adapter and its
+ * capabilities.
+ */
+u_int32
+bt_adapter_info(unit)
+ int unit;
+{
+ return (2); /* 2 outstanding requests at a time per device */
+}
+
+/*
+ * Catch an interrupt from the adaptor
+ */
+int
+btintr(unit)
+ int unit;
+{
+ struct bt_data *bt = btdata[unit];
+ BT_MBI *wmbi;
+ struct bt_mbx *wmbx;
+ struct bt_ccb *ccb;
+ unsigned char stat;
+ int i, wait;
+ int found = 0;
+
+#ifdef UTEST
+ printf("btintr ");
+#endif
+ /*
+ * First acknowlege the interrupt, Then if it's
+ * not telling about a completed operation
+ * just return.
+ */
+ stat = inb(BT_INTR_PORT);
+
+ /* Mail Box out empty ? */
+ if (stat & BT_MBOA) {
+ printf("bt%d: Available Free mbo post\n", unit);
+ /* Disable MBO available interrupt */
+ outb(BT_CMD_DATA_PORT, BT_MBO_INTR_EN);
+ wait = 100000; /* 1 sec enough? */
+ for (i = wait; i; i--) {
+ if (!(inb(BT_CTRL_STAT_PORT) & BT_CDF))
+ break;
+ DELAY(10);
+ }
+ if (i == 0) {
+ printf("bt%d: bt_intr, cmd/data port full\n", unit);
+ outb(BT_CTRL_STAT_PORT, BT_SRST);
+ return 1;
+ }
+ outb(BT_CMD_DATA_PORT, 0x00); /* Disable */
+ wakeup((caddr_t)&bt->bt_mbx);
+ outb(BT_CTRL_STAT_PORT, BT_IRST);
+ return 1;
+ }
+ if (!(stat & BT_MBIF)) {
+ outb(BT_CTRL_STAT_PORT, BT_IRST);
+ return 1;
+ }
+ /*
+ * If it IS then process the competed operation
+ */
+ wmbx = &bt->bt_mbx;
+ wmbi = wmbx->tmbi;
+ AGAIN:
+ while (wmbi->stat != BT_MBI_FREE) {
+ ccb = bt_ccb_phys_kv(bt, (wmbi->ccb_addr));
+ if (!ccb) {
+ wmbi->stat = BT_MBI_FREE;
+ printf("bt: BAD CCB ADDR!\n");
+ continue;
+ }
+ found++;
+ if ((stat = wmbi->stat) != BT_MBI_OK) {
+ switch (stat) {
+ case BT_MBI_ABORT:
+#ifdef UTEST
+ if (bt_debug & BT_SHOWMISC)
+ printf("abort ");
+#endif
+ ccb->host_stat = BT_ABORTED;
+ break;
+
+ case BT_MBI_UNKNOWN:
+ ccb = (struct bt_ccb *) 0;
+#ifdef UTEST
+ if (bt_debug & BT_SHOWMISC)
+ printf("unknown ccb for abort");
+#endif
+ break;
+
+ case BT_MBI_ERROR:
+ break;
+
+ default:
+ panic("Impossible mbxi status");
+
+ }
+#ifdef UTEST
+ if ((bt_debug & BT_SHOWCMDS) && ccb) {
+ u_char *cp;
+ cp = ccb->scsi_cmd;
+ printf("op=%x %x %x %x %x %x\n",
+ cp[0], cp[1], cp[2],
+ cp[3], cp[4], cp[5]);
+ printf("stat %x for mbi addr = 0x%08x\n"
+ ,wmbi->stat, wmbi);
+ printf("addr = 0x%x\n", ccb);
+ }
+#endif
+ }
+ wmbi->stat = BT_MBI_FREE;
+ if (ccb) {
+ untimeout(bt_timeout, (caddr_t)ccb);
+ bt_done(unit, ccb);
+ }
+ /* Set the IN mail Box pointer for next */ bt_nextmbx(wmbi, wmbx, mbi);
+ }
+ if (!found) {
+ for (i = 0; i < BT_MBX_SIZE; i++) {
+ if (wmbi->stat != BT_MBI_FREE) {
+ found++;
+ break;
+ }
+ bt_nextmbx(wmbi, wmbx, mbi);
+ }
+ if (!found) {
+ printf("bt%d: mbi at 0x%08x should be found, stat=%02x..resync\n",
+ unit, wmbi, stat);
+ } else {
+ found = 0;
+ goto AGAIN;
+ }
+ }
+ wmbx->tmbi = wmbi;
+ outb(BT_CTRL_STAT_PORT, BT_IRST);
+ return 1;
+}
+
+/*
+ * A ccb is put onto the free list.
+ */
+void
+bt_free_ccb(unit, ccb, flags)
+ int unit;
+ struct bt_ccb *ccb;
+ int flags;
+{
+ struct bt_data *bt = btdata[unit];
+ unsigned int opri = 0;
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+ ccb->next = bt->bt_ccb_free;
+ bt->bt_ccb_free = ccb;
+ ccb->flags = CCB_FREE;
+ /*
+ * If there were none, wake anybody waiting for one to come free,
+ * starting with queued entries.
+ */
+ if (!ccb->next) {
+ wakeup((caddr_t)&bt->bt_ccb_free);
+ }
+
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+}
+
+/*
+ * Get a free ccb
+ *
+ * If there are none, see if we can allocate a new one. If so, put it in
+ * the hash table too otherwise either return an error or sleep.
+ */
+struct bt_ccb *
+bt_get_ccb(unit, flags)
+ int unit;
+ int flags;
+{
+ struct bt_data *bt = btdata[unit];
+ unsigned opri = 0;
+ struct bt_ccb *ccbp;
+ struct bt_mbx *wmbx; /* Mail Box pointer specified unit */
+ BT_MBO *wmbo; /* Out Mail Box pointer */
+ int hashnum;
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+ /*
+ * If we can and have to, sleep waiting for one to come free
+ * but only if we can't allocate a new one.
+ */
+ while (!(ccbp = bt->bt_ccb_free)) {
+ if (bt->numccbs < BT_CCB_MAX) {
+ if (ccbp = (struct bt_ccb *) malloc(sizeof(struct bt_ccb),
+ M_TEMP,
+ M_NOWAIT)) {
+ bzero(ccbp, sizeof(struct bt_ccb));
+ bt->numccbs++;
+ ccbp->flags = CCB_ACTIVE;
+ /*
+ * put in the phystokv hash table
+ * Never gets taken out.
+ */
+ ccbp->hashkey = KVTOPHYS(ccbp);
+ hashnum = CCB_HASH(ccbp->hashkey);
+ ccbp->nexthash = bt->ccbhash[hashnum];
+ bt->ccbhash[hashnum] = ccbp;
+ } else {
+ printf("bt%d: Can't malloc CCB\n", unit);
+ }
+ goto gottit;
+ } else {
+ if (!(flags & SCSI_NOSLEEP)) {
+ tsleep((caddr_t)&bt->bt_ccb_free, PRIBIO,
+ "btccb", 0);
+ }
+ }
+ }
+ if (ccbp) {
+ /* Get CCB from from free list */
+ bt->bt_ccb_free = ccbp->next;
+ ccbp->flags = CCB_ACTIVE;
+ }
+ gottit:
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+
+ return (ccbp);
+}
+
+/*
+ * given a physical address, find the ccb that
+ * it corresponds to:
+ */
+struct bt_ccb *
+bt_ccb_phys_kv(bt, ccb_phys)
+ struct bt_data *bt;
+ physaddr ccb_phys;
+{
+ int hashnum = CCB_HASH(ccb_phys);
+ struct bt_ccb *ccbp = bt->ccbhash[hashnum];
+
+ while (ccbp) {
+ if (ccbp->hashkey == ccb_phys)
+ break;
+ ccbp = ccbp->nexthash;
+ }
+ return ccbp;
+}
+
+/*
+ * Get a MBO and then Send it
+ */
+BT_MBO *
+bt_send_mbo(int unit, int flags, int cmd, struct bt_ccb *ccb)
+{
+ struct bt_data *bt = btdata[unit];
+ unsigned opri = 0;
+ BT_MBO *wmbo; /* Mail Box Out pointer */
+ struct bt_mbx *wmbx; /* Mail Box pointer specified unit */
+ int i, wait;
+
+ wmbx = &bt->bt_mbx;
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+ /* Get the Target OUT mail Box pointer and move to Next */
+ wmbo = wmbx->tmbo;
+ wmbx->tmbo = (wmbo == &(wmbx->mbo[BT_MBX_SIZE - 1]) ?
+ &(wmbx->mbo[0]) : wmbo + 1);
+
+ /*
+ * Check the outmail box is free or not.
+ * Note: Under the normal operation, it shuld NOT happen to wait.
+ */
+ while (wmbo->cmd != BT_MBO_FREE) {
+ wait = 100000; /* 1 sec enough? */
+ /* Enable MBO available interrupt */
+ outb(BT_CMD_DATA_PORT, BT_MBO_INTR_EN);
+ for (i = wait; i; i--) {
+ if (!(inb(BT_CTRL_STAT_PORT) & BT_CDF))
+ break;
+ DELAY(10);
+ }
+ if (i == 0) {
+ printf("bt%d: bt_send_mbo, cmd/data port full\n", unit);
+ outb(BT_CTRL_STAT_PORT, BT_SRST);
+ return ((BT_MBO *) 0);
+ }
+ outb(BT_CMD_DATA_PORT, 0x01); /* Enable */
+ tsleep((caddr_t)wmbx, PRIBIO, "btsend", 0);
+ /* XXX */ /*can't do this! */
+ /* May be servicing an int */
+ }
+ /* Link CCB to the Mail Box */
+ wmbo->ccb_addr = KVTOPHYS(ccb);
+ ccb->mbx = wmbo;
+ wmbo->cmd = cmd;
+
+ /* Send it! */
+ outb(BT_CMD_DATA_PORT, BT_START_SCSI);
+
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+
+ return (wmbo);
+}
+
+/*
+ * We have a ccb which has been processed by the
+ * adaptor, now we look to see how the operation
+ * went. Wake up the owner if waiting
+ */
+void
+bt_done(unit, ccb)
+ int unit;
+ struct bt_ccb *ccb;
+{
+ struct bt_data *bt = btdata[unit];
+ struct scsi_sense_data *s1, *s2;
+ struct scsi_xfer *xs = ccb->xfer;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("bt_done\n"));
+ /*
+ * Otherwise, put the results of the operation
+ * into the xfer and call whoever started it
+ */
+ if ((ccb->host_stat != BT_OK || ccb->target_stat != SCSI_OK)
+ && (!(xs->flags & SCSI_ERR_OK))) {
+
+ s1 = &(ccb->scsi_sense);
+ s2 = &(xs->sense);
+
+ if (ccb->host_stat) {
+ switch (ccb->host_stat) {
+ case BT_ABORTED: /* No response */
+ case BT_SEL_TIMEOUT: /* No response */
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("timeout reported back\n"));
+ xs->error = XS_TIMEOUT;
+ break;
+ default: /* Other scsi protocol messes */
+ xs->error = XS_DRIVER_STUFFUP;
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("unexpected host_stat: %x\n",
+ ccb->host_stat));
+ }
+ } else {
+ switch (ccb->target_stat) {
+ case 0x02:
+ *s2 = *s1;
+ xs->error = XS_SENSE;
+ break;
+ case 0x08:
+ xs->error = XS_BUSY;
+ break;
+ default:
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("unexpected target_stat: %x\n",
+ ccb->target_stat));
+ xs->error = XS_DRIVER_STUFFUP;
+ }
+ }
+ } else { /* All went correctly OR errors expected */
+ xs->resid = 0;
+ }
+ xs->flags |= ITSDONE;
+ bt_free_ccb(unit, ccb, xs->flags);
+ scsi_done(xs);
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+bt_init(unit)
+ int unit;
+{
+ struct bt_data *bt = btdata[unit];
+ unsigned char ad[4];
+ volatile int i, sts;
+ struct bt_config conf;
+ struct bt_ext_info info;
+ struct bt_board_info binfo;
+
+ /*
+ * reset board, If it doesn't respond, assume
+ * that it's not there.. good for the probe
+ */
+
+ outb(BT_CTRL_STAT_PORT, BT_HRST | BT_SRST);
+
+ for (i = BT_RESET_TIMEOUT; i; i--) {
+ sts = inb(BT_CTRL_STAT_PORT);
+ if (sts == (BT_IDLE | BT_INIT))
+ break;
+ DELAY(1000);
+ }
+ if (i == 0) {
+#ifdef UTEST
+ printf("bt_init: No answer from board\n");
+#endif
+ return (ENXIO);
+ }
+
+ /*
+ * Displaying Board ID and Hardware Revision
+ * 94/05/18 amurai@spec.co.jp
+ */
+ bt_cmd(unit, 1, sizeof(binfo),0,&binfo,BT_GET_BOARD_INFO,sizeof(binfo));
+ printf("bt%d: Bt%c%c%c%c/%c%d-", unit,
+ binfo.id[0],
+ binfo.id[1],
+ binfo.id[2],
+ binfo.id[3],
+ binfo.ver[0],
+ (unsigned) binfo.ver[1]
+ );
+
+ /*
+ * Make sure board has a capability of 32bit addressing.
+ * and Firmware also need a capability of 32bit addressing pointer
+ * in Extended mailbox and ccb structure.
+ * 94/05/18 amurai@spec.co.jp
+ */
+ bt_cmd(unit, 1, sizeof(info),0,&info, BT_INQUIRE_EXTENDED,sizeof(info));
+ switch (info.bus_type) {
+ case BT_BUS_TYPE_24bit: /* PC/AT 24 bit address bus */
+ printf("ISA(24bit) bus\n");
+ break;
+ case BT_BUS_TYPE_32bit: /* EISA/VLB/PCI 32 bit bus */
+ printf("PCI/EISA/VLB(32bit) bus\n");
+ break;
+ case BT_BUS_TYPE_MCA: /* forget it right now */
+ printf("MCA bus architecture...");
+ printf("giving up\n");
+ return (ENXIO);
+ break;
+ default:
+ printf("Unknown state...");
+ printf("giving up\n");
+ return (ENXIO);
+ break;
+ }
+ if ( binfo.id[0] == '5' ) {
+ printf("bt%d: This driver is designed for using 32 bit addressing\n",unit);
+ printf("bt%d: mode firmware and EISA/PCI/VLB bus architecture bus\n",unit);
+ printf("bt%d: WITHOUT any software trick/overhead (i.e.bounce buffer).\n",unit);
+ printf("bt%d: If you have more than 16MBytes memory\n",unit);
+ printf("bt%d: your filesystem will get a serious damage.\n",unit);
+ } else if ( info.bus_type == BT_BUS_TYPE_24bit ) {
+ printf("bt%d: Your board should report a 32bit bus architecture type..\n",unit);
+ printf("bt%d: A firmware on your board may have a problem with over\n",unit);
+ printf("bt%d: 16MBytes memory handling with this driver.\n",unit);
+ }
+
+ /*
+ * Assume we have a board at this stage
+ * setup dma channel from jumpers and save int
+ * level
+ */
+ printf("bt%d: reading board settings, ", unit);
+
+ bt_cmd(unit, 0, sizeof(conf), 0, &conf, BT_CONF_GET);
+ switch (conf.chan) {
+ case EISADMA:
+ bt->bt_dma = -1;
+ break;
+ case CHAN0:
+ outb(0x0b, 0x0c);
+ outb(0x0a, 0x00);
+ bt->bt_dma = 0;
+ break;
+ case CHAN5:
+ outb(0xd6, 0xc1);
+ outb(0xd4, 0x01);
+ bt->bt_dma = 5;
+ break;
+ case CHAN6:
+ outb(0xd6, 0xc2);
+ outb(0xd4, 0x02);
+ bt->bt_dma = 6;
+ break;
+ case CHAN7:
+ outb(0xd6, 0xc3);
+ outb(0xd4, 0x03);
+ bt->bt_dma = 7;
+ break;
+ default:
+ printf("illegal dma setting %x\n", conf.chan);
+ return (EIO);
+ }
+ if (bt->bt_dma == -1)
+ printf("busmastering, ");
+ else
+ printf("dma=%d, ", bt->bt_dma);
+
+ switch (conf.intr) {
+ case INT9:
+ bt->bt_int = 9;
+ break;
+ case INT10:
+ bt->bt_int = 10;
+ break;
+ case INT11:
+ bt->bt_int = 11;
+ break;
+ case INT12:
+ bt->bt_int = 12;
+ break;
+ case INT14:
+ bt->bt_int = 14;
+ break;
+ case INT15:
+ bt->bt_int = 15;
+ break;
+ default:
+ printf("illegal int setting\n");
+ return (EIO);
+ }
+ printf("int=%d\n", bt->bt_int);
+
+ /* who are we on the scsi bus */
+ bt->bt_scsi_dev = conf.scsi_dev;
+ /*
+ * Initialize mail box
+ */
+ *((physaddr *) ad) = KVTOPHYS(&bt->bt_mbx);
+ bt_cmd(unit, 5, 0, 0, 0, BT_MBX_INIT_EXTENDED
+ ,BT_MBX_SIZE
+ ,ad[0]
+ ,ad[1]
+ ,ad[2]
+ ,ad[3]);
+
+ /*
+ * Set Pointer chain null for just in case
+ * Link the ccb's into a free-list W/O mbox
+ * Initialize mail box status to free
+ */
+ if (bt->bt_ccb_free != (struct bt_ccb *) 0) {
+ printf("bt%d: bt_ccb_free is NOT initialized but init here\n",
+ unit);
+ bt->bt_ccb_free = (struct bt_ccb *) 0;
+ }
+ for (i = 0; i < BT_MBX_SIZE; i++) {
+ bt->bt_mbx.mbo[i].cmd = BT_MBO_FREE;
+ bt->bt_mbx.mbi[i].stat = BT_MBI_FREE;
+ }
+ /*
+ * Set up initial mail box for round-robin operation.
+ */
+ bt->bt_mbx.tmbo = &bt->bt_mbx.mbo[0];
+ bt->bt_mbx.tmbi = &bt->bt_mbx.mbi[0];
+ bt_inquire_setup_information(unit, &info);
+
+
+ /*
+ * Note that we are going and return (to probe)
+ */
+ return 0;
+}
+
+void
+bt_inquire_setup_information(
+ int unit,
+ struct bt_ext_info *info )
+{
+ struct bt_data *bt = btdata[unit];
+ struct bt_setup setup;
+ struct bt_sync_value sync;
+ char dummy[8];
+ char sub_ver[3];
+ struct bt_boardID bID;
+ int i;
+
+ /* Inquire Installed Devices */
+ bzero( &dummy[0], sizeof(dummy) );
+ bt_cmd(unit, 0, sizeof(dummy), 100, &dummy[0], BT_DEV_GET);
+
+ /*
+ * If board has a capbility of Syncrhonouse mode,
+ * Get a SCSI Synchronous value
+ */
+ if ( info->s.sync ) {
+ bt_cmd(unit, 1, sizeof(sync), 100,
+ &sync,BT_GET_SYNC_VALUE,sizeof(sync));
+ }
+
+ /*
+ * Inquire Board ID to board for firmware version
+ */
+ bt_cmd(unit, 0, sizeof(bID), 0, &bID, BT_INQUIRE);
+ bt_cmd(unit, 0, 1, 0, &sub_ver[0], BT_INQUIRE_REV_THIRD );
+ i = ((int)(bID.firm_revision-'0')) * 10 + (int)(bID.firm_version-'0');
+ if ( i >= 33 ) {
+ bt_cmd(unit, 0, 1, 0, &sub_ver[1], BT_INQUIRE_REV_FOURTH );
+ } else {
+ /*
+ * Below rev 3.3 firmware has a problem for issuing
+ * the BT_INQUIRE_REV_FOURTH command.
+ */
+ sub_ver[1]='\0';
+ }
+ sub_ver[2]='\0';
+ if (sub_ver[1]==' ')
+ sub_ver[1]='\0';
+ printf("bt%d: version %c.%c%s, ",
+ unit, bID.firm_revision, bID.firm_version, sub_ver );
+
+ /*
+ * Obtain setup information from board.
+ */
+ bt_cmd(unit, 1, sizeof(setup), 0, &setup, BT_SETUP_GET, sizeof(setup));
+
+ if (setup.sync_neg && info->s.sync ) {
+ if ( info->s.maxsync ) {
+ printf("fast sync, "); /* Max 10MB/s */
+ } else {
+ printf("sync, "); /* Max 5MB/s */
+ }
+ } else {
+ if ( info->s.sync ) {
+ printf("async, "); /* Never try by board */
+ } else {
+ printf("async only, "); /* Doesn't has a capability on board */
+ }
+ }
+ if (setup.parity) {
+ printf("parity, ");
+ } else {
+ printf("no parity, ");
+ }
+ printf("%d mbxs, %d ccbs\n", setup.num_mbx, BT_CCB_MAX);
+
+ /*
+ * Displayi SCSI negotiation value by each target.
+ * amurai@spec.co.jp
+ */
+ for (i = 0; i < 8; i++) {
+ if (!setup.sync[i].valid )
+ continue;
+ if ( (!setup.sync[i].offset && !setup.sync[i].period)
+ || !info->s.sync ) {
+ printf("bt%d: targ %d async\n", unit, i);
+ } else {
+ printf("bt%d: targ %d sync rate=%2d.%02dMB/s(%dns), offset=%02d\n",
+ unit, i,
+ 100 / sync.value[i],
+ (100 % sync.value[i]) * 100 / sync.value[i],
+ sync.value[i] * 10,
+ setup.sync[i].offset );
+ }
+ }
+
+ /*
+ * Enable round-robin scheme - appeared at firmware rev. 3.31
+ * Below rev 3.XX firmware has a problem for issuing
+ * BT_ROUND_ROBIN command amurai@spec.co.jp
+ */
+ if ( bID.firm_revision >= '3' ) {
+ printf("bt%d: Enabling Round robin scheme\n", unit);
+ bt_cmd(unit, 1, 0, 0, 0, BT_ROUND_ROBIN, BT_ENABLE);
+ } else {
+ printf("bt%d: Not Enabling Round robin scheme\n", unit);
+ }
+
+}
+
+#ifndef min
+#define min(x,y) (x < y ? x : y)
+#endif /* min */
+
+void
+btminphys(bp)
+ struct buf *bp;
+{
+ if (bp->b_bcount > ((BT_NSEG - 1) * PAGESIZ)) {
+ bp->b_bcount = ((BT_NSEG - 1) * PAGESIZ);
+ }
+}
+
+/*
+ * start a scsi operation given the command and the data address. Also needs
+ * the unit, target and lu.
+ */
+int32
+bt_scsi_cmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_sense_data *s1, *s2;
+ struct bt_ccb *ccb;
+ struct bt_scat_gath *sg;
+ int seg; /* scatter gather seg being worked on */
+ int i = 0;
+ int c = 0;
+ int thiskv;
+ physaddr thisphys, nextphys;
+ int unit = xs->sc_link->adapter_unit;
+ int bytes_this_seg, bytes_this_page, datalen, flags;
+ struct iovec *iovp;
+ struct bt_data *bt = btdata[unit];
+ BT_MBO *mbo;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("bt_scsi_cmd\n"));
+ /*
+ * get a ccb (mbox-out) to use. If the transfer
+ * is from a buf (possibly from interrupt time)
+ * then we can't allow it to sleep
+ */
+ flags = xs->flags;
+ if (xs->bp)
+ flags |= (SCSI_NOSLEEP); /* just to be sure */
+ if (flags & ITSDONE) {
+ printf("bt%d: Already done?\n", unit);
+ xs->flags &= ~ITSDONE;
+ }
+ if (!(flags & INUSE)) {
+ printf("bt%d: Not in use?\n", unit);
+ xs->flags |= INUSE;
+ }
+ if (!(ccb = bt_get_ccb(unit, flags))) {
+ xs->error = XS_DRIVER_STUFFUP;
+ return (TRY_AGAIN_LATER);
+ }
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("start ccb(%x)\n", ccb));
+ /*
+ * Put all the arguments for the xfer in the ccb
+ */
+ ccb->xfer = xs;
+ if (flags & SCSI_RESET) {
+ ccb->opcode = BT_RESET_CCB;
+ } else {
+ /* can't use S/G if zero length */
+ ccb->opcode = (xs->datalen ?
+ BT_INIT_SCAT_GATH_CCB
+ : BT_INITIATOR_CCB);
+ }
+ ccb->target = xs->sc_link->target;
+ ccb->data_out = 0;
+ ccb->data_in = 0;
+ ccb->lun = xs->sc_link->lun;
+ ccb->scsi_cmd_length = xs->cmdlen;
+ ccb->sense_ptr = KVTOPHYS(&(ccb->scsi_sense));
+ ccb->req_sense_length = sizeof(ccb->scsi_sense);
+
+ if ((xs->datalen) && (!(flags & SCSI_RESET))) { /* can use S/G only if not zero length */
+ ccb->data_addr = KVTOPHYS(ccb->scat_gath);
+ sg = ccb->scat_gath;
+ seg = 0;
+#ifdef TFS
+ if (flags & SCSI_DATA_UIO) {
+ iovp = ((struct uio *) xs->data)->uio_iov;
+ datalen = ((struct uio *) xs->data)->uio_iovcnt;
+ xs->datalen = 0;
+ while ((datalen) && (seg < BT_NSEG)) {
+ sg->seg_addr = (physaddr) iovp->iov_base;
+ xs->datalen += sg->seg_len = iovp->iov_len;
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("(0x%x@0x%x)"
+ ,iovp->iov_len, iovp->iov_base));
+ sg++;
+ iovp++;
+ seg++;
+ datalen--;
+ }
+ } else
+#endif /* TFS */
+ {
+ /*
+ * Set up the 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) && (seg < BT_NSEG)) {
+ bytes_this_seg = 0;
+
+ /* put in the base address */
+ sg->seg_addr = thisphys;
+
+ SC_DEBUGN(xs->sc_link, SDEV_DB4,
+ ("0x%x", thisphys));
+
+ /* do it at least once */
+ nextphys = thisphys;
+ while ((datalen) && (thisphys == nextphys))
+ /*
+ * This page is contiguous (physically) with
+ * the the last, just extend the length
+ */
+ {
+ /* how far to the end of the page */
+ nextphys = (thisphys & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ bytes_this_page = nextphys - thisphys;
+ /**** or the data ****/
+ bytes_this_page = min(bytes_this_page
+ ,datalen);
+ bytes_this_seg += bytes_this_page;
+ datalen -= bytes_this_page;
+
+ /* get more ready for the next page */
+ thiskv = (thiskv & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ if (datalen)
+ thisphys = KVTOPHYS(thiskv);
+ }
+ /*
+ * next page isn't contiguous, finish the seg
+ */
+ SC_DEBUGN(xs->sc_link, SDEV_DB4,
+ ("(0x%x)", bytes_this_seg));
+ sg->seg_len = bytes_this_seg;
+ sg++;
+ seg++;
+ }
+ }
+ /* end of iov/kv decision */
+ ccb->data_length = seg * sizeof(struct bt_scat_gath);
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n"));
+ if (datalen) {
+ /*
+ * there's still data, must have run out of segs!
+ */
+ printf("bt%d: bt_scsi_cmd, more than %d DMA segs\n",
+ unit, BT_NSEG);
+ xs->error = XS_DRIVER_STUFFUP;
+ bt_free_ccb(unit, ccb, flags);
+ return (HAD_ERROR);
+ }
+ } else { /* No data xfer, use non S/G values */
+ ccb->data_addr = (physaddr) 0;
+ ccb->data_length = 0;
+ }
+ ccb->link_id = 0;
+ ccb->link_addr = (physaddr) 0;
+ /*
+ * Put the scsi command in the ccb and start it
+ */
+ if (!(flags & SCSI_RESET)) {
+ bcopy(xs->cmd, ccb->scsi_cmd, ccb->scsi_cmd_length);
+ }
+ if (bt_send_mbo(unit, flags, BT_MBO_START, ccb) == (BT_MBO *) 0) {
+ xs->error = XS_DRIVER_STUFFUP;
+ bt_free_ccb(unit, ccb, flags);
+ return (TRY_AGAIN_LATER);
+ }
+ /*
+ * Usually return SUCCESSFULLY QUEUED
+ */
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_sent\n"));
+ if (!(flags & SCSI_NOMASK)) {
+ timeout(bt_timeout, (caddr_t)ccb, (xs->timeout * hz) / 1000);
+ return (SUCCESSFULLY_QUEUED);
+ }
+ /*
+ * If we can't use interrupts, poll on completion
+ */
+ return (bt_poll(unit, xs, ccb));
+}
+
+/*
+ * Poll a particular unit, looking for a particular xs
+ */
+int
+bt_poll(unit, xs, ccb)
+ int unit;
+ struct scsi_xfer *xs;
+ struct bt_ccb *ccb;
+{
+ struct bt_data *bt = btdata[unit];
+ int done = 0;
+ int count = xs->timeout;
+ u_char stat;
+
+ /* timeouts are in msec, so we loop in 1000 usec cycles */
+ while (count) {
+ /*
+ * If we had interrupts enabled, would we
+ * have got an interrupt?
+ */
+ stat = inb(BT_INTR_PORT);
+ if (stat & BT_ANY_INTR) {
+ btintr(unit);
+ }
+ if (xs->flags & ITSDONE) {
+ break;
+ }
+ DELAY(1000); /* only happens in boot so ok */
+ count--;
+ }
+ if (count == 0) {
+ /*
+ * We timed out, so call the timeout handler manually,
+ * accounting for the fact that the clock is not running yet
+ * by taking out the clock queue entry it makes.
+ */
+ bt_timeout((caddr_t)ccb, 0);
+
+ /*
+ * because we are polling, take out the timeout entry
+ * bt_timeout made
+ */
+ untimeout(bt_timeout, (caddr_t)ccb);
+ count = 2000;
+ while (count) {
+ /*
+ * Once again, wait for the int bit
+ */
+ stat = inb(BT_INTR_PORT);
+ if (stat & BT_ANY_INTR) {
+ btintr(unit);
+ }
+ if (xs->flags & ITSDONE) {
+ break;
+ }
+ DELAY(1000); /* only happens in boot so ok */
+ count--;
+ }
+ if (count == 0) {
+ /*
+ * We timed out again... This is bad. Notice that
+ * this time there is no clock queue entry to remove.
+ */
+ bt_timeout((caddr_t)ccb, 0);
+ }
+ }
+ if (xs->error)
+ return (HAD_ERROR);
+ return (COMPLETE);
+}
+
+void
+bt_timeout(caddr_t arg1, int arg2)
+{
+ struct bt_ccb * ccb = (struct bt_ccb *)arg1;
+ int unit;
+ struct bt_data *bt;
+ int s = splbio();
+
+ /*
+ * A timeout routine in kernel DONOT unlink
+ * Entry chains when time outed....So infinity Loop..
+ * 94/04/20 amurai@spec.co.jp
+ */
+ untimeout(bt_timeout, (caddr_t)ccb);
+
+ unit = ccb->xfer->sc_link->adapter_unit;
+ bt = btdata[unit];
+
+#ifdef UTEST
+ bt_print_active_ccbs(unit);
+#endif
+
+ /*
+ * If the ccb's mbx is not free, then the board has gone Far East?
+ */
+ if (bt_ccb_phys_kv(bt, ccb->mbx->ccb_addr) == ccb &&
+ ccb->mbx->cmd != BT_MBO_FREE) {
+ printf("bt%d: not taking commands!\n", unit);
+ Debugger("bt742a");
+ }
+ /*
+ * If it has been through before, then
+ * a previous abort has failed, don't
+ * try abort again
+ */
+ if (ccb->flags == CCB_ABORTED) {
+ /*
+ * abort timed out
+ */
+ printf("bt%d: Abort Operation has timed out\n", unit);
+ ccb->xfer->retries = 0; /* I MEAN IT ! */
+ ccb->host_stat = BT_ABORTED;
+ bt_done(unit, ccb);
+ } else {
+ /* abort the operation that has timed out */
+ printf("bt%d: Try to abort\n", unit);
+ bt_send_mbo(unit, ~SCSI_NOMASK,
+ BT_MBO_ABORT, ccb);
+ /* 2 secs for the abort */
+ ccb->flags = CCB_ABORTED;
+ timeout(bt_timeout, (caddr_t)ccb, 2 * hz);
+ }
+ splx(s);
+}
+
+#ifdef UTEST
+void
+bt_print_ccb(ccb)
+ struct bt_ccb *ccb;
+{
+ printf("ccb:%x op:%x cmdlen:%d senlen:%d\n"
+ ,ccb
+ ,ccb->opcode
+ ,ccb->scsi_cmd_length
+ ,ccb->req_sense_length);
+ printf(" datlen:%d hstat:%x tstat:%x flags:%x\n"
+ ,ccb->data_length
+ ,ccb->host_stat
+ ,ccb->target_stat
+ ,ccb->flags);
+}
+
+void
+bt_print_active_ccbs(int unit)
+{
+ struct bt_data *bt = btdata[unit];
+ struct bt_ccb *ccb;
+ int i = 0;
+
+ while (i < CCB_HASH_SIZE) {
+ ccb = bt->ccbhash[i];
+ while (ccb) {
+ if (ccb->flags != CCB_FREE)
+ bt_print_ccb(ccb);
+ ccb = ccb->nexthash;
+ }
+ i++;
+ }
+}
+#endif /*UTEST */
+#endif /*KERNEL */
diff --git a/sys/i386/isa/clock.c b/sys/i386/isa/clock.c
index cd87b28b90e6..5832801d24ae 100644
--- a/sys/i386/isa/clock.c
+++ b/sys/i386/isa/clock.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)clock.c 7.2 (Berkeley) 5/12/91
- * $Id: clock.c,v 1.6 1994/02/06 22:48:13 davidg Exp $
+ * $Id: clock.c,v 1.9 1994/05/02 09:41:24 sos Exp $
*/
/*
@@ -45,6 +45,7 @@
#include "time.h"
#include "kernel.h"
#include "machine/segments.h"
+#include "machine/frame.h"
#include "i386/isa/icu.h"
#include "i386/isa/isa.h"
#include "i386/isa/rtc.h"
@@ -55,22 +56,225 @@
#ifndef TIMER_FREQ
#define TIMER_FREQ 1193182 /* XXX - should be in isa.h */
#endif
+#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x))
+
+void hardclock();
+static int beeping;
+int timer0_divisor = TIMER_DIV(100); /* XXX should be hz */
+u_int timer0_prescale;
+static char timer0_state = 0, timer2_state = 0;
+static char timer0_reprogram = 0;
+static void (*timer_func)() = hardclock;
+static void (*new_function)();
+static u_int new_rate;
+static u_int hardclock_divisor;
+
+
+void
+timerintr(struct intrframe frame)
+{
+ timer_func(frame);
+ switch (timer0_state) {
+ case 0:
+ break;
+ case 1:
+ if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) {
+ hardclock(frame);
+ timer0_prescale = 0;
+ }
+ break;
+ case 2:
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+ outb(TIMER_CNTR0, TIMER_DIV(new_rate)%256);
+ outb(TIMER_CNTR0, TIMER_DIV(new_rate)/256);
+ enable_intr();
+ timer0_divisor = TIMER_DIV(new_rate);
+ timer0_prescale = 0;
+ timer_func = new_function;
+ timer0_state = 1;
+ break;
+ case 3:
+ if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) {
+ hardclock(frame);
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+ outb(TIMER_CNTR0, TIMER_DIV(hz)%256);
+ outb(TIMER_CNTR0, TIMER_DIV(hz)/256);
+ enable_intr();
+ timer0_divisor = TIMER_DIV(hz);
+ timer0_prescale = 0;
+ timer_func = hardclock;;
+ timer0_state = 0;
+ }
+ break;
+ }
+}
+
+
+int
+acquire_timer0(int rate, void (*function)() )
+{
+ if (timer0_state || !function)
+ return -1;
+
+ new_function = function;
+ new_rate = rate;
+ timer0_state = 2;
+ return 0;
+}
+
+
+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;
+}
+
+
+int
+release_timer2()
+{
+ if (!timer2_state)
+ return -1;
+ timer2_state = 0;
+ outb(TIMER_MODE, TIMER_SEL2|TIMER_SQWAVE|TIMER_16BIT);
+ return 0;
+}
+
+
+static int
+getit()
+{
+ 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 counter_limit, 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(0, 0);
+ n -= 20;
+ /*
+ * 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;
+
+ while (ticks_left > 0) {
+ tick = getit(0, 0);
+#ifdef DELAYDEBUG
+ ++getit_calls;
+#endif
+ if (tick > prev_tick)
+ ticks_left -= prev_tick - (tick - timer0_divisor);
+ 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()
+{
+ outb(IO_PPI, inb(IO_PPI)&0xFC); /* disable counter2 output to speaker */
+ release_timer2();
+ beeping = 0;
+}
+
+
+int
+sysbeep(int pitch, int period)
+{
+
+ 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, 0, period);
+ }
+ return 0;
+}
-static void findcpuspeed(void);
void
startrtclock()
{
int s;
- findcpuspeed(); /* use the clock (while it's free)
- to find the cpu speed */
/* initialize 8253 clock */
outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
/* Correct rounding will buy us a better precision in timekeeping */
- outb (IO_TIMER1, (TIMER_FREQ+hz/2)/hz);
- outb (IO_TIMER1, ((TIMER_FREQ+hz/2)/hz)/256);
+ outb (IO_TIMER1, TIMER_DIV(hz)%256);
+ outb (IO_TIMER1, TIMER_DIV(hz)/256);
+ timer0_divisor = hardclock_divisor = TIMER_DIV(hz);
/* initialize brain-dead battery powered clock */
outb (IO_RTC, RTC_STATUSA);
@@ -83,44 +287,18 @@ startrtclock()
printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS);
}
-unsigned int delaycount; /* calibrated loop variable (1 millisecond) */
-
-#define FIRST_GUESS 0x2000
-static void
-findcpuspeed()
-{
- unsigned char low;
- unsigned int remainder;
-
- /* Put counter in count down mode */
- outb(IO_TIMER1+3, 0x34);
- outb(IO_TIMER1, 0xff);
- outb(IO_TIMER1, 0xff);
- delaycount = FIRST_GUESS;
- spinwait(1);
- /* Read the value left in the counter */
- low = inb(IO_TIMER1); /* least siginifcant */
- remainder = inb(IO_TIMER1); /* most significant */
- remainder = (remainder<<8) + low ;
- /* Formula for delaycount is :
- * (loopcount * timer clock speed)/ (counter ticks * 1000)
- */
- delaycount = (FIRST_GUESS * (TIMER_FREQ/1000)) / (0xffff-remainder);
-}
-
/* convert 2 digit BCD number */
int
-bcd(i)
- int i;
+bcd(int i)
{
return ((i/16)*10 + (i%16));
}
+
/* convert years to seconds (from 1970) */
unsigned long
-ytos(y)
-int y;
+ytos(int y)
{
int i;
unsigned long ret;
@@ -133,16 +311,16 @@ int y;
return ret;
}
+
/* convert months to seconds */
unsigned long
-mtos(m,leap)
-int m,leap;
+mtos(int m, int leap)
{
int i;
unsigned long ret;
ret = 0;
- for(i=1;i<m;i++) {
+ for(i=1; i<m; i++) {
switch(i){
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
ret += 31*24*60*60; break;
@@ -162,11 +340,10 @@ int m,leap;
* from a filesystem.
*/
void
-inittodr(base)
- time_t base;
+inittodr(time_t base)
{
unsigned long sec;
- int leap,day_week,t,yd;
+ int leap, day_week, t, yd;
int sa,s;
/* do we have a realtime clock present? (otherwise we loop below) */
@@ -180,26 +357,25 @@ inittodr(base)
sec = bcd(rtcin(RTC_YEAR)) + 1900;
if (sec < 1970)
sec += 100;
- leap = !(sec % 4); sec = ytos(sec); /* year */
- yd = mtos(bcd(rtcin(RTC_MONTH)),leap); sec += yd; /* month */
- t = (bcd(rtcin(RTC_DAY))-1) * 24*60*60; sec += t; yd += t; /* date */
+
+ leap = !(sec % 4); sec = ytos(sec); /* year */
+ yd = mtos(bcd(rtcin(RTC_MONTH)),leap); sec+=yd; /* month */
+ t = (bcd(rtcin(RTC_DAY))-1) * 24*60*60; sec+=t; yd+=t; /* date */
day_week = rtcin(RTC_WDAY); /* day */
sec += bcd(rtcin(RTC_HRS)) * 60*60; /* hour */
sec += bcd(rtcin(RTC_MIN)) * 60; /* minutes */
sec += bcd(rtcin(RTC_SEC)); /* seconds */
-
sec += tz.tz_minuteswest * 60;
-
time.tv_sec = sec;
}
+
#ifdef garbage
/*
* Initialze the time of day register, based on the time base which is, e.g.
* from a filesystem.
*/
-test_inittodr(base)
- time_t base;
+test_inittodr(time_t base)
{
outb(IO_RTC,9); /* year */
@@ -219,6 +395,7 @@ test_inittodr(base)
}
#endif
+
/*
* Restart the clock.
*/
@@ -227,12 +404,14 @@ resettodr()
{
}
+
/*
* Wire clock interrupt in.
*/
#define V(s) __CONCAT(V, s)
extern void V(clk)();
+
void
enablertclock()
{
@@ -240,12 +419,12 @@ enablertclock()
INTREN(IRQ0);
}
+
/*
* Delay for some number of milliseconds.
*/
void
-spinwait(millisecs)
- int millisecs;
+spinwait(int millisecs)
{
DELAY(1000 * millisecs);
}
diff --git a/sys/i386/isa/com.c b/sys/i386/isa/com.c
index dcaf878685be..25b4dee3238c 100644
--- a/sys/i386/isa/com.c
+++ b/sys/i386/isa/com.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)com.c 7.5 (Berkeley) 5/16/91
- * $Id: com.c,v 1.7 1993/12/19 00:50:32 wollman Exp $
+ * $Id: com.c,v 1.10 1994/05/30 03:14:13 ache Exp $
*/
#include "com.h"
@@ -86,7 +86,7 @@ int comconsinit;
int comdefaultrate = TTYDEF_SPEED;
int commajor;
short com_addr[NCOM];
-struct tty com_tty[NCOM];
+struct tty *com_tty[NCOM];
struct speedtab comspeedtab[] = {
0, 0,
@@ -199,12 +199,11 @@ comopen(int /*dev_t*/ dev, int flag, int mode, struct proc *p)
unit = UNIT(dev);
if (unit >= NCOM || (com_active & (1 << unit)) == 0)
return (ENXIO);
- tp = &com_tty[unit];
+ tp = com_tty[unit] = ttymalloc(com_tty[unit]);
tp->t_oproc = comstart;
tp->t_param = comparam;
tp->t_dev = dev;
if ((tp->t_state & TS_ISOPEN) == 0) {
- tp->t_state |= TS_WOPEN;
ttychars(tp);
if (tp->t_ispeed == 0) {
tp->t_iflag = TTYDEF_IFLAG;
@@ -223,9 +222,8 @@ comopen(int /*dev_t*/ dev, int flag, int mode, struct proc *p)
tp->t_state |= TS_CARR_ON;
while ((flag&O_NONBLOCK) == 0 && (tp->t_cflag&CLOCAL) == 0 &&
(tp->t_state & TS_CARR_ON) == 0) {
- tp->t_state |= TS_WOPEN;
- if (error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH,
- ttopen, 0))
+ if (error = tsleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH,
+ "comdcd", 0))
break;
}
(void) spl0();
@@ -247,7 +245,7 @@ comclose(dev, flag, mode, p)
unit = UNIT(dev);
com = com_addr[unit];
- tp = &com_tty[unit];
+ tp = com_tty[unit];
(*linesw[tp->t_line].l_close)(tp, flag);
outb(com+com_cfcr, inb(com+com_cfcr) & ~CFCR_SBREAK);
#ifdef KGDB
@@ -255,10 +253,16 @@ comclose(dev, flag, mode, p)
if (kgdb_dev != makedev(commajor, unit))
#endif
outb(com+com_ier, 0);
- if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN ||
- (tp->t_state&TS_ISOPEN) == 0)
+ if (tp->t_cflag&HUPCL || (tp->t_state&TS_ISOPEN) == 0)
(void) commctl(dev, 0, DMSET);
ttyclose(tp);
+#ifdef broken /* session holds a ref to the tty; can't deallocate */
+ ttyfree(tp);
+ com_tty[unit] = (struct tty *)NULL;
+#endif
+ return (0);
+
+
return(0);
}
@@ -268,7 +272,7 @@ comread(dev, uio, flag)
struct uio *uio;
int flag;
{
- register struct tty *tp = &com_tty[UNIT(dev)];
+ register struct tty *tp = com_tty[UNIT(dev)];
return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
}
@@ -280,7 +284,7 @@ comwrite(dev, uio, flag)
int flag;
{
int unit = UNIT(dev);
- register struct tty *tp = &com_tty[unit];
+ register struct tty *tp = com_tty[unit];
/*
* (XXX) We disallow virtual consoles if the physical console is
@@ -309,7 +313,7 @@ comintr(unit)
return;
case IIR_RXTOUT:
case IIR_RXRDY:
- tp = &com_tty[unit];
+ tp = com_tty[unit];
/*
* Process received bytes. Inline for speed...
*/
@@ -340,7 +344,7 @@ comintr(unit)
}
break;
case IIR_TXRDY:
- tp = &com_tty[unit];
+ tp = com_tty[unit];
tp->t_state &=~ (TS_BUSY|TS_FLUSH);
if (tp->t_line)
(*linesw[tp->t_line].l_start)(tp);
@@ -371,7 +375,7 @@ comeint(unit, stat, com)
register struct tty *tp;
register int c;
- tp = &com_tty[unit];
+ tp = com_tty[unit];
c = inb(com+com_data);
if ((tp->t_state & TS_ISOPEN) == 0) {
#ifdef KGDB
@@ -401,7 +405,7 @@ commint(unit, com)
register struct tty *tp;
register int stat;
- tp = &com_tty[unit];
+ tp = com_tty[unit];
stat = inb(com+com_msr);
if ((stat & MSR_DDCD) && (comsoftCAR & (1 << unit)) == 0) {
if (stat & MSR_DCD)
@@ -432,7 +436,7 @@ comioctl(dev, cmd, data, flag)
register com;
register int error;
- tp = &com_tty[unit];
+ tp = com_tty[unit];
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
if (error >= 0)
return (error);
@@ -545,26 +549,17 @@ comstart(tp)
s = spltty();
if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
goto out;
- if (RB_LEN(&tp->t_out) <= tp->t_lowat) {
- if (tp->t_state&TS_ASLEEP) {
- tp->t_state &= ~TS_ASLEEP;
- wakeup((caddr_t)&tp->t_out);
- }
- if (tp->t_wsel) {
- selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
- tp->t_wsel = 0;
- tp->t_state &= ~TS_WCOLL;
- }
- }
- if (RB_LEN(&tp->t_out) == 0)
+ if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel)
+ ttwwakeup(tp);
+ if (RB_LEN(tp->t_out) == 0)
goto out;
if (inb(com+com_lsr) & LSR_TXRDY) {
- c = getc(&tp->t_out);
+ c = getc(tp->t_out);
tp->t_state |= TS_BUSY;
outb(com+com_data, c);
if (com_hasfifo & (1 << unit))
- for (c = 1; c < 16 && RB_LEN(&tp->t_out); ++c)
- outb(com+com_data, getc(&tp->t_out));
+ for (c = 1; c < 16 && RB_LEN(tp->t_out); ++c)
+ outb(com+com_data, getc(tp->t_out));
}
out:
splx(s);
@@ -647,7 +642,7 @@ comcnprobe(cp)
/* initialize required fields */
cp->cn_dev = makedev(commajor, unit);
- cp->cn_tp = &com_tty[unit];
+ cp->cn_tp = com_tty[unit];
#ifdef COMCONSOLE
cp->cn_pri = CN_REMOTE; /* Force a serial port console */
#else
@@ -747,43 +742,3 @@ comcnputc(dev, c)
splx(s);
}
#endif
-
-int
-comselect(dev, rw, p)
- dev_t dev;
- int rw;
- struct proc *p;
-{
- register struct tty *tp = &com_tty[UNIT(dev)];
- int nread;
- int s = spltty();
- struct proc *selp;
-
- switch (rw) {
-
- case FREAD:
- nread = ttnread(tp);
- if (nread > 0 ||
- ((tp->t_cflag&CLOCAL) == 0 && (tp->t_state&TS_CARR_ON) == 0))
- goto win;
- if (tp->t_rsel && (selp = pfind(tp->t_rsel)) && selp->p_wchan == (caddr_t)&selwait)
- tp->t_state |= TS_RCOLL;
- else
- tp->t_rsel = p->p_pid;
- break;
-
- case FWRITE:
- if (RB_LEN(&tp->t_out) <= tp->t_lowat)
- goto win;
- if (tp->t_wsel && (selp = pfind(tp->t_wsel)) && selp->p_wchan == (caddr_t)&selwait)
- tp->t_state |= TS_WCOLL;
- else
- tp->t_wsel = p->p_pid;
- break;
- }
- splx(s);
- return (0);
- win:
- splx(s);
- return (1);
-}
diff --git a/sys/i386/isa/debug.h b/sys/i386/isa/debug.h
deleted file mode 100644
index 92ecbd67f0fd..000000000000
--- a/sys/i386/isa/debug.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * from: debug.h, part of Bruce Evans interrupt code
- * $Id: debug.h,v 1.3 1993/11/13 02:25:20 davidg Exp $
- */
-
-#define SHOW_A_LOT_NOT
-
-#define BDBTRAP(name) \
- ss ; \
- cmpb $0,_bdb_exists ; \
- je 1f ; \
- testb $SEL_RPL_MASK,4(%esp) ; \
- jne 1f ; \
- ss ; \
-bdb_/**/name/**/_ljmp: ; \
- ljmp $0,$0 ; \
-1:
-
-#if 1
-#define COUNT_EVENT(group, event) incl (group) + (event) * 4
-#else
-#define COUNT_EVENT(group, event)
-#endif
-
-#ifdef SHOW_A_LOT
-
-#define GREEN 0x27 /* 0x27 for true green, 0x07 for mono */
-#define CLI_STI_X 63
-#define CPL_X 46
-#define IMEN_X 64
-#define IPENDING_X 29
-#define RED 0x47 /* 0x47 for true red, 0x70 for mono */
-
-#define SHOW_BIT(bit) ; \
- movl %ecx,%eax ; \
- shr $bit,%eax ; \
- andl $1,%eax ; \
- movb bit_colors(%eax),%al ; \
- movb %al,bit * 2 + 1(%ebx)
-
-#define SHOW_BITS(var, screen_offset) ; \
- pushl %ebx ; \
- pushl %ecx ; \
- movl _Crtat,%ebx ; \
- addl $screen_offset * 2,%ebx ; \
- movl _/**/var,%ecx ; \
- call show_bits ; \
- popl %ecx ; \
- popl %ebx
-
-#define SHOW_CLI \
- COUNT_EVENT(_intrcnt_show, 0) ; \
- pushl %eax ; \
- movl _Crtat,%eax ; \
- movb $RED,CLI_STI_X * 2 + 1(%eax) ; \
- popl %eax
-
-#define SHOW_CPL \
- COUNT_EVENT(_intrcnt_show, 1) ; \
- SHOW_BITS(cpl, CPL_X) ; \
-
-#define SHOW_IMEN \
- COUNT_EVENT(_intrcnt_show, 2) ; \
- SHOW_BITS(imen, IMEN_X)
-
-#define SHOW_IPENDING \
- COUNT_EVENT(_intrcnt_show, 3) ; \
- SHOW_BITS(ipending, IPENDING_X)
-
-#define SHOW_STI \
- COUNT_EVENT(_intrcnt_show, 4) ; \
- pushl %eax ; \
- movl _Crtat,%eax ; \
- movb $GREEN,CLI_STI_X * 2 + 1(%eax) ; \
- popl %eax
-
-#else /* not SHOW_A_LOT */
-
-#define SHOW_CLI COUNT_EVENT(_intrcnt_show, 0)
-#define SHOW_CPL COUNT_EVENT(_intrcnt_show, 1)
-#define SHOW_IMEN COUNT_EVENT(_intrcnt_show, 2)
-#define SHOW_IPENDING COUNT_EVENT(_intrcnt_show, 3)
-#define SHOW_STI COUNT_EVENT(_intrcnt_show, 4)
-
-#endif /* SHOW_A_LOT */
diff --git a/sys/i386/isa/elink.c b/sys/i386/isa/elink.c
new file mode 100644
index 000000000000..cd0057644a89
--- /dev/null
+++ b/sys/i386/isa/elink.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1994 Charles Hannum. 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 Charles Hannum.
+ * 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: elink.c,v 1.1 1994/05/25 20:06:40 ats Exp $
+ */
+
+/*
+ * Common code for dealing with 3COM ethernet cards.
+ */
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <machine/pio.h>
+#include <i386/isa/elink.h>
+
+/*
+ * Issue a `global reset' to all cards. We have to be careful to do this only
+ * once during autoconfig, to prevent resetting boards that have already been
+ * configured.
+ */
+void
+elink_reset()
+{
+ static int x = 0;
+
+ if (x == 0) {
+ x = 1;
+ outb(ELINK_ID_PORT, ELINK_RESET);
+ }
+}
+
+/*
+ * The `ID sequence' is really just snapshots of an 8-bit CRC register as 0
+ * bits are shifted in. Different board types use different polynomials.
+ */
+void
+elink_idseq(p)
+ register u_char p;
+{
+ register int i;
+ register u_char c;
+
+ c = 0xff;
+ for (i = 255; i; i--) {
+ outb(ELINK_ID_PORT, c);
+ if (c & 0x80) {
+ c <<= 1;
+ c ^= p;
+ } else
+ c <<= 1;
+ }
+}
diff --git a/sys/i386/isa/elink.h b/sys/i386/isa/elink.h
new file mode 100644
index 000000000000..93a5dac6f5ce
--- /dev/null
+++ b/sys/i386/isa/elink.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1994 Charles Hannum. 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 Charles Hannum.
+ * 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: elink.h,v 1.1 1994/05/25 20:06:43 ats Exp $
+ */
+
+#define ELINK_ID_PORT 0x100
+#define ELINK_RESET 0xc0
+
+#define ELINK_507_POLY 0xe7
+#define ELINK_509_POLY 0xcf
+
+void elink_reset __P((void));
+void elink_idseq __P((u_char p));
diff --git a/sys/i386/isa/fd.c b/sys/i386/isa/fd.c
index 8f3e1dc9cc94..f4632b9f2cb1 100644
--- a/sys/i386/isa/fd.c
+++ b/sys/i386/isa/fd.c
@@ -6,6 +6,12 @@
* This code is derived from software contributed to Berkeley by
* Don Ahn.
*
+ * Portions Copyright (c) 1993, 1994 by
+ * jc@irbs.UUCP (John Capo)
+ * vak@zebub.msk.su (Serge Vakulenko)
+ * ache@astral.msk.su (Andrew A. Chernov)
+ * joerg_wunsch@uriah.sax.de (Joerg Wunsch)
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -35,7 +41,7 @@
* SUCH DAMAGE.
*
* from: @(#)fd.c 7.4 (Berkeley) 5/25/91
- * $Id: fd.c,v 1.21.2.1 1994/03/07 02:08:43 rgrimes Exp $
+ * $Id: fd.c,v 1.26 1994/05/22 12:30:32 joerg Exp $
*
*/
@@ -59,28 +65,29 @@
#include <sys/buf.h>
#include <sys/uio.h>
#include <sys/malloc.h>
+#include <sys/proc.h>
#include <sys/syslog.h>
#include "i386/isa/isa.h"
#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"
-#if NFT > 0
-extern int ftopen(), ftintr(), ftattach(), ftclose(), ftioctl();
-#endif
-
#define b_cylin b_resid
-#define FDBLK 512
/* 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
+
#define NUMTYPES 14
#define NUMDENS (NUMTYPES - 6)
-/* This defines (-1) must match index for fd_types */
+/* 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 */
#define FD_1720 1
@@ -132,16 +139,17 @@ struct fdc_data fdc_data[NFDC];
struct fd_data {
struct fdc_data *fdc; /* pointer to controller structure */
int fdsu; /* this units number on this controller */
- int type; /* Drive type (HD, DD */
+ 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;
- int track; /* where we think the head is */
+ int skip;
+ int hddrv;
+ int track; /* where we think the head is */
+ int options; /* user configurable options, see ioctl_fd.h */
} fd_data[NFD];
/***********************************************************************\
@@ -153,10 +161,48 @@ struct fd_data {
* fdsu is the floppy 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 */
+#if NFT > 0
+int ftopen(dev_t, int);
+int ftintr(/* ftu_t */ int ftu);
+int ftclose(/* dev_t */ int, int);
+void ftstrategy(struct buf *);
+int ftioctl(/* dev_t */ int, int, caddr_t, int, struct proc *);
+int ftdump(/* dev_t */ int);
+int ftsize(/* dev_t */ int);
+int ftattach(struct isa_device *, struct isa_device *);
+#endif
+/* autoconfig functions */
+static int fdprobe(struct isa_device *);
+static int fdattach(struct isa_device *);
+
+/* exported functions */
+int fdsize (/* dev_t */ int);
+void fdintr(fdcu_t);
+int Fdopen(/* dev_t */int, int);
+int fdclose(/* dev_t */int, int);
+void fdstrategy(struct buf *);
+int fdioctl(/* dev_t */ int, int, caddr_t, int, struct proc *);
+
+/* 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 void fd_turnoff(caddr_t arg1, int arg2);
+static void fd_motor_on(caddr_t arg1, int arg2);
+static void fd_turnon(fdu_t);
+static void fdc_reset(fdc_p);
+static void fdstart(fdcu_t);
+static void fd_timeout(caddr_t, int);
+static void fd_pseudointr(caddr_t, int);
+static int fdstate(fdcu_t, fdc_p);
static int retrier(fdcu_t);
+static int fdformat(/* dev_t */ int, struct fd_formb *, struct proc *);
+
#define DEVIDLE 0
#define FINDWORK 1
@@ -188,25 +234,18 @@ char *fdstates[] =
"IOTIMEDOUT"
};
-
-int fd_debug = 1;
+/* 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)
+#define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2)
#else /* DEBUG */
#define TRACE0(arg)
-#define TRACE1(arg1,arg2)
+#define TRACE1(arg1, arg2)
#endif /* DEBUG */
-static void fdstart(fdcu_t);
-void fdintr(fdcu_t);
-static void fd_turnoff(caddr_t, int);
-
/****************************************************************************/
/* autoconfiguration stuff */
/****************************************************************************/
-static int fdprobe(struct isa_device *);
-static int fdattach(struct isa_device *);
-
struct isa_driver fdcdriver = {
fdprobe, fdattach, "fdc",
};
@@ -214,56 +253,55 @@ struct isa_driver fdcdriver = {
/*
* probe for existance of controller
*/
-int
+static int
fdprobe(dev)
struct isa_device *dev;
{
fdcu_t fdcu = dev->id_unit;
if(fdc_data[fdcu].flags & FDC_ATTACHED)
{
- printf("fdc: same unit (%d) used multiple times\n",fdcu);
+ printf("fdc: same unit (%d) used multiple times\n", fdcu);
return 0;
}
fdc_data[fdcu].baseport = dev->id_iobase;
/* First - lets reset the floppy controller */
-
- outb(dev->id_iobase+fdout,0);
+ outb(dev->id_iobase+FDOUT, 0);
DELAY(100);
- outb(dev->id_iobase+fdout,FDO_FRST);
+ outb(dev->id_iobase+FDOUT, FDO_FRST);
/* see if it can handle a command */
- if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0)
+ if (out_fdc(fdcu, NE7CMD_SPECIFY) < 0)
{
return(0);
}
- out_fdc(fdcu,0xDF);
- out_fdc(fdcu,2);
+ out_fdc(fdcu, NE7_SPEC_1(3, 240));
+ out_fdc(fdcu, NE7_SPEC_2(2, 0));
return (IO_FDCSIZE);
}
/*
* wire controller into system, look for floppy units
*/
-int
+static int
fdattach(dev)
struct isa_device *dev;
{
- unsigned fdt,st0, cyl;
- int hdr;
+ unsigned fdt;
fdu_t fdu;
fdcu_t fdcu = dev->id_unit;
fdc_p fdc = fdc_data + fdcu;
fd_p fd;
- int fdsu;
+ int fdsu, st0;
struct isa_device *fdup;
fdc->fdcu = fdcu;
fdc->flags |= FDC_ATTACHED;
fdc->dmachan = dev->id_drq;
fdc->state = DEVIDLE;
- hdr = 0;
+ /* reset controller, turn motor off, clear fdout mirror reg */
+ outb(fdc->baseport + FDOUT, ((fdc->fdout = 0)));
printf("fdc%d:", fdcu);
/* check for each floppy drive */
@@ -303,25 +341,33 @@ fdattach(dev)
continue;
}
-#ifdef notyet
/* select it */
- fd_turnon1(fdu);
- spinwait(1000); /* 1 sec */
- out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */
- out_fdc(fdcu,fdsu);
+ set_motor(fdcu, fdsu, TURNON);
spinwait(1000); /* 1 sec */
+ out_fdc(fdcu, NE7CMD_SEEK); /* seek some steps... */
+ out_fdc(fdcu, fdsu);
+ out_fdc(fdcu, 10);
+ spinwait(300); /* ...wait a moment... */
+ out_fdc(fdcu, NE7CMD_SENSEI); /* make controller happy */
+ (void)in_fdc(fdcu);
+ (void)in_fdc(fdcu);
+ out_fdc(fdcu, NE7CMD_RECAL); /* ...and go back to 0 */
+ out_fdc(fdcu, fdsu);
+ spinwait(1000); /* a second be enough for full stroke seek */
/* anything responding */
- out_fdc(fdcu,NE7CMD_SENSEI);
+ out_fdc(fdcu, NE7CMD_SENSEI);
st0 = in_fdc(fdcu);
- cyl = in_fdc(fdcu);
- if (st0 & 0xd0)
+ (void)in_fdc(fdcu);
+ set_motor(fdcu, fdsu, TURNOFF);
+
+ if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
continue;
-#endif
fd->track = -2;
fd->fdc = fdc;
fd->fdsu = fdsu;
+ fd->options = 0;
printf(" [%d: fd%d: ", fdsu, fdu);
switch (fdt) {
@@ -346,15 +392,10 @@ fdattach(dev)
fd->type = NO_TYPE;
break;
}
-
- fd_turnoff((caddr_t)fdu, 0);
- hdr = 1;
}
printf("\n");
- /* Set transfer to 500kbps */
- outb(fdc->baseport+fdctl,0); /*XXX*/
- return 1;
+ return (1);
}
int
@@ -365,102 +406,47 @@ fdsize(dev)
}
/****************************************************************************/
-/* fdstrategy */
-/****************************************************************************/
-void fdstrategy(struct buf *bp)
-{
- register struct buf *dp,*dp0,*dp1;
- long nblocks,blknum;
- int s;
- fdcu_t fdcu;
- fdu_t fdu;
- fdc_p fdc;
- fd_p fd;
-
- fdu = FDUNIT(minor(bp->b_dev));
- fd = &fd_data[fdu];
- fdc = fd->fdc;
- fdcu = fdc->fdcu;
-
-#if NFT > 0
- /* check for controller already busy with tape */
- if (fdc->flags & FDC_TAPE_BUSY) {
- bp->b_error = EBUSY;
- bp->b_flags |= B_ERROR;
- return;
- }
-#endif
- if ((fdu >= NFD) || (bp->b_blkno < 0)) {
- printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n",
- fdu, bp->b_blkno, bp->b_bcount);
- pg("fd:error in fdstrategy");
- 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;
- 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);
- 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);
- return;
-}
-
-/****************************************************************************/
/* motor control stuff */
/* remember to not deselect the drive we're working on */
/****************************************************************************/
-void
-set_motor(fdcu, fdu, reset)
+static void
+set_motor(fdcu, fdsu, turnon)
fdcu_t fdcu;
- fdu_t fdu;
- int reset;
+ int fdsu;
+ int turnon;
{
- int m0,m1;
- int selunit;
- fd_p fd;
- if(fd = fdc_data[fdcu].fd)/* yes an assign! */
- {
- selunit = fd->fdsu;
+ int fdout = fdc_data[fdcu].fdout;
+ int needspecify = 0;
+
+ 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);
}
- else
- {
- selunit = 0;
+
+ outb(fdc_data[fdcu].baseport+FDOUT, fdout);
+ fdc_data[fdcu].fdout = fdout;
+ TRACE1("[0x%x->FDOUT]", fdout);
+
+ if(needspecify) {
+ out_fdc(fdcu, NE7CMD_SPECIFY);
+ out_fdc(fdcu, NE7_SPEC_1(3, 240));
+ out_fdc(fdcu, NE7_SPEC_2(2, 0));
}
- m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR;
- m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR;
- outb(fdc_data[fdcu].baseport+fdout,
- selunit
- | (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
- | (m0 ? FDO_MOEN0 : 0)
- | (m1 ? FDO_MOEN1 : 0));
- TRACE1("[0x%x->fdout]",(
- selunit
- | (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
- | (m0 ? FDO_MOEN0 : 0)
- | (m1 ? FDO_MOEN1 : 0)));
}
+/* ARGSUSED */
static void
fd_turnoff(caddr_t arg1, int arg2)
{
@@ -470,11 +456,12 @@ fd_turnoff(caddr_t arg1, int arg2)
fd_p fd = fd_data + fdu;
s = splbio();
fd->flags &= ~FD_MOTOR;
- set_motor(fd->fdc->fdcu,fd->fdsu,0);
+ set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF);
splx(s);
}
-void
+/* ARGSUSED */
+static void
fd_motor_on(caddr_t arg1, int arg2)
{
fdu_t fdu = (fdu_t)arg1;
@@ -490,27 +477,39 @@ fd_motor_on(caddr_t arg1, int arg2)
splx(s);
}
-static void fd_turnon1(fdu_t);
-
-void
+static void
fd_turnon(fdu)
fdu_t fdu;
{
fd_p fd = fd_data + fdu;
if(!(fd->flags & FD_MOTOR))
{
- fd_turnon1(fdu);
- fd->flags |= FD_MOTOR_WAIT;
+ 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
-fd_turnon1(fdu_t fdu)
+fdc_reset(fdc)
+ fdc_p fdc;
{
- fd_p fd = fd_data + fdu;
- fd->flags |= FD_MOTOR;
- set_motor(fd->fdc->fdcu,fd->fdsu,0);
+ fdcu_t fdcu = fdc->fdcu;
+
+ /* Try a reset, keep motor on */
+ 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);
+
+ out_fdc(fdcu, NE7CMD_SPECIFY);
+ out_fdc(fdcu, NE7_SPEC_1(3, 240));
+ out_fdc(fdcu, NE7_SPEC_2(2, 0));
}
/****************************************************************************/
@@ -522,17 +521,17 @@ in_fdc(fdcu)
{
int baseport = fdc_data[fdcu].baseport;
int i, j = 100000;
- while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM))
+ while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
!= (NE7_DIO|NE7_RQM) && j-- > 0)
if (i == NE7_RQM) return -1;
if (j <= 0)
return(-1);
#ifdef DEBUG
- i = inb(baseport+fddata);
- TRACE1("[fddata->0x%x]",(unsigned char)i);
+ i = inb(baseport+FDDATA);
+ TRACE1("[FDDATA->0x%x]", (unsigned char)i);
return(i);
#else
- return inb(baseport+fddata);
+ return inb(baseport+FDDATA);
#endif
}
@@ -546,17 +545,17 @@ out_fdc(fdcu, x)
/* Check that the direction bit is set */
i = 100000;
- while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0);
+ while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0);
if (i <= 0) return (-1); /* Floppy timed out */
/* Check that the floppy controller is ready for a command */
i = 100000;
- while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0);
+ while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0);
if (i <= 0) return (-1); /* Floppy timed out */
/* Send the command and return */
- outb(baseport+fddata,x);
- TRACE1("[0x%x->fddata]",x);
+ outb(baseport+FDDATA, x);
+ TRACE1("[0x%x->FDDATA]", x);
return (0);
}
@@ -647,17 +646,97 @@ fdclose(dev, flags)
int flags;
{
fdu_t fdu = FDUNIT(minor(dev));
- int type = FDTYPE(minor(dev));
#if NFT > 0
+ int type = FDTYPE(minor(dev));
+
if (type & F_TAPE_TYPE)
- return ftclose(0);
+ return ftclose(dev, flags);
#endif
fd_data[fdu].flags &= ~FD_OPEN;
+ fd_data[fdu].options &= ~FDOPT_NORETRY;
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;
+ fdblk = 128 << (fd->ft->secsize);
+
+#if NFT > 0
+ if (FDTYPE(minor(bp->b_dev)) & F_TAPE_TYPE) {
+ /* ft tapes do not (yet) support strategy i/o */
+ bp->b_error = ENXIO;
+ 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
+ if (!(bp->b_flags & B_FORMAT)) {
+ if ((fdu >= NFD) || (bp->b_blkno < 0)) {
+ printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n",
+ fdu, 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;
+ 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);
+ 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);
+ return;
+}
+
/***************************************************************\
* fdstart *
* We have just queued something.. if the controller is not busy *
@@ -671,9 +750,7 @@ static void
fdstart(fdcu)
fdcu_t fdcu;
{
- register struct buf *dp,*bp;
int s;
- fdu_t fdu;
s = splbio();
if(fdc_data[fdcu].state == DEVIDLE)
@@ -683,38 +760,43 @@ fdstart(fdcu)
splx(s);
}
+/* ARGSUSED */
static void
fd_timeout(caddr_t arg1, int arg2)
{
fdcu_t fdcu = (fdcu_t)arg1;
fdu_t fdu = fdc_data[fdcu].fdu;
- int st0, st3, cyl;
- struct buf *dp,*bp;
- int s;
+ int baseport = fdc_data[fdcu].baseport;
+ struct buf *dp, *bp;
+ int s;
dp = &fdc_data[fdcu].head;
- s = splbio();
bp = dp->b_actf;
- out_fdc(fdcu,NE7CMD_SENSED);
- out_fdc(fdcu,fd_data[fdu].hddrv);
- st3 = in_fdc(fdcu);
-
- out_fdc(fdcu,NE7CMD_SENSEI);
- st0 = in_fdc(fdcu);
- cyl = in_fdc(fdcu);
- printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n",
- fdu,
- st0,
- NE7_ST0BITS,
- cyl,
- st3,
- NE7_ST3BITS);
+ /*
+ * 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] = 0xc0;
+ 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;
@@ -730,11 +812,13 @@ fd_timeout(caddr_t arg1, int arg2)
}
/* just ensure it has the right spl */
+/* ARGSUSED */
static void
fd_pseudointr(caddr_t arg1, int arg2)
{
fdcu_t fdcu = (fdcu_t)arg1;
int s;
+
s = splbio();
fdintr(fdcu);
splx(s);
@@ -756,25 +840,26 @@ fdintr(fdcu_t fdcu)
(ftintr(fdu));
else
#endif
- while(fdstate(fdcu, fdc))
- ;
+ while(fdstate(fdcu, fdc))
+ ;
}
/***********************************************************************\
* The controller state machine. *
* if it returns a non zero value, it should be called again immediatly *
\***********************************************************************/
-int
+static int
fdstate(fdcu, fdc)
fdcu_t fdcu;
fdc_p fdc;
{
- int read, format, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0;
+ int read, format, head, sec = 0, i = 0, sectrac, st0, cyl, st3;
unsigned long blknum;
fdu_t fdu = fdc->fdu;
fd_p fd;
- register struct buf *dp,*bp;
+ register struct buf *dp, *bp;
struct fd_formb *finfo = NULL;
+ size_t fdblk;
dp = &(fdc->head);
bp = dp->b_actf;
@@ -787,16 +872,17 @@ fdstate(fdcu, fdc)
fdc->state = DEVIDLE;
if(fdc->fd)
{
- printf("unexpected valid fd pointer (fdu = %d)\n"
- ,fdc->fdu);
+ printf("unexpected valid fd pointer (fdu = %d)\n",
+ fdc->fdu);
fdc->fd = (fd_p) 0;
fdc->fdu = -1;
}
- TRACE1("[fdc%d IDLE]",fdcu);
+ 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("confused fd pointers\n");
@@ -805,9 +891,9 @@ fdstate(fdcu, fdc)
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);
+ 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)
@@ -818,6 +904,8 @@ fdstate(fdcu, fdc)
fd->skip = 0;
fdc->fd = fd;
fdc->fdu = fdu;
+ outb(fdc->baseport+FDCTL, fd->ft->trans);
+ 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 *
@@ -838,7 +926,7 @@ fdstate(fdcu, fdc)
}
else /* at least make sure we are selected */
{
- set_motor(fdcu,fd->fdsu,0);
+ set_motor(fdcu, fd->fdsu, TURNON);
}
fdc->state = DOSEEK;
break;
@@ -848,33 +936,55 @@ fdstate(fdcu, fdc)
fdc->state = SEEKCOMPLETE;
break;
}
- out_fdc(fdcu,NE7CMD_SEEK); /* Seek function */
- out_fdc(fdcu,fd->fdsu); /* Drive number */
- out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac);
+ out_fdc(fdcu, NE7CMD_SEEK); /* Seek function */
+ out_fdc(fdcu, fd->fdsu); /* Drive number */
+ out_fdc(fdcu, bp->b_cylin * fd->ft->steptrac);
fd->track = -2;
fdc->state = SEEKWAIT;
- timeout(fd_timeout, (caddr_t)fdcu, 2 * hz);
return(0); /* will return later */
case SEEKWAIT:
- untimeout(fd_timeout, (caddr_t)fdcu);
/* allow heads to settle */
- timeout(fd_pseudointr, (caddr_t)fdcu, hz / 50);
+ timeout(fd_pseudointr, (caddr_t)fdcu, hz / 32);
fdc->state = SEEKCOMPLETE;
return(0); /* will return later */
- break;
-
case SEEKCOMPLETE : /* SEEK DONE, START DMA */
/* Make sure seek really happened*/
if(fd->track == -2)
{
int descyl = bp->b_cylin * fd->ft->steptrac;
- out_fdc(fdcu,NE7CMD_SENSEI);
- i = in_fdc(fdcu);
- cyl = in_fdc(fdcu);
+ do {
+ out_fdc(fdcu, NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ /*
+ * if this was a "ready changed" interrupt,
+ * fetch status again (can happen after
+ * enabling controller from reset state)
+ */
+ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
+ if (0 == descyl)
+ {
+ /*
+ * seek to cyl 0 requested; make sure we are
+ * really there
+ */
+ out_fdc(fdcu, NE7CMD_SENSED);
+ out_fdc(fdcu, fdu);
+ st3 = in_fdc(fdcu);
+ if ((st3 & NE7_ST3_T0) == 0) {
+ printf(
+ "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
+ fdu, st3, NE7_ST3BITS);
+ 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, i, NE7_ST0BITS);
+ printf(
+ "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
+ fdu, descyl, cyl, st0, NE7_ST0BITS);
return(retrier(fdcu));
}
}
@@ -884,46 +994,73 @@ fdstate(fdcu, fdc)
fd->skip = (char *)&(finfo->fd_formb_cylno(0))
- (char *)finfo;
isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip,
- format ? bp->b_bcount : FDBLK, fdc->dmachan);
- blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
- + fd->skip/FDBLK;
+ 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;
-/*XXX*/ fd->hddrv = ((head&1)<<2)+fdu;
+ fd->hddrv = ((head&1)<<2)+fdu;
+
+ if(format || !read)
+ {
+ /* make sure the drive is writable */
+ out_fdc(fdcu, NE7CMD_SENSED);
+ out_fdc(fdcu, fdu);
+ st3 = in_fdc(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 */
- out_fdc(fdcu,/* NE7CMD_FORMAT */ 0x4d);
- out_fdc(fdcu,head << 2 | fdu);
- out_fdc(fdcu,finfo->fd_formb_secshift);
- out_fdc(fdcu,finfo->fd_formb_nsecs);
- out_fdc(fdcu,finfo->fd_formb_gaplen);
- out_fdc(fdcu,finfo->fd_formb_fillbyte);
+ out_fdc(fdcu, NE7CMD_FORMAT);
+ out_fdc(fdcu, head << 2 | fdu);
+ out_fdc(fdcu, finfo->fd_formb_secshift);
+ out_fdc(fdcu, finfo->fd_formb_nsecs);
+ out_fdc(fdcu, finfo->fd_formb_gaplen);
+ out_fdc(fdcu, finfo->fd_formb_fillbyte);
}
else
{
if (read)
{
- out_fdc(fdcu,NE7CMD_READ); /* READ */
+ out_fdc(fdcu, NE7CMD_READ); /* READ */
}
else
{
- out_fdc(fdcu,NE7CMD_WRITE); /* WRITE */
+ out_fdc(fdcu, NE7CMD_WRITE); /* WRITE */
}
- out_fdc(fdcu,head << 2 | fdu); /* head & unit */
- out_fdc(fdcu,fd->track); /* track */
- out_fdc(fdcu,head);
- out_fdc(fdcu,sec); /* sector XXX +1? */
- out_fdc(fdcu,fd->ft->secsize); /* sector size */
- out_fdc(fdcu,sectrac); /* sectors/track */
- out_fdc(fdcu,fd->ft->gap); /* gap size */
- out_fdc(fdcu,fd->ft->datalen); /* data length */
+ out_fdc(fdcu, head << 2 | fdu); /* head & unit */
+ out_fdc(fdcu, fd->track); /* track */
+ out_fdc(fdcu, head);
+ out_fdc(fdcu, sec); /* sector XXX +1? */
+ out_fdc(fdcu, fd->ft->secsize); /* sector size */
+ out_fdc(fdcu, sectrac); /* sectors/track */
+ out_fdc(fdcu, fd->ft->gap); /* gap size */
+ out_fdc(fdcu, fd->ft->datalen); /* data length */
}
fdc->state = IOCOMPLETE;
- timeout(fd_timeout, (caddr_t)fdcu, 2 * hz);
+ timeout(fd_timeout, (caddr_t)fdcu, hz);
return(0); /* will return later */
case IOCOMPLETE: /* IO DONE, post-analyze */
untimeout(fd_timeout, (caddr_t)fdcu);
@@ -931,30 +1068,43 @@ fdstate(fdcu, fdc)
{
fdc->status[i] = in_fdc(fdcu);
}
- case IOTIMEDOUT: /*XXX*/
+ fdc->state = IOTIMEDOUT;
+ /* FALLTHROUGH */
+ case IOTIMEDOUT:
isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip,
- format ? bp->b_bcount : FDBLK, fdc->dmachan);
- if (fdc->status[0]&0xF8)
+ format ? bp->b_bcount : fdblk, fdc->dmachan);
+ if (fdc->status[0] & NE7_ST0_IC)
{
- if (fdc->status[1] & 0x10) {
+ if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
+ && fdc->status[1] & NE7_ST1_OR) {
/*
- * Operation not completed in reasonable time.
- * Just restart it, don't increment retry count.
- * (vak)
+ * 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;
+ 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;
- bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads));
+ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk
+ + fd->skip/fdblk;
+ bp->b_cylin =
+ (blknum / (fd->ft->sectrac * fd->ft->heads));
fdc->state = DOSEEK;
}
else
@@ -970,68 +1120,76 @@ fdstate(fdcu, fdc)
}
return(1);
case RESETCTLR:
- /* Try a reset, keep motor on */
- set_motor(fdcu,fd->fdsu,1);
- DELAY(100);
- set_motor(fdcu,fd->fdsu,0);
- outb(fdc->baseport+fdctl,fd->ft->trans);
- TRACE1("[0x%x->fdctl]",fd->ft->trans);
+ fdc_reset(fdc);
fdc->retry++;
fdc->state = STARTRECAL;
break;
case STARTRECAL:
- out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */
- out_fdc(fdcu,0xDF);
- out_fdc(fdcu,2);
- out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */
- out_fdc(fdcu,fdu);
+ out_fdc(fdcu, NE7CMD_RECAL); /* Recalibrate Function */
+ out_fdc(fdcu, fdu);
fdc->state = RECALWAIT;
return(0); /* will return later */
case RECALWAIT:
/* allow heads to settle */
- timeout(fd_pseudointr, (caddr_t)fdcu, hz / 30);
+ timeout(fd_pseudointr, (caddr_t)fdcu, hz / 32);
fdc->state = RECALCOMPLETE;
return(0); /* will return later */
case RECALCOMPLETE:
- out_fdc(fdcu,NE7CMD_SENSEI);
- st0 = in_fdc(fdcu);
- cyl = in_fdc(fdcu);
- if (cyl != 0)
+ do {
+ out_fdc(fdcu, NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ /*
+ * if this was a "ready changed" interrupt,
+ * fetch status again (can happen after
+ * enabling controller from reset state)
+ */
+ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
+ if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0)
{
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:
+ case MOTORWAIT:
if(fd->flags & FD_MOTOR_WAIT)
{
return(0); /* time's not up yet */
}
- fdc->state = DOSEEK;
+ /*
+ * 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("Unexpected FD int->");
- out_fdc(fdcu,NE7CMD_SENSEI);
+ out_fdc(fdcu, NE7CMD_SENSEI);
st0 = in_fdc(fdcu);
cyl = in_fdc(fdcu);
- printf("ST0 = %lx, PCN = %lx\n",i,sec);
- out_fdc(fdcu,0x4A);
- out_fdc(fdcu,fd->fdsu);
+ printf("ST0 = %x, PCN = %x\n", st0, cyl);
+ out_fdc(fdcu, NE7CMD_READID);
+ out_fdc(fdcu, fd->fdsu);
for(i=0;i<7;i++) {
fdc->status[i] = in_fdc(fdcu);
}
- printf("intr 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] );
+ if(fdc->status[0] != -1)
+ printf("intr status :%lx %lx %lx %lx %lx %lx %lx\n",
+ fdc->status[0],
+ fdc->status[1],
+ fdc->status[2],
+ fdc->status[3],
+ fdc->status[4],
+ fdc->status[5],
+ fdc->status[6] );
+ else
+ printf("FDC timed out\n");
return(0);
}
return(1); /* Come back immediatly to new state */
@@ -1042,11 +1200,13 @@ retrier(fdcu)
fdcu_t fdcu;
{
fdc_p fdc = fdc_data + fdcu;
- register struct buf *dp,*bp;
+ 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:
@@ -1061,12 +1221,15 @@ retrier(fdcu)
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)|3);
+ bp->b_dev = makedev(major(bp->b_dev),
+ (FDUNIT(minor(bp->b_dev))<<3)|3);
diskerr(bp, "fd", "hard error", LOG_PRINTF,
- fdc->fd->skip, (struct disklabel *)NULL);
+ fdc->fd->skip / DEV_BSIZE,
+ (struct disklabel *)NULL);
bp->b_dev = sav_b_dev;
printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS);
printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS);
@@ -1101,9 +1264,11 @@ fdformat(dev, finfo, p)
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);
@@ -1119,7 +1284,7 @@ fdformat(dev, finfo, p)
* seek to the requested cylinder
*/
bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
- + finfo->head * fd->ft->sectrac) * FDBLK / DEV_BSIZE;
+ + 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;
@@ -1138,41 +1303,39 @@ fdformat(dev, finfo, p)
splx(s);
if(rv == EWOULDBLOCK)
- {
/* timed out */
- biodone(bp);
rv = EIO;
- }
+ if(bp->b_flags & B_ERROR)
+ rv = bp->b_error;
+ biodone(bp);
free(bp, M_TEMP);
return rv;
}
/*
- * fdioctl() from jc@irbs.UUCP (John Capo)
- * i386/i386/conf.c needs to have fdioctl() declared and remove the line that
- * defines fdioctl to be enxio.
*
- * TODO: Reformat.
- * Think about allocating buffer off stack.
+ * TODO: Think about allocating buffer off stack.
* Don't pass uncast 0's and NULL's to read/write/setdisklabel().
* Watch out for NetBSD's different *disklabel() interface.
*
- * Added functionality for floppy formatting
- * joerg_wunsch@uriah.sax.de (Joerg Wunsch)
*/
int
-fdioctl (dev, cmd, addr, flag, p)
+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;
+ int error = 0;
#if NFT > 0
int type = FDTYPE(minor(dev));
@@ -1182,14 +1345,14 @@ fdioctl (dev, cmd, addr, flag, p)
return ftioctl(dev, cmd, addr, flag, p);
#endif
- error = 0;
+ fdblk = 128 << fd->ft->secsize;
switch (cmd)
{
case DIOCGDINFO:
bzero(buffer, sizeof (buffer));
dl = (struct disklabel *)buffer;
- dl->d_secsize = FDBLK;
+ dl->d_secsize = fdblk;
fdt = fd_data[FDUNIT(minor(dev))].ft;
dl->d_secpercyl = fdt->size / fdt->tracks;
dl->d_type = DTYPE_FLOPPY;
@@ -1221,12 +1384,12 @@ fdioctl (dev, cmd, addr, flag, p)
dl = (struct disklabel *)addr;
- if (error = setdisklabel ((struct disklabel *)buffer,
- dl, 0, NULL))
+ if ((error =
+ setdisklabel ((struct disklabel *)buffer, dl, 0, NULL)))
break;
error = writedisklabel(dev, fdstrategy,
- (struct disklabel *)buffer, NULL);
+ (struct disklabel *)buffer, NULL);
break;
case FD_FORM:
@@ -1243,11 +1406,42 @@ fdioctl (dev, cmd, addr, flag, p)
*(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 = EINVAL;
+ error = ENOTTY;
break;
}
return (error);
}
#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/i386/isa/fdc.h b/sys/i386/isa/fdc.h
index 7d75d5a81b48..ef81b11a8387 100644
--- a/sys/i386/isa/fdc.h
+++ b/sys/i386/isa/fdc.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)fd.c 7.4 (Berkeley) 5/25/91
- * $Id: fdc.h,v 1.2 1994/02/14 22:24:25 nate Exp $
+ * $Id: fdc.h,v 1.3 1994/05/22 12:35:38 joerg Exp $
*
*/
@@ -49,12 +49,12 @@ struct fdc_data
#define FDC_HASFTAPE 0x02
#define FDC_TAPE_BUSY 0x04
struct fd_data *fd;
- int fdu; /* the active drive */
+ int fdu; /* the active drive */
+ int state;
+ int retry;
+ int fdout; /* mirror of the w/o digital output reg */
+ int status[7]; /* copy of the registers */
struct buf head; /* Head of buf chain */
- struct buf rhead; /* Raw head of buf chain */
- int state;
- int retry;
- int status[7]; /* copy of the registers */
};
/***********************************************************************\
diff --git a/sys/i386/isa/fdreg.h b/sys/i386/isa/fdreg.h
index b0e9593a93c5..9b5be2b87b1c 100644
--- a/sys/i386/isa/fdreg.h
+++ b/sys/i386/isa/fdreg.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)fdreg.h 7.1 (Berkeley) 5/9/91
- * $Id: fdreg.h,v 1.4 1994/02/07 22:12:42 alm Exp $
+ * $Id: fdreg.h,v 1.5 1994/05/22 12:35:40 joerg Exp $
*/
/*
@@ -42,24 +42,26 @@
#include "../i386/isa/ic/nec765.h"
/* registers */
-#define fdout 2 /* Digital Output Register (W) */
+#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 0x30 /* motor enable drive 2 */
-#define FDO_MOEN3 0x40 /* motor enable drive 3 */
+#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 FDSTS 4 /* NEC 765 Main Status Register (R) */
+#define FDDATA 5 /* NEC 765 Data Register (R/W) */
-#define fdctl 7 /* Control Register (W) */
+#define FDCTL 7 /* Control Register (W) */
#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 */
-#define fdin 7 /* Digital Input Register (R) */
+#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 */
diff --git a/sys/i386/isa/ft.c b/sys/i386/isa/ft.c
index 09bc127090aa..1204613bcd8e 100644
--- a/sys/i386/isa/ft.c
+++ b/sys/i386/isa/ft.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1993 Steve Gerakines
+ * 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.
@@ -17,8 +17,15 @@
* POSSIBILITY OF SUCH DAMAGE.
*
* ft.c - QIC-40/80 floppy tape driver
- * $Id: ft.c,v 1.4 1994/02/14 22:24:28 nate Exp $
+ * $Id: ft.c,v 1.7 1994/06/22 05:52:36 jkh Exp $
*
+ * 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
@@ -76,12 +83,13 @@
#include "ftreg.h"
/* Enable or disable debugging messages. */
-#define FTDBGALL 0 /* everything */
-/* #define DPRT(a) printf a */
-#define DPRT(a)
+#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 */
@@ -92,11 +100,13 @@ extern int hz; /* system clock rate */
/* 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_MOUNTAIN (F_TAPE_TYPE | 1)
-#define FT_COLORADO (F_TAPE_TYPE | 2)
+/* 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 };
@@ -150,15 +160,25 @@ QIC_Geom *ftg = NULL; /* Current tape's geometry */
/*
* things relating to asynchronous commands
*/
-static int astk_depth; /* async_cmd stack depth */
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_arg[5]; /* up to 5 arguments for async cmds */
+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 {
@@ -172,29 +192,36 @@ enum {
};
/* Call another asyncronous command from within async_cmd(). */
-#define CALL_ACMD(r,f,a,b,c,d,e) \
- astk[astk_depth].over_retries = async_retries; \
- astk[astk_depth].over_func = async_func; \
- astk[astk_depth].over_state = (r); \
- for (i = 0; i < 5; i++) \
- astk[astk_depth].over_arg[i] = async_arg[i]; \
+#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_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \
- async_arg[3]=(d); async_arg[4]=(e); \
- astk_depth++; \
+ 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,d,e) over_async = (r); astk_depth = 0; \
+#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_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \
- async_arg[3]=(d); async_arg[4]=(e); \
+ 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))
+
static struct {
int buff_avail;
+ int buff_done;
int iosts_change;
int long_delay;
int intr_wait;
@@ -223,8 +250,17 @@ struct ft_data {
unsigned char *xptr; /* pointer to buffer blk to xfer */
int xcnt; /* transfer count */
int xblk; /* block number to transfer */
- SegReq *curseg; /* Current segment to do I/O on */
- SegReq *bufseg; /* Buffered segment to r/w ahead */
+ 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 */
@@ -261,85 +297,237 @@ void ftstrategy(struct buf *);
int ftioctl(dev_t, int, caddr_t, int, struct proc *);
int ftdump(dev_t);
int ftsize(dev_t);
-static void ft_timeout(caddr_t arg1, int arg2);
-void async_cmd(ftu_t);
-void async_req(ftu_t, int);
-void async_read(ftu_t, int);
-void async_write(ftu_t, int);
-void tape_start(ftu_t);
-void tape_end(ftu_t);
-void tape_inactive(ftu_t);
+static void ft_timeout(caddr_t, int);
+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));
+}
+
/*
* Probe/attach floppy tapes.
*/
-int ftattach(isadev, fdup)
+int
+ftattach(isadev, fdup)
struct isa_device *isadev, *fdup;
{
- 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;
-
- 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;
-
- tape_start(ftu); /* ready controller for tape */
- tape_cmd(ftu, QC_COL_ENABLE1);
- tape_cmd(ftu, QC_COL_ENABLE2);
- if (tape_status(ftu) >= 0) {
- ft->type = FT_COLORADO;
- fdc->flags |= FDC_HASFTAPE;
- printf(" [%d: ft%d: Colorado tape]",
- fdup->id_physid, fdup->id_unit );
- tape_cmd(ftu, QC_COL_DISABLE);
- goto out;
- }
+ 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;
- tape_start(ftu); /* ready controller for tape */
- tape_cmd(ftu, QC_MTN_ENABLE1);
- tape_cmd(ftu, QC_MTN_ENABLE2);
- if (tape_status(ftu) >= 0) {
- ft->type = FT_MOUNTAIN;
- fdc->flags |= FDC_HASFTAPE;
- printf(" [%d: ft%d: Mountain tape]",
- fdup->id_physid, fdup->id_unit );
- tape_cmd(ftu, QC_MTN_DISABLE);
- goto out;
- }
+ /*
+ * 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;
+ }
+
+ /*
+ * FT_INSIGHT - insight style
+ */
+ tape_start(ftu, 1);
+ if (tape_status(ftu) >= 0) {
+ ft->type = FT_INSIGHT;
+ ftreq_hwinfo(ftu, &hw);
+ goto out;
+ }
out:
- tape_end(ftu);
- ft->attaching = 0;
- return(ft->type);
+ tape_end(ftu);
+ if (ft->type != NO_TYPE) {
+ fdc->flags |= FDC_HASFTAPE;
+ switch(hw.hw_make) {
+ case 0x0000:
+ if (ft->type == FT_COLORADO)
+ manu = "Colorado";
+ else if (ft->type == FT_INSIGHT)
+ manu = "Insight";
+ else if (ft->type == FT_MOUNTAIN && hw.hw_model == 0x05)
+ manu = "Archive";
+ else if (ft->type == FT_MOUNTAIN)
+ manu = "Mountain";
+ else
+ manu = "Unknown";
+ break;
+ case 0x0001:
+ manu = "Colorado";
+ break;
+ case 0x0005:
+ if (hw.hw_model >= 0x09)
+ manu = "Conner";
+ else
+ manu = "Archive";
+ break;
+ case 0x0006:
+ manu = "Mountain";
+ break;
+ case 0x0007:
+ manu = "Wangtek";
+ break;
+ case 0x0222:
+ manu = "IOMega";
+ break;
+ default:
+ manu = "Unknown";
+ break;
+ }
+ printf(" [%d: ft%d: %s tape]", fdup->id_physid, fdup->id_unit, manu);
+ }
+ ft->attaching = 0;
+ return(ft->type);
}
/*
* Perform common commands asynchronously.
*/
-void async_cmd(ftu_t ftu) {
+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 struct {
- int over_func;
- int over_state;
- int over_retries;
- int over_arg[5];
- } astk[15];
static int wanttrk, wantblk, wantdir;
static int curpos, curtrk, curblk, curdir, curdiff;
static int errcnt = 0;
@@ -356,7 +544,7 @@ restate:
*/
switch (async_state) {
case 0:
- cmd = async_arg[0];
+ cmd = async_arg0;
#if FTDBGALL
DPRT(("===>async_seek cmd = %d\n", cmd));
#endif
@@ -364,7 +552,7 @@ restate:
async_state = 1;
i = 0;
if (out_fdc(fdcu, NE7CMD_SEEK) < 0) i = 1;
- if (!i && out_fdc(fdcu, 0x00) < 0) i = 1;
+ if (!i && out_fdc(fdcu, ftu) < 0) i = 1;
if (!i && out_fdc(fdcu, newcn) < 0) i = 1;
if (i) {
if (++async_retries >= 10) {
@@ -399,7 +587,7 @@ restate:
DPRT(("ft%d: async_seek error st0 = $%02x pcn = %d\n",
ftu, st0, pcn));
#endif
- if (async_arg[1]) goto complete;
+ if (async_arg1) goto complete;
async_state = 2;
timeout(ft_timeout, (caddr_t)ftu, hz/50);
break;
@@ -420,14 +608,14 @@ restate:
case 0:
bitn = 0;
retval = 0;
- cmd = async_arg[0];
- nbits = async_arg[1];
+ 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, 0, 0);
+ CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0);
/* NOTREACHED */
case 1:
out_fdc(fdcu, NE7CMD_SENSED);
- out_fdc(fdcu, 0x00);
+ out_fdc(fdcu, ftu);
st3 = in_fdc(fdcu);
if (st3 < 0) {
DPRT(("ft%d: async_status timed out on bit %d r=$%02x\n",
@@ -440,7 +628,7 @@ restate:
if (bitn >= (nbits+2)) {
if ((retval & 1) && (retval & (1 << (nbits+1)))) {
async_ret = (retval & ~(1<<(nbits+1))) >> 1;
- if (async_arg[0] == QC_STATUS && async_arg[2] == 0 &&
+ if (async_arg0 == QC_STATUS && async_arg2 == 0 &&
(async_ret & (QS_ERROR|QS_NEWCART))) {
async_state = 2;
goto restate;
@@ -453,31 +641,31 @@ restate:
}
goto complete;
}
- CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
+ 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, 0, 0);
+ 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_arg[0];
- nbits = async_arg[1];
- CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1, 0, 0);
+ 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, 0, 0);
+ CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0);
case 6:
- CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
+ CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0);
case 7:
- CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
+ CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0);
case 8:
- cmd = async_arg[0];
- CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0);
+ cmd = async_arg0;
+ CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0);
}
break;
@@ -488,9 +676,9 @@ restate:
*/
switch(async_state) {
case 0:
- CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0);
+ CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0);
case 1:
- if ((async_ret & async_arg[0]) != 0) goto complete;
+ 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));
@@ -510,13 +698,13 @@ restate:
*/
switch(async_state) {
case 0:
- cmd = async_arg[0];
- async_retries = (async_arg[2]) ? (async_arg[2]*4) : 10;
- CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 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, 0, 0);
+ CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0);
case 2:
- if ((async_ret & async_arg[1]) != 0) goto complete;
+ if ((async_ret & async_arg1) != 0) goto complete;
if (--async_retries == 0) {
DPRT(("ft%d: acmd_seeksts retries exceeded\n", ftu));
goto complete;
@@ -534,12 +722,12 @@ restate:
switch(async_state) {
case 0:
if (!ft->moving) {
- CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
+ CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
/* NOTREACHED */
}
async_state = 1;
out_fdc(fdcu, 0x4a); /* READ_ID */
- out_fdc(fdcu, 0);
+ out_fdc(fdcu, ftu);
break;
case 1:
for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu);
@@ -548,25 +736,36 @@ restate:
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) == 0x40) {
- if (++errcnt >= 10) {
+ 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 = ft->lastpos;
+ async_ret = -2;
errcnt = 0;
goto complete;
}
- if (errcnt > 2) {
+ 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_STOP, QS_READY, 0, 0, 0);
+ CALL_ACMD(4, ACMD_SEEKSTS, QC_STPAUSE, QS_READY, 0);
}
- DPRT(("readid retry...\n"));
+ 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, 0, 0);
+ CALL_ACMD(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0);
/* NOTREACHED */
}
ft->lastpos = async_ret;
@@ -574,13 +773,13 @@ restate:
goto complete;
/* NOTREACHED */
case 2:
- CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0, 0, 0);
+ 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, 0, 0);
+ CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0);
case 5:
ft->moving = 1;
async_state = 0;
@@ -598,27 +797,27 @@ restate:
*/
switch (async_state) {
case 0:
- wanttrk = async_arg[0] / ftg->g_blktrk;
- wantblk = async_arg[0] % ftg->g_blktrk;
+ 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, 0, 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, 0, 0);
+ CALL_ACMD(2, ACMD_SEEK, QC_SEEKTRACK, 0, 0);
case 2:
cmd = wanttrk+2;
- CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0);
+ CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0);
case 3:
- CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0);
+ 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, 0, 0);
+ CALL_ACMD(6, ACMD_SEEKSTS, cmd, QS_READY, 90);
}
if (ft->laststs & QS_BOT) {
DPRT(("Tape is at BOT\n"));
@@ -632,15 +831,23 @@ restate:
async_state = 6;
goto restate;
}
- CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0);
+ 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, 0, 0);
+ CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
}
async_state = 6;
goto restate;
@@ -650,22 +857,22 @@ restate:
ft->lastpos = curblk - 1;
async_ret = ft->lastpos;
if (ft->moving) goto complete;
- CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0, 0, 0);
+ 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, 0, 0);
+ CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
}
- if ((wantblk - curblk) <= 96) { /* approaching it */
- CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 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, 0, 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, 0, 0);
+ CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0);
break;
case 8:
async_state = 9;
@@ -677,26 +884,26 @@ restate:
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, 0, 0);
+ 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, 0, 0);
+ 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, 0, 0);
+ CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90);
case 13:
- CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0);
+ 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, 0, 0);
+ 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, 0, 0);
+ 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, 0, 0);
+ CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90);
}
break;
}
@@ -704,13 +911,14 @@ restate:
return;
complete:
- if (astk_depth) {
- astk_depth--;
- async_retries = astk[astk_depth].over_retries;
- async_func = astk[astk_depth].over_func;
- async_state = astk[astk_depth].over_state;
- for(i = 0; i < 5; i++)
- async_arg[i] = astk[astk_depth].over_arg[i];
+ 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;
@@ -720,6 +928,7 @@ complete:
async_req(ftu, 2);
break;
case FTIO_READING:
+ case FTIO_RDAHEAD:
async_read(ftu, 2);
break;
case FTIO_WRITING:
@@ -735,10 +944,11 @@ complete:
/*
* Entry point for the async request processor.
*/
-void async_req(ftu_t ftu, int from)
+static void
+async_req(ftu_t ftu, int from)
{
ft_p ft = &ft_data[ftu];
- SegReq *sp;
+ SegReq *nsp, *sp;
static int over_async, lastreq, domore;
int cmd;
@@ -747,56 +957,73 @@ void async_req(ftu_t ftu, int from)
restate:
switch (arq_state) {
case 0: /* Process segment */
- ft->io_sts = ft->curseg->reqtype;
+ 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;
- /* Swap buffered and current segment */
- lastreq = ft->curseg->reqtype;
- ft->curseg->reqtype = FTIO_READY;
- sp = ft->curseg;
- ft->curseg = ft->bufseg;
- ft->bufseg = sp;
+ /* Pull buffer from current I/O queue */
+ if (sp != NULL) {
+ lastreq = sp->reqtype;
+ segio_done(ft, sp);
- wakeup((caddr_t)&ftsem.buff_avail);
+ /* 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) {
- domore = (ft->curseg->reqtype != FTIO_READY);
- ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0);
+ ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0);
}
arq_state = 1;
goto restate;
case 1: /* Next request */
- if (ft->curseg->reqtype != FTIO_READY) {
- ft->curseg->reqcrc = 0;
+ /* 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 = ft->curseg->reqblk;
+ ft->xblk = sp->reqblk;
+ ft->xseg = sp->reqseg;
ft->xcnt = 0;
- ft->xptr = ft->curseg->buff;
- DPRT(("I/O reqblk = %d\n", ft->curseg->reqblk));
+ ft->xptr = sp->buff;
+ DPRT(("I/O reqblk = %d\n", ft->xblk));
goto restate;
}
- if (lastreq == FTIO_READING) {
- ft->curseg->reqtype = FTIO_RDAHEAD;
- ft->curseg->reqblk = ft->xblk;
- ft->curseg->reqcrc = 0;
- ft->curseg->reqcan = 0;
- bzero(ft->curseg->buff, QCV_SEGSIZE);
+
+ /* 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 = ft->curseg->reqblk;
+ ft->xblk = sp->reqblk;
+ ft->xseg = sp->reqseg;
ft->xcnt = 0;
- ft->xptr = ft->curseg->buff;
- DPRT(("Processing readahead reqblk = %d\n", ft->curseg->reqblk));
+ ft->xptr = sp->buff;
+ DPRT(("Processing readahead reqblk = %d\n", ft->xblk));
goto restate;
}
+
if (ft->moving) {
DPRT(("No more I/O.. Stopping.\n"));
- ACMD_FUNC(7, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
+ ft->moving = 0;
+ ACMD_FUNC(7, ACMD_SEEKSTS, QC_PAUSE, QS_READY, 0);
break;
}
arq_state = 7;
@@ -804,26 +1031,26 @@ restate:
case 2: /* End of track */
ft->moving = 0;
- ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 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, 0, 0);
+ ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0);
break;
case 4:
cmd = (ft->xblk / ftg->g_blktrk) + 2;
- if (domore) {
- ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0);
+ if (ft->segh != NULL) {
+ ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0);
} else {
- ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0);
+ ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0);
}
break;
case 5:
ft->moving = 1;
- ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0);
+ ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0);
break;
case 6:
@@ -832,26 +1059,22 @@ restate:
break;
case 7:
- ft->moving = 0;
-
- /* Check one last time to see if a request came in. */
- if (ft->curseg->reqtype != FTIO_READY) {
- DPRT(("async_req: Never say no!\n"));
- arq_state = 1;
- goto restate;
- }
-
/* Time to rest. */
ft->active = 0;
- wakeup((caddr_t)&ftsem.iosts_change); /* wakeup those who want an i/o chg */
+ ft->lastpos = -2;
+
+ /* wakeup those who want an i/o chg */
+ wakeup((caddr_t)wc_iosts_change);
break;
}
}
+
/*
* Entry for async read.
*/
-void async_read(ftu_t ftu, int from)
+static void
+async_read(ftu_t ftu, int from)
{
ft_p ft = &ft_data[ftu];
fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
@@ -859,6 +1082,7 @@ void async_read(ftu_t ftu, int from)
int i, cmd, newcn, rddta[7];
int st0, pcn, where;
static int over_async;
+ static int retries = 0;
if (from == 2) ard_state = over_async;
@@ -872,13 +1096,13 @@ restate:
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, 0, 0);
+ 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, 0, 0);
+ ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0);
}
ard_state = 1;
goto restate;
@@ -887,8 +1111,8 @@ restate:
/* Tape is now moving and in position-- start DMA now! */
isa_dmastart(B_READ, ft->xptr, QCV_BLKSIZE, 2);
out_fdc(fdcu, 0x66); /* read */
- out_fdc(fdcu, 0x00); /* unit */
- out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */
+ out_fdc(fdcu, ftu); /* unit */
+ 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 */
@@ -905,7 +1129,8 @@ restate:
#if FTDBGALL
/* Compute where the controller thinks we are */
- where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5]-1;
+ 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));
@@ -913,34 +1138,44 @@ restate:
/* Check for errors */
if ((rddta[0] & 0xc0) != 0x00) {
- if (rddta[1] & 0x04) {
+#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->curseg->reqcrc |= (1 << ft->xcnt);
+ 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->curseg->reqcan == 0) {
+ if (ft->xcnt < QCV_BLKSEG && ft->segh->reqcan == 0) {
ard_state = 0;
goto restate;
}
- DPRT(("Read done.. Cancel = %d\n", ft->curseg->reqcan));
+ 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, 0, 0);
+ ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0);
break;
case 4:
@@ -960,7 +1195,8 @@ restate:
* routine, if it's 1 then it was a timeout, if it's 2, then an
* async_cmd completed.
*/
-void async_write(ftu_t ftu, int from)
+static void
+async_write(ftu_t ftu, int from)
{
ft_p ft = &ft_data[ftu];
fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
@@ -982,13 +1218,13 @@ restate:
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, 0, 0);
+ 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, 0, 0);
+ ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0);
}
awr_state = 1;
goto restate;
@@ -997,8 +1233,8 @@ restate:
/* Tape is now moving and in position-- start DMA now! */
isa_dmastart(B_WRITE, ft->xptr, QCV_BLKSIZE, 2);
out_fdc(fdcu, 0x45); /* write */
- out_fdc(fdcu, 0x00); /* unit */
- out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */
+ out_fdc(fdcu, ftu); /* unit */
+ 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 */
@@ -1023,13 +1259,16 @@ restate:
/* Check for errors */
if ((rddta[0] & 0xc0) != 0x00) {
- if (rddta[1] & 0x04) {
- /* Probably wrong position */
- ft->lastpos = ft->xblk;
- awr_state = 0;
- goto restate;
- } else if (retries < 5) {
+#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++;
@@ -1037,11 +1276,11 @@ restate:
} else {
/*
* Retries failed. Note the unrecoverable error.
- * Marking the block as bad is fairly useless.
+ * Marking the block as bad is useless right now.
*/
printf("ft%d: unrecoverable write error on block %d\n",
ftu, ft->xblk);
- ft->curseg->reqcrc |= (1 << ft->xcnt);
+ ft->segh->reqcrc |= (1 << ft->xcnt);
}
}
@@ -1063,7 +1302,7 @@ restate:
case 3:
ft->moving = 1;
- ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0);
+ ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0);
break;
case 4:
@@ -1081,11 +1320,14 @@ restate:
/*
* Interrupt handler for active tape. Bounced off of fdintr().
*/
-int ftintr(ftu_t ftu)
+int
+ftintr(ftu_t ftu)
{
int st0, pcn, i;
ft_p ft = &ft_data[ftu];
- fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+ int s = splbio();
+
st0 = 0;
pcn = 0;
@@ -1093,12 +1335,14 @@ int ftintr(ftu_t ftu)
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);
}
@@ -1113,20 +1357,21 @@ int ftintr(ftu_t ftu)
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)&ftsem.intr_wait);
+ 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)&ftsem.intr_wait);
+ wakeup((caddr_t)wc_intr_wait);
}
#if FTDBGALL
else
@@ -1137,20 +1382,23 @@ huh_what:
case FTCMD_READID:
for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu);
ft->sts_wait = FTSTS_INTERRUPT;
- wakeup((caddr_t)&ftsem.intr_wait);
+ wakeup((caddr_t)wc_intr_wait);
break;
default:
goto huh_what;
}
+ splx(s);
return(1);
}
+
/*
* Interrupt timeout routine.
*/
-static void ft_timeout(caddr_t arg1, int arg2)
+static void
+ft_timeout(caddr_t arg1, int arg2)
{
int s;
ftu_t ftu = (ftu_t)arg1;
@@ -1166,17 +1414,19 @@ static void ft_timeout(caddr_t arg1, int arg2)
async_req(ftu, 1);
} else {
ft->sts_wait = FTSTS_TIMEOUT;
- wakeup((caddr_t)&ftsem.intr_wait);
+ 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.
*/
-int ftintr_wait(ftu_t ftu, int cmd, int ticks)
+static int
+ftintr_wait(ftu_t ftu, int cmd, int ticks)
{
int retries, st0, pcn;
ft_p ft = &ft_data[ftu];
@@ -1211,8 +1461,7 @@ int ftintr_wait(ftu_t ftu, int cmd, int ticks)
goto intrdone;
}
- if (ticks) timeout(ft_timeout, (caddr_t)ftu, ticks);
- sleep((caddr_t)&ftsem.intr_wait, FTPRI);
+ ftsleep(wc_intr_wait, ticks);
intrdone:
if (ft->sts_wait == FTSTS_TIMEOUT) { /* timeout */
@@ -1230,11 +1479,13 @@ intrdone:
return(0);
}
+
/*
* Recalibrate tape drive. Parameter totape is true, if we should
* recalibrate to tape drive settings.
*/
-int tape_recal(ftu_t ftu, int totape)
+static int
+tape_recal(ftu_t ftu, int totape)
{
int s;
ft_p ft = &ft_data[ftu];
@@ -1248,7 +1499,7 @@ int tape_recal(ftu_t ftu, int totape)
s = splbio();
out_fdc(fdcu, NE7CMD_RECAL);
- out_fdc(fdcu, 0x00);
+ out_fdc(fdcu, ftu);
if (ftintr_wait(ftu, FTCMD_RECAL, hz)) {
splx(s);
@@ -1265,18 +1516,25 @@ int tape_recal(ftu_t ftu, int totape)
return(0);
}
-static void state_timeout(caddr_t arg1, int arg2)
+
+/*
+ * Timeout for long delays.
+ */
+static void
+state_timeout(caddr_t arg1, int arg2)
{
ftu_t ftu = (ftu_t)arg1;
- wakeup((caddr_t)&ftsem.long_delay);
+ wakeup((caddr_t)wc_long_delay);
}
+
/*
* 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.
*/
-int tape_state(ftu_t ftu, int all, int mask, int seconds)
+static int
+tape_state(ftu_t ftu, int all, int mask, int seconds)
{
int r, tries, maxtries;
@@ -1287,20 +1545,19 @@ int tape_state(ftu_t ftu, int all, int mask, int seconds)
if (all && (r & mask) == mask) return(r);
if ((r & mask) != 0) return(r);
}
- if (seconds) {
- timeout(state_timeout, (caddr_t)ftu, hz/4);
- sleep((caddr_t)&ftsem.long_delay, FTPRI);
- }
+ 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.
*/
-int tape_cmd(ftu_t ftu, int cmd)
+static int
+tape_cmd(ftu_t ftu, int cmd)
{
int newcn;
int retries = 0;
@@ -1316,7 +1573,7 @@ retry:
/* Perform seek */
s = splbio();
out_fdc(fdcu, NE7CMD_SEEK);
- out_fdc(fdcu, 0x00);
+ out_fdc(fdcu, ftu);
out_fdc(fdcu, newcn);
if (ftintr_wait(ftu, FTCMD_SEEK, hz)) {
@@ -1338,25 +1595,40 @@ redo:
return(0);
}
+
/*
* Return status of tape drive
*/
-int tape_status(ftu_t ftu)
+static int
+tape_status(ftu_t ftu)
{
int r, err, tries;
- ft_p ft = &ft_data[ftu];
+ ft_p ft = &ft_data[ftu];
+ int max = (ft->attaching) ? 2 : 3;
- for (r = -1, tries = 0; r < 0 && tries < 3; tries++)
+ for (r = -1, tries = 0; r < 0 && tries < max; tries++)
r = qic_status(ftu, QC_STATUS, 8);
- if (tries == 3) return(-1);
+ if (tries == max) return(-1);
+
+recheck:
DPRT(("tape_status got $%04x\n",r));
ft->laststs = r;
if (r & (QS_ERROR|QS_NEWCART)) {
- if (r & QS_NEWCART) ft->newcart = 1;
err = qic_status(ftu, QC_ERRCODE, 16);
ft->lasterr = err;
- if ((r & QS_NEWCART) == 0 && err && ft->attaching == 0) {
+ 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));
}
@@ -1364,29 +1636,36 @@ int tape_status(ftu_t ftu)
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.
*/
-void tape_start(ftu_t ftu)
+static void
+tape_start(ftu_t ftu, int motor)
{
ft_p ft = &ft_data[ftu];
fdc_p fdc = ft->fdc;
- int s;
-
- DPRT(("tape_start start\n"));
+ int s, mbits;
s = splbio();
+ DPRT(("tape_start start\n"));
/* reset, dma disable */
- outb(fdc->baseport+fdout, 0x00);
+ outb(fdc->baseport+FDOUT, 0x00);
(void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
- /* raise reset, enable DMA */
- outb(fdc->baseport+fdout, FDO_FRST | FDO_FDMAEN);
+ /* raise reset, enable DMA, motor on if needed */
+ if (motor)
+ mbits = (!ftu) ? FDO_MOEN0 : FDO_MOEN1;
+ else
+ mbits = 0;
+
+ outb(fdc->baseport+FDOUT, FDO_FRST | FDO_FDMAEN | mbits);
(void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
splx(s);
@@ -1394,16 +1673,18 @@ void tape_start(ftu_t ftu)
tape_recal(ftu, 1);
/* set transfer speed */
- outb(fdc->baseport+fdctl, FDC_500KBPS);
+ outb(fdc->baseport+FDCTL, FDC_500KBPS);
DELAY(10);
DPRT(("tape_start end\n"));
}
+
/*
* Transfer control back to floppy disks.
*/
-void tape_end(ftu_t ftu)
+static void
+tape_end(ftu_t ftu)
{
ft_p ft = &ft_data[ftu];
fdc_p fdc = ft->fdc;
@@ -1415,41 +1696,59 @@ void tape_end(ftu_t ftu)
s = splbio();
/* reset, dma disable */
- outb(fdc->baseport+fdout, 0x00);
+ outb(fdc->baseport+FDOUT, 0x00);
(void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
/* raise reset, enable DMA */
- outb(fdc->baseport+fdout, FDO_FRST | FDO_FDMAEN);
+ outb(fdc->baseport+FDOUT, FDO_FRST | FDO_FDMAEN);
(void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
splx(s);
/* set transfer speed */
- outb(fdc->baseport+fdctl, FDC_500KBPS);
+ outb(fdc->baseport+FDCTL, FDC_500KBPS);
DELAY(10);
fdc->flags &= ~FDC_TAPE_BUSY;
DPRT(("tape_end end\n"));
}
+
/*
* Wait for the driver to go inactive, cancel readahead if necessary.
*/
-void tape_inactive(ftu_t ftu)
+static void
+tape_inactive(ftu_t ftu)
{
ft_p ft = &ft_data[ftu];
-
- if (ft->curseg->reqtype == FTIO_RDAHEAD) {
- ft->curseg->reqcan = 1; /* XXX cancel rdahead */
- while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI);
+ 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) sleep((caddr_t)&ftsem.iosts_change, FTPRI);
+ while (ft->active) ftsleep(wc_iosts_change, 0);
+ splx(s);
}
+
/*
* Get the geometry of the tape currently in the drive.
*/
-int ftgetgeom(ftu_t ftu)
+static int
+ftgetgeom(ftu_t ftu)
{
int r, i, tries;
int cfg, qic80, ext;
@@ -1459,7 +1758,7 @@ int ftgetgeom(ftu_t ftu)
r = tape_status(ftu);
/* XXX fix me when format mode is finished */
- if ((r & QS_CART) == 0 || (r & QS_FMTOK) == 0) {
+ 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;
@@ -1539,20 +1838,21 @@ int ftgetgeom(ftu_t ftu)
return(0);
}
+
/*
* Switch between tape/floppy. This will send the tape enable/disable
* codes for this drive's manufacturer.
*/
-int set_fdcmode(dev_t dev, int newmode)
+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;
void *buf;
int r, s, i;
- SegReq *sp;
+ SegReq *sp, *rsp;
if (newmode == FDC_TAPE_MODE) {
/* Wake up the tape drive */
@@ -1560,19 +1860,22 @@ int set_fdcmode(dev_t dev, int newmode)
case NO_TYPE:
fdc->flags &= ~FDC_TAPE_BUSY;
return(ENXIO);
+ case FT_NONE:
+ tape_start(ftu, 0);
+ break;
case FT_COLORADO:
- tape_start(ftu);
+ tape_start(ftu, 0);
if (tape_cmd(ftu, QC_COL_ENABLE1)) {
tape_end(ftu);
return(EIO);
}
- if (tape_cmd(ftu, QC_COL_ENABLE2)) {
+ if (tape_cmd(ftu, QC_COL_ENABLE2 + ftu)) {
tape_end(ftu);
return(EIO);
}
break;
case FT_MOUNTAIN:
- tape_start(ftu);
+ tape_start(ftu, 0);
if (tape_cmd(ftu, QC_MTN_ENABLE1)) {
tape_end(ftu);
return(EIO);
@@ -1582,56 +1885,93 @@ int set_fdcmode(dev_t dev, int newmode)
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) {
- tape_cmd(ftu, (ft->type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE);
+ 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->curseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT);
- if (ft->curseg == NULL) {
- printf("ft%d: not enough memory for buffers\n", ftu);
- return(ENOMEM);
- }
- ft->bufseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT);
- if (ft->bufseg == NULL) {
- free(ft->curseg, M_DEVBUF);
- printf("ft%d: not enough memory for buffers\n", ftu);
- return(ENOMEM);
+ 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->curseg->reqtype = FTIO_READY;
- ft->bufseg->reqtype = FTIO_READY;
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; /* a new cart was inserted */
+ 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_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 {
- tape_cmd(ftu, (ft->type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE);
+ 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;
- free(ft->curseg, M_DEVBUF);
- free(ft->bufseg, M_DEVBUF);
}
return(0);
}
@@ -1640,7 +1980,8 @@ int set_fdcmode(dev_t dev, int newmode)
/*
* Perform a QIC status function.
*/
-int qic_status(ftu_t ftu, int cmd, int nbits)
+static int
+qic_status(ftu_t ftu, int cmd, int nbits)
{
int st3, val, r, i;
ft_p ft = &ft_data[ftu];
@@ -1653,7 +1994,7 @@ int qic_status(ftu_t ftu, int cmd, int nbits)
/* Sense drive status */
out_fdc(fdcu, NE7CMD_SENSED);
- out_fdc(fdcu, 0x00);
+ out_fdc(fdcu, ftu);
st3 = in_fdc(fdcu);
if ((st3 & 0x10) == 0) { /* track 0 */
@@ -1668,7 +2009,7 @@ int qic_status(ftu_t ftu, int cmd, int nbits)
}
out_fdc(fdcu, NE7CMD_SENSED);
- out_fdc(fdcu, 0x00);
+ out_fdc(fdcu, ftu);
st3 = in_fdc(fdcu);
if (st3 < 0) {
DPRT(("ft%d: controller timed out on bit %d r=$%02x\n",
@@ -1690,11 +2031,13 @@ int qic_status(ftu_t ftu, int cmd, int nbits)
return(r);
}
+
/*
* Open tape drive for use. Bounced off of Fdopen if tape minor is
* detected.
*/
-int ftopen(dev_t dev, int arg2) {
+int
+ftopen(dev_t dev, int arg2) {
ftu_t ftu = FDUNIT(minor(dev));
int type = FDTYPE(minor(dev));
fdc_p fdc;
@@ -1714,19 +2057,21 @@ int ftopen(dev_t dev, int arg2) {
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)
+int
+ftclose(dev_t dev, int flags)
{
int s;
SegReq *sp;
ftu_t ftu = FDUNIT(minor(dev));
ft_p ft = &ft_data[ftu];
+
/* Wait for any remaining I/O activity to complete. */
- if (ft->curseg->reqtype == FTIO_RDAHEAD) ft->curseg->reqcan = 1;
- while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI);
+ tape_inactive(ftu);
ft->mode = FTM_PRIMARY;
tape_cmd(ftu, QC_PRIMARY);
@@ -1735,94 +2080,124 @@ int ftclose(dev_t dev, int flags)
return(set_fdcmode(dev, FDC_DISK_MODE)); /* Otherwise, close tape */
}
+
/*
- * Perform strategy on a given buffer (not!). The driver was not
- * performing very efficiently using the buffering routines. After
- * support for error correction was added, this routine became
- * obsolete in favor of doing ioctl's. Ugly, yes.
+ * Perform strategy on a given buffer (not!). Changed so that the
+ * driver will at least return 'Operation not supported'.
*/
-void ftstrategy(struct buf *bp)
+void
+ftstrategy(struct buf *bp)
{
- return;
+ bp->b_error = ENODEV;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
}
-/* Read or write a segment. */
-int ftreq_rw(ftu_t ftu, int cmd, QIC_Segment *sr, struct proc *p)
+
+/*
+ * Read or write a segment.
+ */
+static int
+ftreq_rw(ftu_t ftu, int cmd, QIC_Segment *sr, struct proc *p)
{
int r, i, j;
SegReq *sp;
int s;
- long blk, bad;
+ long blk, bad, seg;
unsigned char *cp, *cp2;
ft_p ft = &ft_data[ftu];
- if (!ft->active) {
+ if (!ft->active && ft->segh == NULL) {
r = tape_status(ftu);
- if ((r & QS_CART) == 0) {
+ if ((r & QS_CART) == 0)
return(ENXIO); /* No cartridge */
- }
- if ((r & QS_FMTOK) == 0) {
+ if ((r & QS_FMTOK) == 0)
return(ENXIO); /* Not formatted */
- }
tape_state(ftu, 0, QS_READY, 90);
}
if (ftg == NULL || ft->newcart) {
- while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI);
+ tape_inactive(ftu);
tape_state(ftu, 0, QS_READY, 90);
- if (ftgetgeom(ftu) < 0) {
+ if (ftgetgeom(ftu) < 0)
return(ENXIO);
- }
}
/* Write not allowed on a read-only tape. */
- if (cmd == QIOWRITE && ft->rdonly) {
+ if (cmd == QIOWRITE && ft->rdonly)
return(EROFS);
- }
+
/* Quick check of request and buffer. */
- if (sr == NULL || sr->sg_data == NULL) {
+ if (sr == NULL || sr->sg_data == NULL)
return(EINVAL);
- }
- if (sr->sg_trk >= ftg->g_trktape ||
- sr->sg_seg >= ftg->g_segtrk) {
+
+ /* 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) {
- if (ft->curseg->reqtype == FTIO_RDAHEAD) {
- if (blk == ft->curseg->reqblk) {
- sp = ft->curseg;
+ /*
+ * 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;
- } else
- ft->curseg->reqcan = 1; /* XXX cancel rdahead */
+ }
}
/* Wait until we're ready. */
- while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI);
+ tape_inactive(ftu);
/* Set up a new read request. */
- sp = ft->curseg;
+ 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:
- sleep((caddr_t)&ftsem.buff_avail, FTPRI);
+ ftsleep(wc_buff_done, 0);
+
+rddone:
bad = sp->reqbad;
sr->sg_crcmap = sp->reqcrc & ~bad;
@@ -1833,17 +2208,29 @@ rdwait:
copyout(cp, cp2, QCV_BLKSIZE);
cp2 += QCV_BLKSIZE;
}
+ segio_free(ft, sp);
} else {
- if (ft->curseg->reqtype == FTIO_RDAHEAD) {
- ft->curseg->reqcan = 1; /* XXX cancel rdahead */
- while (ft->active)
- sleep((caddr_t)&ftsem.iosts_change, FTPRI);
+ 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 (ft->bufseg->reqtype != FTIO_READY)
- sleep((caddr_t)&ftsem.buff_avail, FTPRI);
- sp = (ft->curseg->reqtype == FTIO_READY) ? ft->curseg : ft->bufseg;
+ while (sp == NULL) {
+ ftsleep(wc_buff_avail, 0);
+ sp = segio_alloc(ft);
+ }
/* Copy in segment and expand bad blocks. */
bad = sr->sg_badmap;
@@ -1853,28 +2240,22 @@ rdwait:
copyin(cp, cp2, QCV_BLKSIZE);
cp += QCV_BLKSIZE;
}
-
sp->reqblk = blk;
+ sp->reqseg = seg;
sp->reqcan = 0;
sp->reqtype = FTIO_WRITING;
-
- if (!ft->active) {
- DPRT(("Starting write I/O chain\n"));
- arq_state = ard_state = awr_state = 0;
- ft->xblk = sp->reqblk;
- ft->xcnt = 0;
- ft->xptr = sp->buff;
- ft->active = 1;
- timeout(ft_timeout, (caddr_t)ftu, 1);
- }
+ segio_queue(ft, sp);
}
splx(s);
return(0);
}
-/* Rewind to beginning of tape */
-int ftreq_rewind(ftu_t ftu)
+/*
+ * Rewind to beginning of tape
+ */
+static int
+ftreq_rewind(ftu_t ftu)
{
ft_p ft = &ft_data[ftu];
@@ -1891,8 +2272,12 @@ int ftreq_rewind(ftu_t ftu)
return(0);
}
-/* Move to logical beginning or end of track */
-int ftreq_trkpos(ftu_t ftu, int req)
+
+/*
+ * 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];
@@ -1919,8 +2304,12 @@ int ftreq_trkpos(ftu_t ftu, int req)
return(0);
}
-/* Seek tape head to a particular track. */
-int ftreq_trkset(ftu_t ftu, int *trk)
+
+/*
+ * Seek tape head to a particular track.
+ */
+static int
+ftreq_trkset(ftu_t ftu, int *trk)
{
int curtrk, r, cmd;
ft_p ft = &ft_data[ftu];
@@ -1942,27 +2331,45 @@ int ftreq_trkset(ftu_t ftu, int *trk)
return(0);
}
-/* Start tape moving forward. */
-int ftreq_lfwd(ftu_t ftu)
+
+/*
+ * 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 */
-int ftreq_stop(ftu_t ftu)
+
+/*
+ * 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. */
-int ftreq_setmode(ftu_t ftu, int cmd)
+
+/*
+ * 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];
@@ -1989,8 +2396,12 @@ int ftreq_setmode(ftu_t ftu, int cmd)
return(0);
}
-/* Return drive status bits */
-int ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p)
+
+/*
+ * Return drive status bits
+ */
+static int
+ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p)
{
ft_p ft = &ft_data[ftu];
@@ -2001,8 +2412,12 @@ int ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p)
return(0);
}
-/* Return drive configuration bits */
-int ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p)
+
+/*
+ * 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];
@@ -2018,8 +2433,12 @@ int ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p)
return(0);
}
-/* Return current tape's geometry. */
-int ftreq_geom(ftu_t ftu, QIC_Geom *g)
+
+/*
+ * 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);
@@ -2027,8 +2446,12 @@ int ftreq_geom(ftu_t ftu, QIC_Geom *g)
return(0);
}
-/* Return drive hardware information */
-int ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp)
+
+/*
+ * Return drive hardware information
+ */
+static int
+ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp)
{
int r, tries;
int rom, vend;
@@ -2053,10 +2476,31 @@ int ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp)
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)
+int
+ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
{
ftu_t ftu = FDUNIT(minor(dev));
ft_p ft = &ft_data[ftu];
@@ -2104,20 +2548,30 @@ int ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
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);
}
-/* Not implemented */
-int ftdump(dev_t dev)
+/*
+ * Not implemented
+ */
+int
+ftdump(dev_t dev)
{
return(EINVAL);
}
-/* Not implemented */
-int ftsize(dev_t dev)
+/*
+ * Not implemented
+ */
+int
+ftsize(dev_t dev)
{
return(EINVAL);
}
diff --git a/sys/i386/isa/ftreg.h b/sys/i386/isa/ftreg.h
index 7b4ca6a27236..c29540ce0a96 100644
--- a/sys/i386/isa/ftreg.h
+++ b/sys/i386/isa/ftreg.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1993 Steve Gerakines
+ * 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.
@@ -17,6 +17,9 @@
* 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.
*
@@ -42,7 +45,7 @@
#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_SEEKLOAD 14 /* seek load point */
+#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 */
@@ -62,7 +65,7 @@
/* Colorado enable/disable. */
#define QC_COL_ENABLE1 46 /* enable */
-#define QC_COL_ENABLE2 2 /* null-op */
+#define QC_COL_ENABLE2 2 /* unit+2 */
#define QC_COL_DISABLE 47 /* disable */
/* Mountain enable/disable. */
@@ -77,5 +80,7 @@ typedef struct segq {
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;
diff --git a/sys/i386/isa/ic/i82365.h b/sys/i386/isa/ic/i82365.h
new file mode 100644
index 000000000000..ab381250ea72
--- /dev/null
+++ b/sys/i386/isa/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/i386/isa/ic/nec765.h b/sys/i386/isa/ic/nec765.h
index df6c337482aa..c02e6ef362ba 100644
--- a/sys/i386/isa/ic/nec765.h
+++ b/sys/i386/isa/ic/nec765.h
@@ -30,8 +30,7 @@
* 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.2 1993/10/16 13:48:50 rgrimes Exp $
+ * @(#)nec765.h 7.1 (Berkeley) 5/9/91
*/
/*
@@ -47,26 +46,90 @@
#define NE7_RQM 0x80 /* Diskette Controller ReQuest for Master */
/* Status register ST0 */
-#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005drv_chck\004drive_rdy\003top_head"
+#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_FORMAT 0x4c /* format - requires five additional bytes */
#define NE7CMD_RECAL 7 /* recalibrate drive - requires
unit select byte */
#define NE7CMD_SENSEI 8 /* sense controller interrupt status */
-#define NE7CMD_SEEK 15 /* seek drive - requires unit select byte
+#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 */
+
+
+/*
+ * "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/i386/isa/icu.h b/sys/i386/isa/icu.h
index af50335f4468..523f3351a859 100644
--- a/sys/i386/isa/icu.h
+++ b/sys/i386/isa/icu.h
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)icu.h 5.6 (Berkeley) 5/9/91
- * $Id: icu.h,v 1.2 1993/10/16 13:45:51 rgrimes Exp $
+ * $Id: icu.h,v 1.3 1994/04/02 07:00:40 davidg Exp $
*/
/*
@@ -51,12 +51,6 @@
* Interrupt "level" mechanism variables, masks, and macros
*/
extern unsigned imen; /* interrupt mask enable */
-extern unsigned cpl; /* current priority level mask */
-
-extern unsigned highmask; /* group of interrupts masked with splhigh() */
-extern unsigned ttymask; /* group of interrupts masked with spltty() */
-extern unsigned biomask; /* group of interrupts masked with splbio() */
-extern unsigned netmask; /* group of interrupts masked with splimp() */
#define INTREN(s) (imen &= ~(s), SET_ICUS())
#define INTRDIS(s) (imen |= (s), SET_ICUS())
@@ -74,7 +68,7 @@ extern unsigned netmask; /* group of interrupts masked with splimp() */
#endif
/*
- * Interrupt enable bits -- in order of priority
+ * Interrupt enable bits - in normal order of priority (which we change)
*/
#define IRQ0 0x0001 /* highest priority - timer */
#define IRQ1 0x0002
@@ -88,7 +82,7 @@ extern unsigned netmask; /* group of interrupts masked with splimp() */
#define IRQ13 0x2000
#define IRQ14 0x4000
#define IRQ15 0x8000
-#define IRQ3 0x0008
+#define IRQ3 0x0008 /* this is highest after rotation */
#define IRQ4 0x0010
#define IRQ5 0x0020
#define IRQ6 0x0040
diff --git a/sys/i386/isa/icu.s b/sys/i386/isa/icu.s
index d1c843951b83..9484e5cdbedd 100644
--- a/sys/i386/isa/icu.s
+++ b/sys/i386/isa/icu.s
@@ -36,7 +36,7 @@
*
* @(#)icu.s 7.2 (Berkeley) 5/21/91
*
- * $Id: icu.s,v 1.7 1993/12/20 14:58:21 wollman Exp $
+ * $Id: icu.s,v 1.8 1994/04/02 07:00:41 davidg Exp $
*/
/*
@@ -45,215 +45,131 @@
*/
/*
- * XXX - this file is now misnamed. All spls are now soft and the only thing
- * related to the hardware icu is that the bit numbering is the same in the
- * soft priority masks as in the hard ones.
+ * 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.
*/
-#include "sio.h"
-#define HIGHMASK 0xffff
-#define SOFTCLOCKMASK 0x8000
+#include "../net/netisr.h"
.data
-
.globl _cpl
-_cpl: .long 0xffff /* current priority (all off) */
-
+_cpl: .long HWI_MASK | SWI_MASK /* current priority (all off) */
.globl _imen
-_imen: .long 0xffff /* interrupt mask enable (all off) */
-
-/* .globl _highmask */
-_highmask: .long HIGHMASK
-
- .globl _ttymask, _biomask, _netmask
-_ttymask: .long 0
-_biomask: .long 0
-_netmask: .long 0
-
- .globl _ipending, _astpending
+_imen: .long HWI_MASK /* interrupt mask enable (all h/w off) */
+_high_imask: .long HWI_MASK | SWI_MASK
+ .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 _astpending
_astpending: .long 0 /* tells us an AST needs to be taken */
-
.globl _netisr
_netisr: .long 0 /* set with bits for which queue to service */
-
vec:
.long vec0, vec1, vec2, vec3, vec4, vec5, vec6, vec7
.long vec8, vec9, vec10, vec11, vec12, vec13, vec14, vec15
-#define GENSPL(name, mask, event) \
- .globl _spl/**/name ; \
- ALIGN_TEXT ; \
-_spl/**/name: ; \
- COUNT_EVENT(_intrcnt_spl, event) ; \
- movl _cpl,%eax ; \
- movl %eax,%edx ; \
- orl mask,%edx ; \
- movl %edx,_cpl ; \
- SHOW_CPL ; \
- ret
-
-#define FASTSPL(mask) \
- movl mask,_cpl ; \
- SHOW_CPL
-
-#define FASTSPL_VARMASK(varmask) \
- movl varmask,%eax ; \
- movl %eax,_cpl ; \
- SHOW_CPL
-
.text
- ALIGN_TEXT
-unpend_v:
- COUNT_EVENT(_intrcnt_spl, 0)
- bsfl %eax,%eax # slow, but not worth optimizing
- btrl %eax,_ipending
- jnc unpend_v_next # some intr cleared the in-memory bit
- SHOW_IPENDING
- movl Vresume(,%eax,4),%eax
- testl %eax,%eax
- je noresume
- jmp %eax
-
- ALIGN_TEXT
-/*
- * XXX - must be some fastintr, need to register those too.
- */
-noresume:
-#if NSIO > 0
- call _softsio1
-#endif
-unpend_v_next:
- movl _cpl,%eax
- movl %eax,%edx
- notl %eax
- andl _ipending,%eax
- je none_to_unpend
- jmp unpend_v
-
/*
- * Handle return from interrupt after device handler finishes
- */
- ALIGN_TEXT
-doreti:
- COUNT_EVENT(_intrcnt_spl, 1)
- addl $4,%esp # discard unit arg
- popl %eax # get previous priority
-/*
- * Now interrupt frame is a trap frame!
- *
- * XXX - setting up the interrupt frame to be almost a stack frame is mostly
- * a waste of time.
+ * 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
- SHOW_CPL
- movl %eax,%edx
- notl %eax
- andl _ipending,%eax
- jne unpend_v
-none_to_unpend:
- testl %edx,%edx # returning to zero priority?
- jne 1f # nope, going to non-zero priority
- movl _netisr,%eax
- testl %eax,%eax # check for softint s/traps
- jne 2f # there are some
- jmp test_resched # XXX - schedule jumps better
- COUNT_EVENT(_intrcnt_spl, 2) # XXX
-
- ALIGN_TEXT # XXX
-1: # XXX
- COUNT_EVENT(_intrcnt_spl, 3)
+ MEXITCOUNT
popl %es
popl %ds
popal
addl $8,%esp
iret
-#include "../net/netisr.h"
-
-#define DONET(s, c, event) ; \
- .globl c ; \
- btrl $s,_netisr ; \
- jnc 1f ; \
- COUNT_EVENT(_intrcnt_spl, event) ; \
- call c ; \
-1:
-
ALIGN_TEXT
-2:
- COUNT_EVENT(_intrcnt_spl, 4)
-/*
- * XXX - might need extra locking while testing reg copy of netisr, but
- * interrupt routines setting it would not cause any new problems (since we
- * don't loop, fresh bits will not be processed until the next doreti or spl0).
- */
- testl $~((1 << NETISR_SCLK) | (1 << NETISR_AST)),%eax
- je test_ASTs # no net stuff, just temporary AST's
- FASTSPL_VARMASK(_netmask)
-#if 0
- DONET(NETISR_RAW, _rawintr, 5)
-#endif
-
-#ifdef INET
- DONET(NETISR_IP, _ipintr, 6)
-#endif /* INET */
-
-#ifdef IMP
- DONET(NETISR_IMP, _impintr, 7)
-#endif /* IMP */
-
-#ifdef NS
- DONET(NETISR_NS, _nsintr, 8)
-#endif /* NS */
-
-#ifdef ISO
- DONET(NETISR_ISO, _clnlintr, 9)
-#endif /* ISO */
+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
-#ifdef CCITT
- DONET(NETISR_X25, _pkintr, 29)
- DONET(NETISR_HDLC, _hdintr, 30)
-#endif /* CCITT */
+ 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
- FASTSPL($0)
-test_ASTs:
- btrl $NETISR_SCLK,_netisr
- jnc test_resched
- COUNT_EVENT(_intrcnt_spl, 10)
- FASTSPL($SOFTCLOCKMASK)
-/*
- * Back to an interrupt frame for a moment.
- */
- pushl $0 # previous cpl (probably not used)
- pushl $0x7f # dummy unit number
- call _softclock
- addl $8,%esp # discard dummies
- FASTSPL($0)
-test_resched:
-#ifdef notused1
- btrl $NETISR_AST,_netisr
- jnc 2f
-#endif
-#ifdef notused2
- cmpl $0,_want_resched
- je 2f
-#endif
- cmpl $0,_astpending # XXX - put it back in netisr to
- je 2f # reduce the number of tests
+ ALIGN_TEXT
+swi_ast:
+ addl $8,%esp /* discard raddr & cpl to get trap frame */
testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp)
- # to non-kernel (i.e., user)?
- je 2f # nope, leave
- COUNT_EVENT(_intrcnt_spl, 11)
- movl $0,_astpending
+ je swi_ast_phantom
+ movl $T_ASTFLT,(2+8+0)*4(%esp)
call _trap
-2:
- COUNT_EVENT(_intrcnt_spl, 12)
- popl %es
- popl %ds
- popal
- addl $8,%esp
- iret
+ 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
+ jmp doreti_exit /* SWI_AST is highest so we must be done */
/*
* Interrupt priority mechanism
@@ -262,121 +178,84 @@ test_resched:
* -- ipending = active interrupts currently masked by cpl
*/
- GENSPL(bio, _biomask, 13)
- GENSPL(clock, $HIGHMASK, 14) /* splclock == splhigh ex for count */
- GENSPL(high, $HIGHMASK, 15)
- GENSPL(imp, _netmask, 16) /* splimp == splnet except for count */
- GENSPL(net, _netmask, 17)
- GENSPL(softclock, $SOFTCLOCKMASK, 18)
- GENSPL(tty, _ttymask, 19)
-
- .globl _splnone
- .globl _spl0
- ALIGN_TEXT
-_splnone:
-_spl0:
- COUNT_EVENT(_intrcnt_spl, 20)
-in_spl0:
+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
- pushl %eax # save old priority
- testl $(1 << NETISR_RAW) | (1 << NETISR_IP),_netisr
- je over_net_stuff_for_spl0
- movl _netmask,%eax # mask off those network devices
- movl %eax,_cpl # set new priority
- SHOW_CPL
-/*
- * XXX - what about other net intrs?
- */
-#if 0
- DONET(NETISR_RAW, _rawintr, 21)
-#endif
-
-#ifdef INET
- DONET(NETISR_IP, _ipintr, 22)
-#endif /* INET */
-
-#ifdef IMP
- DONET(NETISR_IMP, _impintr, 23)
-#endif /* IMP */
-
-#ifdef NS
- DONET(NETISR_NS, _nsintr, 24)
-#endif /* NS */
-
-#ifdef ISO
- DONET(NETISR_ISO, _clnlintr, 25)
-#endif /* ISO */
-
-over_net_stuff_for_spl0:
- movl $0,_cpl # set new priority
- SHOW_CPL
- movl _ipending,%eax
- testl %eax,%eax
- jne unpend_V
- popl %eax # return old priority
+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
- .globl _splx
ALIGN_TEXT
-_splx:
- COUNT_EVENT(_intrcnt_spl, 26)
- movl 4(%esp),%eax # new priority
- testl %eax,%eax
- je in_spl0 # going to "zero level" is special
- COUNT_EVENT(_intrcnt_spl, 27)
- movl _cpl,%edx # save old priority
- movl %eax,_cpl # set new priority
- SHOW_CPL
- notl %eax
- andl _ipending,%eax
- jne unpend_V_result_edx
- movl %edx,%eax # return old priority
- ret
-
- ALIGN_TEXT
-unpend_V_result_edx:
- pushl %edx
-unpend_V:
- COUNT_EVENT(_intrcnt_spl, 28)
- bsfl %eax,%eax
- btrl %eax,_ipending
- jnc unpend_V_next
- SHOW_IPENDING
- movl Vresume(,%eax,4),%edx
+splz_unpend:
+ bsfl %ecx,%ecx
+ btrl %ecx,_ipending
+ jnc splz_next
+ movl ihandlers(,%ecx,4),%edx
testl %edx,%edx
- je noresumeV
-/*
- * 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(,%eax,4)
+ 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
+
/*
- * XXX - must be some fastintr, need to register those too.
+ * Fake clock IRQ so that it appears to come from our caller and not from
+ * vec0, so that kernel 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.
*/
-noresumeV:
-#if NSIO > 0
- call _softsio1
-#endif
-unpend_V_next:
- movl _cpl,%eax
- notl %eax
- andl _ipending,%eax
- jne unpend_V
- popl %eax
- ret
+ ALIGN_TEXT
+vec0:
+ popl %eax /* return address */
+ pushfl
+#define KCSEL 8
+ pushl $KCSEL
+ pushl %eax
+ cli
+ MEXITCOUNT
+ jmp _Vclk
#define BUILD_VEC(irq_num) \
ALIGN_TEXT ; \
vec/**/irq_num: ; \
int $ICU_OFFSET + (irq_num) ; \
- popl %eax ; \
ret
- BUILD_VEC(0)
BUILD_VEC(1)
BUILD_VEC(2)
BUILD_VEC(3)
@@ -392,3 +271,58 @@ vec/**/irq_num: ; \
BUILD_VEC(13)
BUILD_VEC(14)
BUILD_VEC(15)
+
+ ALIGN_TEXT
+swi_clock:
+ MCOUNT
+ subl %eax,%eax
+ cmpl $_splz,(%esp) /* XXX call from splz()? */
+ jae 1f /* yes, usermode = 0 */
+ movl 4+4+TRAPF_CS_OFF(%esp),%eax /* no, check trap frame */
+ andl $SEL_RPL_MASK,%eax
+1:
+ pushl %eax
+ call _softclock
+ addl $4,%esp
+ ret
+
+#define DONET(s, c, event) ; \
+ .globl c ; \
+ btrl $s,_netisr ; \
+ jnc 9f ; \
+ call c ; \
+9:
+
+ ALIGN_TEXT
+swi_net:
+ MCOUNT
+#if 0
+ DONET(NETISR_RAW, _rawintr,netisr_raw)
+#endif
+#ifdef INET
+ DONET(NETISR_IP, _ipintr,netisr_ip)
+#endif
+#ifdef IMP
+ DONET(NETISR_IMP, _impintr,netisr_imp)
+#endif
+#ifdef NS
+ DONET(NETISR_NS, _nsintr,netisr_ns)
+#endif
+#ifdef ISO
+ DONET(NETISR_ISO, _clnlintr,netisr_iso)
+#endif
+#ifdef CCITT
+ DONET(NETISR_X25, _pkintr, 29)
+ DONET(NETISR_HDLC, _hdintr, 30)
+#endif
+ ret
+
+ ALIGN_TEXT
+swi_tty:
+ MCOUNT
+#include "sio.h"
+#if NSIO > 0
+ jmp _siopoll
+#else
+ ret
+#endif
diff --git a/sys/i386/isa/if_ed.c b/sys/i386/isa/if_ed.c
index 079f09cc9d3b..e806b1e471f9 100644
--- a/sys/i386/isa/if_ed.c
+++ b/sys/i386/isa/if_ed.c
@@ -16,7 +16,7 @@
*/
/*
- * $Id: if_ed.c,v 1.33.2.2 1994/04/17 06:04:33 rgrimes Exp $
+ * $Id: if_ed.c,v 1.42 1994/06/16 08:27:21 davidg Exp $
*/
#include "ed.h"
@@ -65,65 +65,71 @@
#ifndef IFF_ALTPHYS
#define IFF_ALTPHYS IFF_LLC0
#endif
-
+
/*
* ed_softc: per line info and status
*/
-struct ed_softc {
- struct arpcom arpcom; /* ethernet common */
+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 */
+ char *type_str; /* pointer to type string */
+ u_char vendor; /* interface vendor */
+ u_char type; /* interface type code */
- u_short asic_addr; /* ASIC I/O bus address */
- u_short nic_addr; /* NIC (DS8390) I/O bus address */
+ 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 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 bpf; /* BPF "magic cookie" */
- 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 */
-} ed_softc[NED];
-
-int ed_attach(struct isa_device *);
-void ed_init(int);
-void edintr(int);
-int ed_ioctl(struct ifnet *, int, caddr_t);
-int ed_probe(struct isa_device *);
-void ed_start(struct ifnet *);
-void ed_reset(int, int);
-void ed_watchdog(int);
-
-static void ed_get_packet(struct ed_softc *, char *, int /*u_short*/);
+ u_char wd_laar_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 bpf; /* BPF "magic cookie" */
+ 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 */
+} ed_softc[NED];
+
+int ed_attach(struct isa_device *);
+void ed_init(int);
+void edintr(int);
+int ed_ioctl(struct ifnet *, int, caddr_t);
+int ed_probe(struct isa_device *);
+void ed_start(struct ifnet *);
+void ed_reset(int, int);
+void ed_watchdog(int);
+
+#ifdef MULTICAST
+void ds_getmcaf();
+
+#endif
+
+static void ed_get_packet(struct ed_softc *, char *, int /* u_short */ );
static void ed_stop(int);
static inline void ed_rint();
static inline void ed_xmit();
static inline char *ed_ring_copy();
-void ed_pio_readmem(), ed_pio_writemem();
+void ed_pio_readmem(), ed_pio_writemem();
u_short ed_pio_write_mbufs();
extern int ether_output();
@@ -138,7 +144,8 @@ struct isa_driver eddriver = {
ed_attach,
"ed"
};
-/*
+
+/*
* Interrupt conversion table for WD/SMC ASIC
* (IRQ* are defined in icu.h)
*/
@@ -152,7 +159,7 @@ static unsigned short ed_intr_mask[] = {
IRQ15,
IRQ4
};
-
+
/*
* Interrupt conversion table for 585/790 Combo
*/
@@ -166,6 +173,7 @@ static unsigned short ed_790_intr_mask[] = {
IRQ11,
IRQ15
};
+
#define ETHER_MIN_LEN 64
#define ETHER_MAX_LEN 1518
#define ETHER_ADDR_LEN 6
@@ -185,7 +193,7 @@ ed_probe(isa_dev)
struct isa_device *isa_dev;
{
struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
- int nports;
+ int nports;
if (nports = ed_probe_WD80x3(isa_dev))
return (nports);
@@ -196,7 +204,7 @@ ed_probe(isa_dev)
if (nports = ed_probe_Novell(isa_dev))
return (nports);
- return(0);
+ return (0);
}
/*
@@ -219,7 +227,7 @@ ed_probe(isa_dev)
* 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.
+ * Return 1 if 8390 was found, 0 if not.
*/
int
@@ -227,15 +235,15 @@ ed_probe_generic8390(sc)
struct ed_softc *sc;
{
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_RD2 | ED_CR_TXP | ED_CR_STA | ED_CR_STP)) !=
+ (ED_CR_RD2 | ED_CR_STP))
return (0);
if ((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) != ED_ISR_RST)
return (0);
- return(1);
+ return (1);
}
-
+
/*
* Probe and vendor-specific initialization routine for SMC/WD80x3 boards
*/
@@ -244,9 +252,9 @@ ed_probe_WD80x3(isa_dev)
struct isa_device *isa_dev;
{
struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
- int i;
- u_int memsize;
- u_char iptr, isa16bit, sum;
+ 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;
@@ -256,26 +264,27 @@ ed_probe_WD80x3(isa_dev)
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.
+ * 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.
+ * 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);
+ 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);
@@ -318,7 +327,7 @@ ed_probe_WD80x3(isa_dev)
memsize = 16384;
isa16bit = 1;
break;
- case ED_TYPE_WD8013EP: /* also WD8003EP */
+ case ED_TYPE_WD8013EP: /* also WD8003EP */
if (inb(sc->asic_addr + ED_WD_ICR)
& ED_WD_ICR_16BIT) {
isa16bit = 1;
@@ -371,33 +380,35 @@ ed_probe_WD80x3(isa_dev)
sc->type_str = "";
break;
}
+
/*
- * Make some adjustments to initial values depending on what is
- * found in the ICR.
+ * 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)
+ && (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++)
+ 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)
+ * (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;
@@ -406,60 +417,66 @@ ed_probe_WD80x3(isa_dev)
/*
* Check 83C584 interrupt configuration register if this board has one
- * XXX - we could also check the IO address register. But why
- * bother...if we get past this, it *has* to be correct.
+ * XXX - we could also check the IO address register. But why
+ * bother...if we get past this, it *has* to be correct.
*/
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);
+ ((inb(isa_dev->id_iobase + ED_WD_IRR) &
+ (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5);
+
/*
- * Translate it using translation table, and check for correctness.
+ * Translate it using translation table, and check for
+ * correctness.
*/
if (ed_intr_mask[iptr] != isa_dev->id_irq) {
printf("ed%d: kernel configured irq %d doesn't match board configured irq %d\n",
- isa_dev->id_unit, ffs(isa_dev->id_irq) - 1,
- ffs(ed_intr_mask[iptr]) - 1);
- return(0);
+ isa_dev->id_unit, ffs(isa_dev->id_irq) - 1,
+ ffs(ed_intr_mask[iptr]) - 1);
+ return (0);
}
+
/*
* Enable the interrupt.
*/
outb(isa_dev->id_iobase + ED_WD_IRR,
- inb(isa_dev->id_iobase + ED_WD_IRR) | ED_WD_IRR_IEN);
+ 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);
+ 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);
+ (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);
+ inb(isa_dev->id_iobase + ED_WD790_HWR) & ~ED_WD790_HWR_SWH);
if (ed_790_intr_mask[iptr] != isa_dev->id_irq) {
printf("ed%d: kernel configured irq %d doesn't match board configured irq %d %d\n",
- isa_dev->id_unit, ffs(isa_dev->id_irq) - 1,
- ffs(ed_790_intr_mask[iptr]) - 1, iptr);
+ isa_dev->id_unit, ffs(isa_dev->id_irq) - 1,
+ ffs(ed_790_intr_mask[iptr]) - 1, iptr);
return 0;
}
+
/*
* Enable interrupts.
*/
outb(isa_dev->id_iobase + ED_WD790_ICR,
- inb(isa_dev->id_iobase + ED_WD790_ICR) | ED_WD790_ICR_EIL);
+ inb(isa_dev->id_iobase + ED_WD790_ICR) | ED_WD790_ICR_EIL);
}
-
sc->isa16bit = isa16bit;
-#ifdef notyet /* XXX - I'm not sure if PIO mode is even possible on WD/SMC boards */
+/* XXX - I'm not sure if PIO mode is even possible on WD/SMC boards */
+#ifdef notyet
+
/*
* The following allows the WD/SMC boards to be used in Programmed I/O
- * mode - without mapping the NIC memory shared. ...Not the prefered
- * way, but it might be the only way.
+ * mode - without mapping the NIC memory shared. ...Not the prefered
+ * way, but it might be the only way.
*/
if (isa_dev->id_flags & ED_FLAGS_FORCE_PIO) {
sc->mem_shared = 0;
@@ -472,7 +489,7 @@ ed_probe_WD80x3(isa_dev)
#endif
isa_dev->id_msize = memsize;
- sc->mem_start = (caddr_t)isa_dev->id_maddr;
+ sc->mem_start = (caddr_t) isa_dev->id_maddr;
/*
* allocate one xmit buffer if < 16k, two buffers otherwise
@@ -498,27 +515,6 @@ ed_probe_WD80x3(isa_dev)
sc->arpcom.ac_enaddr[i] = inb(sc->asic_addr + ED_WD_PROM + i);
if (sc->mem_shared) {
- /*
- * 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
- } else {
- outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB);
- outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) | 0x80));
- outb(sc->asic_addr + 0x0b, ((kvtop(sc->mem_start) >> 13) & 0x0f) |
- ((kvtop(sc->mem_start) >> 11) & 0x40) |
- (inb(sc->asic_addr + 0x0b) & 0xb0));
- outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) & ~0x80));
- }
/*
* Set upper address bits and 8/16 bit access to shared memory
@@ -530,57 +526,85 @@ ed_probe_WD80x3(isa_dev)
(void) inb(0x84);
} 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)));
+ ED_WD_LAAR_L16EN | ED_WD_LAAR_M16EN |
+ ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI)));
}
- } else {
+ } 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)));
+ ((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
+ } else {
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB);
+ outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) | 0x80));
+ outb(sc->asic_addr + 0x0b, ((kvtop(sc->mem_start) >> 13) & 0x0f) |
+ ((kvtop(sc->mem_start) >> 11) & 0x40) |
+ (inb(sc->asic_addr + 0x0b) & 0xb0));
+ outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) & ~0x80));
+ }
+
+ /*
* 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 %x - check configuration\n",
- isa_dev->id_unit, kvtop(sc->mem_start + i));
+ printf("ed%d: failed to clear shared memory at %x - 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);
+ (void) inb(0x84);
+ }
outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &=
- ~ED_WD_LAAR_M16EN));
+ ~ED_WD_LAAR_M16EN));
(void) inb(0x84);
}
-
- return(0);
+ 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.
+ * 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);
+ (void) inb(0x84);
+ }
outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &=
- ~ED_WD_LAAR_M16EN));
+ ~ED_WD_LAAR_M16EN));
(void) inb(0x84);
}
}
-
return (ED_WD_IO_PORTS);
}
@@ -592,84 +616,84 @@ ed_probe_3Com(isa_dev)
struct isa_device *isa_dev;
{
struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
- int i;
- u_int memsize;
- u_char isa16bit, sum;
+ int i;
+ u_int memsize;
+ u_char isa16bit, sum;
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
+ * configured address
*/
switch (inb(sc->asic_addr + ED_3COM_BCFR)) {
case ED_3COM_BCFR_300:
if (isa_dev->id_iobase != 0x300)
- return(0);
+ return (0);
break;
case ED_3COM_BCFR_310:
if (isa_dev->id_iobase != 0x310)
- return(0);
+ return (0);
break;
case ED_3COM_BCFR_330:
if (isa_dev->id_iobase != 0x330)
- return(0);
+ return (0);
break;
case ED_3COM_BCFR_350:
if (isa_dev->id_iobase != 0x350)
- return(0);
+ return (0);
break;
case ED_3COM_BCFR_250:
if (isa_dev->id_iobase != 0x250)
- return(0);
+ return (0);
break;
case ED_3COM_BCFR_280:
if (isa_dev->id_iobase != 0x280)
- return(0);
+ return (0);
break;
case ED_3COM_BCFR_2A0:
if (isa_dev->id_iobase != 0x2a0)
- return(0);
+ return (0);
break;
case ED_3COM_BCFR_2E0:
if (isa_dev->id_iobase != 0x2e0)
- return(0);
+ return (0);
break;
default:
- return(0);
+ return (0);
}
/*
- * Verify that the kernel shared memory address matches the
- * board configured address.
+ * 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);
+ return (0);
break;
case ED_3COM_PCFR_D8000:
if (kvtop(isa_dev->id_maddr) != 0xd8000)
- return(0);
+ return (0);
break;
case ED_3COM_PCFR_CC000:
if (kvtop(isa_dev->id_maddr) != 0xcc000)
- return(0);
+ return (0);
break;
case ED_3COM_PCFR_C8000:
if (kvtop(isa_dev->id_maddr) != 0xc8000)
- return(0);
+ return (0);
break;
default:
- return(0);
+ 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.
+ * 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);
@@ -677,10 +701,11 @@ ed_probe_3Com(isa_dev)
* 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).
+ * 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);
@@ -695,17 +720,18 @@ ed_probe_3Com(isa_dev)
sc->mem_shared = 1;
/*
- * Hmmm...a 16bit 3Com board has 16k of memory, but only an 8k
- * window to it.
+ * 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.
+ * registers normally appear.
*/
outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_EALO | ED_3COM_CR_XSEL);
@@ -714,9 +740,8 @@ ed_probe_3Com(isa_dev)
/*
* 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
+ * 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);
@@ -727,18 +752,18 @@ ed_probe_3Com(isa_dev)
/*
* select page 0 registers
*/
- outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP);
+ 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.
+ * 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);
+ 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
@@ -751,24 +776,23 @@ ed_probe_3Com(isa_dev)
/*
* select page 0 registers
*/
- outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2|ED_CR_STP);
+ outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2 | ED_CR_STP);
- sc->mem_start = (caddr_t)isa_dev->id_maddr;
+ 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.)
+ * 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)
+ if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)
sc->txb_cnt = 1;
else
sc->txb_cnt = 2;
@@ -776,22 +800,22 @@ ed_probe_3Com(isa_dev)
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;
+ 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;
+ 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.
+ * 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);
@@ -814,21 +838,22 @@ ed_probe_3Com(isa_dev)
break;
default:
printf("ed%d: Invalid irq configuration (%d) must be 2-5 for 3c503\n",
- isa_dev->id_unit, ffs(isa_dev->id_irq) - 1);
- return(0);
+ isa_dev->id_unit, ffs(isa_dev->id_irq) - 1);
+ return (0);
}
/*
- * Initialize GA configuration register. Set bank and enable shared mem.
+ * 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);
+ 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.
+ * 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);
@@ -841,13 +866,12 @@ ed_probe_3Com(isa_dev)
for (i = 0; i < memsize; ++i)
if (sc->mem_start[i]) {
- printf("ed%d: failed to clear shared memory at %x - check configuration\n",
- isa_dev->id_unit, kvtop(sc->mem_start + i));
- return(0);
+ printf("ed%d: failed to clear shared memory at %x - check configuration\n",
+ isa_dev->id_unit, kvtop(sc->mem_start + i));
+ return (0);
}
-
isa_dev->id_msize = memsize;
- return(ED_3COM_IO_PORTS);
+ return (ED_3COM_IO_PORTS);
}
/*
@@ -858,10 +882,10 @@ ed_probe_Novell(isa_dev)
struct isa_device *isa_dev;
{
struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
- u_int memsize, n;
- u_char romdata[16], isa16bit = 0, tmp;
+ u_int memsize, n;
+ u_char romdata[16], isa16bit = 0, tmp;
static char test_pattern[32] = "THIS is A memory TEST pattern";
- char test_buffer[32];
+ char test_buffer[32];
sc->asic_addr = isa_dev->id_iobase + ED_NOVELL_ASIC_OFFSET;
sc->nic_addr = isa_dev->id_iobase + ED_NOVELL_NIC_OFFSET;
@@ -869,50 +893,54 @@ ed_probe_Novell(isa_dev)
/* XXX - do Novell-specific probe here */
/* Reset the board */
+#ifdef GWETHER
+ outb(sc->asic_addr + ED_NOVELL_RESET, 0);
+ DELAY(200);
+#endif /* GWETHER */
tmp = inb(sc->asic_addr + ED_NOVELL_RESET);
/*
* 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.]
+ * 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.]
*/
outb(sc->asic_addr + ED_NOVELL_RESET, tmp);
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
+ * 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);
+ 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);
+ return (0);
sc->vendor = ED_VENDOR_NOVELL;
sc->mem_shared = 0;
isa_dev->id_maddr = 0;
/*
- * 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.
+ * 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.
+ * 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_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);
@@ -921,8 +949,8 @@ ed_probe_Novell(isa_dev)
/*
* 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.
+ * 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));
@@ -930,20 +958,21 @@ ed_probe_Novell(isa_dev)
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_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.
+ * 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 */
+ return (0); /* not an NE2000 either */
sc->type = ED_TYPE_NE2000;
sc->type_str = "NE2000";
@@ -951,11 +980,11 @@ ed_probe_Novell(isa_dev)
sc->type = ED_TYPE_NE1000;
sc->type_str = "NE1000";
}
-
+
/* 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 */
+#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;
@@ -969,9 +998,72 @@ ed_probe_Novell(isa_dev)
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 (memcmp(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 (memcmp(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", isa_dev->id_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 (memcmp(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 (memcmp(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", isa_dev->id_unit, mstart, x);
+ return 0;
+ }
+ printf("ed%d: RAM start at %d, size : %d.\n", isa_dev->id_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).
+ * otherwise).
*/
if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING))
sc->txb_cnt = 1;
@@ -985,14 +1077,19 @@ ed_probe_Novell(isa_dev)
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)];
+ sc->arpcom.ac_enaddr[n] = romdata[n * (sc->isa16bit + 1)];
+
+#ifdef GWETHER
+ if (sc->arpcom.ac_enaddr[2] == 0x86)
+ sc->type_str = "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);
+ return (ED_NOVELL_IO_PORTS);
}
-
+
/*
* Install interface into kernel networking data structures
*/
@@ -1004,7 +1101,7 @@ ed_attach(isa_dev)
struct ifnet *ifp = &sc->arpcom.ac_if;
struct ifaddr *ifa;
struct sockaddr_dl *sdl;
-
+
/*
* Set interface to stopped condition (reset)
*/
@@ -1014,7 +1111,7 @@ ed_attach(isa_dev)
* Initialize ifnet structure
*/
ifp->if_unit = isa_dev->id_unit;
- ifp->if_name = "ed" ;
+ ifp->if_name = "ed";
ifp->if_mtu = ETHERMTU;
ifp->if_init = ed_init;
ifp->if_output = ether_output;
@@ -1025,13 +1122,16 @@ ed_attach(isa_dev)
/*
* Set default state for ALTPHYS flag (used to disable the tranceiver
- * for AUI operation), based on compile-time config option.
+ * for AUI operation), based on compile-time config option.
*/
if (isa_dev->id_flags & ED_FLAGS_DISABLE_TRANCEIVER)
ifp->if_flags =
(IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_ALTPHYS);
else
ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS);
+#ifdef MULTICAST
+ ifp->if_flags |= IFF_MULTICAST;
+#endif
/*
* Attach the interface
@@ -1041,20 +1141,22 @@ ed_attach(isa_dev)
/*
* Search down the ifa address list looking for the AF_LINK type entry
*/
- ifa = ifp->if_addrlist;
+ ifa = ifp->if_addrlist;
while ((ifa != 0) && (ifa->ifa_addr != 0) &&
- (ifa->ifa_addr->sa_family != AF_LINK))
+ (ifa->ifa_addr->sa_family != AF_LINK))
ifa = ifa->ifa_next;
+
/*
* If we find an AF_LINK type entry we fill in the hardware address.
- * This is useful for netstat(1) to keep track of which interface
- * is which.
+ * This is useful for netstat(1) to keep track of which interface is
+ * which.
*/
if ((ifa != 0) && (ifa->ifa_addr != 0)) {
+
/*
* Fill in the link-level address for this interface
*/
- sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ sdl = (struct sockaddr_dl *) ifa->ifa_addr;
sdl->sdl_type = IFT_ETHER;
sdl->sdl_alen = ETHER_ADDR_LEN;
sdl->sdl_slen = 0;
@@ -1065,17 +1167,17 @@ ed_attach(isa_dev)
* Print additional info when attached
*/
printf("ed%d: address %s, ", isa_dev->id_unit,
- ether_sprintf(sc->arpcom.ac_enaddr));
+ ether_sprintf(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 ", sc->isa16bit ? "(16 bit)" : "(8 bit)");
printf("%s\n", ((sc->vendor == ED_VENDOR_3COM) &&
- (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : "");
+ (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : "");
/*
* If BPF is in the kernel, call the attach for it
@@ -1085,16 +1187,16 @@ ed_attach(isa_dev)
#endif
return 1;
}
-
+
/*
* Reset interface.
*/
void
ed_reset(unit, uban)
- int unit;
- int uban; /* XXX */
+ int unit;
+ int uban; /* XXX */
{
- int s;
+ int s;
s = splimp();
@@ -1106,29 +1208,30 @@ ed_reset(unit, uban)
(void) splx(s);
}
-
+
/*
* Take interface offline.
*/
void
ed_stop(unit)
- int unit;
+ int unit;
{
struct ed_softc *sc = &ed_softc[unit];
- int n = 5000;
-
+ int n = 5000;
+
/*
* Stop everything on the interface, and select page 0 registers.
*/
if (sc->is790) {
outb(sc->nic_addr + ED_P0_CR, ED_CR_STP);
} else {
- outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP);
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | 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.
+ * 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);
@@ -1140,7 +1243,7 @@ ed_stop(unit)
*/
void
ed_watchdog(unit)
- int unit;
+ int unit;
{
struct ed_softc *sc = &ed_softc[unit];
@@ -1151,25 +1254,26 @@ ed_watchdog(unit)
}
/*
- * Initialize device.
+ * Initialize device.
*/
void
ed_init(unit)
- int unit;
+ int unit;
{
struct ed_softc *sc = &ed_softc[unit];
struct ifnet *ifp = &sc->arpcom.ac_if;
- int i, s;
- u_char command;
+ int i, s;
+ u_char command;
/* address not known */
- if (ifp->if_addrlist == (struct ifaddr *)0) return;
+ 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.
+ * This init procedure is "mandatory"...don't change what or when
+ * things happen.
*/
s = splimp();
@@ -1190,19 +1294,21 @@ ed_init(unit)
if (sc->is790) {
outb(sc->nic_addr + ED_P0_CR, ED_CR_STP);
} else {
- outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP);
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
}
if (sc->isa16bit) {
+
/*
- * Set FIFO threshold to 8, No auto-init Remote DMA,
- * byte order=80x86, word-wide DMA xfers,
+ * 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);
+ 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);
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS);
}
/*
@@ -1210,11 +1316,19 @@ ed_init(unit)
*/
outb(sc->nic_addr + ED_P0_RBCR0, 0);
outb(sc->nic_addr + ED_P0_RBCR1, 0);
+#ifndef MULTICAST
/*
* Enable reception of broadcast packets
*/
outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB);
+#else
+
+ /*
+ * Tell RCR to do nothing for now.
+ */
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON);
+#endif
/*
* Place NIC in internal loopback mode
@@ -1238,41 +1352,62 @@ ed_init(unit)
/*
* Clear all interrupts. A '1' in each bit position clears the
- * corresponding flag.
+ * corresponding flag.
*/
outb(sc->nic_addr + ED_P0_ISR, 0xff);
/*
* Enable the following interrupts: receive/transmit complete,
- * receive/transmit error, and Receiver OverWrite.
- *
+ * 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);
+ ED_IMR_PRXE | ED_IMR_PTXE | ED_IMR_RXEE | ED_IMR_TXEE | ED_IMR_OVWE);
/*
* Program Command Register for page 1
*/
if (sc->is790) {
- outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STP);
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_STP);
} else {
- outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STP);
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_RD2 | ED_CR_STP);
}
+
/*
* Copy out our station address
*/
for (i = 0; i < ETHER_ADDR_LEN; ++i)
outb(sc->nic_addr + ED_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]);
+#ifndef MULTICAST
#if NBPFILTER > 0
+
/*
- * Initialize multicast address hashing registers to accept
- * all multicasts (only used when in promiscuous mode)
+ * Initialize multicast address hashing registers to accept all
+ * multicasts (only used when in promiscuous mode)
*/
for (i = 0; i < 8; ++i)
outb(sc->nic_addr + ED_P1_MAR0 + i, 0xff);
#endif
+#else
+ /* set up multicast addresses and filter modes */
+ if (sc != 0 && (ifp->if_flags & IFF_MULTICAST) != 0) {
+ u_long mcaf[2];
+
+ if ((ifp->if_flags & IFF_ALLMULTI) != 0) {
+ mcaf[0] = 0xffffffff;
+ mcaf[1] = 0xffffffff;
+ } else
+ ds_getmcaf(sc, mcaf);
+
+ /*
+ * Set multicast filter on chip.
+ */
+ for (i = 0; i < 8; i++)
+ outb(sc->nic_addr + ED_P1_MAR0 + i, ((u_char *) mcaf)[i]);
+ }
+#endif
/*
* Set Current Page pointer to next_packet (initialized above)
@@ -1280,14 +1415,22 @@ ed_init(unit)
outb(sc->nic_addr + ED_P1_CURR, sc->next_packet);
/*
- * Set Command Register for page 0, Remote DMA complete,
- * and interface Start.
+ * Set Command Register for page 0, Remote DMA complete, and interface
+ * Start.
*/
if (sc->is790) {
outb(sc->nic_addr + ED_P1_CR, ED_CR_STA);
} else {
- outb(sc->nic_addr + ED_P1_CR, ED_CR_RD2|ED_CR_STA);
+ outb(sc->nic_addr + ED_P1_CR, ED_CR_RD2 | ED_CR_STA);
}
+#ifdef MULTICAST
+
+ /*
+ * Clear all interrupts
+ */
+ outb(sc->nic_addr + ED_P0_ISR, 0xff);
+#endif
+
/*
* Take interface out of loopback
*/
@@ -1295,7 +1438,7 @@ ed_init(unit)
/*
* If this is a 3Com board, the tranceiver must be software enabled
- * (there is no settable hardware default).
+ * (there is no settable hardware default).
*/
if (sc->vendor == ED_VENDOR_3COM) {
if (ifp->if_flags & IFF_ALTPHYS) {
@@ -1304,6 +1447,25 @@ ed_init(unit)
outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
}
}
+#ifdef MULTICAST
+ i = ED_RCR_AB;
+ if (sc != 0) {
+ if ((ifp->if_flags & IFF_PROMISC) != 0) {
+
+ /*
+ * Set promiscuous mode. Also reconfigure the
+ * multicast filter.
+ */
+ int j;
+
+ i |= ED_RCR_PRO | ED_RCR_AM | ED_RCR_AR | ED_RCR_SEP;
+ for (j = 0; j < 8; j++)
+ outb(sc->nic_addr + ED_P1_MAR0 + j, 0xff);
+ }
+ i |= ED_RCR_AM;
+ }
+ outb(sc->nic_addr + ED_P0_RCR, i);
+#endif
/*
* Set 'running' flag, and clear output active flag.
@@ -1318,11 +1480,12 @@ ed_init(unit)
(void) splx(s);
}
-
+
/*
* This routine actually starts the transmission on the interface
*/
-static inline void ed_xmit(ifp)
+static inline void
+ed_xmit(ifp)
struct ifnet *ifp;
{
struct ed_softc *sc = &ed_softc[ifp->if_unit];
@@ -1336,13 +1499,14 @@ static inline void ed_xmit(ifp)
if (sc->is790) {
outb(sc->nic_addr + ED_P0_CR, ED_CR_STA);
} else {
- outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | 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);
+ sc->txb_next_tx * ED_TXBUF_SIZE);
/*
* Set TX length
@@ -1356,10 +1520,10 @@ static inline void ed_xmit(ifp)
if (sc->is790) {
outb(sc->nic_addr + ED_P0_CR, ED_CR_TXP | ED_CR_STA);
} else {
- outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_TXP|ED_CR_STA);
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_TXP | ED_CR_STA);
}
sc->xmit_busy = 1;
-
+
/*
* Point to next transmit buffer slot and wrap if necessary.
*/
@@ -1389,12 +1553,13 @@ ed_start(ifp)
struct ed_softc *sc = &ed_softc[ifp->if_unit];
struct mbuf *m0, *m;
caddr_t buffer;
- int len;
+ int len;
outloop:
+
/*
- * First, see if there are buffered packets and an idle
- * transmitter - should never happen at this point.
+ * 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 buffers, but transmitter idle\n");
@@ -1405,23 +1570,23 @@ outloop:
* 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.
+ * No room. Indicate this to the outside world and exit.
*/
ifp->if_flags |= IFF_OACTIVE;
return;
}
-
IF_DEQUEUE(&sc->arpcom.ac_if.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.
- */
+
+ /*
+ * 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;
}
@@ -1436,45 +1601,49 @@ outloop:
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.
- */
+
+ /*
+ * 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);
+ ED_3COM_GACFR_RSEL);
break;
- /*
- * Enable 16bit access to shared memory on WD/SMC boards
- * Don't update wd_laar_proto because we want to restore the
- * previous state (because an arp reply in the input code
- * may cause a call-back to ed_start)
- * XXX - the call-back to 'start' is a bug, IMHO.
- */
- case ED_VENDOR_WD_SMC: {
- outb(sc->asic_addr + ED_WD_LAAR,
- (sc->wd_laar_proto | ED_WD_LAAR_M16EN));
- (void) inb(0x84);
- if (sc->is790) {
- outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB);
+
+ /*
+ * Enable 16bit access to shared memory on
+ * WD/SMC boards Don't update wd_laar_proto
+ * because we want to restore the previous
+ * state (because an arp reply in the input
+ * code may cause a call-back to ed_start) XXX
+ * - the call-back to 'start' is a bug, IMHO.
+ */
+ case ED_VENDOR_WD_SMC:{
+ outb(sc->asic_addr + ED_WD_LAAR,
+ (sc->wd_laar_proto | ED_WD_LAAR_M16EN));
(void) inb(0x84);
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB);
+ (void) inb(0x84);
+ }
+ (void) inb(0x84);
+ break;
}
- (void) inb(0x84);
- 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;
+ len += m->m_len;
}
/*
@@ -1484,23 +1653,23 @@ outloop:
switch (sc->vendor) {
case ED_VENDOR_3COM:
outb(sc->asic_addr + ED_3COM_GACFR,
- ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0);
+ ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0);
break;
- case ED_VENDOR_WD_SMC: {
- outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto);
- (void) inb(0x84);
- if (sc->is790) {
- outb(sc->asic_addr + ED_WD_MSR, 0x00);
+ case ED_VENDOR_WD_SMC:{
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, 0x00);
+ (void) inb(0x84);
+ }
+ outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto);
(void) inb(0x84);
+ break;
}
- break;
- }
}
}
} else {
len = ed_pio_write_mbufs(sc, m, buffer);
}
-
+
sc->txb_len[sc->txb_new] = MAX(len, ETHER_MIN_LEN);
sc->txb_inuse++;
@@ -1514,40 +1683,37 @@ outloop:
if (sc->xmit_busy == 0)
ed_xmit(ifp);
+
/*
- * If there is BPF support in the configuration, tap off here.
- * The following has support for converting trailer packets
- * back to normal.
- * XXX - support for trailer packets in BPF should be moved into
- * the bpf code proper to avoid code duplication in all of
- * the drivers.
+ * If there is BPF support in the configuration, tap off here. The
+ * following has support for converting trailer packets back to
+ * normal. XXX - support for trailer packets in BPF should be moved
+ * into the bpf code proper to avoid code duplication in all of the
+ * drivers.
*/
#if NBPFILTER > 0
if (sc->bpf) {
u_short etype;
- int off, datasize, resid;
+ int off, datasize, resid;
struct ether_header *eh;
struct trailer_header trailer_header;
- char ether_packet[ETHER_MAX_LEN];
- char *ep;
+ char ether_packet[ETHER_MAX_LEN];
+ char *ep;
ep = ether_packet;
/*
- * We handle trailers below:
- * Copy ether header first, then residual data,
- * then data. Put all this in a temporary buffer
- * 'ether_packet' and send off to bpf. Since the
- * system has generated this packet, we assume
- * that all of the offsets in the packet are
- * correct; if they're not, the system will almost
- * certainly crash in m_copydata.
- * We make no assumptions about how the data is
- * arranged in the mbuf chain (i.e. how much
- * data is in each mbuf, if mbuf clusters are
- * used, etc.), which is why we use m_copydata
- * to get the ether header rather than assume
- * that this is located in the first mbuf.
+ * We handle trailers below: Copy ether header first, then
+ * residual data, then data. Put all this in a temporary
+ * buffer 'ether_packet' and send off to bpf. Since the system
+ * has generated this packet, we assume that all of the
+ * offsets in the packet are correct; if they're not, the
+ * system will almost certainly crash in m_copydata. We make
+ * no assumptions about how the data is arranged in the mbuf
+ * chain (i.e. how much data is in each mbuf, if mbuf clusters
+ * are used, etc.), which is why we use m_copydata to get the
+ * ether header rather than assume that this is located in the
+ * first mbuf.
*/
/* copy ether header */
m_copydata(m0, 0, sizeof(struct ether_header), ep);
@@ -1555,17 +1721,17 @@ outloop:
ep += sizeof(struct ether_header);
etype = ntohs(eh->ether_type);
if (etype >= ETHERTYPE_TRAIL &&
- etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+ etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) {
datasize = ((etype - ETHERTYPE_TRAIL) << 9);
off = datasize + sizeof(struct ether_header);
/* copy trailer_header into a data structure */
m_copydata(m0, off, sizeof(struct trailer_header),
- (caddr_t)&trailer_header.ether_type);
+ (caddr_t) & trailer_header.ether_type);
/* copy residual data */
- m_copydata(m0, off+sizeof(struct trailer_header),
- resid = ntohs(trailer_header.ether_residual) -
+ m_copydata(m0, off + sizeof(struct trailer_header),
+ resid = ntohs(trailer_header.ether_residual) -
sizeof(struct trailer_header), ep);
ep += resid;
@@ -1590,35 +1756,36 @@ outloop:
*/
goto outloop;
}
-
+
/*
* Ethernet interface receiver interrupt.
*/
static inline void
ed_rint(unit)
- int unit;
+ int unit;
{
register struct ed_softc *sc = &ed_softc[unit];
- u_char boundry, current;
+ u_char boundry, current;
u_short len;
struct ed_ring packet_hdr;
- char *packet_ptr;
+ char *packet_ptr;
/*
* Set NIC to page 1 registers to get 'current' pointer
*/
if (sc->is790) {
- outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STA);
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_STA);
} else {
- outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA);
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_RD2 | 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).
+ * '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)) {
@@ -1628,25 +1795,28 @@ ed_rint(unit)
/*
* The byte count includes the FCS - Frame Check Sequence (a
- * 32 bit CRC).
+ * 32 bit CRC).
*/
if (sc->mem_shared)
- packet_hdr = *(struct ed_ring *)packet_ptr;
+ packet_hdr = *(struct ed_ring *) packet_ptr;
else
ed_pio_readmem(sc, packet_ptr, (char *) &packet_hdr,
- sizeof(packet_hdr));
+ sizeof(packet_hdr));
len = packet_hdr.count;
if ((len >= ETHER_MIN_LEN) && (len <= ETHER_MAX_LEN)) {
+
/*
* Go get packet. len - 4 removes CRC from length.
*/
ed_get_packet(sc, packet_ptr + 4, len - 4);
++sc->arpcom.ac_if.if_ipackets;
} else {
+
/*
- * Really BAD...probably indicates that the ring pointers
- * are corrupted. Also seen on early rev chips under
- * high load - the byte order of the length gets switched.
+ * Really BAD...probably indicates that the ring
+ * pointers are corrupted. Also seen on early rev
+ * chips under high load - the byte order of the
+ * length gets switched.
*/
log(LOG_ERR,
"ed%d: NIC memory corrupt - invalid packet length %d\n",
@@ -1662,8 +1832,8 @@ ed_rint(unit)
sc->next_packet = packet_hdr.next_packet;
/*
- * Update NIC boundry pointer - being careful to keep it
- * one buffer behind. (as recommended by NS databook)
+ * 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)
@@ -1675,18 +1845,18 @@ ed_rint(unit)
if (sc->is790) {
outb(sc->nic_addr + ED_P0_CR, ED_CR_STA);
} else {
- outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | 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)
+ * Set NIC to page 1 registers before looping to top (prepare
+ * to get 'CURR' current pointer)
*/
if (sc->is790) {
- outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STA);
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_STA);
} else {
- outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA);
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_RD2 | ED_CR_STA);
}
}
}
@@ -1696,10 +1866,10 @@ ed_rint(unit)
*/
void
edintr(unit)
- int unit;
+ int unit;
{
struct ed_softc *sc = &ed_softc[unit];
- u_char isr;
+ u_char isr;
/*
* Set NIC to page 0 registers
@@ -1707,36 +1877,36 @@ edintr(unit)
if (sc->is790) {
outb(sc->nic_addr + ED_P0_CR, ED_CR_STA);
} else {
- outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA);
}
+
/*
* loop until there are no more new interrupts
*/
while (isr = inb(sc->nic_addr + ED_P0_ISR)) {
/*
- * 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)
+ * 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.
+ * 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;
+ 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.
+ * 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) {
@@ -1746,8 +1916,9 @@ edintr(unit)
*/
if ((inb(sc->nic_addr + ED_P0_TSR) & ED_TSR_ABT)
&& (collisions == 0)) {
+
/*
- * When collisions total 16, the
+ * When collisions total 16, the
* P0_NCR will indicate 0, and the
* TSR_ABT is set.
*/
@@ -1759,9 +1930,10 @@ edintr(unit)
*/
++sc->arpcom.ac_if.if_oerrors;
} else {
+
/*
* Update total number of successfully
- * transmitted packets.
+ * transmitted packets.
*/
++sc->arpcom.ac_if.if_opackets;
}
@@ -1779,16 +1951,16 @@ edintr(unit)
/*
* Add in total number of collisions on last
- * transmission.
+ * transmission.
*/
sc->arpcom.ac_if.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
+ * 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->arpcom.ac_if);
@@ -1797,110 +1969,111 @@ edintr(unit)
/*
* 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_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) {
++sc->arpcom.ac_if.if_ierrors;
#ifdef DIAGNOSTIC
log(LOG_WARNING,
- "ed%d: warning - receiver ring buffer overrun\n",
- unit);
+ "ed%d: warning - receiver ring buffer overrun\n",
+ unit);
#endif
+
/*
* Stop/reset/re-init NIC
*/
ed_reset(unit, 0);
} else {
- /*
- * Receiver Error. One or more of: CRC error, frame
- * alignment error FIFO overrun, or missed packet.
- */
+ /*
+ * Receiver Error. One or more of: CRC error,
+ * frame alignment error FIFO overrun, or
+ * missed packet.
+ */
if (isr & ED_ISR_RXE) {
++sc->arpcom.ac_if.if_ierrors;
#ifdef ED_DEBUG
printf("ed%d: receive error %x\n", unit,
- inb(sc->nic_addr + ED_P0_RSR));
+ 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).
+ * 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.
+ * 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));
+ ED_WD_LAAR_M16EN));
(void) inb(0x84);
if (sc->is790) {
outb(sc->asic_addr + ED_WD_MSR,
- ED_WD_MSR_MENB);
+ ED_WD_MSR_MENB);
(void) inb(0x84);
}
}
-
- ed_rint (unit);
+ ed_rint(unit);
/* disable 16bit access */
if (sc->isa16bit &&
- (sc->vendor == ED_VENDOR_WD_SMC)) {
+ (sc->vendor == ED_VENDOR_WD_SMC)) {
- outb(sc->asic_addr + ED_WD_LAAR,
- (sc->wd_laar_proto &=
- ~ED_WD_LAAR_M16EN));
- (void) inb(0x84);
if (sc->is790) {
outb(sc->asic_addr + ED_WD_MSR, 0x00);
(void) inb(0x84);
}
+ outb(sc->asic_addr + ED_WD_LAAR,
+ (sc->wd_laar_proto &=
+ ~ED_WD_LAAR_M16EN));
+ (void) inb(0x84);
}
}
}
/*
* 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.
+ * attempt to start output on the interface. This is done
+ * after handling the receiver to give the receiver priority.
*/
if ((sc->arpcom.ac_if.if_flags & IFF_OACTIVE) == 0)
ed_start(&sc->arpcom.ac_if);
/*
- * 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)
+ * 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)
*/
if (sc->is790) {
outb(sc->nic_addr + ED_P0_CR, ED_CR_STA);
} else {
- outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | 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 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);
@@ -1909,7 +2082,7 @@ edintr(unit)
}
}
}
-
+
/*
* Process an ioctl request. This code needs some work - it looks
* pretty ugly.
@@ -1917,13 +2090,13 @@ edintr(unit)
int
ed_ioctl(ifp, command, data)
register struct ifnet *ifp;
- int command;
+ int command;
caddr_t data;
{
- register struct ifaddr *ifa = (struct ifaddr *)data;
+ register struct ifaddr *ifa = (struct ifaddr *) data;
struct ed_softc *sc = &ed_softc[ifp->if_unit];
- struct ifreq *ifr = (struct ifreq *)data;
- int s, error = 0;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int s, error = 0;
s = splimp();
@@ -1936,41 +2109,40 @@ ed_ioctl(ifp, command, data)
#ifdef INET
case AF_INET:
ed_init(ifp->if_unit); /* before arpwhohas */
+
/*
- * See if another station has *our* IP address.
- * i.e.: There is an address conflict! If a
- * conflict exists, a message is sent to the
- * console.
+ * See if another station has *our* IP address. i.e.:
+ * There is an address conflict! If a conflict exists,
+ * a message is sent to the console.
*/
- ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr;
- arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+ ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *) ifp, &IA_SIN(ifa)->sin_addr);
break;
#endif
#ifdef NS
- /*
- * XXX - This code is probably wrong
- */
+
+ /*
+ * 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 {
- /*
- *
+ {
+ 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
*/
- bcopy((caddr_t)ina->x_host.c_host,
- (caddr_t)sc->arpcom.ac_enaddr,
- sizeof(sc->arpcom.ac_enaddr));
+ ed_init(ifp->if_unit);
+ break;
}
- /*
- * Set new address
- */
- ed_init(ifp->if_unit);
- break;
- }
#endif
default:
ed_init(ifp->if_unit);
@@ -1981,13 +2153,15 @@ ed_ioctl(ifp, command, data)
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);
+
+ 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 interface is marked down and it is running, then stop it
*/
@@ -1996,37 +2170,42 @@ ed_ioctl(ifp, command, data)
ed_stop(ifp->if_unit);
ifp->if_flags &= ~IFF_RUNNING;
} else {
- /*
- * If interface is marked up and it is stopped, then start it
- */
+
+ /*
+ * If interface is marked up and it is stopped, then
+ * start it
+ */
if ((ifp->if_flags & IFF_UP) &&
- ((ifp->if_flags & IFF_RUNNING) == 0))
+ ((ifp->if_flags & IFF_RUNNING) == 0))
ed_init(ifp->if_unit);
}
+#ifndef MULTICAST
#if NBPFILTER > 0
if (ifp->if_flags & IFF_PROMISC) {
+
/*
- * Set promiscuous mode on interface.
- * XXX - for multicasts to work, we would need to
- * write 1's in all bits of multicast
- * hashing array. For now we assume that
- * this was done in ed_init().
+ * Set promiscuous mode on interface. XXX - for
+ * multicasts to work, we would need to write 1's in
+ * all bits of multicast hashing array. For now we
+ * assume that this was done in ed_init().
*/
outb(sc->nic_addr + ED_P0_RCR,
- ED_RCR_PRO|ED_RCR_AM|ED_RCR_AB);
+ ED_RCR_PRO | ED_RCR_AM | ED_RCR_AB);
} else {
+
/*
* XXX - for multicasts to work, we would need to
- * rewrite the multicast hashing array with the
- * proper hash (would have been destroyed above).
+ * rewrite the multicast hashing array with the proper
+ * hash (would have been destroyed above).
*/
outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB);
}
#endif
+
/*
- * An unfortunate hack to provide the (required) software control
- * of the tranceiver for 3Com boards. The ALTPHYS flag disables
- * the tranceiver if set.
+ * 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) {
@@ -2035,16 +2214,37 @@ ed_ioctl(ifp, command, data)
outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
}
}
-
break;
+#else
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+
+ /*
+ * Update out multicast list.
+ */
+ error = (command == SIOCADDMULTI) ?
+ ether_addmulti((struct ifreq *) data, &sc->arpcom) :
+ ether_delmulti((struct ifreq *) data, &sc->arpcom);
+ if (error == ENETRESET) {
+
+ /*
+ * Multicast list has changed; set the hardware filter
+ * accordingly.
+ */
+ ed_stop(ifp->if_unit); /* XXX for ds_setmcaf? */
+ ed_init(ifp->if_unit);
+ error = 0;
+ }
+ break;
+#endif
default:
error = EINVAL;
}
(void) splx(s);
return (error);
}
-
+
/*
* Macro to calculate a new address within shared memory when given an offset
* from an address, taking into account ring-wrap.
@@ -2062,13 +2262,13 @@ ed_ioctl(ifp, command, data)
static void
ed_get_packet(sc, buf, len)
struct ed_softc *sc;
- char *buf;
+ char *buf;
u_short len;
{
struct ether_header *eh;
- struct mbuf *m, *head = 0, *ed_ring_to_mbuf();
+ struct mbuf *m, *head = 0, *ed_ring_to_mbuf();
u_short off;
- int resid;
+ int resid;
u_short etype;
struct trailer_header trailer_header;
@@ -2086,8 +2286,8 @@ ed_get_packet(sc, buf, len)
#define EOFF (EROUND - sizeof(struct ether_header))
/*
- * The following assumes there is room for
- * the ether header in the header mbuf
+ * The following assumes there is room for the ether header in the
+ * header mbuf
*/
head->m_data += EOFF;
eh = mtod(head, struct ether_header *);
@@ -2101,18 +2301,16 @@ ed_get_packet(sc, buf, len)
head->m_len += sizeof(struct ether_header);
len -= sizeof(struct ether_header);
- etype = ntohs((u_short)eh->ether_type);
+ etype = ntohs((u_short) eh->ether_type);
/*
- * Deal with trailer protocol:
- * If trailer protocol, calculate the datasize as 'off',
- * which is also the offset to the trailer header.
- * Set resid to the amount of packet data following the
- * trailer header.
- * Finally, copy residual data into mbuf chain.
+ * Deal with trailer protocol: If trailer protocol, calculate the
+ * datasize as 'off', which is also the offset to the trailer header.
+ * Set resid to the amount of packet data following the trailer
+ * header. Finally, copy residual data into mbuf chain.
*/
if (etype >= ETHERTYPE_TRAIL &&
- etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+ etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) {
off = (etype - ETHERTYPE_TRAIL) << 9;
if ((off + sizeof(struct trailer_header)) > len)
@@ -2120,14 +2318,15 @@ ed_get_packet(sc, buf, len)
/*
* If we have shared memory, we can get info directly from the
- * stored packet, otherwise we must get a local copy
- * of the trailer header using PIO.
+ * stored packet, otherwise we must get a local copy of the
+ * trailer header using PIO.
*/
if (sc->mem_shared) {
eh->ether_type = *ringoffset(sc, buf, off, u_short *);
- resid = ntohs(*ringoffset(sc, buf, off+2, u_short *));
+ resid = ntohs(*ringoffset(sc, buf, off + 2, u_short *));
} else {
struct trailer_header trailer_header;
+
ed_pio_readmem(sc,
ringoffset(sc, buf, off, caddr_t),
(char *) &trailer_header,
@@ -2136,30 +2335,35 @@ ed_get_packet(sc, buf, len)
resid = trailer_header.ether_residual;
}
- if ((off + resid) > len) goto bad; /* insanity */
+ if ((off + resid) > len)
+ goto bad; /* insanity */
resid -= sizeof(struct trailer_header);
- if (resid < 0) goto bad; /* insanity */
+ if (resid < 0)
+ goto bad; /* insanity */
- m = ed_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *),
+ m = ed_ring_to_mbuf(sc, ringoffset(sc, buf, off + 4, char *),
head, resid);
- if (m == 0) goto bad;
+ if (m == 0)
+ goto bad;
len = off;
- head->m_pkthdr.len -= 4; /* subtract trailer header */
+ head->m_pkthdr.len -= 4; /* subtract trailer header */
}
/*
- * Pull packet off interface. Or if this was a trailer packet,
- * the data portion is appended.
+ * Pull packet off interface. Or if this was a trailer packet, the
+ * data portion is appended.
*/
m = ed_ring_to_mbuf(sc, buf, m, len);
- if (m == 0) goto bad;
+ if (m == 0)
+ goto bad;
#if NBPFILTER > 0
+
/*
- * Check if there's a BPF listener on this interface.
- * If so, hand off the raw packet to bpf.
+ * Check if there's a BPF listener on this interface. If so, hand off
+ * the raw packet to bpf.
*/
if (sc->bpf) {
bpf_mtap(sc->bpf, head);
@@ -2168,17 +2372,17 @@ ed_get_packet(sc, buf, len)
* 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.
- *
+ *
* XXX This test does not support multicasts.
*/
if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) &&
bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
- sizeof(eh->ether_dhost)) != 0 &&
+ sizeof(eh->ether_dhost)) != 0 &&
bcmp(eh->ether_dhost, etherbroadcastaddr,
sizeof(eh->ether_dhost)) != 0) {
- m_freem(head);
- return;
+ m_freem(head);
+ return;
}
}
#endif
@@ -2213,8 +2417,8 @@ bad: if (head)
* This routine is currently Novell-specific.
*/
void
-ed_pio_readmem(sc,src,dst,amount)
- struct ed_softc *sc;
+ed_pio_readmem(sc, src, dst, amount)
+ struct ed_softc *sc;
unsigned short src;
unsigned char *dst;
unsigned short amount;
@@ -2222,24 +2426,25 @@ ed_pio_readmem(sc,src,dst,amount)
unsigned short tmp_amount;
/* select page 0 registers */
- outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA);
/* round up to a word */
tmp_amount = amount;
- if (amount & 1) ++amount;
+ 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);
+ 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_RSAR1, src >> 8);
outb(sc->nic_addr + ED_P0_CR, ED_CR_RD0 | ED_CR_STA);
if (sc->isa16bit) {
- insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount/2);
+ insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount / 2);
} else
insb(sc->asic_addr + ED_NOVELL_DATA, dst, amount);
@@ -2251,41 +2456,42 @@ ed_pio_readmem(sc,src,dst,amount)
* be even.
*/
void
-ed_pio_writemem(sc,src,dst,len)
+ed_pio_writemem(sc, src, dst, len)
struct ed_softc *sc;
- char *src;
+ char *src;
unsigned short dst;
unsigned short len;
{
- int maxwait=100; /* about 120us */
+ int maxwait = 100; /* about 120us */
/* select page 0 registers */
- outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ 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);
+ 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);
+ 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);
if (sc->isa16bit)
- outsw(sc->asic_addr + ED_NOVELL_DATA, src, len/2);
+ outsw(sc->asic_addr + ED_NOVELL_DATA, src, len / 2);
else
outsb(sc->asic_addr + ED_NOVELL_DATA, src, len);
+
/*
* 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.
+ * 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);
}
@@ -2295,7 +2501,7 @@ ed_pio_writemem(sc,src,dst,len)
* programmed I/O.
*/
u_short
-ed_pio_write_mbufs(sc,m,dst)
+ed_pio_write_mbufs(sc, m, dst)
struct ed_softc *sc;
struct mbuf *m;
unsigned short dst;
@@ -2303,40 +2509,40 @@ ed_pio_write_mbufs(sc,m,dst)
unsigned short len, mb_offset;
struct mbuf *mp;
unsigned char residual[2];
- int maxwait=100; /* about 120us */
+ int maxwait = 100; /* about 120us */
/* First, count up the total number of bytes to copy */
for (len = 0, mp = m; mp; mp = mp->m_next)
len += mp->m_len;
-
+
/* select page 0 registers */
- outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA);
+ 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);
+ 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);
+ 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);
mb_offset = 0;
+
/*
- * Transfer the mbuf chain to the NIC memory.
- * The following code isn't too pretty. The problem is that we can only
- * transfer words to the board, and if an mbuf has an odd number
- * of bytes in it, this is a problem. It's not a simple matter of
- * just removing a byte from the next mbuf (adjusting data++ and
- * len--) because this will hose-over the mbuf chain which might
- * be needed later for BPF. Instead, we maintain an offset
- * (mb_offset) which let's us skip over the first byte in the
- * following mbuf.
+ * Transfer the mbuf chain to the NIC memory. The following code isn't
+ * too pretty. The problem is that we can only transfer words to the
+ * board, and if an mbuf has an odd number of bytes in it, this is a
+ * problem. It's not a simple matter of just removing a byte from the
+ * next mbuf (adjusting data++ and len--) because this will hose-over
+ * the mbuf chain which might be needed later for BPF. Instead, we
+ * maintain an offset (mb_offset) which let's us skip over the first
+ * byte in the following mbuf.
*/
while (m) {
if (m->m_len - mb_offset) {
@@ -2347,24 +2553,27 @@ ed_pio_write_mbufs(sc,m,dst)
(m->m_len - mb_offset) / 2);
/*
- * if odd number of bytes, get the odd byte from
- * the next mbuf with data
+ * if odd number of bytes, get the odd byte
+ * from the next mbuf with data
*/
if ((m->m_len - mb_offset) & 1) {
/* first the last byte in current mbuf */
residual[0] = *(mtod(m, caddr_t) +
- m->m_len - 1);
-
+ m->m_len - 1);
+
/* advance past any empty mbufs */
while (m->m_next && (m->m_next->m_len == 0))
m = m->m_next;
if (m->m_next) {
- /* remove first byte in next mbuf */
+
+ /*
+ * remove first byte in next
+ * mbuf
+ */
residual[1] = *(mtod(m->m_next, caddr_t));
mb_offset = 1;
}
-
outw(sc->asic_addr + ED_NOVELL_DATA,
*((unsigned short *) residual));
} else
@@ -2378,10 +2587,10 @@ ed_pio_write_mbufs(sc,m,dst)
/*
* 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.
+ * 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);
@@ -2390,23 +2599,22 @@ ed_pio_write_mbufs(sc,m,dst)
sc->arpcom.ac_if.if_unit);
ed_reset(sc->arpcom.ac_if.if_unit, 0);
}
-
- return(len);
+ return (len);
}
-
+
/*
* 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)
+ed_ring_copy(sc, src, dst, amount)
struct ed_softc *sc;
- char *src;
- char *dst;
- u_short amount;
+ char *src;
+ char *dst;
+ u_short amount;
{
- u_short tmp_amount;
+ u_short tmp_amount;
/* does copy wrap to lower addr in ring buffer? */
if (src + amount > sc->mem_end) {
@@ -2414,21 +2622,20 @@ ed_ring_copy(sc,src,dst,amount)
/* copy amount up to end of NIC memory */
if (sc->mem_shared)
- bcopy(src,dst,tmp_amount);
+ bcopy(src, dst, tmp_amount);
else
- ed_pio_readmem(sc,src,dst,tmp_amount);
+ ed_pio_readmem(sc, 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, src, dst, amount);
- return(src + amount);
+ return (src + amount);
}
/*
@@ -2441,9 +2648,9 @@ ed_ring_copy(sc,src,dst,amount)
* amount = amount of data to copy
*/
struct mbuf *
-ed_ring_to_mbuf(sc,src,dst,total_len)
+ed_ring_to_mbuf(sc, src, dst, total_len)
struct ed_softc *sc;
- char *src;
+ char *src;
struct mbuf *dst;
u_short total_len;
{
@@ -2452,18 +2659,19 @@ ed_ring_to_mbuf(sc,src,dst,total_len)
while (total_len) {
register u_short amount = min(total_len, M_TRAILINGSPACE(m));
- if (amount == 0) { /* no more data in this mbuf, alloc another */
+ if (amount == 0) { /* no more data in this mbuf, alloc
+ * another */
+
/*
- * If there is enough data for an mbuf cluster, attempt
- * to allocate one of those, otherwise, a regular
- * mbuf will do.
- * Note that a regular mbuf is always required, even if
- * we get a cluster - getting a cluster does not
- * allocate any mbufs, and one is needed to assign
- * the cluster to. The mbuf that has a cluster
- * extension can not be used to contain data - only
- * the cluster can contain data.
- */
+ * If there is enough data for an mbuf cluster,
+ * attempt to allocate one of those, otherwise, a
+ * regular mbuf will do. Note that a regular mbuf is
+ * always required, even if we get a cluster - getting
+ * a cluster does not allocate any mbufs, and one is
+ * needed to assign the cluster to. The mbuf that has
+ * a cluster extension can not be used to contain data
+ * - only the cluster can contain data.
+ */
dst = m;
MGET(m, M_DONTWAIT, MT_DATA);
if (m == 0)
@@ -2476,7 +2684,6 @@ ed_ring_to_mbuf(sc,src,dst,total_len)
dst->m_next = m;
amount = min(total_len, M_TRAILINGSPACE(m));
}
-
src = ed_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount);
m->m_len += amount;
@@ -2485,4 +2692,62 @@ ed_ring_to_mbuf(sc,src,dst,total_len)
}
return (m);
}
+#ifdef MULTICAST
+/*
+ * Compute crc for ethernet address
+ */
+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.
+ */
+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, 6) >> 26;
+ af[index >> 3] |= 1 << (index & 7);
+
+ ETHER_NEXT_MULTI(step, enm);
+ }
+}
+#endif
#endif
diff --git a/sys/i386/isa/if_edreg.h b/sys/i386/isa/if_edreg.h
index 49f7816a4d4e..d2a03f5b508b 100644
--- a/sys/i386/isa/if_edreg.h
+++ b/sys/i386/isa/if_edreg.h
@@ -1,7 +1,7 @@
/*
* National Semiconductor DS8390 NIC register definitions
*
- * $Id: if_edreg.h,v 1.13.2.1 1994/04/17 06:07:24 rgrimes Exp $
+ * $Id: if_edreg.h,v 1.14 1994/04/10 20:06:28 davidg Exp $
*
* Modification history
*
diff --git a/sys/i386/isa/if_el.c b/sys/i386/isa/if_el.c
new file mode 100644
index 000000000000..d0de274baf73
--- /dev/null
+++ b/sys/i386/isa/if_el.c
@@ -0,0 +1,800 @@
+/* Copyright (c) 1994, Matthew E. Kimmel. Permission is hereby granted
+ * to use, copy, modify and distribute this software 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.
+ *
+ * Questions, comments, bug reports and fixes to kimmel@cs.umass.edu.
+ */
+/* Except of course for the portions of code lifted from other FreeBSD
+ * drivers (mainly elread, elget and el_ioctl)
+ */
+/* 3COM Etherlink 3C501 device driver for FreeBSD */
+/* Yeah, I know these cards suck, but you can also get them for free
+ * really easily...
+ */
+/* Bugs/possible improvements:
+ * - Does not currently support DMA
+ * - Does not currently support multicasts
+ */
+#include "el.h"
+#if NEL > 0
+#include "bpfilter.h"
+
+#include "param.h"
+#include "systm.h"
+#include "errno.h"
+#include "ioctl.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "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 NS
+#include "netns/ns.h"
+#include "netns/ns_if.h"
+#endif
+
+#if NBPFILTER > 0
+#include "net/bpf.h"
+#include "net/bpfdesc.h"
+#endif
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/if_elreg.h"
+
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+
+/* For debugging convenience */
+#ifdef EL_DEBUG
+#define dprintf(x) printf x
+#else
+#define dprintf(x)
+#endif
+
+/* el_softc: per line info and status */
+struct el_softc {
+ struct arpcom arpcom; /* Ethernet common */
+ u_short el_base; /* Base I/O addr */
+ caddr_t bpf; /* BPF magic cookie */
+ char el_pktbuf[EL_BUFSIZ]; /* Frame buffer */
+} el_softc[NEL];
+
+/* Prototypes */
+int el_attach(struct isa_device *);
+void el_init(int);
+void elintr(int);
+int el_ioctl(struct ifnet *,int,caddr_t);
+int el_probe(struct isa_device *);
+void el_start(struct ifnet *);
+void el_reset(int,int);
+void el_watchdog(int);
+
+static void el_stop(int);
+static int el_xmit(struct el_softc *,int);
+static inline void elread(struct el_softc *,caddr_t,int);
+static struct mbuf *elget(caddr_t,int,int,struct ifnet *);
+static inline void el_hardreset(int);
+
+/* isa_driver structure for autoconf */
+struct isa_driver eldriver = {
+ el_probe, el_attach, "el"
+};
+
+/* Probe routine. See if the card is there and at the right place. */
+int el_probe(struct isa_device *idev)
+{
+ struct el_softc *sc;
+ u_short base; /* Just for convenience */
+ u_char station_addr[ETHER_ADDR_LEN];
+ int i;
+
+ /* Grab some info for our structure */
+ sc = &el_softc[idev->id_unit];
+ sc->el_base = idev->id_iobase;
+ base = sc->el_base;
+
+ /* First check the base */
+ if((base < 0x280) || (base > 0x3f0)) {
+ printf("el%d: ioaddr must be between 0x280 and 0x3f0\n",
+ idev->id_unit);
+ return(0);
+ }
+
+ /* Now attempt to grab the station address from the PROM
+ * and see if it contains the 3com vendor code.
+ */
+ dprintf(("Probing 3c501 at 0x%x...\n",base));
+
+ /* Reset the board */
+ dprintf(("Resetting board...\n"));
+ outb(base+EL_AC,EL_AC_RESET);
+ DELAY(5);
+ outb(base+EL_AC,0);
+ dprintf(("Reading station address...\n"));
+ /* Now read the address */
+ for(i=0;i<ETHER_ADDR_LEN;i++) {
+ outb(base+EL_GPBL,i);
+ station_addr[i] = inb(base+EL_EAW);
+ }
+ dprintf(("Address is %s\n",ether_sprintf(station_addr)));
+
+ /* If the vendor code is ok, return a 1. We'll assume that
+ * whoever configured this system is right about the IRQ.
+ */
+ if((station_addr[0] != 0x02) || (station_addr[1] != 0x60)
+ || (station_addr[2] != 0x8c)) {
+ dprintf(("Bad vendor code.\n"));
+ return(0);
+ } else {
+ dprintf(("Vendor code ok.\n"));
+ /* Copy the station address into the arpcom structure */
+ bcopy(station_addr,sc->arpcom.ac_enaddr,ETHER_ADDR_LEN);
+ return(1);
+ }
+}
+
+/* Attach the interface to the kernel data structures. By the time
+ * this is called, we know that the card exists at the given I/O address.
+ * We still assume that the IRQ given is correct.
+ */
+int el_attach(struct isa_device *idev)
+{
+ struct el_softc *sc;
+ struct ifnet *ifp;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+ u_short base;
+ int t;
+
+ dprintf(("Attaching el%d...\n",idev->id_unit));
+
+ /* Get things pointing to the right places. */
+ sc = &el_softc[idev->id_unit];
+ ifp = &sc->arpcom.ac_if;
+ base = sc->el_base;
+
+ /* Now reset the board */
+ dprintf(("Resetting board...\n"));
+ el_hardreset(idev->id_unit);
+
+ /* Initialize ifnet structure */
+ ifp->if_unit = idev->id_unit;
+ ifp->if_name = "el";
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_init = el_init;
+ ifp->if_output = ether_output;
+ ifp->if_start = el_start;
+ ifp->if_ioctl = el_ioctl;
+ ifp->if_reset = el_reset;
+ ifp->if_watchdog = el_watchdog;
+ ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS);
+
+ /* Now we can attach the interface */
+ dprintf(("Attaching interface...\n"));
+ if_attach(ifp);
+
+ /* Put the station address in the ifa address list's AF_LINK
+ * entry, if any.
+ */
+ ifa = ifp->if_addrlist;
+ while ((ifa != NULL) && (ifa->ifa_addr != NULL) &&
+ (ifa->ifa_addr->sa_family != AF_LINK))
+ ifa = ifa->ifa_next;
+ if((ifa != NULL) && (ifa->ifa_addr != NULL)) {
+ 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);
+ }
+
+ /* Print out some information for the user */
+ printf("el%d: 3c501 address %s\n",idev->id_unit,
+ ether_sprintf(sc->arpcom.ac_enaddr));
+
+ /* Finally, attach to bpf filter if it is present. */
+#if NBPFILTER > 0
+ dprintf(("Attaching to BPF...\n"));
+ bpfattach(&sc->bpf,ifp,DLT_EN10MB,sizeof(struct ether_header));
+#endif
+
+ dprintf(("el_attach() finished.\n"));
+ return(1);
+}
+
+/* This routine resets the interface. */
+void el_reset(int unit,int uban)
+{
+ int s;
+
+ dprintf(("elreset()\n"));
+ s = splimp();
+ el_stop(unit);
+ el_init(unit);
+ splx(s);
+}
+
+static void el_stop(int unit)
+{
+ struct el_softc *sc;
+
+ sc = &el_softc[unit];
+ outb(sc->el_base+EL_AC,0);
+}
+
+/* Do a hardware reset of the 3c501. Do not call until after el_probe()! */
+static inline void el_hardreset(int unit)
+{
+ register struct el_softc *sc;
+ register int base;
+ register int j;
+
+ sc = &el_softc[unit];
+ base = sc->el_base;
+
+ /* First reset the board */
+ outb(base+EL_AC,EL_AC_RESET);
+ DELAY(5);
+ outb(base+EL_AC,0);
+
+ /* Then give it back its ethernet address. Thanks to the mach
+ * source code for this undocumented goodie...
+ */
+ for(j=0;j<ETHER_ADDR_LEN;j++)
+ outb(base+j,sc->arpcom.ac_enaddr[j]);
+}
+
+/* Initialize interface. */
+void el_init(int unit)
+{
+ struct el_softc *sc;
+ struct ifnet *ifp;
+ int s;
+ u_short base;
+
+ /* Set up pointers */
+ sc = &el_softc[unit];
+ ifp = &sc->arpcom.ac_if;
+ base = sc->el_base;
+
+ /* If address not known, do nothing. */
+ if(ifp->if_addrlist == (struct ifaddr *)0)
+ return;
+
+ s = splimp();
+
+ /* First, reset the board. */
+ dprintf(("Resetting board...\n"));
+ el_hardreset(unit);
+
+ /* Configure rx */
+ dprintf(("Configuring rx...\n"));
+ if(ifp->if_flags & IFF_PROMISC)
+ outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
+ else
+ outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
+ outb(base+EL_RBC,0);
+
+ /* Configure TX */
+ dprintf(("Configuring tx...\n"));
+ outb(base+EL_TXC,0);
+
+ /* Start reception */
+ dprintf(("Starting reception...\n"));
+ outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
+
+ /* Set flags appropriately */
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ /* And start output. */
+ el_start(ifp);
+
+ splx(s);
+}
+
+/* Start output on interface. Get datagrams from the queue and output
+ * them, giving the receiver a chance between datagrams. Call only
+ * from splimp or interrupt level!
+ */
+void el_start(struct ifnet *ifp)
+{
+ struct el_softc *sc;
+ u_short base;
+ struct mbuf *m, *m0;
+ int s, i, len, retries, done;
+
+ /* Get things pointing in the right directions */
+ sc = &el_softc[ifp->if_unit];
+ base = sc->el_base;
+
+ dprintf(("el_start()...\n"));
+ s = splimp();
+
+ /* Don't do anything if output is active */
+ if(sc->arpcom.ac_if.if_flags & IFF_OACTIVE)
+ return;
+ sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;
+
+ /* The main loop. They warned me against endless loops, but
+ * would I listen? NOOO....
+ */
+ while(1) {
+ /* Dequeue the next datagram */
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd,m0);
+
+ /* If there's nothing to send, return. */
+ if(m0 == NULL) {
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+ splx(s);
+ return;
+ }
+
+ /* Disable the receiver */
+ outb(base+EL_AC,EL_AC_HOST);
+ outb(base+EL_RBC,0);
+
+ /* Copy the datagram to the buffer. */
+ len = 0;
+ for(m = m0; m != NULL; m = m->m_next) {
+ if(m->m_len == 0)
+ continue;
+ bcopy(mtod(m,caddr_t),sc->el_pktbuf+len,m->m_len);
+ len += m->m_len;
+ }
+ m_freem(m0);
+
+ len = MAX(len,ETHER_MIN_LEN);
+
+ /* Give the packet to the bpf, if any */
+#if NBPFILTER > 0
+ if(sc->bpf)
+ bpf_tap(sc->bpf,sc->el_pktbuf,len);
+#endif
+
+ /* Transfer datagram to board */
+ dprintf(("el: xfr pkt length=%d...\n",len));
+ i = EL_BUFSIZ - len;
+ outb(base+EL_GPBL,(i & 0xff));
+ outb(base+EL_GPBH,((i>>8)&0xff));
+ outsb(base+EL_BUF,sc->el_pktbuf,len);
+
+ /* Now transmit the datagram */
+ retries=0;
+ done=0;
+ while(!done) {
+ if(el_xmit(sc,len)) { /* Something went wrong */
+ done = -1;
+ break;
+ }
+ /* Check out status */
+ i = inb(base+EL_TXS);
+ dprintf(("tx status=0x%x\n",i));
+ if(!(i & EL_TXS_READY)) {
+ dprintf(("el: err txs=%x\n",i));
+ sc->arpcom.ac_if.if_oerrors++;
+ if(i & (EL_TXS_COLL|EL_TXS_COLL16)) {
+ if((!(i & EL_TXC_DCOLL16)) && retries < 15) {
+ retries++;
+ outb(base+EL_AC,EL_AC_HOST);
+ }
+ }
+ else
+ done = 1;
+ }
+ else {
+ sc->arpcom.ac_if.if_opackets++;
+ done = 1;
+ }
+ }
+ if(done == -1) /* Packet not transmitted */
+ continue;
+
+ /* Now give the card a chance to receive.
+ * Gotta love 3c501s...
+ */
+ (void)inb(base+EL_AS);
+ outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
+ splx(s);
+ /* Interrupt here */
+ s = splimp();
+ }
+}
+
+/* This function actually attempts to transmit a datagram downloaded
+ * to the board. Call at splimp or interrupt, after downloading data!
+ * Returns 0 on success, non-0 on failure
+ */
+static int el_xmit(struct el_softc *sc,int len)
+{
+ int gpl;
+ int i;
+
+ gpl = EL_BUFSIZ - len;
+ dprintf(("el: xmit..."));
+ outb((sc->el_base)+EL_GPBL,(gpl & 0xff));
+ outb((sc->el_base)+EL_GPBH,((gpl>>8)&0xff));
+ outb((sc->el_base)+EL_AC,EL_AC_TXFRX);
+ i = 20000;
+ while((inb((sc->el_base)+EL_AS) & EL_AS_TXBUSY) && (i>0))
+ i--;
+ if(i == 0) {
+ dprintf(("tx not ready\n"));
+ sc->arpcom.ac_if.if_oerrors++;
+ return(-1);
+ }
+ dprintf(("%d cycles.\n",(20000-i)));
+ return(0);
+}
+
+/* controller interrupt */
+void elintr(int unit)
+{
+ register struct el_softc *sc;
+ register base;
+ int stat, rxstat, len, done;
+
+ /* Get things pointing properly */
+ sc = &el_softc[unit];
+ base = sc->el_base;
+
+ dprintf(("elintr: "));
+
+ /* Check board status */
+ stat = inb(base+EL_AS);
+ if(stat & EL_AS_RXBUSY) {
+ (void)inb(base+EL_RXC);
+ outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
+ return;
+ }
+
+ done = 0;
+ while(!done) {
+ rxstat = inb(base+EL_RXS);
+ if(rxstat & EL_RXS_STALE) {
+ (void)inb(base+EL_RXC);
+ outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
+ return;
+ }
+
+ /* If there's an overflow, reinit the board. */
+ if(!(rxstat & EL_RXS_NOFLOW)) {
+ dprintf(("overflow.\n"));
+ el_hardreset(unit);
+ /* Put board back into receive mode */
+ if(sc->arpcom.ac_if.if_flags & IFF_PROMISC)
+ outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
+ else
+ outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
+ (void)inb(base+EL_AS);
+ outb(base+EL_RBC,0);
+ (void)inb(base+EL_RXC);
+ outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
+ return;
+ }
+
+ /* Incoming packet */
+ len = inb(base+EL_RBL);
+ len |= inb(base+EL_RBH) << 8;
+ dprintf(("receive len=%d rxstat=%x ",len,rxstat));
+ outb(base+EL_AC,EL_AC_HOST);
+
+ /* If packet too short or too long, restore rx mode and return
+ */
+ if((len <= sizeof(struct ether_header)) || (len > ETHER_MAX_LEN)) {
+ if(sc->arpcom.ac_if.if_flags & IFF_PROMISC)
+ outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
+ else
+ outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
+ (void)inb(base+EL_AS);
+ outb(base+EL_RBC,0);
+ (void)inb(base+EL_RXC);
+ outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
+ return;
+ }
+
+ sc->arpcom.ac_if.if_ipackets++;
+
+ /* Copy the data into our buffer */
+ outb(base+EL_GPBL,0);
+ outb(base+EL_GPBH,0);
+ insb(base+EL_BUF,sc->el_pktbuf,len);
+ outb(base+EL_RBC,0);
+ outb(base+EL_AC,EL_AC_RX);
+ dprintf(("%s-->",ether_sprintf(sc->el_pktbuf+6)));
+ dprintf(("%s\n",ether_sprintf(sc->el_pktbuf)));
+
+ /* Pass data up to upper levels */
+ len -= sizeof(struct ether_header);
+ elread(sc,(caddr_t)(sc->el_pktbuf),len);
+
+ /* Is there another packet? */
+ stat = inb(base+EL_AS);
+
+ /* If so, do it all again (i.e. don't set done to 1) */
+ if(!(stat & EL_AS_RXBUSY))
+ dprintf(("<rescan> "));
+ else
+ done = 1;
+ }
+
+ (void)inb(base+EL_RXC);
+ outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
+ return;
+}
+
+/* Pass a packet up to the higher levels. Deal with trailer protocol. */
+static inline void elread(struct el_softc *sc,caddr_t buf,int len)
+{
+ register struct ether_header *eh;
+ struct mbuf *m;
+ int off, resid;
+
+ /* Deal with trailer protocol: if type is trailer type
+ * get true type from first 16-bit word past data.
+ * Remember that type was trailer by setting off.
+ */
+ eh = (struct ether_header *)buf;
+ eh->ether_type = ntohs((u_short)eh->ether_type);
+#define eldataaddr(eh,off,type) ((type)(((caddr_t)((eh)+1)+(off))))
+ if(eh->ether_type >= ETHERTYPE_TRAIL &&
+ eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+ off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
+ if(off >= ETHERMTU)
+ return;
+ eh->ether_type = ntohs(*eldataaddr(eh,off,u_short *));
+ resid = ntohs(*(eldataaddr(eh,off+2,u_short *)));
+ if((off+resid) > len)
+ return;
+ len = off + resid;
+ }
+ else
+ off = 0;
+
+ if(len <= 0)
+ return;
+
+#if NBPFILTER > 0
+ /*
+ * Check if there's a bpf filter listening on this interface.
+ * If so, hand off the raw packet to bpf, which must deal with
+ * trailers in its own way.
+ */
+ if(sc->bpf) {
+ eh->ether_type = htons((u_short)eh->ether_type);
+ bpf_tap(sc->bpf,buf,len+sizeof(struct ether_header));
+ eh->ether_type = ntohs((u_short)eh->ether_type);
+
+ /*
+ * Note that the interface cannot be in promiscuous mode if
+ * there are no bpf listeners. And if el are in promiscuous
+ * mode, el have to check if this packet is really ours.
+ *
+ * This test does not support multicasts.
+ */
+ if((sc->arpcom.ac_if.if_flags & IFF_PROMISC)
+ && bcmp(eh->ether_dhost,sc->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0
+ && bcmp(eh->ether_dhost,etherbroadcastaddr,
+ sizeof(eh->ether_dhost)) != 0)
+ return;
+ }
+#endif
+
+ /*
+ * Pull packet off interface. Off is nonzero if packet
+ * has trailing header; neget will then force this header
+ * information to be at the front, but we still have to drop
+ * the type and length which are at the front of any trailer data.
+ */
+ m = elget(buf,len,off,&sc->arpcom.ac_if);
+ if(m == 0)
+ return;
+
+ ether_input(&sc->arpcom.ac_if,eh,m);
+}
+
+/*
+ * Pull read data off a interface.
+ * Len is length of data, with local net header stripped.
+ * Off is non-zero if a trailer protocol was used, and
+ * gives the offset of the trailer information.
+ * We copy the trailer information and then all the normal
+ * data into mbufs. When full cluster sized units are present
+ * we copy into clusters.
+ */
+struct mbuf *
+elget(buf, totlen, off0, ifp)
+ caddr_t buf;
+ int totlen, off0;
+ struct ifnet *ifp;
+{
+ struct mbuf *top, **mp, *m, *p;
+ int off = off0, len;
+ register caddr_t cp = buf;
+ char *epkt;
+
+ buf += sizeof(struct ether_header);
+ cp = buf;
+ epkt = cp + totlen;
+
+
+ if (off) {
+ cp += off + 2 * sizeof(u_short);
+ totlen -= 2 * sizeof(u_short);
+ }
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ return (0);
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = totlen;
+ m->m_len = MHLEN;
+ top = 0;
+ mp = &top;
+ while (totlen > 0) {
+ if (top) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == 0) {
+ m_freem(top);
+ return (0);
+ }
+ m->m_len = MLEN;
+ }
+ len = min(totlen, epkt - cp);
+ if (len >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if (m->m_flags & M_EXT)
+ m->m_len = len = min(len, MCLBYTES);
+ else
+ len = m->m_len;
+ } else {
+ /*
+ * Place initial small packet/header at end of mbuf.
+ */
+ if (len < m->m_len) {
+ if (top == 0 && len + max_linkhdr <= m->m_len)
+ m->m_data += max_linkhdr;
+ m->m_len = len;
+ } else
+ len = m->m_len;
+ }
+ bcopy(cp, mtod(m, caddr_t), (unsigned)len);
+ cp += len;
+ *mp = m;
+ mp = &m->m_next;
+ totlen -= len;
+ if (cp == epkt)
+ cp = buf;
+ }
+ return (top);
+}
+
+/*
+ * Process an ioctl request. This code needs some work - it looks
+ * pretty ugly.
+ */
+int
+el_ioctl(ifp, command, data)
+ register struct ifnet *ifp;
+ int command;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ struct el_softc *sc = &el_softc[ifp->if_unit];
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s, error = 0;
+
+ s = splimp();
+
+ switch (command) {
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ el_init(ifp->if_unit); /* before arpwhohas */
+ /*
+ * See if another station has *our* IP address.
+ * i.e.: There is an address conflict! If a
+ * conflict exists, a message is sent to the
+ * console.
+ */
+ ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+ 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
+ */
+ el_init(ifp->if_unit);
+ break;
+ }
+#endif
+ default:
+ el_init(ifp->if_unit);
+ 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 interface is marked down and it is running, then stop it
+ */
+ if (((ifp->if_flags & IFF_UP) == 0) &&
+ (ifp->if_flags & IFF_RUNNING)) {
+ el_stop(ifp->if_unit);
+ ifp->if_flags &= ~IFF_RUNNING;
+ } else {
+ /*
+ * If interface is marked up and it is stopped, then start it
+ */
+ if ((ifp->if_flags & IFF_UP) &&
+ ((ifp->if_flags & IFF_RUNNING) == 0))
+ el_init(ifp->if_unit);
+ }
+
+ default:
+ error = EINVAL;
+ }
+ (void) splx(s);
+ return (error);
+}
+
+/* Device timeout routine */
+void el_watchdog(int unit)
+{
+ struct el_softc *sc;
+
+ sc = &el_softc[unit];
+
+ log(LOG_ERR,"el%d: device timeout\n",unit);
+ sc->arpcom.ac_if.if_oerrors++;
+ el_reset(unit,0);
+}
+#endif
diff --git a/sys/i386/isa/if_elreg.h b/sys/i386/isa/if_elreg.h
new file mode 100644
index 000000000000..806d6ff68d1b
--- /dev/null
+++ b/sys/i386/isa/if_elreg.h
@@ -0,0 +1,76 @@
+/* Copyright (c) 1994, Matthew E. Kimmel. Permission is hereby granted
+ * to use, copy, modify and distribute this software 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.
+ */
+/* 3COM Etherlink 3C501 Register Definitions */
+
+/* I/O Ports */
+#define EL_RXS 0x6 /* Receive status register */
+#define EL_RXC 0x6 /* Receive command register */
+#define EL_TXS 0x7 /* Transmit status register */
+#define EL_TXC 0x7 /* Transmit command register */
+#define EL_GPBL 0x8 /* GP buffer ptr low byte */
+#define EL_GPBH 0x9 /* GP buffer ptr high byte */
+#define EL_RBL 0xa /* Receive buffer ptr low byte */
+#define EL_RBC 0xa /* Receive buffer clear */
+#define EL_RBH 0xb /* Receive buffer ptr high byte */
+#define EL_EAW 0xc /* Ethernet address window */
+#define EL_AS 0xe /* Auxillary status register */
+#define EL_AC 0xe /* Auxillary command register */
+#define EL_BUF 0xf /* Data buffer */
+
+/* Receive status register bits */
+#define EL_RXS_OFLOW 0x01 /* Overflow error */
+#define EL_RXS_FCS 0x02 /* FCS error */
+#define EL_RXS_DRIB 0x04 /* Dribble error */
+#define EL_RXS_SHORT 0x08 /* Short frame */
+#define EL_RXS_NOFLOW 0x10 /* No overflow */
+#define EL_RXS_GOOD 0x20 /* Received good frame */
+#define EL_RXS_STALE 0x80 /* Stale receive status */
+
+/* Receive command register bits */
+#define EL_RXC_DISABLE 0x00 /* Receiver disabled */
+#define EL_RXC_DOFLOW 0x01 /* Detect overflow */
+#define EL_RXC_DFCS 0x02 /* Detect FCS errs */
+#define EL_RXC_DDRIB 0x04 /* Detect dribble errors */
+#define EL_RXC_DSHORT 0x08 /* Detect short frames */
+#define EL_RXC_DNOFLOW 0x10 /* Detect frames w/o overflow ??? */
+#define EL_RXC_AGF 0x20 /* Accept Good Frames */
+#define EL_RXC_PROMISC 0x40 /* Promiscuous mode */
+#define EL_RXC_ABROAD 0x80 /* Accept address, broadcast */
+#define EL_RXC_AMULTI 0xc0 /* Accept address, multicast */
+
+/* Transmit status register bits */
+#define EL_TXS_UFLOW 0x01 /* Underflow */
+#define EL_TXS_COLL 0x02 /* Collision */
+#define EL_TXS_COLL16 0x04 /* Collision 16 */
+#define EL_TXS_READY 0x08 /* Ready for new frame */
+
+/* Transmit command register bits */
+#define EL_TXC_DUFLOW 0x01 /* Detect underflow */
+#define EL_TXC_DCOLL 0x02 /* Detect collisions */
+#define EL_TXC_DCOLL16 0x04 /* Detect collision 16 */
+#define EL_TXC_DSUCCESS 0x08 /* Detect success */
+
+/* Auxillary status register bits */
+#define EL_AS_RXBUSY 0x01 /* Receive busy */
+#define EL_AS_DMADONE 0x10 /* DMA finished */
+#define EL_AS_TXBUSY 0x80 /* Transmit busy */
+
+/* Auxillary command register bits */
+#define EL_AC_HOST 0x00 /* System bus can access buffer */
+#define EL_AC_IRQE 0x01 /* IRQ enable */
+#define EL_AC_TXBAD 0x02 /* Transmit frames with bad FCS */
+#define EL_AC_TXFRX 0x04 /* Transmit followed by receive */
+#define EL_AC_RX 0x08 /* Receive */
+#define EL_AC_LB 0x0c /* Loopback */
+#define EL_AC_DRQ 0x20 /* DMA request */
+#define EL_AC_RIDE 0x40 /* DRQ and IRQ enabled */
+#define EL_AC_RESET 0x80 /* Reset */
+
+/* Packet buffer size */
+#define EL_BUFSIZ 2048
+
+#define ETHER_ADDR_LEN 6
diff --git a/sys/i386/isa/if_ep.c b/sys/i386/isa/if_ep.c
index 43edd4529485..44e9c9fc0d58 100644
--- a/sys/i386/isa/if_ep.c
+++ b/sys/i386/isa/if_ep.c
@@ -8,7 +8,7 @@
* 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
+ * 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
@@ -22,62 +22,56 @@
* 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_ep.c,v 1.7 1994/02/03 11:51:06 davidg Exp $
- */
-/*
- * TODO:
- * Multi-509 configs.
- * don't pass unit into epstop.
- * epintr returns an int for magnum. 0=not for me. 1=for me. -1=whoknows?
- * deallocate mbufs when ifconfig'd down.
+ * $Id: if_ep.c,v 1.9 1994/05/02 22:27:33 ats Exp $
*/
+
#include "ep.h"
#if NEP > 0
#include "bpfilter.h"
-#include "sys/param.h"
+#include <sys/param.h>
#if defined(__FreeBSD__)
-#include "sys/systm.h"
-#include "sys/kernel.h"
+#include <sys/systm.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 <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"
+#include <sys/select.h>
#endif
-#include "net/if.h"
-#include "net/if_dl.h"
-#include "net/if_types.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"
+#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 NS
-#include "netns/ns.h"
-#include "netns/ns_if.h"
+#include <netns/ns.h>
+#include <netns/ns_if.h>
#endif
#if NBPFILTER > 0
-#include "net/bpf.h"
-#include "net/bpfdesc.h"
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
#endif
-#include "machine/pio.h"
+#include <machine/pio.h>
-#include "i386/isa/isa.h"
-#include "i386/isa/isa_device.h"
-#include "i386/isa/icu.h"
-#include "i386/isa/if_epreg.h"
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/if_epreg.h>
#define ETHER_MIN_LEN 64
#define ETHER_MAX_LEN 1518
@@ -90,12 +84,13 @@ struct ep_softc {
struct arpcom arpcom; /* Ethernet common part */
short ep_io_addr; /* i/o bus address */
char ep_connectors; /* Connectors on this card. */
-#define MAX_MBS 4 /* # of mbufs we keep around */
+#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. */
int tx_start_thresh; /* Current TX_start_thresh. */
caddr_t bpf; /* BPF "magic cookie" */
+ char bus32bit; /* 32bit access possible */
} ep_softc[NEP];
static int epprobe __P((struct isa_device *));
@@ -104,7 +99,8 @@ static int epioctl __P((struct ifnet * ifp, int, caddr_t));
void epinit __P((int));
void epintr __P((int));
-void epmbufqueue __P((caddr_t, int));
+void epmbuffill __P((caddr_t, int));
+void epmbufempty __P((struct ep_softc *));
void epread __P((struct ep_softc *));
void epreset __P((int));
void epstart __P((struct ifnet *));
@@ -274,7 +270,7 @@ epinit(unit)
return;
s = splimp();
- while (inb(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
;
GO_WINDOW(0);
@@ -350,7 +346,7 @@ epinit(unit)
*/
sc->last_mb = 0;
sc->next_mb = 0;
- epmbufqueue((caddr_t)sc, 0);
+ epmbuffill((caddr_t)sc, 0);
epstart(ifp);
@@ -421,10 +417,19 @@ startagain:
outw(BASE + EP_W1_TX_PIO_WR_1, 0xffff); /* Second dword meaningless */
for (top = m; m != 0; m = m->m_next) {
- 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));
+ 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/4,
+ 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 */
@@ -658,7 +663,7 @@ epread(sc)
if (m == 0)
goto out;
} else {
- timeout(epmbufqueue, (caddr_t)sc, 0);
+ timeout(epmbuffill, (caddr_t)sc, 0);
sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
}
if (totlen >= MINCLSIZE)
@@ -667,11 +672,22 @@ epread(sc)
mcur->m_next = m;
lenthisone = min(totlen, M_TRAILINGSPACE(m));
}
- 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);
+ 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) {
@@ -679,15 +695,17 @@ epread(sc)
sc->mb[sc->next_mb] = 0;
if (top == 0) {
MGETHDR(m, M_DONTWAIT, MT_DATA);
- if (top == 0)
+ 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(m, caddr_t) + m->m_len,
+ 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);
@@ -700,7 +718,7 @@ epread(sc)
top->m_pkthdr.rcvif = &sc->arpcom.ac_if;
outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
- while (inb(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
;
++sc->arpcom.ac_if.if_ipackets;
#if NBPFILTER > 0
@@ -728,12 +746,11 @@ epread(sc)
return;
out: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
- while (inb(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
;
if (top)
m_freem(top);
- return;
}
@@ -773,7 +790,7 @@ epioctl(ifp, cmd, data)
else {
ifp->if_flags &= ~IFF_RUNNING;
bcopy((caddr_t) ina->x_host.c_host,
- (caddr_t)sc->arpcom.ns_addr
+ (caddr_t)sc->arpcom.ac_enaddr,
sizeof(sc->arpcom.ac_enaddr));
}
epinit(ifp->if_unit);
@@ -789,6 +806,7 @@ epioctl(ifp, cmd, data)
if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) {
ifp->if_flags &= ~IFF_RUNNING;
epstop(ifp->if_unit);
+ epmbufempty(sc);
break;
}
if (ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0)
@@ -815,7 +833,6 @@ epreset(unit)
epstop(unit);
epinit(unit);
splx(s);
- return;
}
void
@@ -838,7 +855,7 @@ epstop(unit)
outw(BASE + EP_COMMAND, RX_DISABLE);
outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
- while (inb(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
;
outw(BASE + EP_COMMAND, TX_DISABLE);
outw(BASE + EP_COMMAND, STOP_TRANSCEIVER);
@@ -848,7 +865,6 @@ epstop(unit)
outw(BASE + EP_COMMAND, SET_RD_0_MASK);
outw(BASE + EP_COMMAND, SET_INTR_MASK);
outw(BASE + EP_COMMAND, SET_RX_FILTER);
- return;
}
@@ -937,24 +953,42 @@ is_eeprom_busy(is)
}
void
-epmbufqueue(sp, dummy_arg)
+epmbuffill(sp, dummy_arg)
caddr_t sp;
int dummy_arg;
{
struct ep_softc *sc = (struct ep_softc *)sp;
- int i;
+ int s, i;
- if (sc->mb[sc->last_mb])
- return;
+ s = splimp();
i = sc->last_mb;
do {
- MGET(sc->mb[i], M_DONTWAIT, MT_DATA);
- if (!sc->mb[i])
+ 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;
- return;
+ splx(s);
+}
+
+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;
+ untimeout(epmbuffill, sc);
+ splx(s);
}
#endif /* NEP > 0 */
diff --git a/sys/i386/isa/if_ie507.h b/sys/i386/isa/if_ie507.h
new file mode 100644
index 000000000000..4bf87fcbb597
--- /dev/null
+++ b/sys/i386/isa/if_ie507.h
@@ -0,0 +1,19 @@
+/*
+ * $Id: if_ie507.h,v 1.1 1994/05/25 20:06:49 ats Exp $
+ * Definitions for 3C507
+ */
+
+#define IE507_CTRL 6 /* control port */
+#define IE507_ICTRL 10 /* interrupt control */
+#define IE507_ATTN 11 /* any write here sends a chan attn */
+#define IE507_MADDR 14 /* shared memory configuration */
+#define IE507_IRQ 15 /* IRQ configuration */
+
+#define EL_CTRL_BNK1 0x01 /* register bank 1 */
+#define EL_CTRL_IEN 0x04 /* interrupt enable */
+#define EL_CTRL_INTL 0x08 /* interrupt active latch */
+#define EL_CTRL_16BIT 0x10 /* bus width; clear = 8-bit, set = 16-bit */
+#define EL_CTRL_LOOP 0x20 /* loopback mode */
+#define EL_CTRL_NRST 0x80 /* turn off to reset */
+#define EL_CTRL_RESET (EL_CTRL_LOOP)
+#define EL_CTRL_NORMAL (EL_CTRL_NRST | EL_CTRL_IEN | EL_CTRL_BNK1)
diff --git a/sys/i386/isa/if_is.c b/sys/i386/isa/if_is.c
index 341885f36ed1..234a0d937c1f 100644
--- a/sys/i386/isa/if_is.c
+++ b/sys/i386/isa/if_is.c
@@ -332,15 +332,15 @@ is_attach(isa_dev)
* are only 16 bits wide!
*/
-#define MAXMEM ((NRBUF+NTBUF)*(BUFSIZE) + (NRBUF+NTBUF)*sizeof(struct mds) \
+#define ISMAXMEM ((NRBUF+NTBUF)*(BUFSIZE) + (NRBUF+NTBUF)*sizeof(struct mds) \
+ sizeof(struct init_block) + 8)
- is->init_block = (struct init_block *)malloc(MAXMEM,M_TEMP,M_NOWAIT);
+ is->init_block = (struct init_block *)malloc(ISMAXMEM,M_TEMP,M_NOWAIT);
if (!is->init_block) {
printf("is%d : Couldn't allocate memory for card\n",unit);
}
/*
* XXX -- should take corrective action if not
- * quadword alilgned, the 8 byte slew factor in MAXMEM
+ * quadword alilgned, the 8 byte slew factor in ISMAXMEM
* allows for this.
*/
@@ -483,7 +483,7 @@ is_init(unit)
/* Address not known */
if (ifp->if_addrlist == (struct ifaddr *)0) return;
- s = splnet();
+ s = splimp();
/*
* Lance must be stopped
@@ -984,7 +984,7 @@ is_ioctl(ifp, cmd, data)
struct ifreq *ifr = (struct ifreq *)data;
int s, error = 0;
- s = splnet();
+ s = splimp();
switch (cmd) {
diff --git a/sys/i386/isa/if_ze.c b/sys/i386/isa/if_ze.c
new file mode 100644
index 000000000000..6ffb96c96dbc
--- /dev/null
+++ b/sys/i386/isa/if_ze.c
@@ -0,0 +1,1951 @@
+/*-
+ * 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 "ze.h"
+#if NZE > 0
+#include "bpfilter.h"
+
+#include "param.h"
+#include "systm.h"
+#include "errno.h"
+#include "ioctl.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "syslog.h"
+
+#include "net/if.h"
+#include "net/if_dl.h"
+#include "net/if_types.h"
+#include "net/netisr.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 NS
+#include "netns/ns.h"
+#include "netns/ns_if.h"
+#endif
+
+#if NBPFILTER > 0
+#include "net/bpf.h"
+#include "net/bpfdesc.h"
+#endif
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/if_zereg.h"
+
+#include "i386/include/pio.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 addresss 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)
+
+/*
+ * 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);
+}
+
+static void
+pcic_print_regs (int slot)
+{
+ int i, j;
+
+ for (i = 0; i < 0x40; i += 16) {
+ for (j = 0; j < 16; ++j)
+ printf ("%02x ", pcic_getb (slot, i + j));
+ printf ("\n");
+ }
+}
+
+/*
+ * 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.
+ */
+
+enum memtype { COMMON, ATTRIBUTE };
+
+static 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);
+}
+
+static 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
+ */
+
+static 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
+static 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.
+ */
+
+static void
+pcic_map_irq (int slot, int irq)
+{
+ if (irq < 3 || irq == 6 || irq == 8 || irq == 13 || irq > 15) {
+ printf ("ze: 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));
+}
+
+static void
+pcic_power_on (int slot)
+{
+ pcic_putb (slot, PCIC_POWER,
+ pcic_getb (slot, PCIC_POWER) | PCIC_DISRST | PCIC_PCPWRE);
+ DELAY (50000);
+ pcic_putb (slot, PCIC_POWER,
+ pcic_getb (slot, PCIC_POWER) | PCIC_OUTENA);
+}
+
+static 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 (50000);
+ pcic_putb (slot, PCIC_INT_GEN,
+ pcic_getb (slot, PCIC_INT_GEN) | PCIC_CARDRESET);
+}
+
+
+/*****************************************************************************
+ * Driver for Ethernet Adapter *
+ *****************************************************************************/
+/*
+ * ze_softc: per line info and status
+ */
+struct ze_softc {
+ struct arpcom arpcom; /* ethernet common */
+
+ char *type_str; /* pointer to type string */
+ char *mau; /* type of media access unit */
+#if 0
+ u_char vendor; /* interface vendor */
+ u_char type; /* interface type code */
+#endif
+
+#if 0
+ u_short vector; /* interrupt vector */
+#endif
+ u_short nic_addr; /* NIC (DS8390) I/O bus address */
+
+ caddr_t smem_start; /* shared memory start address */
+ caddr_t smem_end; /* shared memory end address */
+ u_long smem_size; /* total shared memory size */
+ caddr_t smem_ring; /* start of RX ring-buffer (in smem) */
+
+ caddr_t bpf; /* BPF "magic cookie" */
+
+ u_char memwidth; /* width of access to card mem 8 or 16 */
+ u_char xmit_busy; /* transmitter is busy */
+ u_char txb_cnt; /* Number of transmit buffers */
+ u_char txb_next; /* Pointer to next buffer ready to xmit */
+ u_short txb_next_len; /* next xmit buffer length */
+ u_char data_buffered; /* data has been buffered in interface memory */
+ 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 */
+} ze_softc[NZE];
+
+int ze_attach(), ze_ioctl(), ze_probe();
+void ze_init(), ze_start(), ze_stop(), ze_intr();
+void ze_reset(), ze_watchdog(), ze_get_packet();
+
+static inline void ze_rint();
+static inline void ze_xmit();
+static inline char *ze_ring_copy();
+
+extern int ether_output();
+
+struct isa_driver zedriver = {
+ ze_probe,
+ ze_attach,
+ "ze"
+};
+
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+#define ETHER_ADDR_LEN 6
+#define ETHER_HDR_SIZE 14
+
+static unsigned char enet_addr[6];
+static unsigned char card_info[256];
+
+#define CARD_INFO "IBM Corp.~Ethernet~0933495"
+
+/*
+ * 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
+ze_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 0
+ printf ("[%02x] %02x ", i, link);
+ for (j = 4; j < 2 * link + 4 && j < 32; j += 2)
+ printf ("%02x ", scratch[j + i]);
+ printf ("\n");
+#endif
+ 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 (memcmp (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
+ze_find_adapter (unsigned char *scratch)
+{
+ int slot;
+
+ for (slot = 0; 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
+ */
+ unsigned char idbyte = pcic_getb (slot, PCIC_ID_REV);
+
+ if (idbyte != 0x82 && idbyte != 0x83 &&
+ idbyte != 0x88 && idbyte != 0x89) {
+#if 0
+ printf ("ibmccae: pcic slot %d: wierd id/rev code 0x%02x\n",
+ slot, idbyte);
+#endif
+ continue;
+ }
+ if ((pcic_getb (slot, PCIC_STATUS) & PCIC_CD) != PCIC_CD) {
+ printf ("ze: 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 ((ze_check_cis (scratch)) > 0) {
+ /* found it */
+ printf ("ze: found card in slot %d\n", slot);
+ return slot;
+ }
+ else
+ printf ("ze: pcmcia slot %d: %s\n", slot, card_info);
+ pcic_unmap_memory (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)
+ */
+int
+ze_probe(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ze_softc *sc = &ze_softc[isa_dev->id_unit];
+ int i, x;
+ u_int memsize;
+ u_char iptr, memwidth, sum, tmp;
+ int slot;
+
+ if ((slot = ze_find_adapter (isa_dev->id_maddr)) < 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
+ */
+ /*
+ * 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);
+ pcic_unmap_memory (slot, 0);
+
+ /*
+ * (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), 0x20000, 8L,
+ ATTRIBUTE, 1);
+ POKE(isa_dev->id_maddr, 0x80); /* reset the card (how long?) */
+ DELAY (10000);
+ /*
+ * 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);
+
+ /*
+ * (3) now map in the shared memory buffer. This has to be mapped
+ * as words, not bytes, and on a 16k boundary. The offset value
+ * was derived by installing IBM's POINTETH.SYS under DOS and
+ * looking at the PCIC registers; it's not documented in IBM's
+ * tech ref manual ([1]).
+ */
+ pcic_map_memory (slot, 0, kvtop (isa_dev->id_maddr), 0x4000L, 0x4000L,
+ COMMON, 2);
+
+ /*
+ * (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...
+ */
+#ifdef SHARED_MEMORY
+ pcic_map_io (slot, 0, isa_dev->id_iobase, 32, 1);
+#else
+ pcic_map_io (slot, 0, isa_dev->id_iobase, 16, 1);
+ pcic_map_io (slot, 1, isa_dev->id_iobase+16, 16, 2);
+#endif /* SHARED_MEMORY */
+
+ /*
+ * (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);
+
+#if 0
+ /* tell the PCIC to use level-mode interrupts */
+ /* XXX this register may not be present on all controllers */
+ pcic_putb (slot, PCIC_GLO_CTRL,
+ pcic_getb (slot, PCIC_GLO_CTRL) | PCIC_LVL_MODE);
+#endif
+
+#if 0
+ pcic_print_regs (slot);
+#endif
+ /*
+ * Setup i/o addresses
+ */
+ sc->nic_addr = isa_dev->id_iobase;
+#if 0
+ sc->vector = isa_dev->id_irq;
+#endif
+ sc->smem_start = (caddr_t)isa_dev->id_maddr;
+
+#if 0
+ sc->vendor = ZE_VENDOR_IBM;
+ sc->type = xxx;
+#endif
+
+ /* reset card to force it into a known state */
+ tmp = inb (isa_dev->id_iobase + ZE_RESET);
+ DELAY(5000);
+ outb (isa_dev->id_iobase + ZE_RESET, tmp);
+ DELAY(5000);
+
+ /*
+ * query MAM bit in misc register for 10base2
+ */
+ tmp = inb (isa_dev->id_iobase + ZE_MISC);
+ sc->mau = tmp & 0x09 ? "10base2" : "10baseT";
+
+ /* set width/size */
+ sc->type_str = "IBM PCMCIA";
+ memsize = 16*1024;
+ sc->memwidth = 16;
+
+ /* allocate 1 xmit buffer */
+ sc->smem_ring = sc->smem_start + (ZE_PAGE_SIZE * ZE_TXBUF_SIZE);
+ sc->txb_cnt = 1;
+ sc->rec_page_start = ZE_TXBUF_SIZE + ZE_PAGE_OFFSET;
+ sc->smem_size = memsize;
+ sc->smem_end = sc->smem_start + memsize;
+ sc->rec_page_stop = memsize / ZE_PAGE_SIZE + ZE_PAGE_OFFSET;
+ sc->tx_page_start = ZE_PAGE_OFFSET;
+
+ /* get station address */
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ sc->arpcom.ac_enaddr[i] = enet_addr[i];
+
+ isa_dev->id_msize = memsize;
+ return 32;
+}
+
+/*
+ * Install interface into kernel networking data structures
+ */
+int
+ze_attach(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ze_softc *sc = &ze_softc[isa_dev->id_unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+
+ /*
+ * Set interface to stopped condition (reset)
+ */
+ ze_stop(isa_dev->id_unit);
+
+ /*
+ * Initialize ifnet structure
+ */
+ ifp->if_unit = isa_dev->id_unit;
+ ifp->if_name = "ze" ;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_init = ze_init;
+ ifp->if_output = ether_output;
+ ifp->if_start = ze_start;
+ ifp->if_ioctl = ze_ioctl;
+ ifp->if_reset = ze_reset;
+ ifp->if_watchdog = ze_watchdog;
+
+ /*
+ * Set default state for LLC0 flag (used to disable the tranceiver
+ * for AUI operation), based on compile-time config option.
+ */
+ if (isa_dev->id_flags & ZE_FLAGS_DISABLE_TRANCEIVER)
+ ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS
+ | IFF_LLC0);
+ else
+ ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS);
+
+ /*
+ * Attach the interface
+ */
+ if_attach(ifp);
+
+ /*
+ * Search down the ifa address list looking for the AF_LINK type entry
+ */
+ ifa = ifp->if_addrlist;
+ while ((ifa != 0) && (ifa->ifa_addr != 0) &&
+ (ifa->ifa_addr->sa_family != AF_LINK))
+ ifa = ifa->ifa_next;
+ /*
+ * If we find an AF_LINK type entry we fill in the hardware address.
+ * This is useful for netstat(1) to keep track of which interface
+ * is which.
+ */
+ if ((ifa != 0) && (ifa->ifa_addr != 0)) {
+ /*
+ * Fill in the link-level address for this interface
+ */
+ 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);
+ }
+
+ /*
+ * Print additional info when attached
+ */
+ printf("ze%d: address %s, type %s (%dbit)%s, MAU %s\n",
+ isa_dev->id_unit,
+ ether_sprintf(sc->arpcom.ac_enaddr), sc->type_str,
+ sc->memwidth,
+ (ifp->if_flags & IFF_LLC0 ? " [tranceiver disabled]" : ""),
+ sc->mau);
+
+ /*
+ * If BPF is in the kernel, call the attach for it
+ */
+#if NBPFILTER > 0
+ bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+ return 1;
+}
+
+/*
+ * Reset interface.
+ */
+void
+ze_reset(unit)
+ int unit;
+{
+ int s;
+
+ s = splnet();
+
+ /*
+ * Stop interface and re-initialize.
+ */
+ ze_stop(unit);
+ ze_init(unit);
+
+ (void) splx(s);
+}
+
+/*
+ * Take interface offline.
+ */
+void
+ze_stop(unit)
+ int unit;
+{
+ struct ze_softc *sc = &ze_softc[unit];
+ int n = 5000;
+
+ /*
+ * Stop everything on the interface, and select page 0 registers.
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_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 + ZE_P0_ISR) & ZE_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.
+ */
+void
+ze_watchdog(unit)
+ int unit;
+{
+#if 1
+ struct ze_softc *sc = &ze_softc[unit];
+ u_char isr, imr;
+ u_short imask;
+
+ /* select page zero */
+ outb (sc->nic_addr + ZE_P0_CR,
+ (inb (sc->nic_addr + ZE_P0_CR) & 0x3f) | ZE_CR_PAGE_0);
+
+ /* read interrupt status register */
+ isr = inb (sc->nic_addr + ZE_P0_ISR) & 0xff;
+
+ /* select page two */
+ outb (sc->nic_addr + ZE_P0_CR,
+ (inb (sc->nic_addr + ZE_P0_CR) & 0x3f) | ZE_CR_PAGE_2);
+
+ /* read interrupt mask register */
+ imr = inb (sc->nic_addr + ZE_P2_IMR) & 0xff;
+
+ imask = inb(IO_ICU2) << 8 | inb(IO_ICU1);
+
+ log (LOG_ERR, "ze%d: device timeout, isr=%02x, imr=%02x, imask=%04x\n",
+ unit, isr, imr, imask);
+#else
+ log(LOG_ERR, "ze%d: device timeout\n", unit);
+#endif
+
+ ze_reset(unit);
+}
+
+/*
+ * Initialize device.
+ */
+void
+ze_init(unit)
+ int unit;
+{
+ struct ze_softc *sc = &ze_softc[unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int i, s;
+ u_char command;
+
+
+ /* 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 = splnet();
+
+ /* reset transmitter flags */
+ sc->data_buffered = 0;
+ sc->xmit_busy = 0;
+ sc->arpcom.ac_if.if_timer = 0;
+
+ sc->txb_next = 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 + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STP);
+
+ if (sc->memwidth == 16) {
+ /*
+ * Set FIFO threshold to 8, No auto-init Remote DMA,
+ * byte order=80x86, word-wide DMA xfers
+ */
+ outb(sc->nic_addr + ZE_P0_DCR, ZE_DCR_FT1|ZE_DCR_WTS);
+ } else {
+ /*
+ * Same as above, but byte-wide DMA xfers
+ */
+ outb(sc->nic_addr + ZE_P0_DCR, ZE_DCR_FT1);
+ }
+
+ /*
+ * Clear Remote Byte Count Registers
+ */
+ outb(sc->nic_addr + ZE_P0_RBCR0, 0);
+ outb(sc->nic_addr + ZE_P0_RBCR1, 0);
+
+ /*
+ * Enable reception of broadcast packets
+ */
+ outb(sc->nic_addr + ZE_P0_RCR, ZE_RCR_AB);
+
+ /*
+ * Place NIC in internal loopback mode
+ */
+ outb(sc->nic_addr + ZE_P0_TCR, ZE_TCR_LB0);
+
+ /*
+ * Initialize transmit/receive (ring-buffer) Page Start
+ */
+ outb(sc->nic_addr + ZE_P0_TPSR, sc->tx_page_start);
+ outb(sc->nic_addr + ZE_P0_PSTART, sc->rec_page_start);
+
+ /*
+ * Initialize Receiver (ring-buffer) Page Stop and Boundry
+ */
+ outb(sc->nic_addr + ZE_P0_PSTOP, sc->rec_page_stop);
+ outb(sc->nic_addr + ZE_P0_BNRY, sc->rec_page_start);
+
+ /*
+ * Clear all interrupts. A '1' in each bit position clears the
+ * corresponding flag.
+ */
+ outb(sc->nic_addr + ZE_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 + ZE_P0_IMR,
+ ZE_IMR_PRXE|ZE_IMR_PTXE|ZE_IMR_RXEE|ZE_IMR_TXEE|ZE_IMR_OVWE);
+
+ /*
+ * Program Command Register for page 1
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_PAGE_1|ZE_CR_RD2|ZE_CR_STP);
+
+ /*
+ * Copy out our station address
+ */
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ outb(sc->nic_addr + ZE_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]);
+
+#if NBPFILTER > 0
+ /*
+ * Initialize multicast address hashing registers to accept
+ * all multicasts (only used when in promiscuous mode)
+ */
+ for (i = 0; i < 8; ++i)
+ outb(sc->nic_addr + ZE_P1_MAR0 + i, 0xff);
+#endif
+
+ /*
+ * Set Current Page pointer to next_packet (initialized above)
+ */
+ outb(sc->nic_addr + ZE_P1_CURR, sc->next_packet);
+
+ /*
+ * Set Command Register for page 0, Remote DMA complete,
+ * and interface Start.
+ */
+ outb(sc->nic_addr + ZE_P1_CR, ZE_CR_RD2|ZE_CR_STA);
+
+ /*
+ * Take interface out of loopback
+ */
+ outb(sc->nic_addr + ZE_P0_TCR, 0);
+
+#if 0
+ /*
+ * If this is a 3Com board, the tranceiver must be software enabled
+ * (there is no settable hardware default).
+ */
+ if (sc->vendor == ZE_VENDOR_3COM) {
+ if (ifp->if_flags & IFF_LLC0) {
+ outb(sc->asic_addr + ZE_3COM_CR, 0);
+ } else {
+ outb(sc->asic_addr + ZE_3COM_CR, ZE_3COM_CR_XSEL);
+ }
+ }
+#endif
+
+ /*
+ * Set 'running' flag, and clear output active flag.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * ...and attempt to start output
+ */
+ ze_start(ifp);
+
+ (void) splx(s);
+}
+
+/*
+ * This routine actually starts the transmission on the interface
+ */
+static inline void
+ze_xmit(ifp)
+ struct ifnet *ifp;
+{
+ struct ze_softc *sc = &ze_softc[ifp->if_unit];
+ u_short len = sc->txb_next_len;
+
+ /*
+ * Set NIC for page 0 register access
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA);
+
+ /*
+ * Set TX buffer start page
+ */
+ outb(sc->nic_addr + ZE_P0_TPSR, sc->tx_page_start +
+ sc->txb_next * ZE_TXBUF_SIZE);
+
+ /*
+ * Set TX length
+ */
+ outb(sc->nic_addr + ZE_P0_TBCR0, len & 0xff);
+ outb(sc->nic_addr + ZE_P0_TBCR1, len >> 8);
+
+ /*
+ * Set page 0, Remote DMA complete, Transmit Packet, and *Start*
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_TXP|ZE_CR_STA);
+
+ sc->xmit_busy = 1;
+ sc->data_buffered = 0;
+
+ /*
+ * Switch buffers if we are doing double-buffered transmits
+ */
+ if ((sc->txb_next == 0) && (sc->txb_cnt > 1))
+ sc->txb_next = 1;
+ else
+ sc->txb_next = 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 splnet _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
+ze_start(ifp)
+ struct ifnet *ifp;
+{
+ struct ze_softc *sc = &ze_softc[ifp->if_unit];
+ struct mbuf *m0, *m;
+ caddr_t buffer;
+ int len;
+ u_char laar_tmp;
+
+outloop:
+ /*
+ * See if there is room to send more data (i.e. one or both of the
+ * buffers is empty).
+ */
+ if (sc->data_buffered)
+ if (sc->xmit_busy) {
+ /*
+ * No room. Indicate this to the outside world
+ * and exit.
+ */
+ ifp->if_flags |= IFF_OACTIVE;
+ return;
+ } else {
+ /*
+ * Data is buffered, but we're not transmitting, so
+ * start the xmit on the buffered data.
+ * Note that ze_xmit() resets the data_buffered flag
+ * before returning.
+ */
+ ze_xmit(ifp);
+ }
+
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
+ if (m == NULL) {
+ /*
+ * The following isn't pretty; 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 the secondary buffer with data then we still want to
+ * accept more.
+ * Note that it isn't necessary to test the data_buffered flag -
+ * we wouldn't have tried to de-queue the packet in the first place
+ * if it was set.
+ */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ return;
+ }
+
+ /*
+ * Copy the mbuf chain into the transmit buffer
+ */
+#if 0
+ /*
+ * Enable 16bit access to shared memory on WD/SMC boards
+ */
+ if (sc->memwidth == 16)
+ if (sc->vendor == ZE_VENDOR_WD_SMC) {
+ laar_tmp = inb(sc->asic_addr + ZE_WD_LAAR);
+ outb(sc->asic_addr + ZE_WD_LAAR, laar_tmp | ZE_WD_LAAR_M16EN);
+ }
+#endif
+
+ buffer = sc->smem_start + (sc->txb_next * ZE_TXBUF_SIZE * ZE_PAGE_SIZE);
+ len = 0;
+ for (m0 = m; m != 0; m = m->m_next) {
+ bcopy(mtod(m, caddr_t), buffer, m->m_len);
+ buffer += m->m_len;
+ len += m->m_len;
+ }
+
+#if 0
+ /*
+ * Restore previous shared mem access type
+ */
+ if (sc->memwidth == 16)
+ if (sc->vendor == ZE_VENDOR_WD_SMC) {
+ outb(sc->asic_addr + ZE_WD_LAAR, laar_tmp);
+ }
+#endif
+
+ sc->txb_next_len = MAX(len, ETHER_MIN_LEN);
+
+ if (sc->txb_cnt > 1)
+ /*
+ * only set 'buffered' flag if doing multiple buffers
+ */
+ sc->data_buffered = 1;
+
+ if (sc->xmit_busy == 0)
+ ze_xmit(ifp);
+ /*
+ * If there is BPF support in the configuration, tap off here.
+ * The following has support for converting trailer packets
+ * back to normal.
+ */
+#if NBPFILTER > 0
+ if (sc->bpf) {
+ u_short etype;
+ int off, datasize, resid;
+ struct ether_header *eh;
+ struct trailer_header {
+ u_short ether_type;
+ u_short ether_residual;
+ } trailer_header;
+ char ether_packet[ETHER_MAX_LEN];
+ char *ep;
+
+ ep = ether_packet;
+
+ /*
+ * We handle trailers below:
+ * Copy ether header first, then residual data,
+ * then data. Put all this in a temporary buffer
+ * 'ether_packet' and send off to bpf. Since the
+ * system has generated this packet, we assume
+ * that all of the offsets in the packet are
+ * correct; if they're not, the system will almost
+ * certainly crash in m_copydata.
+ * We make no assumptions about how the data is
+ * arranged in the mbuf chain (i.e. how much
+ * data is in each mbuf, if mbuf clusters are
+ * used, etc.), which is why we use m_copydata
+ * to get the ether header rather than assume
+ * that this is located in the first mbuf.
+ */
+ /* copy ether header */
+ m_copydata(m0, 0, sizeof(struct ether_header), ep);
+ eh = (struct ether_header *) ep;
+ ep += sizeof(struct ether_header);
+ etype = ntohs(eh->ether_type);
+ if (etype >= ETHERTYPE_TRAIL &&
+ etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+ datasize = ((etype - ETHERTYPE_TRAIL) << 9);
+ off = datasize + sizeof(struct ether_header);
+
+ /* copy trailer_header into a data structure */
+ m_copydata(m0, off, sizeof(struct trailer_header),
+ &trailer_header.ether_type);
+
+ /* copy residual data */
+ m_copydata(m0, off+sizeof(struct trailer_header),
+ resid = ntohs(trailer_header.ether_residual) -
+ sizeof(struct trailer_header), ep);
+ ep += resid;
+
+ /* copy data */
+ m_copydata(m0, sizeof(struct ether_header),
+ datasize, ep);
+ ep += datasize;
+
+ /* restore original ether packet type */
+ eh->ether_type = trailer_header.ether_type;
+
+ bpf_tap(sc->bpf, ether_packet, ep - ether_packet);
+ } else
+ bpf_mtap(sc->bpf, m0);
+ }
+#endif
+
+ m_freem(m0);
+
+ /*
+ * If we are doing double-buffering, a buffer might be free to
+ * fill with another packet, so loop back to the top.
+ */
+ if (sc->txb_cnt > 1)
+ goto outloop;
+ else {
+ ifp->if_flags |= IFF_OACTIVE;
+ return;
+ }
+}
+
+/*
+ * Ethernet interface receiver interrupt.
+ */
+static inline void /* only called from one place, so may as well integrate */
+ze_rint(unit)
+ int unit;
+{
+ register struct ze_softc *sc = &ze_softc[unit];
+ u_char boundry, current;
+ u_short len;
+ struct ze_ring *packet_ptr;
+
+ /*
+ * Set NIC to page 1 registers to get 'current' pointer
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_PAGE_1|ZE_CR_RD2|ZE_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 + ZE_P1_CURR)) {
+
+ /* get pointer to this buffer header structure */
+ packet_ptr = (struct ze_ring *)(sc->smem_ring +
+ (sc->next_packet - sc->rec_page_start) * ZE_PAGE_SIZE);
+
+ /*
+ * The byte count includes the FCS - Frame Check Sequence (a
+ * 32 bit CRC).
+ */
+ len = packet_ptr->count;
+ if ((len >= ETHER_MIN_LEN) && (len <= ETHER_MAX_LEN)) {
+ /*
+ * Go get packet. len - 4 removes CRC from length.
+ * (packet_ptr + 1) points to data just after the packet ring
+ * header (+4 bytes)
+ */
+ ze_get_packet(sc, (caddr_t)(packet_ptr + 1), len - 4);
+ ++sc->arpcom.ac_if.if_ipackets;
+ } else {
+ /*
+ * Really BAD...probably indicates that the ring pointers
+ * are corrupted. Also seen on early rev chips under
+ * high load - the byte order of the length gets switched.
+ */
+ log(LOG_ERR,
+ "ze%d: shared memory corrupt - invalid packet length %d\n",
+ unit, len);
+ ze_reset(unit);
+ return;
+ }
+
+ /*
+ * Update next packet pointer
+ */
+ sc->next_packet = packet_ptr->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 + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA);
+
+ outb(sc->nic_addr + ZE_P0_BNRY, boundry);
+
+ /*
+ * Set NIC to page 1 registers before looping to top (prepare to
+ * get 'CURR' current pointer)
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_PAGE_1|ZE_CR_RD2|ZE_CR_STA);
+ }
+}
+
+/*
+ * Ethernet interface interrupt processor
+ */
+void
+zeintr(unit)
+ int unit;
+{
+ struct ze_softc *sc = &ze_softc[unit];
+ u_char isr;
+
+ /*
+ * Set NIC to page 0 registers
+ */
+ outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA);
+
+ /*
+ * loop until there are no more new interrupts
+ */
+ while (isr = inb(sc->nic_addr + ZE_P0_ISR)) {
+
+ /*
+ * reset all the bits that we are 'acknowleging'
+ * by writing a '1' to each bit position that was set
+ * (writing a '1' *clears* the bit)
+ */
+ outb(sc->nic_addr + ZE_P0_ISR, isr);
+
+ /*
+ * 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.
+ */
+ if (isr & ZE_ISR_TXE) {
+ u_char tsr = inb(sc->nic_addr + ZE_P0_TSR);
+ u_char ncr = inb(sc->nic_addr + ZE_P0_NCR);
+
+ /*
+ * Excessive collisions (16)
+ */
+ if ((tsr & ZE_TSR_ABT) && (ncr == 0)) {
+ /*
+ * When collisions total 16, the P0_NCR will
+ * indicate 0, and the TSR_ABT is set.
+ */
+ sc->arpcom.ac_if.if_collisions += 16;
+ } else
+ sc->arpcom.ac_if.if_collisions += ncr;
+
+ /*
+ * update output errors counter
+ */
+ ++sc->arpcom.ac_if.if_oerrors;
+
+ /*
+ * reset tx busy and output active flags
+ */
+ sc->xmit_busy = 0;
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * clear watchdog timer
+ */
+ sc->arpcom.ac_if.if_timer = 0;
+ }
+
+
+ /*
+ * Receiver Error. One or more of: CRC error, frame alignment error
+ * FIFO overrun, or missed packet.
+ */
+ if (isr & ZE_ISR_RXE) {
+ ++sc->arpcom.ac_if.if_ierrors;
+#ifdef ZE_DEBUG
+#if 0
+ printf("ze%d: receive error %x\n", unit,
+ inb(sc->nic_addr + ZE_P0_RSR));
+#else
+ printf("ze%d: receive error %b\n", unit,
+ inb(sc->nic_addr + ZE_P0_RSR),
+ "\20\8DEF\7REC DISAB\6PHY/MC\5MISSED\4OVR\3ALIGN\2FCS\1RCVD");
+#endif
+#endif
+ }
+
+ /*
+ * 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 & ZE_ISR_OVW) {
+ ++sc->arpcom.ac_if.if_ierrors;
+#if 0
+ /* sigh. this happens too often on our net */
+ log(LOG_WARNING,
+ "ze%d: warning - receiver ring buffer overrun\n",
+ unit);
+#endif
+ /*
+ * Stop/reset/re-init NIC
+ */
+ ze_reset(unit);
+ }
+
+ /*
+ * Transmission completed normally.
+ */
+ if (isr & ZE_ISR_PTX) {
+
+ /*
+ * reset tx busy and output active flags
+ */
+ sc->xmit_busy = 0;
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * clear watchdog timer
+ */
+ sc->arpcom.ac_if.if_timer = 0;
+
+ /*
+ * Update total number of successfully transmitted
+ * packets.
+ */
+ ++sc->arpcom.ac_if.if_opackets;
+
+ /*
+ * Add in total number of collisions on last
+ * transmission.
+ */
+ sc->arpcom.ac_if.if_collisions += inb(sc->nic_addr +
+ ZE_P0_TBCR0);
+ }
+
+ /*
+ * Receive Completion. Go and get the packet.
+ * 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).
+ */
+ if (isr & (ZE_ISR_PRX|ZE_ISR_RXE)) {
+#if 0
+ /*
+ * Enable access to shared memory on WD/SMC boards
+ */
+ if (sc->memwidth == 16)
+ if (sc->vendor == ZE_VENDOR_WD_SMC) {
+ outb(sc->asic_addr + ZE_WD_LAAR,
+ inb(sc->asic_addr + ZE_WD_LAAR)
+ | ZE_WD_LAAR_M16EN);
+ }
+#endif
+ ze_rint (unit);
+
+#if 0
+ /*
+ * Disable access to shared memory
+ */
+ if (sc->memwidth == 16)
+ if (sc->vendor == ZE_VENDOR_WD_SMC) {
+ outb(sc->asic_addr + ZE_WD_LAAR,
+ inb(sc->asic_addr + ZE_WD_LAAR)
+ & ~ZE_WD_LAAR_M16EN);
+ }
+#endif
+ }
+
+ /*
+ * If it looks like the transmitter can take more data,
+ * attempt to start output on the interface. If data is
+ * already buffered and ready to go, send it first.
+ */
+ if ((sc->arpcom.ac_if.if_flags & IFF_OACTIVE) == 0) {
+ if (sc->data_buffered)
+ ze_xmit(&sc->arpcom.ac_if);
+ ze_start(&sc->arpcom.ac_if);
+ }
+
+ /*
+ * 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 + ZE_P0_CR, ZE_CR_RD2|ZE_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 & ZE_ISR_CNT) {
+ (void) inb(sc->nic_addr + ZE_P0_CNTR0);
+ (void) inb(sc->nic_addr + ZE_P0_CNTR1);
+ (void) inb(sc->nic_addr + ZE_P0_CNTR2);
+ }
+ }
+}
+
+/*
+ * Process an ioctl request. This code needs some work - it looks
+ * pretty ugly.
+ */
+int
+ze_ioctl(ifp, command, data)
+ register struct ifnet *ifp;
+ int command;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ze_softc *sc = &ze_softc[ifp->if_unit];
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s, error = 0;
+
+ s = splnet();
+
+ switch (command) {
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ ze_init(ifp->if_unit); /* before arpwhohas */
+ /*
+ * See if another station has *our* IP address.
+ * i.e.: There is an address conflict! If a
+ * conflict exists, a message is sent to the
+ * console.
+ */
+ ((struct arpcom *)ifp)->ac_ipaddr =
+ IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+ 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
+ */
+ ze_init(ifp->if_unit);
+ break;
+ }
+#endif
+ default:
+ ze_init(ifp->if_unit);
+ break;
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+ /*
+ * If interface is marked down and it is running, then stop it
+ */
+ if (((ifp->if_flags & IFF_UP) == 0) &&
+ (ifp->if_flags & IFF_RUNNING)) {
+ ze_stop(ifp->if_unit);
+ ifp->if_flags &= ~IFF_RUNNING;
+ } else {
+ /*
+ * If interface is marked up and it is stopped, then start it
+ */
+ if ((ifp->if_flags & IFF_UP) &&
+ ((ifp->if_flags & IFF_RUNNING) == 0))
+ ze_init(ifp->if_unit);
+ }
+#if NBPFILTER > 0
+ if (ifp->if_flags & IFF_PROMISC) {
+ /*
+ * Set promiscuous mode on interface.
+ * XXX - for multicasts to work, we would need to
+ * write 1's in all bits of multicast
+ * hashing array. For now we assume that
+ * this was done in ze_init().
+ */
+ outb(sc->nic_addr + ZE_P0_RCR,
+ ZE_RCR_PRO|ZE_RCR_AM|ZE_RCR_AB);
+ } else {
+ /*
+ * XXX - for multicasts to work, we would need to
+ * rewrite the multicast hashing array with the
+ * proper hash (would have been destroyed above).
+ */
+ outb(sc->nic_addr + ZE_P0_RCR, ZE_RCR_AB);
+ }
+#endif
+#if 0
+ /*
+ * An unfortunate hack to provide the (required) software control
+ * of the tranceiver for 3Com boards. The LLC0 flag disables
+ * the tranceiver if set.
+ */
+ if (sc->vendor == ZE_VENDOR_3COM) {
+ if (ifp->if_flags & IFF_LLC0) {
+ outb(sc->asic_addr + ZE_3COM_CR, 0);
+ } else {
+ outb(sc->asic_addr + ZE_3COM_CR, ZE_3COM_CR_XSEL);
+ }
+ }
+#endif
+
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ (void) splx(s);
+ return (error);
+}
+
+/*
+ * Macro to calculate a new address within shared memory when given an offset
+ * from an address, taking into account ring-wrap.
+ */
+#define ringoffset(sc, start, off, type) \
+ ((type)( ((caddr_t)(start)+(off) >= (sc)->smem_end) ? \
+ (((caddr_t)(start)+(off))) - (sc)->smem_end \
+ + (sc)->smem_ring: \
+ ((caddr_t)(start)+(off)) ))
+
+/*
+ * 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.
+ */
+void
+ze_get_packet(sc, buf, len)
+ struct ze_softc *sc;
+ char *buf;
+ u_short len;
+{
+ struct ether_header *eh;
+ struct mbuf *m, *head = NULL, *ze_ring_to_mbuf();
+ u_short off;
+ int resid;
+ u_short etype;
+ struct trailer_header {
+ u_short trail_type;
+ u_short trail_residual;
+ } trailer_header;
+
+ /* Allocate a header mbuf */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ goto bad;
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ m->m_pkthdr.len = len;
+ m->m_len = 0;
+ head = m;
+
+ eh = (struct ether_header *)buf;
+
+ /* The following sillines is to make NFS happy */
+#define EROUND ((sizeof(struct ether_header) + 3) & ~3)
+#define EOFF (EROUND - sizeof(struct ether_header))
+
+ /*
+ * The following assumes there is room for
+ * the ether header in the header mbuf
+ */
+ head->m_data += EOFF;
+ bcopy(buf, mtod(head, caddr_t), sizeof(struct ether_header));
+ buf += sizeof(struct ether_header);
+ head->m_len += sizeof(struct ether_header);
+ len -= sizeof(struct ether_header);
+
+ etype = ntohs((u_short)eh->ether_type);
+
+ /*
+ * Deal with trailer protocol:
+ * If trailer protocol, calculate the datasize as 'off',
+ * which is also the offset to the trailer header.
+ * Set resid to the amount of packet data following the
+ * trailer header.
+ * Finally, copy residual data into mbuf chain.
+ */
+ if (etype >= ETHERTYPE_TRAIL &&
+ etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
+
+ off = (etype - ETHERTYPE_TRAIL) << 9;
+ if ((off + sizeof(struct trailer_header)) > len)
+ goto bad; /* insanity */
+
+ eh->ether_type = *ringoffset(sc, buf, off, u_short *);
+ resid = ntohs(*ringoffset(sc, buf, off+2, u_short *));
+
+ if ((off + resid) > len) goto bad; /* insanity */
+
+ resid -= sizeof(struct trailer_header);
+ if (resid < 0) goto bad; /* insanity */
+
+ m = ze_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *), head, resid);
+ if (m == NULL) goto bad;
+
+ len = off;
+ head->m_pkthdr.len -= 4; /* subtract trailer header */
+ }
+
+ /*
+ * Pull packet off interface. Or if this was a trailer packet,
+ * the data portion is appended.
+ */
+ m = ze_ring_to_mbuf(sc, buf, m, len);
+ if (m == NULL) goto bad;
+
+#if NBPFILTER > 0
+ /*
+ * Check if there's a BPF listener on this interface.
+ * If so, hand off the raw packet to bpf.
+ */
+ if (sc->bpf) {
+ bpf_mtap(sc->bpf, head);
+
+ /*
+ * 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.
+ *
+ * XXX This test does not support multicasts.
+ */
+ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) &&
+ 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(head);
+ return;
+ }
+ }
+#endif
+
+ /*
+ * Fix up data start offset in mbuf to point past ether header
+ */
+ m_adj(head, sizeof(struct ether_header));
+
+ /*
+ * silly ether_input routine needs 'type' in host byte order
+ */
+ eh->ether_type = ntohs(eh->ether_type);
+
+ ether_input(&sc->arpcom.ac_if, eh, head);
+ return;
+
+bad: if (head)
+ m_freem(head);
+ return;
+}
+
+/*
+ * Supporting routines
+ */
+
+/*
+ * 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 *
+ze_ring_copy(sc,src,dst,amount)
+ struct ze_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->smem_end) {
+ tmp_amount = sc->smem_end - src;
+ bcopy(src,dst,tmp_amount); /* copy amount up to end of smem */
+ amount -= tmp_amount;
+ src = sc->smem_ring;
+ dst += tmp_amount;
+ }
+
+ bcopy(src, dst, amount);
+
+ return(src + amount);
+}
+
+/*
+ * Copy data from receive buffer to end of mbuf chain
+ * allocate additional mbufs as needed. return pointer
+ * to last mbuf in chain.
+ * sc = ze info (softc)
+ * src = pointer in ze ring buffer
+ * dst = pointer to last mbuf in mbuf chain to copy to
+ * amount = amount of data to copy
+ */
+struct mbuf *
+ze_ring_to_mbuf(sc,src,dst,total_len)
+ struct ze_softc *sc;
+ char *src;
+ struct mbuf *dst;
+ u_short total_len;
+{
+ register struct mbuf *m = dst;
+
+ while (total_len) {
+ register u_short amount = min(total_len, M_TRAILINGSPACE(m));
+
+ if (amount == 0) { /* no more data in this mbuf, alloc another */
+ /*
+ * If there is enough data for an mbuf cluster, attempt
+ * to allocate one of those, otherwise, a regular
+ * mbuf will do.
+ * Note that a regular mbuf is always required, even if
+ * we get a cluster - getting a cluster does not
+ * allocate any mbufs, and one is needed to assign
+ * the cluster to. The mbuf that has a cluster
+ * extension can not be used to contain data - only
+ * the cluster can contain data.
+ */
+ dst = m;
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return (0);
+
+ if (total_len >= MINCLSIZE)
+ MCLGET(m, M_DONTWAIT);
+
+ m->m_len = 0;
+ dst->m_next = m;
+ amount = min(total_len, M_TRAILINGSPACE(m));
+ }
+
+ src = ze_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount);
+
+ m->m_len += amount;
+ total_len -= amount;
+
+ }
+ return (m);
+}
+#endif
+
diff --git a/sys/i386/isa/if_zereg.h b/sys/i386/isa/if_zereg.h
new file mode 100644
index 000000000000..3cd501f682cb
--- /dev/null
+++ b/sys/i386/isa/if_zereg.h
@@ -0,0 +1,859 @@
+/*
+ * National Semiconductor DS8390 NIC register definitions
+ *
+ * if_edreg.h,v
+ * Revision 1.1.2.1 1993/07/21 13:50:04 cgd
+ * from davidg:
+ * Added config file override for memory size and added flags to force
+ * 8bit or 16bit operation, and a flag to disable transmitter double buffering.
+ * See the updated "ed.relnotes" file for information about how to set
+ * the flags.
+ * This should be considered the first "production" release. It still
+ * needs a manual page, though.
+ *
+ * Revision 1.1 1993/07/03 12:21:07 cgd
+ * add support for David Greenman "ed" driver
+ *
+ * Revision 1.2 93/06/23 03:03:05 davidg
+ * added some additional definitions for the 83C584 bus interface
+ * chip (SMC/WD boards)
+ *
+ * Revision 1.1 93/06/23 03:01:07 davidg
+ * Initial revision
+ *
+ */
+
+/*
+ * Page 0 register offsets
+ */
+#define ZE_P0_CR 0x00 /* Command Register */
+
+#define ZE_P0_CLDA0 0x01 /* Current Local DMA Addr low (read) */
+#define ZE_P0_PSTART 0x01 /* Page Start register (write) */
+
+#define ZE_P0_CLDA1 0x02 /* Current Local DMA Addr high (read) */
+#define ZE_P0_PSTOP 0x02 /* Page Stop register (write) */
+
+#define ZE_P0_BNRY 0x03 /* Boundary Pointer */
+
+#define ZE_P0_TSR 0x04 /* Transmit Status Register (read) */
+#define ZE_P0_TPSR 0x04 /* Transmit Page Start (write) */
+
+#define ZE_P0_NCR 0x05 /* Number of Collisions Reg (read) */
+#define ZE_P0_TBCR0 0x05 /* Transmit Byte count, low (write) */
+
+#define ZE_P0_FIFO 0x06 /* FIFO register (read) */
+#define ZE_P0_TBCR1 0x06 /* Transmit Byte count, high (write) */
+
+#define ZE_P0_ISR 0x07 /* Interrupt Status Register */
+
+#define ZE_P0_CRDA0 0x08 /* Current Remote DMA Addr low (read) */
+#define ZE_P0_RSAR0 0x08 /* Remote Start Address low (write) */
+
+#define ZE_P0_CRDA1 0x09 /* Current Remote DMA Addr high (read) */
+#define ZE_P0_RSAR1 0x09 /* Remote Start Address high (write) */
+
+#define ZE_P0_RBCR0 0x0a /* Remote Byte Count low (write) */
+
+#define ZE_P0_RBCR1 0x0b /* Remote Byte Count high (write) */
+
+#define ZE_P0_RSR 0x0c /* Receive Status (read) */
+#define ZE_P0_RCR 0x0c /* Receive Configuration Reg (write) */
+
+#define ZE_P0_CNTR0 0x0d /* frame alignment error counter (read) */
+#define ZE_P0_TCR 0x0d /* Transmit Configuration Reg (write) */
+
+#define ZE_P0_CNTR1 0x0e /* CRC error counter (read) */
+#define ZE_P0_DCR 0x0e /* Data Configuration Reg (write) */
+
+#define ZE_P0_CNTR2 0x0f /* missed packet counter (read) */
+#define ZE_P0_IMR 0x0f /* Interrupt Mask Register (write) */
+
+/*
+ * Page 1 register offsets
+ */
+#define ZE_P1_CR 0x00 /* Command Register */
+#define ZE_P1_PAR0 0x01 /* Physical Address Register 0 */
+#define ZE_P1_PAR1 0x02 /* Physical Address Register 1 */
+#define ZE_P1_PAR2 0x03 /* Physical Address Register 2 */
+#define ZE_P1_PAR3 0x04 /* Physical Address Register 3 */
+#define ZE_P1_PAR4 0x05 /* Physical Address Register 4 */
+#define ZE_P1_PAR5 0x06 /* Physical Address Register 5 */
+#define ZE_P1_CURR 0x07 /* Current RX ring-buffer page */
+#define ZE_P1_MAR0 0x08 /* Multicast Address Register 0 */
+#define ZE_P1_MAR1 0x09 /* Multicast Address Register 1 */
+#define ZE_P1_MAR2 0x0a /* Multicast Address Register 2 */
+#define ZE_P1_MAR3 0x0b /* Multicast Address Register 3 */
+#define ZE_P1_MAR4 0x0c /* Multicast Address Register 4 */
+#define ZE_P1_MAR5 0x0d /* Multicast Address Register 5 */
+#define ZE_P1_MAR6 0x0e /* Multicast Address Register 6 */
+#define ZE_P1_MAR7 0x0f /* Multicast Address Register 7 */
+
+/*
+ * Page 2 register offsets
+ */
+#define ZE_P2_CR 0x00 /* Command Register */
+#define ZE_P2_PSTART 0x01 /* Page Start (read) */
+#define ZE_P2_CLDA0 0x01 /* Current Local DMA Addr 0 (write) */
+#define ZE_P2_PSTOP 0x02 /* Page Stop (read) */
+#define ZE_P2_CLDA1 0x02 /* Current Local DMA Addr 1 (write) */
+#define ZE_P2_RNPP 0x03 /* Remote Next Packet Pointer */
+#define ZE_P2_TPSR 0x04 /* Transmit Page Start (read) */
+#define ZE_P2_LNPP 0x05 /* Local Next Packet Pointer */
+#define ZE_P2_ACU 0x06 /* Address Counter Upper */
+#define ZE_P2_ACL 0x07 /* Address Counter Lower */
+#define ZE_P2_RCR 0x0c /* Receive Configuration Register (read) */
+#define ZE_P2_TCR 0x0d /* Transmit Configuration Register (read) */
+#define ZE_P2_DCR 0x0e /* Data Configuration Register (read) */
+#define ZE_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 ZE_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 ZE_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 ZE_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 ZE_CR_RD0 0x08
+#define ZE_CR_RD1 0x10
+#define ZE_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 ZE_CR_PS0 0x40
+#define ZE_CR_PS1 0x80
+/* bit encoded aliases */
+#define ZE_CR_PAGE_0 0x00 /* (for consistency) */
+#define ZE_CR_PAGE_1 0x40
+#define ZE_CR_PAGE_2 0x80
+
+/*
+ * Interrupt Status Register (ISR) definitions
+ */
+
+/*
+ * PRX: Packet Received. Indicates packet received with no errors.
+ */
+#define ZE_ISR_PRX 0x01
+
+/*
+ * PTX: Packet Transmitted. Indicates packet transmitted with no errors.
+ */
+#define ZE_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 ZE_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 ZE_ISR_TXE 0x08
+
+/*
+ * OVW: OverWrite. Indicates a receive ring-buffer overrun. Incoming network
+ * would exceed (has exceeded?) the boundry pointer, resulting in data
+ * that was previously received and not yet read from the buffer to be
+ * overwritten.
+ */
+#define ZE_ISR_OVW 0x10
+
+/*
+ * CNT: Counter Overflow. Set when the MSB of one or more of the Network Talley
+ * Counters has been set.
+ */
+#define ZE_ISR_CNT 0x20
+
+/*
+ * RDC: Remote Data Complete. Indicates that a Remote DMA operation has completed.
+ */
+#define ZE_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 ZE_ISR_RST 0x80
+
+/*
+ * Interrupt Mask Register (IMR) definitions
+ */
+
+/*
+ * PRXE: Packet Received interrupt Enable. If set, a received packet will cause
+ * an interrupt.
+ */
+#define ZE_IMR_PRXE 0x01
+
+/*
+ * PTXE: Packet Transmit interrupt Enable. If set, an interrupt is generated when
+ * a packet transmission completes.
+ */
+#define ZE_IMR_PTXE 0x02
+
+/*
+ * RXEE: Receive Error interrupt Enable. If set, an interrupt will occur whenever a
+ * packet is received with an error.
+ */
+#define ZE_IMR_RXEE 0x04
+
+/*
+ * TXEE: Transmit Error interrupt Enable. If set, an interrupt will occur whenever
+ * a transmission results in an error.
+ */
+#define ZE_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 boundry pointer is exceeded.
+ */
+#define ZE_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 ZE_IMR_CNTE 0x20
+
+/*
+ * RDCE: Remote DMA Complete interrupt Enable. If set, an interrupt is generated
+ * when a remote DMA transfer has completed.
+ */
+#define ZE_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 ZE_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 ZE_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 ZE_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 ZE_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 boundry pointer is automatically updated
+ */
+#define ZE_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 ZE_DCR_FT0 0x20
+#define ZE_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 ZE_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 ZE_TCR_LB0 0x02
+#define ZE_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 ZE_TCR_ATD 0x08
+
+/*
+ * OFST: Collision Offset enable. This bit when set modifies the backoff
+ * algorithm to allow prioritization of nodes.
+ */
+#define ZE_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 ZE_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 ZE_TSR_COL 0x04
+
+/*
+ * ABT: Transmit aborted. Indicates that the transmission was aborted due to
+ * excessive collisions.
+ */
+#define ZE_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 ZE_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 ZE_TSR_FU 0x20
+
+/*
+ * CDH: CD Heartbeat. Indicates that the collision detection circuitry
+ * isn't working correctly during a collision heartbeat test.
+ */
+#define ZE_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 ZE_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 ZE_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 ZE_RCR_AR 0x02
+
+/*
+ * AB: Accept Broadcast. If set, packets sent to the broadcast address will be
+ * accepted.
+ */
+#define ZE_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 ZE_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 ZE_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 ZE_RCR_MON 0x20
+
+/*
+ * bits 6 and 7 are unused/reserved.
+ */
+
+/*
+ * Receiver Status Register (RSR) definitions
+ */
+
+/*
+ * PRX: Packet Received without error.
+ */
+#define ZE_RSR_PRX 0x01
+
+/*
+ * CRC: CRC error. Indicates that a packet has a CRC error. Also set for frame
+ * alignment errors.
+ */
+#define ZE_RSR_CRC 0x02
+
+/*
+ * FAE: Frame Alignment Error. Indicates that the incoming packet did not end on
+ * a byte boundry and the CRC did not match at the last byte boundry.
+ */
+#define ZE_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 ZE_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
+ * boundry pointer), or because the transfer to the ring-buffer was inhibited
+ * by RCR_MON - monitor mode.
+ */
+#define ZE_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 ZE_RSR_PHY 0x20
+
+/*
+ * DIS: Receiver Disabled. Set to indicate that the receiver has enetered monitor
+ * mode. Cleared when the receiver exits monitor mode.
+ */
+#define ZE_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 ZE_RSR_DFR 0x80
+
+/*
+ * receive ring discriptor
+ *
+ * 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.
+ */
+struct ze_ring {
+ struct edr_status { /* received packet status */
+ u_char rs_prx:1, /* packet received intack */
+ rs_crc:1, /* crc error */
+ rs_fae:1, /* frame alignment error */
+ rs_fo:1, /* fifo overrun */
+ rs_mpa:1, /* packet received intack */
+ rs_phy:1, /* packet received intack */
+ rs_dis:1, /* packet received intack */
+ rs_dfr:1; /* packet received intack */
+ } ze_rcv_status; /* received packet status */
+ u_char next_packet; /* pointer to next packet */
+ u_short count; /* bytes in packet (length + 4) */
+};
+
+/*
+ * Common constants
+ */
+#define ZE_PAGE_SIZE 256 /* Size of RAM pages in bytes */
+#define ZE_TXBUF_SIZE 6 /* Size of TX buffer in pages */
+#define ZE_PAGE_OFFSET 0x40 /* mem buffer starts at 0x4000 */
+
+/*
+ * Vendor types
+ */
+#define ZE_VENDOR_WD_SMC 0x00 /* Western Digital/SMC */
+#define ZE_VENDOR_3COM 0x01 /* 3Com */
+
+/*
+ * Compile-time config flags
+ */
+/*
+ * this sets the default for enabling/disablng the tranceiver
+ */
+#define ZE_FLAGS_DISABLE_TRANCEIVER 0x01
+
+/*
+ * This forces the board to be used in 8/16bit mode even if it
+ * autoconfigs differently
+ */
+#define ZE_FLAGS_FORCE_8BIT_MODE 0x02
+#define ZE_FLAGS_FORCE_16BIT_MODE 0x04
+
+/*
+ * This disables the use of double transmit buffers.
+ */
+#define ZE_FLAGS_NO_DOUBLE_BUFFERING 0x08
+
+/*
+ * definitions for IBM credit card adapter for ethernet
+ */
+
+#define ZE_DATA_IO 0x10
+#define ZE_MISC 0x18
+#define ZE_RESET 0x1F
+
+#if 0
+/*
+ * Definitions for Western digital/SMC WD80x3 series ASIC
+ */
+/*
+ * Memory Select Register (MSR)
+ */
+#define ZE_WD_MSR 0
+
+#define ZE_WD_MSR_ADDR 0x3f /* Memory decode bits 18-13 */
+#define ZE_WD_MSR_MENB 0x40 /* Memory enable */
+#define ZE_WD_MSR_RST 0x80 /* Reset board */
+
+/*
+ * Interface Configuration Register (ICR)
+ */
+#define ZE_WD_ICR 1
+
+#define ZE_WD_ICR_16BIT 0x01 /* 16-bit interface */
+#define ZE_WD_ICR_OAR 0x02 /* select register. 0=BIO 1=EAR */
+#define ZE_WD_ICR_IR2 0x04 /* high order bit of encoded IRQ */
+#define ZE_WD_ICR_MSZ 0x08 /* memory size (0=8k 1=32k) */
+#define ZE_WD_ICR_RLA 0x10 /* recall LAN address */
+#define ZE_WD_ICR_RX7 0x20 /* recall all but i/o and LAN address */
+#define ZE_WD_ICR_RIO 0x40 /* recall i/o address */
+#define ZE_WD_ICR_STO 0x80 /* store to non-volatile memory */
+
+/*
+ * IO Address Register (IAR)
+ */
+#define ZE_WD_IAR 2
+
+/*
+ * EEROM Address Register
+ */
+#define ZE_WD_EAR 3
+
+/*
+ * Interrupt Request Register (IRR)
+ */
+#define ZE_WD_IRR 4
+
+#define ZE_WD_IRR_0WS 0x01 /* use 0 wait-states on 8 bit bus */
+#define ZE_WD_IRR_OUT1 0x02 /* WD83C584 pin 1 output */
+#define ZE_WD_IRR_OUT2 0x04 /* WD83C584 pin 2 output */
+#define ZE_WD_IRR_OUT3 0x08 /* WD83C584 pin 3 output */
+#define ZE_WD_IRR_FLASH 0x10 /* Flash RAM is in the ROM socket */
+
+/*
+ * The three bit 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 ZE_WD_IRR_IR0 0x20 /* bit 0 of encoded IRQ */
+#define ZE_WD_IRR_IR1 0x40 /* bit 1 of encoded IRQ */
+#define ZE_WD_IRR_IEN 0x80 /* Interrupt enable */
+
+/*
+ * LA Address Register (LAAR)
+ */
+#define ZE_WD_LAAR 5
+
+#define ZE_WD_LAAR_ADDRHI 0x1f /* bits 23-19 of RAM address */
+#define ZE_WD_LAAR_0WS16 0x20 /* enable 0 wait-states on 16 bit bus */
+#define ZE_WD_LAAR_L16EN 0x40 /* enable 16-bit operation */
+#define ZE_WD_LAAR_M16EN 0x80 /* enable 16-bit memory access */
+
+/* i/o base offset to station address/card-ID PROM */
+#define ZE_WD_PROM 8
+
+/* i/o base offset to CARD ID */
+#define ZE_WD_CARD_ID ZE_WD_PROM+6
+
+#define ZE_TYPE_WD8003S 0x02
+#define ZE_TYPE_WD8003E 0x03
+#define ZE_TYPE_WD8013EBT 0x05
+#define ZE_TYPE_WD8013EB 0x27
+#define ZE_TYPE_WD8013EBP 0x2c
+#define ZE_TYPE_WD8013EPC 0x29
+
+/* Bit definitions in card ID */
+#define ZE_WD_REV_MASK 0x1f /* Revision mask */
+#define ZE_WD_SOFTCONFIG 0x20 /* Soft config */
+#define ZE_WD_LARGERAM 0x40 /* Large RAM */
+#define ZE_MICROCHANEL 0x80 /* Microchannel bus (vs. isa) */
+
+/*
+ * Checksum total. All 8 bytes in station address PROM will add up to this
+ */
+#define ZE_WD_ROM_CHECKSUM_TOTAL 0xFF
+
+#define ZE_WD_NIC_OFFSET 0x10 /* I/O base offset to NIC */
+#define ZE_WD_ASIC_OFFSET 0 /* I/O base offset to ASIC */
+#define ZE_WD_IO_PORTS 32 /* # of i/o addresses used */
+
+#define ZE_WD_PAGE_OFFSET 0 /* page offset for NIC access to mem */
+
+/*
+ * Definitions for 3Com 3c503
+ */
+#define ZE_3COM_NIC_OFFSET 0
+#define ZE_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 ZE_3COM_IO_PORTS 16 /* # of i/o addresses used */
+
+#define ZE_3COM_PAGE_OFFSET 0x20 /* memory starts in second bank */
+
+/*
+ * Page Start Register. Must match PSTART in NIC
+ */
+#define ZE_3COM_PSTR 0
+
+/*
+ * Page Stop Register. Must match PSTOP in NIC
+ */
+#define ZE_3COM_PSPR 1
+
+/*
+ * Drq Timer Register. Determines number of bytes to be transfered during
+ * a DMA burst.
+ */
+#define ZE_3COM_DQTR 2
+
+/*
+ * Base Configuration Register. Read-only register which contains the
+ * board-configured I/O base address of the adapter. Bit encoded.
+ */
+#define ZE_3COM_BCFR 3
+
+#define ZE_3COM_BCFR_2E0 0x01
+#define ZE_3COM_BCFR_2A0 0x02
+#define ZE_3COM_BCFR_280 0x04
+#define ZE_3COM_BCFR_250 0x08
+#define ZE_3COM_BCFR_350 0x10
+#define ZE_3COM_BCFR_330 0x20
+#define ZE_3COM_BCFR_310 0x40
+#define ZE_3COM_BCFR_300 0x80
+
+/*
+ * EPROM Configuration Register. Read-only register which contains the
+ * board-configured memory base address. Bit encoded.
+ */
+#define ZE_3COM_PCFR 4
+
+#define ZE_3COM_PCFR_C8000 0x10
+#define ZE_3COM_PCFR_CC000 0x20
+#define ZE_3COM_PCFR_D8000 0x40
+#define ZE_3COM_PCFR_DC000 0x80
+
+/*
+ * GA Configuration Register. Gate-Array Configuration Register.
+ */
+#define ZE_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 ZE_3COM_GACFR_MBS0 0x01
+#define ZE_3COM_GACFR_MBS1 0x02
+#define ZE_3COM_GACFR_MBS2 0x04
+
+#define ZE_3COM_GACFR_RSEL 0x08 /* enable shared memory */
+#define ZE_3COM_GACFR_TEST 0x10 /* for GA testing */
+#define ZE_3COM_GACFR_OWS 0x20 /* select 0WS access to GA */
+#define ZE_3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */
+#define ZE_3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */
+
+/*
+ * Control Register. Miscellaneous control functions.
+ */
+#define ZE_3COM_CR 6
+
+#define ZE_3COM_CR_RST 0x01 /* Reset GA and NIC */
+#define ZE_3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */
+#define ZE_3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */
+#define ZE_3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */
+#define ZE_3COM_CR_SHARE 0x10 /* select interrupt sharing option */
+#define ZE_3COM_CR_DBSEL 0x20 /* Double buffer select */
+#define ZE_3COM_CR_DDIR 0x40 /* DMA direction select */
+#define ZE_3COM_CR_START 0x80 /* Start DMA controller */
+
+/*
+ * Status Register. Miscellaneous status information.
+ */
+#define ZE_3COM_STREG 7
+
+#define ZE_3COM_STREG_REV 0x07 /* GA revision */
+#define ZE_3COM_STREG_DIP 0x08 /* DMA in progress */
+#define ZE_3COM_STREG_DTC 0x10 /* DMA terminal count */
+#define ZE_3COM_STREG_OFLW 0x20 /* Overflow */
+#define ZE_3COM_STREG_UFLW 0x40 /* Underflow */
+#define ZE_3COM_STREG_DPRDY 0x80 /* Data port ready */
+
+/*
+ * Interrupt/DMA Configuration Register
+ */
+#define ZE_3COM_IDCFR 8
+
+#define ZE_3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */
+#define ZE_3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */
+#define ZE_3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */
+#define ZE_3COM_IDCFR_UNUSED 0x08 /* not used */
+#define ZE_3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */
+#define ZE_3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */
+#define ZE_3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */
+#define ZE_3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */
+
+/*
+ * DMA Address Register MSB
+ */
+#define ZE_3COM_DAMSB 9
+
+/*
+ * DMA Address Register LSB
+ */
+#define ZE_3COM_DALSB 0x0a
+
+/*
+ * Vector Pointer Register 2
+ */
+#define ZE_3COM_VPTR2 0x0b
+
+/*
+ * Vector Pointer Register 1
+ */
+#define ZE_3COM_VPTR1 0x0c
+
+/*
+ * Vector Pointer Register 0
+ */
+#define ZE_3COM_VPTR0 0x0d
+
+/*
+ * Register File Access MSB
+ */
+#define ZE_3COM_RFMSB 0x0e
+
+/*
+ * Register File Access LSB
+ */
+#define ZE_3COM_RFLSB 0x0f
+#endif
diff --git a/sys/i386/isa/ipl.h b/sys/i386/isa/ipl.h
new file mode 100644
index 000000000000..248ca5666d67
--- /dev/null
+++ b/sys/i386/isa/ipl.h
@@ -0,0 +1,7 @@
+#ifndef _ISA_IPL_H_
+#define _ISA_IPL_H_
+
+#define NHWI 16 /* number of h/w interrupts */
+#define HWI_MASK 0xffff /* bits corresponding to h/w interrupts */
+
+#endif /* _ISA_IPL_H_ */
diff --git a/sys/i386/isa/isa.c b/sys/i386/isa/isa.c
index 3dbc748d9d55..588be2cccc7f 100644
--- a/sys/i386/isa/isa.c
+++ b/sys/i386/isa/isa.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)isa.c 7.2 (Berkeley) 5/13/91
- * $Id: isa.c,v 1.14 1994/01/22 21:52:04 rgrimes Exp $
+ * $Id: isa.c,v 1.19 1994/06/22 05:52:39 jkh Exp $
*/
/*
@@ -124,9 +124,11 @@ haveseen(dvp, tmpdvp)
if ((dvp->id_iobase >= tmpdvp->id_iobase) &&
(dvp->id_iobase <=
(tmpdvp->id_iobase + tmpdvp->id_alive - 1))) {
+#ifndef ALLOW_CONFLICT_IOADDR
conflict(dvp, tmpdvp, dvp->id_iobase,
"I/O address", "0x%x");
status = 1;
+#endif
}
}
/*
@@ -143,12 +145,14 @@ haveseen(dvp, tmpdvp)
if((KERNBASE + dvp->id_maddr >= tmpdvp->id_maddr) &&
(KERNBASE + dvp->id_maddr <=
(tmpdvp->id_maddr + tmpdvp->id_msize - 1))) {
+#ifndef ALLOW_CONFLICT_MEMADDR
conflict(dvp, tmpdvp, dvp->id_maddr, "maddr",
"0x%x");
status = 1;
+#endif
}
}
-#ifndef COM_MULTIPORT
+#ifndef ALLOW_CONFLICT_IRQ
/*
* Check for IRQ conflicts.
*/
@@ -160,6 +164,7 @@ haveseen(dvp, tmpdvp)
}
}
#endif
+#ifndef ALLOW_CONFLICT_DRQ
/*
* Check for DRQ conflicts.
*/
@@ -170,6 +175,7 @@ haveseen(dvp, tmpdvp)
status = 1;
}
}
+#endif
}
return (status);
}
@@ -213,38 +219,45 @@ isa_configure() {
printf("Probing for devices on the ISA bus:\n");
for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) {
if (!haveseen_isadev(dvp))
- config_isadev(dvp,&ttymask);
+ config_isadev(dvp,&tty_imask);
}
for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) {
if (!haveseen_isadev(dvp))
- config_isadev(dvp,&biomask);
+ config_isadev(dvp,&bio_imask);
}
for (dvp = isa_devtab_net; dvp->id_driver; dvp++) {
if (!haveseen_isadev(dvp))
- config_isadev(dvp,&netmask);
+ config_isadev(dvp,&net_imask);
}
for (dvp = isa_devtab_null; dvp->id_driver; dvp++) {
if (!haveseen_isadev(dvp))
config_isadev(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 netmask when the line is
+ * 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 splnet when only one
+ * 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
- netmask |= ttymask;
- ttymask |= netmask;
+ net_imask |= tty_imask;
+ tty_imask = net_imask;
+#endif
+ /* bio_imask |= tty_imask ; can some tty devices use buffers? */
+#ifdef DIAGNOSTIC
+ printf("bio_imask %x tty_imask %x net_imask %x\n",
+ bio_imask, tty_imask, net_imask);
#endif
- /* if netmask == 0, then the loopback code can do some really
- * bad things.
- */
- if (netmask == 0)
- netmask = 0x10000;
- /* biomask |= ttymask ; can some tty devices use buffers? */
- printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask);
splnone();
}
@@ -337,15 +350,12 @@ extern inthand_t
IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11),
IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15);
-static inthand_func_t defvec[16] = {
+static inthand_func_t defvec[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) };
-/* out of range default interrupt vector gate entry */
-extern inthand_t IDTVEC(intrdefault);
-
/*
* Fill in default interrupt table (in case of spuruious interrupt
* during configuration of kernel, setup interrupt control unit
@@ -356,12 +366,8 @@ isa_defaultirq()
int i;
/* icu vectors */
- for (i = NRSVIDT ; i < NRSVIDT+ICU_LEN ; i++)
- setidt(i, defvec[i], SDT_SYS386IGT, SEL_KPL);
-
- /* out of range vectors */
- for (i = NRSVIDT; i < NIDT; i++)
- setidt(i, &IDTVEC(intrdefault), SDT_SYS386IGT, SEL_KPL);
+ for (i = 0; i < ICU_LEN; i++)
+ setidt(ICU_OFFSET + i, defvec[i], SDT_SYS386IGT, SEL_KPL);
/* initialize 8259's */
outb(IO_ICU1, 0x11); /* reset; program device, four bytes */
@@ -530,7 +536,7 @@ isa_dmarangecheck(caddr_t va, unsigned length, unsigned chan) {
#define ISARAM_END RAM_END
if (phys == 0)
panic("isa_dmacheck: no physical page present");
- if (phys > ISARAM_END)
+ if (phys >= ISARAM_END)
return (1);
if (priorpage) {
if (priorpage + NBPG != phys)
@@ -588,14 +594,18 @@ isa_freephysmem(caddr_t va, unsigned length) {
/*
* Handle a NMI, possibly a machine check.
+ * This is generally one of two things, either an memory parity error
+ * or a bus master timeout failure. A bus-master timeout is indicated
+ * by bit 4 of port 0x461 going high.
+ *
* return true to panic system, false to ignore.
*/
int
isa_nmi(cd)
int cd;
{
-
- log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70));
+ log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x, port 461 %x\n",
+ inb(0x61), inb(0x70), inb(0x461));
return(0);
}
@@ -627,165 +637,6 @@ isa_strayintr(d)
}
/*
- * Wait "n" microseconds.
- * Relies on timer 1 counting down from (TIMER_FREQ / hz) at
- * (1 * TIMER_FREQ) Hz.
- * Note: timer had better have been programmed before this is first used!
- * (The standard programming causes the timer to generate a square wave and
- * the counter is decremented twice every cycle.)
- */
-#define CF (1 * TIMER_FREQ)
-#define TIMER_FREQ 1193182 /* XXX - should be elsewhere */
-
-void
-DELAY(n)
- int n;
-{
- int counter_limit;
- int prev_tick;
- int tick;
- int ticks_left;
- int sec;
- int 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(0, 0);
- n -= 20;
-
- /*
- * Calculate (n * (CF / 1e6)) without using floating point and without
- * any avoidable overflows.
- */
- sec = n / 1000000;
- usec = n - sec * 1000000;
- ticks_left = sec * CF
- + usec * (CF / 1000000)
- + usec * ((CF % 1000000) / 1000) / 1000
- + usec * (CF % 1000) / 1000000;
-
- counter_limit = TIMER_FREQ / hz;
- while (ticks_left > 0) {
- tick = getit(0, 0);
-#ifdef DELAYDEBUG
- ++getit_calls;
-#endif
- if (tick > prev_tick)
- ticks_left -= prev_tick - (tick - counter_limit);
- 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
-}
-
-int
-getit(unit, timer)
- int unit;
- int timer;
-{
- int high;
- int low;
-
- /*
- * XXX - isa.h defines bogus timers. There's no such timer as
- * IO_TIMER_2 = 0x48. There's a timer in the CMOS RAM chip but
- * its interface is quite different. Neither timer is an 8252.
- * We actually only call this with unit = 0 and timer = 0. It
- * could be static...
- */
- /*
- * Protect ourself against interrupts.
- * XXX - sysbeep() and sysbeepstop() need protection.
- */
- disable_intr();
- /*
- * Latch the count for 'timer' (cc00xxxx, c = counter, x = any).
- */
- outb(IO_TIMER1 + 3, timer << 6);
-
- low = inb(IO_TIMER1 + timer);
- high = inb(IO_TIMER1 + timer);
- enable_intr();
- return ((high << 8) | low);
-}
-
-static int beeping;
-
-static void
-sysbeepstop(f, dummy)
- caddr_t f;
- int dummy;
-{
- /* disable counter 2 */
- outb(0x61, inb(0x61) & 0xFC);
- if (f)
- timeout(sysbeepstop, (caddr_t)0, (int)f);
- else
- beeping = 0;
-}
-
-void
-sysbeep(int pitch, int period)
-{
-
- outb(0x61, inb(0x61) | 3); /* enable counter 2 */
- /*
- * XXX - move timer stuff to clock.c.
- * Program counter 2:
- * ccaammmb, c counter, a = access, m = mode, b = BCD
- * 1011x110, 11 for aa = LSB then MSB, x11 for mmm = square wave.
- */
- outb(0x43, 0xb6); /* set command for counter 2, 2 byte write */
-
- outb(0x42, pitch);
- outb(0x42, (pitch>>8));
-
- if (!beeping) {
- beeping = period;
- timeout(sysbeepstop, (caddr_t)(period/2), period);
- }
-}
-
-/*
- * Pass command to keyboard controller (8042)
- */
-unsigned
-kbc_8042cmd(val)
- int val;
-{
-
- while (inb(KBSTATP)&KBS_IBF);
- if (val) outb(KBCMDP, val);
- while (inb(KBSTATP)&KBS_IBF);
- return (inb(KBDATAP));
-}
-
-/*
* find an ISA device in a given isa_devtab_* table, given
* the table to search, the expected id_driver entry, and the unit number.
*
diff --git a/sys/i386/isa/isa.h b/sys/i386/isa/isa.h
index cb083fdcf4cb..32786e6008c1 100644
--- a/sys/i386/isa/isa.h
+++ b/sys/i386/isa/isa.h
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)isa.h 5.7 (Berkeley) 5/9/91
- * $Id: isa.h,v 1.4 1994/01/05 15:03:28 rgrimes Exp $
+ * $Id: isa.h,v 1.5 1994/04/21 14:20:54 sos Exp $
*/
#ifndef _I386_ISA_ISA_H_
@@ -47,12 +47,8 @@
#ifndef LOCORE
#include <sys/cdefs.h>
-unsigned char rtcin __P((int));
extern unsigned int atdevbase; /* offset in virtual memory of ISA io mem */
-void sysbeep __P((int, int));
-unsigned kbd_8042cmd __P((int));
-struct isa_device;
-int isa_irq_pending __P((struct isa_device *dvp));
+unsigned char rtcin __P((int));
#endif
@@ -69,6 +65,7 @@ int isa_irq_pending __P((struct isa_device *dvp));
#define IO_TIMER1 0x040 /* 8253 Timer #1 */
#define IO_TIMER2 0x048 /* 8253 Timer #2 */
#define IO_KBD 0x060 /* 8042 Keyboard */
+#define IO_PPI 0x061 /* Programmabel Peripheral Interface */
#define IO_RTC 0x070 /* RTC */
#define IO_NMI IO_RTC /* NMI Control */
#define IO_DMAPG 0x080 /* DMA Page Registers */
diff --git a/sys/i386/isa/isa_device.h b/sys/i386/isa/isa_device.h
index 6299f89912a4..670dacf4dfa6 100644
--- a/sys/i386/isa/isa_device.h
+++ b/sys/i386/isa/isa_device.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)isa_device.h 7.1 (Berkeley) 5/9/91
- * $Id: isa_device.h,v 1.5 1994/01/04 20:06:30 nate Exp $
+ * $Id: isa_device.h,v 1.7 1994/06/08 14:01:04 davidg Exp $
*/
#ifndef _I386_ISA_ISA_DEVICE_H_
@@ -46,7 +46,7 @@
*/
struct isa_device {
struct isa_driver *id_driver;
- short id_iobase; /* base i/o address */
+ 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)*/
diff --git a/sys/i386/isa/kbdtables.h b/sys/i386/isa/kbdtables.h
index 011bc2cd1879..1fbd3215f367 100644
--- a/sys/i386/isa/kbdtables.h
+++ b/sys/i386/isa/kbdtables.h
@@ -14,7 +14,7 @@
* DK9210 Aalborg SO Phone: +45 9814 8076
*
* @(#)kbdtables.h 1.3 940123
- * $Id: kbdtables.h,v 1.11 1994/02/01 09:27:43 ache Exp $
+ * $Id: kbdtables.h,v 1.13 1994/05/27 01:09:16 ache Exp $
*/
#define SET8 0x80 /* eight bit for emacs SET8-key */
@@ -374,17 +374,17 @@ keymap_t key_map = { 0x69, /* swedish iso8859 keymap */
/* 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', '"', NOP, NOP, NOP, '@', NOP, NOP, 0x3B, 0x00,
-/* sc=04 */ '3', 0xA3, NOP, NOP, NOP, '#', NOP, NOP, 0x3B, 0x00,
-/* sc=05 */ '4', '$', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=03 */ '2', '"', NOP, NOP, '@', NOP, NOP, NOP, 0x37, 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', '&', 0x1E, NOP, NOP, '^', 0x1E, NOP, 0x19, 0x00,
-/* sc=08 */ '7', '/', NOP, NOP, NOP, '&', NOP, NOP, 0x3B, 0x00,
-/* sc=09 */ '8', '(', NOP, NOP, NOP, '*', NOP, NOP, 0x3B, 0x00,
-/* sc=0a */ '9', ')', NOP, NOP, NOP, '(', NOP, NOP, 0x3B, 0x00,
-/* sc=0b */ '0', '=', NOP, NOP, NOP, ')', NOP, NOP, 0x3B, 0x00,
-/* sc=0c */ '+', '?', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00,
-/* sc=0d */ 0xB4, '`', NOP, NOP, '=', '+', NOP, NOP, 0x33, 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, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00,
/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
@@ -397,8 +397,8 @@ keymap_t key_map = { 0x69, /* swedish iso8859 keymap */
/* 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, '[', '{', 0x1B, NOP, 0x31, 0x01,
-/* sc=1b */ 0xA8, '^', NOP, NOP, ']', '}', 0x1D, NOP, 0x31, 0x00,
+/* 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,
@@ -410,11 +410,11 @@ keymap_t key_map = { 0x69, /* swedish iso8859 keymap */
/* 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 */ 0xF8, 0xD8, NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x01,
-/* sc=28 */ 0xE6, 0xC6, NOP, NOP, '\'', '"', NOP, NOP, 0x33, 0x01,
-/* sc=29 */ '<', '>', NOP, NOP, '\\', '|', 0x1C, NOP, 0x31, 0x00,
+/* 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, 0x33, 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,
@@ -457,7 +457,7 @@ keymap_t key_map = { 0x69, /* swedish iso8859 keymap */
/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 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=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,
@@ -569,7 +569,7 @@ keymap_t key_map = { 0xe9, /* keys number */
/* 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 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0x82, 0x02,
+/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, RBT, 0x83, 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,
@@ -698,7 +698,7 @@ keymap_t key_map = { 0xe9, /* keys number */
/* 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 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0x82, 0x02,
+/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, RBT, 0x83, 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,
diff --git a/sys/i386/isa/lpa.c b/sys/i386/isa/lpa.c
index 89434cbf3da7..8d4aa3f7470f 100644
--- a/sys/i386/isa/lpa.c
+++ b/sys/i386/isa/lpa.c
@@ -45,7 +45,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: lpa.c,v 1.5 1994/02/22 09:04:08 rgrimes Exp $
+ * $Id: lpa.c,v 1.6 1994/04/11 07:57:36 csgr Exp $
*/
/*
@@ -178,6 +178,18 @@ lpaprobe(struct isa_device *dvp)
u_char data;
u_char mask;
int i;
+ static int warned = 0;
+
+
+ /* Warn users that the lpa driver should no longer be used */
+ if(!warned) {
+ printf("*************************************************\n");
+ printf("WARNING: The lpa driver is now obsolete, and will\n");
+ printf("WARNING: be removed soon.\n");
+ printf("WARNING: Please change your config to use lpt\n");
+ printf("*************************************************\n");
+ warned = 1;
+ }
status = IO_LPTSIZE;
diff --git a/sys/i386/isa/lpt.c b/sys/i386/isa/lpt.c
index 42f92c74f8d0..2715ea8b193f 100644
--- a/sys/i386/isa/lpt.c
+++ b/sys/i386/isa/lpt.c
@@ -46,7 +46,7 @@
* SUCH DAMAGE.
*
* from: unknown origin, 386BSD 0.1
- * $Id: lpt.c,v 1.9 1994/02/22 09:05:13 rgrimes Exp $
+ * $Id: lpt.c,v 1.13 1994/06/08 14:34:54 davidg Exp $
*/
/*
@@ -66,11 +66,14 @@
#include "ioctl.h"
#include "tty.h"
#include "uio.h"
+#include "syslog.h"
#include "i386/isa/isa.h"
#include "i386/isa/isa_device.h"
#include "i386/isa/lptreg.h"
+#include "i386/include/lpt.h"
+
#define LPINITRDY 4 /* wait up to 4 seconds for a ready */
#define LPTOUTTIME 4 /* wait up to 4 seconds for a ready */
#define LPPRI (PZERO+8)
@@ -90,15 +93,6 @@
int lptflag = 1;
#endif
-void lptout();
-
-int lptprobe(), lptattach();
-void lptintr();
-
-struct isa_driver lptdriver = {
- lptprobe, lptattach, "lpt"
-};
-
#define LPTUNIT(s) ((s)&0x03)
#define LPTFLAGS(s) ((s)&0xfc)
@@ -122,6 +116,8 @@ struct lpt_softc {
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 */
+
} lpt_sc[NLPT] ;
/* bits for state */
@@ -149,12 +145,21 @@ struct lpt_softc {
#define MAX_SPIN 20 /* Max delay for device ready in usecs */
+static void lptout (struct lpt_softc * sc);
+int lptprobe (struct isa_device *dvp);
+int lptattach (struct isa_device *isdp);
+void lptintr (int unit);
+
+struct isa_driver lptdriver = {
+ lptprobe, lptattach, "lpt"
+};
+
/*
* Internal routine to lptprobe to do port tests of one byte value
*/
-int
+static int
lpt_port_test(short port, u_char data, u_char mask)
{
int temp, timeout;
@@ -279,8 +284,7 @@ end_probe:
/* XXX Todo - try and detect if interrupt is working */
int
-lptattach(isdp)
- struct isa_device *isdp;
+lptattach(struct isa_device *isdp)
{
struct lpt_softc *sc;
@@ -292,7 +296,7 @@ lptattach(isdp)
/* 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;
+ sc->sc_irq = LP_HAS_IRQ | LP_USE_IRQ | LP_ENABLE_IRQ;
printf("lpt%d: Interrupt-driven port\n", isdp->id_unit);
} else {
sc->sc_irq = 0;
@@ -308,9 +312,7 @@ lptattach(isdp)
*/
int
-lptopen(dev, flag)
- dev_t dev;
- int flag;
+lptopen(dev_t dev, int flag)
{
struct lpt_softc *sc;
int s;
@@ -321,7 +323,6 @@ lptopen(dev, flag)
if ((unit >= NLPT) || (sc->sc_port == 0))
return (ENXIO);
- /* Only check open bit */
if (sc->sc_state) {
lprintf("lp: still open\n") ;
lprintf("still open %x\n", sc->sc_state);
@@ -333,6 +334,13 @@ lptopen(dev, flag)
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 */
if((sc->sc_flags & LP_NO_PRIME) == 0) {
if((sc->sc_flags & LP_PRIMEOPEN) || sc->sc_primed == 0) {
@@ -384,20 +392,19 @@ lptopen(dev, flag)
lprintf("irq %x\n", sc->sc_irq);
if(sc->sc_irq & LP_USE_IRQ) {
sc->sc_state |= TOUT;
- timeout (lptout, (caddr_t)sc, hz/2);
+ timeout ((timeout_func_t)lptout, (caddr_t)sc, hz/2);
}
lprintf("opened.\n");
return(0);
}
-void
-lptout (sc)
- struct lpt_softc *sc;
+static void
+lptout (struct lpt_softc * sc)
{ int pl;
lprintf ("T %x ", inb(sc->sc_port+lpt_status));
if (sc->sc_state&OPEN)
- timeout (lptout, (caddr_t)sc, hz/2);
+ timeout ((timeout_func_t)lptout, (caddr_t)sc, hz/2);
else sc->sc_state &= ~TOUT;
if (sc->sc_state & ERROR)
@@ -423,9 +430,7 @@ lptout (sc)
*/
int
-lptclose(dev, flag)
- dev_t dev;
- int flag;
+lptclose(dev_t dev, int flag)
{
struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev));
int port = sc->sc_port;
@@ -458,13 +463,13 @@ lptclose(dev, flag)
* This code is only used when we are polling the port
*/
static int
-pushbytes(sc)
- struct lpt_softc *sc;
+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 */
@@ -518,9 +523,7 @@ pushbytes(sc)
*/
int
-lptwrite(dev, uio)
- dev_t dev;
- struct uio *uio;
+lptwrite(dev_t dev, struct uio * uio)
{
register unsigned n;
int pl, err;
@@ -531,26 +534,26 @@ lptwrite(dev, uio)
sc->sc_cp = sc->sc_inbuf->b_un.b_addr ;
uiomove(sc->sc_cp, n, uio);
sc->sc_xfercnt = n ;
- if(sc->sc_irq & LP_USE_IRQ)
- while (sc->sc_xfercnt > 0) {
- 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);
- }
+ 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);
}
- else { /* polled write */
+ 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);
@@ -567,8 +570,7 @@ lptwrite(dev, uio)
*/
void
-lptintr(unit)
- int unit;
+lptintr(int unit)
{
struct lpt_softc *sc = lpt_sc + unit;
int port = sc->sc_port, sts;
@@ -607,22 +609,40 @@ lptintr(unit)
}
int
-lptioctl(dev, cmd, data, flag)
- dev_t dev;
- int cmd;
- caddr_t data;
- int flag;
+lptioctl(dev_t dev, int cmd, caddr_t data, int flag)
{
- int error;
+ 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;
- error = 0;
switch (cmd) {
-#ifdef THISISASAMPLE
- case XXX:
- dothis; andthis; andthat;
- error=x;
- break;
-#endif /* THISISASAMPLE */
+ 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;
}
diff --git a/sys/i386/isa/mcd.c b/sys/i386/isa/mcd.c
index 190d42be3b75..3245dadc4097 100644
--- a/sys/i386/isa/mcd.c
+++ b/sys/i386/isa/mcd.c
@@ -39,7 +39,7 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id: mcd.c,v 1.10.2.2 1994/03/23 23:51:39 rgrimes Exp $
+ * $Id: mcd.c,v 1.16 1994/04/30 17:03:33 gclarkii Exp $
*/
static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";
@@ -350,6 +350,9 @@ MCD_TRACE("strategy: drive not valid\n",0,0,0,0);
if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) {
goto done;
}
+ } else {
+ bp->b_pblkno = bp->b_blkno;
+ bp->b_resid = 0;
}
/* queue it */
diff --git a/sys/i386/isa/npx.c b/sys/i386/isa/npx.c
index 2979e0cfd7ab..e83a503feeed 100644
--- a/sys/i386/isa/npx.c
+++ b/sys/i386/isa/npx.c
@@ -32,7 +32,7 @@
* SUCH DAMAGE.
*
* from: @(#)npx.c 7.2 (Berkeley) 5/12/91
- * $Id: npx.c,v 1.6 1994/01/03 07:55:43 davidg Exp $
+ * $Id: npx.c,v 1.9 1994/06/11 02:36:32 davidg Exp $
*/
#include "npx.h"
@@ -114,7 +114,7 @@ struct isa_driver npxdriver = {
npxprobe, npxattach, "npx",
};
-u_int npx0mask;
+u_int npx0_imask = SWI_CLOCK_MASK;
struct proc *npxproc;
static bool_t npx_ex16;
@@ -292,7 +292,7 @@ npxprobe1(dvp)
* Bad, we are stuck with IRQ13.
*/
npx_irq13 = 1;
- npx0mask = dvp->id_irq; /* npxattach too late */
+ npx0_imask = dvp->id_irq; /* npxattach too late */
return (IO_NPXSIZE);
}
/*
@@ -321,10 +321,12 @@ npxattach(dvp)
struct isa_device *dvp;
{
if (!npx_ex16 && !npx_irq13) {
- if (npx_exists)
+ if (npx_exists) {
printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit);
- else
+ npx_exists = 0;
+ } else {
printf("npx%d: 387 Emulator\n",dvp->id_unit);
+ }
}
npxinit(__INITIAL_NPXCW__);
return (1); /* XXX unused */
@@ -528,8 +530,8 @@ npxsave(addr)
old_icu1_mask = inb(IO_ICU1 + 1);
old_icu2_mask = inb(IO_ICU2 + 1);
save_idt_npxintr = idt[npx_intrno];
- outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0mask));
- outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0mask >> 8));
+ outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask));
+ outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8));
idt[npx_intrno] = npx_idt_probeintr;
enable_intr();
stop_emulating();
@@ -541,10 +543,10 @@ npxsave(addr)
icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */
icu2_mask = inb(IO_ICU2 + 1);
outb(IO_ICU1 + 1,
- (icu1_mask & ~npx0mask) | (old_icu1_mask & npx0mask));
+ (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask));
outb(IO_ICU2 + 1,
- (icu2_mask & ~(npx0mask >> 8))
- | (old_icu2_mask & (npx0mask >> 8)));
+ (icu2_mask & ~(npx0_imask >> 8))
+ | (old_icu2_mask & (npx0_imask >> 8)));
idt[npx_intrno] = save_idt_npxintr;
enable_intr(); /* back to usual state */
}
diff --git a/sys/i386/isa/pcaudio.c b/sys/i386/isa/pcaudio.c
new file mode 100644
index 000000000000..0c48e407a7be
--- /dev/null
+++ b/sys/i386/isa/pcaudio.c
@@ -0,0 +1,430 @@
+/*-
+ * Copyright (c) 1994 Søren 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.
+ * 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.5 1994/05/27 08:51:03 sos Exp $
+ */
+
+#include "systm.h"
+#include "uio.h"
+#include "ioctl.h"
+#include "proc.h"
+#include "file.h"
+#include "sound/ulaw.h"
+#include "machine/cpufunc.h"
+#include "machine/pio.h"
+#include "machine/pcaudioio.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/timerreg.h"
+
+#include "pca.h"
+#if NPCA > 0
+
+#define BUF_SIZE 4*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 */
+ char coll; /* select collision */
+ pid_t wsel; /* pid of select'ing proc */
+} pca_status;
+
+static char buffer1[BUF_SIZE];
+static char buffer2[BUF_SIZE];
+static char volume_table[256];
+
+static int pca_sleep = 0;
+static int pca_initialized = 0;
+
+void pcaintr(int regs);
+int pcaprobe(struct isa_device *dvp);
+int pcaattach(struct isa_device *dvp);
+int pcaclose(dev_t dev, int flag);
+int pcaopen(dev_t dev, int flag);
+int pcawrite(dev_t dev, struct uio *uio, int flag);
+int pcaioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p);
+int pcaselect(dev_t dev, int rw, struct proc *p);
+
+struct isa_driver pcadriver = {
+ pcaprobe, pcaattach, "pca",
+};
+
+
+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];
+ pca_status.oldval = inb(IO_PPI) | 0x03;
+ /* acquire the timers */
+ if (acquire_timer2(TIMER_LSB|TIMER_ONESHOT)) {
+ return -1;
+ }
+ if (acquire_timer0(INTERRUPT_RATE, pcaintr)) {
+ release_timer2();
+ return -1;
+ }
+ pca_status.timer_on = 1;
+ return 0;
+}
+
+
+static void
+pca_stop(void)
+{
+ /* release the timers */
+ release_timer0();
+ release_timer2();
+ /* 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();
+ release_timer2();
+ pca_status.timer_on = 0;
+}
+
+
+static void
+pca_continue()
+{
+ pca_status.oldval = inb(IO_PPI) | 0x03;
+ acquire_timer2(TIMER_LSB|TIMER_ONESHOT);
+ acquire_timer0(INTERRUPT_RATE, pcaintr);
+ pca_status.timer_on = 1;
+}
+
+
+static void
+pca_wait(void)
+{
+ while (pca_status.in_use[0] || pca_status.in_use[1]) {
+ pca_sleep = 1;
+ tsleep((caddr_t)&pca_sleep, PZERO|PCATCH, "pca_drain", 0);
+ }
+}
+
+
+int
+pcaprobe(struct isa_device *dvp)
+{
+ return(-1);
+}
+
+
+int
+pcaattach(struct isa_device *dvp)
+{
+ printf(" PCM audio driver\n", dvp->id_unit);
+ pca_init();
+ return 1;
+}
+
+
+int
+pcaopen(dev_t dev, int flag)
+{
+ /* 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;
+}
+
+
+int
+pcaclose(dev_t dev, int flag)
+{
+ /* 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;
+}
+
+
+int
+pcawrite(dev_t dev, struct uio *uio, int flag)
+{
+ int count, 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;
+ tsleep((caddr_t)&pca_sleep, PZERO|PCATCH, "pca_wait",0);
+ }
+ 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;
+}
+
+
+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:
+ pca_wait();
+ return 0;
+
+ case AUDIO_FLUSH:
+ pca_stop();
+ return 0;
+
+ }
+ return ENXIO;
+}
+
+
+void
+pcaintr(int regs)
+{
+ if (pca_status.index < pca_status.in_use[pca_status.current]) {
+ disable_intr();
+ __asm__("outb %0,$0x61\n"
+ "andb $0xFE,%0\n"
+ "outb %0,$0x61"
+ : : "a" ((char)pca_status.oldval) );
+ __asm__("xlatb\n"
+ "outb %0,$0x42"
+ : : "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((caddr_t)&pca_sleep);
+ pca_sleep = 0;
+ }
+ if (pca_status.wsel) {
+ selwakeup(pca_status.wsel, pca_status.coll);
+ pca_status.wsel = 0;
+ pca_status.coll = 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 && (p1 = pfind(pca_status.wsel))
+ && p1->p_wchan == (caddr_t)&selwait)
+ pca_status.coll = 1;
+ else
+ pca_status.wsel = p->p_pid;
+ splx(s);
+ return 0;
+ default:
+ splx(s);
+ return(0);
+ }
+}
+#endif
diff --git a/sys/i386/isa/pccons.c b/sys/i386/isa/pccons.c
index 43fba38b1552..3d729da6dc54 100644
--- a/sys/i386/isa/pccons.c
+++ b/sys/i386/isa/pccons.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)pccons.c 5.11 (Berkeley) 5/21/91
- * $Id: pccons.c,v 1.13.2.1 1994/05/04 05:09:30 rgrimes Exp $
+ * $Id: pccons.c,v 1.17 1994/05/30 03:15:09 ache Exp $
*/
/*
@@ -63,7 +63,7 @@
int pc_xmode;
#endif /* XSERVER */
-struct tty pccons;
+struct tty *pccons;
struct pcconsoftc {
char cs_flags;
@@ -289,13 +289,12 @@ pcopen(dev, flag, mode, p)
if (minor(dev) != 0)
return (ENXIO);
- tp = &pccons;
+ tp = pccons = ttymalloc(pccons);
tp->t_oproc = pcstart;
tp->t_param = pcparam;
tp->t_dev = dev;
openf++;
if ((tp->t_state & TS_ISOPEN) == 0) {
- tp->t_state |= TS_WOPEN;
ttychars(tp);
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
@@ -316,8 +315,12 @@ pcclose(dev, flag, mode, p)
int flag, mode;
struct proc *p;
{
- (*linesw[pccons.t_line].l_close)(&pccons, flag);
- ttyclose(&pccons);
+ (*linesw[pccons->t_line].l_close)(pccons, flag);
+ ttyclose(pccons);
+#ifdef broken /* session holds a ref to the tty; can't deallocate */
+ ttyfree(pccons);
+ pccons = (struct tty *)NULL;
+#endif
return(0);
}
@@ -328,7 +331,7 @@ pcread(dev, uio, flag)
struct uio *uio;
int flag;
{
- return ((*linesw[pccons.t_line].l_read)(&pccons, uio, flag));
+ return ((*linesw[pccons->t_line].l_read)(pccons, uio, flag));
}
/*ARGSUSED*/
@@ -338,7 +341,7 @@ pcwrite(dev, uio, flag)
struct uio *uio;
int flag;
{
- return ((*linesw[pccons.t_line].l_write)(&pccons, uio, flag));
+ return ((*linesw[pccons->t_line].l_write)(pccons, uio, flag));
}
/*
@@ -347,10 +350,8 @@ pcwrite(dev, uio, flag)
* Catch the character, and see who it goes to.
*/
void
-pcrint(dev, irq, cpl)
- dev_t dev;
- int irq; /* XXX ??? */
- int cpl;
+pcrint(unit)
+ int unit;
{
int c;
char *cp;
@@ -361,7 +362,7 @@ pcrint(dev, irq, cpl)
if (pcconsoftc.cs_flags & CSF_POLLING)
return;
#ifdef KDB
- if (kdbrintr(c, &pccons))
+ if (kdbrintr(c, pccons))
return;
#endif
if (!openf)
@@ -369,11 +370,11 @@ pcrint(dev, irq, cpl)
#ifdef XSERVER /* 15 Aug 92*/
/* send at least one character, because cntl-space is a null */
- (*linesw[pccons.t_line].l_rint)(*cp++ & 0xff, &pccons);
+ (*linesw[pccons->t_line].l_rint)(*cp++ & 0xff, pccons);
#endif /* XSERVER */
while (*cp)
- (*linesw[pccons.t_line].l_rint)(*cp++ & 0xff, &pccons);
+ (*linesw[pccons->t_line].l_rint)(*cp++ & 0xff, pccons);
}
#ifdef XSERVER /* 15 Aug 92*/
@@ -389,7 +390,7 @@ pcioctl(dev, cmd, data, flag)
caddr_t data;
int flag;
{
- register struct tty *tp = &pccons;
+ register struct tty *tp = pccons;
register error;
#ifdef XSERVER /* 15 Aug 92*/
@@ -436,12 +437,12 @@ pcxint(dev)
if (!pcconsintr)
return;
- pccons.t_state &= ~TS_BUSY;
+ pccons->t_state &= ~TS_BUSY;
pcconsoftc.cs_timo = 0;
- if (pccons.t_line)
- (*linesw[pccons.t_line].l_start)(&pccons);
+ if (pccons->t_line)
+ (*linesw[pccons->t_line].l_start)(pccons);
else
- pcstart(&pccons);
+ pcstart(pccons);
}
void
@@ -454,20 +455,11 @@ pcstart(tp)
if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
goto out;
do {
- if (RB_LEN(&tp->t_out) <= tp->t_lowat) {
- if (tp->t_state&TS_ASLEEP) {
- tp->t_state &= ~TS_ASLEEP;
- wakeup((caddr_t)&tp->t_out);
- }
- if (tp->t_wsel) {
- selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
- tp->t_wsel = 0;
- tp->t_state &= ~TS_WCOLL;
- }
- }
- if (RB_LEN(&tp->t_out) == 0)
+ if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel)
+ ttwwakeup(tp);
+ if (RB_LEN(tp->t_out) == 0)
goto out;
- c = getc(&tp->t_out);
+ c = getc(tp->t_out);
tp->t_state |= TS_BUSY; /* 21 Aug 92*/
splx(s);
sput(c, 0);
@@ -491,7 +483,7 @@ pccnprobe(cp)
/* initialize required fields */
cp->cn_dev = makedev(maj, 0);
- cp->cn_tp = &pccons;
+ cp->cn_tp = pccons;
cp->cn_pri = CN_INTERNAL;
}
@@ -639,26 +631,13 @@ static u_char shift_down, ctrl_down, alt_down, caps, num, scroll;
/* translate ANSI color codes to standard pc ones */
static char fgansitopc[] =
{ FG_BLACK, FG_RED, FG_GREEN, FG_BROWN, FG_BLUE,
- FG_MAGENTA, FG_CYAN, FG_LIGHTGREY};
+ FG_MAGENTA, FG_CYAN, FG_LIGHTGREY
+};
static char bgansitopc[] =
{ BG_BLACK, BG_RED, BG_GREEN, BG_BROWN, BG_BLUE,
- BG_MAGENTA, BG_CYAN, BG_LIGHTGREY};
-
-static void move_up(u_short *s, u_short *d, u_int len)
-{
- s += len;
- d += len;
- while (len-- > 0)
- *--d = *--s;
-}
-
-
-static void move_down(u_short *s, u_short *d, u_int len)
-{
- while (len-- > 0)
- *d++ = *s++;
-}
+ BG_MAGENTA, BG_CYAN, BG_LIGHTGREY
+};
/*
* sput has support for emulation of the 'pc3' termcap entry.
@@ -872,7 +851,7 @@ sput(c, ka)
posy = (crtat - Crtat) / vs.ncol;
if (vs.cx > posy)
vs.cx = posy;
- bcopy(Crtat+vs.ncol*vs.cx, Crtat, vs.ncol*(vs.nrow-vs.cx)*CHR);
+ bcopyw(Crtat+vs.ncol*vs.cx, Crtat, vs.ncol*(vs.nrow-vs.cx)*CHR);
fillw((at <<8)+' ',
(Crtat + vs.ncol * (vs.nrow - vs.cx)),
vs.ncol * vs.cx);
@@ -884,7 +863,7 @@ sput(c, ka)
posy = (crtat - Crtat) / vs.ncol;
if (vs.cx > vs.nrow - posy)
vs.cx = vs.nrow - posy;
- bcopy(Crtat, Crtat+vs.ncol*vs.cx, vs.ncol*(vs.nrow-vs.cx)*CHR);
+ bcopyw(Crtat, Crtat+vs.ncol*vs.cx, vs.ncol*(vs.nrow-vs.cx)*CHR);
fillw((at <<8)+' ', Crtat, vs.ncol*vs.cx);
/* crtat += vs.ncol*vs.cx;*/ /* XXX */
vs.esc = 0; vs.ebrac = 0; vs.eparm = 0;
@@ -897,7 +876,7 @@ sput(c, ka)
src = Crtat + posy * vs.ncol;
dst = src + vs.cx * vs.ncol;
count = vs.nrow - (posy + vs.cx);
- move_up(src, dst, count * vs.ncol);
+ bcopyw(src, dst, count * vs.ncol * CHR);
fillw((at <<8)+' ', src, vs.cx * vs.ncol);
vs.esc = 0; vs.ebrac = 0; vs.eparm = 0;
break;
@@ -909,7 +888,7 @@ sput(c, ka)
dst = Crtat + posy * vs.ncol;
src = dst + vs.cx * vs.ncol;
count = vs.nrow - (posy + vs.cx);
- move_down(src, dst, count * vs.ncol);
+ bcopyw(src, dst, count * vs.ncol * CHR);
src = dst + count * vs.ncol;
fillw((at <<8)+' ', src, vs.cx * vs.ncol);
vs.esc = 0; vs.ebrac = 0; vs.eparm = 0;
@@ -999,7 +978,7 @@ sput(c, ka)
}
if (sc && crtat >= Crtat+vs.ncol*vs.nrow) { /* scroll check */
if (openf) do (void)sgetc(1); while (scroll);
- bcopy(Crtat+vs.ncol, Crtat, vs.ncol*(vs.nrow-1)*CHR);
+ bcopyw(Crtat+vs.ncol, Crtat, vs.ncol*(vs.nrow-1)*CHR);
fillw ((at << 8) + ' ', Crtat + vs.ncol*(vs.nrow-1),
vs.ncol);
crtat -= vs.ncol;
@@ -1557,6 +1536,12 @@ loop:
#endif /* !XSERVER*/
}
+ /*
+ * Check for cntl-alt-del
+ */
+ if ((dt == 83) && ctrl_down && alt_down)
+ cpu_reset();
+
#include "ddb.h"
#if NDDB > 0
/*
@@ -1564,7 +1549,7 @@ loop:
*/
if ((dt == 1) && ctrl_down && alt_down) {
Debugger("manual escape to debugger");
- dt |= 0x80; /* discard esc (ddb discarded ctrl-alt) */
+ goto loop;
}
#endif
@@ -1797,7 +1782,7 @@ void cons_normal()
int pcmmap(dev_t dev, int offset, int nprot)
{
- if (offset > 0x20000)
+ if (offset > 0x20000 - PAGE_SIZE)
return -1;
return i386_btop((0xa0000 + offset));
}
diff --git a/sys/i386/isa/psm.c b/sys/i386/isa/psm.c
new file mode 100644
index 000000000000..d12298727ca3
--- /dev/null
+++ b/sys/i386/isa/psm.c
@@ -0,0 +1,464 @@
+/*-
+ * Copyright (c) 1992, 1993 Erik Forsberg.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ``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 I 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.
+ */
+
+/*
+ * Ported to 386bsd Oct 17, 1992
+ * Sandi Donno, Computer Science, University of Cape Town, South Africa
+ * Please send bug reports to sandi@cs.uct.ac.za
+ *
+ * Thanks are also due to Rick Macklem, rick@snowhite.cis.uoguelph.ca -
+ * although I was only partially successful in getting the alpha release
+ * of his "driver for the Logitech and ATI Inport Bus mice for use with
+ * 386bsd and the X386 port" to work with my Microsoft mouse, I nevertheless
+ * found his code to be an invaluable reference when porting this driver
+ * to 386bsd.
+ *
+ * Further modifications for latest 386BSD+patchkit and port to NetBSD,
+ * Andrew Herbert <andrew@werple.apana.org.au> - 8 June 1993
+ *
+ * Cloned from the Microsoft Bus Mouse driver, also by Erik Forsberg, by
+ * Andrew Herbert - 12 June 1993
+ *
+ * Modified for PS/2 mouse by Charles Hannum <mycroft@ai.mit.edu>
+ * - 13 June 1993
+ *
+ * Modified for PS/2 AUX mouse by Shoji Yuen <yuen@nuie.nagoya-u.ac.jp>
+ * - 24 October 1993
+ */
+
+#include "psm.h"
+
+#if NPSM > 0
+
+#include "param.h"
+#include "kernel.h"
+#include "systm.h"
+#include "buf.h"
+#include "malloc.h"
+#include "ioctl.h"
+#include "tty.h"
+#include "file.h"
+#include "proc.h"
+#include "vnode.h"
+
+#include "i386/include/mouse.h"
+#include "i386/include/pio.h" /* Julian's fast IO macros */
+#include "i386/isa/isa_device.h"
+
+#ifdef 0
+#include "syslog.h" /* For debugging */
+#endif
+
+#define DATA 0 /* Offset for data port, read-write */
+#define CNTRL 4 /* Offset for control port, write-only */
+#define STATUS 4 /* Offset for status port, read-only */
+
+/* status bits */
+#define PSM_OUTPUT_ACK 0x02 /* output acknowledge */
+
+/* controller commands */
+#define PSM_ENABLE 0xa8 /* enable auxiliary port */
+#define PSM_DISABLE 0xa7 /* disable auxiliary port */
+#define PSM_INT_ENABLE 0x47 /* enable controller interrupts */
+#define PSM_INT_DISABLE 0x65 /* disable controller interrupts */
+
+/* m+use commands */
+#define PSM_SET_SCALE11 0xe6 /* set 1:1 scaling */
+#define PSM_SET_SCALE21 0xe7 /* set 2:1 scaling */
+#define PSM_SET_RES 0xe8 /* set resolution */
+#define PSM_GET_SCALE 0xe9 /* set scaling factor */
+#define PSM_SET_STREAM 0xea /* set streaming mode */
+#define PSM_SET_SAMPLE 0xf3 /* set sampling rate */
+#define PSM_DEV_ENABLE 0xf4 /* mouse on */
+#define PSM_DEV_DISABLE 0xf5 /* mouse off */
+#define PSM_RESET 0xff /* reset */
+
+#define PSMUNIT(dev) (minor(dev) >> 1)
+
+#ifndef min
+#define min(x,y) (x < y ? x : y)
+#endif min
+
+int psmprobe (struct isa_device *);
+int psmattach (struct isa_device *);
+void psm_poll_status(void);
+
+static int psmaddr[NPSM]; /* Base I/O port addresses per unit */
+
+#define MSBSZ 1024 /* Output queue size (pwr of 2 is best) */
+
+struct ringbuf {
+ int count, first, last;
+ char queue[MSBSZ];
+};
+
+static struct psm_softc { /* Driver status information */
+ struct ringbuf inq; /* Input queue */
+ pid_t rsel; /* Process selecting for Input */
+ unsigned char state; /* Mouse driver state */
+ unsigned char status; /* Mouse button status */
+ unsigned char button; /* Previous mouse button status bits */
+ int x, y; /* accumulated motion in the X,Y axis */
+} psm_softc[NPSM];
+
+#define OPEN 1 /* Device is open */
+#define ASLP 2 /* Waiting for mouse data */
+
+struct isa_driver psmdriver = { psmprobe, psmattach, "psm" };
+
+#define AUX_PORT 0x60 /* AUX_PORT base (S.Yuen) */
+
+static void psm_write_dev(int inport, u_char value)
+{
+ psm_poll_status();
+ outb(inport+CNTRL, 0xd4);
+ psm_poll_status();
+ outb(inport+DATA,value);
+}
+
+static inline void psm_command(int ioport, u_char value)
+{
+ psm_poll_status();
+ outb(ioport+CNTRL, 0x60);
+ psm_poll_status();
+ outb(ioport+DATA, value);
+}
+
+int psmprobe(struct isa_device *dvp)
+{
+ /* XXX: Needs a real probe routine. */
+
+ int ioport,c,unit;
+
+ ioport=dvp->id_iobase;
+ unit=dvp->id_unit;
+ psm_write_dev(ioport,0xff); /* Reset aux device */
+ psm_poll_status();
+ outb(ioport+CNTRL,0xa9);
+ psm_poll_status();
+ outb(ioport+CNTRL,0xaa);
+ c = inb(ioport+DATA);
+ if(c&0x04) {
+/* printf("PS/2 AUX mouse is not found\n");*/
+ psm_command(ioport,0x65);
+ psmaddr[unit] = 0; /* Device not found */
+ return(0);}
+/* printf("PS/2 AUX mouse found. Installing driver\n");*/
+ return (4);
+}
+
+int psmattach(struct isa_device *dvp)
+{
+ int unit = dvp->id_unit;
+ int ioport = dvp->id_iobase;
+ struct psm_softc *sc = &psm_softc[unit];
+
+ /* Save I/O base address */
+
+ psmaddr[unit] = ioport;
+
+ /* Disable mouse interrupts */
+
+ psm_poll_status();
+ outb(ioport+CNTRL, PSM_ENABLE);
+#ifdef 0
+ psm_write(ioport, PSM_SET_RES);
+ psm_write(ioport, 0x03); /* 8 counts/mm */
+ psm_write(ioport, PSM_SET_SCALE);
+ psm_write(ioport, 0x02); /* 2:1 */
+ psm_write(ioport, PSM_SET_SCALE21);
+ psm_write(ioport, PSM_SET_SAMPLE);
+ psm_write(ioport, 0x64); /* 100 samples/sec */
+ psm_write(ioport, PSM_SET_STREAM);
+#endif
+ psm_poll_status();
+ outb(ioport+CNTRL, PSM_DISABLE);
+ psm_command(ioport, PSM_INT_DISABLE);
+
+ /* Setup initial state */
+
+ sc->state = 0;
+
+ /* Done */
+
+ return(0);
+}
+
+int psmopen(dev_t dev, int flag, int fmt, struct proc *p)
+{
+ int unit = PSMUNIT(dev);
+ struct psm_softc *sc;
+ int ioport;
+
+ /* Validate unit number */
+
+ if (unit >= NPSM)
+ return(ENXIO);
+
+ /* Get device data */
+
+ sc = &psm_softc[unit];
+ ioport = psmaddr[unit];
+
+ /* If device does not exist */
+
+ if (ioport == 0)
+ return(ENXIO);
+
+ /* Disallow multiple opens */
+ if (sc->state & OPEN)
+ return(EBUSY);
+
+ /* Initialize state */
+
+ sc->state |= OPEN;
+ sc->rsel = 0;
+ sc->status = 0;
+ sc->button = 0;
+ sc->x = 0;
+ sc->y = 0;
+
+ /* Allocate and initialize a ring buffer */
+
+ sc->inq.count = sc->inq.first = sc->inq.last = 0;
+
+ /* Enable Bus Mouse interrupts */
+
+ psm_write_dev(ioport, PSM_DEV_ENABLE);
+ psm_poll_status();
+ outb(ioport+CNTRL, PSM_ENABLE);
+ psm_command(ioport, PSM_INT_ENABLE);
+
+ /* Successful open */
+
+ return(0);
+}
+
+void psm_poll_status(void)
+{
+
+ while(inb(AUX_PORT+STATUS)&0x03) {
+ if(inb(AUX_PORT+STATUS) & 0x2 == 0x2)
+ inb(AUX_PORT+DATA);}
+ return;
+}
+
+
+int psmclose(dev_t dev, int flag, int fmt, struct proc *p)
+{
+ int unit, ioport;
+ struct psm_softc *sc;
+
+ /* Get unit and associated info */
+
+ unit = PSMUNIT(dev);
+ sc = &psm_softc[unit];
+ ioport = psmaddr[unit];
+
+ /* Disable further mouse interrupts */
+
+ psm_command(ioport,PSM_INT_DISABLE);
+ psm_poll_status();
+ outb(ioport+CNTRL,PSM_DISABLE );
+
+ /* Complete the close */
+
+ sc->state &= ~OPEN;
+
+ /* close is almost always successful */
+
+ return(0);
+}
+
+int psmread(dev_t dev, struct uio *uio, int flag)
+{
+ int s;
+ int error = 0; /* keep compiler quiet, even though initialisation
+ is unnecessary */
+ unsigned length;
+ struct psm_softc *sc;
+ unsigned char buffer[100];
+
+ /* Get device information */
+
+ sc = &psm_softc[PSMUNIT(dev)];
+
+ /* Block until mouse activity occured */
+
+ s = spltty();
+ while (sc->inq.count == 0) {
+ if (minor(dev) & 0x1) {
+ splx(s);
+ return(EWOULDBLOCK);
+ }
+ sc->state |= ASLP;
+ error = tsleep((caddr_t)sc, PZERO | PCATCH, "psmrea", 0);
+ if (error != 0) {
+ splx(s);
+ return(error);
+ }
+ }
+
+ /* Transfer as many chunks as possible */
+
+ while (sc->inq.count > 0 && uio->uio_resid > 0) {
+ length = min(sc->inq.count, uio->uio_resid);
+ if (length > sizeof(buffer))
+ length = sizeof(buffer);
+
+ /* Remove a small chunk from input queue */
+
+ if (sc->inq.first + length >= MSBSZ) {
+ bcopy(&sc->inq.queue[sc->inq.first],
+ buffer, MSBSZ - sc->inq.first);
+ bcopy(sc->inq.queue, &buffer[MSBSZ-sc->inq.first],
+ length - (MSBSZ - sc->inq.first));
+ }
+ else
+ bcopy(&sc->inq.queue[sc->inq.first], buffer, length);
+
+ sc->inq.first = (sc->inq.first + length) % MSBSZ;
+ sc->inq.count -= length;
+
+ /* Copy data to user process */
+
+ error = uiomove(buffer, length, uio);
+ if (error)
+ break;
+ }
+
+ sc->x = sc->y = 0;
+
+ /* Allow interrupts again */
+
+ splx(s);
+ return(error);
+}
+
+int psmioctl(dev_t dev, caddr_t addr, int cmd, int flag, struct proc *p)
+{
+ struct psm_softc *sc;
+ struct mouseinfo info;
+ int s, error;
+
+ /* Get device information */
+
+ sc = &psm_softc[PSMUNIT(dev)];
+
+ /* Perform IOCTL command */
+
+ switch (cmd) {
+
+ case MOUSEIOCREAD:
+
+ /* Don't modify info while calculating */
+
+ s = spltty();
+
+ /* Build mouse status octet */
+
+ info.status = sc->status;
+ if (sc->x || sc->y)
+ info.status |= MOVEMENT;
+
+ /* Encode X and Y motion as good as we can */
+
+ if (sc->x > 127)
+ info.xmotion = 127;
+ else if (sc->x < -128)
+ info.xmotion = -128;
+ else
+ info.xmotion = sc->x;
+
+ if (sc->y > 127)
+ info.ymotion = 127;
+ else if (sc->y < -128)
+ info.ymotion = -128;
+ else
+ info.ymotion = sc->y;
+
+ /* Reset historical information */
+
+ sc->x = 0;
+ sc->y = 0;
+ sc->status &= ~BUTCHNGMASK;
+
+ /* Allow interrupts and copy result buffer */
+
+ splx(s);
+ error = copyout(&info, addr, sizeof(struct mouseinfo));
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ /* Return error code */
+
+ return(error);
+}
+
+void psmintr(unit)
+ int unit;
+{
+ struct psm_softc *sc = &psm_softc[unit];
+ int ioport = psmaddr[unit];
+
+ sc->inq.queue[sc->inq.last++ % MSBSZ] = inb(ioport+DATA);
+ sc->inq.count++;
+ if (sc -> state & ASLP) {
+ sc->state &= ~ASLP;
+ wakeup((caddr_t)sc);
+ }
+ if (sc->rsel) {
+ selwakeup(sc->rsel, 0);
+ sc->rsel = 0;
+ }
+}
+
+int psmselect(dev_t dev, int rw, struct proc *p)
+{
+ int s, ret;
+ struct psm_softc *sc = &psm_softc[PSMUNIT(dev)];
+
+ /* Silly to select for output */
+
+ if (rw == FWRITE)
+ return(0);
+
+ /* Return true if a mouse event available */
+
+ s = spltty();
+ if (sc->inq.count)
+ ret = 1;
+ else {
+ sc->rsel = p->p_pid;
+ ret = 0;
+ }
+ splx(s);
+
+ return(ret);
+}
+#endif
+
+
diff --git a/sys/i386/isa/seagate.c b/sys/i386/isa/seagate.c
new file mode 100644
index 000000000000..31c515166663
--- /dev/null
+++ b/sys/i386/isa/seagate.c
@@ -0,0 +1,2036 @@
+/*
+ * (Free/Net/386)BSD ST01/02, Future Domain TMC-885, TMC-950 SCSI driver for
+ * Julians SCSI-code
+ *
+ * Copyright 1994, Kent Palmkvist (kentp@isy.liu.se)
+ * Copyright 1994, Robert Knier (rknier@qgraph.com)
+ * Copyright 1992, 1994 Drew Eckhardt (drew@colorado.edu)
+ * Copyright 1994, Julian Elischer (julian@tfs.com)
+ *
+ * Others that has contributed by example code is
+ * Glen Overby (overby@cray.com)
+ * Tatu Yllnen
+ * Brian E Litzinger
+ *
+ * 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 DEVELOPERS ``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 DEVELOPERS 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.
+ */
+
+/*
+ *
+ * kentp 940307 alpha version based on newscsi-03 version of Julians SCSI-code
+ * kentp 940314 Added possibility to not use messages
+ * rknier 940331 Added fast transfer code
+ * rknier 940407 Added assembler coded data transfers
+ *
+ * $Id: seagate.c,v 1.3 1994/06/16 13:26:14 sean Exp $
+ */
+
+/*
+ * What should really be done:
+ *
+ * Add missing tests for timeouts
+ * Restructure interrupt enable/disable code (runs to long with int disabled)
+ * Find bug? giving problem with tape status
+ * Add code to handle Future Domain 840, 841, 880 and 881
+ * adjust timeouts (startup is very slow)
+ * add code to use tagged commands in SCSI2
+ * Add code to handle slow devices better (sleep if device not disconnecting)
+ * Fix unnecessary interrupts
+ */
+
+/* Note to users trying to share a disk between DOS and unix:
+ * The ST01/02 is a translating host-adapter. It is not giving DOS
+ * the same number of heads/tracks/sectors as specified by the disk.
+ * It is therefore important to look at what numbers DOS thinks the
+ * disk has. Use these to disklabel your disk in an appropriate manner
+ */
+
+#include <sys/types.h>
+
+#ifdef KERNEL /* don't laugh.. look for main() */
+#include <sea.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 <sys/user.h>
+#include <i386/isa/isa_device.h>
+#endif /* KERNEL */
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#ifdef KERNEL
+#include "ddb.h"
+#include "kernel.h"
+#else /* KERNEL */
+#define NSEA 1
+#endif /* KERNEL */
+
+extern int hz;
+
+#define SEA_SCB_MAX 8 /* allow maximally 8 scsi control blocks */
+#define SCB_TABLE_SIZE 8 /* start with 8 scb entries in table */
+#define BLOCK_SIZE 512 /* size of READ/WRITE areas on SCSI card */
+
+/*
+ * defining PARITY causes parity data to be checked
+ */
+#define PARITY 1
+
+/*
+ * defining SEA_BLINDTRANSFER will make DATA IN and DATA OUT to be done with
+ * blind transfers, i.e. no check is done for scsi phase changes. This will
+ * result in data loss if the scsi device does not send its data using
+ * BLOCK_SIZE bytes at a time.
+ * If SEA_BLINDTRANSFER defined and SEA_ASSEMBLER also defined will result in
+ * the use of blind transfers coded in assembler. SEA_ASSEMBLER is no good
+ * without SEA_BLINDTRANSFER defined.
+ */
+#define SEA_BLINDTRANSFER 1 /* do blind transfers */
+#define SEA_ASSEMBLER 1 /* Use assembly code for fast transfers */
+
+/*
+ * defining SEANOMSGS causes messages not to be used (thereby disabling
+ * disconnects)
+ */
+/* #define SEANOMSGS 1 */
+
+/*
+ * defining SEA_NODATAOUT makes dataout phase being aborted
+ */
+/* #define SEA_NODATAOUT 1 */
+
+/*
+ * defining SEA_SENSEFIRST make REQUEST_SENSE opcode to be placed first
+ */
+/* #define SEA_SENSEFIRST 1 */
+
+#define SEA_FREEBSD11 1 /* intermediate def. for FreeBSD 1.1 BETA */
+ /* timeout function has changed */
+
+/* Debugging definitions. Should not be used unless you want a lot of
+ printouts even under normal conditions */
+
+/* #define SEADEBUG 1 */ /* General info about errors */
+/* #define SEADEBUG1 1 */ /* Info about internal results and errors */
+/* #define SEADEBUG2 1 */ /* Display a lot about timeouts etc */
+/* #define SEADEBUG3 1 */
+/* #define SEADEBUG4 1 */
+/* #define SEADEBUG5 1 */
+/* #define SEADEBUG6 1 */ /* Display info about queue-lengths */
+/* #define SEADEBUG7 1 */ /* Extra check on STATUS before phase check */
+/* #define SEADEBUG8 1 */ /* Disregard non-BSY state in
+ sea_information_transfer */
+/* #define SEADEBUG9 1 */ /* Enable printouts */
+/* #define SEADEBUG11 1 */ /* stop everything except access to scsi id 1 */
+/* #define SEADEBUG15 1 */ /* Display every byte sent/received */
+
+#define NUM_CONCURRENT 1 /* number of concurrent ops per board */
+
+/******************************* board definitions **************************/
+/*
+ * CONTROL defines
+ */
+
+#define CMD_RST 0x01 /* scsi reset */
+#define CMD_SEL 0x02 /* scsi select */
+#define CMD_BSY 0x04 /* scsi busy */
+#define CMD_ATTN 0x08 /* scsi attention */
+#define CMD_START_ARB 0x10 /* start arbitration bit */
+#define CMD_EN_PARITY 0x20 /* enable scsi parity generation */
+#define CMD_INTR 0x40 /* enable scsi interrupts */
+#define CMD_DRVR_ENABLE 0x80 /* scsi enable */
+
+/*
+ * STATUS
+ */
+
+#define STAT_BSY 0x01 /* scsi busy */
+#define STAT_MSG 0x02 /* scsi msg */
+#define STAT_IO 0x04 /* scsi I/O */
+#define STAT_CD 0x08 /* scsi C/D */
+#define STAT_REQ 0x10 /* scsi req */
+#define STAT_SEL 0x20 /* scsi select */
+#define STAT_PARITY 0x40 /* parity error bit */
+#define STAT_ARB_CMPL 0x80 /* arbitration complete bit */
+
+/*
+ * REQUESTS
+ */
+
+#define REQ_MASK (STAT_CD | STAT_IO | STAT_MSG)
+#define REQ_DATAOUT 0
+#define REQ_DATAIN STAT_IO
+#define REQ_CMDOUT STAT_CD
+#define REQ_STATIN (STAT_CD | STAT_IO)
+#define REQ_MSGOUT (STAT_MSG | STAT_CD)
+#define REQ_MSGIN (STAT_MSG | STAT_CD | STAT_IO)
+
+#define REQ_UNKNOWN 0xff
+
+#define SEAGATERAMOFFSET 0x00001800
+
+#ifdef PARITY
+ #define BASE_CMD (CMD_EN_PARITY | CMD_INTR)
+#else
+ #define BASE_CMD (CMD_INTR)
+#endif
+
+#define SEAGATE 1
+#define FD 2
+
+/******************************************************************************
+ * This should be placed in a more generic file (presume in /sys/scsi)
+ * Message codes:
+ */
+#define MSG_ABORT 0x06
+#define MSG_NOP 0x08
+#define MSG_COMMAND_COMPLETE 0x00
+#define MSG_DISCONNECT 0x04
+#define MSG_IDENTIFY 0x80
+#define MSG_BUS_DEV_RESET 0x0c
+#define MSG_MESSAGE_REJECT 0x07
+#define MSG_SAVE_POINTERS 0x02
+#define MSG_RESTORE_POINTERS 0x03
+/******************************************************************************/
+
+#define IDENTIFY(can_disconnect,lun) (MSG_IDENTIFY | ((can_disconnect) ? \
+ 0x40 : 0) | ((lun) & 0x07))
+
+/* scsi control block used to keep info about a scsi command */
+struct sea_scb
+{
+ int flags; /* status of the instruction */
+#define SCB_FREE 0
+#define SCB_ACTIVE 1
+#define SCB_ABORTED 2
+#define SCB_TIMEOUT 4
+#define SCB_ERROR 8
+#define SCB_TIMECHK 16 /* We have set a timeout on this one */
+ struct sea_scb *next; /* in free list */
+ struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */
+ u_char * data; /* position in data buffer so far */
+ int32 datalen; /* bytes remaining to transfer */;
+};
+
+/*
+ * data structure describing current status of the scsi bus. One for each
+ * controller card.
+ */
+struct sea_data
+{
+ caddr_t basemaddr; /* Base address for card */
+ char ctrl_type; /* FD or SEAGATE */
+ caddr_t st0x_cr_sr; /* Address of control and status register */
+ caddr_t st0x_dr; /* Address of data register */
+ u_short vect; /* interrupt vector for this card */
+ int our_id; /* our scsi id */
+ int numscb; /* number of scsi control blocks */
+ struct scsi_link sc_link; /* struct connecting different data */
+ struct sea_scb *connected; /* currently connected command */
+ struct sea_scb *issue_queue; /* waiting to be issued */
+ struct sea_scb *disconnected_queue; /* waiting to reconnect */
+ struct sea_scb scbs[SCB_TABLE_SIZE];
+ struct sea_scb *free_scb; /* free scb list */
+ volatile unsigned char busy[8]; /* index=target, bit=lun, Keep track of
+ busy luns at device target */
+} *seadata[NSEA];
+
+/* flag showing if main routine is running. */
+static volatile int main_running = 0;
+
+#define STATUS (*(volatile unsigned char *) sea->st0x_cr_sr)
+#define CONTROL STATUS
+#define DATA (*(volatile unsigned char *) sea->st0x_dr)
+
+/*
+ * These are "special" values for the tag parameter passed to sea_select
+ * Not implemented right now.
+ */
+
+#define TAG_NEXT -1 /* Use next free tag */
+#define TAG_NONE -2 /*
+ * Establish I_T_L nexus instead of I_T_L_Q
+ * even on SCSI-II devices.
+ */
+
+typedef struct {
+ char *signature ;
+ unsigned offset;
+ unsigned length;
+ unsigned char type;
+} BiosSignature;
+
+/*
+ * Signatures for automatic recognition of board type
+ */
+
+static const BiosSignature signatures[] = {
+{"ST01 v1.7 (C) Copyright 1987 Seagate", 15, 37, SEAGATE},
+{"SCSI BIOS 2.00 (C) Copyright 1987 Seagate", 15, 40, SEAGATE},
+
+/*
+ * The following two lines are NOT mistakes. One detects ROM revision
+ * 3.0.0, the other 3.2. Since seagate has only one type of SCSI adapter,
+ * and this is not going to change, the "SEAGATE" and "SCSI" together
+ * are probably "good enough"
+ */
+
+{"SEAGATE SCSI BIOS ", 16, 17, SEAGATE},
+{"SEAGATE SCSI BIOS ", 17, 17, SEAGATE},
+
+ /*
+ * However, future domain makes several incompatible SCSI boards, so specific
+ * signatures must be used.
+ */
+
+ {"FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89", 5, 45, FD},
+ {"FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89", 5, 46, FD},
+ {"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90",5, 47, FD},
+ {"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90",5, 47, FD},
+ {"FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90", 5, 46, FD},
+ {"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92", 5, 44, FD},
+ {"FUTURE DOMAIN TMC-950", 5, 21, FD},
+ };
+
+#define NUM_SIGNATURES (sizeof(signatures) / sizeof(BiosSignature))
+
+static const char * seagate_bases[] = {
+ (char *) 0xc8000, (char *) 0xca000, (char *) 0xcc000,
+ (char *) 0xce000, (char *) 0xdc000, (char *) 0xde000
+};
+
+#define NUM_BASES (sizeof(seagate_bases)/sizeof(char *))
+
+int sea_probe(struct isa_device *dev);
+int sea_attach(struct isa_device *dev);
+int seaintr(int unit);
+int32 sea_scsi_cmd(struct scsi_xfer *xs);
+#ifdef SEA_FREEBSD11
+void sea_timeout(caddr_t, int);
+#else
+void sea_timeout(struct sea_scb *scb);
+#endif
+void seaminphys(struct buf *bp);
+void sea_done(int unit, struct sea_scb *scb);
+u_int32 sea_adapter_info(int unit);
+struct sea_scb *sea_get_scb(int unit, int flags);
+void sea_free_scb(int unit, struct sea_scb *scb, int flags);
+static void sea_main(void);
+static void sea_information_transfer(struct sea_data *sea);
+int sea_poll(int unit, struct scsi_xfer *xs, struct sea_scb *scb);
+int sea_init(int unit);
+int sea_send_scb(struct sea_data *sea, struct sea_scb *scb);
+int sea_reselect(struct sea_data *sea);
+int sea_select(struct sea_data *sea, struct sea_scb *scb);
+int sea_transfer_pio(struct sea_data *sea, u_char *phase, int32 *count,
+ u_char **data);
+int sea_abort(int unit, struct sea_scb *scb);
+
+static sea_unit = 0;
+static sea_slot = -1; /* last found board seagate_bases address index */
+#define FAIL 1
+#define SUCCESS 0
+
+#ifdef KERNEL
+struct scsi_adapter sea_switch =
+{
+ sea_scsi_cmd,
+ seaminphys,
+ 0,
+ 0,
+ sea_adapter_info,
+ "sea",
+ 0,0
+};
+
+/* the below structure is so we have a default dev struct for our link struct */
+struct scsi_device sea_dev =
+{
+ NULL, /* use default error handler */
+ NULL, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
+ "sea",
+ 0,
+ 0,0
+};
+
+struct isa_driver seadriver =
+{
+ sea_probe,
+ sea_attach,
+ "sea"
+};
+
+#endif /* KERNEL */
+
+#ifdef SEADEBUG6
+void sea_queue_length()
+{
+ struct sea_scb *tmp;
+ int length = 0;
+
+ if(seadata[0]->connected)
+ length = 1;
+ for(tmp = seadata[0]->issue_queue; tmp != NULL; tmp = tmp->next, length++);
+ for(tmp = seadata[0]->disconnected_queue ; tmp != NULL; tmp->next, length++);
+ printf("length:%d ",length);
+}
+#endif
+
+/***********************************************************************\
+* Check if the device can be found at the port given and if so, detect *
+* the type of board. Set it up ready for further work. Takes the *
+* isa_dev structure from autoconf as an argument. *
+* Returns 1 if card recognized, 0 if errors *
+\***********************************************************************/
+int
+sea_probe(dev)
+struct isa_device *dev;
+{
+ int j;
+ int unit = sea_unit;
+ struct sea_data *sea;
+ dev->id_unit = unit;
+
+#ifdef SEADEBUG2
+ printf("sea_probe ");
+#endif
+
+ /* find unit and check we have that many defined */
+ if(unit >= NSEA) {
+ printf("sea%d: unit number too high\n",unit);
+ return(0);
+ }
+ dev->id_unit = unit;
+#ifdef SEADEBUG2
+ printf("unit: %d\n",unit);
+ printf("dev_addr: 0x%lx\n",dev->id_maddr);
+#endif
+ /* allocate a storage area for us */
+
+ if (seadata[unit]) {
+ printf("sea%d: memory already allocated\n", unit);
+ return(0);
+ }
+#ifdef SEADEBUG2
+ printf("Before malloc\n");
+#endif
+ sea = malloc(sizeof(struct sea_data), M_TEMP, M_NOWAIT);
+ if (!sea) {
+ printf("sea%d: cannot malloc!\n", unit);
+ return(0);
+ }
+
+#ifdef SEADEBUG2
+ printf("after malloc\n");
+ for(j=0;j<32767;j++);
+#endif
+ bzero(sea,sizeof(struct sea_data));
+ seadata[unit] = sea;
+
+ /* check for address if no one specified */
+ sea->basemaddr = NULL;
+
+ /* Could try to find a board by looking through all possible addresses */
+ /* This is not done the right way now, because I have not found a way */
+ /* to get a boards virtual memory address given its physical. There is */
+ /* a function that returns the physical address for a given virtual */
+ /* address, but not the other way around */
+
+ if(dev->id_maddr == 0) {
+/*
+ for(sea_slot++;sea_slot<NUM_BASES;sea_slot++)
+ for(j = 0; !sea->basemaddr && j < NUM_SIGNATURES; ++j)
+ if(!memcmp((void *)(seagate_bases[sea_slot]+signatures[j].offset),
+ (void *) signatures[j].signature, signatures[j].length)) {
+ sea->basemaddr = (void *)seagate_bases[sea_slot];
+ break;
+ }
+*/
+ } else {
+
+#ifdef SEADEBUG2
+ printf("id_maddr != 0\n");
+ for(j = 0; j < 32767 ; j++);
+ for(j = 0; j < 32767 ; j++);
+#endif
+ /* find sea_slot position for overridden memory address */
+ for(j = 0; ((char *)vtophys(dev->id_maddr) != seagate_bases[j]) &&
+ j<NUM_BASES; ++j);
+ if(j == NUM_BASES) {
+ printf("sea: board not expected at address 0x%lx\n",dev->id_maddr);
+ seadata[unit]=NULL;
+ free(sea, M_TEMP);
+ return(0);
+ } else if(sea_slot > j) {
+ printf("sea: board address 0x%lx already probed!\n", dev->id_maddr);
+ seadata[unit]=NULL;
+ free(sea, M_TEMP);
+ return(0);
+ } else {
+ sea->basemaddr = dev->id_maddr;
+ }
+
+ }
+#ifdef SEADEBUG2
+ printf("sea->basemaddr = %lx\n", sea->basemaddr);
+#endif
+
+ /* check board type */ /* No way to define this through config */
+ for(j = 0; j < NUM_SIGNATURES; j++)
+ if(!memcmp((void *) (sea->basemaddr + signatures[j].offset),
+ (void *) signatures[j].signature, signatures[j].length)) {
+ sea->ctrl_type = signatures[j].type;
+ break;
+ }
+ if(j == NUM_SIGNATURES) {
+ printf("sea: Board type unknown at address 0x%lx\n",
+ sea->basemaddr);
+ seadata[unit]=NULL;
+ free(sea, M_TEMP);
+ return(0);
+ }
+
+ /* Find controller and data memory addresses */
+ sea->st0x_cr_sr = (void *) (((unsigned char *) sea->basemaddr) +
+ ((sea->ctrl_type == SEAGATE) ? 0x1a00 : 0x1c00));
+ sea->st0x_dr = (void *) (((unsigned char *) sea->basemaddr) +
+ ((sea->ctrl_type == SEAGATE) ? 0x1c00 : 0x1e00));
+
+ /* Test controller RAM (works the same way on future domain cards?) */
+ *(sea->basemaddr + SEAGATERAMOFFSET) = 0xa5;
+ *(sea->basemaddr + SEAGATERAMOFFSET + 1) = 0x5a;
+
+ if((*(sea->basemaddr + SEAGATERAMOFFSET) != (char) 0xa5) ||
+ (*(sea->basemaddr + SEAGATERAMOFFSET + 1) != (char) 0x5a)) {
+ printf("sea%d: Board RAM failure\n",unit);
+ }
+
+ if(sea_init(unit) != 0) {
+ seadata[unit] = NULL;
+ free(sea,M_TEMP);
+ return(0);
+ }
+
+ /* if its there put in it's interrupt vector */
+ /* (Doesn't use dma, so no drq is set) */
+ sea->vect = dev->id_irq;
+
+ sea_unit++;
+ return(1);
+}
+
+/***********************************************\
+* Attach all sub-devices we can find *
+\***********************************************/
+int
+sea_attach(dev)
+ struct isa_device *dev;
+{
+ int unit = dev->id_unit;
+ struct sea_data *sea = seadata[unit];
+
+#ifdef SEADEBUG2
+ printf("sea_attach called\n");
+#endif
+
+ /* fill in the prototype scsi_link */
+ sea->sc_link.adapter_unit = unit;
+ sea->sc_link.adapter_targ = sea->our_id;
+ sea->sc_link.adapter = &sea_switch;
+ sea->sc_link.device = &sea_dev;
+
+ /*****************************************************\
+ * ask the adapter what subunits are present *
+ \*****************************************************/
+ scsi_attachdevs(&(sea->sc_link));
+ return 1;
+}
+
+/***********************************************\
+* Return some information to the caller about *
+* the adapter and its capabilities *
+\***********************************************/
+u_int32
+sea_adapter_info(unit)
+ int unit;
+{
+#ifdef SEADEBUG2
+ printf("sea_adapter_info called\n");
+#endif
+ return 1;
+}
+
+/***********************************************\
+* Catch an interrupt from the adaptor *
+\***********************************************/
+int
+seaintr(unit)
+ int unit;
+{
+ int done;
+ struct sea_data *sea = seadata[unit];
+ int oldpri;
+
+#if SEADEBUG2
+ printf(";");
+#endif
+
+ do {
+ done = 1;
+ /* dispatch to appropriate routine if found and done=0 */
+ /* should check to see that this card really caused the interrupt */
+ if ((STATUS & (STAT_SEL | STAT_IO)) == (STAT_SEL | STAT_IO)) {
+ /* Reselect interrupt */
+#ifdef SEADEBUG2
+ printf(";2");
+#endif
+ done = 0;
+/* enable_intr(); */ /* ?? How should this be done ?? */
+ sea_reselect(sea);
+ } else if (STATUS & STAT_PARITY) {
+ /* Parity error interrupt */
+#ifdef SEADEBUG2
+ printf(";3");
+#endif
+ printf("sea%d: PARITY interrupt\n", unit);
+ } else {
+#ifdef SEADEBUG2
+/* printf("sea%d: unknown interrupt\n",unit); */
+ printf(";4%x", STATUS);
+#endif
+ }
+ if (!done) {
+ oldpri = splbio(); /* disable_intr(); */
+ if (!main_running) {
+#ifdef SEADEBUG2
+ printf(";5");
+#endif
+ main_running = 1;
+ sea_main();
+ /* main_running is cleared in sea_main once it can't
+ * do more work, and sea_main exits with interrupts
+ * disabled
+ */
+ splx(oldpri); /* enable_intr(); */
+ } else {
+ splx(oldpri); /* enable_intr(); */
+ }
+ }
+ } while (!done);
+ return 1;
+}
+
+/***********************************************\
+* Setup data structures, and reset the board *
+* and the scsi bus *
+\***********************************************/
+int
+sea_init(unit)
+ int unit;
+{
+ long l;
+ int i;
+ struct sea_data *sea = seadata[unit];
+
+#ifdef SEADEBUG2
+ printf("sea_init called\n");
+#endif
+/* Reset the scsi bus (I don't know if this is needed */
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_RST;
+ DELAY(25); /* hold reset for at least 25 microseconds */
+ CONTROL = BASE_CMD;
+ DELAY(10); /* wait a Bus Clear Delay (800 ns + bus free delay (800 ns) */
+ /* Set our id (don't know anything about this) */
+ if(sea->ctrl_type == SEAGATE)
+ sea->our_id = 7;
+ else
+ sea->our_id = 6;
+ /* init fields used by our routines */
+ sea->connected = NULL;
+ sea->issue_queue = NULL;
+ sea->disconnected_queue = NULL;
+ for (i=0; i<8 ; i++)
+ sea->busy[i] = 0;
+
+ /* link up the free list of scbs */
+ sea->numscb = SCB_TABLE_SIZE;
+ sea->free_scb = (struct sea_scb *) & (sea->scbs[0]);
+ for(i=1;i< SCB_TABLE_SIZE ; i++) {
+ sea->scbs[i-1].next = &(sea->scbs[i]);
+ }
+ sea->scbs[SCB_TABLE_SIZE - 1].next = NULL;
+
+ return(0);
+}
+
+/***********************************************\
+* *
+\***********************************************/
+void seaminphys(bp)
+ struct buf *bp;
+{
+#ifdef SEADEBUG2
+/* printf("seaminphys called\n"); */
+ printf(",");
+#endif
+}
+
+/***********************************************\
+* start a scsi operation given the command and *
+* the data address. Also needs the unit, target *
+* and lu *
+* get a free scb and set it up *
+* call send_scb *
+* either start timer or wait until done *
+\***********************************************/
+int32 sea_scsi_cmd(xs)
+struct scsi_xfer *xs;
+{
+ struct scsi_sense_data *s1, *s2;
+ struct sea_scb *scb;
+ int i = 0;
+ int flags;
+ int unit = xs->sc_link->adapter_unit;
+ struct sea_data *sea = seadata[unit];
+ int s;
+ unsigned int stat;
+ int32 result;
+
+#ifdef SEADEBUG2
+ /* printf("scsi_cmd\n"); */
+ printf("=");
+#endif
+
+#ifdef SEADEBUG11
+ if(xs->sc_link->target != 1) {
+ xs->flags |= ITSDONE;
+ xs->error = XS_TIMEOUT;
+ return(HAD_ERROR);
+ }
+#endif
+
+ flags = xs->flags;
+ if(xs->bp) flags |= (SCSI_NOSLEEP);
+ if(flags & ITSDONE) {
+ printf("sea%d: Already done?", unit);
+ xs->flags &= ~ITSDONE;
+ }
+ if(!(flags & INUSE)) {
+ printf("sea%d: Not in use?", unit);
+ xs->flags |= INUSE;
+ }
+ if (!(scb = sea_get_scb(unit, flags))) {
+#ifdef SEADEBUG2
+ printf("=2");
+#endif
+ xs->error = XS_DRIVER_STUFFUP;
+ return(TRY_AGAIN_LATER);
+ }
+
+ /*
+ * Put all the arguments for the xfer in the scb
+ */
+ scb->xfer = xs;
+ scb->datalen = xs->datalen;
+ scb->data = xs->data;
+
+ if(flags & SCSI_RESET) {
+ /* Try to send a reset command to the card. This is done by calling the
+ * Reset function. Should then return COMPLETE. Need to take care of the
+ * possible current connected command.
+ * Not implemented right now.
+ */
+ printf("sea%d: Got a SCSI_RESET!\n",unit);
+ }
+
+ /* setup the scb to contain necessary values */
+ /* The interresting values can be read from the xs that is saved */
+ /* I therefore think that the structure can be kept very small */
+ /* the driver doesn't use DMA so the scatter/gather is not needed ? */
+#ifdef SEADEBUG6
+ sea_queue_length();
+#endif
+ if (sea_send_scb(sea, scb) == 0) {
+#ifdef SEADEBUG2
+ printf("=3");
+#endif
+ xs->error = XS_DRIVER_STUFFUP;
+ sea_free_scb(unit, scb, flags);
+ return (TRY_AGAIN_LATER);
+ }
+
+ /*
+ * Usually return SUCCESSFULLY QUEUED
+ */
+ if (!(flags & SCSI_NOMASK)) {
+ if(xs->flags & ITSDONE) { /* timout timer not started, already finished */
+ /* Tried to return COMPLETE but the machine hanged with this */
+#ifdef SEADEBUG2
+ printf("=6");
+#endif
+ return(SUCCESSFULLY_QUEUED);
+ }
+#ifdef SEA_FREEBSD11
+ timeout(sea_timeout, (caddr_t)scb, (xs->timeout * hz) / 1000);
+#else
+ timeout(sea_timeout, scb, (xs->timeout * hz) / 1000);
+#endif
+ scb->flags |= SCB_TIMECHK;
+#ifdef SEADEBUG2
+ printf("=4");
+#endif
+ return(SUCCESSFULLY_QUEUED);
+ }
+
+ /*
+ * If we can't use interrupts, poll on completion
+ */
+
+ result = sea_poll(unit, xs, scb);
+#ifdef SEADEBUG2
+ printf("=5 %lx", result);
+#endif
+ return result;
+}
+
+/*
+ * Get a free scb. If there are none, see if we can allocate a new one. If so,
+ * put it in the hash table too, otherwise return an error or sleep.
+ */
+
+struct sea_scb *
+sea_get_scb(unit, flags)
+ int unit;
+ int flags;
+{
+ struct sea_data *sea = seadata[unit];
+ unsigned opri = 0;
+ struct sea_scb * scbp;
+ int hashnum;
+
+#ifdef SEADEBUG2
+/* printf("get_scb\n"); */
+ printf("(");
+#endif
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+#ifdef SEADEBUG3
+ printf("(2 %lx ", sea->free_scb);
+#endif
+
+ /*
+ * If we can and have to, sleep waiting for one to come free
+ * but only if we can´t allocate a new one.
+ */
+ while (!(scbp = sea->free_scb)) {
+#ifdef SEADEBUG12
+ printf("(3");
+#endif
+ if (sea->numscb < SEA_SCB_MAX) {
+ printf("malloced new scbs\n");
+ if (scbp = (struct sea_scb *) malloc(sizeof(struct sea_scb),
+ M_TEMP, M_NOWAIT)) {
+ bzero(scbp, sizeof(struct sea_scb));
+ sea->numscb++;
+ scbp->flags = SCB_ACTIVE;
+ scbp->next = NULL;
+ } else {
+ printf("sea%d: Can't malloc SCB\n",unit);
+ }
+ goto gottit;
+ } else {
+#ifdef SEADEBUG12
+ printf("(4");
+#endif
+ if(!(flags & SCSI_NOSLEEP)) {
+#ifdef SEADEBUG2
+ printf("(5");
+#endif
+ tsleep(&sea->free_scb, PRIBIO, "seascb", 0);
+ }
+ }
+ }
+ if (scbp) {
+#ifdef SEADEBUG2
+ printf("(6");
+#endif
+ /* Get SCB from free list */
+ sea->free_scb = scbp->next;
+ scbp->next = NULL;
+ scbp->flags = SCB_ACTIVE;
+ }
+ gottit:
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+
+ return(scbp);
+}
+
+/*
+ * sea_send_scb
+ *
+ * Try to send this command to the board. Because this board does not use any
+ * mailboxes, this routine simply adds the command to the queue held by the
+ * sea_data structure.
+ * A check is done to see if the command contains a REQUEST_SENSE command, and
+ * if so the command is put first in the queue, otherwise the command is added
+ * to the end of the queue. ?? Not correct ??
+ */
+int
+sea_send_scb(struct sea_data *sea, struct sea_scb *scb)
+{
+ struct sea_scb *tmp;
+ int oldpri = 0;
+
+#ifdef SEADEBUG2
+ printf("+");
+#endif
+
+ if(!(scb->xfer->flags & SCSI_NOSLEEP)) {
+ oldpri = splbio();
+ }
+
+ /* add to head of queue if queue empty or command is REQUEST_SENSE */
+
+ if (!(sea->issue_queue)
+#ifdef SEA_SENSEFIRST
+ || (scb->xfer->cmd->opcode == (u_char) REQUEST_SENSE)
+#endif
+ ) {
+#ifdef SEADEBUG2
+ printf("+2");
+#endif
+ scb->next = sea->issue_queue;
+ sea->issue_queue = scb;
+ } else {
+#ifdef SEADEBUG2
+ printf("+3");
+#endif
+ for (tmp = sea->issue_queue; tmp->next; tmp = tmp->next);
+ tmp->next = scb;
+ scb->next = NULL; /* placed at the end of the queue */
+ }
+ /* Try to do some work on the card */
+ if (!main_running) {
+ main_running = 1;
+ sea_main();
+ /* main running is cleared in sea_main once it can't
+ * do more work, and sea_main exits with interrupts
+ * disabled
+ */
+ }
+ if(!(scb->xfer->flags & SCSI_NOSLEEP)) {
+ splx(oldpri);
+ }
+ return (1); /* No possible errors right now */
+}
+
+/*
+ * sea_main(void)
+ *
+ * corroutine that runs as long as more work can be done on the seagate host
+ * adapter in a system. Both sea_scsi_cmd and sea_intr will try to start it in
+ * case it is not running.
+ */
+
+static void sea_main(void)
+{
+ struct sea_data *sea; /* This time we look at all cards */
+ struct sea_scb *tmp, *prev;
+ int done;
+ int unit;
+ int oldpri;
+
+#ifdef SEADEBUG2
+ printf(".");
+#endif
+
+ /*
+ * This should not be run with interrupts disabled, but use the splx code
+ * instead
+ */
+ do {
+ done = 1;
+ for (sea=seadata[unit=0]; (unit < NSEA) && seadata[unit] ;
+ sea=seadata[++unit]) {
+ oldpri = splbio();
+ if (!sea->connected) {
+#ifdef SEADEBUG2
+ printf(".2");
+#endif
+ /*
+ * Search through the issue_queue for a command destined for a
+ * target that's not busy.
+ */
+ for (tmp = sea->issue_queue, prev = NULL; tmp ;
+ prev = tmp, tmp = tmp->next)
+ /* When we find one, remove it from the issue queue. */
+ if (!(sea->busy[tmp->xfer->sc_link->target] &
+ (1 << tmp->xfer->sc_link->lun))) {
+ if (prev)
+ prev->next = tmp->next;
+ else
+ sea->issue_queue = tmp->next;
+ tmp->next = NULL;
+
+ /* re-enable interrupts after finding one */
+ splx(oldpri);
+
+ /*
+ * Attempt to establish an I_T_L nexus here.
+ * On success, sea->connected is set.
+ * On failure, we must add the command back to
+ * the issue queue so we can keep trying.
+ */
+#ifdef SEADEBUG2
+ printf(".3");
+#endif
+
+ /* REQUEST_SENSE commands are issued without tagged
+ * queueing, even on SCSI-II devices because the
+ * contingent alligence condition exists for the
+ * entire unit.
+ */
+
+ /* First check that if any device has tried a reconnect while
+ * we have done other things with interrupts disabled
+ */
+
+ if ((STATUS & (STAT_SEL | STAT_IO)) == (STAT_SEL | STAT_IO)) {
+#ifdef SEADEBUG2
+ printf(".7");
+#endif
+ sea_reselect(sea);
+ break;
+ }
+ if (!sea_select(sea, tmp)) {
+#ifdef SEADEBUG2
+ /* printf("Select returned ok\n"); */
+ printf(".4");
+#endif
+ break;
+ } else {
+ oldpri = splbio();
+ tmp->next = sea->issue_queue;
+ sea->issue_queue = tmp;
+ splx(oldpri);
+ printf("sea_main: select failed\n");
+ }
+ } /* if target/lun is not busy */
+ } /* if (!sea->connected) */
+
+ if (sea->connected) { /* we are connected. Do the task */
+ splx(oldpri);
+#ifdef SEADEBUG2
+/* printf("sea_main: starting information transfer!\n"); */
+ printf(".5");
+#endif
+ sea_information_transfer(sea);
+#ifdef SEADEBUG2
+/* printf("sea_main: sea->connected:%lx\n", sea->connected); */
+ printf(".6%lx ", sea->connected);
+#endif
+ done = 0;
+ } else
+ break;
+ } /* for instance */
+ } while (!done);
+ main_running = 0;
+}
+
+void
+sea_free_scb(unit, scb, flags)
+ int unit;
+ struct sea_scb *scb;
+ int flags;
+{
+ struct sea_data *sea = seadata[unit];
+ unsigned int opri = 0;
+
+#ifdef SEADEBUG2
+/* printf("free_scb\n"); */
+ printf(")");
+#endif
+
+ if(!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+ scb->next = sea->free_scb;
+ sea->free_scb = scb;
+ scb->flags = SCB_FREE;
+ /*
+ * If there were none, wake anybody waiting for one to come free,
+ * starting with queued entries.
+ */
+ if(!scb->next) {
+#ifdef SEADEBUG2
+/* printf("free_scb waking up sleep\n"); */
+ printf(")2");
+#endif
+#ifdef SEA_FREEBSD11
+ wakeup((caddr_t)&sea->free_scb);
+#else
+ wakeup(&sea->free_scb);
+#endif
+ }
+
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+}
+
+#ifdef SEA_FREEBSD11
+void
+sea_timeout(caddr_t arg1, int arg2)
+#else
+void
+sea_timeout(struct sea_scb *scb)
+#endif
+{
+#ifdef SEA_FREEBSD11
+ struct sea_scb *scb = (struct sea_scb *)arg1;
+#endif
+ int unit;
+ struct sea_data *sea;
+ int s=splbio();
+
+#ifdef SEADEBUG2
+/* printf("sea_timeout called\n"); */
+ printf(":");
+#endif
+
+ unit = scb->xfer->sc_link->adapter_unit;
+ sea = seadata[unit];
+#ifndef SEADEBUG /* print message only if not waiting unless debug */
+ if(!(scb->xfer->flags & SCSI_NOMASK))
+#endif
+ printf("sea%d:%d:%d (%s%d) timed out ", unit,
+ scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun,
+ scb->xfer->sc_link->device->name,
+ scb->xfer->sc_link->dev_unit);
+
+ /*
+ * If it has been through before, then
+ * a previous abort has failed, don't
+ * try abort again
+ */
+ if (/* (sea_abort(unit, scb) != 1) ||*/ (scb->flags & SCB_ABORTED)) {
+ /*
+ * abort timed out
+ */
+#ifdef SEADEBUG2
+/* printf("sea%d: Abort Operation has timed out\n", unit); */
+ printf(":2");
+#endif
+ scb->xfer->retries = 0;
+ scb->flags |= SCB_ABORTED;
+ sea_done(unit, scb);
+ } else {
+ #ifdef SEADEBUG2
+ /* printf("sea%d: Try to abort\n", unit); */
+ printf(":3");
+ #endif
+ sea_abort(unit, scb);
+ /* sea_send_scb(sea, ~SCSI_NOMASK, SEA_SCB_ABORT, scb); */
+ /* 2 seconds for the abort */
+ #ifdef SEA_FREEBSD11
+ timeout(sea_timeout, (caddr_t)scb, 2*hz);
+ #else
+ timeout(sea_timeout, scb, 2*hz);
+ #endif
+ scb->flags |= (SCB_ABORTED | SCB_TIMECHK);
+ }
+ splx(s);
+}
+
+int
+sea_reselect(sea)
+ struct sea_data *sea;
+{
+ unsigned char target_mask;
+ long l;
+ unsigned char lun, phase;
+ unsigned char msg[3];
+ int32 len;
+ u_char *data;
+ struct sea_scb *tmp = 0, *prev = 0;
+ int abort = 0;
+
+#if SEADEBUG2
+/* printf("sea_reselect called\n"); */
+ printf("}");
+#endif
+
+ if (!((target_mask = STATUS) & STAT_SEL)) {
+ printf("sea: wrong state 0x%x\n", target_mask);
+ return(0);
+ }
+ /* wait for a device to win the reselection phase */
+ /* signals this by asserting the I/O signal */
+ for(l=10; l && (STATUS & (STAT_SEL | STAT_IO | STAT_BSY))
+ != (STAT_SEL | STAT_IO | 0);l--);
+ /* !! Check for timeout here */
+ /* the data bus contains original initiator id ORed with target id */
+ target_mask = DATA;
+ /* see that we really are the initiator */
+ if (!(target_mask & ((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40))) {
+ printf("sea: polled reselection was not for me: %x\n",target_mask);
+ return(0);
+ }
+ /* find target who won */
+ target_mask &= ((sea->ctrl_type == SEAGATE) ? ~0x80 : ~0x40);
+ /* host responds by asserting the BSY signal */
+ CONTROL = (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY);
+ /* target should respond by deasserting the SEL signal */
+ for(l=50000;l && (STATUS & STAT_SEL);l++);
+ /* remove the busy status */
+ CONTROL = (BASE_CMD | CMD_DRVR_ENABLE);
+ /* we are connected. Now we wait for the MSGIN condition */
+ for(l=50000; l && !(STATUS & STAT_REQ);l--);
+ /* !! Add timeout check here */
+ /* hope we get an IDENTIFY message */
+ len = 3;
+ data = msg;
+ phase = REQ_MSGIN;
+ sea_transfer_pio(sea, &phase, &len, &data);
+
+ if (!(msg[0] & 0x80)) {
+ printf("sea: Expecting IDENTIFY message, got 0x%x\n", msg[0]);
+ abort = 1;
+ } else {
+ lun = (msg[0] & 0x07);
+
+ /*
+ * Find the command corresponding to the I_T_L or I_T_L_Q nexus we
+ * just restablished, and remove it from the disconnected queue.
+ */
+
+ for(tmp = sea->disconnected_queue, prev = NULL;
+ tmp; prev=tmp, tmp = tmp->next)
+ if((target_mask == (1 << tmp->xfer->sc_link->target)) &&
+ (lun == tmp->xfer->sc_link->lun)) {
+ if(prev) {
+#ifdef SEADEBUG2
+ printf("}2");
+#endif
+ prev->next = tmp->next;
+ } else {
+#ifdef SEADEBUG2
+ printf("}3");
+#endif
+ sea->disconnected_queue = tmp->next;
+ }
+ tmp->next = NULL;
+ break;
+ }
+ if (!tmp) {
+ printf("sea: warning : target %02x lun %d not in disconnect_queue\n",
+ target_mask, lun);
+ /*
+ * Since we have an established nexus that we can't do anything with,
+ * we must abort it.
+ */
+ abort = 1;
+ }
+ }
+
+ if(abort) {
+#ifdef SEADEBUG2
+ printf("}4");
+#endif
+ msg[0] = MSG_ABORT;
+ len = 1;
+ data = msg;
+ phase = REQ_MSGOUT;
+ CONTROL = (BASE_CMD | CMD_ATTN);
+ sea_transfer_pio(sea, &phase, &len, &data);
+ } else {
+#ifdef SEADEBUG2
+ printf("}5");
+#endif
+ sea->connected = tmp;
+ }
+ /* return value not used yet */
+ return 0;
+}
+
+/* Transfer data in given phase using polled I/O
+*/
+
+int sea_transfer_pio(struct sea_data *sea, u_char *phase, int32 *count,
+ u_char **data)
+{
+ register unsigned char p = *phase, tmp;
+ register int c = *count;
+ register unsigned char *d = *data;
+ unsigned long int timeout;
+
+#if SEADEBUG2
+/* printf("sea_transfer_pio called: len:%x\n",c); */
+ printf("-1 %x %x", c, p);
+#endif
+
+ do {
+ /* wait for assertion of REQ, after which the phase bits will be valid */
+ for(timeout = 0; timeout < 5000000L ; timeout++)
+ if ((tmp = STATUS) & STAT_REQ)
+ break;
+ if (!(tmp & STAT_REQ)) {
+ printf("sea_transfer_pio: timeout waiting for STAT_REQ\n");
+ break;
+ }
+
+ /* check for phase mismatch */
+ /* Reached if the target decides that it has finished the transfer */
+ if ((tmp & REQ_MASK) != p) {
+#ifdef SEADEBUG1
+/* printf("-2 %x", tmp); */
+ printf("sea:pio phase mismatch:%x, want:%x, len:%x\n",tmp,p,c);
+#endif
+ break;
+ }
+
+ /* Do actual transfer from SCSI bus to/from memory */
+ if (!(p & STAT_IO))
+ DATA = *d;
+ else
+ *d = DATA;
+#ifdef SEADEBUG15
+ printf("-7%x", *d);
+#endif
+ ++d;
+
+ /* The SCSI standard suggests that in MSGOUT phase, the initiator
+ * should drop ATN on the last byte of the message phase
+ * after REQ has been asserted for the handshake but before
+ * the initiator raises ACK.
+ * Don't know how to accomplish this on the ST01/02
+ */
+ /* We don't mind right now. */
+
+ /* The st01 code doesn't wait for STAT_REQ to be deasserted. Is this ok? */
+/* for(timeout=0;timeout<200000L;timeout++)
+ if(!(STATUS & STAT_REQ))
+ break;
+ if(STATUS & STAT_REQ)
+ printf("timeout on wait for !STAT_REQ"); */
+/* printf("*"); */
+ } while (--c);
+
+ *count = c;
+ *data = d;
+ tmp = STATUS;
+ if (tmp & STAT_REQ) {
+#if SEADEBUG2
+ printf("-3%x", tmp);
+#endif
+ *phase = tmp & REQ_MASK;
+ } else {
+#if SEADEBUG2
+ printf("-4%x", tmp);
+#endif
+ *phase = REQ_UNKNOWN;
+ }
+ if (!c || (*phase == p)) {
+#if SEADEBUG2
+ printf("-5%x %x", c, *phase);
+#endif
+ return 0;
+ } else {
+#if SEADEBUG2
+ printf("-6");
+#endif
+ return -1;
+ }
+}
+
+/* sea_select
+ * establish I_T_L or I_T_L_Q nexus for new or existing command
+ * including ARBITRATION, SELECTION, and initial message out for IDENTIFY and
+ * queue messages.
+ * return -1 if selection could not execute for some reason, 0 if selection
+ * succeded or failed because the taget did not respond
+ */
+int sea_select(struct sea_data *sea, struct sea_scb *scb)
+{
+ unsigned char tmp[3], phase;
+ u_char *data;
+ int32 len;
+ unsigned long timeout;
+
+#ifdef SEADEBUG2
+/* printf("sea_select called\n"); */
+ printf("{");
+#endif
+
+ CONTROL = BASE_CMD;
+ DATA = ((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40);
+ CONTROL = (BASE_CMD & ~CMD_INTR) | CMD_START_ARB;
+ /* wait for arbitration to complete */
+ for (timeout = 0; timeout < 3000000L ; timeout++) {
+ if (STATUS & STAT_ARB_CMPL)
+ break;
+ }
+ if (!(STATUS & STAT_ARB_CMPL)) {
+ if (STATUS & STAT_SEL) {
+ printf("sea: arbitration lost\n");
+ scb->flags |= SCB_ERROR;
+ } else {
+ printf("sea: arbitration timeout.\n");
+ scb->flags |=SCB_TIMEOUT;
+ }
+ CONTROL = BASE_CMD;
+ return(-1);
+ }
+ DELAY(2);
+
+#if SEADEBUG2
+/* printf("after arbitration: STATUS=%x\n", STATUS); */
+ printf("{2 %x", STATUS);
+#endif
+
+ DATA = (unsigned char)((1 << scb->xfer->sc_link->target) |
+ ((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40));
+#ifdef SEANOMSGS
+ CONTROL = (BASE_CMD & (~CMD_INTR))| CMD_DRVR_ENABLE | CMD_SEL;
+#else
+ CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE | CMD_SEL | CMD_ATTN;
+#endif
+ DELAY(1);
+ /* wait for a bsy from target */
+ for (timeout = 0; timeout < 2000000L; timeout++) {
+ if (STATUS & STAT_BSY)
+ break;
+ }
+
+#if SEADEBUG2
+/* printf("after wait for BSY: STATUS=%x,count=%lx\n", STATUS, timeout); */
+ printf("{3 %x %x", STATUS, timeout);
+#endif
+
+ if (!(STATUS & STAT_BSY)) {
+ /* should return some error to the higher level driver */
+ CONTROL = BASE_CMD;
+#if SEADEBUG2
+/* printf("sea: target did not respond\n"); */
+ printf("{4");
+#endif
+ scb->flags |= SCB_TIMEOUT;
+ return 0;
+ }
+
+ /* Try to make the target to take a message from us */
+#ifdef SEANOMSGS
+ CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE;
+#else
+ CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE | CMD_ATTN;
+#endif
+
+ DELAY(1);
+
+ /* should start a msg_out phase */
+ for (timeout = 0; timeout < 2000000L ; timeout++) {
+ if (STATUS & STAT_REQ)
+ break;
+ }
+
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE;
+
+#if SEADEBUG2 || SEADEBUG9
+/* printf("after wait for STAT_REQ: STATUS=%x,count=%lx\n", STATUS, timeout);
+ printf("2:nd try after wait for STAT_REQ: STATUS=%x\n", STATUS); */
+ printf("{5%x", timeout);
+#endif
+
+ if (!(STATUS & STAT_REQ)) {
+ /* This should not be taken as an error, but more like an unsupported
+ * feature!
+ * Should set a flag indicating that the target don't support messages, and
+ * continue without failure. (THIS IS NOT AN ERROR!)
+ */
+#if SEADEBUG
+/* printf("{6"); */
+ printf("sea: WARNING: target %x don't support messages?\n",
+ scb->xfer->sc_link->target);
+#endif
+ } else {
+ tmp[0] = IDENTIFY(1, scb->xfer->sc_link->lun); /* allow disconnects */
+ len = 1;
+ data = tmp;
+ phase = REQ_MSGOUT;
+ /* Should do test on result of sea_transfer_pio */
+#if SEADEBUG2
+/* printf("Trying a msg out phase\n"); */
+ printf("{7");
+#endif
+ sea_transfer_pio(sea, &phase, &len, &data);
+ }
+ if (!(STATUS & STAT_BSY)) {
+ printf("sea: after successful arbitrate: No STAT_BSY!\n");
+ }
+
+#if SEADEBUG2
+ printf("{8");
+#endif
+ sea->connected = scb;
+ sea->busy[scb->xfer->sc_link->target] |= (1 << scb->xfer->sc_link->lun);
+ /* this assignment should depend on possibility to send a message to target */
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE;
+ /* reset pointer in command ??? */
+ return 0;
+}
+
+/* sea_abort
+ send an abort to the target
+ return 1 success, 0 on failure
+ */
+int sea_abort(int unit, struct sea_scb *scb)
+{
+ struct sea_data *sea = seadata[unit];
+ struct sea_scb *tmp, **prev;
+ unsigned char msg, phase, *msgptr;
+ int32 len;
+ int oldpri;
+
+#ifdef SEADEBUG2
+/* printf("sea_abort called\n"); */
+ printf("\\");
+#endif
+
+ oldpri = splbio();
+
+ /* If the command hasn't been issued yet, we simply remove it from the
+ * issue queue
+ */
+ for (prev = (struct sea_scb **) &(sea->issue_queue),
+ tmp = sea->issue_queue; tmp;
+ prev = (struct sea_scb **) &(tmp->next), tmp = tmp->next)
+ if (scb == tmp) {
+ (*prev) = tmp->next;
+ tmp->next = NULL;
+ /* set some type of error result for this operation */
+ splx(oldpri);
+#ifdef SEADEBUG2
+ printf("\\2");
+#endif
+ return 1;
+ }
+
+ /* If any commands are connected, we're going to fail the abort
+ * and let the high level SCSI driver retry at a later time or issue a
+ * reset
+ */
+
+ if(sea->connected) {
+ splx(oldpri);
+#ifdef SEADEBUG2
+/* printf("sea:abort error connected\n"); */
+ printf("\\3");
+#endif
+ return 0;
+ }
+
+ /* If the command is currently disconnected from the bus, and there are
+ * no connected commands, we reconnect the I_T_L or I_T_L_Q nexus
+ * associated with it, go into message out, and send an abort message.
+ */
+
+ for (tmp = sea->disconnected_queue; tmp; tmp = tmp->next)
+ if (scb == tmp) {
+ splx(oldpri);
+#ifdef SEADEBUG2
+ printf("\\4");
+#endif
+ if (sea_select(sea,scb)) {
+#ifdef SEADEBUG2
+ printf("\\5");
+#endif
+ return 0;
+ }
+ msg = MSG_ABORT;
+ msgptr = &msg;
+ len = 1;
+ phase = REQ_MSGOUT;
+ CONTROL = BASE_CMD | CMD_ATTN;
+ sea_transfer_pio(sea, &phase, &len, &msgptr);
+
+ oldpri = splbio();
+ for (prev = (struct sea_scb **) &(sea->disconnected_queue),
+ tmp = sea->disconnected_queue; tmp ;
+ prev = (struct sea_scb **) &(tmp->next), tmp = tmp->next)
+ if (scb == tmp) {
+ *prev = tmp->next;
+ tmp->next = NULL;
+ /* set some type of error result for the operation */
+#ifdef SEADEBUG2
+ printf("\\6");
+#endif
+ splx(oldpri);
+ return 1;
+ }
+ }
+
+ /* command not found in any queue, race condition in the code ? */
+
+ splx(oldpri);
+#ifdef SEADEBUG2
+/* printf("sea: WARNING: SCSI command probably completed successfully\n"
+ " before abortion\n"); */
+ printf("\\7");
+#endif
+ return 1;
+
+}
+
+void sea_done(int unit, struct sea_scb *scb)
+{
+ struct sea_data *sea = seadata[unit];
+ struct scsi_xfer *xs = scb->xfer;
+
+
+#ifdef SEADEBUG2
+/* printf("sea_done called\n"); */
+ printf("&");
+#endif
+
+ if (scb->flags & SCB_TIMECHK) {
+#ifdef SEADEBUG2
+ printf("&2");
+#endif
+#ifdef SEA_FREEBSD11
+ untimeout(sea_timeout, (caddr_t)scb);
+#else
+ untimeout(sea_timeout, scb);
+#endif
+ }
+
+ xs->resid = scb->datalen; /* How much of the buffer was not touched */
+
+ if ((scb->flags == SCB_ACTIVE) || (xs->flags & SCSI_ERR_OK)) {
+#ifdef SEADEBUG2
+/* printf("sea_done:Report no err in xs\n"); */
+ printf("&3");
+#endif
+/* xs->resid = 0; */
+/* xs->error = 0; */
+ } else {
+
+ if (!(scb->flags == SCB_ACTIVE)) {
+ if ((scb->flags & SCB_TIMEOUT) || (scb->flags & SCB_ABORTED)) {
+#ifdef SEADEBUG2
+ printf("&6");
+#endif
+ xs->error = XS_TIMEOUT;
+ }
+ if (scb->flags & SCB_ERROR) {
+#ifdef SEADEBUG2
+ printf("&7");
+#endif
+ xs->error = XS_DRIVER_STUFFUP;
+ }
+ } else {
+
+ /* !!! Add code to check for target status */
+ /* say all error now */
+ xs->error = XS_DRIVER_STUFFUP;
+#ifdef SEADEBUG2
+ printf("&4");
+#endif
+ }
+ }
+ xs->flags |= ITSDONE;
+ sea_free_scb(unit, scb, xs->flags);
+ scsi_done(xs);
+#ifdef SEADEBUG2
+/* printf("Leaving sea_done\n"); */
+ printf("&5");
+#endif
+}
+
+/* wait for completion of command in polled mode */
+
+int sea_poll(int unit, struct scsi_xfer *xs, struct sea_scb *scb)
+{
+ int count = 500; /* xs->timeout; */
+ int oldpri;
+
+#ifdef SEADEBUG2
+/* printf("sea_poll called\n"); */
+ printf("?");
+#endif
+
+ while (count) {
+ /* try to do something */
+ oldpri = splbio();
+ if (!main_running) {
+ main_running = 1;
+ sea_main();
+ /* main_running is cleared in sea_main once it can't
+ * do more work, and sea_main exits with interrupts
+ * disabled
+ */
+ splx(oldpri);
+ } else {
+ splx(oldpri);
+ }
+ if (xs->flags & ITSDONE) {
+ break;
+ }
+ DELAY(10);
+ count--;
+ }
+#ifdef SEADEBUG2
+ printf("?2 %x ", count);
+/* printf("sea_poll: count:%x\n",count); */
+#endif
+ if (count == 0) {
+ /* we timed out, so call the timeout handler manually,
+ * accounting for the fact that the clock is not running yet
+ * by taking out the clock queue entry it makes.
+ */
+#ifdef SEADEBUG2
+ printf("?3");
+#endif
+#ifdef SEA_FREEBSD11
+ sea_timeout((caddr_t)scb, 0);
+#else
+ sea_timeout(scb);
+#endif
+
+ /* because we are polling, take out the timeout entry
+ * sea_timeout made
+ */
+#ifdef SEADEBUG2
+ printf("?4");
+#endif
+#ifdef SEA_FREEBSD11
+ untimeout(sea_timeout, (caddr_t) scb);
+#else
+ untimeout(sea_timeout, scb);
+#endif
+ count = 50;
+ while (count) {
+ /* once again, wait for the int bit */
+ oldpri = splbio();
+ if (!main_running) {
+ main_running = 1;
+ sea_main();
+ /* main_running is cleared by sea_main once it can't
+ * do more work, and sea_main exits with interrupts
+ * disabled
+ */
+ splx(oldpri);
+ } else {
+ splx(oldpri);
+ }
+ if (xs->flags & ITSDONE) {
+ break;
+ }
+ DELAY(10);
+ count--;
+ }
+ if (count == 0) {
+ /* we timed out again... This is bad. Notice that
+ * this time there is no clock queue entry to remove
+ */
+#ifdef SEADEBUG2
+ printf("?5");
+#endif
+#ifdef SEA_FREEBSD11
+ sea_timeout((caddr_t)scb, 0);
+#else
+ sea_timeout(scb);
+#endif
+ }
+ }
+#ifdef SEADEBUG2
+/* printf("sea_poll: xs->error:%x\n",xs->error); */
+ printf("?6%x",xs->error);
+#endif
+ if (xs->error) {
+#ifdef SEADEBUG2
+/* printf("done return error\n"); */
+ printf("?7");
+#endif
+ return (HAD_ERROR);
+ }
+#ifdef SEADEBUG2
+/* printf("done return complete\n"); */
+ printf("?8");
+#endif
+ return (COMPLETE);
+}
+
+/*
+ * sea_information_transfer
+ * Do the transfer. We know we are connected. Update the flags,
+ * call sea_done when task accomplished. Dialog controlled by the
+ * target
+ */
+static void sea_information_transfer (struct sea_data *sea)
+{
+ long int timeout;
+ int unit = sea->sc_link.adapter_unit;
+ unsigned char msgout = MSG_NOP;
+ int32 len;
+ int oldpri;
+ u_char *data;
+ unsigned char phase, tmp, old_phase=REQ_UNKNOWN;
+ struct sea_scb *scb = sea->connected;
+ int loop;
+
+#if SEADEBUG2
+/* printf("sea_information_transfer called\n"); */
+ printf("!");
+#endif
+
+ for(timeout = 0; timeout < 10000000L ; timeout++) {
+ tmp = STATUS;
+ if (!(tmp & STAT_BSY)) {
+/* for(loop=0;loop < 20 ; loop++) {
+ if((tmp=STATUS) & STAT_BSY)
+ break;
+ } */
+#ifndef SEADEBUG8
+ if(!(tmp & STAT_BSY)) {
+ printf("sea: !STAT_BSY unit in data transfer!\n");
+ oldpri = splbio();
+ sea->connected = NULL;
+ scb->flags = SCB_ERROR;
+ splx(oldpri);
+ sea_done(unit, scb);
+ return;
+ }
+#endif
+ }
+
+ /* we only have a valid SCSI phase when REQ is asserted */
+ if (tmp & STAT_REQ) {
+ phase = (tmp & REQ_MASK);
+ if (phase != old_phase) {
+ old_phase = phase;
+ }
+
+#ifdef SEADEBUG7
+ printf("!2%x", phase);
+ for(loop=0;loop < 20; loop++) {
+ phase = STATUS;
+ printf("!6%x",phase);
+ phase = phase & REQ_MASK;
+ }
+#endif
+
+ switch (phase) {
+ case REQ_DATAOUT:
+#ifdef SEA_NODATAOUT
+ printf("sea: SEA_NODATAOUT set, attempted DATAOUT aborted\n");
+ msgout = MSG_ABORT;
+ CONTROL = BASE_CMD | CMD_ATTN;
+ break;
+#endif
+ case REQ_DATAIN:
+/* data = scb->xfer->data;
+ len = scb->xfer->datalen;
+*/ if(!(scb->data)) {
+ printf("no data address!\n");
+ }
+#ifdef SEA_BLINDTRANSFER
+ if (scb->datalen && !(scb->datalen % BLOCK_SIZE)) {
+ while (scb->datalen) {
+ for(timeout = 0; timeout < 5000000L ; timeout++)
+ if((tmp = STATUS) & STAT_REQ)
+ break;
+ if(!(tmp & STAT_REQ)) {
+ printf("sea_transfer_pio: timeout waiting for STAT_REQ\n");
+ /* getchar(); */
+ }
+ if((tmp & REQ_MASK) != phase) {
+#ifdef SEADEBUG1
+ printf("sea:infotransfer phase mismatch:%x, want:%x, len:%x\n",
+ tmp,phase,scb->datalen);
+ /* getchar(); */
+#endif
+ break;
+ }
+ if(!(phase & STAT_IO)) {
+#ifdef SEA_ASSEMBLER
+ asm("
+ shr $2, %%ecx;
+ cld;
+ rep;
+ movsl; " : :
+ "D" (sea->st0x_dr), "S" (scb->data), "c" (BLOCK_SIZE) :
+ "cx", "si", "di" );
+ scb->data += BLOCK_SIZE;
+#else
+ for(count=0; count < BLOCK_SIZE; count++) {
+ DATA = *(scb->data);
+ scb->data++;
+ }
+#endif
+ } else {
+#ifdef SEA_ASSEMBLER
+ asm("
+ shr $2, %%ecx;
+ cld;
+ rep;
+ movsl; " : :
+ "S" (sea->st0x_dr), "D" (scb->data), "c" (BLOCK_SIZE) :
+ "cx", "si", "di" );
+ scb->data += BLOCK_SIZE;
+#else
+ for(count=0; count < BLOCK_SIZE; count++) {
+ *scb->data = DATA;
+ scb->data++;
+ }
+#endif
+ }
+ scb->datalen -= BLOCK_SIZE;
+ }
+ }
+
+ /* save current position into the command structure */
+/* scb->xfer->data = data;
+ scb->xfer->datalen = len; */
+#endif
+
+ sea_transfer_pio(sea, &phase, &(scb->datalen), &(scb->data));
+/* scb->xfer->data = data;
+ scb->xfer->datalen = len;
+*/ break;
+
+ case REQ_MSGIN:
+ /* don't handle multi-byte messages here, because they
+ * should not be present here
+ */
+ len = 1;
+ data = &tmp;
+ sea_transfer_pio(sea, &phase, &len, &data);
+ /* scb->MessageIn = tmp; */
+
+ switch (tmp) {
+
+ case MSG_ABORT:
+ scb->flags = SCB_ABORTED;
+ printf("sea:Command aborted by target\n");
+ CONTROL = BASE_CMD;
+ sea_done(unit, scb);
+ return;
+
+ case MSG_COMMAND_COMPLETE:
+ oldpri = splbio();
+ sea->connected = NULL;
+ splx(oldpri);
+#ifdef SEADEBUG2
+ printf("!3");
+#endif
+ sea->busy[scb->xfer->sc_link->target] &=
+ ~(1 << scb->xfer->sc_link->lun);
+
+ CONTROL = BASE_CMD;
+ sea_done(unit, scb);
+ return;
+ case MSG_MESSAGE_REJECT:
+ /* printf("sea: message_reject recieved\n"); */
+ printf("!4");
+ break;
+ case MSG_DISCONNECT:
+ oldpri = splbio();
+ scb->next = sea->disconnected_queue;
+ sea->disconnected_queue = scb;
+ sea->connected = NULL;
+ CONTROL = BASE_CMD;
+ splx(oldpri);
+#ifdef SEADEBUG2
+/* printf("msg_disconnect\n"); */
+ printf("!5");
+#endif
+ return;
+ /* save/restore of pointers are ignored */
+ case MSG_SAVE_POINTERS:
+ case MSG_RESTORE_POINTERS:
+#if SEADEBUG2
+ printf("sea: rec save/restore ptrs\n");
+#endif
+ break;
+ default:
+ /* this should be handled in the pio data transfer phase, as the
+ * ATN should be raised before ACK goes false when rejecting a message
+ */
+#ifdef SEADEBUG
+ printf("sea: Unknown message in:%x\n", tmp);
+#endif
+ break;
+ } /* switch (tmp) */
+ break;
+ case REQ_MSGOUT:
+ len = 1;
+ data = &msgout;
+ /* sea->last_message = msgout; */
+ sea_transfer_pio(sea, &phase, &len, &data);
+ if (msgout == MSG_ABORT) {
+ printf("sea: sent message abort to target\n");
+ oldpri = splbio();
+ sea->busy[scb->xfer->sc_link->target] &=
+ ~(1 << scb->xfer->sc_link->lun);
+ sea->connected = NULL;
+ scb->flags = SCB_ABORTED;
+ splx(oldpri);
+ /* enable interrupt from scsi */
+ sea_done(unit, scb);
+ return;
+ }
+ msgout = MSG_NOP;
+ break;
+ case REQ_CMDOUT:
+ len = scb->xfer->cmdlen;
+ data = (char *) scb->xfer->cmd;
+ sea_transfer_pio(sea, &phase, &len, &data);
+ break;
+ case REQ_STATIN:
+ len = 1;
+ data = &tmp;
+ sea_transfer_pio(sea, &phase, &len, &data);
+ scb->xfer->status = tmp;
+ break;
+ default:
+ printf("sea: unknown phase\n");
+ } /* switch (phase) */
+ } /* if (tmp & STAT_REQ) */
+ } /* for (...) */
+ /* if we get here we have got a timeout! */
+ printf("sea: Timeout in data transfer\n");
+ scb->flags = SCB_TIMEOUT;
+ /* should I clear scsi-bus state? */
+ sea_done(unit, scb);
+}
+
+
diff --git a/sys/i386/isa/sio.c b/sys/i386/isa/sio.c
index 4cee4fe8c60f..6fa09247e5f3 100644
--- a/sys/i386/isa/sio.c
+++ b/sys/i386/isa/sio.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)com.c 7.5 (Berkeley) 5/16/91
- * $Id: sio.c,v 1.28 1994/02/07 18:37:21 ache Exp $
+ * $Id: sio.c,v 1.56 1994/06/16 08:08:44 ache Exp $
*/
#include "sio.h"
@@ -49,17 +49,19 @@
#include "proc.h"
#include "user.h"
#include "conf.h"
+#include "dkstat.h"
#include "file.h"
#include "uio.h"
#include "kernel.h"
+#include "malloc.h"
#include "syslog.h"
+#include "i386/isa/icu.h" /* XXX */
#include "i386/isa/isa.h"
#include "i386/isa/isa_device.h"
#include "i386/isa/comreg.h"
#include "i386/isa/ic/ns16550.h"
-#define FAKE_DCD(unit) ((unit) == comconsole)
#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */
#define RB_I_HIGH_WATER (RBSZ - 2 * RS_IBUFSIZE)
#define RB_I_LOW_WATER ((RBSZ - 2 * RS_IBUFSIZE) * 7 / 8)
@@ -67,38 +69,27 @@
#define TTY_BI TTY_FE /* XXX */
#define TTY_OE TTY_PE /* XXX */
-#ifndef COM_BIDIR
-#define UNIT(x) (minor(x)) /* XXX */
-#else /* COM_BIDIR */
-#define COM_UNITMASK 0x7f
-#define COM_CALLOUTMASK 0x80 /* for both minor and dev */
-#define UNIT(x) (minor(x) & COM_UNITMASK)
-#define CALLOUT(x) (minor(x) & COM_CALLOUTMASK)
-#endif /* COM_BIDIR */
+#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_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_NOFIFO(dev) ((dev)->id_flags & 0x02)
-#ifndef FIFO_TRIGGER
-/*
- * This driver is fast enough to work with any value and for high values
- * to be only slightly more efficient. Low values may be better because
- * they give lower latency.
- * TODO: always use low values for low speeds. Mouse movements are jerky
- * if more than one packet arrives at once. The low speeds used for
- * serial mice help avoid this, but not if (large) fifos are enabled.
- */
-#define FIFO_TRIGGER FIFO_TRIGGER_14
-#endif
+#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02)
+#define COM_VERBOSE(dev) ((dev)->id_flags & 0x80)
#define com_scr 7 /* scratch register for 16450-16550 (R/W) */
-#define setsofttty() (ipending |= 1 << 4) /* XXX */
/*
* Input buffer watermarks.
@@ -124,17 +115,19 @@
* CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam())
* CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam())
* TS_FLUSH is not used.
- * Bug: I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
+ * 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 *error_desc[] = {
+static char const * const error_desc[] = {
#define CE_OVERRUN 0
"silo overflow",
#define CE_INTERRUPT_BUF_OVERFLOW 1
@@ -153,20 +146,19 @@ typedef u_char bool_t; /* boolean */
/* 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 */
+ u_char ftl; /* current rx fifo trigger level */
+ u_char ftl_init; /* ftl_max for next open() */
+ u_char ftl_max; /* maximum ftl for curent open() */
bool_t hasfifo; /* nonzero for 16550 UARTs */
u_char mcr_image; /* copy of value written to MCR */
-#ifdef COM_BIDIR
- bool_t bidir; /* is this unit bidirectional? */
- bool_t active; /* is the port active _at all_? */
- bool_t active_in; /* is the incoming port in use? */
- bool_t active_out; /* is the outgoing port in use? */
-#endif /* COM_BIDIR */
#ifdef COM_MULTIPORT
bool_t multiport; /* is this unit part of a multiport device? */
#endif /* COM_MULTIPORT */
- int dtr_wait; /* time to hold DTR down on close (* 1/HZ) */
+ 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
@@ -177,6 +169,7 @@ struct com_s {
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 */
@@ -195,6 +188,19 @@ struct com_s {
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;
+
+#ifdef TIOCTIMESTAMP
+ bool_t do_timestamp;
+ struct timeval timestamp;
+#endif
+
u_long bytes_in; /* statistics */
u_long bytes_out;
u_int delta_error_counts[CE_NTYPES];
@@ -210,48 +216,56 @@ struct com_s {
};
/*
- * These functions in the com module ought to be declared (with a prototype)
- * in a com-driver system header. The void ones may need to be int to match
- * ancient devswitch declarations, but they don't actually return anything.
+ * The public functions in the com module ought to be declared in a com-driver
+ * system header.
*/
#define Dev_t int /* promoted dev_t */
-struct consdev;
+/* Interrupt handling entry points. */
+void siointr __P((int unit));
+void siopoll __P((void));
+
+/* Device switch entry points. */
+int sioopen __P((Dev_t dev, int oflags, int devtype,
+ struct proc *p));
int sioclose __P((Dev_t dev, int fflag, int devtype,
struct proc *p));
-void siointr __P((int unit));
+int sioread __P((Dev_t dev, struct uio *uio, int ioflag));
+int siowrite __P((Dev_t dev, struct uio *uio, int ioflag));
int sioioctl __P((Dev_t dev, int cmd, caddr_t data,
int fflag, struct proc *p));
+void siostop __P((struct tty *tp, int rw));
+#define sioreset noreset
+int sioselect __P((Dev_t dev, int rw, struct proc *p));
+#define siommap nommap
+#define siostrategy nostrategy
+
+/* Console device entry points. */
int siocngetc __P((Dev_t dev));
+struct consdev;
void siocninit __P((struct consdev *cp));
void siocnprobe __P((struct consdev *cp));
void siocnputc __P((Dev_t dev, int c));
-int sioopen __P((Dev_t dev, int oflags, int devtype,
- struct proc *p));
-void siopoll __P((void));
-int sioread __P((Dev_t dev, struct uio *uio, int ioflag));
-int sioselect __P((Dev_t dev, int rw, struct proc *p));
-void siostop __P((struct tty *tp, int rw));
-int siowrite __P((Dev_t dev, struct uio *uio, int ioflag));
-void softsio1 __P((void));
static int sioattach __P((struct isa_device *dev));
+static void siodtrwakeup __P((caddr_t chan, int ticks));
static void comflush __P((struct com_s *com));
static void comhardclose __P((struct com_s *com));
-static void cominit __P((int unit, int rate));
-static void comintr1 __P((struct com_s *com));
+static void siointr1 __P((struct com_s *com));
static void commctl __P((struct com_s *com, int bits, int how));
static int comparam __P((struct tty *tp, struct termios *t));
static int sioprobe __P((struct isa_device *dev));
-static void comstart __P((struct tty *tp));
-static void comwakeup __P((caddr_t chan, int ticks));
+static void comstart __P((struct tty *tp));
+static void comwakeup __P((caddr_t chan, int ticks));
static int tiocm_xxx2mcr __P((int tiocm_xxx));
/* 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 com_s com_structs[NSIO];
+#ifdef TIOCTIMESTAMP
+static struct timeval intr_timestamp;
+#endif
struct isa_driver siodriver = {
sioprobe, sioattach, "sio"
@@ -262,15 +276,11 @@ static int comconsole = COMCONSOLE;
#else
static int comconsole = -1;
#endif
-static bool_t comconsinit;
static speed_t comdefaultrate = TTYDEF_SPEED;
static u_int com_events; /* input chars + weighted output completions */
static int commajor;
-struct tty sio_tty[NSIO];
-extern struct tty *constty;
-extern u_int ipending; /* XXX */
-extern int tk_nin; /* XXX */
-extern int tk_rawcc; /* XXX */
+struct tty *sio_tty[NSIO];
+extern struct tty *constty; /* XXX */
#ifdef KGDB
#include "machine/remote-sl.h"
@@ -311,7 +321,11 @@ sioprobe(dev)
{
static bool_t already_init;
Port_t *com_ptr;
+ bool_t failures[10];
+ int fn;
+ struct isa_device *idev;
Port_t iobase;
+ u_char mcr_image;
int result;
if (!already_init) {
@@ -319,6 +333,7 @@ sioprobe(dev)
* 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.
*/
for (com_ptr = likely_com_ports;
com_ptr < &likely_com_ports[sizeof likely_com_ports
@@ -327,8 +342,45 @@ sioprobe(dev)
outb(*com_ptr + com_mcr, 0);
already_init = TRUE;
}
+
+ /*
+ * If the port is on a multiport card and has a master port,
+ * initialize the common interrupt control register in the
+ * master 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)) {
+ idev = find_isadev(isa_devtab_tty, &siodriver,
+ COM_MPMASTER(dev));
+ if (idev == NULL) {
+ printf("sio%d: master device %d not found\n",
+ dev->id_unit, COM_MPMASTER(dev));
+ return (0);
+ }
+ if (idev->id_irq == 0) {
+ printf("sio%d: master device %d irq not configured\n",
+ dev->id_unit, COM_MPMASTER(dev));
+ return (0);
+ }
+ if (!COM_NOTAST4(dev)) {
+ outb(idev->id_iobase + com_scr, 0x80);
+ mcr_image = 0;
+ }
+ }
+ else
+#endif /* COM_MULTIPORT */
+ if (idev->id_irq == 0) {
+ printf("sio%d: irq not configured\n", dev->id_unit);
+ return (0);
+ }
+
+ bzero(failures, sizeof failures);
iobase = dev->id_iobase;
- result = IO_COMSIZE;
/*
* We don't want to get actual interrupts, just masked ones.
@@ -339,55 +391,115 @@ sioprobe(dev)
disable_intr();
/*
- * Initialize the speed so that any junk in the THR or output fifo will
- * be transmitted in a known time. (There may be lots of junk after a
- * soft reboot, and output interrupts don't work right after a master
- * reset, at least for 16550s. (The speed is undefined after MR, but
- * MR empties the THR and the TSR so it's not clear why this matters)).
- * Enable output interrupts (only) and check the following:
+ * 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.
+ */
+ outb(IO_ICU1 + 1, 0xff);
+
+ /*
+ * 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) * 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.
+ */
+ 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.
+ */
+ 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((2 + 1) * 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.
+ */
+ 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.
*/
- 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); /* ensure IER is addressed */
- outb(iobase + com_mcr, MCR_IENABLE); /* open gate early */
- outb(iobase + com_ier, 0); /* ensure edge on next intr */
- outb(iobase + com_ier, IER_ETXRDY); /* generate interrupt */
- DELAY((16 + 1) * 9600 / 10); /* enough to drain 16 bytes */
- if ( inb(iobase + com_cfcr) != CFCR_8BITS
- || inb(iobase + com_ier) != IER_ETXRDY
- || inb(iobase + com_mcr) != MCR_IENABLE
-#ifndef COM_MULTIPORT /* XXX - need to do more to enable interrupts */
- || !isa_irq_pending(dev)
-#endif
- || (inb(iobase + com_iir) & IIR_IMASK) != IIR_TXRDY
- || isa_irq_pending(dev)
- || !(inb(iobase + com_iir) & IIR_NOPEND))
- result = 0;
+ failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS;
+ failures[1] = inb(iobase + com_ier) - IER_ETXRDY;
+ failures[2] = inb(iobase + com_mcr) - mcr_image;
+ if (idev->id_irq != 0)
+ failures[3] = isa_irq_pending(idev) ? 0 : 1;
+ failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY;
+ failures[5] = isa_irq_pending(idev) ? 1 : 0;
+ 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 set. It gates the OUT2 output of the UART to
+ * 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_mcr, MCR_IENABLE); /* dummy to avoid bus echo */
- if ( inb(iobase + com_ier) != 0
- || isa_irq_pending(dev)
- || !(inb(iobase + com_iir) & IIR_NOPEND))
- result = 0;
- if (result == 0)
- outb(iobase + com_mcr, 0);
+ outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */
+ failures[7] = inb(iobase + com_ier);
+ failures[8] = isa_irq_pending(idev) ? 1 : 0;
+ failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
+ outb(IO_ICU1 + 1, imen); /* XXX */
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);
}
@@ -403,9 +515,9 @@ sioattach(isdp)
iobase = isdp->id_iobase;
unit = isdp->id_unit;
- if (unit == comconsole)
- DELAY(1000); /* XXX */
- s = spltty();
+ com = malloc(sizeof *com, M_TTYS, M_NOWAIT);
+ if (com == NULL)
+ return (0);
/*
* sioprobe() has initialized the device registers as follows:
@@ -414,15 +526,13 @@ sioattach(isdp)
* 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.
+ * o mcr = MCR_IENABLE, or 0 if the port has a master port.
* Keeping MCR_DTR and MCR_RTS off might stop the external
* device from sending before we are ready.
*/
-
- com = &com_structs[unit];
+ bzero(com, sizeof *com);
com->cfcr_image = CFCR_8BITS;
- com->mcr_image = MCR_IENABLE;
- com->dtr_wait = 200;
+ com->dtr_wait = 3 * hz;
com->tx_fifo_size = 1;
com->iptr = com->ibuf = com->ibuf1;
com->ibufend = com->ibuf1 + RS_IBUFSIZE;
@@ -431,9 +541,30 @@ sioattach(isdp)
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;
- com->tp = &sio_tty[unit];
+
+ /*
+ * 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) {
+ 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);
@@ -470,10 +601,11 @@ sioattach(isdp)
break;
case FIFO_TRIGGER_14:
printf(" 16550A");
- if (COM_NOFIFO(isdp)) {
- printf(" fifo software disabled");
- } else {
+ if (COM_NOFIFO(isdp))
+ printf(" fifo disabled");
+ else {
com->hasfifo = TRUE;
+ com->ftl_init = FIFO_TRIGGER_14;
com->tx_fifo_size = 16;
}
break;
@@ -483,31 +615,36 @@ determined_type: ;
#ifdef COM_MULTIPORT
if (COM_ISMULTIPORT(isdp)) {
- struct isa_device *masterdev;
-
com->multiport = TRUE;
- printf(" (multiport)");
-
- /* set the master's common-interrupt-enable reg.,
- * as appropriate. YYY See your manual
- */
- /* enable only common interrupt for port */
- outb(com->modem_ctl_port, com->mcr_image = 0);
-
- masterdev = find_isadev(isa_devtab_tty, &siodriver,
- COM_MPMASTER(isdp));
- outb(masterdev->id_iobase + com_scr, 0x80);
- } else
- com->multiport = FALSE;
+ printf(" (multiport");
+ if (unit == COM_MPMASTER(isdp))
+ printf(" master");
+ printf(")");
+ }
#endif /* COM_MULTIPORT */
printf("\n");
#ifdef KGDB
if (kgdb_dev == makedev(commajor, unit)) {
- if (comconsole == unit)
+ if (unit == comconsole)
kgdb_dev = -1; /* can't debug over console port */
else {
- cominit(unit, kgdb_rate);
+ int divisor;
+
+ /*
+ * XXX now unfinished and broken. Need to do
+ * something more like a full open(). There's no
+ * suitable interrupt handler so don't enable device
+ * interrupts. Watch out for null tp's.
+ */
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ divisor = ttspeedtab(kgdb_rate, comspeedtab);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS);
+ outb(com->modem_status_port,
+ com->mcr_image |= MCR_DTR | MCR_RTS);
+
if (kgdb_debug_init) {
/*
* Print prefix of device name,
@@ -521,16 +658,11 @@ determined_type: ;
}
#endif
- /*
- * Need to reset baud rate, etc. of next print so reset comconsinit.
- */
- if (unit == comconsole)
- comconsinit = FALSE;
-
+ s = spltty();
com_addr(unit) = com;
splx(s);
if (!comwakeup_started) {
- comwakeup((caddr_t) NULL, 0);
+ comwakeup((caddr_t)NULL, 0);
comwakeup_started = TRUE;
}
return (1);
@@ -544,207 +676,127 @@ sioopen(dev, flag, mode, p)
int mode;
struct proc *p;
{
-#ifdef COM_BIDIR
- bool_t callout;
-#endif /* COM_BIDIR */
struct com_s *com;
- int error = 0;
+ int error;
Port_t iobase;
+ int mynor;
int s;
struct tty *tp;
int unit;
- unit = UNIT(dev);
+ mynor = minor(dev);
+ unit = MINOR_TO_UNIT(mynor);
if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL)
return (ENXIO);
-#ifdef COM_BIDIR
- /* if it's a callout device, and bidir not possible on that dev, die */
- callout = CALLOUT(dev);
- if (callout && !(com->bidir))
- return (ENXIO);
-#endif /* COM_BIDIR */
-
- tp = com->tp;
+ if (mynor & CONTROL_MASK)
+ return (0);
+ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]);
s = spltty();
-
-#ifdef COM_BIDIR
-
-bidir_open_top:
- /* if it's bidirectional, we've gotta deal with it... */
- if (com->bidir) {
- if (callout) {
- if (com->active_in) {
- /* it's busy. die */
- splx(s);
- return (EBUSY);
- } else {
- /* it's ours. lock it down, and set it up */
- com->active_out = TRUE;
+ /*
+ * 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((caddr_t)&com->dtr_wait, TTIPRI | PCATCH,
+ "siodtr", 0);
+ if (error != 0)
+ goto out;
+ }
+ 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) {
- /* it's busy, outgoing. wait, if possible */
if (flag & O_NONBLOCK) {
- /* can't wait; bail */
- splx(s);
- return (EBUSY);
- } else {
- /* wait for it... */
- error = tsleep((caddr_t)&com->active_out,
- TTIPRI|PCATCH,
- "siooth",
- 0);
- /* if there was an error, take off. */
- if (error != 0) {
- splx(s);
- return (error);
- }
- /* else take it from the top */
- goto bidir_open_top;
- }
- } else if (com->prev_modem_status & MSR_DCD
- || FAKE_DCD(unit)) {
- /* there's a carrier on the line; we win */
- com->active_in = TRUE;
- } else {
- /* there is no carrier on the line */
- if (flag & O_NONBLOCK) {
- /* can't wait; let it open */
- com->active_in = TRUE;
- } else {
- /* put DTR & RTS up */
- /* XXX - bring up RTS earlier? */
- commctl(com, MCR_DTR | MCR_RTS, DMSET);
- outb(com->iobase + com_ier, IER_EMSC);
-
- /* wait for it... */
- error = tsleep((caddr_t)&com->active_in,
- TTIPRI|PCATCH,
- "siodcd",
- 0);
-
- /* if not active, turn intrs and DTR off */
- if (!com->active) {
- outb(com->iobase + com_ier, 0);
- commctl(com, MCR_DTR, DMBIC);
- }
-
- /* if there was an error, take off. */
- if (error != 0) {
- splx(s);
- return (error);
- }
- /* else take it from the top */
- goto bidir_open_top;
+ error = EBUSY;
+ goto out;
}
+ error = tsleep((caddr_t)&com->active_out,
+ TTIPRI | PCATCH, "siobi", 0);
+ if (error != 0)
+ goto out;
+ goto open_top;
}
}
- }
-
- com->active = TRUE;
-#endif /* COM_BIDIR */
-
- tp->t_oproc = comstart;
- tp->t_param = comparam;
- tp->t_dev = dev;
- if (!(tp->t_state & TS_ISOPEN)) {
- tp->t_state |= TS_WOPEN;
- ttychars(tp);
- if (tp->t_ispeed == 0) {
- /*
- * We no longer use the flags from <sys/ttydefaults.h>
- * since those 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.
- */
- tp->t_iflag = 0;
- tp->t_oflag = 0;
-#ifdef COMCONSOLE
- if (unit == comconsole)
- tp->t_oflag = TTYDEF_OFLAG;
-#endif
- tp->t_cflag = CREAD | CS8 | HUPCL;
- tp->t_lflag = 0;
- tp->t_ispeed = tp->t_ospeed = comdefaultrate;
+ if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
+ error = EBUSY;
+ goto out;
}
-
+ } else {
/*
- * XXX the full state after a first open() needs to be
- * programmable and separate for callin and callout.
+ * 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.
*/
-#ifdef COM_BIDIR
- if (com->bidir) {
- if (callout)
- tp->t_cflag |= CLOCAL;
- else
- tp->t_cflag &= ~CLOCAL;
- }
-#endif
-
+ tp->t_oproc = comstart;
+ tp->t_param = comparam;
+ tp->t_dev = dev;
+ tp->t_termios = mynor & CALLOUT_MASK
+ ? com->it_out : com->it_in;
commctl(com, MCR_DTR | MCR_RTS, DMSET);
+ com->ftl_max = com->ftl_init;
+ ++com->wopeners;
error = comparam(tp, &tp->t_termios);
+ --com->wopeners;
if (error != 0)
goto out;
+ /*
+ * XXX we should goto open_top if comparam() slept.
+ */
ttsetwater(tp);
iobase = com->iobase;
if (com->hasfifo) {
- /* (re)enable and drain FIFO */
- outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER
- | FIFO_RCV_RST | FIFO_XMT_RST);
+ /* Drain fifo. */
+ outb(iobase + com_fifo,
+ FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST
+ | com->ftl);
DELAY(100);
}
disable_intr();
(void) inb(com->line_status_port);
(void) inb(com->data_port);
- com->last_modem_status =
- com->prev_modem_status = inb(com->modem_status_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);
enable_intr();
- if (com->prev_modem_status & MSR_DCD || FAKE_DCD(unit))
- tp->t_state |= TS_CARR_ON;
- } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
- splx(s);
- return (EBUSY);
- }
- while (!(flag & O_NONBLOCK) && !(tp->t_cflag & CLOCAL)
-#ifdef COM_BIDIR
- /* We went through a lot of trouble to open it,
- * but it's certain we have a carrier now, so
- * don't spend any time on it now.
+ /*
+ * 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".
*/
- && !(com->bidir)
-#endif /* COM_BIDIR */
- && !(tp->t_state & TS_CARR_ON)) {
- tp->t_state |= TS_WOPEN;
- error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH,
- ttopen, 0);
+ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK)
+ (*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);
+ --com->wopeners;
if (error != 0)
- break;
+ goto out;
+ goto open_top;
}
+ error = (*linesw[tp->t_line].l_open)(dev, tp, 0);
+ if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
+ com->active_out = TRUE;
out:
splx(s);
- if (error == 0)
- error = (*linesw[tp->t_line].l_open)(dev, tp, 0);
-
-#ifdef COM_BIDIR
- /* wakeup sleepers */
- wakeup((caddr_t) &com->active_in);
-#endif /* COM_BIDIR */
-
- /*
- * XXX - the next step was once not done, so interrupts, DTR and RTS
- * remained hot if the process was killed while it was sleeping
- * waiting for carrier. Now there is the opposite problem. If several
- * processes are sleeping waiting for carrier on the same line and one
- * is killed, interrupts are turned off so the other processes will
- * never see the carrier rise.
- */
- if (error != 0 && !(tp->t_state & TS_ISOPEN))
+ if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0)
comhardclose(com);
- tp->t_state &= ~TS_WOPEN;
-
return (error);
}
@@ -757,13 +809,21 @@ sioclose(dev, flag, mode, p)
struct proc *p;
{
struct com_s *com;
+ int mynor;
+ int s;
struct tty *tp;
- com = com_addr(UNIT(dev));
+ 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);
+ siostop(tp, FREAD | FWRITE);
comhardclose(com);
ttyclose(tp);
+ splx(s);
return (0);
}
@@ -776,10 +836,13 @@ comhardclose(com)
struct tty *tp;
int unit;
- s = spltty();
+ unit = DEV_TO_UNIT(com->tp->t_dev);
iobase = com->iobase;
+ s = spltty();
+#ifdef TIOCTIMESTAMP
+ com->do_timestamp = 0;
+#endif
outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
- unit = com - &com_structs[0];
#ifdef KGDB
/* do not disable interrupts or hang up if debugging */
if (kgdb_dev != makedev(commajor, unit))
@@ -787,29 +850,29 @@ comhardclose(com)
{
outb(iobase + com_ier, 0);
tp = com->tp;
- if (tp->t_cflag & HUPCL || tp->t_state & TS_WOPEN
- || !(com->prev_modem_status & MSR_DCD) && !FAKE_DCD(unit)
+ 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
+ && !(com->prev_modem_status & MSR_DCD)
+ && !(com->it_in.c_cflag & CLOCAL)
|| !(tp->t_state & TS_ISOPEN)) {
commctl(com, MCR_RTS, DMSET);
- if (com->dtr_wait != 0)
- /*
- * Uninterruptible sleep since we want to
- * wait a fixed time.
- * XXX - delay in open() (if necessary),
- * not here (always).
- */
- tsleep((caddr_t)&com->dtr_wait, TTIPRI,
- "sioclose", com->dtr_wait);
+ if (com->dtr_wait != 0) {
+ timeout(siodtrwakeup, (caddr_t)com,
+ com->dtr_wait);
+ com->state |= CS_DTR_OFF;
+ }
}
}
-
-#ifdef COM_BIDIR
- com->active = com->active_in = com->active_out = FALSE;
-
- /* wakeup sleepers who are waiting for out to finish */
- wakeup((caddr_t) &com->active_out);
-#endif /* COM_BIDIR */
-
+ com->active_out = FALSE;
+ wakeup((caddr_t)&com->active_out);
+ wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */
splx(s);
}
@@ -819,8 +882,13 @@ sioread(dev, uio, flag)
struct uio *uio;
int flag;
{
- struct tty *tp = com_addr(UNIT(dev))->tp;
+ int mynor;
+ struct tty *tp;
+ mynor = minor(dev);
+ if (mynor & CONTROL_MASK)
+ return (ENODEV);
+ tp = com_addr(MINOR_TO_UNIT(mynor))->tp;
return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
}
@@ -830,9 +898,15 @@ siowrite(dev, uio, flag)
struct uio *uio;
int flag;
{
- int unit = UNIT(dev);
- struct tty *tp = com_addr(unit)->tp;
+ int mynor;
+ struct tty *tp;
+ int unit;
+ mynor = minor(dev);
+ if (mynor & CONTROL_MASK)
+ return (ENODEV);
+ unit = MINOR_TO_UNIT(mynor);
+ 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
@@ -844,17 +918,37 @@ siowrite(dev, uio, flag)
return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
}
+static void
+siodtrwakeup(chan, ticks)
+ caddr_t chan;
+ int ticks;
+{
+ struct com_s *com;
+
+ com = (struct com_s *)chan;
+ com->state &= ~CS_DTR_OFF;
+ wakeup((caddr_t)&com->dtr_wait);
+}
+
+#ifdef TIOCTIMESTAMP
+/* Interrupt routine for timekeeping purposes */
+void
+siointrts(unit)
+ int unit;
+{
+ microtime(&intr_timestamp);
+ siointr(unit);
+}
+#endif
+
void
siointr(unit)
int unit;
{
- struct com_s *com;
-
#ifndef COM_MULTIPORT
- com = com_addr(unit);
- if (com != NULL)
- comintr1(com);
+ siointr1(com_addr(unit));
#else /* COM_MULTIPORT */
+ struct com_s *com;
bool_t possibly_more_intrs;
/*
@@ -869,29 +963,30 @@ siointr(unit)
for (unit = 0; unit < NSIO; ++unit) {
com = com_addr(unit);
if (com != NULL
- && !(inb(com->int_id_port) & IIR_NOPEND)) {
- /*
- * XXX call comintr1() instead of here from
- * comwakeup(). The interrupt edge problem
- * only exists for real interrupts.
- */
- comintr1(com);
+ && (inb(com->int_id_port) & IIR_IMASK)
+ != IIR_NOPEND) {
+ siointr1(com);
possibly_more_intrs = TRUE;
}
}
} while (possibly_more_intrs);
-#endif /* COM_MULTIPORT */
+#endif /* COM_MULTIPORT */
}
static void
-comintr1(com)
+siointr1(com)
struct com_s *com;
{
- u_char line_status;
- u_char modem_status;
- u_char *ioptr;
- u_char recv_data;
-
+ u_char line_status;
+ u_char modem_status;
+ u_char *ioptr;
+ u_char recv_data;
+
+#ifdef TIOCTIMESTAMP
+ if (com->do_timestamp)
+ /* XXX a little bloat here... */
+ com->timestamp = intr_timestamp;
+#endif
while (TRUE) {
line_status = inb(com->line_status_port);
@@ -903,6 +998,8 @@ comintr1(com)
else
recv_data = inb(com->data_port);
++com->bytes_in;
+ if (com->hotchar != 0 && recv_data == com->hotchar)
+ setsofttty();
#ifdef KGDB
/* trap into kgdb? (XXX - needs testing and optim) */
if (recv_data == FRAME_END
@@ -981,7 +1078,7 @@ if (com->iptr - com->ibuf == 8)
while (--ocount != 0);
} else {
outb(com->data_port, *ioptr++);
- ++com->bytes_out;
+ ++com->bytes_out;
}
com->optr = ioptr;
if (ioptr >= com->obufend) {
@@ -994,7 +1091,7 @@ if (com->iptr - com->ibuf == 8)
/* finished? */
#ifndef COM_MULTIPORT
- if (inb(com->int_id_port) & IIR_NOPEND)
+ if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
#endif /* COM_MULTIPORT */
return;
}
@@ -1002,9 +1099,9 @@ if (com->iptr - com->ibuf == 8)
static int
tiocm_xxx2mcr(tiocm_xxx)
- int tiocm_xxx;
+ int tiocm_xxx;
{
- int mcr;
+ int mcr;
mcr = 0;
if (tiocm_xxx & TIOCM_DTR)
@@ -1027,26 +1124,75 @@ sioioctl(dev, cmd, data, flag, p)
Port_t iobase;
int mcr;
int msr;
+ int mynor;
int s;
int tiocm_xxx;
struct tty *tp;
- com = com_addr(UNIT(dev));
+ mynor = minor(dev);
+ com = com_addr(MINOR_TO_UNIT(mynor));
+ 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)
+ 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);
+ default:
+ return (ENOTTY);
+ }
+ }
tp = com->tp;
+ 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);
if (error >= 0)
return (error);
error = ttioctl(tp, cmd, data, flag);
-
-#ifdef COM_BIDIR
- /* XXX: plug security hole while sticky bits not yet implemented */
- if (com->bidir && com->active_in && p->p_ucred->cr_uid != 0)
- tp->t_cflag &= ~CLOCAL;
-#endif
-
if (error >= 0)
return (error);
-
iobase = com->iobase;
s = spltty();
switch (cmd) {
@@ -1094,57 +1240,24 @@ sioioctl(dev, cmd, data, flag, p)
tiocm_xxx |= TIOCM_RI;
*(int *)data = tiocm_xxx;
break;
-#ifdef COM_BIDIR
- case TIOCMSBIDIR:
- /* must be root to set bidir. capability */
- error = suser(p->p_ucred, &p->p_acflag);
- if (error != 0) {
- splx(s);
- return(EPERM);
- }
-
- /* if it's the console, can't do it (XXX why?) */
- if (UNIT(dev) == comconsole) {
- splx(s);
- return(ENOTTY);
- }
-
-#if 0
- /* XXX - can't do the next, for obvious reasons...
- * but there are problems to be looked at...
- */
- /* if the port is active, don't do it */
- if (com->active) {
- splx(s);
- return(EBUSY);
- }
-#endif
-
- com->bidir = *(int *)data;
- break;
- case TIOCMGBIDIR:
- *(int *)data = com->bidir;
- break;
-#endif /* COM_BIDIR */
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(EPERM);
+ return (EPERM);
}
-
- /* if it's the console, can't do it (XXX why?) */
- if (UNIT(dev) == comconsole) {
- splx(s);
- return(ENOTTY);
- }
-
- com->dtr_wait = *(int *)data;
+ com->dtr_wait = *(int *)data * 100 / hz;
break;
case TIOCMGDTRWAIT:
*(int *)data = com->dtr_wait;
break;
+#ifdef TIOCTIMESTAMP
+ case TIOCTIMESTAMP:
+ com->do_timestamp = TRUE;
+ *(struct timeval *)data = com->timestamp;
+ break;
+#endif
default:
splx(s);
return (ENOTTY);
@@ -1165,7 +1278,7 @@ comflush(com)
com_events -= LOTS_OF_EVENTS;
com->state &= ~(CS_ODONE | CS_BUSY);
enable_intr();
- rbp = &com->tp->t_out;
+ rbp = com->tp->t_out;
rbp->rb_hd += com->ocount;
rbp->rb_hd = RB_ROLLOVER(rbp, rbp->rb_hd);
com->ocount = 0;
@@ -1175,24 +1288,14 @@ comflush(com)
void
siopoll()
{
- static bool_t awake = FALSE;
- int s;
int unit;
if (com_events == 0)
return;
- disable_intr();
- if (awake) {
- enable_intr();
- return;
- }
- awake = TRUE;
- enable_intr();
- s = spltty();
repeat:
for (unit = 0; unit < NSIO; ++unit) {
- u_char *buf;
- struct com_s *com;
+ u_char *buf;
+ struct com_s *com;
u_char *ibuf;
int incc;
struct tty *tp;
@@ -1201,12 +1304,25 @@ repeat:
if (com == NULL)
continue;
tp = com->tp;
+ if (tp == NULL)
+ 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 {
+ /*
+ * Prepare to reduce input latency for packet
+ * discplines with a end of packet character.
+ * XXX should be elsewhere.
+ */
+ if (tp->t_line == SLIPDISC)
+ com->hotchar = 0xc0;
+ else if (tp->t_line == PPPDISC)
+ com->hotchar = 0x7e;
+ else
+ com->hotchar = 0;
buf = ibuf;
disable_intr();
incc = com->iptr - buf;
@@ -1224,7 +1340,15 @@ repeat:
* of input, so enable RTS if it is now disabled and
* there is room in the high-level buffer.
*/
- if (!(com->mcr_image & MCR_RTS)
+ /*
+ * 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.
+ */
+ if ((com->state & CS_RTS_IFLOW)
+ && !(com->mcr_image & MCR_RTS)
&& !(tp->t_state & TS_RTS_IFLOW))
outb(com->modem_ctl_port,
com->mcr_image |= MCR_RTS);
@@ -1242,35 +1366,45 @@ repeat:
com_events -= LOTS_OF_EVENTS;
com->state &= ~CS_CHECKMSR;
enable_intr();
- if (delta_modem_status & MSR_DCD && !FAKE_DCD(unit)) {
- if (com->prev_modem_status & MSR_DCD) {
- (*linesw[tp->t_line].l_modem)(tp, 1);
-#ifdef COM_BIDIR
- wakeup((caddr_t) &com->active_in);
-#endif /* COM_BIDIR */
- } else
- (*linesw[tp->t_line].l_modem)(tp, 0);
- }
+ if (delta_modem_status & MSR_DCD)
+ (*linesw[tp->t_line].l_modem)
+ (tp, com->prev_modem_status & MSR_DCD);
}
/* XXX */
if (TRUE) {
- u_int delta;
- int errnum;
- u_long total;
+ u_int delta;
+ int errnum;
+ u_long total;
for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
- disable_intr();
+ disable_intr();
delta = com->delta_error_counts[errnum];
com->delta_error_counts[errnum] = 0;
- enable_intr();
- if (delta != 0) {
- total =
- com->error_counts[errnum] += delta;
+ enable_intr();
+ if (delta == 0 || !(tp->t_state & TS_ISOPEN))
+ continue;
+ total = com->error_counts[errnum] += delta;
log(LOG_WARNING,
"sio%d: %u more %s%s (total %lu)\n",
unit, delta, error_desc[errnum],
delta == 1 ? "" : "s", total);
+ if (errnum == CE_OVERRUN && com->hasfifo
+ && com->ftl > FIFO_TRIGGER_1) {
+ static u_char ftl_in_bytes[] =
+ { 1, 4, 8, 14, };
+
+ com->ftl_init = FIFO_TRIGGER_8;
+#define FIFO_TRIGGER_DELTA FIFO_TRIGGER_4
+ com->ftl_max =
+ com->ftl -= FIFO_TRIGGER_DELTA;
+ outb(com->iobase + com_fifo,
+ FIFO_ENABLE | com->ftl);
+ log(LOG_WARNING,
+ "sio%d: reduced fifo trigger level to %d\n",
+ unit,
+ ftl_in_bytes[com->ftl
+ / FIFO_TRIGGER_DELTA]);
}
}
}
@@ -1285,7 +1419,7 @@ repeat:
if (incc <= 0 || !(tp->t_state & TS_ISOPEN))
continue;
if (com->state & CS_RTS_IFLOW
- && RB_LEN(&tp->t_raw) + incc >= RB_I_HIGH_WATER
+ && RB_LEN(tp->t_raw) + incc >= RB_I_HIGH_WATER
&& !(tp->t_state & TS_RTS_IFLOW)
/*
* XXX - need RTS flow control for all line disciplines.
@@ -1312,7 +1446,7 @@ repeat:
tk_rawcc += incc;
tp->t_rawcc += incc;
com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
- += incc - rb_write(&tp->t_raw, (char *) buf,
+ += incc - rb_write(tp->t_raw, (char *) buf,
incc);
ttwakeup(tp);
if (tp->t_state & TS_TTSTOP
@@ -1348,8 +1482,6 @@ repeat:
}
if (com_events >= LOTS_OF_EVENTS)
goto repeat;
- splx(s);
- awake = FALSE;
}
static int
@@ -1370,11 +1502,11 @@ comparam(tp, t)
divisor = ttspeedtab(t->c_ospeed, comspeedtab);
if (t->c_ispeed == 0)
t->c_ispeed = t->c_ospeed;
- if (divisor < 0 || t->c_ispeed != t->c_ospeed)
+ 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 */
- unit = UNIT(tp->t_dev);
+ unit = DEV_TO_UNIT(tp->t_dev);
com = com_addr(unit);
iobase = com->iobase;
s = spltty();
@@ -1405,12 +1537,28 @@ comparam(tp, t)
if (cflag & CSTOPB)
cfcr |= CFCR_STOPB;
+ if (com->hasfifo) {
+ /*
+ * 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->ftl = t->c_ospeed <= 4800
+ ? FIFO_TRIGGER_1 : FIFO_TRIGGER_14;
+ if (com->ftl > com->ftl_max)
+ com->ftl = com->ftl_max;
+ outb(iobase + com_fifo, FIFO_ENABLE | com->ftl);
+ }
+
/*
* 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 siointr() might lose some receiver
+ * line status port outside of siointr1() might lose some receiver
* error bits, but that is acceptable here.
*/
disable_intr();
@@ -1419,8 +1567,8 @@ retry:
enable_intr();
while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
!= (LSR_TSRE | LSR_TXRDY)) {
- error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH,
- "sioparam", 1);
+ error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH,
+ "siotx", hz / 100);
if (error != 0 && error != EAGAIN) {
if (!(tp->t_state & TS_TTSTOP)) {
disable_intr();
@@ -1441,7 +1589,7 @@ retry:
* sufficient, for similar reasons.
*/
if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
- != (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY))
goto retry;
if (divisor != 0) {
@@ -1460,7 +1608,7 @@ retry:
/*
* Set up state to handle output flow control.
* XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
- * Now has 16+ msec latency, while CTS flow has 50- usec latency.
+ * Now has 10+ msec latency, while CTS flow has 50- usec latency.
*/
com->state &= ~CS_CTS_OFLOW;
com->state |= CS_ODEVREADY;
@@ -1471,15 +1619,12 @@ retry:
}
/*
- * Recover from fiddling with CS_TTGO. We used to call siointr()
+ * Recover from fiddling with CS_TTGO. We used to call siointr1()
* unconditionally, but that defeated the careful discarding of
* stale input in sioopen().
- *
- * XXX sioopen() is not careful waiting for carrier for the callout
- * case.
*/
if (com->state >= (CS_BUSY | CS_TTGO))
- comintr1(com);
+ siointr1(com);
enable_intr();
splx(s);
@@ -1494,7 +1639,7 @@ comstart(tp)
int s;
int unit;
- unit = UNIT(tp->t_dev);
+ unit = DEV_TO_UNIT(tp->t_dev);
com = com_addr(unit);
s = spltty();
disable_intr();
@@ -1506,35 +1651,31 @@ comstart(tp)
if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW)
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.
+ */
if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater)
outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
}
enable_intr();
if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
goto out;
- if (RB_LEN(&tp->t_out) <= tp->t_lowat) {
- if (tp->t_state & TS_ASLEEP) {
- tp->t_state &= ~TS_ASLEEP;
- wakeup((caddr_t)&tp->t_out);
- }
- if (tp->t_wsel) {
- selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
- tp->t_wsel = 0;
- tp->t_state &= ~TS_WCOLL;
- }
- }
+ if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel)
+ ttwwakeup(tp);
if (com->ocount != 0) {
disable_intr();
- comintr1(com);
+ siointr1(com);
enable_intr();
- } else if (RB_LEN(&tp->t_out) != 0) {
+ } else if (RB_LEN(tp->t_out) != 0) {
tp->t_state |= TS_BUSY;
- com->ocount = RB_CONTIGGET(&tp->t_out);
+ com->ocount = RB_CONTIGGET(tp->t_out);
disable_intr();
- com->obufend = (com->optr = (u_char *) tp->t_out.rb_hd)
+ com->obufend = (com->optr = (u_char *)tp->t_out->rb_hd)
+ com->ocount;
com->state |= CS_BUSY;
- comintr1(com); /* fake interrupt to start output */
+ siointr1(com); /* fake interrupt to start output */
enable_intr();
}
out:
@@ -1548,7 +1689,7 @@ siostop(tp, rw)
{
struct com_s *com;
- com = com_addr(UNIT(tp->t_dev));
+ com = com_addr(DEV_TO_UNIT(tp->t_dev));
if (rw & FWRITE)
comflush(com);
disable_intr();
@@ -1569,11 +1710,9 @@ sioselect(dev, rw, p)
int rw;
struct proc *p;
{
-#ifdef COM_BIDIR
- return ttselect(dev & ~COM_CALLOUTMASK, rw, p);
-#else
- return ttselect(dev, rw, p);
-#endif
+ if (minor(dev) & CONTROL_MASK)
+ return (ENODEV);
+ return (ttselect(dev & ~MINOR_MAGIC_MASK, rw, p));
}
static void
@@ -1600,15 +1739,20 @@ commctl(com, bits, how)
static void
comwakeup(chan, ticks)
- caddr_t chan;
- int ticks;
+ caddr_t chan;
+ int ticks;
{
- int unit;
+ int unit;
+
+ timeout(comwakeup, (caddr_t)NULL, hz / 100);
+
+ if (com_events != 0) {
+ int s;
- timeout(comwakeup, (caddr_t) NULL, hz / 100);
- if (com_events != 0)
- /* schedule siopoll() to run when the cpl allows */
- setsofttty();
+ s = splsofttty();
+ siopoll();
+ splx(s);
+ }
/* recover from lost output interrupts */
for (unit = 0; unit < NSIO; ++unit) {
@@ -1617,27 +1761,97 @@ comwakeup(chan, ticks)
com = com_addr(unit);
if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) {
disable_intr();
- comintr1(com);
+ siointr1(com);
enable_intr();
}
}
- return;
-}
-
-void
-softsio1()
-{
- siopoll();
}
/*
* Following are all routines needed for SIO to act as console
- * XXX - not tested in this version
- * XXX - i386/cons.c only knows about the com driver (NCOM and not NSIO)
- * XXX - check that the corresponding serial interrupts are never enabled
*/
#include "i386/i386/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;
+
+ /*
+ * 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);
+ outb(iobase + com_mcr, 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 oscllations 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;
@@ -1645,14 +1859,14 @@ siocnprobe(cp)
int unit;
/* locate the major number */
+ /* XXX - should be elsewhere since KGDB uses it */
for (commajor = 0; commajor < nchrdev; commajor++)
if (cdevsw[commajor].d_open == sioopen)
break;
/* XXX: ick */
- unit = UNIT(CONUNIT);
- com_addr(unit) = &com_structs[unit];
- com_addr(unit)->iobase = CONADDR;
+ unit = DEV_TO_UNIT(CONUNIT);
+ siocniobase = CONADDR;
/* make sure hardware exists? XXX */
@@ -1669,43 +1883,11 @@ void
siocninit(cp)
struct consdev *cp;
{
- int unit;
-
- unit = UNIT(cp->cn_dev);
- cominit(unit, comdefaultrate);
- comconsole = unit;
- comconsinit = TRUE;
-}
-
-static void
-cominit(unit, rate)
- int unit;
- int rate;
-{
- Port_t iobase;
- int s;
-
- iobase = com_addr(unit)->iobase;
- s = splhigh();
- outb(iobase + com_cfcr, CFCR_DLAB);
- rate = ttspeedtab(comdefaultrate, comspeedtab);
- outb(iobase + com_dlbl, rate & 0xFF);
- outb(iobase + com_dlbh, rate >> 8);
- outb(iobase + com_cfcr, CFCR_8BITS);
- outb(iobase + com_fifo,
- FIFO_ENABLE | FIFO_TRIGGER | FIFO_RCV_RST | FIFO_XMT_RST);
- DELAY(100);
- (void) inb(iobase + com_lsr);
- (void) inb(iobase + com_data);
- (void) inb(iobase + com_msr);
-
/*
- * XXX - fishy to enable interrupts and then poll.
- * It shouldn't be necessary to ready the iir.
+ * XXX can delete more comconsole stuff now that i/o routines are
+ * fairly reentrant.
*/
- outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC);
- (void) inb(iobase + com_iir);
- splx(s);
+ comconsole = DEV_TO_UNIT(cp->cn_dev);
}
int
@@ -1715,13 +1897,15 @@ siocngetc(dev)
int c;
Port_t iobase;
int s;
+ struct siocnstate sp;
- iobase = com_addr(UNIT(dev))->iobase;
- s = splhigh();
+ iobase = siocniobase;
+ s = spltty();
+ siocnopen(&sp);
while (!(inb(iobase + com_lsr) & LSR_RXRDY))
;
c = inb(iobase + com_data);
- (void) inb(iobase + com_iir);
+ siocnclose(&sp);
splx(s);
return (c);
}
@@ -1731,30 +1915,14 @@ siocnputc(dev, c)
dev_t dev;
int c;
{
- Port_t iobase;
int s;
- int timo;
+ struct siocnstate sp;
- iobase = com_addr(UNIT(dev))->iobase;
- s = splhigh();
-#ifdef KGDB
- if (dev != kgdb_dev)
-#endif
- if (!comconsinit) {
- cominit(UNIT(dev), comdefaultrate);
- comconsinit = TRUE;
- }
- /* wait for any pending transmission to finish */
- timo = 50000;
- while (!(inb(iobase + com_lsr) & LSR_TXRDY) && --timo)
- ;
- outb(iobase + com_data, c);
- /* wait for this transmission to complete */
- timo = 1500000;
- while (!(inb(iobase + com_lsr) & LSR_TXRDY) && --timo)
- ;
- /* clear any interrupts generated by this transmission */
- (void) inb(iobase + com_iir);
+ s = spltty();
+ siocnopen(&sp);
+ siocntxwait();
+ outb(siocniobase + com_data, c);
+ siocnclose(&sp);
splx(s);
}
diff --git a/sys/i386/isa/sound/CHANGELOG b/sys/i386/isa/sound/CHANGELOG
new file mode 100644
index 000000000000..6a9bef153ce3
--- /dev/null
+++ b/sys/i386/isa/sound/CHANGELOG
@@ -0,0 +1,75 @@
+Changelog for version 2.5
+-------------------------
+
+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 usefull 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/i386/isa/sound/RELNOTES.Linux b/sys/i386/isa/sound/RELNOTES.Linux
index 4082f4e255b0..ea57d0a950c4 100644
--- a/sys/i386/isa/sound/RELNOTES.Linux
+++ b/sys/i386/isa/sound/RELNOTES.Linux
@@ -1,67 +1,74 @@
-Release notes for the Linux Sound Driver 1.99.9
------------------------------------------------
-
-******** THIS IS A BETA TEST RELEASE ********
-which means that there can be some untested things. In theory
-there is a risk that this driver causes some trouble to your system.
-You should not use this driver before backing up your disks.
-
-
-
-
-Welcome to use the Gravis UltraSound driver for Linux. This
-driver still supports the same cards than version 1.0c
-(SoundBlaster, SB Pro, Pro Audio Spectrum 16 and AdLib).
-In addition there is rather limited support for MPU-401
+Release notes for the Linux Sound Driver 2.5
+--------------------------------------------
+There is also a version called 2.5-beta floating around the net. This
+version contains some fixes after it. Mainly to the SB and GUS code.
+
+CAUTION! The SVR4.2 port has not been tested much. Backup your system
+ carefully before trying it.
+
+This is mainly a bug fix release. There are couple of new things such as
+linear volume mode for GUS and MIDI recording for SB 2.0 and SB Pro.
+Also this version supports the mixer of GUS v3.7. (Support for GUS MAX and
+the 16-bit daughtercard is coming sooner or later).
+
+NOTE! The sound driver is a part of the Linux kernel distribution also.
+ Check that your kernel doesn't have more recent version than this
+ when installing a separately distributed sound driver. The
+ version number of this driver is defined in the makefile.
+
+This version contains a driver for the SB16 also.
+The SB16 driver requires separate DMA channels for the 8 and 16 bit
+modes. There should be a way to share the 8 bit DMA channels between
+these modes but this feature is not supported yet.
+The SB16 DSP support is by Joerg Schubert (jsb@sth.ruhr-uni-bochum.de).
+
+The SB16 driver has also the Midi input capability even at the same
+time with the /dev/dsp. Also the WaveBlaster daughter board is supported.
+No support for the ASP chip yet (the ASP chip can be installed but it's
+not used by 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 $OS/Readme.
+
+This version supports the following soundcards:
+GUS, SoundBlaster, SB Pro, SB16, Pro Audio Spectrum 16 and AdLib.
+In addition there is rather limited support for MPU-401.
(and compatible) midi cards. Also the OPL-3 synthesizer
-of the SB Pro and PAS16 cards is now supported in the 4 OP
-modes.
Most of the features of the /dev/sequencer device file are
available just for GUS owners.
-The SoundBlaster 16 and SB 16 ASP cards are not supported.
-They could work in mono mode with speeds < 22 kHz.
-The OPL-3 chicp of the SB 16 should work (without problems?).
-Is there anybody willing to implement the SB 16 support
-(have the SB 16 and the SDK for it)?
-
-
-This is the first version of the driver which has almost
-all of the features which I have planned to include into
-version 2.0. Some features are still missing and some ones
-doesn't work.
-
NOTE! There are separate driver for CD-ROMS supported by
some soundcards. The driver for CDU31A (Fusion 16) is
called cdu31a-0.6.diff.z. It will be contained in the
Linux version 0.99.12. The driver for the CD-ROM of SB Pro
is sbpcd0.4.tar.gz (these were the latest versions when I wrote
this). These files should be at least at sunsite.unc.edu.
- As far as I know, there is no driver for the SCSI interface of PAS16
- (yet).
+ Also the SCSI interface of the PAS16 should be supported by
+ Linux 0.99.13k and later.
There is also a driver for joystick. Look for file joystick-0.5.tar.gz
(sunsite).
- Since this driver is a sound driver, it will not contain support
- for SCSI/CD-ROM/Joystick -devices.
Compatibility with the earlier versions
---------------------------------------
-This is just like the version 1.99.7/1.99.8. There is just some minor
-enhancements. Most of them are portability fixes. If you are porting
-this driver to any OS, please look at the 386bsd/os.h. There is some
-new macros and some macros have more parameters. In addition this file
-contains some usefull comments.
+In this version the ultrasound.h no longer includes the sys/soundcard.h
+You have to change the gmod.c of the snd-util-2.0 package and to add an
+include for it.
-**** There is some ISC and 386bsd stuff in this driver. Please stay away ****
-This stuff is here just because I want to be in sync with the porters. These
-ports don't work yet.
+IMPORTANT!!!!!!!!!!!!!!!!!!!!!!
+
+This version is not binary or source compatible with the version 1.0c.
The ioctl() interface has changed completely since version 1.0c. All
programs using this driver must be at least recompiled.
-The snd-util-1.99.6 package contains some utilities for this version.
+The snd-util-2.0 package contains some utilities for this version.
The version 1.0c and earlier used a 'nonportable' ioctl calling scheme
where the input argument was passed by value and the output value was
@@ -78,59 +85,46 @@ After version 1.99.0 this must be done as the following:
If you have an application written for the version 1.0, you should search
for the strings SNDCTL_ and SOUND_ and to check the parameters.
+The following ioctl calls have changed:
+
+ SNDCTL_SEQ_GETOUTCOUNT
+ SNDCTL_SEQ_GETINCOUNT
+ SNDCTL_SEQ_TESTMIDI
+ SNDCTL_DSP_SPEED
+ SNDCTL_DSP_STEREO
+ SNDCTL_DSP_GETBLKSIZE
+ SNDCTL_DSP_SAMPLESIZE
+ SOUND_PCM_WRITE_CHANNELS
+ SOUND_PCM_WRITE_FILTER
+ SOUND_PCM_READ_RATE
+ SOUND_PCM_READ_CHANNELS
+ SOUND_PCM_READ_BITS
+ SOUND_PCM_READ_FILTER
+ SOUND_PCM_WRITE_BITS
+ SOUND_PCM_WRITE_RATE
+ SOUND_MIXER_READ_* (several ones)
+ SOUND_MIXER_WRITE_* (several ones)
Since the this version will support more than one synthesizer devices
at the same time, the ioctl(SNDCTL_FM_LOAD_INSTR) is obsolete. In addition
there is some new fields which must be initialized. Look at the sbiset.c in
-the snd-util-1.99.6 package for further info.
-
-The GUS patch format has changed since the version 1.99.3. You have to
-use latest versions of the programs in the sound/gustest directory. In
-addition the version 0.4g of the Adagio package supports this format.
-
-New features
-------------
-
-There is also some changes which make this version more usable than
-the version 1.0c.
-
-- /dev/dsp and /dev/audio
-
-The DMA buffering is now little bit more intelligent than earlier. The
-buffer size is selected run-time so that a buffer holds data for 0.5 to
-1.0 seconds of recording or playback. This makes recording more comfortable
-than with version 1.0. With the previous version there was sometimes more
-than 10 seconds of delay before the driver returned the first input byte.
-
-There is also support for more than one digitized voice devices. The device
-files /dev/dsp1 and /dev/audio1 (minor 19 and 20) are available with PAS16.
-The /dev/dsp (/dev/audio) is connected to the PCM circuit of the PAS16 itself
-and the /dev/dsp1 (/dev/audio1) to the SB emulation of PAS16 card. Two
-dsp/audio devices are available also if you have combination of SB and GUS.
-With GUS and PAS16 you will have even three dsp/audio devices. These devices
-can be used independently and can be active at the same time (3 channels
-at the same time propably don't work).
+the snd-util-2.0 package for further info.
-The dsp/audio support of PAS16 should be much cleaner now since the
-constant clicking sound between the DMA blocks (about once per second) has
-been eliminated.
+This version is almost 100% compatible with the alpha test version (1.99.9). The
+difference is in the installation procedure.
-The stereo playback of GUS doesn't work perfectly. There is lot of
-clicking in the output.
+Using this driver with other operating systems than Linux
+---------------------------------------------------------
-- /dev/mixer
+This package contains just the Linux version. The version 2.3
+for SCO is available at nic.funet.fi:pub/OS/Linux/ALPHA/sound.
+The version 2.3 doesn't work well with xxxxxBSD. Use the version
+2.3 for them.
-No changes.
-
-There is no mixer for the GUS yet.
-
-- /dev/sequencer
-
-This part has the most changes. Mostly to support the rich
-features of the Gravis UltraSound. There is also the support
-for the OPL-3 synthesizer chip.
+/dev/sndstat
+------------
-- /dev/sndstat
+The /dev/sndstat is now available in the SCO and BSD versions also.
This is a new devicefile for debugging purposes. A better place for
it is in the /proc -directory but I was just too lazy to implement it
@@ -139,11 +133,13 @@ info about the current configuration (see the example below). If you
send me a error/problem report, please include a printout from this
device to your message (cat /dev/sndstat).
+Note! This device file is currently present only in the Linux version
+ of this driver.
+
------ cut here --- cat /dev/sndstat example --------
Sound Driver:1.99.7 (Fri Jul 9 17:01:47 GMT 1993 root@lucifer.savolai.fi)
Config options: 0x00000d4b
-Major number: 14
HW config:
Type 4: Gravis Ultrasound at 0x210 irq 15 drq 6
Type 3: ProAudioSpectrum at 0x388 irq 10 drq 3
@@ -166,12 +162,15 @@ Midi devices:
Mixer(s) installed
------ cut here ---- End of Example -----------
+Known bugs/limitations
+----------------------
-Known bugs
-----------
-
-- There was clicking during stereo playback to /dev/dsp with GUS.
- * Fixed in 1.99.9 *
+- High speed recording of long audio samples (>20 second) to disk
+ is not possible. Everything works until next sync() which delays the
+ recording process too much. A delay longer than 0.1 to 0.3 seconds is
+ too much.
+- The SB16 driver sometimes swaps the left and right channels together.
+- Midi input doesn't work with SB and SB Pro (SB16 works).
- It's not possible to open /dev/dsp (or /dev/audio) while the
/dev/sequencer is open for output and GUS is the only soundcard
installed. It's possible if /dev/dsp is opened before /dev/sequencer
@@ -180,12 +179,77 @@ Known bugs
- MPU-401 driver hangs the computer on boot if there is no MPU-401 installed.
It uses by default the I/O port 0x330 whic is used by Adaptec 1542 SCSI
adapter.
+- There are some problems in midi input with MPU-401 and the SB16 midi
+ (MPU-401 emulation). This makes it impossible to read long sysex dumps
+ using these devices.
- The /dev/sequencer playback to GUS sounds sometimes rather weird. Hitting
^C and playing again should solve this problem. This is propably caused by
- incompatibilities between GUS and certain VLB motherboards. Try to avoid
+ incompatibilities between GUS and certain VLB motherboards (like mine).
+ Try to avoid
switching between VTs while patches are being loaded to the GUS.
-- There was some problems with GUS and Mitsumi CD in version 1.99.8. Fixed
- in 1.99.9.
-- /dev/audio sounded like stereo with GUS. Fixed in 1.99.9.
+ This problem disappears completely if you define GUS_PATCH_NO_DMA in the
+ local.h (after make config in linux). The drawback is that patch loading
+ without DMA takes several times longer than with DMA.
- There is a skeleton of the patch manager support. It don't work in
this version.
+
+
+Future development
+------------------
+
+- Since this driver is no longer just the Linux Sound Driver, it's time
+ to give it a new name. I have planned to use name VoxWare.
+- I'm writing a Hacker's guide to the VoxWare sound driver. Should
+ be ready within this(/next) year (alpha version).
+- Completion of the ISC, SCO and BSD ports. Port to SVR4.2.
+- I'm interested to implement/include support for new soundcards and
+ operating systems.
+
+ Hint for the soundcard and OS manufacturers:
+ I'm collecting soundcards (high end ones) and SDKs for them. In
+ addition I'm collecting PC operating systems. I will be happy if
+ somebody sends me such items. In addition such kind of donation
+ makes it easier to change the VoxWare driver to support your
+ soundcard or operating system. However, please contact me before
+ sending anything.
+
+I will propably release some fix versions within this and next year. At
+least when the non-Linux versions get ready. The next major release (3.0)
+will be quite complete rewrite and released after about a year (end of 94 or
+beginning of 95).
+
+
+Contributors
+------------
+
+This driver contains code by several contributors. In addition several other
+persons have given usefull 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 Initial ISC port
+ Jim Lowe Initial 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.
+ Ian Hartas SVR4.2 port
+ Markus Aroharju and
+ Risto Kankkunen Major contributions to the mixer support
+ of GUS v3.7.
+ Hunyue Yau Sound Galaxy NX Pro mixer support.
+
+Regards,
+
+Hannu Savolainen
+hannu@voxware.pp.fi, Hannu.Savolainen@Helsinki.fi
+
+Snail mail: Hannu Savolainen
+ Pallaksentie 4 A 2
+ 00970 Helsinki
+ Finland
diff --git a/sys/i386/isa/sound/adlib_card.c b/sys/i386/isa/sound/adlib_card.c
index 29e521e914d9..6365069384a5 100644
--- a/sys/i386/isa/sound/adlib_card.c
+++ b/sys/i386/isa/sound/adlib_card.c
@@ -1,11 +1,10 @@
-
/*
- * linux/kernel/chr_drv/sound/adlib_card.c
- *
+ * 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
@@ -13,7 +12,7 @@
* 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
@@ -25,7 +24,7 @@
* 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"
diff --git a/sys/i386/isa/sound/audio.c b/sys/i386/isa/sound/audio.c
index 775e6abeb021..f27f9d5fa09f 100644
--- a/sys/i386/isa/sound/audio.c
+++ b/sys/i386/isa/sound/audio.c
@@ -1,10 +1,10 @@
/*
- * linux/kernel/chr_drv/sound/audio.c
- *
+ * 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
@@ -12,7 +12,7 @@
* 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
@@ -24,7 +24,7 @@
* 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"
@@ -38,23 +38,45 @@
#define OFF 0
static int wr_buff_no[MAX_DSP_DEV]; /* != -1, if there is a
+
* incomplete output block */
static int wr_buff_size[MAX_DSP_DEV], wr_buff_ptr[MAX_DSP_DEV];
+
+static int audio_mode[MAX_DSP_DEV];
+
+#define AM_NONE 0
+#define AM_WRITE 1
+#define AM_READ 2
+
static char *wr_dma_buf[MAX_DSP_DEV];
int
audio_open (int dev, struct fileinfo *file)
{
- int mode;
int ret;
+ int bits;
+ int dev_type = dev & 0x0f;
+ int mode = file->mode & O_ACCMODE;
dev = dev >> 4;
- mode = file->mode & O_ACCMODE;
+
+ if (dev_type == SND_DEV_DSP16)
+ bits = 16;
+ else
+ bits = 8;
if ((ret = DMAbuf_open (dev, mode)) < 0)
return ret;
+ if (DMAbuf_ioctl (dev, SNDCTL_DSP_SAMPLESIZE, bits, 1) != bits)
+ {
+ audio_release (dev, file);
+ return RET_ERROR (ENXIO);
+ }
+
wr_buff_no[dev] = -1;
+ audio_mode[dev] = AM_NONE;
+
return ret;
}
@@ -106,12 +128,20 @@ audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
{
int c, p, l;
int err;
+ int dev_type = dev & 0x0f;
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)
@@ -136,15 +166,25 @@ audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
if (l > (wr_buff_size[dev] - wr_buff_ptr[dev]))
l = (wr_buff_size[dev] - wr_buff_ptr[dev]);
- COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l);
+ if (!dsp_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
+ dsp_devs[dev]->copy_from_user (dev,
+ wr_dma_buf[dev], wr_buff_ptr[dev], buf, p, l);
+
/* Insert local processing here */
+ if (dev_type == SND_DEV_AUDIO)
+ {
#ifdef linux
- /* This just allows interrupts while the conversion is running */
- __asm__ ("sti");
+ /* This just allows interrupts while the conversion is running */
+ __asm__ ("sti");
#endif
- translate_bytes (ulaw_dsp, &wr_dma_buf[dev][wr_buff_ptr[dev]], l);
+ translate_bytes (ulaw_dsp, (unsigned char *) &wr_dma_buf[dev][wr_buff_ptr[dev]], l);
+ }
c -= l;
p += l;
@@ -169,11 +209,24 @@ audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
int c, p, l;
char *dmabuf;
int buff_no;
+ int dev_type = dev & 0x0f;
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)) < 0)
@@ -183,12 +236,16 @@ audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
l = c;
/* Insert any local processing here. */
+
+ if (dev_type == SND_DEV_AUDIO)
+ {
#ifdef linux
- /* This just allows interrupts while the conversion is running */
- __asm__ ("sti");
+ /* This just allows interrupts while the conversion is running */
+ __asm__ ("sti");
#endif
- translate_bytes (dsp_ulaw, dmabuf, l);
+ translate_bytes (dsp_ulaw, (unsigned char *) dmabuf, l);
+ }
COPY_TO_USER (buf, p, dmabuf, l);
@@ -205,6 +262,8 @@ int
audio_ioctl (int dev, struct fileinfo *file,
unsigned int cmd, unsigned int arg)
{
+ int dev_type = dev & 0x0f;
+
dev = dev >> 4;
switch (cmd)
@@ -235,11 +294,10 @@ audio_ioctl (int dev, struct fileinfo *file,
break;
default:
-#if 1
- return RET_ERROR (EIO);
-#else
+ if (dev_type == SND_DEV_AUDIO)
+ return RET_ERROR (EIO);
+
return DMAbuf_ioctl (dev, cmd, arg, 0);
-#endif
}
}
@@ -266,14 +324,14 @@ audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
int
audio_open (int dev, struct fileinfo *file)
- {
- return RET_ERROR (ENXIO);
- }
+{
+ 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)
diff --git a/sys/i386/isa/sound/debug.h b/sys/i386/isa/sound/debug.h
deleted file mode 100644
index 79b4acdeba8f..000000000000
--- a/sys/i386/isa/sound/debug.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Kernel driver debugging stuff - cmetz@thor.tjhsst.edu */
-
-#ifndef _DEBUG_H_
-#define _DEBUG_H_
-
-#if defined (DEBUG_ME) && !defined(HALT_DEBUGGING)
-
-#define DEB(X) printk("%s %s: ", __FILE__, __FUNCTION__); X
-#define DEB1(X) printk("%s %s: ", __FILE__, __FUNCTION__); X
-#define RETURN_HEX(X, Y) { Y _foo; _foo = X; printk("%s %s: 0x%x\n", __FILE__, __FUNCTION__, ((int)_foo)); return(_foo); }
-#define RETURN_DEC(X, Y) { Y _foo; _foo = X; printk("%s %s: %d\n", __FILE__, __FUNCTION__, ((int)_foo)); return(_foo); }
-#define RETURN_PTR(X, Y) { Y _foo; _foo = X; printk("%s %s: 0x%08x\n", __FILE__, __FUNCTION__, ((void *)_foo)); return(_foo); }
-#define RETURN_ERR(X) { int _foo; _foo = X; printk("%s %s: ", __FILE__, __FUNCTION__); switch(_foo) { case 0: printk("No error"); break; case -ENODEV: printk("ENODEV"); break; case -EBUSY: printk("EBUSY"); break; default: printk("Error %d", _foo); } printk(".\n"); return(_foo); }
-#define DEB_OUTB OUTB
-#define DEB_INB INB
-
-#else
-
-#define DEB(X)
-#define DEB1(X)
-#define RETURN_HEX(X, Y) return(X)
-#define RETURN_DEC(X, Y) return(X)
-#define RETURN_PTR(X, Y) return(X)
-#define RETURN_ERR(X) return(X)
-#define DEB_OUTB OUTB
-#define DEB_INB INB
-
-#endif
-#endif
diff --git a/sys/i386/isa/sound/dev_table.c b/sys/i386/isa/sound/dev_table.c
index 7eabad4ab8a0..7f7cae11d02e 100644
--- a/sys/i386/isa/sound/dev_table.c
+++ b/sys/i386/isa/sound/dev_table.c
@@ -1,10 +1,10 @@
/*
- * linux/kernel/chr_drv/sound/dev_table.c
- *
+ * 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
@@ -12,7 +12,7 @@
* 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
@@ -24,7 +24,7 @@
* 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_
@@ -38,21 +38,24 @@ sndtable_init (long mem_start)
int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
for (i = 0; i < (n - 1); i++)
- if (supported_drivers[i].probe (&supported_drivers[i].config))
- {
+ if (supported_drivers[i].enabled)
+ if (supported_drivers[i].probe (&supported_drivers[i].config))
+ {
#ifndef SHORT_BANNERS
- printk ("snd%d",
- supported_drivers[i].card_type);
+ printk ("snd%d",
+ supported_drivers[i].card_type);
#endif
- mem_start = supported_drivers[i].attach (mem_start, &supported_drivers[i].config);
+ mem_start = supported_drivers[i].attach (mem_start, &supported_drivers[i].config);
#ifndef SHORT_BANNERS
- printk (" at 0x%03x irq %d drq %d\n",
- supported_drivers[i].config.io_base,
- supported_drivers[i].config.irq,
- supported_drivers[i].config.dma);
+ printk (" at 0x%x irq %d drq %d\n",
+ supported_drivers[i].config.io_base,
+ supported_drivers[i].config.irq,
+ supported_drivers[i].config.dma);
#endif
- }
+ }
+ else
+ supported_drivers[i].enabled = 0; /* Mark as not detected */
return mem_start;
}
@@ -66,7 +69,15 @@ sndtable_probe (int unit, struct address_info *hw_config)
for (i = 0; i < (n - 1); i++)
if (supported_drivers[i].card_type == unit)
- return supported_drivers[i].probe (hw_config);
+ {
+ supported_drivers[i].config.io_base = hw_config->io_base;
+ supported_drivers[i].config.irq = hw_config->irq;
+ supported_drivers[i].config.dma = hw_config->dma;
+ if (supported_drivers[i].probe (hw_config))
+ return 1;
+ supported_drivers[i].enabled = 0; /* Mark as not detected */
+ return 0;
+ }
return FALSE;
}
@@ -86,6 +97,10 @@ sndtable_init_card (int unit, struct address_info *hw_config)
for (i = 0; i < (n - 1); i++)
if (supported_drivers[i].card_type == unit)
{
+ supported_drivers[i].config.io_base = hw_config->io_base;
+ supported_drivers[i].config.irq = hw_config->irq;
+ supported_drivers[i].config.dma = hw_config->dma;
+
if (supported_drivers[i].attach (0, hw_config) != 0)
panic ("snd#: Invalid memory allocation\n");
return TRUE;
@@ -100,4 +115,103 @@ sndtable_get_cardcount (void)
return num_dspdevs + num_mixers + num_synths + num_midis;
}
+#ifdef linux
+void
+sound_setup (char *str, int *ints)
+{
+ int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+ /*
+ * First disable all drivers
+ */
+
+ for (i = 0; i < n; i++)
+ supported_drivers[i].enabled = 0;
+
+ if (ints[0] == 0 || ints[1] == 0)
+ return;
+ /*
+ * Then enable them one by time
+ */
+
+ for (i = 1; i <= ints[0]; i++)
+ {
+ int card_type, ioaddr, irq, dma, ptr, j;
+ unsigned int val;
+
+ val = (unsigned int) ints[i];
+
+ card_type = (val & 0x0ff00000) >> 20;
+
+ if (card_type > 127)
+ {
+ /* Add any future extensions here */
+ return;
+ }
+
+ ioaddr = (val & 0x000fff00) >> 8;
+ irq = (val & 0x000000f0) >> 4;
+ dma = (val & 0x0000000f);
+
+ ptr = -1;
+ for (j = 0; j < n && ptr == -1; j++)
+ if (supported_drivers[j].card_type == card_type)
+ ptr = j;
+
+ if (ptr == -1)
+ printk ("Sound: Invalid setup parameter 0x%08x\n", val);
+ else
+ {
+ supported_drivers[ptr].enabled = 1;
+ supported_drivers[ptr].config.io_base = ioaddr;
+ supported_drivers[ptr].config.irq = irq;
+ supported_drivers[ptr].config.dma = dma;
+ }
+ }
+}
+
+#else
+void
+sound_chconf (int card_type, int ioaddr, int irq, int dma)
+{
+ int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+ int ptr, j;
+
+ ptr = -1;
+ for (j = 0; j < n && ptr == -1; j++)
+ if (supported_drivers[j].card_type == card_type)
+ ptr = j;
+
+ if (ptr != -1)
+ {
+ supported_drivers[ptr].enabled = 1;
+ if (ioaddr)
+ supported_drivers[ptr].config.io_base = ioaddr;
+ if (irq)
+ supported_drivers[ptr].config.irq = irq;
+ if (dma)
+ supported_drivers[ptr].config.dma = dma;
+ }
+}
+
+#endif
+
+struct address_info *
+sound_getconf (int card_type)
+{
+ int j, ptr;
+ int n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+ ptr = -1;
+ for (j = 0; j < n && ptr == -1; j++)
+ if (supported_drivers[j].card_type == card_type)
+ ptr = j;
+
+ if (ptr == -1)
+ return (struct address_info *) NULL;
+
+ return &supported_drivers[ptr].config;
+}
+
#endif
diff --git a/sys/i386/isa/sound/dev_table.h b/sys/i386/isa/sound/dev_table.h
index 9bfd7847ecd2..4b656ba39c2b 100644
--- a/sys/i386/isa/sound/dev_table.h
+++ b/sys/i386/isa/sound/dev_table.h
@@ -46,6 +46,7 @@ struct card_info {
long (*attach) (long mem_start, struct address_info *hw_config);
int (*probe) (struct address_info *hw_config);
struct address_info config;
+ int enabled;
};
/** UWM -- new MIDI structure here.. **/
@@ -57,10 +58,15 @@ struct generic_midi_info{
struct audio_operations {
char name[32];
+ int flags;
+#define NOTHING_SPECIAL 0
+#define NEEDS_RESTART 1
int (*open) (int dev, int mode);
void (*close) (int dev);
- void (*output_block) (int dev, unsigned long buf, int count, int intrflag);
- void (*start_input) (int dev, unsigned long buf, int count, int intrflag);
+ 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);
@@ -93,6 +99,7 @@ struct synth_operations {
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);
};
@@ -159,31 +166,42 @@ struct generic_midi_operations {
*/
struct card_info supported_drivers[] = {
-#ifndef EXCLUDE_MPU401
+#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI)
{SNDCARD_MPU401,"Roland MPU-401", attach_mpu401, probe_mpu401,
- {MPU_BASE, MPU_IRQ, 0}},
-#endif
-
-#ifndef EXCLUDE_GUS
- {SNDCARD_GUS, "Gravis Ultrasound", attach_gus_card, probe_gus,
- {GUS_BASE, GUS_IRQ, GUS_DMA}},
+ {MPU_BASE, MPU_IRQ, 0}, SND_DEFAULT_ENABLE},
#endif
#ifndef EXCLUDE_PAS
{SNDCARD_PAS, "ProAudioSpectrum", attach_pas_card, probe_pas,
- {PAS_BASE, PAS_IRQ, PAS_DMA}},
+ {PAS_BASE, PAS_IRQ, PAS_DMA}, SND_DEFAULT_ENABLE},
#endif
#ifndef EXCLUDE_SB
{SNDCARD_SB, "SoundBlaster", attach_sb_card, probe_sb,
- {SBC_BASE, SBC_IRQ, SBC_DMA}},
+ {SBC_BASE, SBC_IRQ, SBC_DMA}, SND_DEFAULT_ENABLE},
+#endif
+
+#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SBPRO)
+#ifndef EXCLUDE_AUDIO
+ {SNDCARD_SB16, "SoundBlaster16", sb16_dsp_init, sb16_dsp_detect,
+ {SBC_BASE, SBC_IRQ, SB16_DMA}, SND_DEFAULT_ENABLE},
+#endif
+#ifndef EXCLUDE_MIDI
+ {SNDCARD_SB16MIDI,"SB16 MPU-401", attach_sb16midi, probe_sb16midi,
+ {SB16MIDI_BASE, SBC_IRQ, 0}, SND_DEFAULT_ENABLE},
+#endif
+#endif
+
+#ifndef EXCLUDE_GUS
+ {SNDCARD_GUS, "Gravis Ultrasound", attach_gus_card, probe_gus,
+ {GUS_BASE, GUS_IRQ, GUS_DMA}, SND_DEFAULT_ENABLE},
#endif
#ifndef EXCLUDE_YM3812
{SNDCARD_ADLIB, "AdLib", attach_adlib_card, probe_adlib,
- {FM_MONO, 0, 0}},
+ {FM_MONO, 0, 0}, SND_DEFAULT_ENABLE},
#endif
- {0, "*?*", NULL}
+ {0, "*?*", NULL, 0}
};
int num_sound_drivers =
@@ -220,6 +238,8 @@ struct generic_midi_operations {
long sndtable_init(long mem_start);
int sndtable_get_cardcount (void);
long CMIDI_init(long mem_start); /* */
+struct address_info *sound_getconf(int card_type);
+void sound_chconf(int card_type, int ioaddr, int irq, int dma);
#endif
#endif
diff --git a/sys/i386/isa/sound/dmabuf.c b/sys/i386/isa/sound/dmabuf.c
index ace02d149a41..851a70a16b1b 100644
--- a/sys/i386/isa/sound/dmabuf.c
+++ b/sys/i386/isa/sound/dmabuf.c
@@ -1,10 +1,10 @@
/*
- * linux/kernel/chr_drv/sound/dmabuf.c
- *
+ * sound/dmabuf.c
+ *
* The DMA buffer manager for digitized voice applications
- *
+ *
* 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
@@ -12,7 +12,7 @@
* 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
@@ -24,7 +24,7 @@
* 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"
@@ -35,7 +35,7 @@
#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS)
-#define MAX_SUB_BUFFERS 16
+#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR)
/*
* The DSP channel can be used either for input or output. Variable
@@ -48,7 +48,6 @@
#define DMODE_NONE 0
#define DMODE_OUTPUT 1
#define DMODE_INPUT 2
-#define DMODE_INIT 3
DEFINE_WAIT_QUEUES (dev_sleeper[MAX_DSP_DEV], dev_sleep_flag[MAX_DSP_DEV]);
@@ -58,22 +57,6 @@ static int dma_mode[MAX_DSP_DEV] =
static volatile int dmabuf_interrupted[MAX_DSP_DEV] =
{0};
-#ifdef ISC
-/* I don't like this. */
-#undef INTERRUPTIBLE_SLEEP_ON
-#define INTERRUPTIBLE_SLEEP_ON(A,F) { \
- A = F = 1; \
- if (sleep(&(A), (PZERO + 5) | PCATCH)) { \
- A = F = 0; \
- dmabuf_interrupted[dev] = 1; \
- dev_busy[dev] = 0; \
- dma_reset(dev); \
- dmabuf_interrupted[dev] = 0; \
- /* longjmp(u.u_qsav, 1); Where it goes??? */ \
- } \
- }
-#endif
-
/*
* Pointers to raw buffers
*/
@@ -89,7 +72,10 @@ int snd_raw_count[MAX_DSP_DEV];
*/
static int dev_busy[MAX_DSP_DEV];
+static int dev_needs_restart[MAX_DSP_DEV];
+static int dev_modes[MAX_DSP_DEV];
static int dev_active[MAX_DSP_DEV];
+static int dev_started[MAX_DSP_DEV];
static int dev_qlen[MAX_DSP_DEV];
static int dev_qhead[MAX_DSP_DEV];
static int dev_qtail[MAX_DSP_DEV];
@@ -102,8 +88,10 @@ static int bufferalloc_done[MAX_DSP_DEV] =
*/
static int dev_nbufs[MAX_DSP_DEV]; /* # of logical buffers ( >=
- * sound_buffcounts[dev] */
+
+ * sound_buffcounts[dev] */
static int dev_counts[MAX_DSP_DEV][MAX_SUB_BUFFERS];
+static int dev_subdivision[MAX_DSP_DEV];
static unsigned long dev_buf_phys[MAX_DSP_DEV][MAX_SUB_BUFFERS];
static char *dev_buf[MAX_DSP_DEV][MAX_SUB_BUFFERS] =
{
@@ -117,8 +105,8 @@ reorganize_buffers (int dev)
* This routine breaks the physical device buffers to logical ones.
*/
- unsigned long i, p, n;
- unsigned long sr, nc, sz, bsz;
+ unsigned i, p, n;
+ unsigned sr, nc, sz, bsz;
sr = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1);
nc = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1);
@@ -148,6 +136,17 @@ reorganize_buffers (int dev)
if (sound_buffcounts[dev] == 1 && bsz == sound_buffsizes[dev])
bsz >>= 1; /* Need at least 2 buffers */
+ if (dev_subdivision[dev] == 0)
+ dev_subdivision[dev] = 1; /* Default value */
+
+ bsz /= dev_subdivision[dev]; /* Use smaller buffers */
+
+ if (bsz == 0)
+ bsz = 4096; /* Just a sanity check */
+
+ while ((sound_buffsizes[dev] * sound_buffcounts[dev]) / bsz > MAX_SUB_BUFFERS)
+ bsz <<= 1; /* Too much buffers */
+
dev_buffsize[dev] = bsz;
n = 0;
@@ -178,6 +177,21 @@ reorganize_buffers (int dev)
bufferalloc_done[dev] = 1;
}
+static void
+dma_init_buffers (int dev)
+{
+ RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]);
+ dev_underrun[dev] = 0;
+
+ dev_busy[dev] = 1;
+
+ bufferalloc_done[dev] = 0;
+
+ dev_active[dev] = dev_qlen[dev] = dev_qtail[dev] = dev_qhead[dev] = 0;
+ dev_needs_restart[dev] = dev_started[dev] = 0;
+ dma_mode[dev] = DMODE_NONE;
+}
+
int
DMAbuf_open (int dev, int mode)
{
@@ -198,20 +212,23 @@ DMAbuf_open (int dev, int mode)
return RET_ERROR (ENXIO);
}
+#ifdef USE_RUNTIME_DMAMEM
+ sound_dma_malloc (dev);
+#endif
+
if (snd_raw_buf[dev][0] == NULL)
return RET_ERROR (ENOSPC); /* Memory allocation failed during boot */
if ((retval = dsp_devs[dev]->open (dev, mode)) < 0)
return retval;
- dev_underrun[dev] = 0;
-
- dev_busy[dev] = 1;
+ dev_modes[dev] = mode;
+ dev_subdivision[dev] = 0;
- reorganize_buffers (dev);
- bufferalloc_done[dev] = 0;
-
- dev_qlen[dev] = dev_qtail[dev] = dev_qhead[dev] = 0;
+ dma_init_buffers (dev);
+ dsp_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_BITS, 8, 1);
+ dsp_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_CHANNELS, 1, 1);
+ dsp_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_RATE, DSP_DEFAULT_SPEED, 1);
return 0;
}
@@ -219,35 +236,37 @@ DMAbuf_open (int dev, int mode)
static void
dma_reset (int dev)
{
+ int retval;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
dsp_devs[dev]->reset (dev);
+ dsp_devs[dev]->close (dev);
- dev_qlen[dev] = 0;
- dev_qhead[dev] = 0;
- dev_qtail[dev] = 0;
- dev_active[dev] = 0;
+ if ((retval = dsp_devs[dev]->open (dev, dev_modes[dev])) < 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;
- unsigned long time;
- int timed_out;
if (dma_mode[dev] == DMODE_OUTPUT)
{
DISABLE_INTR (flags);
- timed_out = 0;
- time = GET_TIME ();
-
- while ((!(PROCESS_ABORTING || dmabuf_interrupted[dev]) && !timed_out)
+ while ((!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) ||
+ dmabuf_interrupted[dev]))
&& dev_qlen[dev])
{
- REQUEST_TIMEOUT (10 * HZ, dev_sleeper[dev]);
- INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]);
- if ((GET_TIME () - time) > (10 * HZ))
- timed_out = 1;
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 10 * HZ);
+ if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+ return dev_qlen[dev];
}
RESTORE_INTR (flags);
@@ -259,11 +278,11 @@ dma_sync (int dev)
DISABLE_INTR (flags);
if (dsp_devs[dev]->has_output_drained) /* Device has hidden buffers */
{
- while (!(PROCESS_ABORTING || dmabuf_interrupted[dev])
+ while (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) ||
+ dmabuf_interrupted[dev])
&& !dsp_devs[dev]->has_output_drained (dev))
{
- REQUEST_TIMEOUT (HZ / 4, dev_sleeper[dev]);
- INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]);
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], HZ / 4);
}
}
RESTORE_INTR (flags);
@@ -275,16 +294,20 @@ int
DMAbuf_release (int dev, int mode)
{
- if (!(PROCESS_ABORTING || dmabuf_interrupted[dev])
+ if (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) ||
+ dmabuf_interrupted[dev])
&& (dma_mode[dev] == DMODE_OUTPUT))
{
dma_sync (dev);
}
- dma_reset (dev);
+#ifdef USE_RUNTIME_DMAMEM
+ sound_dma_free (dev);
+#endif
- if (!dev_active[dev])
- dsp_devs[dev]->close (dev);
+ dsp_devs[dev]->reset (dev);
+
+ dsp_devs[dev]->close (dev);
dma_mode[dev] = DMODE_NONE;
dev_busy[dev] = 0;
@@ -296,40 +319,65 @@ int
DMAbuf_getrdbuffer (int dev, char **buf, int *len)
{
unsigned long flags;
+ int err = EIO;
- if (!bufferalloc_done[dev])
- reorganize_buffers (dev);
-
- if (!dma_mode[dev])
+ DISABLE_INTR (flags);
+ if (!dev_qlen[dev])
{
- int err;
+ if (dev_needs_restart[dev])
+ {
+ dma_reset (dev);
+ dev_needs_restart[dev] = 0;
+ }
- if ((err = dsp_devs[dev]->prepare_for_input (dev,
- dev_buffsize[dev], dev_nbufs[dev])) < 0)
- return err;
- dma_mode[dev] = DMODE_INPUT;
- }
+ if (dma_mode[dev] == DMODE_OUTPUT) /* Was output -> direction change */
+ {
+ dma_sync (dev);
+ dma_reset (dev);
+ dma_mode[dev] = DMODE_NONE;
+ }
- if (dma_mode[dev] != DMODE_INPUT)
- return RET_ERROR (EBUSY); /* Can't change mode on fly */
+ if (!bufferalloc_done[dev])
+ reorganize_buffers (dev);
+
+ if (!dma_mode[dev])
+ {
+ int err;
+
+ if ((err = dsp_devs[dev]->prepare_for_input (dev,
+ dev_buffsize[dev], dev_nbufs[dev])) < 0)
+ {
+ RESTORE_INTR (flags);
+ return err;
+ }
+ dma_mode[dev] = DMODE_INPUT;
+ }
- DISABLE_INTR (flags);
- if (!dev_qlen[dev])
- {
if (!dev_active[dev])
{
- dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]], dev_buffsize[dev], 0);
+ dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]],
+ dev_buffsize[dev], 0,
+ !sound_dma_automode[dev] ||
+ !dev_started[dev]);
dev_active[dev] = 1;
+ dev_started[dev] = 1;
}
/* Wait for the next block */
- REQUEST_TIMEOUT (10 * HZ, dev_sleeper[dev]);
- INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]);
+ 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");
+ err = EIO;
+ SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ else
+ err = EINTR;
}
RESTORE_INTR (flags);
if (!dev_qlen[dev])
- return RET_ERROR (EINTR);
+ return RET_ERROR (err);
*buf = &dev_buf[dev][dev_qhead[dev]][dev_counts[dev][dev_qhead[dev]]];
*len = dev_buffsize[dev] - dev_counts[dev][dev_qhead[dev]];
@@ -391,6 +439,7 @@ DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
case SNDCTL_DSP_SYNC:
dma_sync (dev);
+ dma_reset (dev);
return 0;
break;
@@ -401,10 +450,37 @@ DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
return IOCTL_OUT (arg, dev_buffsize[dev]);
break;
+ case SNDCTL_DSP_SUBDIVIDE:
+ {
+ int fact = IOCTL_IN (arg);
+
+ if (fact == 0)
+ {
+ fact = dev_subdivision[dev];
+ if (fact == 0)
+ fact = 1;
+ return IOCTL_OUT (arg, fact);
+ }
+
+ if (dev_subdivision[dev] != 0) /* Too 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);
+
+ dev_subdivision[dev] = fact;
+ return IOCTL_OUT (arg, fact);
+ }
+ break;
+
default:
return dsp_devs[dev]->ioctl (dev, cmd, arg, local);
}
+ /* NOTREACHED */
return RET_ERROR (EIO);
}
@@ -412,6 +488,20 @@ int
DMAbuf_getwrbuffer (int dev, char **buf, int *size)
{
unsigned long flags;
+ int err = EIO;
+
+ if (dma_mode[dev] == DMODE_INPUT) /* Was input -> Direction change */
+ {
+ dma_reset (dev);
+ dma_mode[dev] = DMODE_NONE;
+ }
+ else if (dev_needs_restart[dev]) /* Restart buffering */
+ {
+ dma_sync (dev);
+ dma_reset (dev);
+ }
+
+ dev_needs_restart[dev] = 0;
if (!bufferalloc_done[dev])
reorganize_buffers (dev);
@@ -426,10 +516,11 @@ DMAbuf_getwrbuffer (int dev, char **buf, int *size)
return err;
}
- if (dma_mode[dev] != DMODE_OUTPUT)
- return RET_ERROR (EBUSY); /* Can't change mode on fly */
DISABLE_INTR (flags);
+
+ RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]);
+
if (dev_qlen[dev] == dev_nbufs[dev])
{
if (!dev_active[dev])
@@ -440,14 +531,20 @@ DMAbuf_getwrbuffer (int dev, char **buf, int *size)
}
/* Wait for free space */
- REQUEST_TIMEOUT (60 * HZ, dev_sleeper[dev]); /* GUS requires up to 60
- * sec */
- INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]);
+ 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");
+ err = EIO;
+ SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ else if (PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ err = EINTR;
}
RESTORE_INTR (flags);
if (dev_qlen[dev] == dev_nbufs[dev])
- return RET_ERROR (EIO); /* We have got signal (?) */
+ return RET_ERROR (err); /* We have got signal (?) */
*buf = dev_buf[dev][dev_qtail[dev]];
*size = dev_buffsize[dev];
@@ -466,12 +563,18 @@ DMAbuf_start_output (int dev, int buff_no, int l)
dev_counts[dev][dev_qtail[dev]] = l;
+ dev_needs_restart[dev] = (l != dev_buffsize[dev]) &&
+ (sound_dma_automode[dev] || dsp_devs[dev]->flags & NEEDS_RESTART);
+
dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev];
if (!dev_active[dev])
{
dev_active[dev] = 1;
- dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]], dev_counts[dev][dev_qhead[dev]], 0);
+ dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]],
+ dev_counts[dev][dev_qhead[dev]], 0,
+ !sound_dma_automode[dev] || !dev_started[dev]);
+ dev_started[dev] = 1;
}
return 0;
@@ -504,27 +607,33 @@ DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
set_dma_count (chan, sound_buffsizes[dev]);
enable_dma (chan);
RESTORE_INTR (flags);
-#else
+#else /* linux */
#ifdef __386BSD__
printk ("sound: Invalid DMA mode for device %d\n", dev);
isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
(caddr_t)snd_raw_buf_phys[dev][0],
- (unsigned)sound_buffsizes[dev],
- (unsigned)chan);
-#else
-#ifdef ISC
+ sound_buffsizes[dev],
+ chan);
+#else /* __386BSD__ */
+#if defined(ISC) || defined(SCO) || defined(SVR42)
+#ifndef DMAMODE_AUTO
printk ("sound: Invalid DMA mode for device %d\n", dev);
- dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode) | DMAMODE_AUTO,
- snd_raw_buf_phys[dev][0], count - 1);
+#endif /* DMAMODE_AUTO */
+ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode)
+#ifdef DMAMODE_AUTO
+ | DMAMODE_AUTO
+#endif /* DMAMODE_AUTO */
+ ,
+ snd_raw_buf_phys[dev][0], count);
dma_enable (chan);
-#else
-# error This routine is not valid for this OS.
-#endif
-#endif
+#else /* SYSV */
+#error This routine is not valid for this OS.
+#endif /* SYSV */
+#endif /* __386BSD__ */
-#endif
+#endif /* linux */
}
else
{
@@ -537,24 +646,24 @@ DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
set_dma_count (chan, count);
enable_dma (chan);
RESTORE_INTR (flags);
-#else
+#else /* linux */
#ifdef __386BSD__
isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
(caddr_t)physaddr,
count,
chan);
-#else
+#else /* __386BSD__ */
-#ifdef ISC
+#if defined(ISC) || defined(SCO) || defined(SVR42)
dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode),
- physaddr, count - 1);
+ physaddr, count);
dma_enable (chan);
-#else
-# error This routine is not valid for this OS.
-#endif /* !ISC */
-#endif
+#else /* SYSV */
+#error This routine is not valid for this OS.
+#endif /* SYSV */
+#endif /* __386BSD__ */
-#endif
+#endif /* linux */
}
return count;
@@ -584,37 +693,33 @@ DMAbuf_init (long mem_start)
}
void
-DMAbuf_outputintr (int dev)
+DMAbuf_outputintr (int dev, int underrun_flag)
{
unsigned long flags;
- dev_active[dev] = 0;
dev_qlen[dev]--;
dev_qhead[dev] = (dev_qhead[dev] + 1) % dev_nbufs[dev];
+ dev_active[dev] = 0;
if (dev_qlen[dev])
{
- dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]], dev_counts[dev][dev_qhead[dev]], 1);
+ dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]],
+ dev_counts[dev][dev_qhead[dev]], 1,
+ !sound_dma_automode[dev]);
dev_active[dev] = 1;
}
- else
+ else if (underrun_flag)
{
- if (dev_busy[dev])
- {
- dev_underrun[dev]++;
- dsp_devs[dev]->halt_xfer (dev);
- }
- else
- { /* Device has been closed */
- dsp_devs[dev]->close (dev);
- }
+ dev_underrun[dev]++;
+ dsp_devs[dev]->halt_xfer (dev);
+ dev_needs_restart[dev] = (sound_dma_automode[dev] ||
+ dsp_devs[dev]->flags & NEEDS_RESTART);
}
DISABLE_INTR (flags);
- if (dev_sleep_flag[dev])
+ if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
{
- dev_sleep_flag[dev] = 0;
- WAKE_UP (dev_sleeper[dev]);
+ WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
}
RESTORE_INTR (flags);
}
@@ -624,30 +729,33 @@ DMAbuf_inputintr (int dev)
{
unsigned long flags;
- dev_active[dev] = 0;
if (!dev_busy[dev])
{
dsp_devs[dev]->close (dev);
}
else if (dev_qlen[dev] == (dev_nbufs[dev] - 1))
{
+ printk ("Sound: Recording overrun\n");
dev_underrun[dev]++;
dsp_devs[dev]->halt_xfer (dev);
+ dev_active[dev] = 0;
+ dev_needs_restart[dev] = sound_dma_automode[dev];
}
else
{
dev_qlen[dev]++;
dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev];
- dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]], dev_buffsize[dev], 1);
+ dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]],
+ dev_buffsize[dev], 1,
+ !sound_dma_automode[dev]);
dev_active[dev] = 1;
}
DISABLE_INTR (flags);
- if (dev_sleep_flag[dev])
+ if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
{
- dev_sleep_flag[dev] = 0;
- WAKE_UP (dev_sleeper[dev]);
+ WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
}
RESTORE_INTR (flags);
}
@@ -691,13 +799,13 @@ DMAbuf_reset_dma (int chan)
/*
* The sound_mem_init() is called by mem_init() immediately after mem_map is
* initialized and before free_page_list is created.
- *
+ *
* This routine allocates DMA buffers at the end of available physical memory (
* <16M) and marks pages reserved at mem_map.
*/
#else
-/* Stub versions if audio services not included */
+/* Stub versions if audio services not included */
int
DMAbuf_open (int dev, int mode)
@@ -784,7 +892,7 @@ DMAbuf_inputintr (int dev)
}
void
-DMAbuf_outputintr (int dev)
+DMAbuf_outputintr (int dev, int underrun_flag)
{
return;
}
diff --git a/sys/i386/isa/sound/dsp.c b/sys/i386/isa/sound/dsp.c
deleted file mode 100644
index fca931afab3e..000000000000
--- a/sys/i386/isa/sound/dsp.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * linux/kernel/chr_drv/sound/dsp.c
- *
- * Device file manager for /dev/dsp
- *
- * 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
-
-#define ON 1
-#define OFF 0
-
-static int wr_buff_no[MAX_DSP_DEV]; /* != -1, if there is a
- * incomplete output block */
-static int wr_buff_size[MAX_DSP_DEV], wr_buf_ptr[MAX_DSP_DEV];
-static char *wr_dma_buf[MAX_DSP_DEV];
-
-int
-dsp_open (int dev, struct fileinfo *file, int bits)
-{
- int mode;
- int ret;
-
- dev = dev >> 4;
- mode = file->mode & O_ACCMODE;
-
- if ((ret = DMAbuf_open (dev, mode)) < 0)
- return ret;
-
- if (DMAbuf_ioctl (dev, SNDCTL_DSP_SAMPLESIZE, bits, 1) != bits)
- {
- dsp_release (dev, file);
- return RET_ERROR (ENXIO);
- }
-
- wr_buff_no[dev] = -1;
-
- return ret;
-}
-
-void
-dsp_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_buf_ptr[dev]);
-
- wr_buff_no[dev] = -1;
- }
-
- DMAbuf_release (dev, mode);
-}
-
-
-int
-dsp_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 (!count) /* Flush output */
- {
- if (wr_buff_no[dev] >= 0)
- {
- DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_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])) < 0)
- return wr_buff_no[dev];
- wr_buf_ptr[dev] = 0;
- }
-
- l = c;
- if (l > (wr_buff_size[dev] - wr_buf_ptr[dev]))
- l = (wr_buff_size[dev] - wr_buf_ptr[dev]);
-
- if (!dsp_devs[dev]->copy_from_user)
- { /* No device specific copy routine */
- COPY_FROM_USER (&wr_dma_buf[dev][wr_buf_ptr[dev]], buf, p, l);
- }
- else
- dsp_devs[dev]->copy_from_user (dev,
- wr_dma_buf[dev], wr_buf_ptr[dev], buf, p, l);
-
- c -= l;
- p += l;
- wr_buf_ptr[dev] += l;
-
- if (wr_buf_ptr[dev] >= wr_buff_size[dev])
- {
- if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev])) < 0)
- return err;
-
- wr_buff_no[dev] = -1;
- }
-
- }
-
- return count;
-}
-
-
-int
-dsp_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;
-
- while (c)
- {
- if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l)) < 0)
- return buff_no;
-
- if (l > c)
- l = c;
-
- /* Insert any local processing here. */
-
- COPY_TO_USER (buf, 0, dmabuf, l);
-
- DMAbuf_rmchars (dev, buff_no, l);
-
- p += l;
- c -= l;
- }
-
- return count - c;
-}
-
-int
-dsp_ioctl (int dev, struct fileinfo *file,
- unsigned int cmd, unsigned int arg)
-{
-
- dev = dev >> 4;
-
- switch (cmd)
- {
- case SNDCTL_DSP_SYNC:
- if (wr_buff_no[dev] >= 0)
- {
- DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_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_buf_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;
-
- default:
- return DMAbuf_ioctl (dev, cmd, arg, 0);
- }
-}
-
-long
-dsp_init (long mem_start)
-{
- return mem_start;
-}
-
-#else
-/* Stub version */
-int
-dsp_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
-{
- return RET_ERROR (EIO);
-}
-
-int
-dsp_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
-{
- return RET_ERROR (EIO);
-}
-
-int
-dsp_open (int dev, struct fileinfo *file, int bits)
-{
- return RET_ERROR (ENXIO);
-}
-
-void
-dsp_release (int dev, struct fileinfo *file)
- {
- };
-int
-dsp_ioctl (int dev, struct fileinfo *file,
- unsigned int cmd, unsigned int arg)
-{
- return RET_ERROR (EIO);
-}
-
-int
-dsp_lseek (int dev, struct fileinfo *file, off_t offset, int orig)
-{
- return RET_ERROR (EIO);
-}
-
-long
-dsp_init (long mem_start)
-{
- return mem_start;
-}
-
-#endif
-
-#endif
diff --git a/sys/i386/isa/sound/gus_card.c b/sys/i386/isa/sound/gus_card.c
index 414034deef6f..c7cfc0a7ab6c 100644
--- a/sys/i386/isa/sound/gus_card.c
+++ b/sys/i386/isa/sound/gus_card.c
@@ -1,10 +1,10 @@
/*
- * linux/kernel/chr_drv/sound/gus_card.c
- *
+ * 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
@@ -12,7 +12,7 @@
* 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
@@ -24,7 +24,7 @@
* 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"
@@ -37,76 +37,12 @@ void gusintr (int);
int gus_base, gus_irq, gus_dma;
-static int
-set_gus_irq (int interrupt_level)
-{
- int retcode = EINVAL;
-
-#ifdef linux
- struct sigaction sa;
-
- sa.sa_handler = gusintr;
-
-#ifdef SND_SA_INTERRUPT
- sa.sa_flags = SA_INTERRUPT;
-#else
- sa.sa_flags = 0;
-#endif
-
- sa.sa_mask = 0;
- sa.sa_restorer = NULL;
-
- retcode = irqaction (interrupt_level, &sa);
-
- if (retcode < 0)
- {
- printk ("GUS: IRQ%d already in use\n", interrupt_level);
- }
-
-#else
- /* # error Unimplemented for this OS */
-#endif
- return retcode;
-}
-
-int
-gus_set_midi_irq (int interrupt_level)
-{
- int retcode = EINVAL;
-
-#ifdef linux
- struct sigaction sa;
-
- sa.sa_handler = gus_midi_interrupt;
-
-#ifdef SND_SA_INTERRUPT
- sa.sa_flags = SA_INTERRUPT;
-#else
- sa.sa_flags = 0;
-#endif
-
- sa.sa_mask = 0;
- sa.sa_restorer = NULL;
-
- retcode = irqaction (interrupt_level, &sa);
-
- if (retcode < 0)
- {
- printk ("GUS: IRQ%d already in use\n", interrupt_level);
- }
-
-#else
- /* # error Unimplemented for this OS */
-#endif
- return retcode;
-}
-
long
attach_gus_card (long mem_start, struct address_info *hw_config)
{
int io_addr;
- set_gus_irq (hw_config->irq);
+ snd_set_irq_handler (hw_config->irq, gusintr);
if (gus_wave_detect (hw_config->io_base)) /* Try first the default */
{
@@ -127,7 +63,7 @@ attach_gus_card (long mem_start, struct address_info *hw_config)
if (io_addr != hw_config->io_base) /* Already tested */
if (gus_wave_detect (io_addr))
{
- printk (" WARNING! GUS found at %03x, config was %03x ", io_addr, hw_config->io_base);
+ 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);
#ifndef EXCLUDE_MIDI
mem_start = gus_midi_init (mem_start);
@@ -168,7 +104,10 @@ void
gusintr (int unit)
{
unsigned char src;
- unsigned long flags;
+
+#ifdef linux
+ sti ();
+#endif
while (1)
{
@@ -195,9 +134,7 @@ gusintr (int unit)
if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ))
{
- DISABLE_INTR (flags);
gus_voice_irq ();
- RESTORE_INTR (flags);
}
}
}
diff --git a/sys/i386/isa/sound/gus_hw.h b/sys/i386/isa/sound/gus_hw.h
index 48233e7e1e32..f97a0b8670e3 100644
--- a/sys/i386/isa/sound/gus_hw.h
+++ b/sys/i386/isa/sound/gus_hw.h
@@ -24,6 +24,8 @@
#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 */
@@ -32,4 +34,17 @@
# 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/i386/isa/sound/gus_linearvol.h b/sys/i386/isa/sound/gus_linearvol.h
new file mode 100644
index 000000000000..7ad0c30d4fd9
--- /dev/null
+++ b/sys/i386/isa/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/i386/isa/sound/gus_midi.c b/sys/i386/isa/sound/gus_midi.c
index b5cd6844cd1d..935c5c90b324 100644
--- a/sys/i386/isa/sound/gus_midi.c
+++ b/sys/i386/isa/sound/gus_midi.c
@@ -1,10 +1,10 @@
/*
- * linux/kernel/chr_drv/sound/gus2_midi.c
- *
+ * 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
@@ -12,7 +12,7 @@
* 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
@@ -24,7 +24,7 @@
* 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"
@@ -215,7 +215,7 @@ gus_midi_buffer_status (int dev)
static struct midi_operations gus_midi_operations =
{
- {"Gravis UltraSound", 0},
+ {"Gravis UltraSound", 0, 0, SNDCARD_GUS},
gus_midi_open,
gus_midi_close,
gus_midi_ioctl,
diff --git a/sys/i386/isa/sound/gus_vol.c b/sys/i386/isa/sound/gus_vol.c
index b3f1e84060ad..055a1170e9fb 100644
--- a/sys/i386/isa/sound/gus_vol.c
+++ b/sys/i386/isa/sound/gus_vol.c
@@ -1,10 +1,11 @@
/*
* gus_vol.c - Compute volume for GUS.
- *
+ *
* Greg Lee 1993.
*/
#include "sound_config.h"
#ifndef EXCLUDE_GUS
+#include "gus_linearvol.h"
#define GUS_VOLUME gus_wave_volume
@@ -20,7 +21,7 @@ extern int gus_wave_volume;
* 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.
@@ -38,24 +39,37 @@ gus_adagio_vol (int vel, int mainv, int xpn, int voicev)
*/
x = 256 + 6 * (voicev - 64);
- /* Boost expression by voice volume above neutral. */
+ /*
+ * Boost expression by voice volume above neutral.
+ */
if (voicev > 65)
xpn += voicev - 64;
xpn += (voicev - 64) / 2;
- /* Combine multiplicative and level components. */
+ /*
+ * Combine multiplicative and level components.
+ */
x = vel * xpn * 6 + (voicev / 4) * x;
#ifdef GUS_VOLUME
/*
* Further adjustment by installation-specific master volume control
- * (default 50).
+ * (default 60).
*/
x = (x * GUS_VOLUME * GUS_VOLUME) / 10000;
#endif
- if (x < (1 << 11))
- return (11 << 8);
+#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);
@@ -82,7 +96,9 @@ gus_adagio_vol (int vel, int mainv, int xpn, int voicev)
*/
m = x - (1 << i);
- /* Adjust mantissa to 8 bits. */
+ /*
+ * Adjust mantissa to 8 bits.
+ */
if (m > 0)
{
if (i > 8)
@@ -91,11 +107,41 @@ gus_adagio_vol (int vel, int mainv, int xpn, int voicev)
m <<= 8 - i;
}
- /* low volumes give occasional sour notes */
- if (i < 11)
- return (11 << 8);
-
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/i386/isa/sound/gus_wave.c b/sys/i386/isa/sound/gus_wave.c
index deb3a1525991..9f6442e0fcff 100644
--- a/sys/i386/isa/sound/gus_wave.c
+++ b/sys/i386/isa/sound/gus_wave.c
@@ -1,11 +1,10 @@
-
/*
- * linux/kernel/chr_drv/sound/gus_wave.c
- *
+ * sound/gus_wave.c
+ *
* Driver for the Gravis UltraSound wave table synth.
- *
+ *
* 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
@@ -13,7 +12,7 @@
* 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
@@ -25,18 +24,20 @@
* 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 GUS_LINEAR_VOLUME */
-
#include "sound_config.h"
+#ifdef __FreeBSD__
#include <machine/ultrasound.h>
+#else
+#include "ultrasound.h"
+#endif
#include "gus_hw.h"
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
-#define MAX_SAMPLE 256
+#define MAX_SAMPLE 128
#define MAX_PATCH 256
struct voice_info
@@ -57,6 +58,7 @@ struct voice_info
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];
@@ -67,6 +69,11 @@ struct voice_info
*/
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;
+
};
extern int gus_base;
@@ -77,24 +84,30 @@ extern int snd_raw_count[MAX_DSP_DEV];
static long gus_mem_size = 0;
static long free_mem_ptr = 0;
static int gus_busy = 0;
-static int nr_voices = 0; /* Number of currently allowed voices */
+static int nr_voices = 0;
static int gus_devnum = 0;
static int volume_base, volume_scale, volume_method;
+static int gus_line_vol = 100, gus_mic_vol = 0;
+static int gus_recmask = SOUND_MASK_MIC;
+static int recording_active = 0;
-#define VOL_METHOD_ADAGIO 1
-int gus_wave_volume = 60; /* Master wolume for wave (0 to 100) */
+int gus_wave_volume = 60;
+int gus_pcm_volume = 80;
static unsigned char mix_image = 0x00;
/*
- * Current version of this_one driver doesn't allow synth and PCM functions
+ * 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 the second
- * chn */
+#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 the
+ * second * * * chn */
static int gus_sampling_speed;
static int gus_sampling_channels;
@@ -105,13 +118,34 @@ DEFINE_WAIT_QUEUE (dram_sleeper, dram_sleep_flag);
/*
* Variables and buffers for PCM output
*/
-#define MAX_PCM_BUFFERS 32 /* Don't change */
-static int pcm_bsize, /* Current blocksize */
- pcm_nblk, /* Current # of blocks */
- pcm_banksize; /* # bytes allocated for channels */
-static int pcm_datasize[MAX_PCM_BUFFERS]; /* Actual # of bytes in blk */
-static volatile int pcm_head, pcm_tail, pcm_qlen; /* DRAM queue */
+#define MAX_PCM_BUFFERS (32*MAX_REALTIME_FACTOR) /*
+ * * * Don't
+ * * * change
+ *
+ */
+
+static int pcm_bsize, /*
+ * Current blocksize
+ */
+ pcm_nblk, /*
+ * Current # of blocks
+ */
+ pcm_banksize; /*
+
+
+ * * * * # bytes allocated for channels */
+static int pcm_datasize[MAX_PCM_BUFFERS]; /*
+
+
+ * * * * Actual # of bytes
+ * in blk * */
+static volatile int pcm_head, pcm_tail, pcm_qlen; /*
+
+
+ * * * * DRAM queue
+ * */
static volatile int pcm_active;
+static int pcm_opened = 0;
static int pcm_current_dev;
static int pcm_current_block;
static unsigned long pcm_current_buf;
@@ -122,28 +156,66 @@ 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 */
+ 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[MAX_SAMPLE + 1];
+static struct patch_info *samples;
static long sample_ptrs[MAX_SAMPLE + 1];
static int sample_map[32];
static int free_sample;
@@ -158,10 +230,15 @@ static struct synth_info gus_info =
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 /* Dont use ramping */
-#define FAST_RAMP 0 /* Fastest possible ramp */
+#define INSTANT_RAMP -1 /*
+ * * * Dont use ramping */
+#define FAST_RAMP 0 /*
+ * * * Fastest possible ramp */
static void
reset_sample_memory (void)
@@ -175,7 +252,9 @@ reset_sample_memory (void)
for (i = 0; i < 32; i++)
patch_map[i] = -1;
- gus_poke (0, 0); /* Put silence here */
+ gus_poke (0, 0); /*
+ * Put silence here
+ */
gus_poke (1, 0);
free_mem_ptr = 2;
@@ -230,14 +309,14 @@ gus_peek (long addr)
}
void
-gus_write8 (int reg, unsigned char data)
+gus_write8 (int reg, unsigned int data)
{
unsigned long flags;
DISABLE_INTR (flags);
OUTB (reg, u_Command);
- OUTB (data, u_DataHi);
+ OUTB ((unsigned char) (data & 0xff), u_DataHi);
RESTORE_INTR (flags);
}
@@ -271,7 +350,7 @@ gus_look8 (int reg)
}
void
-gus_write16 (int reg, unsigned short data)
+gus_write16 (int reg, unsigned int data)
{
unsigned long flags;
@@ -279,8 +358,8 @@ gus_write16 (int reg, unsigned short data)
OUTB (reg, u_Command);
- OUTB (data & 0xff, u_DataLo);
- OUTB ((data >> 8) & 0xff, u_DataHi);
+ OUTB ((unsigned char) (data & 0xff), u_DataLo);
+ OUTB ((unsigned char) ((data >> 8) & 0xff), u_DataHi);
RESTORE_INTR (flags);
}
@@ -322,6 +401,10 @@ gus_write_addr (int reg, unsigned long address, int is16bit)
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));
}
static void
@@ -347,11 +430,11 @@ gus_select_max_voices (int nvoices)
}
static void
-gus_voice_on (unsigned char mode)
+gus_voice_on (unsigned int mode)
{
- gus_write8 (0x00, mode & 0xfc);
+ gus_write8 (0x00, (unsigned char) (mode & 0xfc));
gus_delay ();
- gus_write8 (0x00, mode & 0xfc);
+ gus_write8 (0x00, (unsigned char) (mode & 0xfc));
}
static void
@@ -361,10 +444,18 @@ gus_voice_off (void)
}
static void
-gus_voice_mode (unsigned char mode)
-{
- gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); /* Don't start or stop
- * voice */
+gus_voice_mode (unsigned int m)
+{
+ unsigned char mode = (unsigned char) (m & 0xff);
+
+ gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); /*
+ * Don't
+ * start
+ * or
+ * stop
+ * *
+ * voice
+ */
gus_delay ();
gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc));
}
@@ -382,44 +473,56 @@ gus_voice_freq (unsigned long freq)
}
static void
-gus_voice_volume (unsigned short vol)
+gus_voice_volume (unsigned int vol)
{
- gus_write8 (0x0d, 0x03); /* Stop ramp before setting volume */
- gus_write16 (0x09, vol << 4);
+ gus_write8 (0x0d, 0x03); /*
+ * Stop ramp before setting volume
+ */
+ gus_write16 (0x09, (unsigned short) (vol << 4));
}
static void
-gus_voice_balance (unsigned char balance)
+gus_voice_balance (unsigned int balance)
{
- gus_write8 (0x0c, balance);
+ gus_write8 (0x0c, (unsigned char) (balance & 0xff));
}
static void
-gus_ramp_range (unsigned short low, unsigned short high)
+gus_ramp_range (unsigned int low, unsigned int high)
{
- gus_write8 (0x07, (low >> 4) & 0xff);
- gus_write8 (0x08, (high >> 4) & 0xff);
+ gus_write8 (0x07, (unsigned char) ((low >> 4) & 0xff));
+ gus_write8 (0x08, (unsigned char) ((high >> 4) & 0xff));
}
static void
-gus_ramp_rate (unsigned char scale, unsigned char rate)
+gus_ramp_rate (unsigned int scale, unsigned int rate)
{
- gus_write8 (0x06, ((scale & 0x03) << 6) | (rate & 0x3f));
+ gus_write8 (0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f)));
}
static void
-gus_rampon (unsigned char mode)
+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 char mode)
-{
- gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); /* Don't start or stop
- * ramping */
+gus_ramp_mode (unsigned int m)
+{
+ unsigned char mode = (unsigned char) (m & 0xff);
+
+ gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); /*
+ * Don't
+ * start
+ * or
+ * stop
+ * *
+ * ramping
+ */
gus_delay ();
gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc));
}
@@ -431,6 +534,20 @@ gus_rampoff (void)
}
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;
@@ -438,11 +555,22 @@ gus_voice_init (int voice)
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_voice_volume (0);
- gus_write_addr (0x0a, 0, 0); /* Set current position to 0 */
- gus_write8 (0x00, 0x03); /* Voice off */
- gus_write8 (0x0d, 0x03); /* Ramping off */
+ gus_write_addr (0x0a, 0, 0); /*
+ * Set current position to 0
+ */
+ gus_write8 (0x00, 0x03); /*
+ * Voice off
+ */
+ gus_write8 (0x0d, 0x03); /*
+ * Ramping off
+ */
RESTORE_INTR (flags);
+}
+
+static void
+gus_voice_init2 (int voice)
+{
voices[voice].panning = 0;
voices[voice].mode = 0;
voices[voice].orig_freq = 20000;
@@ -459,6 +587,7 @@ gus_voice_init (int voice)
voices[voice].main_vol = 127;
voices[voice].patch_vol = 127;
voices[voice].expression_vol = 127;
+ voices[voice].sample_pending = -1;
}
static void
@@ -466,11 +595,17 @@ 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 ();
- return; /* Sustain */
+ RESTORE_INTR (flags);
+ return; /*
+ * Sustain
+ */
}
if (voices[voice].env_phase >= 5)
@@ -484,20 +619,31 @@ step_envelope (int voice)
}
prev_vol = voices[voice].current_volume;
- gus_voice_volume (prev_vol);
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];
- gus_write8 (0x06, rate); /* Ramping rate */
+
+ 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 */
+ if (((vol - prev_vol) / 64) == 0) /*
+ * No significant volume change
+ */
{
- step_envelope (voice); /* Continue with the next phase */
+ RESTORE_INTR (flags);
+ step_envelope (voice); /*
+ * Continue with the next phase
+ */
return;
}
@@ -506,16 +652,21 @@ step_envelope (int voice)
if (vol >= (4096 - 64))
vol = 4096 - 65;
gus_ramp_range (0, vol);
- gus_rampon (0x20); /* Increasing, irq */
+ gus_rampon (0x20); /*
+ * Increasing, irq
+ */
}
else
{
if (vol <= 64)
vol = 65;
- gus_ramp_range (vol, 4095);
- gus_rampon (0x60); /* Decreasing, irq */
+ gus_ramp_range (vol, 4030);
+ gus_rampon (0x60); /*
+ * Decreasing, irq
+ */
}
voices[voice].current_volume = vol;
+ RESTORE_INTR (flags);
}
static void
@@ -528,19 +679,26 @@ init_envelope (int voice)
}
static void
-start_release (int voice)
+start_release (int voice, long int flags)
{
if (gus_read8 (0x00) & 0x03)
- return; /* Voice already stopped */
+ return; /*
+ * Voice already stopped
+ */
- voices[voice].env_phase = 2; /* Will be incremented by step_envelope */
+ 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 */
+ gus_read16 (0x09) >> 4; /*
+ * Get current volume
+ */
voices[voice].mode &= ~WAVE_SUSTAIN_ON;
gus_rampoff ();
+ RESTORE_INTR (flags);
step_envelope (voice);
}
@@ -548,25 +706,38 @@ 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 */
+ gus_write8 (0x00, 0x03); /*
+ * Hard stop
+ */
+ RESTORE_INTR (flags);
return;
}
- is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bit samples */
+ is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /*
+ * 8 or 16
+ * bit
+ * samples
+ */
if (voices[voice].mode & WAVE_ENVELOPES)
{
- start_release (voice);
+ start_release (voice, flags);
return;
}
/*
* Ramp the volume down but not too quickly.
*/
- if ((gus_read16 (0x09) >> 4) < 100) /* Get current volume */
+ if ((gus_read16 (0x09) >> 4) < 100) /*
+ * Get current volume
+ */
{
gus_voice_off ();
gus_rampoff ();
@@ -574,10 +745,13 @@ gus_voice_fade (int voice)
return;
}
- gus_ramp_range (65, 4095);
+ gus_ramp_range (65, 4030);
gus_ramp_rate (2, 4);
- gus_rampon (0x40 | 0x20); /* Down, once, irq */
+ gus_rampon (0x40 | 0x20); /*
+ * Down, once, irq
+ */
voices[voice].volume_irq_mode = VMODE_HALT;
+ RESTORE_INTR (flags);
}
static void
@@ -592,14 +766,25 @@ gus_reset (void)
for (i = 0; i < 32; i++)
{
- gus_voice_init (i); /* Turn voice off */
+ gus_voice_init (i); /*
+ * Turn voice off
+ */
+ gus_voice_init2 (i);
}
- INB (u_Status); /* Touch the status register */
+ 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_look8 (0x41); /*
+ * Clear any pending DMA IRQs
+ */
+ gus_look8 (0x49); /*
+ * Clear any pending sample IRQs
+ */
+ gus_read8 (0x0f); /*
+ * Clear pending IRQs
+ */
}
@@ -607,7 +792,7 @@ static void
gus_initialize (void)
{
unsigned long flags;
- unsigned char dma_image, irq_image, tmp;
+ register unsigned char dma_image, irq_image, tmp;
static unsigned char gus_irq_map[16] =
{0, 0, 1, 3, 0, 2, 0, 4, 0, 0, 0, 5, 6, 0, 0, 7};
@@ -617,11 +802,15 @@ gus_initialize (void)
DISABLE_INTR (flags);
- gus_write8 (0x4c, 0); /* Reset GF1 */
+ gus_write8 (0x4c, 0); /*
+ * Reset GF1
+ */
gus_delay ();
gus_delay ();
- gus_write8 (0x4c, 1); /* Release Reset */
+ gus_write8 (0x4c, 1); /*
+ * Release Reset
+ */
gus_delay ();
gus_delay ();
@@ -629,25 +818,49 @@ gus_initialize (void)
* Clear all interrupts
*/
- gus_write8 (0x41, 0); /* DMA control */
- gus_write8 (0x45, 0); /* Timer control */
- gus_write8 (0x49, 0); /* Sample control */
+ 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 */
+ 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
@@ -655,7 +868,9 @@ gus_initialize (void)
OUTB (0x05, gus_base + 0x0f);
- mix_image |= 0x02; /* Disable line out */
+ mix_image |= 0x02; /*
+ * Disable line out
+ */
OUTB (mix_image, u_Mixer);
OUTB (0x00, u_IRQDMAControl);
@@ -664,12 +879,9 @@ gus_initialize (void)
/*
* Now set up the DMA and IRQ interface
- *
+ *
* The GUS supports two IRQs and two DMAs.
- *
- * If GUS_MIDI_IRQ is defined and if it's != GUS_IRQ, separate Midi IRQ is set
- * up. Otherwise the same IRQ is shared by the both devices.
- *
+ *
* 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.
@@ -680,23 +892,13 @@ gus_initialize (void)
if (!tmp)
printk ("Warning! GUS IRQ not selected\n");
irq_image |= tmp;
+ irq_image |= 0x40; /*
+ * Combine IRQ1 (GF1) and IRQ2 (Midi)
+ */
- if (GUS_MIDI_IRQ != gus_irq)
- { /* The midi irq was defined and != wave irq */
- tmp = gus_irq_map[GUS_MIDI_IRQ];
- tmp <<= 3;
-
- if (!tmp)
- printk ("Warning! GUS Midi IRQ not selected\n");
- else
- gus_set_midi_irq (GUS_MIDI_IRQ);
-
- irq_image |= tmp;
- }
- else
- irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */
-
- dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */
+ dma_image = 0x40; /*
+ * Combine DMA1 (DRAM) and IRQ2 (ADC)
+ */
tmp = gus_dma_map[gus_dma];
if (!tmp)
printk ("Warning! GUS DMA not selected\n");
@@ -706,37 +908,73 @@ gus_initialize (void)
* 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, 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 */
+ /*
+ * Doing it first time
+ */
- gus_select_voice (0); /* This disables writes to IRQ/DMA reg */
+ OUTB (mix_image, u_Mixer); /*
+ * Select DMA control
+ */
+ OUTB (dma_image | 0x80, u_IRQDMAControl); /*
+ * Set DMA address
+ */
- mix_image &= ~0x02; /* Enable line out */
- mix_image |= 0x08; /* Enable IRQ */
- OUTB (mix_image, u_Mixer); /* Turn mixer channels on */
+ 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 */
+ /*
+ * Doing it second time
+ */
- gusintr (0); /* Serve pending interrupts */
+ 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 (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 */
@@ -747,31 +985,37 @@ gus_wave_detect (int baseaddr)
gus_delay ();
gus_delay ();
- gus_poke (0x000, 0xaa);
- gus_poke (0x100, 0x55);
+ /* See if there is first block there.... */
+ gus_poke (0L, 0xaa);
+ if (gus_peek (0L) != 0xaa)
+ return (0);
- if (gus_peek (0x000) != 0xaa)
- return 0;
- if (gus_peek (0x100) != 0x55)
- return 0;
-
- gus_mem_size = 0x40000; /* 256k */
- gus_poke (0x40000, 0xaa);
- if (gus_peek (0x40000) != 0xaa)
- return 1;
+ /* Now zero it out so that I can check for mirroring .. */
+ gus_poke (0L, 0x00);
+ for (i = 1L; i < 1024L; i++)
+ {
+ int n, failed;
- gus_mem_size = 0x80000; /* 512k */
- gus_poke (0x80000, 0xaa);
- if (gus_peek (0x80000) != 0xaa)
- return 1;
+ /* check for mirroring ... */
+ if (gus_peek (0L) != 0)
+ break;
+ loc = i << 10;
- gus_mem_size = 0xc0000; /* 768k */
- gus_poke (0xc0000, 0xaa);
- if (gus_peek (0xc0000) != 0xaa)
- return 1;
+ for (n = loc - 1, failed = 0; n <= loc; n++)
+ {
+ gus_poke (loc, 0xaa);
+ if (gus_peek (loc) != 0xaa)
+ failed = 1;
- gus_mem_size = 0x100000; /* 1M */
+ gus_poke (loc, 0x55);
+ if (gus_peek (loc) != 0x55)
+ failed = 1;
+ }
+ if (failed)
+ break;
+ }
+ gus_mem_size = i << 10;
return 1;
}
@@ -816,16 +1060,26 @@ guswave_set_instr (int dev, int voice, int instr_no)
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 */
+ return RET_ERROR (EINVAL);/*
+ * Patch not defined
+ */
}
- if (sample_ptrs[sample_no] == -1) /* Sample not loaded */
+ 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);
@@ -837,14 +1091,25 @@ guswave_set_instr (int dev, int voice, int instr_no)
}
static int
+#ifdef FUTURE_VERSION
+guswave_kill_note (int dev, int voice, int note, int velocity)
+#else
guswave_kill_note (int dev, int voice, int velocity)
+#endif
{
unsigned long flags;
DISABLE_INTR (flags);
- gus_select_voice (voice);
- gus_voice_fade (voice);
- RESTORE_INTR (flags);
+ 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);
+ }
return 0;
}
@@ -855,20 +1120,26 @@ guswave_aftertouch (int dev, int voice, int pressure)
short lo_limit, hi_limit;
unsigned long flags;
- return; /* Currently disabled */
+ return; /*
+ * Currently disabled
+ */
if (voice < 0 || voice > 31)
return;
if (voices[voice].mode & WAVE_ENVELOPES && voices[voice].env_phase != 2)
- return; /* Don't mix with envelopes */
+ 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 */
+ compute_and_set_volume (voice, 255, 0); /*
+ * Back to original volume
+ */
RESTORE_INTR (flags);
return;
}
@@ -887,7 +1158,9 @@ guswave_aftertouch (int dev, int voice, int pressure)
}
gus_ramp_range (lo_limit, hi_limit);
gus_ramp_rate (3, 8);
- gus_rampon (0x58); /* Bidirectional, Down, Loop */
+ gus_rampon (0x58); /*
+ * Bidirectional, Down, Loop
+ */
RESTORE_INTR (flags);
}
@@ -899,38 +1172,57 @@ guswave_panning (int dev, int voice, int 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)
{
- voices[voice].midi_volume = volume;
+ 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;
- switch (volume_method)
- {
- case VOL_METHOD_ADAGIO:
- voices[voice].initial_volume =
- gus_adagio_vol (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 + (volume * volume_scale);
- }
+ default:
+ voices[voice].initial_volume = volume_base +
+ (voices[voice].midi_volume * volume_scale);
}
- if (voices[voice].initial_volume > 4095)
- voices[voice].initial_volume = 4095;
+ 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;
@@ -938,6 +1230,7 @@ compute_and_set_volume (int voice, int volume, int ramp_time)
{
gus_rampoff ();
gus_voice_volume (target);
+ RESTORE_INTR (flags);
return;
}
@@ -947,10 +1240,13 @@ compute_and_set_volume (int voice, int volume, int ramp_time)
rate = 16;
gus_ramp_rate (0, rate);
- if ((target - current) / 64 == 0) /* Too close */
+ if ((target - current) / 64 == 0) /*
+ * Too close
+ */
{
gus_rampoff ();
gus_voice_volume (target);
+ RESTORE_INTR (flags);
return;
}
@@ -959,7 +1255,9 @@ compute_and_set_volume (int voice, int volume, int ramp_time)
if (target > (4095 - 65))
target = 4095 - 65;
gus_ramp_range (current, target);
- gus_rampon (0x00); /* Ramp up, once, no irq */
+ gus_rampon (0x00); /*
+ * Ramp up, once, no irq
+ */
}
else
{
@@ -967,8 +1265,11 @@ compute_and_set_volume (int voice, int volume, int ramp_time)
target = 65;
gus_ramp_range (target, current);
- gus_rampon (0x40); /* Ramp down, once, no irq */
+ gus_rampon (0x40); /*
+ * Ramp down, once, no irq
+ */
}
+ RESTORE_INTR (flags);
}
static void
@@ -979,11 +1280,15 @@ dynamic_volume_change (int voice)
DISABLE_INTR (flags);
gus_select_voice (voice);
- status = gus_read8 (0x00); /* Voice status */
+ status = gus_read8 (0x00); /*
+ * Voice status
+ */
RESTORE_INTR (flags);
if (status & 0x03)
- return; /* Voice not started */
+ return; /*
+ * Voice not started
+ */
if (!(voices[voice].mode & WAVE_ENVELOPES))
{
@@ -997,10 +1302,14 @@ dynamic_volume_change (int voice)
DISABLE_INTR (flags);
gus_select_voice (voice);
- status = gus_read8 (0x0d); /* Ramping status */
+ status = gus_read8 (0x0d); /*
+ * Ramping status
+ */
RESTORE_INTR (flags);
- if (status & 0x03) /* Sustain phase? */
+ if (status & 0x03) /*
+ * Sustain phase?
+ */
{
compute_and_set_volume (voice, voices[voice].midi_volume, 1);
return;
@@ -1011,9 +1320,12 @@ dynamic_volume_change (int voice)
compute_volume (voice, voices[voice].midi_volume);
-#if 0 /* Is this really required */
+#if 0 /*
+ * * * Is this really required */
voices[voice].current_volume =
- gus_read16 (0x09) >> 4; /* Get current volume */
+ gus_read16 (0x09) >> 4; /*
+ * Get current volume
+ */
voices[voice].env_phase--;
step_envelope (voice);
@@ -1033,38 +1345,59 @@ guswave_controller (int dev, int voice, int ctrl_num, int value)
{
case CTRL_PITCH_BENDER:
voices[voice].bender = value;
- 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);
+ 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;
-
+#ifdef FUTURE_VERSION
+ case CTL_EXPRESSION:
+ value /= 128;
+#endif
case CTRL_EXPRESSION:
- volume_method = VOL_METHOD_ADAGIO;
- voices[voice].expression_vol = value;
- dynamic_volume_change (voice);
+ 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;
+#ifdef FUTURE_VERSION
+ case CTL_PAN:
+ voices[voice].panning = (value * 2) - 128;
+ break;
+
+ case CTL_MAIN_VOLUME:
+ value = (value * 100) / 16383;
+#endif
+
case CTRL_MAIN_VOLUME:
- volume_method = VOL_METHOD_ADAGIO;
voices[voice].main_vol = value;
- dynamic_volume_change (voice);
+ if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+ dynamic_volume_change (voice);
break;
- default: /* Ignore */
+ default: /*
+ * Ignore
+ */
break;
}
}
static int
-guswave_start_note (int dev, int voice, int note_num, int volume)
+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;
@@ -1123,7 +1456,9 @@ guswave_start_note (int dev, int voice, int note_num, int volume)
if (samples[samplep].low_note <= note_freq && note_freq <= samples[samplep].high_note)
sample = samplep;
else
- samplep = samples[samplep].key; /* Follow link */
+ samplep = samples[samplep].key; /*
+ * Follow link
+ */
}
if (sample == -1)
sample = best_sample;
@@ -1131,10 +1466,16 @@ guswave_start_note (int dev, int voice, int note_num, int volume)
if (sample == -1)
{
printk ("GUS: Patch %d not defined for note %d\n", patch, note_num);
- return 0; /* Should play default patch ??? */
+ return 0; /*
+ * Should play default patch ???
+ */
}
- is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bit samples */
+ is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; /*
+ * 8 or 16
+ * bit
+ * samples
+ */
voices[voice].mode = samples[sample].mode;
voices[voice].patch_vol = samples[sample].volume;
@@ -1151,7 +1492,9 @@ guswave_start_note (int dev, int voice, int note_num, int volume)
sample_map[voice] = sample;
- base_note = samples[sample].base_note / 100; /* To avoid overflows */
+ base_note = samples[sample].base_note / 100; /*
+ * To avoid overflows
+ */
note_freq /= 100;
freq = samples[sample].base_freq * note_freq / base_note;
@@ -1175,20 +1518,27 @@ guswave_start_note (int dev, int voice, int note_num, int volume)
if (samples[sample].mode & WAVE_16_BITS)
{
- mode |= 0x04; /* 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
- *************************************************************************/
+ * CAUTION! Interrupts disabled. Don't return before enabling
+ *************************************************************************/
DISABLE_INTR (flags);
gus_select_voice (voice);
- gus_voice_off (); /* It may still be running */
+ gus_voice_off (); /*
+ * It may still be running
+ */
gus_rampoff ();
+
+ RESTORE_INTR (flags);
+
if (voices[voice].mode & WAVE_ENVELOPES)
{
compute_volume (voice, volume);
@@ -1197,36 +1547,66 @@ guswave_start_note (int dev, int voice, int note_num, int volume)
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, is16bits); /* Sample start=end */
+ gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len -
+ voices[voice].offset_pending, is16bits); /* Sample
+ * start=end */
else
- gus_write_addr (0x0a, sample_ptrs[sample], is16bits); /* Sample start=begin */
+ gus_write_addr (0x0a, sample_ptrs[sample] + voices[voice].offset_pending,
+ is16bits); /* Sample start=begin */
if (samples[sample].mode & WAVE_LOOPING)
{
- mode |= 0x08; /* Looping on */
+ mode |= 0x08; /*
+ * Looping on
+ */
if (samples[sample].mode & WAVE_BIDIR_LOOP)
- mode |= 0x10; /* Bidirectional looping on */
+ mode |= 0x10; /*
+ * Bidirectional looping on
+ */
if (samples[sample].mode & WAVE_LOOP_BACK)
{
- gus_write_addr (0x0a, /* Put the current location = loop_end */
- sample_ptrs[sample] + samples[sample].loop_end, is16bits);
- mode |= 0x40; /* Loop backwards */
+ 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 */
+ 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 it down at the
- * end */
+ mode |= 0x20; /*
+ * Loop irq at the end
+ */
+ voices[voice].loop_irq_mode = LMODE_FINISH; /*
+ * Ramp it 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, is16bits); /* Loop end location */
+ 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);
@@ -1236,13 +1616,81 @@ guswave_start_note (int dev, int voice, int note_num, int volume)
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
+ {
+ RESTORE_INTR (flags);
+ ret_val = guswave_start_note2 (dev, 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);
+ guswave_set_instr (voices[voice].dev_pending, voice,
+ voices[voice].sample_pending);
+ voices[voice].sample_pending = -1;
+ DISABLE_INTR (flags);
+ }
+
+ if ((mode & 0x01) || ((gus_read16 (0x09) >> 4) < 2065))
+ {
+ ret_val = guswave_start_note2 (dev, voice, note_num, volume);
+ }
+ else
+ {
+ voices[voice].dev_pending = dev;
+ 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_init (i);
+ gus_voice_init2 (i);
+ }
}
static int
@@ -1253,9 +1701,12 @@ guswave_open (int dev, int mode)
if (gus_busy)
return RET_ERROR (EBUSY);
+ gus_initialize ();
+
if ((err = DMAbuf_open_dma (gus_devnum)))
return err;
+ RESET_WAIT_QUEUE (dram_sleeper, dram_sleep_flag);
gus_busy = 1;
active_device = GUS_DEV_WAVE;
@@ -1280,22 +1731,29 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr,
{
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; /*
+ * Size of
+ * the header
+ * * info
+ */
+
if (format != GUS_PATCH)
{
- printk ("GUS Error: Invalid patch format (key) 0x%04x\n", format);
+ printk ("GUS Error: Invalid patch format (key) 0x%x\n", format);
return RET_ERROR (EINVAL);
}
- if (count < sizeof (patch))
+ if (count < sizeof_patch)
{
printk ("GUS Error: Patch header too short\n");
return RET_ERROR (EINVAL);
}
- count -= sizeof (patch);
+ count -= sizeof_patch;
if (free_sample >= MAX_SAMPLE)
{
@@ -1308,7 +1766,7 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr,
* been transferred already.
*/
- COPY_FROM_USER (&((char *) &patch)[offs], addr, offs, sizeof (patch) - offs);
+ COPY_FROM_USER (&((char *) &patch)[offs], addr, offs, sizeof_patch - offs);
instr = patch.instr_no;
@@ -1321,13 +1779,13 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr,
if (count < patch.len)
{
printk ("GUS Warning: Patch record too short (%d<%d)\n",
- count, patch.len);
+ count, (int) patch.len);
patch.len = count;
}
if (patch.len <= 0 || patch.len > gus_mem_size)
{
- printk ("GUS: Invalid sample length %d\n", patch.len);
+ printk ("GUS: Invalid sample length %d\n", (int) patch.len);
return RET_ERROR (EINVAL);
}
@@ -1346,7 +1804,9 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr,
}
}
- free_mem_ptr = (free_mem_ptr + 31) & ~31; /* Alignment 32 bytes */
+ free_mem_ptr = (free_mem_ptr + 31) & ~31; /*
+ * Alignment 32 bytes
+ */
#define GUS_BANK_SIZE (256*1024)
@@ -1357,20 +1817,24 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr,
*/
if (patch.len >= GUS_BANK_SIZE)
{
- printk ("GUS: Sample (16 bit) too long %d\n", patch.len);
+ 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 = /* Align to 256K*N */
+ unsigned long tmp_mem = /*
+ * Align to 256K*N
+ */
((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 */
+ free_mem_ptr = tmp_mem; /*
+ * This leaves unusable memory
+ */
}
}
@@ -1379,12 +1843,14 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr,
sample_ptrs[free_sample] = free_mem_ptr;
- /* Tremolo is not possible with envelopes */
+ /*
+ * Tremolo is not possible with envelopes
+ */
if (patch.mode & WAVE_ENVELOPES)
patch.mode &= ~WAVE_TREMOLO;
- memcpy ((char *) &samples[free_sample], &patch, sizeof (patch));
+ memcpy ((char *) &samples[free_sample], &patch, sizeof_patch);
/*
* Link this_one sample to the list of samples for patch 'instr'.
@@ -1401,7 +1867,9 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr,
src_offs = 0;
target = free_mem_ptr;
- while (left) /* Not all moved */
+ while (left) /*
+ * Not all moved
+ */
{
blk_size = sound_buffsizes[gus_devnum];
if (blk_size > left)
@@ -1413,13 +1881,15 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr,
blk_end = target + blk_size;
if ((target >> 18) != (blk_end >> 18))
- { /* Have to split the block */
+ { /*
+ * Have to split the block
+ */
blk_end &= ~(256 * 1024 - 1);
blk_size = blk_end - target;
}
-#ifdef GUS_NO_DMA
+#if defined(GUS_NO_DMA) || defined(GUS_PATCH_NO_DMA)
/*
* For some reason the DMA is not possible. We have to use PIO.
*/
@@ -1429,24 +1899,35 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr,
for (i = 0; i < blk_size; i++)
{
- GET_BYTE_FROM_USER (data, addr, sizeof (patch) + 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 */
+#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 (snd_raw_buf[gus_devnum][0],
- addr, sizeof (patch) + src_offs,
+ addr, sizeof_patch + src_offs,
blk_size);
- gus_write8 (0x41, 0); /* Disable GF1 DMA */
+ DISABLE_INTR (flags); /******** INTERRUPTS DISABLED NOW ********/
+ gus_write8 (0x41, 0); /*
+ * Disable GF1 DMA
+ */
DMAbuf_start_dma (gus_devnum, snd_raw_buf_phys[gus_devnum][0],
blk_size, DMA_MODE_WRITE);
@@ -1464,30 +1945,46 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr,
address |= (hold_address & 0x000c0000L);
}
- gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */
+ gus_write16 (0x42, (address >> 4) & 0xffff); /*
+ * DRAM DMA address
+ */
/*
* Start the DMA transfer
*/
- dma_command = 0x21; /* IRQ enable, DMA start */
+ dma_command = 0x21; /*
+ * IRQ enable, DMA start
+ */
if (patch.mode & WAVE_UNSIGNED)
- dma_command |= 0x80; /* Invert MSB */
+ dma_command |= 0x80; /*
+ * Invert MSB
+ */
if (patch.mode & WAVE_16_BITS)
- dma_command |= 0x40; /* 16 bit _DATA_ */
+ dma_command |= 0x40; /*
+ * 16 bit _DATA_
+ */
if (sound_dsp_dmachan[gus_devnum] > 3)
- dma_command |= 0x04; /* 16 bit DMA channel */
+ dma_command |= 0x04; /*
+ * 16 bit DMA channel
+ */
- gus_write8 (0x41, dma_command); /* Let's go luteet (=bugs) */
+ gus_write8 (0x41, dma_command); /*
+ * Let's go luteet (=bugs)
+ */
/*
* Sleep here until the DRAM DMA done interrupt is served
*/
active_device = GUS_DEV_WAVE;
- INTERRUPTIBLE_SLEEP_ON (dram_sleeper, dram_sleep_flag);
+ 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 */
+#endif /*
+ * * * GUS_NO_DMA */
/*
* Now the next part
@@ -1497,7 +1994,9 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr,
src_offs += blk_size;
target += blk_size;
- gus_write8 (0x41, 0); /* Stop DMA */
+ gus_write8 (0x41, 0); /*
+ * Stop DMA
+ */
}
free_mem_ptr += patch.len;
@@ -1521,6 +2020,10 @@ guswave_hw_control (int dev, unsigned char *event)
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)
{
@@ -1538,7 +2041,9 @@ guswave_hw_control (int dev, unsigned char *event)
case _GUS_VOICEON:
DISABLE_INTR (flags);
gus_select_voice (voice);
- p1 &= ~0x20; /* Disable intr */
+ p1 &= ~0x20; /*
+ * Disable intr
+ */
gus_voice_on (p1);
RESTORE_INTR (flags);
break;
@@ -1551,16 +2056,15 @@ guswave_hw_control (int dev, unsigned char *event)
break;
case _GUS_VOICEFADE:
- DISABLE_INTR (flags);
- gus_select_voice (voice);
gus_voice_fade (voice);
- RESTORE_INTR (flags);
break;
case _GUS_VOICEMODE:
DISABLE_INTR (flags);
gus_select_voice (voice);
- p1 &= ~0x20; /* Disable intr */
+ p1 &= ~0x20; /*
+ * Disable intr
+ */
gus_voice_mode (p1);
RESTORE_INTR (flags);
break;
@@ -1586,14 +2090,18 @@ guswave_hw_control (int dev, unsigned char *event)
RESTORE_INTR (flags);
break;
- case _GUS_VOICEVOL2: /* Just update the voice value */
+ case _GUS_VOICEVOL2: /*
+ * Just update the voice value
+ */
voices[voice].initial_volume =
voices[voice].current_volume = p1;
break;
case _GUS_RAMPRANGE:
if (voices[voice].mode & WAVE_ENVELOPES)
- break; /* NO-NO */
+ break; /*
+ * NO-NO
+ */
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_ramp_range (p1, p2);
@@ -1602,7 +2110,9 @@ guswave_hw_control (int dev, unsigned char *event)
case _GUS_RAMPRATE:
if (voices[voice].mode & WAVE_ENVELOPES)
- break; /* NO-NO */
+ break; /*
+ * NO-NO
+ */
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_ramp_rate (p1, p2);
@@ -1611,27 +2121,37 @@ guswave_hw_control (int dev, unsigned char *event)
case _GUS_RAMPMODE:
if (voices[voice].mode & WAVE_ENVELOPES)
- break; /* NO-NO */
+ break; /*
+ * NO-NO
+ */
DISABLE_INTR (flags);
gus_select_voice (voice);
- p1 &= ~0x20; /* Disable intr */
+ p1 &= ~0x20; /*
+ * Disable intr
+ */
gus_ramp_mode (p1);
RESTORE_INTR (flags);
break;
case _GUS_RAMPON:
if (voices[voice].mode & WAVE_ENVELOPES)
- break; /* NO-NO */
+ break; /*
+ * NO-NO
+ */
DISABLE_INTR (flags);
gus_select_voice (voice);
- p1 &= ~0x20; /* Disable intr */
+ p1 &= ~0x20; /*
+ * Disable intr
+ */
gus_rampon (p1);
RESTORE_INTR (flags);
break;
case _GUS_RAMPOFF:
if (voices[voice].mode & WAVE_ENVELOPES)
- break; /* NO-NO */
+ break; /*
+ * NO-NO
+ */
DISABLE_INTR (flags);
gus_select_voice (voice);
gus_rampoff ();
@@ -1643,6 +2163,13 @@ guswave_hw_control (int dev, unsigned char *event)
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:;
}
}
@@ -1710,6 +2237,8 @@ gus_sampling_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
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;
@@ -1730,7 +2259,9 @@ gus_sampling_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
return gus_sampling_bits;
return IOCTL_OUT (arg, gus_sampling_bits);
- case SOUND_PCM_WRITE_FILTER: /* NOT YET IMPLEMENTED */
+ case SOUND_PCM_WRITE_FILTER: /*
+ * NOT YET IMPLEMENTED
+ */
return IOCTL_OUT (arg, RET_ERROR (EINVAL));
break;
@@ -1760,6 +2291,8 @@ gus_sampling_open (int dev, int mode)
if (gus_busy)
return RET_ERROR (EBUSY);
+ gus_initialize ();
+
gus_busy = 1;
active_device = 0;
@@ -1767,10 +2300,13 @@ gus_sampling_open (int dev, int mode)
reset_sample_memory ();
gus_select_max_voices (14);
- gus_sampling_set_bits (8);
- gus_sampling_set_channels (1);
- gus_sampling_set_speed (DSP_DEFAULT_SPEED);
pcm_active = 0;
+ pcm_opened = 1;
+ if (mode & OPEN_READ)
+ {
+ recording_active = 1;
+ set_input_volumes ();
+ }
return 0;
}
@@ -1780,7 +2316,31 @@ gus_sampling_close (int dev)
{
gus_reset ();
gus_busy = 0;
+ pcm_opened = 0;
active_device = 0;
+
+ if (recording_active)
+ set_input_volumes ();
+
+ recording_active = 0;
+}
+
+static void
+gus_sampling_update_volume (void)
+{
+ unsigned long flags;
+ int voice;
+
+ DISABLE_INTR (flags);
+ if (pcm_active && pcm_opened)
+ for (voice = 0; voice < gus_sampling_channels; voice++)
+ {
+ 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
@@ -1800,18 +2360,24 @@ play_next_pcm_block (void)
for (chn = 0; chn < gus_sampling_channels; chn++)
{
mode[chn] = 0x00;
- ramp_mode[chn] = 0x03; /* Ramping and rollover off */
+ ramp_mode[chn] = 0x03; /*
+ * Ramping and rollover off
+ */
if (chn == 0)
{
- mode[chn] |= 0x20; /* Loop irq */
+ 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 */
+ mode[chn] |= 0x04; /*
+ * 16 bit data
+ */
}
else
is16bits = 0;
@@ -1819,15 +2385,23 @@ play_next_pcm_block (void)
dram_loc = this_one * pcm_bsize;
dram_loc += chn * pcm_banksize;
- if (this_one == (pcm_nblk - 1)) /* Last of the DRAM buffers */
+ if (this_one == (pcm_nblk - 1)) /*
+ * Last of the DRAM buffers
+ */
{
- mode[chn] |= 0x08; /* Enable loop */
- ramp_mode[chn] = 0x03;/* Disable rollover */
+ mode[chn] |= 0x08; /*
+ * Enable loop
+ */
+ ramp_mode[chn] = 0x03;/*
+ * Disable rollover
+ */
}
else
{
if (chn == 0)
- ramp_mode[chn] = 0x04; /* Enable rollover bit */
+ ramp_mode[chn] = 0x04; /*
+ * Enable rollover bit
+ */
}
DISABLE_INTR (flags);
@@ -1835,13 +2409,21 @@ play_next_pcm_block (void)
gus_voice_freq (speed);
if (gus_sampling_channels == 1)
- gus_voice_balance (7); /* mono */
+ gus_voice_balance (7); /*
+ * mono
+ */
else if (chn == 0)
- gus_voice_balance (0); /* left */
+ gus_voice_balance (0); /*
+ * left
+ */
else
- gus_voice_balance (15); /* right */
+ gus_voice_balance (15); /*
+ * right
+ */
- if (!pcm_active) /* Voice not started yet */
+ if (!pcm_active) /*
+ * Voice not started yet
+ */
{
/*
* The playback was not started yet (or there has been a pause).
@@ -1850,38 +2432,67 @@ play_next_pcm_block (void)
* the normal loop with irq.
*/
- gus_voice_off (); /* It could already be running */
+ gus_voice_off (); /*
+ * It could already be running
+ */
gus_rampoff ();
- gus_voice_volume (4000);
- gus_ramp_range (65, 4030);
+ 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 location */
+ gus_write_addr (0x0a, dram_loc, is16bits); /*
+ * Starting position
+ */
+ gus_write_addr (0x02, chn * pcm_banksize, is16bits); /*
+ * Loop start
+ * location
+ */
if (chn != 0)
gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk),
- is16bits); /* Loop end location */
+ is16bits); /*
+ * Loop end location
+ */
}
if (chn == 0)
- gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /* Loop end location */
+ gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /*
+ * Loop
+ * end
+ * location
+ */
else
- mode[chn] |= 0x08; /* Enable loop */
+ mode[chn] |= 0x08; /*
+ * Enable loop
+ */
if (pcm_datasize[this_one] != pcm_bsize)
{
- /* Incomplete block. Possibly the last one. */
+ /*
+ * Incomplete block. Possibly the last one.
+ */
if (chn == 0)
{
- mode[chn] &= ~0x08; /* Disable loop */
- mode[chn] |= 0x20;/* Enable loop IRQ */
+ mode[chn] &= ~0x08; /*
+ * Disable loop
+ */
+ mode[chn] |= 0x20;/*
+ * Enable loop IRQ
+ */
voices[0].loop_irq_mode = LMODE_PCM_STOP;
- ramp_mode[chn] = 0x03; /* No rollover bit */
+ 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 loop */
+ gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /*
+ * Loop
+ * end
+ * location
+ */
+ mode[chn] &= ~0x08; /*
+ * Disable loop
+ */
}
}
@@ -1908,7 +2519,7 @@ gus_transfer_output_block (int dev, unsigned long buf,
* 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.
*/
@@ -1935,7 +2546,9 @@ gus_transfer_output_block (int dev, unsigned long buf,
else
this_one = pcm_current_block;
- gus_write8 (0x41, 0); /* Disable GF1 DMA */
+ gus_write8 (0x41, 0); /*
+ * Disable GF1 DMA
+ */
DMAbuf_start_dma (dev, buf + (chn * count), count, DMA_MODE_WRITE);
address = this_one * pcm_bsize;
@@ -1949,38 +2562,56 @@ gus_transfer_output_block (int dev, unsigned long buf,
address |= (hold_address & 0x000c0000L);
}
- gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */
+ gus_write16 (0x42, (address >> 4) & 0xffff); /*
+ * DRAM DMA address
+ */
- dma_command = 0x21; /* IRQ enable, DMA start */
+ dma_command = 0x21; /*
+ * IRQ enable, DMA start
+ */
if (gus_sampling_bits != 8)
- dma_command |= 0x40; /* 16 bit _DATA_ */
+ dma_command |= 0x40; /*
+ * 16 bit _DATA_
+ */
else
- dma_command |= 0x80; /* Invert MSB */
+ dma_command |= 0x80; /*
+ * Invert MSB
+ */
if (sound_dsp_dmachan[dev] > 3)
- dma_command |= 0x04; /* 16 bit DMA channel */
+ dma_command |= 0x04; /*
+ * 16 bit DMA channel
+ */
- gus_write8 (0x41, dma_command); /* Kick on */
+ gus_write8 (0x41, dma_command); /*
+ * Kick on
+ */
- if (chn == (gus_sampling_channels - 1)) /* Last channel */
+ if (chn == (gus_sampling_channels - 1)) /*
+ * Last channel
+ */
{
- /* Last (right or mono) channel data */
+ /*
+ * Last (right or mono) channel data
+ */
active_device = GUS_DEV_PCM_DONE;
if (!pcm_active && (pcm_qlen > 2 || count < pcm_bsize))
{
play_next_pcm_block ();
}
}
- else /* Left channel data. The right channel is
- * transferred after DMA interrupt */
+ 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)
+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;
@@ -1990,7 +2621,8 @@ gus_sampling_output_block (int dev, unsigned long buf, int total_count, int intr
}
static void
-gus_sampling_start_input (int dev, unsigned long buf, int count, int intrflag)
+gus_sampling_start_input (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
{
unsigned long flags;
unsigned char mode;
@@ -1999,13 +2631,21 @@ gus_sampling_start_input (int dev, unsigned long buf, int count, int intrflag)
DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
- mode = 0xa0; /* DMA IRQ enable, invert MSB */
+ mode = 0xa0; /*
+ * DMA IRQ enable, invert MSB
+ */
if (sound_dsp_dmachan[dev] > 3)
- mode |= 0x04; /* 16 bit DMA channel */
+ mode |= 0x04; /*
+ * 16 bit DMA channel
+ */
if (gus_sampling_channels > 1)
- mode |= 0x02; /* Stereo */
- mode |= 0x01; /* DMA enable */
+ mode |= 0x02; /*
+ * Stereo
+ */
+ mode |= 0x01; /*
+ * DMA enable
+ */
gus_write8 (0x49, mode);
@@ -2019,7 +2659,9 @@ gus_sampling_prepare_for_input (int dev, int bsize, int bcount)
rate = (9878400 / (gus_sampling_speed + 2)) / 16;
- gus_write8 (0x48, rate & 0xff); /* Set sampling frequency */
+ gus_write8 (0x48, rate & 0xff); /*
+ * Set sampling frequency
+ */
if (gus_sampling_bits != 8)
{
@@ -2072,7 +2714,9 @@ 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);
+ {
+ COPY_FROM_USER (&localbuf[localoffs], userbuf, useroffs, len);
+ }
else if (gus_sampling_bits == 8)
{
int in_left = useroffs;
@@ -2119,19 +2763,39 @@ gus_copy_from_user (int dev, char *localbuf, int localoffs,
static struct audio_operations gus_sampling_operations =
{
"Gravis UltraSound",
- 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, /* halt_xfer */
+ NEEDS_RESTART,
+ 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_has_output_drained,
gus_copy_from_user
};
+#ifdef FUTURE_VERSION
+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);
+}
+
+#endif
+
static int
guswave_patchmgr (int dev, struct patmgr_info *rec)
{
@@ -2161,7 +2825,9 @@ guswave_patchmgr (int dev, struct patmgr_info *rec)
while (ptr >= 0 && ptr < free_sample)
{
rec->data.data8[i]++;
- ptr = samples[ptr].key; /* Follow link */
+ ptr = samples[ptr].key; /*
+ * Follow link
+ */
}
}
return 0;
@@ -2176,7 +2842,9 @@ guswave_patchmgr (int dev, struct patmgr_info *rec)
while (ptr >= 0 && ptr < free_sample)
{
rec->data.data32[n++] = ptr;
- ptr = samples[ptr].key; /* Follow link */
+ ptr = samples[ptr].key; /*
+ * Follow link
+ */
}
}
rec->parm1 = n;
@@ -2196,8 +2864,12 @@ guswave_patchmgr (int dev, struct patmgr_info *rec)
pat = (struct patch_info *) rec->data.data8;
- pat->key = GUS_PATCH; /* Restore patch type */
- rec->parm1 = sample_ptrs[ptr]; /* DRAM address */
+ pat->key = GUS_PATCH; /*
+ * Restore patch type
+ */
+ rec->parm1 = sample_ptrs[ptr]; /*
+ * DRAM address
+ */
rec->parm2 = sizeof (struct patch_info);
}
return 0;
@@ -2213,10 +2885,14 @@ guswave_patchmgr (int dev, struct patmgr_info *rec)
pat = (struct patch_info *) rec->data.data8;
- if (pat->len > samples[ptr].len) /* Cannot expand sample */
+ if (pat->len > samples[ptr].len) /*
+ * Cannot expand sample
+ */
return RET_ERROR (EINVAL);
- pat->key = samples[ptr].key; /* Ensure the link is correct */
+ pat->key = samples[ptr].key; /*
+ * Ensure the link is correct
+ */
memcpy ((char *) &samples[ptr], rec->data.data8,
sizeof (struct patch_info));
@@ -2226,7 +2902,9 @@ guswave_patchmgr (int dev, struct patmgr_info *rec)
return 0;
break;
- case PM_READ_PATCH: /* Returns a block of wave data from the DRAM */
+ case PM_READ_PATCH: /*
+ * Returns a block of wave data from the DRAM
+ */
{
int sample = rec->parm1;
int n;
@@ -2237,9 +2915,13 @@ guswave_patchmgr (int dev, struct patmgr_info *rec)
return RET_ERROR (EINVAL);
if (offs < 0 || offs >= samples[sample].len)
- return RET_ERROR (EINVAL); /* Invalid offset */
+ return RET_ERROR (EINVAL); /*
+ * Invalid offset
+ */
- n = samples[sample].len - offs; /* Nr of bytes left */
+ n = samples[sample].len - offs; /*
+ * Nr of bytes left
+ */
if (l > n)
l = n;
@@ -2248,18 +2930,26 @@ guswave_patchmgr (int dev, struct patmgr_info *rec)
l = sizeof (rec->data.data8);
if (l <= 0)
- return RET_ERROR (EINVAL); /* Was there a bug? */
+ return RET_ERROR (EINVAL); /*
+ * Was there a bug?
+ */
- offs += sample_ptrs[sample]; /* Begin offsess + offset to DRAM */
+ 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 */
+ rec->parm1 = n; /*
+ * Nr of bytes copied
+ */
}
return 0;
break;
- case PM_WRITE_PATCH: /* Writes a block of wave data to the DRAM */
+ case PM_WRITE_PATCH: /*
+ * Writes a block of wave data to the DRAM
+ */
{
int sample = rec->parm1;
int n;
@@ -2270,9 +2960,13 @@ guswave_patchmgr (int dev, struct patmgr_info *rec)
return RET_ERROR (EINVAL);
if (offs < 0 || offs >= samples[sample].len)
- return RET_ERROR (EINVAL); /* Invalid offset */
+ return RET_ERROR (EINVAL); /*
+ * Invalid offset
+ */
- n = samples[sample].len - offs; /* Nr of bytes left */
+ n = samples[sample].len - offs; /*
+ * Nr of bytes left
+ */
if (l > n)
l = n;
@@ -2281,13 +2975,19 @@ guswave_patchmgr (int dev, struct patmgr_info *rec)
l = sizeof (rec->data.data8);
if (l <= 0)
- return RET_ERROR (EINVAL); /* Was there a bug? */
+ return RET_ERROR (EINVAL); /*
+ * Was there a bug?
+ */
- offs += sample_ptrs[sample]; /* Begin offsess + offset to DRAM */
+ 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 */
+ rec->parm1 = n; /*
+ * Nr of bytes copied
+ */
}
return 0;
break;
@@ -2300,6 +3000,9 @@ guswave_patchmgr (int dev, struct patmgr_info *rec)
static struct synth_operations guswave_operations =
{
&gus_info,
+#ifdef FUTURE_VERSION
+ 0,
+#endif
SYNTH_TYPE_SAMPLE,
SAMPLE_TYPE_GUS,
guswave_open,
@@ -2314,13 +3017,273 @@ static struct synth_operations guswave_operations =
guswave_aftertouch,
guswave_controller,
guswave_panning,
- guswave_patchmgr
+ guswave_volume_method,
+ guswave_patchmgr,
+#ifdef FUTURE_VERSION
+ guswave_bender
+#endif
+};
+
+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
+ * volume
+ */
+
+ 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 =
+{
+ 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)
{
- printk ("snd4: <Gravis UltraSound %dk>", gus_mem_size / 1024);
+ unsigned long flags;
+ unsigned char val;
+ char *model_num = "2.4";
+ int gus_type = 0x24; /* 2.4 */
+ int mixer_type = 0;
+
+ /*
+ * 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.
+ */
+ }
+
+
+#ifdef __FreeBSD__
+ printk ("snd4: <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
+
+#ifndef SCO
+ sprintf (gus_info.name, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024);
+#endif
if (irq < 0 || irq > 15)
{
@@ -2342,6 +3305,9 @@ gus_wave_init (long mem_start, int irq, int dma)
else
synth_devs[num_synths++] = &guswave_operations;
+ PERMANENT_MALLOC (struct patch_info *, samples,
+ (MAX_SAMPLE + 1) * sizeof (*samples), mem_start);
+
reset_sample_memory ();
gus_initialize ();
@@ -2350,13 +3316,29 @@ gus_wave_init (long mem_start, int irq, int dma)
{
dsp_devs[gus_devnum = num_dspdevs++] = &gus_sampling_operations;
sound_dsp_dmachan[gus_devnum] = dma;
- sound_buffcounts[gus_devnum] = 1;
+ sound_buffcounts[gus_devnum] = DSP_BUFFCOUNT;
sound_buffsizes[gus_devnum] = DSP_BUFFSIZE;
sound_dma_automode[gus_devnum] = 0;
}
else
printk ("GUS: Too many PCM devices available\n");
+ /*
+ * Mixer dependent initialization.
+ */
+
+ switch (mixer_type)
+ {
+ case ICS2101:
+ gus_line_vol=gus_mic_vol=gus_wave_volume = gus_pcm_volume = 100;
+ return ics2101_mixer_init (mem_start);
+
+ case CS4231:
+ /* Available soon */
+ default:
+ return gus_default_mixer_init (mem_start);
+ }
+
return mem_start;
}
@@ -2371,7 +3353,9 @@ do_loop_irq (int voice)
gus_select_voice (voice);
tmp = gus_read8 (0x00);
- tmp &= ~0x20; /* Disable wave IRQ for this_one voice */
+ tmp &= ~0x20; /*
+ * Disable wave IRQ for this_one voice
+ */
gus_write8 (0x00, tmp);
mode = voices[voice].loop_irq_mode;
@@ -2381,23 +3365,33 @@ do_loop_irq (int voice)
switch (mode)
{
- case LMODE_FINISH: /* Final loop finished, shoot volume down */
+ case LMODE_FINISH: /*
+ * Final loop finished, shoot volume down
+ */
- if ((gus_read16 (0x09) >> 4) < 100) /* Get current volume */
+ if ((gus_read16 (0x09) >> 4) < 100) /*
+ * Get current volume
+ */
{
gus_voice_off ();
gus_rampoff ();
gus_voice_init (voice);
- return;
+ break;
}
gus_ramp_range (65, 4065);
- gus_ramp_rate (0, 63); /* Fastest possible rate */
- gus_rampon (0x20 | 0x40); /* Ramp down, once, irq */
+ 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; /* Requires extensive processing */
+ pcm_active = 0; /*
+ * Requires extensive processing
+ */
case LMODE_PCM:
{
int orig_qlen = pcm_qlen;
@@ -2409,7 +3403,9 @@ do_loop_irq (int voice)
play_next_pcm_block ();
}
else
- { /* Out of data. Just stop the voice */
+ { /*
+ * Out of data. Just stop the voice
+ */
gus_voice_off ();
gus_rampoff ();
pcm_active = 0;
@@ -2417,7 +3413,7 @@ do_loop_irq (int voice)
if (orig_qlen == pcm_nblk)
{
- DMAbuf_outputintr (gus_devnum);
+ DMAbuf_outputintr (gus_devnum, 0);
}
}
break;
@@ -2439,7 +3435,9 @@ do_volume_irq (int voice)
gus_select_voice (voice);
tmp = gus_read8 (0x0d);
- tmp &= ~0x20; /* Disable volume ramp IRQ */
+ tmp &= ~0x20; /*
+ * Disable volume ramp IRQ
+ */
gus_write8 (0x0d, tmp);
mode = voices[voice].volume_irq_mode;
@@ -2448,19 +3446,35 @@ do_volume_irq (int voice)
switch (mode)
{
- case VMODE_HALT: /* Decay phase finished */
+ 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, 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:;
}
-
- RESTORE_INTR (flags);
}
void
@@ -2473,24 +3487,38 @@ gus_voice_irq (void)
while (1)
{
- src = gus_read8 (0x0f); /* Get source info */
+ src = gus_read8 (0x0f); /*
+ * Get source info
+ */
voice = src & 0x1f;
src &= 0xc0;
if (src == (0x80 | 0x40))
- return; /* No interrupt */
+ return; /*
+ * No interrupt
+ */
voice_bit = 1 << voice;
- if (!(src & 0x80)) /* Wave IRQ pending */
- if (!(wave_ignore & voice_bit) && voice < nr_voices) /* Not done yet */
+ if (!(src & 0x80)) /*
+ * Wave IRQ pending
+ */
+ if (!(wave_ignore & voice_bit) && 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) && voice < nr_voices) /* Not done yet */
+ if (!(src & 0x40)) /*
+ * Volume IRQ pending
+ */
+ if (!(volume_ignore & voice_bit) && voice < nr_voices) /*
+ * Not done
+ * yet
+ */
{
volume_ignore |= voice_bit;
do_volume_irq (voice);
@@ -2503,13 +3531,17 @@ guswave_dma_irq (void)
{
unsigned char status;
- status = gus_look8 (0x41); /* Get DMA IRQ Status */
- if (status & 0x40) /* DMA Irq pending */
+ status = gus_look8 (0x41); /*
+ * Get DMA IRQ Status
+ */
+ if (status & 0x40) /*
+ * DMA Irq pending
+ */
switch (active_device)
{
case GUS_DEV_WAVE:
- if (dram_sleep_flag)
- WAKE_UP (dram_sleeper);
+ if (SOMEONE_WAITING (dram_sleeper, dram_sleep_flag))
+ WAKE_UP (dram_sleeper, dram_sleep_flag);
break;
case GUS_DEV_PCM_CONTINUE:
@@ -2521,15 +3553,19 @@ guswave_dma_irq (void)
case GUS_DEV_PCM_DONE:
if (pcm_qlen < pcm_nblk)
{
- DMAbuf_outputintr (gus_devnum);
+ DMAbuf_outputintr (gus_devnum, pcm_qlen == 0);
}
break;
default:;
}
- status = gus_look8 (0x49); /* Get Sampling IRQ Status */
- if (status & 0x40) /* Sampling Irq pending */
+ status = gus_look8 (0x49); /*
+ * Get Sampling IRQ Status
+ */
+ if (status & 0x40) /*
+ * Sampling Irq pending
+ */
{
DMAbuf_inputintr (gus_devnum);
}
diff --git a/sys/i386/isa/sound/ics2101.c b/sys/i386/isa/sound/ics2101.c
new file mode 100644
index 000000000000..0e54c608de36
--- /dev/null
+++ b/sys/i386/isa/sound/ics2101.c
@@ -0,0 +1,265 @@
+/*
+ * 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)
+
+#ifdef __FreeBSD__
+#include <machine/ultrasound.h>
+#else
+#include "ultrasound.h"
+#endif
+#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_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/i386/isa/sound/local.h b/sys/i386/isa/sound/local.h
index f81bd7821673..36092e37d978 100644
--- a/sys/i386/isa/sound/local.h
+++ b/sys/i386/isa/sound/local.h
@@ -1,10 +1,15 @@
-/* These few lines are used by FreeBSD (only??). */
-
+/* for FreeBSD */
#include "snd.h"
#if NSND > 0
-#define CONFIGURE_SOUNDCARD
+#define KERNEL_SOUNDCARD
#endif
-#define DSP_BUFFSIZE 32768
+#define DSP_BUFFSIZE 65536
+#define NO_AUTODMA /* still */
#define SELECTED_SOUND_OPTIONS 0xffffffff
+#define SOUND_VERSION_STRING "2.5"
+#define SOUND_CONFIG_DATE "Sat Apr 23 07:45:17 MSD 1994"
+#define SOUND_CONFIG_BY "ache"
+#define SOUND_CONFIG_HOST "dream.demos.su"
+#define SOUND_CONFIG_DOMAIN ""
diff --git a/sys/i386/isa/sound/midi.c b/sys/i386/isa/sound/midi.c
index 8d604a80abc2..6ea51b061b9c 100644
--- a/sys/i386/isa/sound/midi.c
+++ b/sys/i386/isa/sound/midi.c
@@ -31,24 +31,27 @@
#ifndef EXCLUDE_CHIP_MIDI
-static int generic_midi_busy[MAX_MIDI_DEV];
+static int generic_midi_busy[MAX_MIDI_DEV];
-long CMIDI_init (long mem_start)
+long
+CMIDI_init (long mem_start)
{
-
- int i;
- int n = num_midi_drivers;
- /* int n = sizeof (midi_supported) / sizeof( struct generic_midi_info );
- */
- for (i = 0; i < n; i++)
- {
- if ( midi_supported[i].attach (mem_start) )
+
+ int i;
+ int n = num_midi_drivers;
+
+ /*
+ * int n = sizeof (midi_supported) / sizeof( struct generic_midi_info );
+ */
+ for (i = 0; i < n; i++)
{
- printk("MIDI: Successfully attached %s\n",midi_supported[i].name);
- }
+ if (midi_supported[i].attach (mem_start))
+ {
+ printk ("MIDI: Successfully attached %s\n", midi_supported[i].name);
+ }
- }
- return (mem_start);
+ }
+ return (mem_start);
}
@@ -56,142 +59,143 @@ int
CMIDI_open (int dev, struct fileinfo *file)
{
- int mode, err, retval;
+ int mode, err, retval;
- dev = dev >> 4;
+ dev = dev >> 4;
- mode = file->mode & O_ACCMODE;
+ mode = file->mode & O_ACCMODE;
- if (generic_midi_busy[dev])
- return (RET_ERROR(EBUSY));
+ if (generic_midi_busy[dev])
+ return (RET_ERROR (EBUSY));
-
- if (dev >= num_generic_midis)
- {
- printk(" MIDI device %d not installed.\n", dev);
- return (ENXIO);
- }
- if (!generic_midi_devs[dev])
- {
- printk(" MIDI device %d not initialized\n",dev);
+ if (dev >= num_generic_midis)
+ {
+ printk (" MIDI device %d not installed.\n", dev);
return (ENXIO);
- }
+ }
+
+ if (!generic_midi_devs[dev])
+ {
+ printk (" MIDI device %d not initialized\n", dev);
+ return (ENXIO);
+ }
+
+ /* If all good and healthy, go ahead and issue call! */
- /* If all good and healthy, go ahead and issue call! */
-
- retval = generic_midi_devs[dev]->open (dev, mode) ;
+ retval = generic_midi_devs[dev]->open (dev, mode);
- /* If everything ok, set device as busy */
+ /* If everything ok, set device as busy */
- if ( retval >= 0 )
- generic_midi_busy[dev] = 1;
-
- return ( retval );
+ if (retval >= 0)
+ generic_midi_busy[dev] = 1;
+
+ return (retval);
}
-int
-CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+int
+CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
{
- int retval;
-
- dev = dev >> 4;
+ int retval;
+
+ dev = dev >> 4;
- if (dev >= num_generic_midis)
- {
- printk(" MIDI device %d not installed.\n", dev);
- return (ENXIO);
- }
+ if (dev >= num_generic_midis)
+ {
+ printk (" MIDI device %d not installed.\n", dev);
+ return (ENXIO);
+ }
- /* Make double sure of healthiness -- doubt
- * Need we check this again??
- *
- */
+ /*
+ * Make double sure of healthiness -- doubt Need we check this again??
+ *
+ */
- if (!generic_midi_devs[dev])
- {
- printk(" MIDI device %d not initialized\n",dev);
+ if (!generic_midi_devs[dev])
+ {
+ printk (" MIDI device %d not initialized\n", dev);
return (ENXIO);
- }
+ }
- /* If all good and healthy, go ahead and issue call! */
+ /* If all good and healthy, go ahead and issue call! */
-
- retval = generic_midi_devs[dev]->write (dev, buf);
- return ( retval );
+ retval = generic_midi_devs[dev]->write (dev, buf);
-}
+ return (retval);
+
+}
int
-CMIDI_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count)
+CMIDI_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
{
- int retval;
-
- dev = dev >> 4;
-
- if (dev >= num_generic_midis)
- {
- printk(" MIDI device %d not installed.\n", dev);
- return (ENXIO);
- }
-
- /* Make double sure of healthiness -- doubt
- * Need we check this again??
- *
- */
-
- if (!generic_midi_devs[dev])
- {
- printk(" MIDI device %d not initialized\n",dev);
+ int retval;
+
+ dev = dev >> 4;
+
+ if (dev >= num_generic_midis)
+ {
+ printk (" MIDI device %d not installed.\n", dev);
return (ENXIO);
- }
+ }
- /* If all good and healthy, go ahead and issue call! */
+ /*
+ * Make double sure of healthiness -- doubt Need we check this again??
+ *
+ */
+
+ if (!generic_midi_devs[dev])
+ {
+ printk (" MIDI device %d not initialized\n", dev);
+ return (ENXIO);
+ }
+
+ /* If all good and healthy, go ahead and issue call! */
-
- retval = generic_midi_devs[dev]->read(dev,buf);
- return (retval);
+ retval = generic_midi_devs[dev]->read (dev, buf);
+
+ return (retval);
+
+}
-}
-
int
CMIDI_close (int dev, struct fileinfo *file)
{
- int retval;
- dev = dev >> 4;
-
- if (dev >= num_generic_midis)
- {
- printk(" MIDI device %d not installed.\n", dev);
- return (ENXIO);
- }
-
- /* Make double sure of healthiness -- doubt
- * Need we check this again??
- *
- */
-
- if (!generic_midi_devs[dev])
- {
- printk(" MIDI device %d not initialized\n",dev);
+ int retval;
+
+ dev = dev >> 4;
+
+ if (dev >= num_generic_midis)
+ {
+ printk (" MIDI device %d not installed.\n", dev);
+ return (ENXIO);
+ }
+
+ /*
+ * Make double sure of healthiness -- doubt Need we check this again??
+ *
+ */
+
+ if (!generic_midi_devs[dev])
+ {
+ printk (" MIDI device %d not initialized\n", dev);
return (ENXIO);
- }
+ }
- /* If all good and healthy, go ahead and issue call! */
+ /* If all good and healthy, go ahead and issue call! */
- generic_midi_devs[dev]->close(dev);
+ generic_midi_devs[dev]->close (dev);
- generic_midi_busy[dev] = 0; /* Free the device */
+ generic_midi_busy[dev] = 0; /* Free the device */
- return (0) ;
+ return (0);
}
diff --git a/sys/i386/isa/sound/midibuf.c b/sys/i386/isa/sound/midibuf.c
index 0196f84876bf..7dadb3f8fa74 100644
--- a/sys/i386/isa/sound/midibuf.c
+++ b/sys/i386/isa/sound/midibuf.c
@@ -1,12 +1,12 @@
/*
- * linux/kernel/chr_drv/sound/midibuf.c
- *
+ * sound/midibuf.c
+ *
* Device file manager for /dev/midi
- *
+ *
* NOTE! This part of the driver is currently just a stub.
- *
+ *
* 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
@@ -14,7 +14,7 @@
* 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
@@ -26,7 +26,7 @@
* 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"
diff --git a/sys/i386/isa/sound/mpu401.c b/sys/i386/isa/sound/mpu401.c
index e8c011bc7940..38ba486b142b 100644
--- a/sys/i386/isa/sound/mpu401.c
+++ b/sys/i386/isa/sound/mpu401.c
@@ -1,12 +1,12 @@
/*
- * linux/kernel/chr_drv/sound/mpu401.c
- *
+ * sound/mpu401.c
+ *
* The low level driver for Roland MPU-401 compatible Midi cards.
- *
+ *
* This version supports just the DUMB UART mode.
- *
+ *
* 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
@@ -14,7 +14,7 @@
* 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
@@ -26,7 +26,7 @@
* 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"
@@ -61,102 +61,16 @@ static int my_dev;
static int reset_mpu401 (void);
static void (*midi_input_intr) (int dev, unsigned char data);
-static void
-mpu401_input_loop (void)
-{
- int count;
-
- count = 10;
-
- while (count) /* Not timed out */
- if (input_avail ())
- {
- unsigned char c = mpu401_read ();
-
- count = 100;
-
- if (mpu401_opened & OPEN_READ)
- midi_input_intr (my_dev, c);
- }
- else
- while (!input_avail () && count)
- count--;
-}
-
void
mpuintr (int unit)
{
- if (input_avail ())
- mpu401_input_loop ();
-}
-
-/*
- * It looks like there is no input interrupts in the UART mode. Let's try
- * polling.
- */
-
-/* XXX WARNING: FreeBSD doesn't seem to have timer_lists like this,
- * so until I figure out how to do the analogous thing in FreeBSD, I'm
- * not all that sure WHAT this driver will do! It might work, depending
- * on the condition taken, but then again it might not! -jkh
- * XXX WARNING
- */
-static void
-poll_mpu401 (unsigned long dummy)
-{
- unsigned long flags;
-
-#ifdef linux
- static struct timer_list mpu401_timer =
- {NULL, 0, 0, poll_mpu401};
-#endif
-
- if (!(mpu401_opened & OPEN_READ))
- return; /* No longer required */
-
- DISABLE_INTR (flags);
-
- if (input_avail ())
- mpu401_input_loop ();
-
-#ifdef linux
- mpu401_timer.expires = 1;
- add_timer (&mpu401_timer); /* Come back later */
-#endif
-
- RESTORE_INTR (flags);
-}
-
-static int
-set_mpu401_irq (int interrupt_level)
-{
- int retcode = EINVAL;
-
-#ifdef linux
- struct sigaction sa;
-
- sa.sa_handler = mpuintr;
-
-#ifdef SND_SA_INTERRUPT
- sa.sa_flags = SA_INTERRUPT;
-#else
- sa.sa_flags = 0;
-#endif
-
- sa.sa_mask = 0;
- sa.sa_restorer = NULL;
-
- retcode = irqaction (interrupt_level, &sa);
-
- if (retcode < 0)
+ while (input_avail ())
{
- printk ("MPU-401: IRQ%d already in use\n", interrupt_level);
- }
+ unsigned char c = mpu401_read ();
-#else
- /* # error Unimplemented for this OS */
-#endif
- return retcode;
+ if (mpu401_opened & OPEN_READ)
+ midi_input_intr (my_dev, c);
+ }
}
static int
@@ -171,11 +85,10 @@ mpu401_open (int dev, int mode,
return RET_ERROR (EBUSY);
}
- mpu401_input_loop ();
+ mpuintr (0);
midi_input_intr = input;
mpu401_opened = mode;
- poll_mpu401 (0); /* Enable input polling */
return 0;
}
@@ -199,7 +112,7 @@ mpu401_out (int dev, unsigned char midi_byte)
DISABLE_INTR (flags);
if (input_avail ())
- mpu401_input_loop ();
+ mpuintr (0);
RESTORE_INTR (flags);
@@ -257,7 +170,7 @@ mpu401_buffer_status (int dev)
static struct midi_operations mpu401_operations =
{
- {"MPU-401", 0},
+ {"MPU-401", 0, 0, SNDCARD_MPU401},
mpu401_open,
mpu401_close,
mpu401_ioctl,
@@ -294,12 +207,14 @@ attach_mpu401 (long mem_start, struct address_info *hw_config)
RESTORE_INTR (flags);
+#ifdef __FreeBSD__
printk ("snd5: <Roland MPU-401>");
+#else
+ printk (" <Roland MPU-401>");
+#endif
my_dev = num_midis;
-#ifdef linux
mpu401_dev = num_midis;
-#endif
midi_devs[num_midis++] = &mpu401_operations;
return mem_start;
}
@@ -311,7 +226,7 @@ reset_mpu401 (void)
int ok, timeout, n;
/*
- * Send the RESET command. Try twice if no success at the first time.
+ * Send the RESET command. Try again if no success at the first time.
*/
ok = 0;
@@ -337,7 +252,7 @@ reset_mpu401 (void)
mpu401_opened = 0;
if (ok)
- mpu401_input_loop (); /* Flush input before enabling interrupts */
+ mpuintr (0); /* Flush input before enabling interrupts */
RESTORE_INTR (flags);
@@ -353,7 +268,7 @@ probe_mpu401 (struct address_info *hw_config)
mpu401_base = hw_config->io_base;
mpu401_irq = hw_config->irq;
- if (set_mpu401_irq (mpu401_irq) < 0)
+ if (snd_set_irq_handler (mpu401_irq, mpuintr) < 0)
return 0;
ok = reset_mpu401 ();
diff --git a/sys/i386/isa/sound/opl3.c b/sys/i386/isa/sound/opl3.c
index 2dbed1f96e00..6e3dccab8c43 100644
--- a/sys/i386/isa/sound/opl3.c
+++ b/sys/i386/isa/sound/opl3.c
@@ -1,10 +1,10 @@
/*
- * linux/kernel/chr_drv/sound/opl3.c
- *
+ * 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
@@ -12,7 +12,7 @@
* 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
@@ -24,7 +24,7 @@
* 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, */
@@ -38,7 +38,7 @@
#define MAX_VOICE 18
#define OFFS_4OP 11 /* Definitions for the operators OP3 and OP4
- * begin here */
+ * begin here */
static int opl3_enabled = 0;
static int left_address = 0x388, right_address = 0x388, both_address = 0;
@@ -59,8 +59,7 @@ struct voice_info
static struct voice_info voices[MAX_VOICE];
-typedef struct sbi_instrument instr_array[SBFM_MAXINSTR];
-static instr_array instrmap;
+static struct sbi_instrument *instrmap;
static struct sbi_instrument *active_instrument[MAX_VOICE] =
{NULL};
@@ -71,17 +70,20 @@ 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 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, const unsigned char addr, const unsigned char val);
+static void opl3_command (int io_addr, unsigned int addr, unsigned int val);
static int opl3_kill_note (int dev, int voice, 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;
@@ -112,7 +114,7 @@ enter_4op_mode (void)
for (i = 0; i < 12; i++)
logical_voices[i] = voices_4op[i];
- nr_voices = 6;
+ nr_voices = 12;
}
static int
@@ -140,7 +142,7 @@ opl3_ioctl (int dev,
break;
case SNDCTL_SYNTH_INFO:
- fm_info.nr_voices = nr_voices;
+ fm_info.nr_voices = (nr_voices == 12) ? 6 : nr_voices;
IOCTL_TO_USER ((char *) arg, 0, &fm_info, sizeof (fm_info));
return 0;
@@ -169,10 +171,10 @@ 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.
*/
@@ -184,6 +186,9 @@ opl3_detect (int ioaddr)
return 0; /* Do avoid duplicate initializations */
}
+ if (opl3_enabled)
+ ioaddr = left_address;
+
opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); /* Reset timers 1 and 2 */
opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); /* Reset the IRQ of FM
* chicp */
@@ -192,7 +197,7 @@ opl3_detect (int ioaddr)
if ((stat1 & 0xE0) != 0x00)
{
- return 0; /* Should be 0x00 */
+ return 0; /* Should be 0x00 */
}
opl3_command (ioaddr, TIMER1_REGISTER, 0xff); /* Set timer 1 to 0xff */
@@ -270,7 +275,7 @@ 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%04x\n", instr->key);
+ printk ("FM warning: Invalid patch format field (key) 0x%x\n", instr->key);
memcpy ((char *) &(instrmap[instr_no]), (char *) instr, sizeof (*instr));
return 0;
@@ -362,7 +367,7 @@ set_voice_volume (int voice, int volume)
vol2 = instr->operators[3];
if ((instr->operators[10] & 0x01))
- { /* Additive synthesis */
+ { /* Additive synthesis */
calc_vol (&vol1, volume);
calc_vol (&vol2, volume);
}
@@ -616,7 +621,7 @@ freq_to_fnum (int freq, int *block, int *fnum)
}
static void
-opl3_command (int io_addr, const unsigned char addr, const unsigned char val)
+opl3_command (int io_addr, unsigned int addr, unsigned int val)
{
int i;
@@ -625,7 +630,7 @@ opl3_command (int io_addr, const unsigned char addr, const unsigned char val)
* register. The OPL-3 survives with just two INBs
*/
- OUTB (addr, io_addr); /* Select register */
+ OUTB ((unsigned char) (addr & 0xff), io_addr); /* Select register */
if (!opl3_enabled)
tenmicrosec ();
@@ -633,7 +638,7 @@ opl3_command (int io_addr, const unsigned char addr, const unsigned char val)
for (i = 0; i < 2; i++)
INB (io_addr);
- OUTB (val, io_addr + 1); /* Write to register */
+ OUTB ((unsigned char) (val & 0xff), io_addr + 1); /* Write to register */
if (!opl3_enabled)
{
@@ -745,6 +750,11 @@ 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) \
@@ -850,7 +860,7 @@ opl3_controller (int dev, int voice, int ctrl_num, int value)
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 */
+ * of f-num */
voices[voice].keyon_byte = data;
opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
break;
@@ -884,6 +894,7 @@ static struct synth_operations opl3_operations =
opl3_aftertouch,
opl3_controller,
opl3_panning,
+ opl3_volume_method,
opl3_patchmgr
};
@@ -892,17 +903,26 @@ opl3_init (long mem_start)
{
int i;
+ PERMANENT_MALLOC (struct sbi_instrument *, instrmap,
+ SBFM_MAXINSTR * sizeof (*instrmap), mem_start);
+
synth_devs[num_synths++] = &opl3_operations;
fm_model = 0;
opl3_ok = 1;
if (opl3_enabled)
{
+#ifdef __FreeBSD__
printk ("snd1: <Yamaha OPL-3 FM>");
+#else
+ printk (" <Yamaha OPL-3 FM>");
+#endif
fm_model = 2;
nr_voices = 18;
fm_info.nr_drums = 0;
fm_info.capabilities |= SYNTH_CAP_OPL3;
+#ifndef SCO
strcpy (fm_info.name, "Yamaha OPL-3");
+#endif
for (i = 0; i < 18; i++)
if (physical_voices[i].ioaddr == USE_LEFT)
@@ -917,7 +937,11 @@ opl3_init (long mem_start)
}
else
{
+#ifdef __FreeBSD__
printk ("snd1: <Yamaha 2-OP FM>");
+#else
+ printk (" <Yamaha 2-OP FM>");
+#endif
fm_model = 1;
nr_voices = 9;
fm_info.nr_drums = 0;
diff --git a/sys/i386/isa/sound/os.h b/sys/i386/isa/sound/os.h
index fba20980c3f0..c6b688ac6aa9 100644
--- a/sys/i386/isa/sound/os.h
+++ b/sys/i386/isa/sound/os.h
@@ -3,7 +3,7 @@
/*
* OS specific settings for FreeBSD
*
- * Copyright by Hannu Savolainen 1993
+ * Copyright by UWM - comments to soft-eng@cs.uwm.edu
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * This should be used as an example when porting the driver to a new
+ * 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).
@@ -44,7 +44,6 @@
#include "param.h"
#include "systm.h"
-#include "kernel.h"
#include "ioctl.h"
#include "tty.h"
#include "proc.h"
@@ -52,6 +51,7 @@
#include "conf.h"
#include "file.h"
#include "uio.h"
+#include "kernel.h"
#include "syslog.h"
#include "errno.h"
#include "malloc.h"
@@ -63,10 +63,6 @@
*/
#ifdef CONFIGURE_SOUNDCARD
-/* lbolt is required by the FreeBSD version (only???) */
-extern int __timeout_val;
-extern int __process_aborting;
-
/*
* select() is currently implemented in Linux specific way. Don't enable.
* I don't remember what the SHORT_BANNERS means so forget it.
@@ -163,14 +159,25 @@ typedef struct uio snd_rw_buf;
* 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 int flag = 0
+#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 int flag = {0}
+#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
@@ -179,34 +186,28 @@ typedef struct uio snd_rw_buf;
* 1 or 0 could be returned (1 should be better than 0).
* I'm not sure if the following is correct for FreeBSD.
*/
-#define PROCESS_ABORTING (__process_aborting | curproc->p_sig)
-/*
- * REQUEST_TIMEOUT is called before sleep. It shoud ensure that the
- * process is woken up after given number of ticks (1/HZ secs.).
- * The wqueue gives the wait queue.
- */
-#define REQUEST_TIMEOUT(nticks, wqueue) __timeout_val = nticks;
+#define PROCESS_ABORTING(q, f) (f.aborting | curproc->p_sig)
/*
* The following macro calls sleep. It should be implemented such that
* the process is resumed if it receives a signal. The following is propably
* not the way how it should be done on 386bsd.
- * The on_what parameter is a wait_queue defined with DEFINE_WAIT_QUEUE()
- * The second parameter is a flag. It must be initialized to 1 before sleep
- * and to zero after proces continues.
+ * The on_what 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 INTERRUPTIBLE_SLEEP_ON(on_what, flag) \
+#define DO_SLEEP(q, f, time_limit) \
{ \
- flag = 1; \
- flag=tsleep((caddr_t)&(on_what), (PRIBIO-5)|PCATCH, "sndint", __timeout_val); \
- if(flag == ERESTART) __process_aborting = 1;\
- else __process_aborting = 0;\
- __timeout_val = 0; \
- flag = 0; \
+ int flag, chn; \
+ f.mode = WK_SLEEP; \
+ q = &chn; \
+ flag=tsleep((caddr_t)&(chn), (PRIBIO-5)|PCATCH, "sndint", time_limit); \
+ if(flag == ERESTART) f.aborting = 1;\
+ else f.aborting = 0;\
+ f.mode &= ~WK_SLEEP; \
}
-
/* An the following wakes up a process */
-#define WAKE_UP(who) wakeup((caddr_t)&(who))
+#define WAKE_UP(q, f) {f.mode = WK_WAKEUP;wakeup((caddr_t)q);}
/*
* Timing macros. This driver assumes that there is a timer running in the
@@ -215,6 +216,7 @@ typedef struct uio snd_rw_buf;
*/
#ifndef HZ
+extern int hz;
#define HZ hz
#endif
@@ -223,9 +225,9 @@ typedef struct uio snd_rw_buf;
* ticks. This can overflow, so the timeout might be real big...
*
*/
+extern unsigned long get_time(void);
#define GET_TIME() get_time()
-extern long get_time(void);
-/*#define GET_TIME() (lbolt)*/ /* Returns current time (1/HZ secs since boot) */
+/*#define GET_TIME() (lbolt) */ /* Returns current time (1/HZ secs since boot) */
/*
* The following three macros are called before and after atomic
@@ -248,7 +250,11 @@ extern long get_time(void);
*/
#define INB inb
-#define OUTB(addr, data) outb(data, addr)
+/*
+ * The outb(0, 0x80) is just for slowdown. It's bit unsafe since
+ * this address could be used for something usefull.
+ */
+#define OUTB(addr, data) {outb(data, addr);outb(0, 0x80);}
/* memcpy() was not defined og 386bsd. Lets define it here */
#define memcpy(d, s, c) bcopy(s, d, c)
@@ -274,6 +280,32 @@ extern long get_time(void);
#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_TEMP, M_WAITOK)
+
+/*
+ * 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
*/
diff --git a/sys/i386/isa/sound/pas.h b/sys/i386/isa/sound/pas.h
index 4dadea3713b8..55d83737b609 100644
--- a/sys/i386/isa/sound/pas.h
+++ b/sys/i386/isa/sound/pas.h
@@ -128,24 +128,26 @@
#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 CHIP_REV 0xFF88 /* R 0=PAS, 1=PAS+, 2=CDPC, 3=PAS16C, 4=PAS16D */
#define PAS_NONE 0
#define PAS_PLUS 1
#define PAS_CDPC 2
#define PAS_16 3
+#define PAS_16D 4
#ifdef DEFINE_TRANSLATIONS
char I_C_2_PCM_DMA_translate[] = /* R W PCM PCM DMA channel value translations */
{ 4, 1, 2, 3, 0, 5, 6, 7 };
char I_C_3_PCM_IRQ_translate[] = /* R W PCM PCM IRQ level value translation */
- { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 };
+ { 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 };
char E_C_MPU401_IRQ_translate[] = /* R W MIDI MPU401 emulation IRQ value translation */
- { 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x05, 0x06, 0x07 };
+ { 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01, 0x05, 0x06, 0x07 };
char E_C_SB_IRQ_translate[] = /* R W PCM SB emulation IRQ translate */
- { 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x00, 0x28, 0x30, 0x38 };
+ { 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 };
char E_C_SB_DMA_translate[] = /* R W PCM SB emulation DMA translate */
- { 0x00, 0x40, 0x80, 0xC0 };
+ { 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 };
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, 3, 0, 2, 3 };
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, 0, 2, 3 };
#else
extern char I_C_2_PCM_DMA_translate[]; /* R W PCM PCM DMA channel value translations */
extern char I_C_3_PCM_IRQ_translate[]; /* R W PCM PCM IRQ level value translation */
diff --git a/sys/i386/isa/sound/pas2_card.c b/sys/i386/isa/sound/pas2_card.c
index 96fe651fbaac..a8b79df2c8cd 100644
--- a/sys/i386/isa/sound/pas2_card.c
+++ b/sys/i386/isa/sound/pas2_card.c
@@ -1,12 +1,12 @@
#define _PAS2_CARD_C_
#define SND_SA_INTERRUPT
/*
- * linux/kernel/chr_drv/sound/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
@@ -14,7 +14,7 @@
* 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
@@ -26,7 +26,7 @@
* 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"
@@ -46,8 +46,9 @@ static int pas_intr_mask = 0;
static int pas_irq = 0;
static char pas_model;
+static unsigned char board_rev_id;
static char *pas_model_names[] =
-{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16"};
+{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16", "Pro AudioSpectrum 16D"};
/* pas_read() and pas_write() are equivalents of INB() and OUTB() */
/* These routines perform the I/O address translation required */
@@ -65,6 +66,23 @@ pas_write (unsigned char data, int ioaddr)
OUTB (data, ioaddr ^ translat_code);
}
+/*
+ * 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.
+ */
+
+void
+mix_write (unsigned char data, int ioaddr)
+{
+ if (pas_model == PAS_16D) {
+ outw ((ioaddr ^ translat_code) - 1, data | (data << 8));
+ outb (0, 0x80);
+ } else
+ OUTB (data, ioaddr ^ translat_code);
+}
+
void
pas2_msg (char *foo)
{
@@ -100,39 +118,6 @@ pasintr (int unused)
}
-static int
-set_pas_irq (int interrupt_level)
-{
-#ifdef linux
- int retcode;
- struct sigaction sa;
-
- pas_write (0xff, INTERRUPT_STATUS); /* Reset pending interrupts */
-
- sa.sa_handler = pasintr;
-
-#ifdef SND_SA_INTERRUPT
- sa.sa_flags = SA_INTERRUPT;
-#else
- sa.sa_flags = 0;
-#endif
-
- sa.sa_mask = 0;
- sa.sa_restorer = NULL;
-
- retcode = irqaction (interrupt_level, &sa);
-
- if (retcode < 0)
- {
- printk ("ProAudioSpectrum: IRQ%d already in use\n", interrupt_level);
- }
- return retcode;
-#else
- /* # error This routine does not work with this OS */
- return EINVAL;
-#endif
-}
-
int
pas_set_intr (int mask)
{
@@ -143,7 +128,7 @@ pas_set_intr (int mask)
if (!pas_intr_mask)
{
- if ((err = set_pas_irq (pas_irq)) < 0)
+ if ((err = snd_set_irq_handler (pas_irq, pasintr)) < 0)
return err;
}
pas_intr_mask |= mask;
@@ -163,7 +148,7 @@ pas_remove_intr (int mask)
if (!pas_intr_mask)
{
- RELEASE_IRQ (pas_irq);
+ snd_release_irq (pas_irq);
}
return 0;
}
@@ -230,36 +215,66 @@ config_pas_hw (struct address_info *hw_config)
}
}
+ /*
+ * 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(S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2); Don't do this */
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)
+ if (pas_model == PAS_16 || pas_model == PAS_16D)
pas_write (8, PRESCALE_DIVIDER);
else
pas_write (0, PRESCALE_DIVIDER);
- pas_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER);
- pas_write (5, PARALLEL_MIXER);
+ mix_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER);
+ mix_write (5, PARALLEL_MIXER);
#if !defined(EXCLUDE_SB_EMULATION) || !defined(EXCLUDE_SB)
- /* Turn on Sound Blaster compatibility */
- /* bit 1 = SB emulation */
- /* bit 0 = MPU401 emulation (CDPC only :-( ) */
- pas_write (0x02, COMPATIBILITY_ENABLE);
+ {
+ struct address_info *sb_config;
+
+ if ((sb_config = sound_getconf (SNDCARD_SB)))
+ {
+ unsigned char irq_dma;
- /* "Emulation address" */
- pas_write ((SBC_BASE >> 4) & 0x0f, EMULATION_ADDRESS);
+ /* 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)
@@ -305,7 +320,7 @@ detect_pas_hw (struct address_info *hw_config)
if (board_id != foo) /* Not a PAS2 */
return 0;
- if ((pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f]));
+ pas_model = pas_read (CHIP_REV);
return pas_model;
}
@@ -318,9 +333,14 @@ attach_pas_card (long mem_start, struct address_info *hw_config)
if (detect_pas_hw (hw_config))
{
- if ((pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f]))
+ board_rev_id = pas_read (BOARD_REV_ID);
+ if (pas_model = pas_read (CHIP_REV))
{
- printk ("snd3: <%s rev %d>", pas_model_names[(int) pas_model], pas_read (BOARD_REV_ID));
+#ifdef __FreeBSD__
+ printk ("snd3: <%s rev %d>", pas_model_names[(int) pas_model], board_rev_id);
+#else
+ printk (" <%s rev %d>", pas_model_names[(int) pas_model], board_rev_id);
+#endif
}
if (config_pas_hw (hw_config))
@@ -330,11 +350,11 @@ attach_pas_card (long mem_start, struct address_info *hw_config)
mem_start = pas_pcm_init (mem_start, hw_config);
#endif
-# if !defined(EXCLUDE_SB_EMULATION) && !defined(EXCLUDE_SB)
+#if !defined(EXCLUDE_SB_EMULATION) && !defined(EXCLUDE_SB)
sb_dsp_disable_midi (); /* The SB emulation don't support
* midi */
-# endif
+#endif
#ifndef EXCLUDE_YM3812
enable_opl3_mode (0x388, 0x38a, 0);
@@ -350,7 +370,6 @@ attach_pas_card (long mem_start, struct address_info *hw_config)
}
}
- printk ("\n");
return mem_start;
}
diff --git a/sys/i386/isa/sound/pas2_midi.c b/sys/i386/isa/sound/pas2_midi.c
index 1a605905420f..4a07b0b59bf8 100644
--- a/sys/i386/isa/sound/pas2_midi.c
+++ b/sys/i386/isa/sound/pas2_midi.c
@@ -1,10 +1,10 @@
/*
- * linux/kernel/chr_drv/sound/pas2_midi.c
- *
+ * 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
@@ -12,7 +12,7 @@
* 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
@@ -24,7 +24,7 @@
* 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"
@@ -212,7 +212,7 @@ pas_buffer_status (int dev)
static struct midi_operations pas_midi_operations =
{
- {"Pro Audio Spectrum", 0},
+ {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS},
pas_midi_open,
pas_midi_close,
pas_midi_ioctl,
@@ -283,7 +283,7 @@ pas_midi_interrupt (void)
if (stat & M_S_OUTPUT_OVERRUN)
{
- printk ("MIDI output overrun %02x,%02x,%d \n", pas_read (MIDI_FIFO_STATUS), stat, ofifo_bytes);
+ printk ("MIDI output overrun %x,%x,%d \n", pas_read (MIDI_FIFO_STATUS), stat, ofifo_bytes);
ofifo_bytes = 100;
}
diff --git a/sys/i386/isa/sound/pas2_mixer.c b/sys/i386/isa/sound/pas2_mixer.c
index 33135d24e859..b3868773d64c 100644
--- a/sys/i386/isa/sound/pas2_mixer.c
+++ b/sys/i386/isa/sound/pas2_mixer.c
@@ -1,12 +1,12 @@
#define _PAS2_MIXER_C_
/*
- * linux/kernel/chr_drv/sound/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
@@ -14,7 +14,7 @@
* 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
@@ -26,7 +26,7 @@
* 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"
@@ -46,7 +46,7 @@ static int mode_control = 0;
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_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)
@@ -72,14 +72,6 @@ mixer_output (int right_vol, int left_vol, int div, int bits,
int left = left_vol * div / 100;
int right = right_vol * div / 100;
- /*
- * 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 don't need to do this because the call to pas_write more than
- * compensates for the timing problems.
- */
-
if (bits & P_M_MV508_MIXER)
{ /* Select input or output mixer */
left |= mixer;
@@ -87,17 +79,17 @@ mixer_output (int right_vol, int left_vol, int div, int bits,
}
if (bits == P_M_MV508_BASS || bits == P_M_MV508_TREBLE)
- { /* Bass and trebble are mono devices */
- pas_write (P_M_MV508_ADDRESS | bits, PARALLEL_MIXER);
- pas_write (left, PARALLEL_MIXER);
+ { /* Bass and trebble are mono devices */
+ mix_write (P_M_MV508_ADDRESS | bits, PARALLEL_MIXER);
+ mix_write (left, PARALLEL_MIXER);
right_vol = left_vol;
}
else
{
- pas_write (P_M_MV508_ADDRESS | P_M_MV508_LEFT | bits, PARALLEL_MIXER);
- pas_write (left, PARALLEL_MIXER);
- pas_write (P_M_MV508_ADDRESS | P_M_MV508_RIGHT | bits, PARALLEL_MIXER);
- pas_write (right, PARALLEL_MIXER);
+ 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));
@@ -106,8 +98,8 @@ mixer_output (int right_vol, int left_vol, int div, int bits,
void
set_mode (int new_mode)
{
- pas_write (P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER);
- pas_write (new_mode, PARALLEL_MIXER);
+ mix_write (P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER);
+ mix_write (new_mode, PARALLEL_MIXER);
mode_control = new_mode;
}
diff --git a/sys/i386/isa/sound/pas2_pcm.c b/sys/i386/isa/sound/pas2_pcm.c
index 878de5aaca94..b6d53fbccd01 100644
--- a/sys/i386/isa/sound/pas2_pcm.c
+++ b/sys/i386/isa/sound/pas2_pcm.c
@@ -1,11 +1,11 @@
#define _PAS2_PCM_C_
/*
- * linux/kernel/chr_drv/sound/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
@@ -13,7 +13,7 @@
* 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
@@ -25,7 +25,7 @@
* 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"
@@ -75,6 +75,31 @@ pcm_set_speed (int 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);
@@ -148,6 +173,8 @@ pas_pcm_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
break;
case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return pcm_set_channels (arg);
return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg)));
break;
@@ -200,12 +227,6 @@ pas_pcm_open (int dev, int mode)
TRACE (printk ("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode));
- if (mode != OPEN_READ && mode != OPEN_WRITE)
- {
- printk ("PAS2: Attempt to open PCM device for simultaneous read and write");
- return RET_ERROR (EINVAL);
- }
-
if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0)
return err;
@@ -217,10 +238,6 @@ pas_pcm_open (int dev, int mode)
pcm_count = 0;
- pcm_set_bits (8);
- pcm_set_channels (1);
- pcm_set_speed (DSP_DEFAULT_SPEED);
-
return 0;
}
@@ -242,7 +259,8 @@ pas_pcm_close (int dev)
}
static void
-pas_pcm_output_block (int dev, unsigned long buf, int count, int intrflag)
+pas_pcm_output_block (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
{
unsigned long flags, cnt;
@@ -251,7 +269,6 @@ pas_pcm_output_block (int dev, unsigned long buf, int count, int intrflag)
cnt = count;
if (sound_dsp_dmachan[dev] > 3)
cnt >>= 1;
- cnt--;
if (sound_dma_automode[dev] &&
intrflag &&
@@ -263,11 +280,11 @@ pas_pcm_output_block (int dev, unsigned long buf, int count, int intrflag)
pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
PCM_CONTROL);
- DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+ if (restart_dma)
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
if (sound_dsp_dmachan[dev] > 3)
count >>= 1;
- count--;
if (count != pcm_count)
{
@@ -288,7 +305,8 @@ pas_pcm_output_block (int dev, unsigned long buf, int count, int intrflag)
}
static void
-pas_pcm_start_input (int dev, unsigned long buf, int count, int intrflag)
+pas_pcm_start_input (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
{
unsigned long flags;
int cnt;
@@ -298,7 +316,6 @@ pas_pcm_start_input (int dev, unsigned long buf, int count, int intrflag)
cnt = count;
if (sound_dsp_dmachan[dev] > 3)
cnt >>= 1;
- cnt--;
if (sound_dma_automode[my_devnum] &&
intrflag &&
@@ -307,13 +324,12 @@ pas_pcm_start_input (int dev, unsigned long buf, int count, int intrflag)
DISABLE_INTR (flags);
- DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+ if (restart_dma)
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
if (sound_dsp_dmachan[dev] > 3)
count >>= 1;
- count--;
-
if (count != pcm_count)
{
pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
@@ -346,14 +362,15 @@ pas_pcm_prepare_for_output (int dev, int bsize, int bcount)
static struct audio_operations pas_pcm_operations =
{
"Pro Audio Spectrum",
- 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, /* */
+ NOTHING_SPECIAL,
+ 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, /* halt_xfer */
NULL, /* has_output_drained */
NULL /* copy_from_user */
@@ -374,6 +391,7 @@ pas_pcm_init (long mem_start, struct address_info *hw_config)
{
dsp_devs[my_devnum = num_dspdevs++] = &pas_pcm_operations;
sound_dsp_dmachan[my_devnum] = hw_config->dma;
+#ifndef NO_AUTODMA
if (hw_config->dma > 3)
{
sound_buffcounts[my_devnum] = 1;
@@ -386,6 +404,11 @@ pas_pcm_init (long mem_start, struct address_info *hw_config)
sound_buffsizes[my_devnum] = DSP_BUFFSIZE;
sound_dma_automode[my_devnum] = 1;
}
+#else
+ sound_buffcounts[my_devnum] = DSP_BUFFCOUNT;
+ sound_buffsizes[my_devnum] = DSP_BUFFSIZE;
+ sound_dma_automode[my_devnum] = 0;
+#endif
}
else
printk ("PAS2: Too many PCM devices available\n");
@@ -413,7 +436,7 @@ pas_pcm_interrupt (unsigned char status, int cause)
{
case PCM_DAC:
- DMAbuf_outputintr (my_devnum);
+ DMAbuf_outputintr (my_devnum, 1);
break;
case PCM_ADC:
diff --git a/sys/i386/isa/sound/patmgr.c b/sys/i386/isa/sound/patmgr.c
index 140a42845e5b..042d42d4067c 100644
--- a/sys/i386/isa/sound/patmgr.c
+++ b/sys/i386/isa/sound/patmgr.c
@@ -1,10 +1,10 @@
/*
- * linux/kernel/chr_drv/sound/patmgr.c
- *
+ * 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
@@ -12,7 +12,7 @@
* 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
@@ -24,7 +24,7 @@
* 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
@@ -58,6 +58,8 @@ pmgr_open (int dev)
return RET_ERROR (EBUSY);
pmgr_opened[dev] = 1;
+ RESET_WAIT_QUEUE (server_procs[dev], server_wait_flag[dev]);
+
return 0;
}
@@ -71,8 +73,8 @@ pmgr_release (int dev)
mbox[dev]->key = PM_ERROR;
mbox[dev]->parm1 = RET_ERROR (EIO);
- if (appl_wait_flag)
- WAKE_UP (appl_proc);
+ if (SOMEONE_WAITING (appl_proc, appl_wait_flag))
+ WAKE_UP (appl_proc, appl_wait_flag);
}
pmgr_opened[dev] = 0;
@@ -90,13 +92,14 @@ pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
return RET_ERROR (EIO);
}
- while (!ok && !PROCESS_ABORTING)
+ 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)
+ while (!(mbox[dev] && msg_direction[dev] == A_TO_S) &&
+ !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev]))
{
- INTERRUPTIBLE_SLEEP_ON (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)
@@ -158,9 +161,9 @@ pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
COPY_FROM_USER (&((char *) mbox[dev])[4], buf, 4, count - 4);
msg_direction[dev] = S_TO_A;
- if (appl_wait_flag)
+ if (SOMEONE_WAITING (appl_proc, appl_wait_flag))
{
- WAKE_UP (appl_proc);
+ WAKE_UP (appl_proc, appl_wait_flag);
}
}
@@ -185,12 +188,12 @@ pmgr_access (int dev, struct patmgr_info *rec)
mbox[dev] = rec;
msg_direction[dev] = A_TO_S;
- if (server_wait_flag[dev])
+ if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev]))
{
- WAKE_UP (server_procs[dev]);
+ WAKE_UP (server_procs[dev], server_wait_flag[dev]);
}
- INTERRUPTIBLE_SLEEP_ON (appl_proc, appl_wait_flag);
+ DO_SLEEP (appl_proc, appl_wait_flag, 0);
if (msg_direction[dev] != S_TO_A)
{
@@ -239,12 +242,12 @@ pmgr_inform (int dev, int event, unsigned long p1, unsigned long p2,
mbox[dev]->parm3 = p3;
msg_direction[dev] = A_TO_S;
- if (server_wait_flag[dev])
+ if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev]))
{
- WAKE_UP (server_procs[dev]);
+ WAKE_UP (server_procs[dev], server_wait_flag[dev]);
}
- INTERRUPTIBLE_SLEEP_ON (appl_proc, appl_wait_flag);
+ DO_SLEEP (appl_proc, appl_wait_flag, 0);
if (mbox[dev])
KERNEL_FREE (mbox[dev]);
mbox[dev] = NULL;
diff --git a/sys/i386/isa/sound/pro_midi.c b/sys/i386/isa/sound/pro_midi.c
index 606657d403da..b9ffa26a9ab2 100644
--- a/sys/i386/isa/sound/pro_midi.c
+++ b/sys/i386/isa/sound/pro_midi.c
@@ -1,5 +1,5 @@
/*
- * Copyright by UWM -- comments to soft-eng@cs.uwm.edu
+ * Copyright by UWM - comments to soft-eng@cs.uwm.edu
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -37,140 +37,148 @@
/** Structure for handling operations **/
-static struct generic_midi_operations pro_midi_operations = {
+static struct generic_midi_operations pro_midi_operations =
+{
- {"Pro_Audio_Spectrum 16 MV101", 0},
- pro_midi_open,
- pro_midi_close,
- pro_midi_write,
- pro_midi_read
+ {"Pro_Audio_Spectrum 16 MV101", 0},
+ pro_midi_open,
+ pro_midi_close,
+ pro_midi_write,
+ pro_midi_read
};
/*
- * Note! Note! Note!
- * Follow the same model for any other attach function you
+ * Note! Note! Note! Follow the same model for any other attach function you
* may write
*/
-long pro_midi_attach( long mem_start)
+long
+pro_midi_attach (long mem_start)
{
pro_midi_dev = num_generic_midis;
generic_midi_devs[num_generic_midis++] = &pro_midi_operations;
return mem_start;
-}
+}
-int pro_midi_open(int dev, int mode)
+int
+pro_midi_open (int dev, int mode)
{
- int intr_mask, s;
+ int intr_mask, s;
- s = splhigh();
+ s = splhigh ();
- /* Reset the input and output FIFO pointers */
+ /* Reset the input and output FIFO pointers */
- outb(MIDI_CONTROL,M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO);
+ outb (MIDI_CONTROL, M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO);
- /* Get the interrupt status */
+ /* Get the interrupt status */
- intr_mask = inb(INTERRUPT_MASK);
+ intr_mask = inb (INTERRUPT_MASK);
- /* Enable MIDI IRQ */
+ /* Enable MIDI IRQ */
- intr_mask |= I_M_MIDI_IRQ_ENABLE;
- outb(INTERRUPT_MASK, intr_mask);
+ intr_mask |= I_M_MIDI_IRQ_ENABLE;
+ outb (INTERRUPT_MASK, intr_mask);
/* Enable READ/WRITE on MIDI port. This part is quite unsure though */
- outb(MIDI_CONTROL,M_C_ENA_OUTPUT_IRQ | M_C_ENA_INPUT_IRQ);
+ outb (MIDI_CONTROL, M_C_ENA_OUTPUT_IRQ | M_C_ENA_INPUT_IRQ);
/* Acknowledge pending interrupts */
- outb(MIDI_STATUS,0xff);
+ outb (MIDI_STATUS, 0xff);
- splx(s);
+ splx (s);
- return(ESUCCESS);
+ return (ESUCCESS);
}
-void pro_midi_close(int dev)
+void
+pro_midi_close (int dev)
{
- int intr_mask;
+ int intr_mask;
- /* Clean up */
+ /* Clean up */
- outb(MIDI_CONTROL,M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO);
- intr_mask = inb(INTERRUPT_MASK);
- intr_mask &= ~I_M_MIDI_IRQ_ENABLE;
- outb(INTERRUPT_MASK,intr_mask);
+ outb (MIDI_CONTROL, M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO);
+ intr_mask = inb (INTERRUPT_MASK);
+ intr_mask &= ~I_M_MIDI_IRQ_ENABLE;
+ outb (INTERRUPT_MASK, intr_mask);
- return;
+ return;
}
-int pro_midi_write(int dev, struct uio *uio)
+int
+pro_midi_write (int dev, struct uio *uio)
{
- int s;
- unsigned char data;
+ int s;
+ unsigned char data;
- /* printf("midi: Going to do write routine..\n"); */
- while(uio->uio_resid) {
+ /* printf("midi: Going to do write routine..\n"); */
+ while (uio->uio_resid)
+ {
- if ( uiomove(&data,1,uio) ) return(ENOTTY);
+ if (uiomove (&data, 1, uio))
+ return (ENOTTY);
- s = splhigh();
+ s = splhigh ();
- DELAY(30);
- outb(MIDI_DATA,data);
- DELAY(70); /* Ze best pause.. find a better one if
- * you can :)
- */
- splx(s);
- }
+ DELAY (30);
+ outb (MIDI_DATA, data);
+ DELAY (70); /* Ze best pause.. find a better one if you
+ * can :) */
+ splx (s);
+ }
- return(ESUCCESS);
+ return (ESUCCESS);
}
-int pro_midi_read(int dev, struct uio *uio)
+int
+pro_midi_read (int dev, struct uio *uio)
{
- int s;
- unsigned char data;
+ int s;
+ unsigned char data;
- s = splhigh();
+ s = splhigh ();
- /* For each uio_iov[] entry .... */
+ /* For each uio_iov[] entry .... */
- while (uio->uio_resid) {
+ while (uio->uio_resid)
+ {
- if((( inb(MIDI_STATUS) & M_S_INPUT_AVAIL) == 0 ) &&
- ((inb(MIDI_FIFO_STATUS) & MIDI_INPUT_AVAILABLE) == 0 ) )
+ if (((inb (MIDI_STATUS) & M_S_INPUT_AVAIL) == 0) &&
+ ((inb (MIDI_FIFO_STATUS) & MIDI_INPUT_AVAILABLE) == 0))
- data = 0xfe;
- else
- data = inb(MIDI_DATA);
+ data = 0xfe;
+ else
+ data = inb (MIDI_DATA);
- if ( uiomove(&data, 1 , uio)) {
+ if (uiomove (&data, 1, uio))
+ {
- printf("midi: Bad copyout()!\n");
- return(ENOTTY);
+ printf ("midi: Bad copyout()!\n");
+ return (ENOTTY);
- }
+ }
- }
- splx(s);
- return(ESUCCESS);
+ }
+ splx (s);
+ return (ESUCCESS);
}
diff --git a/sys/i386/isa/sound/sb.h b/sys/i386/isa/sound/sb.h
new file mode 100644
index 000000000000..bb8ae12d7e60
--- /dev/null
+++ b/sys/i386/isa/sound/sb.h
@@ -0,0 +1,28 @@
+#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)
+/* 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/i386/isa/sound/sb16_dsp.c b/sys/i386/isa/sound/sb16_dsp.c
new file mode 100644
index 000000000000..b545f8cac222
--- /dev/null
+++ b/sys/i386/isa/sound/sb16_dsp.c
@@ -0,0 +1,627 @@
+/*
+ * 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, sbc_minor, sbc_major;
+
+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);
+int sb_reset_dsp (void);
+
+static struct audio_operations sb16_dsp_operations =
+{
+ "SoundBlaster 16",
+ NOTHING_SPECIAL,
+ 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
+wait_data_avail (unsigned long t)
+{
+ int loopc = 5000000;
+
+ t += GET_TIME ();
+ do
+ {
+ if (INB (DSP_DATA_AVAIL) & 0x80)
+ return 1;
+ }
+ while (--loopc && GET_TIME () < t);
+ printk ("!data_avail l=%d\n", loopc);
+ return 0;
+}
+
+static int
+read_dsp (int t)
+{
+ if (!wait_data_avail ((unsigned long) t))
+ return -1;
+ else
+ return INB (DSP_READ);
+}
+
+static int
+dsp_ini2 (void)
+{
+#if 0
+ /* sb_setmixer(0x83, sb_getmixer(0x83) | 0x03); */
+ sb_dsp_command (0xe2);
+ sb_dsp_command (0x76); /* E0 ??? */
+ sb_dsp_command (0xe2);
+ sb_dsp_command (0x30); /* A0 ??? */
+ sb_dsp_command (0xe4);
+ sb_dsp_command (0xaa);
+ sb_dsp_command (0xe8);
+ if (read_dsp (100) != 0xaa)
+ printk ("Error dsp_ini2\n");
+#endif
+ return 0;
+}
+
+/*
+ static char *dsp_getmessage(unsigned char command,int maxn)
+ {
+ static char buff[100];
+ int n=0;
+
+ sb_dsp_command(command);
+ while(n<maxn && wait_data_avail(2L)) {
+ buff[++n]=INB(DSP_READ);
+ if(!buff[n])
+ break;
+ }
+ buff[0]=n;
+ return buff;
+ }
+
+ static void dsp_showmessage(unsigned char command,int len)
+ {
+ int n;
+ unsigned char *c;
+ c=dsp_getmessage(command,len);
+ printk("DSP C=%x l=%d,lr=%d b=",command,len,c[0]);
+ for(n=1;n<=c[0];n++)
+ if(c[n]>=' ' & c[n]<='z')
+ printk("%c",c[n]);
+ else
+ printk("|%x|",c[n]);
+ printk("\n");
+ }
+ */
+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:
+ return RET_ERROR (EINVAL);
+ }
+ 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_SAMPLESIZE:
+ 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;
+
+ if (ALLOC_DMA_CHN (dma8))
+ {
+ printk ("SB16: Unable to grab DMA%d\n", dma8);
+ sb_free_irq ();
+ return RET_ERROR (EBUSY);
+ }
+
+ if (dma16 != dma8)
+ if (ALLOC_DMA_CHN (dma16))
+ {
+ printk ("SB16: Unable to grab DMA%d\n", dma16);
+ sb_free_irq ();
+ RELEASE_DMA_CHN (dma8);
+ return RET_ERROR (EBUSY);
+ }
+
+ dsp_ini2 ();
+
+ 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 = sound_dsp_dmachan[dev];
+
+ 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 (sound_dma_automode[dev] &&
+ 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));
+ /* sb_dsp_command (0);
+ sb_dsp_command (0); */
+
+ RESTORE_INTR (flags);
+ dsp_count = cnt;
+ irq_mode = IMODE_OUTPUT;
+ intr_active = 1;
+}
+
+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 = sound_dsp_dmachan[dev];
+
+ 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 (sound_dma_automode[dev] &&
+ 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)
+ {
+ sb16_dsp_halt (dev);
+ 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));
+
+ /* sb_dsp_command (0);
+ sb_dsp_command (0); */
+ RESTORE_INTR (flags);
+ dsp_count = cnt;
+ irq_mode = IMODE_INPUT;
+ intr_active = 1;
+}
+
+static int
+sb16_dsp_prepare_for_input (int dev, int bsize, int bcount)
+{
+ sound_dsp_dmachan[my_dev] = dsp_16bit ? dma16 : dma8;
+ dsp_count = 0;
+ dsp_cleanup ();
+ return 0;
+}
+
+static int
+sb16_dsp_prepare_for_output (int dev, int bsize, int bcount)
+{
+ sound_dsp_dmachan[my_dev] = 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);
+ }
+}
+
+static void
+set_irq_hw (int level)
+{
+ int ival;
+
+ switch (level)
+ {
+ case 5:
+ ival = 2;
+ break;
+ case 7:
+ ival = 4;
+ break;
+ case 10:
+ ival = 8;
+ break;
+ 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;
+
+#ifndef SCO
+ sprintf (sb16_dsp_operations.name, "SoundBlaster 16 %d.%d", sbc_major, sbc_minor);
+#endif
+
+#ifdef __FreeBSD__
+ printk ("snd6: <%s>", sb16_dsp_operations.name);
+#else
+ printk (" <%s>", sb16_dsp_operations.name);
+#endif
+
+ if (num_dspdevs < MAX_DSP_DEV)
+ {
+ dsp_devs[my_dev = num_dspdevs++] = &sb16_dsp_operations;
+ sound_dsp_dmachan[my_dev] = hw_config->dma;
+#ifndef NO_AUTODMA
+ sound_buffcounts[my_dev] = 1;
+ sound_dma_automode[my_dev] = 1;
+#else
+ sound_buffcounts[my_dev] = DSP_BUFFCOUNT;
+ sound_dma_automode[my_dev] = 0;
+#endif
+ sound_buffsizes[my_dev] = 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; /* Already initialized */
+
+ 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 (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;
+ }
+
+ dma16 = hw_config->dma;
+ dma8 = sb_config->dma;
+ set_irq_hw (sb_config->irq);
+ sb_setmixer (DMA_NR, (1 << hw_config->dma) | (1 << sb_config->dma));
+
+ 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/i386/isa/sound/sb16_midi.c b/sys/i386/isa/sound/sb16_midi.c
new file mode 100644
index 000000000000..39808c80fe0d
--- /dev/null
+++ b/sys/i386/isa/sound/sb16_midi.c
@@ -0,0 +1,287 @@
+/*
+ * 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)
+
+#define DATAPORT (sb16midi_base) /* MPU-401 Data I/O Port on IBM */
+#define COMDPORT (sb16midi_base+1) /* MPU-401 Command Port on IBM */
+#define STATPORT (sb16midi_base+1) /* MPU-401 Status Port on IBM */
+
+#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 /* Mask for Data Read Redy Bit */
+#define INPUT_AVAIL 0x80 /* Mask for Data Send Ready Bit */
+#define MPU_ACK 0xFE /* MPU-401 Acknowledge Response */
+#define MPU_RESET 0xFF /* MPU-401 Total Reset Command */
+#define UART_MODE_ON 0x3F /* MPU-401 "Dumb UART Mode" */
+
+static int sb16midi_opened = 0;
+static int sb16midi_base = 0x330;
+static int sb16midi_detected = 0;
+static int my_dev;
+
+static int reset_sb16midi (void);
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+extern int sbc_major;
+
+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_command (int dev, unsigned char 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 */
+}
+
+static struct midi_operations sb16midi_operations =
+{
+ {"SoundBlaster MPU-401", 0, 0, SNDCARD_SB16MIDI},
+ sb16midi_open,
+ sb16midi_close,
+ sb16midi_ioctl,
+ sb16midi_out,
+ sb16midi_start_read,
+ sb16midi_end_read,
+ sb16midi_kick,
+ sb16midi_command,
+ sb16midi_buffer_status
+};
+
+
+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);
+
+#ifdef __FreeBSD__
+ printk ("snd7: <SoundBlaster MPU-401>");
+#else
+ printk (" <SoundBlaster MPU-401>");
+#endif
+
+ 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;
+
+ sb16midi_base = hw_config->io_base;
+ if (sbc_major < 4)
+ return 0; /* SB16 not detected */
+
+ if (sb_get_irq () < 0)
+ return 0;
+
+ ok = reset_sb16midi ();
+
+ sb16midi_detected = ok;
+ return ok;
+}
+
+#endif
+
+#endif
diff --git a/sys/i386/isa/sound/sb_card.c b/sys/i386/isa/sound/sb_card.c
index e2527c51a35e..f7588e1ca172 100644
--- a/sys/i386/isa/sound/sb_card.c
+++ b/sys/i386/isa/sound/sb_card.c
@@ -1,11 +1,10 @@
-
/*
- * linux/kernel/chr_drv/sound/sb_card.c
- *
+ * 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
@@ -13,7 +12,7 @@
* 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
@@ -25,7 +24,7 @@
* 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"
diff --git a/sys/i386/isa/sound/sb_dsp.c b/sys/i386/isa/sound/sb_dsp.c
index 4c273646bdd3..17fb4b70dc6a 100644
--- a/sys/i386/isa/sound/sb_dsp.c
+++ b/sys/i386/isa/sound/sb_dsp.c
@@ -1,7 +1,7 @@
/*
- * linux/kernel/chr_drv/sound/sb_dsp.c
+ * sound/sb_dsp.c
*
- * The low level driver for the SoundBlaster DS chips.
+ * The low level driver for the SoundBlaster DSP chip.
*
* Copyright by Hannu Savolainen 1993
*
@@ -25,152 +25,81 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * The mixer support is based on the SB-BSD 1.5 driver by (C) Steve Haehnichen
- * <shaehnic@ucsd.edu>
+ * Modified:
+ * Hunyue Yau Jan 6 1994
+ * Added code to support Sound Galaxy NX Pro
+ *
*/
#include "sound_config.h"
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB)
+#include "sb.h"
+#include "sb_mixer.h"
#undef SB_TEST_IRQ
-#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 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)
-
-static int sbc_base = 0;
+int sbc_base = 0;
static int sbc_irq = 0;
-
-#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
-
-#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | SOUND_MASK_VOLUME)
-
-/*
- * Mixer registers
- *
- * NOTE! RECORD_SRC == IN_FILTER
- */
-
-#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 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)
-
-/* Convenient byte masks */
-#define B1(x) ((x) & 0x01)
-#define B2(x) ((x) & 0x03)
-#define B3(x) ((x) & 0x07)
-#define B4(x) ((x) & 0x0f)
-#define B5(x) ((x) & 0x1f)
-#define B6(x) ((x) & 0x3f)
-#define B7(x) ((x) & 0x7f)
-#define B8(x) ((x) & 0xff)
-#define F(x) (!!(x)) /* 0 or 1 only */
-
-#define MONO_DAC 0x00
-#define STEREO_DAC 0x02
-
-/* DSP Commands */
-
-#define DSP_CMD_SPKON 0xD1
-#define DSP_CMD_SPKOFF 0xD3
+static int open_mode=0;
/*
* The DSP channel can be used either for input or output. Variable
- * 'irq_mode' will be set when the program calls read or write first time
+ * '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.
*/
-#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
-
-static int sb_dsp_ok = 0; /* Set to 1 after successful initialization */
+int sb_dsp_ok = 0; /* Set to 1 after successful initialization */
static int midi_disabled = 0;
-static int dsp_highspeed = 0, dsp_stereo = 0;
+int sb_dsp_highspeed = 0;
+int sbc_major = 1;
+int 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;
-#ifndef EXCLUDE_SBPRO
-static int rec_devices = SOUND_MASK_MIC;
-static int hi_filter = 0, filter_in = 0, filter_out = 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;
-#endif
+volatile int sb_irq_mode = IMODE_NONE; /* IMODE_INPUT, IMODE_OUTPUT
-static int midi_mode = NORMAL_MIDI;
-static int midi_busy = 0; /* 1 if the process has output to MIDI */
-static int dsp_busy = 0;
-
-static volatile int irq_mode = IMODE_NONE; /* IMODE_INPUT, IMODE_OUTPUT
* or IMODE_NONE */
static volatile int irq_ok = 0;
-static int dsp_model = 1; /* DSP version */
-static int dsp_mono = 1; /* 1 SB, 0 SB Pro */
-static int duplex_midi = 0;
+int sb_duplex_midi = 0;
static int my_dev = 0;
-static volatile int intr_active = 0;
+volatile int sb_intr_active = 0;
static int dsp_speed (int);
static int dsp_set_stereo (int mode);
-static int dsp_command (unsigned char val);
-
-#ifndef EXCLUDE_SBPRO
-static void setmixer (unsigned char port, unsigned char value);
-static int getmixer (unsigned char port);
-static void init_mixer (void);
-static int detect_mixer (void);
-
-#endif
+int sb_dsp_command (unsigned char val);
#if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO)
/* Common code for the midi and pcm functions */
-static int
-dsp_command (unsigned char val)
+int
+sb_dsp_command (unsigned char val)
{
- int i, limit;
+ int i;
+ unsigned long limit;
- limit = GET_TIME () + 10; /* The timeout is 0.1 secods */
+ limit = GET_TIME () + HZ / 10;/* The timeout is 0.1 secods */
/*
- * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
+ * 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 < 5000000 && GET_TIME () < limit; i++)
+ for (i = 0; i < 500000 && GET_TIME () < limit; i++)
{
if ((INB (DSP_STATUS) & 0x80) == 0)
{
@@ -179,42 +108,60 @@ dsp_command (unsigned char val)
}
}
- printk ("SoundBlaster: DSP Command(%02x) Timeout.\n", val);
+ printk ("SoundBlaster: DSP Command(%x) Timeout.\n", val);
printk ("IRQ conflict???\n");
return 0;
}
void
-sbintr (int unused)
+sbintr (int unit)
{
- int status, data;
+ 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 (unit);
+
+#ifndef EXCLUDE_MIDI
+ if (src & 4)
+ sb16midiintr (unit); /* MPU401 interrupt */
+#endif
+
+#endif
+
+ if (!(src & 1))
+ return; /* Not a DSP interupt */
+ }
+#endif
status = INB (DSP_DATA_AVAIL);/* Clear interrupt */
- if (intr_active)
- switch (irq_mode)
+ if (sb_intr_active)
+ switch (sb_irq_mode)
{
case IMODE_OUTPUT:
- intr_active = 0;
- DMAbuf_outputintr (my_dev);
+ sb_intr_active = 0;
+ DMAbuf_outputintr (my_dev, 1);
break;
case IMODE_INPUT:
- intr_active = 0;
+ sb_intr_active = 0;
DMAbuf_inputintr (my_dev);
/* A complete buffer has been input. Let's start new one */
break;
case IMODE_INIT:
- intr_active = 0;
+ sb_intr_active = 0;
irq_ok = 1;
break;
case IMODE_MIDI:
- printk ("+");
- data = INB (DSP_READ);
- printk ("%02x", data);
-
+ sb_midi_interrupt (unit);
break;
default:
@@ -222,40 +169,36 @@ sbintr (int unused)
}
}
-static int
-set_dsp_irq (int interrupt_level)
-{
- int retcode = EINVAL;
+static int sb_irq_usecount = 0;
-#ifdef linux
- struct sigaction sa;
+int
+sb_get_irq (void)
+{
+ int ok;
- sa.sa_handler = sbintr;
+ if (!sb_irq_usecount)
+ if ((ok = snd_set_irq_handler (sbc_irq, sbintr)) < 0)
+ return ok;
-#ifdef SND_SA_INTERRUPT
- sa.sa_flags = SA_INTERRUPT;
-#else
- sa.sa_flags = 0;
-#endif
+ sb_irq_usecount++;
- sa.sa_mask = 0;
- sa.sa_restorer = NULL;
+ return 0;
+}
- retcode = irqaction (interrupt_level, &sa);
+void
+sb_free_irq (void)
+{
+ if (!sb_irq_usecount)
+ return;
- if (retcode < 0)
- {
- printk ("SoundBlaster: IRQ%d already in use\n", interrupt_level);
- }
+ sb_irq_usecount--;
-#else
- /* # error Unimplemented for this OS */
-#endif
- return retcode;
+ if (!sb_irq_usecount)
+ snd_release_irq (sbc_irq);
}
-static int
-reset_dsp (void)
+int
+sb_reset_dsp (void)
{
int loopc;
@@ -283,9 +226,9 @@ static void
dsp_speaker (char state)
{
if (state)
- dsp_command (DSP_CMD_SPKON);
+ sb_dsp_command (DSP_CMD_SPKON);
else
- dsp_command (DSP_CMD_SPKOFF);
+ sb_dsp_command (DSP_CMD_SPKOFF);
}
static int
@@ -293,61 +236,81 @@ dsp_speed (int speed)
{
unsigned char tconst;
unsigned long flags;
-
+ int max_speed = 44100;
if (speed < 4000)
speed = 4000;
- if (speed > 44100)
- speed = 44100; /* Invalid speed */
+ /*
+ * Older SB models don't support higher speeds than 22050.
+ */
+
+ if (sbc_major < 2 ||
+ (sbc_major == 2 && sbc_minor == 0))
+ max_speed = 22050;
- if (dsp_model == 1 && speed > 22050)
- speed = 22050;
- /* SB Classic doesn't support higher speed */
+ /*
+ * 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 */
if (dsp_stereo && speed > 22050)
speed = 22050;
/* Max. stereo speed is 22050 */
- if ((speed > 22050) && midi_busy)
+ 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 <<= 1;
+ speed *= 2;
/* Now the speed should be valid */
if (speed > 22050)
{ /* High speed mode */
- tconst = (unsigned char) ((65536 - (256000000 / speed)) >> 8);
- dsp_highspeed = 1;
+ int tmp;
+
+ tconst = (unsigned char) ((65536 -
+ ((256000000 + speed / 2) / speed)) >> 8);
+ sb_dsp_highspeed = 1;
DISABLE_INTR (flags);
- if (dsp_command (0x40))
- dsp_command (tconst);
+ if (sb_dsp_command (0x40))
+ sb_dsp_command (tconst);
RESTORE_INTR (flags);
- speed = (256000000 / (65536 - (tconst << 8)));
+ tmp = 65536 - (tconst << 8);
+ speed = (256000000 + tmp / 2) / tmp;
}
else
{
- dsp_highspeed = 0;
- tconst = (256 - (1000000 / speed)) & 0xff;
+ int tmp;
+
+ sb_dsp_highspeed = 0;
+ tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
DISABLE_INTR (flags);
- if (dsp_command (0x40)) /* Set time constant */
- dsp_command (tconst);
+ if (sb_dsp_command (0x40))/* Set time constant */
+ sb_dsp_command (tconst);
RESTORE_INTR (flags);
- speed = 1000000 / (256 - tconst);
+ tmp = 256 - tconst;
+ speed = (1000000 + tmp / 2) / tmp;
}
if (dsp_stereo)
- speed >>= 1;
+ speed /= 2;
dsp_current_speed = speed;
return speed;
@@ -358,49 +321,47 @@ dsp_set_stereo (int mode)
{
dsp_stereo = 0;
- if (dsp_mono == 1)
+#ifdef EXCLUDE_SBPRO
+ return 0;
+#else
+ if (sbc_major < 3 || sb16)
return 0; /* Sorry no stereo */
- if (mode && midi_busy)
+ if (mode && sb_midi_busy)
{
printk ("SB Warning: Stereo DSP not possible simultaneously with MIDI output\n");
return 0;
}
dsp_stereo = !!mode;
-
-#ifndef EXCLUDE_SBPRO
- setmixer (OUT_FILTER, ((getmixer (OUT_FILTER) & ~STEREO_DAC)
- | (mode ? STEREO_DAC : MONO_DAC)));
+ return dsp_stereo;
#endif
- dsp_speed (dsp_current_speed);/* Speed must be recalculated if #channels
- * changes */
- return mode;
}
static void
-sb_dsp_output_block (int dev, unsigned long buf, int count, int intrflag)
+sb_dsp_output_block (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
{
unsigned long flags;
- if (!irq_mode)
+ if (!sb_irq_mode)
dsp_speaker (ON);
- irq_mode = IMODE_OUTPUT;
+ sb_irq_mode = IMODE_OUTPUT;
DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
if (sound_dsp_dmachan[dev] > 3)
count >>= 1;
count--;
- if (dsp_highspeed)
+ if (sb_dsp_highspeed)
{
DISABLE_INTR (flags);
- if (dsp_command (0x48)) /* High speed size */
+ if (sb_dsp_command (0x48))/* High speed size */
{
- dsp_command (count & 0xff);
- dsp_command ((count >> 8) & 0xff);
- dsp_command (0x91); /* High speed 8 bit DAC */
+ 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");
@@ -409,43 +370,44 @@ sb_dsp_output_block (int dev, unsigned long buf, int count, int intrflag)
else
{
DISABLE_INTR (flags);
- if (dsp_command (0x14)) /* 8-bit DAC (DMA) */
+ if (sb_dsp_command (0x14))/* 8-bit DAC (DMA) */
{
- dsp_command (count & 0xff);
- dsp_command ((count >> 8) & 0xff);
+ 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);
}
- intr_active = 1;
+ sb_intr_active = 1;
}
static void
-sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag)
+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 (!irq_mode)
+ if (!sb_irq_mode)
dsp_speaker (OFF);
- irq_mode = IMODE_INPUT;
+ sb_irq_mode = IMODE_INPUT;
DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
if (sound_dsp_dmachan[dev] > 3)
count >>= 1;
count--;
- if (dsp_highspeed)
+ if (sb_dsp_highspeed)
{
DISABLE_INTR (flags);
- if (dsp_command (0x48)) /* High speed size */
+ if (sb_dsp_command (0x48))/* High speed size */
{
- dsp_command (count & 0xff);
- dsp_command ((count >> 8) & 0xff);
- dsp_command (0x99); /* High speed 8 bit ADC */
+ 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");
@@ -454,23 +416,23 @@ sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag)
else
{
DISABLE_INTR (flags);
- if (dsp_command (0x24)) /* 8-bit ADC (DMA) */
+ if (sb_dsp_command (0x24))/* 8-bit ADC (DMA) */
{
- dsp_command (count & 0xff);
- dsp_command ((count >> 8) & 0xff);
+ 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);
}
- intr_active = 1;
+ sb_intr_active = 1;
}
static void
dsp_cleanup (void)
{
- intr_active = 0;
+ sb_intr_active = 0;
}
static int
@@ -478,6 +440,17 @@ sb_dsp_prepare_for_input (int dev, int bsize, int bcount)
{
dsp_cleanup ();
dsp_speaker (OFF);
+
+ if (sbc_major == 3) /* SB Pro */
+ {
+ if (dsp_stereo)
+ sb_dsp_command (0xa8);
+ else
+ sb_dsp_command (0xa0);
+
+ dsp_speed (dsp_current_speed); /* Speed must be recalculated if #channels
+ * changes */
+ }
return 0;
}
@@ -486,6 +459,15 @@ 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 */
+ {
+ sb_mixer_set_stereo (dsp_stereo);
+ dsp_speed (dsp_current_speed); /* Speed must be recalculated if #channels
+ * changes */
+ }
+#endif
return 0;
}
@@ -495,50 +477,80 @@ sb_dsp_halt_xfer (int dev)
}
static int
-sb_dsp_open (int dev, int mode)
+verify_irq (void)
{
- int retval;
+#if 0
+ DEFINE_WAIT_QUEUE (testq, testf);
- if (!sb_dsp_ok)
+ irq_ok = 0;
+
+ if (sb_get_irq () == -1)
{
- printk ("SB Error: SoundBlaster board not installed\n");
- return RET_ERROR (ENXIO);
+ 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 Error: Incorrect IRQ setting (%d)\n", sbc_irq);
+ 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 (intr_active || (midi_busy && midi_mode == UART_MIDI))
+ 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 (mode != OPEN_READ && mode != OPEN_WRITE)
+ if (!irq_verified)
{
- printk ("SoundBlaster error: DAC and ACD not possible simultaneously\n");
- return RET_ERROR (EINVAL);
+ verify_irq ();
+ irq_verified = 1;
}
+ else if (!irq_ok)
+ printk ("SB Warning: Incorrect IRQ setting %d\n",
+ sbc_irq);
- retval = set_dsp_irq (sbc_irq);
+ retval = sb_get_irq ();
if (retval)
return retval;
if (!DMAbuf_open_dma (dev))
{
- RELEASE_IRQ (sbc_irq);
+ sb_free_irq ();
printk ("SB: DMA Busy\n");
return RET_ERROR (EBUSY);
}
- dsp_set_stereo (OFF);
- dsp_speed (DSP_DEFAULT_SPEED);
- irq_mode = IMODE_NONE;
+ sb_irq_mode = IMODE_NONE;
- dsp_busy = 1;
+ sb_dsp_busy = 1;
+ open_mode = mode;
return 0;
}
@@ -547,12 +559,12 @@ static void
sb_dsp_close (int dev)
{
DMAbuf_close_dma (dev);
- RELEASE_IRQ (sbc_irq);
+ sb_free_irq ();
dsp_cleanup ();
- dsp_speed (DSP_DEFAULT_SPEED);
- dsp_set_stereo (OFF);
dsp_speaker (OFF);
- dsp_busy = 0;
+ sb_dsp_busy = 0;
+ sb_dsp_highspeed = 0;
+ open_mode = 0;
}
static int
@@ -573,6 +585,8 @@ sb_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
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;
@@ -614,7 +628,8 @@ sb_dsp_reset (int dev)
DISABLE_INTR (flags);
- reset_dsp ();
+ sb_reset_dsp ();
+ dsp_speed (dsp_current_speed);
dsp_cleanup ();
RESTORE_INTR (flags);
@@ -631,542 +646,19 @@ sb_dsp_detect (struct address_info *hw_config)
if (sb_dsp_ok)
return 0; /* Already initialized */
- if (!reset_dsp ())
+ if (!sb_reset_dsp ())
return 0;
return 1; /* Detected */
}
-#ifndef EXCLUDE_SBPRO
-
-static void
-setmixer (unsigned char port, unsigned char value)
-{
- OUTB (port, MIXER_ADDR); /* Select register */
- tenmicrosec ();
- OUTB (value, MIXER_DATA);
- tenmicrosec ();
-}
-
-static int
-getmixer (unsigned char port)
-{
- int val;
-
- OUTB (port, MIXER_ADDR); /* Select register */
- tenmicrosec ();
- val = INB (MIXER_DATA);
- tenmicrosec ();
-
- return val;
-}
-
-static int
-detect_mixer (void)
-{
- /*
- * 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?)
- */
- setmixer (FM_VOL, 0xff);
- setmixer (VOC_VOL, 0x33);
-
- if (getmixer (FM_VOL) != 0xff)
- return 0; /* No match */
- if (getmixer (VOC_VOL) != 0x33)
- return 0;
-
- return 1;
-}
-
-static void
-init_mixer (void)
-{
- setmixer (MASTER_VOL, 0xbb);
- setmixer (VOC_VOL, 0x99);
- setmixer (LINE_VOL, 0xbb);
- setmixer (FM_VOL, 0x99);
- setmixer (CD_VOL, 0x11);
- setmixer (MIC_MIX, 0x11);
- setmixer (RECORD_SRC, 0x31);
- setmixer (OUT_FILTER, 0x31);
-}
-
-static void
-set_filter (int record_source, int hifreq_filter, int filter_input, int filter_output)
-{
- setmixer (RECORD_SRC, (record_source
- | (hifreq_filter ? FREQ_HI : FREQ_LOW)
- | (filter_input ? FILT_ON : FILT_OFF)));
-
- setmixer (OUT_FILTER, ((dsp_stereo ? STEREO_DAC : MONO_DAC)
- | (filter_output ? FILT_ON : FILT_OFF)));
-
- hi_filter = hifreq_filter;
- filter_in = filter_input;
- filter_out = filter_output;
-}
-
-static int
-mixer_output (int right_vol, int left_vol, int div, int device)
-{
- int left = ((left_vol * div) + 50) / 100;
- int right = ((right_vol * div) + 50) / 100;
-
- setmixer (device, ((left & 0xf) << 4) | (right & 0xf));
-
- return (left_vol | (right_vol << 8));
-}
-
-static int
-sbp_mixer_set (int whichDev, unsigned int level)
-{
- int left, right, devmask;
-
- left = level & 0x7f;
- right = (level & 0x7f00) >> 8;
-
- switch (whichDev)
- {
- case SOUND_MIXER_VOLUME: /* Master volume (0-15) */
- return mixer_output (right, left, 15, MASTER_VOL);
- break;
- case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-15) */
- return mixer_output (right, left, 15, FM_VOL);
- break;
- case SOUND_MIXER_PCM: /* PAS PCM (0-15) */
- return mixer_output (right, left, 15, VOC_VOL);
- break;
- case SOUND_MIXER_LINE: /* External line (0-15) */
- return mixer_output (right, left, 15, LINE_VOL);
- break;
- case SOUND_MIXER_CD: /* CD (0-15) */
- return mixer_output (right, left, 15, CD_VOL);
- break;
- case SOUND_MIXER_MIC: /* External microphone (0-7) */
- return mixer_output (right, left, 7, MIC_VOL);
- break;
-
- case SOUND_MIXER_RECSRC:
- devmask = level & POSSIBLE_RECORDING_DEVICES;
-
- if (devmask != SOUND_MASK_MIC &&
- devmask != SOUND_MASK_LINE &&
- devmask != SOUND_MASK_CD)
- { /* More than one devices selected. Drop the
- * previous selection */
- devmask &= ~rec_devices;
- }
-
- 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 ^ rec_devices)/* Input source changed */
- {
- switch (devmask)
- {
-
- case SOUND_MASK_MIC:
- set_filter (SRC_MIC, hi_filter, filter_in, filter_out);
- break;
-
- case SOUND_MASK_LINE:
- set_filter (SRC_LINE, hi_filter, filter_in, filter_out);
- break;
-
- case SOUND_MASK_CD:
- set_filter (SRC_CD, hi_filter, filter_in, filter_out);
- break;
-
- default:
- set_filter (SRC_MIC, hi_filter, filter_in, filter_out);
- }
- }
-
- rec_devices = devmask;
-
- return rec_devices;
- break;
-
- default:
- return RET_ERROR (EINVAL);
- }
-
-}
-
-static int
-mixer_input (int div, int device)
-{
- int level, left, right, half;
-
- level = getmixer (device);
- half = div / 2;
-
- left = ((((level & 0xf0) >> 4) * 100) + half) / div;
- right = (((level & 0x0f) * 100) + half) / div;
-
- return (right << 8) | left;
-}
-
-static int
-sbp_mixer_get (int whichDev)
-{
-
- switch (whichDev)
- {
- case SOUND_MIXER_VOLUME: /* Master volume (0-15) */
- return mixer_input (15, MASTER_VOL);
- break;
- case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-15) */
- return mixer_input (15, FM_VOL);
- break;
- case SOUND_MIXER_PCM: /* PAS PCM (0-15) */
- return mixer_input (15, VOC_VOL);
- break;
- case SOUND_MIXER_LINE: /* External line (0-15) */
- return mixer_input (15, LINE_VOL);
- break;
- case SOUND_MIXER_CD: /* CD (0-15) */
- return mixer_input (15, CD_VOL);
- break;
- case SOUND_MIXER_MIC: /* External microphone (0-7) */
- return mixer_input (7, MIC_VOL);
- break;
-
- default:
- return RET_ERROR (EINVAL);
- }
-
-}
-
-/*
- * Sets mixer volume levels. All levels except mic are 0 to 15, mic is 7. See
- * sbinfo.doc for details on granularity and such. Basically, the mixer
- * forces the lowest bit high, effectively reducing the possible settings by
- * one half. Yes, that's right, volume levels have 8 settings, and
- * microphone has four. Sucks.
- */
-static int
-mixer_set_levels (struct sb_mixer_levels *user_l)
-{
- struct sb_mixer_levels l;
-
- IOCTL_FROM_USER ((char *) &l, ((char *) user_l), 0, sizeof (l));
-
- if (l.master.l & ~0xF || l.master.r & ~0xF
- || l.line.l & ~0xF || l.line.r & ~0xF
- || l.voc.l & ~0xF || l.voc.r & ~0xF
- || l.fm.l & ~0xF || l.fm.r & ~0xF
- || l.cd.l & ~0xF || l.cd.r & ~0xF
- || l.mic & ~0x7)
- return (RET_ERROR (EINVAL));
-
- setmixer (MASTER_VOL, (l.master.l << 4) | l.master.r);
- setmixer (LINE_VOL, (l.line.l << 4) | l.line.r);
- setmixer (VOC_VOL, (l.voc.l << 4) | l.voc.r);
- setmixer (FM_VOL, (l.fm.l << 4) | l.fm.r);
- setmixer (CD_VOL, (l.cd.l << 4) | l.cd.r);
- setmixer (MIC_VOL, l.mic);
- return (0);
-}
-
-/*
- * This sets aspects of the Mixer that are not volume levels. (Recording
- * source, filter level, I/O filtering, and stereo.)
- */
-
-static int
-mixer_set_params (struct sb_mixer_params *user_p)
-{
- struct sb_mixer_params p;
-
- IOCTL_FROM_USER ((char *) &p, (char *) user_p, 0, sizeof (p));
-
- if (p.record_source != SRC_MIC
- && p.record_source != SRC_CD
- && p.record_source != SRC_LINE)
- return (EINVAL);
-
- /*
- * I'm not sure if this is The Right Thing. Should stereo be entirely
- * under control of DSP? I like being able to toggle it while a sound is
- * playing, so I do this... because I can.
- */
-
- dsp_stereo = !!p.dsp_stereo;
-
- set_filter (p.record_source, p.hifreq_filter, p.filter_input, p.filter_output);
-
- switch (p.record_source)
- {
-
- case SRC_MIC:
- rec_devices = SOUND_MASK_MIC;
- break;
-
- case SRC_LINE:
- rec_devices = SOUND_MASK_LINE;
- break;
-
- case SRC_CD:
- rec_devices = SOUND_MASK_CD;
- }
-
- return (0);
-}
-
-/* Read the current mixer level settings into the user's struct. */
-static int
-mixer_get_levels (struct sb_mixer_levels *user_l)
-{
- S_BYTE val;
- struct sb_mixer_levels l;
-
- val = getmixer (MASTER_VOL); /* Master */
- l.master.l = B4 (val >> 4);
- l.master.r = B4 (val);
-
- val = getmixer (LINE_VOL); /* FM */
- l.line.l = B4 (val >> 4);
- l.line.r = B4 (val);
-
- val = getmixer (VOC_VOL); /* DAC */
- l.voc.l = B4 (val >> 4);
- l.voc.r = B4 (val);
-
- val = getmixer (FM_VOL); /* FM */
- l.fm.l = B4 (val >> 4);
- l.fm.r = B4 (val);
-
- val = getmixer (CD_VOL); /* CD */
- l.cd.l = B4 (val >> 4);
- l.cd.r = B4 (val);
-
- val = getmixer (MIC_VOL); /* Microphone */
- l.mic = B3 (val);
-
- IOCTL_TO_USER ((char *) user_l, 0, (char *) &l, sizeof (l));
-
- return (0);
-}
-
-/* Read the current mixer parameters into the user's struct. */
-static int
-mixer_get_params (struct sb_mixer_params *user_params)
-{
- S_BYTE val;
- struct sb_mixer_params params;
-
- val = getmixer (RECORD_SRC);
- params.record_source = val & 0x07;
- params.hifreq_filter = !!(val & FREQ_HI);
- params.filter_input = (val & FILT_OFF) ? OFF : ON;
- params.filter_output = (getmixer (OUT_FILTER) & FILT_OFF) ? OFF : ON;
- params.dsp_stereo = dsp_stereo;
-
- IOCTL_TO_USER ((char *) user_params, 0, (char *) &params, sizeof (params));
- return (0);
-}
-
-static int
-sb_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
-{
- if (((cmd >> 8) & 0xff) == 'M')
- {
- if (cmd & IOC_IN)
- return IOCTL_OUT (arg, sbp_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_DEVMASK:
- return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES);
- break;
-
- case SOUND_MIXER_STEREODEVS:
- return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~SOUND_MASK_MIC);
- break;
-
- case SOUND_MIXER_RECMASK:
- return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES);
- break;
-
- case SOUND_MIXER_CAPS:
- return IOCTL_OUT (arg, SOUND_CAP_EXCL_INPUT);
- break;
-
- default:
- return IOCTL_OUT (arg, sbp_mixer_get (cmd & 0xff));
- }
- }
- }
- else
- {
- switch (cmd)
- {
- case MIXER_IOCTL_SET_LEVELS:
- return (mixer_set_levels ((struct sb_mixer_levels *) arg));
- case MIXER_IOCTL_SET_PARAMS:
- return (mixer_set_params ((struct sb_mixer_params *) arg));
- case MIXER_IOCTL_READ_LEVELS:
- return (mixer_get_levels ((struct sb_mixer_levels *) arg));
- case MIXER_IOCTL_READ_PARAMS:
- return (mixer_get_params ((struct sb_mixer_params *) arg));
- case MIXER_IOCTL_RESET:
- init_mixer ();
- return (0);
- default:
- return RET_ERROR (EINVAL);
- }
- }
-}
-
-/* End of mixer code */
-#endif
-
-#ifndef EXCLUDE_MIDI
-
-/* Midi code */
-
-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 (mode != OPEN_WRITE && !duplex_midi)
- {
- if (num_midis == 1)
- printk ("SoundBlaster: Midi input not currently supported\n");
- return RET_ERROR (EPERM);
- }
-
- midi_mode = NORMAL_MIDI;
- if (mode != OPEN_WRITE)
- {
- if (dsp_busy || intr_active)
- return RET_ERROR (EBUSY);
- midi_mode = UART_MIDI;
- }
-
- if (dsp_highspeed || dsp_stereo)
- {
- printk ("SB Error: Midi output not possible during stereo or high speed audio\n");
- return RET_ERROR (EBUSY);
- }
-
- if (midi_mode == UART_MIDI)
- {
- irq_mode = IMODE_MIDI;
-
- reset_dsp ();
- dsp_speaker (OFF);
-
- if (!dsp_command (0x35))
- return RET_ERROR (EIO); /* Enter the UART mode */
- intr_active = 1;
-
- if ((ret = set_dsp_irq (sbc_irq)) < 0)
- {
- reset_dsp ();
- return 0; /* IRQ not free */
- }
- }
-
- midi_busy = 1;
-
- return 0;
-}
-
-static void
-sb_midi_close (int dev)
-{
- if (midi_mode == UART_MIDI)
- {
- reset_dsp (); /* The only way to kill the UART mode */
- RELEASE_IRQ (sbc_irq);
- }
- intr_active = 0;
- midi_busy = 0;
-}
-
-static int
-sb_midi_out (int dev, unsigned char midi_byte)
-{
- unsigned long flags;
-
- midi_busy = 1; /* Kill all notes after close */
-
- if (midi_mode == NORMAL_MIDI)
- {
- DISABLE_INTR (flags);
- if (dsp_command (0x38))
- dsp_command (midi_byte);
- else
- printk ("SB Error: Unable to send a MIDI byte\n");
- RESTORE_INTR (flags);
- }
- else
- dsp_command (midi_byte); /* UART write */
-
- return 1;
-}
-
-static int
-sb_midi_start_read (int dev)
-{
- if (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 (midi_mode == UART_MIDI)
- {
- reset_dsp ();
- intr_active = 0;
- }
- return 0;
-}
-
-static int
-sb_midi_ioctl (int dev, unsigned cmd, unsigned arg)
-{
- return RET_ERROR (EPERM);
-}
-
-/* End of midi code */
-#endif
+static char card_name[32] = "SoundBlaster";
#ifndef EXCLUDE_AUDIO
static struct audio_operations sb_dsp_operations =
{
"SoundBlaster",
+ NOTHING_SPECIAL,
sb_dsp_open,
sb_dsp_close,
sb_dsp_output_block,
@@ -1182,132 +674,102 @@ static struct audio_operations sb_dsp_operations =
#endif
-#ifndef EXCLUDE_SBPRO
-static struct mixer_operations sb_mixer_operations =
-{
- sb_mixer_ioctl
-};
-
-#endif
-
-#ifndef EXCLUDE_MIDI
-static struct midi_operations sb_midi_operations =
-{
- {"SoundBlaster", 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 */
-};
-
-#endif
-
-static int
-verify_irq (void)
-{
-#if 0
- unsigned long loop;
-
- irq_ok = 0;
-
- if (set_dsp_irq (sbc_irq) == -1)
- {
- printk ("*** SB Error: Irq %d already in use\n", sbc_irq);
- return 0;
- }
-
-
- irq_mode = IMODE_INIT;
-
- dsp_command (0xf2); /* This should cause immediate interrupt */
-
- for (loop = 100000; loop > 0 && !irq_ok; loop--);
-
- RELEASE_IRQ (sbc_irq);
-
- if (!irq_ok)
- {
- printk ("SB Warning: IRQ test not passed!");
- irq_ok = 1;
- }
-#else
- irq_ok = 1;
-#endif
- return irq_ok;
-}
-
long
sb_dsp_init (long mem_start, struct address_info *hw_config)
{
- int i, major, minor;
+ int i;
+ int prostat = 0;
- major = minor = 0;
- dsp_command (0xe1); /* Get version */
+ sbc_major = sbc_minor = 0;
+ sb_dsp_command (0xe1); /* Get version */
for (i = 1000; i; i--)
{
- if (inb (DSP_DATA_AVAIL) & 0x80)
+ if (INB (DSP_DATA_AVAIL) & 0x80)
{ /* wait for Data Ready */
- if (major == 0)
- major = inb (DSP_READ);
+ if (sbc_major == 0)
+ sbc_major = INB (DSP_READ);
else
{
- minor = inb (DSP_READ);
+ sbc_minor = INB (DSP_READ);
break;
}
}
}
- dsp_model = major;
+
+ if (sbc_major == 2 || sbc_major == 3) /* SB 2.0 or SB Pro */
+ sb_duplex_midi = 1;
+
+ if (sbc_major == 4)
+ sb16 = 1;
#ifndef EXCLUDE_SBPRO
- if (detect_mixer ())
- {
- dsp_mono = 0;
- sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", major, minor);
- init_mixer ();
- mixer_devs[num_mixers++] = &sb_mixer_operations;
+ if (sbc_major >= 3 ||
+ (sbc_major == 2 && sbc_minor == 1)) /* Sound Galaxy ??? */
+ prostat = sb_mixer_init (sbc_major);
+#endif
- if (major >= 2)
- duplex_midi = 1;
+#ifndef EXCLUDE_YM3812
+ if (sbc_major > 3 ||
+ (sbc_major == 3 && INB (0x388) == 0x00)) /* Non OPL-3 should return 0x06 */
+ enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH);
+#endif
-#ifndef EXCLUDE_YM8312
- if (major > 3 || (major == 3 && minor > 0)) /* SB Pro2 or later */
+ if (sbc_major >= 3)
+ {
+#ifndef SCO
+ if (prostat)
{
- enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH);
+#ifndef EXCLUDE_AUDIO
+ sprintf (sb_dsp_operations.name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor);
+#endif
+ sprintf (card_name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor);
+ }
+ else
+ {
+#ifndef EXCLUDE_AUDIO
+ sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor);
+#endif
+ sprintf (card_name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor);
}
#endif
}
else
+ {
+#ifndef SCO
+#ifndef EXCLUDE_AUDIO
+ sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", sbc_major, sbc_minor);
#endif
- sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", major, minor);
-
- printk ("snd2: <%s>", sb_dsp_operations.name);
+ sprintf (card_name, "SoundBlaster %d.%d", sbc_major, sbc_minor);
+#endif
+ }
- if (!verify_irq ())
- return mem_start;
+#ifdef __FreeBSD__
+ printk ("snd2: <%s>", card_name);
+#else
+ printk (" <%s>", card_name);
+#endif
#ifndef EXCLUDE_AUDIO
- if (num_dspdevs < MAX_DSP_DEV)
- {
- dsp_devs[my_dev = num_dspdevs++] = &sb_dsp_operations;
- sound_buffcounts[my_dev] = DSP_BUFFCOUNT;
- sound_buffsizes[my_dev] = DSP_BUFFSIZE;
- sound_dsp_dmachan[my_dev] = hw_config->dma;
- sound_dma_automode[my_dev] = 0;
- }
- else
- printk ("SB: Too many DSP devices available\n");
+#if !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SBPRO)
+ if (!sb16) /* There is a better driver for SB16 */
+#endif
+ if (num_dspdevs < MAX_DSP_DEV)
+ {
+ dsp_devs[my_dev = num_dspdevs++] = &sb_dsp_operations;
+ sound_buffcounts[my_dev] = DSP_BUFFCOUNT;
+ sound_buffsizes[my_dev] = DSP_BUFFSIZE;
+ sound_dsp_dmachan[my_dev] = hw_config->dma;
+ sound_dma_automode[my_dev] = 0;
+ }
+ else
+ printk ("SB: Too many DSP devices available\n");
#endif
#ifndef EXCLUDE_MIDI
- if (!midi_disabled) /* Midi don't work in the SB emulation mode
- * of PAS */
- midi_devs[num_midis++] = &sb_midi_operations;
+ 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;
diff --git a/sys/i386/isa/sound/sb_midi.c b/sys/i386/isa/sound/sb_midi.c
new file mode 100644
index 000000000000..fed19aba3a08
--- /dev/null
+++ b/sys/i386/isa/sound/sb_midi.c
@@ -0,0 +1,224 @@
+/*
+ * 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 after successful initialization */
+
+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;/* IMODE_INPUT, IMODE_OUTPUT
+
+ * or IMODE_NONE */
+extern int sb_duplex_midi;
+extern int sb_intr_active;
+extern int sbc_base;
+
+static int input_opened = 0;
+static void (*midi_input_intr) (int dev, unsigned char data);
+static int my_dev = 0;
+
+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 (mode != OPEN_WRITE && !sb_duplex_midi)
+ {
+ if (num_midis == 1)
+ printk ("SoundBlaster: MIDI input not supported with plain SB\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 (0xf2)) /* This is undodumented, isn't it */
+ return RET_ERROR (EIO); /* be nice to 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;
+ my_dev = dev;
+ 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;
+
+ sb_midi_busy = 1; /* Kill all notes after close */
+
+ 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);
+}
+
+static struct midi_operations sb_midi_operations =
+{
+ {"SoundBlaster", 0, 0, SNDCARD_SB},
+ 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 */
+};
+
+void
+sb_midi_init (int model)
+{
+ midi_devs[num_midis++] = &sb_midi_operations;
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sb_mixer.c b/sys/i386/isa/sound/sb_mixer.c
new file mode 100644
index 000000000000..39b97caf8cd7
--- /dev/null
+++ b/sys/i386/isa/sound/sb_mixer.c
@@ -0,0 +1,422 @@
+
+/*
+ * sound/sb_mixer.c
+ *
+ * The low level mixer driver for the SoundBlaster Pro and SB16 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:
+ * 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;
+
+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);
+#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 = ((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];
+}
+
+static int
+sb_mixer_set (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 (!(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:
+ 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 =
+{
+ 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.
+ * 0 == Plain SB 16 or SB Pro
+ * 1 == SG NX Pro detected.
+ *
+ * Used to update message.
+ */
+int
+sb_mixer_init (int major_model)
+{
+ int mixerstat;
+
+ sb_setmixer (0x00, 0); /* Reset mixer */
+
+ mixerstat = detect_mixer ();
+
+ if (!mixerstat)
+ return 0; /* No mixer. Why? */
+
+ mixer_initialized = 1;
+ mixer_model = major_model;
+
+ switch (major_model)
+ {
+ case 3:
+ mixer_caps = SOUND_CAP_EXCL_INPUT;
+#ifdef __SGNXPRO__
+ if (mixerstat == 2)
+ { /* A SGNXPRO was detected */
+ supported_devices = SGNXPRO_MIXER_DEVICES;
+ supported_rec_devices = SGNXPRO_RECORDING_DEVICES;
+ iomap = &sgnxpro_mix;
+ }
+ else
+#endif
+ { /* Otherwise plain SB Pro */
+ supported_devices = SBPRO_MIXER_DEVICES;
+ supported_rec_devices = SBPRO_RECORDING_DEVICES;
+ iomap = &sbpro_mix;
+ }
+
+ break;
+
+ case 4:
+ mixer_caps = 0;
+ supported_devices = SB16_MIXER_DEVICES;
+ supported_rec_devices = SB16_RECORDING_DEVICES;
+ iomap = &sb16_mix;
+ break;
+
+ default:
+ printk ("SB Warning: Unsupported mixer type\n");
+ return 0;
+ }
+
+ mixer_devs[num_mixers++] = &sb_mixer_operations;
+ sb_mixer_reset ();
+ return (mixerstat == 2);
+}
+
+#endif
diff --git a/sys/i386/isa/sound/sb_mixer.h b/sys/i386/isa/sound/sb_mixer.h
new file mode 100644
index 000000000000..4caf7730226f
--- /dev/null
+++ b/sys/i386/isa/sound/sb_mixer.h
@@ -0,0 +1,212 @@
+/*
+ * 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_RECLEV | \
+ 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__
+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__
+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)
+};
+#endif
+
+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)
+};
+
+static unsigned short levels[SOUND_MIXER_NRDEVICES] =
+{
+ 0x5a5a, /* Master Volume */
+ 0x3232, /* Bass */
+ 0x3232, /* Treble */
+ 0x4b4b, /* FM */
+ 0x4b4b, /* PCM */
+ 0x4b4b, /* PC Speaker */
+ 0x4b4b, /* Ext Line */
+ 0x0000, /* Mic */
+ 0x4b4b, /* CD */
+ 0x4b4b, /* Recording monitor */
+ 0x4b4b, /* SB PCM */
+ 0x4b4b}; /* Recording level */
+
+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 */
+};
+
+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 */
+};
+#endif
diff --git a/sys/i386/isa/sound/sequencer.c b/sys/i386/isa/sound/sequencer.c
index 7df08120ebcc..1a3943289958 100644
--- a/sys/i386/isa/sound/sequencer.c
+++ b/sys/i386/isa/sound/sequencer.c
@@ -1,10 +1,10 @@
/*
- * linux/kernel/chr_drv/sound/sequencer.c
- *
+ * 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
@@ -12,7 +12,7 @@
* 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
@@ -24,7 +24,7 @@
* 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
@@ -37,20 +37,24 @@
static int sequencer_ok = 0;
DEFINE_WAIT_QUEUE (seq_sleeper, seq_sleep_flag);
-DEFINE_WAIT_QUEUE (midi_sleeper, midi_sleep_flag);
+/* DEFINE_WAIT_QUEUE (midi_sleeper, midi_sleep_flag); */
+#define midi_sleeper seq_sleeper
+#define midi_sleep_flag seq_sleep_flag
static int midi_opened[MAX_MIDI_DEV] =
{0}; /* 1 if the process has opened MIDI */
static int midi_written[MAX_MIDI_DEV] =
{0};
-long seq_time = 0; /* Reference point for the timer */
+unsigned long seq_time = 0; /* Reference point for the timer */
#include "tuning.h"
#define EV_SZ 8
-static unsigned char queue[SEQ_MAX_QUEUE][EV_SZ];
-static unsigned char iqueue[SEQ_MAX_QUEUE][4];
+#define IEV_SZ 4
+static unsigned char *queue = NULL; /* SEQ_MAX_QUEUE * EV_SZ bytes */
+static unsigned char *iqueue = NULL; /* SEQ_MAX_QUEUE * IEV_SZ bytes */
+
static volatile int qhead = 0, qtail = 0, qlen = 0;
static volatile int iqhead = 0, iqtail = 0, iqlen = 0;
static volatile int seq_playing = 0;
@@ -83,13 +87,16 @@ sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
{
if (!iqlen)
{
- INTERRUPTIBLE_SLEEP_ON (midi_sleeper, midi_sleep_flag);
+ if (c != count) /* Some data has been received */
+ return count - c; /* Return what we have */
+
+ DO_SLEEP (midi_sleeper, midi_sleep_flag, 0);
if (!iqlen)
return count - c;
}
- COPY_TO_USER (buf, p, &iqueue[iqhead][0], 4);
+ COPY_TO_USER (buf, p, &iqueue[iqhead * IEV_SZ], IEV_SZ);
p += 4;
c -= 4;
@@ -114,14 +121,14 @@ copy_to_input (unsigned char *event)
if (iqlen >= (SEQ_MAX_QUEUE - 1))
return; /* Overflow */
- memcpy (iqueue[iqtail], event, 4);
+ memcpy (&iqueue[iqtail * IEV_SZ], event, IEV_SZ);
iqlen++;
iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
DISABLE_INTR (flags);
- if (midi_sleep_flag)
+ if (SOMEONE_WAITING (midi_sleeper, midi_sleep_flag))
{
- WAKE_UP (midi_sleeper);
+ WAKE_UP (midi_sleeper, midi_sleep_flag);
}
RESTORE_INTR (flags);
}
@@ -266,16 +273,16 @@ seq_queue (unsigned char *note)
if (!seq_playing)
seq_startplay (); /* Give chance to drain the queue */
- if (qlen >= SEQ_MAX_QUEUE && !seq_sleep_flag)
+ if (qlen >= SEQ_MAX_QUEUE && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
{
/* Sleep until there is enough space on the queue */
- INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag);
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, 0);
}
if (qlen >= SEQ_MAX_QUEUE)
return 0; /* To be sure */
- memcpy (&queue[qtail][0], note, EV_SZ);
+ memcpy (&queue[qtail * EV_SZ], note, EV_SZ);
qtail = (qtail + 1) % SEQ_MAX_QUEUE;
qlen++;
@@ -323,6 +330,10 @@ extended_event (unsigned char *q)
synth_devs[dev]->controller (dev, q[3], q[4], *(short *) &q[5]);
break;
+ case SEQ_VOLMODE:
+ synth_devs[dev]->volume_method (dev, q[3]);
+ break;
+
default:
return RET_ERROR (EINVAL);
}
@@ -342,7 +353,7 @@ seq_startplay (void)
qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
qlen--;
- q = &queue[this_one][0];
+ q = &queue[this_one * EV_SZ];
switch (q[0])
{
@@ -378,10 +389,9 @@ seq_startplay (void)
unsigned long flags;
DISABLE_INTR (flags);
- if (seq_sleep_flag)
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
{
- seq_sleep_flag = 0;
- WAKE_UP (seq_sleeper);
+ WAKE_UP (seq_sleeper, seq_sleep_flag);
}
RESTORE_INTR (flags);
}
@@ -449,10 +459,9 @@ seq_startplay (void)
unsigned long flags;
DISABLE_INTR (flags);
- if (seq_sleep_flag)
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
{
- seq_sleep_flag = 0;
- WAKE_UP (seq_sleeper);
+ WAKE_UP (seq_sleeper, seq_sleep_flag);
}
RESTORE_INTR (flags);
}
@@ -461,86 +470,87 @@ seq_startplay (void)
int
sequencer_open (int dev, struct fileinfo *file)
- {
- int retval, mode, i;
+{
+ int retval, mode, i;
- dev = dev >> 4;
- mode = file->mode & O_ACCMODE;
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
- DEB (printk ("sequencer_open(dev=%d)\n", dev));
+ DEB (printk ("sequencer_open(dev=%d)\n", dev));
- if (!sequencer_ok)
- {
- printk ("Soundcard: Sequencer not initialized\n");
- return RET_ERROR (ENXIO);
- }
+ if (!sequencer_ok)
+ {
+ printk ("Soundcard: Sequencer not initialized\n");
+ return RET_ERROR (ENXIO);
+ }
- if (dev) /* Patch manager device */
- {
- int err;
+ if (dev) /* Patch manager device */
+ {
+ int err;
- dev--;
- if (pmgr_present[dev])
- return RET_ERROR (EBUSY);
- if ((err = pmgr_open (dev)) < 0)
- return err; /* Failed */
+ dev--;
+ if (pmgr_present[dev])
+ return RET_ERROR (EBUSY);
+ if ((err = pmgr_open (dev)) < 0)
+ return err; /* Failed */
- pmgr_present[dev] = 1;
- return err;
- }
+ pmgr_present[dev] = 1;
+ return err;
+ }
- if (sequencer_busy)
- {
- printk ("Sequencer busy\n");
- return RET_ERROR (EBUSY);
- }
+ if (sequencer_busy)
+ {
+ printk ("Sequencer busy\n");
+ return RET_ERROR (EBUSY);
+ }
- if (!(num_synths + num_midis))
- return RET_ERROR (ENXIO);
+ if (!(num_synths + num_midis))
+ return RET_ERROR (ENXIO);
- synth_open_mask = 0;
+ synth_open_mask = 0;
- if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
- for (i = 0; i < num_synths; i++) /* Open synth devices */
- if (synth_devs[i]->open (i, mode) < 0)
- printk ("Sequencer: Warning! Cannot open synth device #%d\n", i);
- else
- synth_open_mask |= (1 << i);
+ if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+ for (i = 0; i < num_synths; i++) /* Open synth devices */
+ if (synth_devs[i]->open (i, mode) < 0)
+ printk ("Sequencer: Warning! Cannot open synth device #%d\n", i);
+ else
+ synth_open_mask |= (1 << i);
- seq_time = GET_TIME ();
+ seq_time = GET_TIME ();
- for (i = 0; i < num_midis; i++)
- {
- midi_opened[i] = 0;
- midi_written[i] = 0;
- }
+ for (i = 0; i < num_midis; i++)
+ {
+ midi_opened[i] = 0;
+ midi_written[i] = 0;
+ }
- if (mode == OPEN_READ || mode == OPEN_READWRITE)
- { /* Initialize midi input devices */
- if (!num_midis)
- {
- printk ("Sequencer: No Midi devices. Input not possible\n");
- return RET_ERROR (ENXIO);
- }
+ if (mode == OPEN_READ || mode == OPEN_READWRITE)
+ { /* Initialize midi input devices */
+ if (!num_midis)
+ {
+ printk ("Sequencer: No Midi devices. Input not possible\n");
+ return RET_ERROR (ENXIO);
+ }
- for (i = 0; i < num_midis; i++)
- {
- if ((retval = midi_devs[i]->open (i, mode,
+ for (i = 0; i < num_midis; i++)
+ {
+ if ((retval = midi_devs[i]->open (i, mode,
sequencer_midi_input, sequencer_midi_output)) >= 0)
- midi_opened[i] = 1;
- }
- }
+ midi_opened[i] = 1;
+ }
+ }
- sequencer_busy = 1;
- seq_sleep_flag = midi_sleep_flag = 0;
- output_treshold = SEQ_MAX_QUEUE / 2;
+ 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);
+ for (i = 0; i < num_synths; i++)
+ if (pmgr_present[i])
+ pmgr_inform (i, PM_E_OPENED, 0, 0, 0, 0);
- return 0;
- }
+ return 0;
+}
void
seq_drain_midi_queues (void)
@@ -553,7 +563,7 @@ seq_drain_midi_queues (void)
n = 1;
- while (!PROCESS_ABORTING && n)
+ while (!PROCESS_ABORTING (midi_sleeper, midi_sleep_flag) && n)
{
n = 0;
@@ -568,70 +578,70 @@ seq_drain_midi_queues (void)
*/
if (n)
{
- REQUEST_TIMEOUT (HZ / 10, seq_sleeper);
- INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag);
+ 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;
+{
+ int i;
+ int mode = file->mode & O_ACCMODE;
- dev = dev >> 4;
+ dev = dev >> 4;
- DEB (printk ("sequencer_release(dev=%d)\n", dev));
+ DEB (printk ("sequencer_release(dev=%d)\n", dev));
- if (dev) /* Patch manager device */
- {
- dev--;
- pmgr_release (dev);
- pmgr_present[dev] = 0;
- return;
- }
+ if (dev) /* Patch manager device */
+ {
+ dev--;
+ pmgr_release (dev);
+ pmgr_present[dev] = 0;
+ return;
+ }
- /*
+ /*
* Wait until the queue is empty
- */
- while (!PROCESS_ABORTING && 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 */
+ while (!PROCESS_ABORTING (seq_sleeper, seq_sleep_flag) && qlen)
+ {
+ seq_sync ();
+ }
- for (i = 0; i < num_midis; i++)
- if (midi_opened[i])
- midi_devs[i]->close (i);
+ 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 */
- if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
- for (i = 0; i < num_synths; i++)
- if (synth_open_mask & (1 << i)) /* Actually opened */
- if (synth_devs[i])
- synth_devs[i]->close (i);
+ for (i = 0; i < num_midis; i++)
+ if (midi_opened[i])
+ midi_devs[i]->close (i);
+ if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
for (i = 0; i < num_synths; i++)
- if (pmgr_present[i])
- pmgr_inform (i, PM_E_CLOSED, 0, 0, 0, 0);
+ if (synth_open_mask & (1 << i)) /* Actually opened */
+ if (synth_devs[i])
+ synth_devs[i]->close (i);
- sequencer_busy = 0;
- }
+ for (i = 0; i < num_synths; i++)
+ if (pmgr_present[i])
+ pmgr_inform (i, PM_E_CLOSED, 0, 0, 0, 0);
+
+ sequencer_busy = 0;
+}
static int
seq_sync (void)
{
- if (qlen && !seq_playing && !PROCESS_ABORTING)
+ if (qlen && !seq_playing && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag))
seq_startplay ();
- if (qlen && !seq_sleep_flag) /* Queue not empty */
+ if (qlen && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) /* Queue not empty */
{
- INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag);
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, 0);
}
return qlen;
@@ -654,8 +664,7 @@ midi_outc (int dev, unsigned char data)
while (n && !midi_devs[dev]->putc (dev, data))
{
- REQUEST_TIMEOUT (1, seq_sleeper);
- INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag);
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, 4);
n--;
}
}
@@ -684,7 +693,8 @@ seq_reset (void)
{
for (chn = 0; chn < 16; chn++)
{
- midi_outc (i, 0xb0 + chn); /* Channel message */
+ midi_outc (i,
+ (unsigned char) (0xb0 + (chn & 0xff))); /* Channel msg */
midi_outc (i, 0x7b);/* All notes off */
midi_outc (i, 0); /* Dummy parameter */
}
@@ -697,7 +707,7 @@ seq_reset (void)
seq_playing = 0;
- if (seq_sleep_flag)
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
printk ("Sequencer Warning: Unexpected sleeping process\n");
}
@@ -720,7 +730,7 @@ sequencer_ioctl (int dev, struct fileinfo *file,
if (mode == OPEN_READ)
return 0;
- while (qlen && !PROCESS_ABORTING)
+ while (qlen && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag))
seq_sync ();
return 0;
break;
@@ -979,7 +989,6 @@ sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * w
case SEL_IN:
if (!iqlen)
{
- midi_sleep_flag = 1;
select_wait (&midi_sleeper, wait);
return 0;
}
@@ -990,7 +999,6 @@ sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * w
case SEL_OUT:
if (qlen >= SEQ_MAX_QUEUE)
{
- seq_sleep_flag = 1;
select_wait (&seq_sleeper, wait);
return 0;
}
@@ -1039,7 +1047,7 @@ note_to_freq (int note_num)
else if (octave > BASE_OCTAVE)
note_freq <<= (octave - BASE_OCTAVE);
- /* note_freq >>= 1; */
+ /* note_freq >>= 1; */
return note_freq;
}
@@ -1048,7 +1056,7 @@ unsigned long
compute_finetune (unsigned long base_freq, int bend, int range)
{
unsigned long amount;
- int negative, semitones, cents;
+ int negative, semitones, cents, multiplier = 1;
if (!bend)
return base_freq;
@@ -1072,13 +1080,20 @@ compute_finetune (unsigned long base_freq, int bend, int range)
if (bend > range)
bend = range;
- if (bend > 2399)
- bend = 2399;
+ /*
+ if (bend > 2399)
+ bend = 2399;
+ */
+ while (bend > 2399)
+ {
+ multiplier *= 4;
+ bend -= 2400;
+ }
semitones = bend / 100;
cents = bend % 100;
- amount = semitone_tuning[semitones] * cent_tuning[cents] / 10000;
+ amount = semitone_tuning[semitones] * multiplier * cent_tuning[cents] / 10000;
if (negative)
return (base_freq * 10000) / amount; /* Bend down */
@@ -1092,6 +1107,9 @@ 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;
}
@@ -1111,14 +1129,14 @@ sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
int
sequencer_open (int dev, struct fileinfo *file)
- {
- return RET_ERROR (ENXIO);
- }
+{
+ 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)
@@ -1138,11 +1156,14 @@ 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 /* ALLOW_SELECT */
#endif
diff --git a/sys/i386/isa/sound/sound_calls.h b/sys/i386/isa/sound/sound_calls.h
index 89bd79604359..abc82001b776 100644
--- a/sys/i386/isa/sound/sound_calls.h
+++ b/sys/i386/isa/sound/sound_calls.h
@@ -16,23 +16,10 @@ int DMAbuf_open_dma (int chan);
void DMAbuf_close_dma (int chan);
void DMAbuf_reset_dma (int chan);
void DMAbuf_inputintr(int dev);
-void DMAbuf_outputintr(int dev);
+void DMAbuf_outputintr(int dev, int underflow_flag);
/*
- * System calls for the /dev/dsp
- */
-
-int dsp_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
-int dsp_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
-int dsp_open (int dev, struct fileinfo *file, int bits);
-void dsp_release (int dev, struct fileinfo *file);
-int dsp_ioctl (int dev, struct fileinfo *file,
- unsigned int cmd, unsigned int arg);
-int dsp_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
-long dsp_init (long mem_start);
-
-/*
- * System calls for the /dev/audio
+ * System calls for /dev/dsp and /dev/audio
*/
int audio_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
@@ -108,11 +95,47 @@ void tenmicrosec(void);
void request_sound_timer (int count);
void sound_stop_timer(void);
int snd_ioctl_return(int *addr, int value);
+int snd_set_irq_handler (int interrupt_level, void(*hndlr)(int));
+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 unused);
+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);
+
+/* From sb_midi.c */
+void sb_midi_init(int model);
+void sb_midi_interrupt(int dummy);
+
+/* 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);
@@ -156,9 +179,10 @@ int gus_wave_detect(int baseaddr);
long gus_wave_init(long mem_start, int irq, int dma);
void gus_voice_irq(void);
unsigned char gus_read8 (int reg);
-void gus_write8(int reg, unsigned char data);
+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);
@@ -179,3 +203,6 @@ 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);
diff --git a/sys/i386/isa/sound/sound_config.h b/sys/i386/isa/sound/sound_config.h
index 9cbd8103b8f5..0d30c12aadc4 100644
--- a/sys/i386/isa/sound/sound_config.h
+++ b/sys/i386/isa/sound/sound_config.h
@@ -30,6 +30,16 @@
#include "local.h"
+
+#undef CONFIGURE_SOUNDCARD
+#undef DYNAMIC_BUFFER
+
+#ifdef KERNEL_SOUNDCARD
+#define CONFIGURE_SOUNDCARD
+#define DYNAMIC_BUFFER
+#undef LOADABLE_SOUNDCARD
+#endif
+
#ifdef EXCLUDE_SEQUENCER
#ifndef EXCLUDE_MIDI
#define EXCLUDE_MIDI
@@ -42,6 +52,10 @@
#endif
#endif
+#ifndef SND_DEFAULT_ENABLE
+#define SND_DEFAULT_ENABLE 1
+#endif
+
/** UWM - new MIDI stuff **/
#ifdef EXCLUDE_CHIP_MIDI
@@ -75,6 +89,14 @@ If your card has nonstandard I/O address or IRQ number, change defines
#define SBC_DMA 1
#endif
+#ifndef SB16_DMA
+#define SB16_DMA 6
+#endif
+
+#ifndef SB16MIDI_BASE
+#define SB16MIDI_BASE 0x300
+#endif
+
#ifndef PAS_BASE
#define PAS_BASE 0x388
#endif
@@ -111,6 +133,10 @@ If your card has nonstandard I/O address or IRQ number, change defines
#define MPU_IRQ 6
#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
@@ -132,8 +158,6 @@ If your card has nonstandard I/O address or IRQ number, change defines
#define DMA_AUTOINIT 0x10
-#define SND_MAJOR 14
-
#define FM_MONO 0x388 /* This is the I/O address used by AdLib */
/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the
@@ -178,10 +202,10 @@ If your card has nonstandard I/O address or IRQ number, change defines
#define ON 1
#define OFF 0
-#define MAX_DSP_DEV 3
-#define MAX_MIXER_DEV 1
+#define MAX_DSP_DEV 4
+#define MAX_MIXER_DEV 2
#define MAX_SYNTH_DEV 3
-#define MAX_MIDI_DEV 3
+#define MAX_MIDI_DEV 4
struct fileinfo {
int mode; /* Open mode */
@@ -193,6 +217,15 @@ struct address_info {
int dma;
};
+/*
+ * 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
diff --git a/sys/i386/isa/sound/sound_switch.c b/sys/i386/isa/sound/sound_switch.c
new file mode 100644
index 000000000000..68c757586083
--- /dev/null
+++ b/sys/i386/isa/sound/sound_switch.c
@@ -0,0 +1,445 @@
+/*
+ * 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 ("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\nHW config: \n"))
+ return;
+
+ for (i = 0; i < (num_sound_drivers - 1); i++)
+ {
+ if (!supported_drivers[i].enabled)
+ if (!put_status ("("))
+ return;
+
+ if (!put_status ("Type "))
+ return;
+ if (!put_status_int (supported_drivers[i].card_type, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (supported_drivers[i].name))
+ return;
+ if (!put_status (" at 0x"))
+ return;
+ if (!put_status_int (supported_drivers[i].config.io_base, 16))
+ return;
+ if (!put_status (" irq "))
+ return;
+ if (!put_status_int (supported_drivers[i].config.irq, 10))
+ return;
+ if (!put_status (" drq "))
+ return;
+ if (!put_status_int (supported_drivers[i].config.dma, 10))
+ return;
+
+ if (!supported_drivers[i].enabled)
+ if (!put_status (")"))
+ return;
+
+ if (!put_status ("\n"))
+ return;
+ }
+
+ if (!put_status ("\nPCM devices:\n"))
+ return;
+
+ for (i = 0; i < num_dspdevs; i++)
+ {
+ if (!put_status_int (i, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (dsp_devs[i]->name))
+ return;
+ if (!put_status ("\n"))
+ return;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ if (num_mixers)
+ {
+ if (!put_status ("\nMixer(s) installed\n"))
+ return;
+ }
+ else
+ {
+ if (!put_status ("\nNo mixers installed\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:
+ return sequencer_read (dev, file, buf, count);
+ break;
+
+#ifndef EXCLUDE_MPU401
+ 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:
+ 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;
+
+ 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:
+ if ((retval = sequencer_open (dev, file)) < 0)
+ return retval;
+ break;
+
+#ifndef EXCLUDE_MPU401
+ 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:
+ sequencer_release (dev, file);
+ break;
+
+#ifndef EXCLUDE_MPU401
+ 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));
+
+ switch (dev & 0x0f)
+ {
+
+ case SND_DEV_CTL:
+
+ if (!num_mixers)
+ return RET_ERROR (ENXIO);
+
+ if ((dev >> 4) >= num_mixers)
+ return RET_ERROR (ENXIO);
+
+ return mixer_devs[dev >> 4]->ioctl (dev >> 4, cmd, arg);
+ break;
+
+ case SND_DEV_SEQ:
+ 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_MPU401
+ 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/i386/isa/sound/soundcard.c b/sys/i386/isa/sound/soundcard.c
index 79c9b09298d1..9691e4f07bec 100644
--- a/sys/i386/isa/sound/soundcard.c
+++ b/sys/i386/isa/sound/soundcard.c
@@ -34,28 +34,20 @@
#include "dev_table.h"
-int __timeout_val = 0;
-int __process_aborting = 0;
-
-u_int snd1mask;
-u_int snd2mask;
-u_int snd3mask;
-u_int snd4mask;
-u_int snd5mask;
-
-struct sbc_device
-{
- int usecount;
-};
+u_int snd1_imask;
+u_int snd2_imask;
+u_int snd3_imask;
+u_int snd4_imask;
+u_int snd5_imask;
+u_int snd6_imask;
+u_int snd7_imask;
+u_int snd8_imask;
+u_int snd9_imask;
#define FIX_RETURN(ret) {if ((ret)<0) return -(ret); else return 0;}
-static struct sbc_device sbc_devices[SND_NDEVS];
static int timer_running = 0;
-static int in_use = 0; /* Total # of open device files (excluding
- * minor 0) */
-
static int soundcards_installed = 0; /* Number of installed
* soundcards */
static int soundcard_configured = 0;
@@ -75,12 +67,19 @@ int sndwrite (int dev, struct uio *uio);
int sndselect (int dev, int rw);
static void sound_mem_init(void);
+unsigned
long
-get_time()
+get_time(void)
{
extern struct timeval time;
-
- return(time.tv_usec + (time.tv_sec*1000000));
+struct timeval timecopy;
+int x;
+
+ x = splclock();
+ timecopy = time;
+ splx(x);
+ return timecopy.tv_usec/(1000000/HZ) +
+ (unsigned long)timecopy.tv_sec*HZ;
}
@@ -91,41 +90,7 @@ sndread (int dev, struct uio *buf)
dev = minor (dev);
- DEB (printk ("sound_read(dev=%d, count=%d)\n", dev, count));
-
- switch (dev & 0x0f) /* It really has to be 0x0f */
- {
- case SND_DEV_AUDIO:
- FIX_RETURN (audio_read (dev, &files[dev], buf, count));
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- FIX_RETURN (dsp_read (dev, &files[dev], buf, count));
- break;
-
- case SND_DEV_SEQ:
- FIX_RETURN (sequencer_read (dev, &files[dev], buf, count));
- break;
-
-#ifndef EXCLUDE_CHIP_MIDI
- case CMIDI_DEV_PRO:
- FIX_RETURN (CMIDI_read (dev, &files[dev], buf, count));
-
- break;
-#endif
-
-
-#ifndef EXCLUDE_MPU401
- case SND_DEV_MIDIN:
- FIX_RETURN (MIDIbuf_read (dev, &files[dev], buf, count));
-#endif
-
- default:
- ;
- }
-
- FIX_RETURN (-EPERM);
+ FIX_RETURN (sound_read_sw (dev, &files[dev], buf, count));
}
int
@@ -133,37 +98,9 @@ sndwrite (int dev, struct uio *buf)
{
int count = buf->uio_resid;
- DEB (printk ("sound_write(dev=%d, count=%d)\n", dev, count));
-
dev = minor (dev);
- switch (dev & 0x0f) /* It really has to be 0x0f */
- {
-
- case SND_DEV_SEQ:
- FIX_RETURN (sequencer_write (dev, &files[dev], buf, count));
- break;
-
- case SND_DEV_AUDIO:
- FIX_RETURN (audio_write (dev, &files[dev], buf, count));
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- FIX_RETURN (dsp_write (dev, &files[dev], buf, count));
- break;
-
-#ifndef EXCLUDE_CHIP_MIDI
- case CMIDI_DEV_PRO:
- FIX_RETURN (CMIDI_write (dev, &files[dev], buf, count));
- break;
-#endif
-
- default:
- FIX_RETURN (-EPERM);
- }
-
- FIX_RETURN (count);
+ FIX_RETURN (sound_write_sw (dev, &files[dev], buf, count));
}
int
@@ -173,16 +110,6 @@ sndopen (dev_t dev, int flags)
dev = minor (dev);
- /* printf("SND: Minor number is now : %ld\n",dev); */
-
- DEB (printk ("sound_open(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount));
-
- if ((dev >= SND_NDEVS) || (dev < 0))
- {
- printk ("Invalid minor device %d\n", dev);
- FIX_RETURN (-ENODEV);
- }
-
if (!soundcard_configured && dev)
{
printk ("SoundCard Error: The soundcard system has not been configured\n");
@@ -198,62 +125,7 @@ sndopen (dev_t dev, int flags)
else if (flags & FWRITE)
files[dev].mode = OPEN_WRITE;
- switch (dev & 0x0f) /* It has to be 0x0f. Trust me */
- {
- case SND_DEV_CTL:
- if (!soundcards_installed)
- if (soundcard_configured)
- {
- printk ("Soundcard not installed\n");
- FIX_RETURN (-ENODEV);
- }
- break;
-
- case SND_DEV_SEQ:
- if ((retval = sequencer_open (dev, &files[dev])) < 0)
- FIX_RETURN (retval);
- break;
-
-/** UWM stuff **/
-
-#ifndef EXCLUDE_CHIP_MIDI
- case CMIDI_DEV_PRO:
- FIX_RETURN ( CMIDI_open (dev, &files[dev]) );
- break;
-#endif
-
-
-#ifndef EXCLUDE_MPU401
- case SND_DEV_MIDIN:
- if ((retval = MIDIbuf_open (dev, &files[dev])) < 0)
- FIX_RETURN (retval);
- break;
-#endif
-
- case SND_DEV_AUDIO:
- if ((retval = audio_open (dev, &files[dev])) < 0)
- FIX_RETURN (retval);
- break;
-
- case SND_DEV_DSP:
- if ((retval = dsp_open (dev, &files[dev], 8)) < 0)
- FIX_RETURN (retval);
- break;
-
- case SND_DEV_DSP16:
- if ((retval = dsp_open (dev, &files[dev], 16)) < 0)
- FIX_RETURN (retval);
- break;
-
- default:
- printk ("Invalid minor device %d\n", dev);
- FIX_RETURN (-ENODEV);
- }
-
- sbc_devices[dev].usecount++;
- in_use++;
-
- FIX_RETURN (0);
+ FIX_RETURN(sound_open_sw (dev, &files[dev]));
}
int
@@ -262,41 +134,7 @@ sndclose (dev_t dev, int flags)
dev = minor (dev);
- DEB (printk ("sound_release(dev=%d)\n", dev));
-
- switch (dev & 0x0f) /* Has to be 0x0f */
- {
- case SND_DEV_SEQ:
- sequencer_release (dev, &files[dev]);
- break;
-
-#ifndef EXCLUDE_CHIP_MIDI
- case CMIDI_DEV_PRO:
- CMIDI_close (dev, &files[dev]);
- break;
-#endif
-
-#ifndef EXCLUDE_MPU401
- case SND_DEV_MIDIN:
- MIDIbuf_release (dev, &files[dev]);
- break;
-#endif
-
- case SND_DEV_AUDIO:
- audio_release (dev, &files[dev]);
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- dsp_release (dev, &files[dev]);
- break;
-
- default:;
- }
-
- sbc_devices[dev].usecount--;
- in_use--; /* If not control port */
-
+ sound_release_sw(dev, &files[dev]);
FIX_RETURN (0);
}
@@ -305,46 +143,7 @@ sndioctl (dev_t dev, int cmd, caddr_t arg, int mode)
{
dev = minor (dev);
- DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
-
- switch (dev & 0x0f)
- {
-
- case SND_DEV_CTL:
- if (!num_mixers)
- FIX_RETURN (-ENODEV);
-
- if (dev >= num_mixers)
- FIX_RETURN (-ENODEV);
-
- FIX_RETURN (mixer_devs[dev]->ioctl (dev, cmd, (unsigned int) arg));
- break;
-
- case SND_DEV_SEQ:
- FIX_RETURN (sequencer_ioctl (dev, &files[dev], cmd, (unsigned int) arg));
- break;
-
- case SND_DEV_AUDIO:
- FIX_RETURN (audio_ioctl (dev, &files[dev], cmd, (unsigned int) arg));
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- FIX_RETURN (dsp_ioctl (dev, &files[dev], cmd, (unsigned int) arg));
- break;
-
-#ifndef EXCLUDE_MPU401
- case SND_DEV_MIDIN:
- FIX_RETURN (MIDIbuf_ioctl (dev, &files[dev], cmd, (unsigned int) arg));
- break;
-#endif
-
- default:
- FIX_RETURN (-EPERM);
- break;
- }
-
- FIX_RETURN (-EPERM);
+ FIX_RETURN (sound_ioctl_sw (dev, &files[dev], cmd, (unsigned int) arg));
}
int
@@ -380,7 +179,6 @@ sndprobe (struct isa_device *dev)
hw_config.io_base = dev->id_iobase;
hw_config.irq = ipri_to_irq (dev->id_irq);
hw_config.dma = dev->id_drq;
-
return sndtable_probe (dev->id_unit, &hw_config);
}
@@ -430,7 +228,6 @@ sndattach (struct isa_device *dev)
dsp_initialized = 1;
mem_start = DMAbuf_init (mem_start);
mem_start = audio_init (mem_start);
- mem_start = dsp_init (mem_start);
}
/** UWM stuff **/
@@ -459,11 +256,6 @@ sndattach (struct isa_device *dev)
mem_start = sequencer_init (mem_start);
}
- for (i = 0; i < SND_NDEVS; i++)
- {
- sbc_devices[i].usecount = 0;
- }
-
return TRUE;
}
@@ -514,7 +306,7 @@ void
sound_stop_timer (void)
{
if (timer_running)
- untimeout ((timeout_func_t)sequencer_timer, 0); /* XXX should fix */
+ untimeout ((timeout_func_t)sequencer_timer, 0);
timer_running = 0;
}
@@ -533,26 +325,12 @@ sound_mem_init (void)
dsp_init_mask |= (1 << dev);
if (sound_dma_automode[dev])
- {
- sound_dma_automode[dev] = 0; /* Not possible with FreeBSD */
- }
-
- if (sound_buffcounts[dev] == 1)
- {
- sound_buffcounts[dev] = 2;
- sound_buffsizes[dev] /= 2;
- }
-
- if (sound_buffsizes[dev] > 65536) /* Larger is not possible (yet) */
- sound_buffsizes[dev] = 65536;
+ sound_buffcounts[dev] = 1;
-#if 0
if (sound_dsp_dmachan[dev] > 3 && sound_buffsizes[dev] > 65536)
dma_pagesize = 131072; /* 128k */
else
dma_pagesize = 65536;
-#endif
- dma_pagesize = 4096; /* use bounce buffer */
/* More sanity checks */
@@ -566,31 +344,17 @@ sound_mem_init (void)
for (snd_raw_count[dev] = 0; snd_raw_count[dev] < sound_buffcounts[dev]; snd_raw_count[dev]++)
{
- /*
- * The DMA buffer allocation algorithm hogs memory. We allocate
- * a memory area which is two times the requires size. This
- * guarantees that it contains at least one valid DMA buffer.
- *
- * This really needs some kind of finetuning.
- */
- char *tmpbuf = malloc (2*sound_buffsizes[dev], M_DEVBUF, M_NOWAIT);
- unsigned long addr, rounded;
+ char *tmpbuf = contigmalloc (sound_buffsizes[dev], M_DEVBUF, M_NOWAIT,
+ 0xFFFFFFul, 0ul, dma_pagesize - 1);
if (tmpbuf == NULL)
{
printk ("snd: Unable to allocate %d bytes of buffer\n",
- 2 * sound_buffsizes[dev]);
+ sound_buffsizes[dev]);
return;
}
- addr = kvtop (tmpbuf);
- /*
- * Align the start address
- */
- rounded = (addr & ~(dma_pagesize - 1)) + dma_pagesize;
-
- snd_raw_buf[dev][snd_raw_count[dev]] =
- &tmpbuf[rounded - addr]; /* Compute offset */
+ snd_raw_buf[dev][snd_raw_count[dev]] = tmpbuf;
/*
* Use virtual address as the physical address, since
* isa_dmastart performs the phys address computation.
@@ -616,4 +380,15 @@ snd_ioctl_return (int *addr, int value)
return 0;
}
+int
+snd_set_irq_handler (int interrupt_level, void(*hndlr)(int))
+{
+ return 1;
+}
+
+void
+snd_release_irq(int vect)
+{
+}
+
#endif
diff --git a/sys/i386/isa/spkr.c b/sys/i386/isa/spkr.c
index 01c81f4b02a3..77bf59630243 100644
--- a/sys/i386/isa/spkr.c
+++ b/sys/i386/isa/spkr.c
@@ -4,7 +4,7 @@
* 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.7 1994/01/25 23:04:27 ache Exp $
+ * $Id: spkr.c,v 1.8 1994/04/21 14:21:50 sos Exp $
*/
#include "speaker.h"
@@ -17,7 +17,8 @@
#include "errno.h"
#include "buf.h"
#include "uio.h"
-
+#include "i386/isa/isa.h"
+#include "i386/isa/timerreg.h"
#include "machine/speaker.h"
/**************** MACHINE DEPENDENT PART STARTS HERE *************************
@@ -48,10 +49,7 @@
* Channel 2 LSB first, (Square Wave) Encoding
* MSB second
*/
-#define PPI 0x61 /* port of Programmable Peripheral Interface */
#define PPI_SPKR 0x03 /* turn these PPI bits on to pass sound */
-#define PIT_CTRL 0x43 /* PIT control address */
-#define PIT_COUNT 0x42 /* PIT count address */
#define PIT_MODE 0xB6 /* set timer mode for sound generation */
/*
@@ -75,13 +73,17 @@ unsigned int thz, ticks;
/* set timer to generate clicks at given frequency in Hertz */
sps = spltty();
- outb(PIT_CTRL, PIT_MODE); /* prepare timer */
- outb(PIT_COUNT, (divisor & 0xff)); /* send lo byte */
- outb(PIT_COUNT, (divisor >> 8)); /* send hi byte */
+
+ if (acquire_timer2(PIT_MODE)) {
+ /* enter list of waiting procs ??? */
+ return;
+ }
+ outb(TIMER_CNTR2, (divisor & 0xff)); /* send lo byte */
+ outb(TIMER_CNTR2, (divisor >> 8)); /* send hi byte */
splx(sps);
/* turn the speaker on */
- outb(PPI, inb(PPI) | PPI_SPKR);
+ outb(IO_PPI, inb(IO_PPI) | PPI_SPKR);
/*
* Set timeout to endtone function, then give up the timeslice.
@@ -89,7 +91,8 @@ unsigned int thz, ticks;
* emitted.
*/
(void) tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks);
- outb(PPI, inb(PPI) & ~PPI_SPKR);
+ outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR);
+ release_timer2();
}
static void rest(ticks)
diff --git a/sys/i386/isa/syscons.c b/sys/i386/isa/syscons.c
index afd5f468fc54..21558dbaa872 100644
--- a/sys/i386/isa/syscons.c
+++ b/sys/i386/isa/syscons.c
@@ -35,7 +35,7 @@
* SUCH DAMAGE.
*
* from:@(#)syscons.c 1.3 940129
- * $Id: syscons.c,v 1.35.2.2 1994/05/03 07:01:43 rgrimes Exp $
+ * $Id: syscons.c,v 1.50 1994/05/30 03:16:02 ache Exp $
*
*/
@@ -64,7 +64,6 @@
#include "machine/psl.h"
#include "machine/frame.h"
#include "machine/pc/display.h"
-#include "iso8859.font"
#include "kbdtables.h"
#include "sc.h"
@@ -74,6 +73,10 @@
#define NCONS 12
#endif
+#if !defined(NO_HARDFONTS)
+#include "i386/isa/iso8859.font"
+#endif
+
/* status flags */
#define LOCK_KEY_MASK 0x0000F
#define LED_MASK 0x00007
@@ -86,15 +89,21 @@
#define VIDEOMEM 0x000A0000
/* misc defines */
-#define MAX_ESC_PAR 3
+#define MAX_ESC_PAR 5
#define TEXT80x25 1
#define TEXT80x50 2
+#define LOAD 1
+#define SAVE 0
#define COL 80
#define ROW 25
#define BELL_DURATION 5
#define BELL_PITCH 800
#define TIMER_FREQ 1193182 /* should be in isa.h */
+#define CONSOLE_BUFSIZE 1024
#define PCBURST 128
+#define FONT_8_LOADED 0x001
+#define FONT_14_LOADED 0x002
+#define FONT_16_LOADED 0x004
/* defines related to hardware addresses */
#define MONO_BASE 0x3B4 /* crt controller base mono */
@@ -163,33 +172,37 @@ static default_attr kernel_default = {
(FG_BLACK | BG_LIGHTGREY) << 8
};
-#define CONSOLE_BUFFER_SIZE 1024
-int console_buffer_count;
-char console_buffer[CONSOLE_BUFFER_SIZE];
-
static scr_stat console[NCONS];
static scr_stat *cur_console = &console[0];
static scr_stat *new_scp, *old_scp;
static term_stat kernel_console;
static default_attr *current_default;
+static int console_buffer_count;
+static char console_buffer[CONSOLE_BUFSIZE];
static int switch_in_progress = 0;
static u_short *crtat = 0;
static u_int crtc_addr = MONO_BASE;
static char crtc_vga = 0;
static u_char shfts = 0, ctls = 0, alts = 0, agrs = 0, metas = 0;
static u_char nlkcnt = 0, clkcnt = 0, slkcnt = 0, alkcnt = 0;
+static char *font_8 = NULL, *font_14 = NULL, *font_16 = NULL;
+static int fonts_loaded = 0;
static char palette[3*256];
static const u_int n_fkey_tab = sizeof(fkey_tab) / sizeof(*fkey_tab);
static int cur_cursor_pos = -1;
static char in_putc = 0;
static char polling = 0;
+#if ASYNCH
+static u_char kbd_reply = 0;
+#endif
static int delayed_next_scr;
static char saved_console = -1; /* saved console number */
-static long scrn_blank_time = 0; /* screen saver timout value */
+static long scrn_blank_time = 0; /* screen saver timeout value */
static int scrn_blanked = 0; /* screen saver active flag */
static int scrn_saver = 0; /* screen saver routine */
static long scrn_time_stamp;
static u_char scr_map[256];
+
extern int hz;
extern struct timeval time;
@@ -233,18 +246,14 @@ static u_char *get_fstr(u_int c, u_int *len);
static void update_leds(int which);
static void kbd_wait(void);
static void kbd_cmd(u_char command);
-static void kbd_cmd2(u_char command, u_char arg);
-static int kbd_reply(void);
static void set_mode(scr_stat *scp);
static void set_border(int color);
-static void load_font(int segment, int size, char* font);
+static void copy_font(int direction, int segment, int size, char* font);
static void save_palette(void);
static void load_palette(void);
static void change_winsize(struct tty *tp, int x, int y);
-
/* available screen savers */
-
static void none_saver(int test);
static void blank_saver(int test);
static void fade_saver(int test);
@@ -269,42 +278,26 @@ static const struct {
#if defined(NetBSD)
#define VIRTUAL_TTY(x) pc_tty[x] ? (pc_tty[x]) : (pc_tty[x] = ttymalloc())
#define CONSOLE_TTY pc_tty[NCONS] ? (pc_tty[NCONS]) : (pc_tty[NCONS] = ttymalloc())
-#define frametype struct trapframe
-#define eflags tf_eflags
extern u_short *Crtat;
struct tty *pc_tty[NCONS+1];
int ttrstrt();
#endif
#if defined(__FreeBSD__)
-#define frametype struct trapframe
-#define eflags tf_eflags
+#define VIRTUAL_TTY(x) (pccons[x] = ttymalloc(pccons[x]))
+#define CONSOLE_TTY (pccons[NCONS] = ttymalloc(pccons[NCONS]))
#define timeout_t timeout_func_t
#define MONO_BUF (KERNBASE+0xB0000)
#define CGA_BUF (KERNBASE+0xB8000)
-#endif
-
-#if defined(__386BSD__) && !defined(__FreeBSD__)
-#define frametype struct syscframe
-#define eflags sf_eflags
-#define timeout_t caddr_t
-#define MONO_BUF (0xFE0B0000)
-#define CGA_BUF (0xFE0B8000)
-#endif
-
-#if defined(__386BSD__) || defined(__FreeBSD__)
-#define VIRTUAL_TTY(x) &pccons[x]
-#define CONSOLE_TTY &pccons[NCONS]
-u_short *Crtat = (u_short *)MONO_BUF;
-struct tty pccons[NCONS+1];
-void consinit(void) {scinit();}
#include "ddb.h"
#if NDDB > 0
#define DDB 1
#endif
+struct tty *pccons[NCONS+1];
+u_short *Crtat = (u_short *)MONO_BUF;
+void consinit(void) {scinit();}
#endif
-
struct isa_driver scdriver = {
pcprobe, pcattach, "sc",
};
@@ -312,18 +305,47 @@ struct isa_driver scdriver = {
int pcprobe(struct isa_device *dev)
{
+ int i, retries = 5;
+ unsigned char val;
+
/* Enable interrupts and keyboard controller */
kbd_wait();
outb(KB_STAT, KB_WRITE);
- kbd_cmd(0x4D);
+ kbd_wait();
+ outb(KB_DATA, KB_MODE);
- /* Start keyboard stuff RESET */
- for (;;) {
- kbd_cmd(KB_RESET);
- if (kbd_reply() == KB_ACK && /* command accepted */
- kbd_reply() == 0xaa) /* self test passed */
- break;
- printf("Keyboard reset failed\n");
+ /* 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)
+ printf("scprobe: keyboard won't accept RESET command\n");
+ else {
+gotack:
+ DELAY(10);
+ while ((inb(KB_STAT) & KB_BUF_FULL) == 0) DELAY(10);
+ DELAY(10);
+ val = inb(KB_DATA);
+ if (val == KB_ACK)
+ goto gotack;
+ if (val != KB_RESET_DONE)
+ printf("scprobe: keyboard RESET failed %02x\n", val);
}
return (IO_KBDSIZE);
}
@@ -358,15 +380,28 @@ int pcattach(struct isa_device *dev)
if (crtc_vga) {
get_cursor_shape(&start, &end);
#endif
+#if defined(HARDFONTS)
+ font_8 = font_8x8;
+ font_14 = font_8x14;
+ font_16 = font_8x16;
+ fonts_loaded = FONT_8_LOADED|FONT_14_LOADED|FONT_16_LOADED;
+ copy_font(LOAD, 1, 8, font_8);
+ copy_font(LOAD, 2, 14, font_14);
+ copy_font(LOAD, 0, 16, font_16);
+#else
+ 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, 0, 16, font_16);
+ fonts_loaded = FONT_16_LOADED;
+#endif
save_palette();
- load_font(0, 16, font_8x16);
- load_font(1, 8, font_8x8);
- load_font(2, 14, font_8x14);
}
current_default = &user_default;
for (i = 0; i < NCONS; i++) {
scp = &console[i];
- scp->scr_buf = (u_short *)malloc(COL * ROW * 2, M_DEVBUF, M_NOWAIT);
+ scp->scr_buf = (u_short *)malloc(COL * ROW * 2,
+ M_DEVBUF, M_NOWAIT);
scp->mode = TEXT80x25;
scp->term.esc = 0;
scp->term.std_attr = current_default->std_attr;
@@ -379,13 +414,14 @@ int pcattach(struct isa_device *dev)
scp->ysize = ROW;
scp->bell_pitch = BELL_PITCH;
scp->bell_duration = BELL_DURATION;
- scp->status = 0;
+ scp->status = NLKED;
scp->pid = 0;
scp->proc = NULL;
scp->smode.mode = VT_AUTO;
if (i > 0) {
scp->crt_base = scp->crtat = scp->scr_buf;
- fillw(scp->term.cur_attr|scr_map[0x20], scp->scr_buf, COL*ROW);
+ fillw(scp->term.cur_attr|scr_map[0x20],
+ scp->scr_buf, COL*ROW);
}
}
/* get cursor going */
@@ -394,6 +430,7 @@ int pcattach(struct isa_device *dev)
console[0].cursor_end);
#endif
cursor_pos(1);
+ update_leds(console[0].status);
return 0;
}
@@ -430,6 +467,7 @@ static int get_scr_num()
return i < NCONS ? i : 0;
}
+
int pcopen(dev_t dev, int flag, int mode, struct proc *p)
{
struct tty *tp = get_tty_ptr(dev);
@@ -441,7 +479,6 @@ int pcopen(dev_t dev, int flag, int mode, struct proc *p)
tp->t_param = pcparam;
tp->t_dev = dev;
if (!(tp->t_state & TS_ISOPEN)) {
- tp->t_state |= TS_WOPEN;
ttychars(tp);
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
@@ -567,7 +604,7 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
{
int i, error;
struct tty *tp;
- frametype *fp;
+ struct trapframe *fp;
scr_stat *scp;
tp = get_tty_ptr(dev);
@@ -619,25 +656,32 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
case CONS_80x50TEXT: /* set 80x50 text mode */
if (!crtc_vga)
return ENXIO;
- scp->mode = TEXT80x50;
- scp->ysize = 50;
- free(scp->scr_buf, M_DEVBUF);
- scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*2,
- M_DEVBUF, M_NOWAIT);
- if (scp != cur_console)
- scp->crt_base = scp->scr_buf;
- set_mode(scp);
- clear_screen(scp);
- change_winsize(tp, scp->xsize, scp->ysize);
- return 0;
+ /* is there a 8x8 font loaded ? */
+ if (fonts_loaded & FONT_8_LOADED) {
+ scp->mode = TEXT80x50;
+ scp->ysize = 50;
+ free(scp->scr_buf, M_DEVBUF);
+ scp->scr_buf =
+ (u_short *)malloc(scp->xsize * scp->ysize * 2,
+ M_DEVBUF, M_NOWAIT);
+ if (scp != cur_console)
+ scp->crt_base = scp->scr_buf;
+ set_mode(scp);
+ clear_screen(scp);
+ change_winsize(tp, scp->xsize, scp->ysize);
+ return 0;
+ }
+ else
+ return EINVAL;
case CONS_GETVERS: /* get version number */
*(int*)data = 0x103; /* version 1.3 */
return 0;
case CONS_GETINFO: /* get current (virtual) console info */
- if (*data == sizeof(struct vid_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;
@@ -654,6 +698,7 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
return 0;
}
return EINVAL;
+ }
case VT_SETMODE: /* set screen switcher mode */
bcopy(data, &scp->smode, sizeof(struct vt_mode));
@@ -740,13 +785,13 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
return 0;
case KDENABIO: /* allow io operations */
- fp = (frametype *)p->p_regs;
- fp->eflags |= PSL_IOPL;
+ fp = (struct trapframe *)p->p_regs;
+ fp->tf_eflags |= PSL_IOPL;
return 0;
case KDDISABIO: /* disallow io operations (default) */
- fp = (frametype *)p->p_regs;
- fp->eflags &= ~PSL_IOPL;
+ fp = (struct trapframe *)p->p_regs;
+ fp->tf_eflags &= ~PSL_IOPL;
return 0;
case KDSETMODE: /* set current mode of this (virtual) console */
@@ -754,9 +799,9 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
case KD_TEXT: /* switch to TEXT (known) mode */
/* restore fonts & palette ! */
if (crtc_vga) {
- load_font(0, 16, font_8x16);
- load_font(1, 8, font_8x8);
- load_font(2, 14, font_8x14);
+ copy_font(LOAD, 0, 16, font_16);
+ copy_font(LOAD, 1, 8, font_8);
+ copy_font(LOAD, 2, 14, font_14);
load_palette();
}
/* FALL THROUGH */
@@ -793,7 +838,7 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
scp->status &= ~LOCK_KEY_MASK;
scp->status |= *data;
if (scp == cur_console)
- update_leds(scp->status & LED_MASK);
+ update_leds(scp->status);
return 0;
}
return EINVAL;
@@ -805,7 +850,10 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
case KDSETRAD: /* set keyboard repeat & delay rates */
if (*data & 0x80)
return EINVAL;
- kbd_cmd2(KB_SETRAD, *data);
+ i = spltty();
+ kbd_cmd(KB_SETRAD);
+ kbd_cmd(*data);
+ splx(i);
return 0;
case KDSKBMODE: /* set keyboard mode */
@@ -837,18 +885,20 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
if (scp == cur_console) {
if (*(int*)data) {
int pitch = TIMER_FREQ/(*(int*)data);
- /* enable counter 2 */
- outb(0x61, inb(0x61) | 3);
/* set command for counter 2, 2 byte write */
- outb(TIMER_MODE,
- TIMER_SEL2|TIMER_16BIT|TIMER_SQWAVE);
+ 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);
}
else {
- /* disable counter 2 */
- outb(0x61, inb(0x61) & 0xFC);
+ /* disable counter 2 output to speaker */
+ outb(IO_PPI, inb(IO_PPI) & 0xFC);
+ release_timer2();
}
}
return 0;
@@ -862,7 +912,7 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
scp->status &= ~LED_MASK;
scp->status |= *data;
if (scp == cur_console)
- update_leds(scp->status & LED_MASK);
+ update_leds(scp->status);
return 0;
}
return EINVAL;
@@ -914,48 +964,63 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
case PIO_FONT8x8: /* set 8x8 dot font */
if (!crtc_vga)
return ENXIO;
- bcopy(data, &font_8x8, sizeof(font_8x8));
- load_font(1, 8, font_8x8);
+ bcopy(data, font_8, 8*256);
+ fonts_loaded |= FONT_8_LOADED;
+ copy_font(LOAD, 1, 8, font_8);
return 0;
case GIO_FONT8x8: /* get 8x8 dot font */
if (!crtc_vga)
return ENXIO;
- bcopy(&font_8x8, data, sizeof(font_8x8));
- return 0;
+ if (fonts_loaded & FONT_8_LOADED) {
+ 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_8x14, sizeof(font_8x14));
- load_font(2, 14, font_8x14);
+ bcopy(data, font_14, 14*256);
+ fonts_loaded |= FONT_14_LOADED;
+ copy_font(LOAD, 2, 14, font_14);
return 0;
case GIO_FONT8x14: /* get 8x14 dot font */
if (!crtc_vga)
return ENXIO;
- bcopy(&font_8x14, data, sizeof(font_8x14));
- return 0;
+ if (fonts_loaded & FONT_14_LOADED) {
+ 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_8x16, sizeof(font_8x16));
- load_font(0, 16, font_8x16);
+ bcopy(data, font_16, 16*256);
+ fonts_loaded |= FONT_16_LOADED;
+ copy_font(LOAD, 0, 16, font_16);
return 0;
case GIO_FONT8x16: /* get 8x16 dot font */
if (!crtc_vga)
return ENXIO;
- bcopy(&font_8x16, data, sizeof(font_8x16));
- return 0;
+ if (fonts_loaded & FONT_16_LOADED) {
+ bcopy(font_16, data, 16*256);
+ return 0;
+ }
+ else
+ return ENXIO;
case CONSOLE_X_MODE_ON: /* just to be compatible */
if (saved_console < 0) {
saved_console = get_scr_num();
switch_scr(minor(dev));
- fp = (frametype *)p->p_regs;
- fp->eflags |= PSL_IOPL;
+ fp = (struct trapframe *)p->p_regs;
+ fp->tf_eflags |= PSL_IOPL;
scp->status |= UNKNOWN_MODE;
scp->status |= KBD_RAW_MODE;
return 0;
@@ -963,12 +1028,12 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
return EAGAIN;
case CONSOLE_X_MODE_OFF:/* just to be compatible */
- fp = (frametype *)p->p_regs;
- fp->eflags &= ~PSL_IOPL;
+ fp = (struct trapframe *)p->p_regs;
+ fp->tf_eflags &= ~PSL_IOPL;
if (crtc_vga) {
- load_font(0, 16, font_8x16);
- load_font(1, 8, font_8x8);
- load_font(2, 14, font_8x14);
+ copy_font(LOAD, 0, 16, font_16);
+ copy_font(LOAD, 1, 8, font_8);
+ copy_font(LOAD, 2, 14, font_14);
load_palette();
}
scp->status &= ~UNKNOWN_MODE;
@@ -987,7 +1052,7 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
*/
if (data)
sysbeep(TIMER_FREQ/((int*)data)[0],
- ((int*)data)[1]*hz/3000);
+ ((int*)data)[1]*hz/1000);
else
sysbeep(scp->bell_pitch, scp->bell_duration);
return 0;
@@ -1054,7 +1119,7 @@ void pcstart(struct tty *tp)
}
splx(s);
-#else /* __FreeBSD__ & __386BSD__ */
+#else /* __FreeBSD__ */
int c, s, len, i;
scr_stat *scp = get_scr_stat(tp->t_dev);
@@ -1065,28 +1130,18 @@ void pcstart(struct tty *tp)
s = spltty();
if (!(tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) {
for (;;) {
- if (RB_LEN(&tp->t_out) <= tp->t_lowat) {
- if (tp->t_state & TS_ASLEEP) {
- tp->t_state &= ~TS_ASLEEP;
- wakeup((caddr_t)&tp->t_out);
- }
- if (tp->t_wsel) {
- selwakeup(tp->t_wsel,
- tp->t_state & TS_WCOLL);
- tp->t_wsel = 0;
- tp->t_state &= ~TS_WCOLL;
- }
- }
- if (RB_LEN(&tp->t_out) == 0)
+ if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT)
+ || tp->t_wsel)
+ ttwwakeup(tp);
+ if (RB_LEN(tp->t_out) == 0)
break;
if (scp->status & SLKED)
break;
len = 0;
while( len < PCBURST) {
- c = getc(&tp->t_out);
- if (c == -1)
+ buf[len++] = getc(tp->t_out);
+ if( RB_LEN(tp->t_out) == 0)
break;
- buf[len++] = c;
}
tp->t_state |= TS_BUSY;
splx(s);
@@ -1122,9 +1177,6 @@ void pccnprobe(struct consdev *cp)
/* initialize required fields */
cp->cn_dev = makedev(maj, NCONS);
cp->cn_pri = CN_INTERNAL;
-#if defined(__FreeBSD__) || defined(__386BSD__)
- cp->cn_tp = CONSOLE_TTY;
-#endif
}
@@ -1161,10 +1213,12 @@ int pccngetc(dev_t dev)
return(c);
}
+
static void none_saver(int test)
{
}
+
static void fade_saver(int test)
{
static int count = 0;
@@ -1195,6 +1249,7 @@ static void fade_saver(int test)
}
}
+
static void blank_saver(int test)
{
u_char val;
@@ -1212,18 +1267,18 @@ static void blank_saver(int test)
static u_long rand_next = 1;
+
static int rand()
{
return ((rand_next = rand_next * 1103515245 + 12345) & 0x7FFFFFFF);
}
+#define NUM_STARS 50
+
/*
* Alternate saver that got its inspiration from a well known utility
* package for an unfamous OS.
*/
-
-#define NUM_STARS 50
-
static void star_saver(int test)
{
scr_stat *scp = cur_console;
@@ -1331,6 +1386,7 @@ static void snake_saver(int test)
}
}
+
static void cursor_shape(int start, int end)
{
outb(crtc_addr, 10);
@@ -1381,10 +1437,6 @@ static void clear_screen(scr_stat *scp)
static int switch_scr(u_int next_scr)
{
- if (in_putc) { /* delay switch if in putc */
- delayed_next_scr = next_scr+1;
- return 0;
- }
if (switch_in_progress &&
(cur_console->proc != pfind(cur_console->pid)))
switch_in_progress = 0;
@@ -1402,7 +1454,10 @@ static int switch_scr(u_int next_scr)
return EINVAL;
}
}
-
+ if (in_putc) { /* delay switch if in putc */
+ delayed_next_scr = next_scr+1;
+ return 0;
+ }
switch_in_progress = 1;
old_scp = cur_console;
new_scp = &console[next_scr];
@@ -1448,7 +1503,13 @@ static void exchange_scr(void)
new_scp->crt_base = Crtat;
move_crsr(new_scp, new_scp->xpos, new_scp->ypos);
bcopy(new_scp->scr_buf, Crtat, new_scp->xsize * new_scp->ysize * 2);
- update_leds(new_scp->status & LED_MASK);
+ update_leds(new_scp->status);
+ if ((old_scp->status & UNKNOWN_MODE) && crtc_vga) {
+ copy_font(LOAD, 0, 16, font_16);
+ copy_font(LOAD, 1, 8, font_8);
+ copy_font(LOAD, 2, 14, font_14);
+ load_palette();
+ }
if (old_scp->status & KBD_RAW_MODE || new_scp->status & KBD_RAW_MODE)
shfts = ctls = alts = agrs = metas = 0;
delayed_next_scr = 0;
@@ -1464,6 +1525,7 @@ static void move_crsr(scr_stat *scp, int x, int y)
scp->crtat = scp->crt_base + scp->ypos * scp->xsize + scp->xpos;
}
+
static void move_up(u_short *s, u_short *d, u_int len)
{
s += len;
@@ -1472,12 +1534,14 @@ static void move_up(u_short *s, u_short *d, u_int len)
*--d = *--s;
}
+
static void move_down(u_short *s, u_short *d, u_int len)
{
while (len-- > 0)
*d++ = *s++;
}
+
static void scan_esc(scr_stat *scp, u_char c)
{
static u_char ansi_col[16] =
@@ -1746,39 +1810,45 @@ static void scan_esc(scr_stat *scp, u_char c)
break;
case 'm': /* change attribute */
- if (scp->term.num_param == 0)
- n = 0;
- else
- n = scp->term.param[0];
- switch (n) {
- case 0: /* back to normal */
+ if (scp->term.num_param == 0) {
scp->term.cur_attr = scp->term.std_attr;
break;
- case 1: /* highlight (bold) */
- scp->term.cur_attr &= 0xFF00;
- scp->term.cur_attr |= 0x0800;
- break;
- case 4: /* highlight (underline) */
- scp->term.cur_attr &= 0x0F00;
- scp->term.cur_attr |= 0x0800;
- break;
- case 5: /* blink */
- scp->term.cur_attr &= 0xFF00;
- scp->term.cur_attr |= 0x8000;
- break;
- case 7: /* reverse video */
- scp->term.cur_attr = scp->term.rev_attr;
- break;
- case 30: case 31: case 32: case 33: /* set fg color */
- case 34: case 35: case 36: case 37:
- scp->term.cur_attr = (scp->term.cur_attr & 0xF0FF)
- | (ansi_col[(n - 30) & 7] << 8);
- break;
- case 40: case 41: case 42: case 43: /* set bg color */
- case 44: case 45: case 46: case 47:
- scp->term.cur_attr = (scp->term.cur_attr & 0x0FFF)
- | (ansi_col[(n - 40) & 7] << 12);
- break;
+ }
+ for (i = 0; i < scp->term.num_param; i++) {
+ switch (n = scp->term.param[i]) {
+ case 0: /* back to normal */
+ scp->term.cur_attr = scp->term.std_attr;
+ break;
+ case 1: /* highlight (bold) */
+ scp->term.cur_attr &= 0xFF00;
+ scp->term.cur_attr |= 0x0800;
+ break;
+ case 4: /* highlight (underline) */
+ scp->term.cur_attr &= 0xFF00;
+ scp->term.cur_attr |= 0x0800;
+ break;
+ case 5: /* blink */
+ scp->term.cur_attr &= 0xFF00;
+ scp->term.cur_attr |= 0x8000;
+ break;
+ case 7: /* reverse video */
+ scp->term.cur_attr = scp->term.rev_attr;
+ break;
+ case 30: case 31: /* set fg color */
+ case 32: case 33: case 34:
+ case 35: case 36: case 37:
+ scp->term.cur_attr =
+ (scp->term.cur_attr & 0xF8FF)
+ | (ansi_col[(n-30) & 7] << 8);
+ break;
+ case 40: case 41: /* set bg color */
+ case 42: case 43: case 44:
+ case 45: case 46: case 47:
+ scp->term.cur_attr =
+ (scp->term.cur_attr & 0x8FFF)
+ | (ansi_col[(n-40) & 7] << 12);
+ break;
+ }
}
break;
@@ -1979,6 +2049,7 @@ static void ansi_put(scr_stat *scp, u_char c)
switch_scr(delayed_next_scr - 1);
}
+
static void scinit(void)
{
u_short volatile *cp = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short), was;
@@ -2030,7 +2101,7 @@ static void scinit(void)
console[0].border = BG_BLACK;;
console[0].xsize = COL;
console[0].ysize = ROW;
- console[0].status = 0;
+ console[0].status = NLKED;
console[0].pid = 0;
console[0].proc = NULL;
console[0].smode.mode = VT_AUTO;
@@ -2065,7 +2136,7 @@ static void scput(u_char c)
scp->term = save;
--in_putc;
} else {
- if( console_buffer_count < CONSOLE_BUFFER_SIZE)
+ if( console_buffer_count < CONSOLE_BUFSIZE)
console_buffer[console_buffer_count++] = c;
}
}
@@ -2087,9 +2158,20 @@ static u_char *get_fstr(u_int c, u_int *len)
static void update_leds(int which)
{
+ int s;
static u_char xlate_leds[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
- kbd_cmd2(KB_SETLEDS, xlate_leds[which & LED_MASK]);
+ /* 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);
}
@@ -2099,7 +2181,7 @@ static void update_leds(int which)
*/
u_int scgetc(int noblock)
{
- u_char val, code, release;
+ u_char scancode, keycode;
u_int state, action;
struct key_t *key;
static u_char esc_flag = 0, compose = 0;
@@ -2109,132 +2191,136 @@ next_code:
kbd_wait();
/* First see if there is something in the keyboard port */
if (inb(KB_STAT) & KB_BUF_FULL)
- val = inb(KB_DATA);
+ scancode = inb(KB_DATA);
else if (noblock)
return(NOKEY);
else
goto next_code;
if (cur_console->status & KBD_RAW_MODE)
- return val;
-
- code = val & 0x7F;
- release = val & 0x80;
-
+ 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(code) {
- case 0x38: /* left alt (compose key) */
- if (release && compose) {
+ switch(scancode) {
+ case 0xB8: /* left alt (compose key) */
+ if (compose) {
compose = 0;
if (chr > 255) {
sysbeep(BELL_PITCH, BELL_DURATION);
chr = 0;
}
}
- else {
- if (!compose) {
- compose = 1;
- chr = 0;
- }
+ break;
+ case 0x38:
+ if (!compose) {
+ compose = 1;
+ chr = 0;
}
break;
- case 0x60:
- case 0x61:
- esc_flag = code;
+ case 0xE0:
+ case 0xE1:
+ esc_flag = scancode;
goto next_code;
}
break;
- case 0x60: /* 0xE0 prefix */
+ case 0xE0: /* 0xE0 prefix */
esc_flag = 0;
- switch (code) {
- case 0x1c: /* right enter key */
- code = 0x59;
+ switch (keycode) {
+ case 0x1C: /* right enter key */
+ keycode = 0x59;
break;
- case 0x1d: /* right ctrl key */
- code = 0x5a;
+ case 0x1D: /* right ctrl key */
+ keycode = 0x5A;
break;
case 0x35: /* keypad divide key */
- code = 0x5b;
+ keycode = 0x5B;
break;
case 0x37: /* print scrn key */
- code = 0x5c;
+ keycode = 0x5C;
break;
case 0x38: /* right alt key (alt gr) */
- code = 0x5d;
+ keycode = 0x5D;
break;
case 0x47: /* grey home key */
- code = 0x5e;
+ keycode = 0x5E;
break;
case 0x48: /* grey up arrow key */
- code = 0x5f;
+ keycode = 0x5F;
break;
case 0x49: /* grey page up key */
- code = 0x60;
+ keycode = 0x60;
break;
- case 0x4b: /* grey left arrow key */
- code = 0x61;
+ case 0x4B: /* grey left arrow key */
+ keycode = 0x61;
break;
- case 0x4d: /* grey right arrow key */
- code = 0x62;
+ case 0x4D: /* grey right arrow key */
+ keycode = 0x62;
break;
- case 0x4f: /* grey end key */
- code = 0x63;
+ case 0x4F: /* grey end key */
+ keycode = 0x63;
break;
case 0x50: /* grey down arrow key */
- code = 0x64;
+ keycode = 0x64;
break;
case 0x51: /* grey page down key */
- code = 0x65;
+ keycode = 0x65;
break;
case 0x52: /* grey insert key */
- code = 0x66;
+ keycode = 0x66;
break;
case 0x53: /* grey delete key */
- code = 0x67;
+ keycode = 0x67;
break;
default: /* ignore everything else */
goto next_code;
}
break;
- case 0x61: /* 0xE1 prefix */
+ case 0xE1: /* 0xE1 prefix */
esc_flag = 0;
- if (code == 0x1D)
+ if (keycode == 0x1D)
esc_flag = 0x1D;
goto next_code;
/* NOT REACHED */
case 0x1D: /* pause / break */
esc_flag = 0;
- if (code != 0x45)
+ if (keycode != 0x45)
goto next_code;
- code = 0x68;
+ keycode = 0x68;
break;
}
if (compose) {
- switch (code) {
- case 0x47:
- case 0x48: /* keypad 7,8,9 */
- case 0x49:
- if (!release)
- chr = (code - 0x40) + chr*10;
+ 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: /* keypad 4,5,6 */
- case 0x4d:
- if (!release)
- chr = (code - 0x47) + chr*10;
+ case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */
+ chr = (scancode - 0x47) + chr*10;
goto next_code;
- case 0x4f:
- case 0x50: /* keypad 1,2,3 */
- case 0x51:
- if (!release)
- chr = (code - 0x4e) + chr*10;
+ case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */
+ chr = (scancode - 0x4E) + chr*10;
goto next_code;
case 0x52: /* keypad 0 */
- if (!release)
- chr *= 10;
+ 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:
@@ -2250,15 +2336,15 @@ next_code:
state = (shfts ? 1 : 0 ) | (2 * (ctls ? 1 : 0)) | (4 * (alts ? 1 : 0));
if ((!agrs && (cur_console->status & ALKED))
|| (agrs && !(cur_console->status & ALKED)))
- code += ALTGR_OFFSET;
- key = &key_map.key[code];
+ 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 (release) { /* key released */
+ if (scancode & 0x80) { /* key released */
if (key->spcl & 0x80) {
switch (action) {
case LSH:
@@ -2316,7 +2402,7 @@ next_code:
cur_console->status &= ~NLKED;
else
cur_console->status |= NLKED;
- update_leds(cur_console->status & LED_MASK);
+ update_leds(cur_console->status);
}
break;
case CLK:
@@ -2326,7 +2412,7 @@ next_code:
cur_console->status &= ~CLKED;
else
cur_console->status |= CLKED;
- update_leds(cur_console->status & LED_MASK);
+ update_leds(cur_console->status);
}
break;
case SLK:
@@ -2338,7 +2424,7 @@ next_code:
}
else
cur_console->status |= SLKED;
- update_leds(cur_console->status & LED_MASK);
+ update_leds(cur_console->status);
}
break;
case ALK:
@@ -2348,6 +2434,7 @@ next_code:
cur_console->status &= ~ALKED;
else
cur_console->status |= ALKED;
+ update_leds(cur_console->status);
}
break;
@@ -2455,6 +2542,7 @@ u_int sgetc(int noblock)
return (scgetc(noblock) & 0xff);
}
+
int pcmmap(dev_t dev, int offset, int nprot)
{
if (offset > 0x20000)
@@ -2465,9 +2553,9 @@ int pcmmap(dev_t dev, int offset, int nprot)
static void kbd_wait(void)
{
- int i;
+ int i = 1000;
- for (i=0; i<1000; i++) { /* up to 10 msec */
+ while (i--) {
if ((inb(KB_STAT) & KB_READY) == 0)
break;
DELAY (10);
@@ -2477,37 +2565,35 @@ static void kbd_wait(void)
static void kbd_cmd(u_char command)
{
- kbd_wait();
- outb(KB_DATA, command);
-}
-
-
-static void kbd_cmd2(u_char command, u_char arg)
-{
- int r, s = spltty();
+ int retry = 5;
do {
- kbd_cmd(command);
- r = kbd_reply();
- if (r == KB_ACK) {
- kbd_cmd(arg & 0x7f);
- r = kbd_reply();
+ 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;
}
- } while (r != KB_ACK);
- splx(s);
-}
-
-
-static int kbd_reply()
-{
- int i;
-
- kbd_wait();
- for (i=0; i<60000; i++) { /* at least 300 msec, 600 msec enough */
- if (inb(KB_STAT) & KB_BUF_FULL)
- return ((u_char) inb(KB_DATA));
- DELAY (10);
- }
- return(-1);
+#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--);
}
@@ -2560,10 +2646,11 @@ static void set_border(int color)
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 */
+ outb(ATC, 0x20); /* enable Palette */
}
-static void load_font(int segment, int size, char* font)
+
+static void copy_font(int direction, int segment, int size, char* font)
{
int ch, line, s;
u_char val;
@@ -2582,9 +2669,14 @@ static void load_font(int segment, int size, char* font)
outb(GDCIDX, 0x06); outb(GDCREG, 0x05); /* addr = a0000, 64kb */
splx(s);
for (ch=0; ch < 256; ch++)
- for (line=0; line < size; line++)
- *((char *)atdevbase+(segment*0x4000)+(ch*32)+line) =
- font[(ch*size)+line];
+ for (line=0; line < size; line++)
+ if (direction)
+ *((char *)atdevbase+(segment*0x4000)+(ch*32)+line) =
+ font[(ch*size)+line];
+ else
+ font[(ch*size)+line] =
+ *((char *)atdevbase+(segment*0x4000)+(ch*32)+line);
+
/* setup vga for text mode again */
s = splhigh();
inb(crtc_addr+6); /* reset flip/flop */
@@ -2617,6 +2709,7 @@ static void load_palette(void)
outb(ATC, 0x20); /* enable palette */
}
+
static void save_palette(void)
{
int i;
diff --git a/sys/i386/isa/tw.c b/sys/i386/isa/tw.c
new file mode 100644
index 000000000000..0a90aa3ace1c
--- /dev/null
+++ b/sys/i386/isa/tw.c
@@ -0,0 +1,997 @@
+/*-
+ * Copyright (c) 1992, 1993 Eugene W. Stark
+ * 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 Eugene W. Stark.
+ * 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 EUGENE W. STARK (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 "tw.h"
+#if NTW > 0
+
+/*
+ * Driver configuration parameters
+ */
+
+/*
+ * Time for 1/2 of a power line cycle, in microseconds.
+ * Change this to 10000 for 50Hz power. Phil Sampson
+ * (vk2jnt@gw.vk2jnt.ampr.org OR sampson@gidday.enet.dec.com)
+ * reports that this works (at least in Australia) using a
+ * TW7223 module (a local version of the TW523).
+ */
+#define HALFCYCLE 8333 /* 1/2 cycle = 8333us at 60Hz */
+
+/*
+ * Undefine the following if you don't have the high-resolution "microtime"
+ * routines (leave defined for FreeBSD, which has them).
+ */
+#define HIRESTIME
+
+/*
+ * End of driver configuration parameters
+ */
+
+/*
+ * 386BSD Device Driver for X-10 POWERHOUSE (tm)
+ * Two-Way Power Line Interface, Model #TW523
+ *
+ * written by Eugene W. Stark (stark@cs.sunysb.edu)
+ * December 2, 1992
+ *
+ * REVISION HISTORY
+ *
+ * Date Who What
+ * 12/2/92 stark Initial Release
+ * 2/7/93 stark Minor upgrades to timing code
+ * 3/4/93 stark Updated to use high-res. "microtime" routines
+ * 5/9/93 stark Minor changes to console error reporting
+ * 10/30/93 stark Clean-up for FreeBSD release
+ * 3/14/93 stark Clean-up for FreeBSD-1.1 release
+ *
+ * NOTES:
+ *
+ * The TW523 is a carrier-current modem for home control/automation purposes.
+ * It is made by:
+ *
+ * X-10 Inc.
+ * 185A LeGrand Ave.
+ * Northvale, NJ 07647
+ * USA
+ * (201) 784-9700 or 1-800-526-0027
+ *
+ * X-10 Home Controls Inc.
+ * 1200 Aerowood Drive, Unit 20
+ * Mississauga, Ontario
+ * (416) 624-4446 or 1-800-387-3346
+ *
+ * The TW523 is designed for communications using the X-10 protocol,
+ * which is compatible with a number of home control systems, including
+ * Radio Shack "Plug 'n Power(tm)" and Stanley "Lightmaker(tm)."
+ * I bought my TW523 from:
+ *
+ * Home Control Concepts
+ * 9353-C Activity Road
+ * San Diego, CA 92126
+ * (619) 693-8887
+ *
+ * They supplied me with the TW523 (which has an RJ-11 four-wire modular
+ * telephone connector), a modular cable, an RJ-11 to DB-25 connector with
+ * internal wiring, documentation from X-10 on the TW523 (very good),
+ * an instruction manual by Home Control Concepts (not very informative),
+ * and a floppy disk containing binary object code of some demonstration/test
+ * programs and of a C function library suitable for controlling the TW523
+ * by an IBM PC under MS-DOS (not useful to me other than to verify that
+ * the unit worked). I suggest saving money and buying the bare TW523
+ * rather than the TW523 development kit (what I bought), because if you
+ * are running 386BSD you don't really care about the DOS binaries.
+ *
+ * The interface to the TW-523 consists of four wires on the RJ-11 connector,
+ * which are jumpered to somewhat more wires on the DB-25 connector, which
+ * in turn is intended to plug into the PC parallel printer port. I dismantled
+ * the DB-25 connector to find out what they had done:
+ *
+ * Signal RJ-11 pin DB-25 pin(s) Parallel Port
+ * Transmit TX 4 (Y) 2, 4, 6, 8 Data out
+ * Receive RX 3 (G) 10, 14 -ACK, -AutoFeed
+ * Common 2 (R) 25 Common
+ * Zero crossing 1 (B) 17 -Select Input
+ *
+ * The zero crossing signal is used to synchronize transmission to the
+ * zero crossings of the AC line, as detailed in the X-10 documentation.
+ * It would be nice if one could generate interrupts with this signal,
+ * however one needs interrupts on both the rising and falling edges,
+ * and the -ACK signal to the parallel port interrupts only on the falling
+ * edge, so it can't be done without additional hardware.
+ *
+ * In this driver, the transmit function is performed in a non-interrupt-driven
+ * fashion, by polling the zero crossing signal to determine when a transition
+ * has occurred. This wastes CPU time during transmission, but it seems like
+ * the best that can be done without additional hardware. One problem with
+ * the scheme is that preemption of the CPU during transmission can cause loss
+ * of sync. The driver tries to catch this, by noticing that a long delay
+ * loop has somehow become foreshortened, and the transmission is aborted with
+ * an error return. It is up to the user level software to handle this
+ * situation (most likely by retrying the transmission).
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "user.h"
+#include "buf.h"
+#include "kernel.h"
+#include "ioctl.h"
+#include "tty.h"
+#include "uio.h"
+#include "syslog.h"
+
+#ifdef HIRESTIME
+#include "time.h"
+#endif /* HIRESTIME */
+
+#include "i386/isa/isa_device.h"
+
+void twdelay25();
+void twdelayn(int n);
+void twsetuptimes(int *a);
+int twprobe();
+int twattach();
+void twintr(int unit);
+
+/*
+ * Transmission is done by calling write() to send three byte packets of data.
+ * The first byte contains a four bit house code (0=A to 15=P).
+ * The second byte contains five bit unit/key code (0=unit 1 to 15=unit 16,
+ * 16=All Units Off to 31 = Status Request). The third byte specifies
+ * the number of times the packet is to be transmitted without any
+ * gaps between successive transmissions. Normally this is 2, as per
+ * the X-10 documentation, but sometimes (e.g. for bright and dim codes)
+ * it can be another value. Each call to write can specify an arbitrary
+ * number of data bytes. An incomplete packet is buffered until a subsequent
+ * call to write() provides data to complete it. At most one packet will
+ * actually be processed in any call to write(). Successive calls to write()
+ * leave a three-cycle gap between transmissions, per the X-10 documentation.
+ *
+ * Reception is done using read().
+ * The driver produces a series of three-character packets.
+ * In each packet, the first character consists of flags,
+ * the second character is a four bit house code (0-15),
+ * and the third character is a five bit key/function code (0-31).
+ * The flags are the following:
+ */
+
+#define TW_RCV_LOCAL 1 /* The packet arrived during a local transmission */
+#define TW_RCV_ERROR 2 /* An invalid/corrupted packet was received */
+
+/*
+ * IBM PC parallel port definitions relevant to TW523
+ */
+
+#define tw_data 0 /* Data to tw523 (R/W) */
+
+#define tw_status 1 /* Status of tw523 (R) */
+#define TWS_RDATA 0x40 /* tw523 receive data */
+
+#define tw_control 2 /* Control tw523 (R/W) */
+#define TWC_SYNC 0x08 /* tw523 sync (pin 17) */
+#define TWC_ENA 0x10 /* tw523 interrupt enable */
+
+/*
+ * Miscellaneous defines
+ */
+
+#define TWUNIT(dev) (minor(dev)) /* Extract unit number from device */
+#define TWPRI (PZERO+8) /* I don't know any better, so let's */
+ /* use the same as the line printer */
+
+struct isa_driver twdriver = {
+ twprobe, twattach, "tw"
+};
+
+/*
+ * Software control structure for TW523
+ */
+
+#define TWS_XMITTING 1 /* Transmission in progress */
+#define TWS_RCVING 2 /* Reception in progress */
+#define TWS_WANT 4 /* A process wants received data */
+#define TWS_OPEN 8 /* Is it currently open? */
+#define TWS_COLL 16 /* Collision in select */
+
+#define TW_SIZE 3*60 /* Enough for about 10 sec. of input */
+
+struct tw_sc {
+ u_int sc_port; /* I/O Port */
+ u_int sc_state; /* Current software control state */
+ pid_t sc_selp; /* Process sleeping on select */
+ u_char sc_xphase; /* Current state of sync (for transmitter) */
+ u_char sc_rphase; /* Current state of sync (for receiver) */
+ u_char sc_flags; /* Flags for current reception */
+ short sc_rcount; /* Number of bits received so far */
+ int sc_bits; /* Bits received so far */
+ u_char sc_pkt[3]; /* Packet not yet transmitted */
+ short sc_pktsize; /* How many bytes in the packet? */
+ u_char sc_buf[TW_SIZE]; /* We buffer our own input */
+ int sc_nextin; /* Next free slot in circular buffer */
+ int sc_nextout; /* First used slot in circular buffer */
+#ifdef HIRESTIME
+ int sc_xtimes[22]; /* Times for bits in current xmit packet */
+ int sc_rtimes[22]; /* Times for bits in current rcv packet */
+#endif /* HIRESTIME */
+} tw_sc[NTW];
+
+/*
+ * Counter value for delay loop.
+ * It is adjusted by twprobe so that the delay loop takes about 25us.
+ */
+
+#define TWDELAYCOUNT 161 /* Works on my 486DX/33 */
+int twdelaycount;
+
+/*
+ * Twdelay25 is used for very short delays of about 25us.
+ * It is implemented with a calibrated delay loop, and should be
+ * fairly accurate ... unless we are preempted by an interrupt.
+ *
+ * We use this to wait for zero crossings because the X-10 specs say we
+ * are supposed to assert carrier within 25us when one happens.
+ * I don't really believe we can do this, but the X-10 devices seem to be
+ * fairly forgiving.
+ */
+
+void twdelay25()
+{
+ int cnt;
+ for(cnt = twdelaycount; cnt; cnt--); /* Should take about 25us */
+}
+
+/*
+ * Twdelayn is used to time the length of the 1ms carrier pulse.
+ * This is not very critical, but if we have high-resolution time-of-day
+ * we check it every apparent 200us to make sure we don't get too far off
+ * if we happen to be interrupted during the delay.
+ */
+
+void twdelayn(int n)
+{
+#ifdef HIRESTIME
+ int t, d;
+ struct timeval tv;
+ microtime(&tv);
+ t = tv.tv_usec;
+ t += n;
+#endif /* HIRESTIME */
+ while(n > 0) {
+ twdelay25();
+ n -= 25;
+#ifdef HIRESTIME
+ if((n & 0x7) == 0) {
+ microtime(&tv);
+ d = tv.tv_usec - t;
+ if(d >= 0 && d < 1000000) return;
+ }
+#endif /* HIRESTIME */
+ }
+}
+
+int twprobe(idp)
+ struct isa_device *idp;
+{
+ struct tw_sc sc;
+ int d;
+ int tries;
+
+ sc.sc_port = idp->id_iobase;
+ /*
+ * Iteratively check the timing of a few sync transitions, and adjust
+ * the loop delay counter, if necessary, to bring the timing reported
+ * by wait_for_zero() close to HALFCYCLE. Give up if anything
+ * ridiculous happens.
+ */
+ if(twdelaycount == 0) { /* Only adjust timing for first unit */
+ twdelaycount = TWDELAYCOUNT;
+ for(tries = 0; tries < 10; tries++) {
+ sc.sc_xphase = inb(idp->id_iobase + tw_control) & TWC_SYNC;
+ if(wait_for_zero(&sc) >= 0) {
+ d = wait_for_zero(&sc);
+ if(d <= HALFCYCLE/100 || d >= HALFCYCLE*100) {
+ twdelaycount = 0;
+ return(0);
+ }
+ twdelaycount = (twdelaycount * d)/HALFCYCLE;
+ }
+ }
+ }
+ /*
+ * Now do a final check, just to make sure
+ */
+ sc.sc_xphase = inb(idp->id_iobase + tw_control) & TWC_SYNC;
+ if(wait_for_zero(&sc) >= 0) {
+ d = wait_for_zero(&sc);
+ if(d <= (HALFCYCLE * 110)/100 && d >= (HALFCYCLE * 90)/100) return(1);
+ }
+ return(0);
+}
+
+int twattach(idp)
+ struct isa_device *idp;
+{
+ struct tw_sc *sc;
+
+ sc = &tw_sc[idp->id_unit];
+ sc->sc_port = idp->id_iobase;
+ sc->sc_state = 0;
+ return (1);
+}
+
+int twopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+ struct tw_sc *sc = &tw_sc[TWUNIT(dev)];
+ int s;
+ int port;
+
+ s = spltty();
+ if(sc->sc_state == 0) {
+ sc->sc_state = TWS_OPEN;
+ sc->sc_nextin = sc->sc_nextout = 0;
+ sc->sc_pktsize = 0;
+ sc->sc_selp = 0;
+ outb(sc->sc_port+tw_control, TWC_ENA);
+ }
+ splx(s);
+ return(0);
+}
+
+int twclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+ struct tw_sc *sc = &tw_sc[TWUNIT(dev)];
+ int s;
+ int port = sc->sc_port;
+
+ s = spltty();
+ sc->sc_state = 0;
+ outb(sc->sc_port+tw_control, 0);
+ splx(s);
+ return(0);
+}
+
+int twread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ u_char buf[3];
+ struct tw_sc *sc = &tw_sc[TWUNIT(dev)];
+ int error, cnt, s;
+
+ s = spltty();
+ cnt = MIN(uio->uio_resid, 3);
+ if((error = twgetbytes(sc, buf, cnt)) == 0) {
+ error = uiomove(buf, cnt, uio);
+ }
+ splx(s);
+ return(error);
+}
+
+int twwrite(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ struct tw_sc *sc;
+ int house, key, reps;
+ int s, error;
+ int cnt;
+
+ sc = &tw_sc[TWUNIT(dev)];
+ /*
+ * Note: Although I had intended to allow concurrent transmitters,
+ * there is a potential problem here if two processes both write
+ * into the sc_pkt buffer at the same time. The following code
+ * is an additional critical section that needs to be synchronized.
+ */
+ s = spltty();
+ cnt = MIN(3 - sc->sc_pktsize, uio->uio_resid);
+ if(error = uiomove(&(sc->sc_pkt[sc->sc_pktsize]), cnt, uio)) {
+ splx(s);
+ return(error);
+ }
+ sc->sc_pktsize += cnt;
+ if(sc->sc_pktsize < 3) { /* Only transmit 3-byte packets */
+ splx(s);
+ return(0);
+ }
+ sc->sc_pktsize = 0;
+ /*
+ * Collect house code, key code, and rep count, and check for sanity.
+ */
+ house = sc->sc_pkt[0];
+ key = sc->sc_pkt[1];
+ reps = sc->sc_pkt[2];
+ if(house >= 16 || key >= 32) {
+ splx(s);
+ return(ENODEV);
+ }
+ /*
+ * Synchronize with the receiver operating in the bottom half, and
+ * also with concurrent transmitters.
+ * We don't want to interfere with a packet currently being received,
+ * and we would like the receiver to recognize when a packet has
+ * originated locally.
+ */
+ while(sc->sc_state & (TWS_RCVING | TWS_XMITTING)) {
+ if(error = tsleep((caddr_t)sc, TWPRI|PCATCH, "twwrite", 0)) {
+ splx(s);
+ return(error);
+ }
+ }
+ sc->sc_state |= TWS_XMITTING;
+ /*
+ * Everything looks OK, let's do the transmission.
+ */
+ splx(s); /* Enable interrupts because this takes a LONG time */
+ error = twsend(sc, house, key, reps);
+ s = spltty();
+ sc->sc_state &= ~TWS_XMITTING;
+ wakeup((caddr_t)sc);
+ splx(s);
+ if(error) return(EIO);
+ else return(0);
+}
+
+/*
+ * Determine if there is data available for reading
+ */
+
+int twselect(dev, rw, p)
+ dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ struct tw_sc *sc;
+ struct proc *pp;
+ int s, i;
+
+ sc = &tw_sc[TWUNIT(dev)];
+ s = spltty();
+ if(sc->sc_nextin != sc->sc_nextout) {
+ splx(s);
+ return(1);
+ }
+ if(sc->sc_selp && (pp = pfind(sc->sc_selp))
+ && pp->p_wchan == (caddr_t)&selwait) {
+ sc->sc_state |= TWS_COLL;
+ } else {
+ sc->sc_selp = p->p_pid;
+ }
+ splx(s);
+ return(0);
+}
+
+/*
+ * X-10 Protocol
+ */
+
+#define X10_START_LENGTH 4
+char X10_START[] = { 1, 1, 1, 0 };
+
+/*
+ * Each bit of the 4-bit house code and 5-bit key code
+ * is transmitted twice, once in true form, and then in
+ * complemented form. This is already taken into account
+ * in the following tables.
+ */
+
+#define X10_HOUSE_LENGTH 8
+char X10_HOUSE[16][8] = {
+ 0, 1, 1, 0, 1, 0, 0, 1, /* A = 0110 */
+ 1, 0, 1, 0, 1, 0, 0, 1, /* B = 1110 */
+ 0, 1, 0, 1, 1, 0, 0, 1, /* C = 0010 */
+ 1, 0, 0, 1, 1, 0, 0, 1, /* D = 1010 */
+ 0, 1, 0, 1, 0, 1, 1, 0, /* E = 0001 */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* F = 1001 */
+ 0, 1, 1, 0, 0, 1, 1, 0, /* G = 0101 */
+ 1, 0, 1, 0, 0, 1, 1, 0, /* H = 1101 */
+ 0, 1, 1, 0, 1, 0, 1, 0, /* I = 0111 */
+ 1, 0, 1, 0, 1, 0, 1, 0, /* J = 1111 */
+ 0, 1, 0, 1, 1, 0, 1, 0, /* K = 0011 */
+ 1, 0, 0, 1, 1, 0, 1, 0, /* L = 1011 */
+ 0, 1, 0, 1, 0, 1, 0, 1, /* M = 0000 */
+ 1, 0, 0, 1, 0, 1, 0, 1, /* N = 1000 */
+ 0, 1, 1, 0, 0, 1, 0, 1, /* O = 0100 */
+ 1, 0, 1, 0, 0, 1, 0, 1 /* P = 1100 */
+};
+
+#define X10_KEY_LENGTH 10
+char X10_KEY[32][10] = {
+ 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, /* 01100 => 1 */
+ 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, /* 11100 => 2 */
+ 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, /* 00100 => 3 */
+ 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, /* 10100 => 4 */
+ 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, /* 00010 => 5 */
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, /* 10010 => 6 */
+ 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, /* 01010 => 7 */
+ 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, /* 11010 => 8 */
+ 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, /* 01110 => 9 */
+ 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, /* 11110 => 10 */
+ 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, /* 00110 => 11 */
+ 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 10110 => 12 */
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, /* 00000 => 13 */
+ 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 10000 => 14 */
+ 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, /* 01000 => 15 */
+ 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, /* 11000 => 16 */
+ 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, /* 00001 => All Units Off */
+ 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, /* 00011 => All Units On */
+ 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, /* 00101 => On */
+ 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, /* 00111 => Off */
+ 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 01001 => Dim */
+ 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, /* 01011 => Bright */
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, /* 01101 => All LIGHTS Off */
+ 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, /* 01111 => Extended Code */
+ 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, /* 10001 => Hail Request */
+ 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, /* 10011 => Hail Acknowledge */
+ 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, /* 10101 => Preset Dim 0 */
+ 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, /* 10111 => Preset Dim 1 */
+ 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, /* 11000 => Extended Data (analog) */
+ 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, /* 11011 => Status = on */
+ 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, /* 11101 => Status = off */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 /* 11111 => Status request */
+};
+
+/*
+ * Tables for mapping received X-10 code back to house/key number.
+ */
+
+short X10_HOUSE_INV[16] = { 12, 4, 2, 10, 14, 6, 0, 8,
+ 13, 5, 3, 11, 15, 7, 1, 9 };
+
+short X10_KEY_INV[32] = { 12, 16, 4, 17, 2, 18, 10, 19,
+ 14, 20, 6, 21, 0, 22, 8, 23,
+ 13, 24, 5, 25, 3, 26, 11, 27,
+ 15, 28, 7, 29, 1, 30, 9, 31 };
+
+/*
+ * Transmit a packet containing house code h and key code k
+ */
+
+#define TWRETRY 10 /* Try 10 times to sync with AC line */
+
+int twsend(sc, h, k, cnt)
+struct tw_sc *sc;
+int h, k, cnt;
+{
+ int i;
+ int port = sc->sc_port;
+
+ /*
+ * Make sure we get a reliable sync with a power line zero crossing
+ */
+ for(i = 0; i < TWRETRY; i++) {
+ if(wait_for_zero(sc) > 100) goto insync;
+ }
+ log(LOG_ERR, "TWXMIT: failed to sync.\n");
+ return(-1);
+
+ insync:
+ /*
+ * Be sure to leave 3 cycles space between transmissions
+ */
+ for(i = 6; i > 0; i--)
+ if(next_zero(sc) < 0) return(-1);
+ /*
+ * The packet is transmitted cnt times, with no gaps.
+ */
+ while(cnt--) {
+ /*
+ * Transmit the start code
+ */
+ for(i = 0; i < X10_START_LENGTH; i++) {
+ outb(port+tw_data, X10_START[i] ? 0xff : 0x00); /* Waste no time! */
+#ifdef HIRESTIME
+ if(i == 0) twsetuptimes(sc->sc_xtimes);
+ if(twchecktime(sc->sc_xtimes[i], HALFCYCLE/20) == 0) {
+ outb(port+tw_data, 0);
+ return(-1);
+ }
+#endif /* HIRESTIME */
+ twdelayn(1000); /* 1ms pulse width */
+ outb(port+tw_data, 0);
+ if(next_zero(sc) < 0) return(-1);
+ }
+ /*
+ * Transmit the house code
+ */
+ for(i = 0; i < X10_HOUSE_LENGTH; i++) {
+ outb(port+tw_data, X10_HOUSE[h][i] ? 0xff : 0x00); /* Waste no time! */
+#ifdef HIRESTIME
+ if(twchecktime(sc->sc_xtimes[i+X10_START_LENGTH], HALFCYCLE/20) == 0) {
+ outb(port+tw_data, 0);
+ return(-1);
+ }
+#endif /* HIRESTIME */
+ twdelayn(1000); /* 1ms pulse width */
+ outb(port+tw_data, 0);
+ if(next_zero(sc) < 0) return(-1);
+ }
+ /*
+ * Transmit the unit/key code
+ */
+ for(i = 0; i < X10_KEY_LENGTH; i++) {
+ outb(port+tw_data, X10_KEY[k][i] ? 0xff : 0x00);
+#ifdef HIRESTIME
+ if(twchecktime(sc->sc_xtimes[i+X10_START_LENGTH+X10_HOUSE_LENGTH],
+ HALFCYCLE/20) == 0) {
+ outb(port+tw_data, 0);
+ return(-1);
+ }
+#endif /* HIRESTIME */
+ twdelayn(1000); /* 1ms pulse width */
+ outb(port+tw_data, 0);
+ if(next_zero(sc) < 0) return(-1);
+ }
+ }
+ return(0);
+}
+
+/*
+ * Waste CPU cycles to get in sync with a power line zero crossing.
+ * The value returned is roughly how many microseconds we wasted before
+ * seeing the transition. To avoid wasting time forever, we give up after
+ * waiting patiently for 1/4 sec (15 power line cycles at 60 Hz),
+ * which is more than the 11 cycles it takes to transmit a full
+ * X-10 packet.
+ */
+
+int wait_for_zero(sc)
+struct tw_sc *sc;
+{
+ int i, old, new, max, cnt;
+ int port = sc->sc_port + tw_control;
+
+ old = sc->sc_xphase;
+ max = 10000; /* 10000 * 25us = 0.25 sec */
+ i = 0;
+ while(max--) {
+ new = inb(port) & TWC_SYNC;
+ if(new != old) {
+ sc->sc_xphase = new;
+ return(i*25);
+ }
+ i++;
+ twdelay25();
+ }
+ return(-1);
+}
+
+/*
+ * Wait for the next zero crossing transition, and if we don't have
+ * high-resolution time-of-day, check to see that the zero crossing
+ * appears to be arriving on schedule.
+ * We expect to be waiting almost a full half-cycle (8.333ms-1ms = 7.333ms).
+ * If we don't seem to wait very long, something is wrong (like we got
+ * preempted!) and we should abort the transmission because
+ * there's no telling how long it's really been since the
+ * last bit was transmitted.
+ */
+
+int next_zero(sc)
+struct tw_sc *sc;
+{
+ int d;
+#ifdef HIRESTIME
+ if((d = wait_for_zero(sc)) < 0) {
+#else
+ if((d = wait_for_zero(sc)) < 6000 || d > 8500) {
+ /* No less than 6.0ms, no more than 8.5ms */
+#endif /* HIRESTIME */
+ log(LOG_ERR, "TWXMIT framing error: %d\n", d);
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * Put a three-byte packet into the circular buffer
+ * Should be called at priority spltty()
+ */
+
+int twputpkt(sc, p)
+struct tw_sc *sc;
+u_char *p;
+{
+ int i, next;
+
+ for(i = 0; i < 3; i++) {
+ next = sc->sc_nextin+1;
+ if(next >= TW_SIZE) next = 0;
+ if(next == sc->sc_nextout) { /* Buffer full */
+/*
+ log(LOG_ERR, "TWRCV: Buffer overrun\n");
+ */
+ return(1);
+ }
+ sc->sc_buf[sc->sc_nextin] = *p++;
+ sc->sc_nextin = next;
+ }
+ if(sc->sc_state & TWS_WANT) {
+ sc->sc_state &= ~TWS_WANT;
+ wakeup((caddr_t)(&sc->sc_buf));
+ }
+ if(sc->sc_selp) {
+ selwakeup(sc->sc_selp, sc->sc_state & TWS_COLL);
+ sc->sc_selp = 0;
+ sc->sc_state &= ~TWS_COLL;
+ }
+ return(0);
+}
+
+/*
+ * Get bytes from the circular buffer
+ * Should be called at priority spltty()
+ */
+
+int twgetbytes(sc, p, cnt)
+struct tw_sc *sc;
+u_char *p;
+int cnt;
+{
+ int error;
+
+ while(cnt--) {
+ while(sc->sc_nextin == sc->sc_nextout) { /* Buffer empty */
+ sc->sc_state |= TWS_WANT;
+ if(error = tsleep((caddr_t)(&sc->sc_buf), TWPRI|PCATCH, "twread", 0)) {
+ return(error);
+ }
+ }
+ *p++ = sc->sc_buf[sc->sc_nextout++];
+ if(sc->sc_nextout >= TW_SIZE) sc->sc_nextout = 0;
+ }
+ return(0);
+}
+
+/*
+ * Abort reception that has failed to complete in the required time.
+ */
+
+void twabortrcv(sc)
+struct tw_sc *sc;
+{
+ int s;
+ u_char pkt[3];
+
+ s = spltty();
+ sc->sc_state &= ~TWS_RCVING;
+ sc->sc_flags |= TW_RCV_ERROR;
+ pkt[0] = sc->sc_flags;
+ pkt[1] = pkt[2] = 0;
+ twputpkt(sc, pkt);
+ log(LOG_ERR, "TWRCV: aborting (%x, %d)\n", sc->sc_bits, sc->sc_rcount);
+ wakeup((caddr_t)sc);
+ splx(s);
+}
+
+/*
+ * This routine handles interrupts that occur when there is a falling
+ * transition on the RX input. There isn't going to be a transition
+ * on every bit (some are zero), but if we are smart and keep track of
+ * how long it's been since the last interrupt (via the zero crossing
+ * detect line and/or high-resolution time-of-day routine), we can
+ * reconstruct the transmission without having to poll.
+ */
+
+void twintr(unit)
+int unit;
+{
+ struct tw_sc *sc = &tw_sc[unit];
+ int port;
+ int newphase;
+ u_char pkt[3];
+
+ port = sc->sc_port;
+ /*
+ * Ignore any interrupts that occur if the device is not open.
+ */
+ if(sc->sc_state == 0) return;
+ newphase = inb(port + tw_control) & TWC_SYNC;
+ /*
+ * NEW PACKET:
+ * If we aren't currently receiving a packet, set up a new packet
+ * and put in the first "1" bit that has just arrived.
+ * Arrange for the reception to be aborted if too much time goes by.
+ */
+ if((sc->sc_state & TWS_RCVING) == 0) {
+#ifdef HIRESTIME
+ twsetuptimes(sc->sc_rtimes);
+#endif /* HIRESTIME */
+ sc->sc_state |= TWS_RCVING;
+ sc->sc_rcount = 1;
+ if(sc->sc_state & TWS_XMITTING) sc->sc_flags = TW_RCV_LOCAL;
+ else sc->sc_flags = 0;
+ sc->sc_bits = 0;
+ sc->sc_rphase = newphase;
+ timeout((timeout_func_t)twabortrcv, (caddr_t)sc, hz/4);
+ return;
+ }
+ /*
+ * START CODE:
+ * The second and third bits are a special case.
+ */
+ if(sc->sc_rcount < 3) {
+#ifdef HIRESTIME
+ if(twchecktime(sc->sc_rtimes[sc->sc_rcount], HALFCYCLE/3)
+ && newphase != sc->sc_rphase) {
+#else
+ if(newphase != sc->sc_rphase) {
+#endif
+ sc->sc_rcount++;
+ } else {
+ /*
+ * Invalid start code -- abort reception.
+ */
+ sc->sc_state &= ~TWS_RCVING;
+ sc->sc_flags |= TW_RCV_ERROR;
+/*
+ pkt[0] = sc->sc_flags;
+ pkt[1] = pkt[2] = 0;
+ twputpkt(sc, pkt);
+ wakeup((caddr_t)sc);
+ */
+ untimeout((timeout_func_t)twabortrcv, (caddr_t)sc);
+ log(LOG_ERR, "TWRCV: Invalid start code\n");
+ return;
+ }
+ if(sc->sc_rcount == 3) {
+ /*
+ * We've gotten three "1" bits in a row. The start code
+ * is really 1110, but this might be followed by a zero
+ * bit from the house code, so if we wait any longer we
+ * might be confused about the first house code bit.
+ * So, we guess that the start code is correct and insert
+ * the trailing zero without actually having seen it.
+ * We don't change sc_rphase in this case, because two
+ * bit arrivals in a row preserve parity.
+ */
+ sc->sc_rcount++;
+ return;
+ }
+ /*
+ * Update sc_rphase to the current phase before returning.
+ */
+ sc->sc_rphase = newphase;
+ return;
+ }
+ /*
+ * GENERAL CASE:
+ * Now figure out what the current bit is that just arrived.
+ * The X-10 protocol transmits each data bit twice: once in
+ * true form and once in complemented form on the next half
+ * cycle. So, there will be at least one interrupt per bit.
+ * By comparing the phase we see at the time of the interrupt
+ * with the saved sc_rphase, we can tell on which half cycle
+ * the interrupt occrred. This assumes, of course, that the
+ * packet is well-formed. We do the best we can at trying to
+ * catch errors by aborting if too much time has gone by, and
+ * by tossing out a packet if too many bits arrive, but the
+ * whole scheme is probably not as robust as if we had a nice
+ * interrupt on every half cycle of the power line.
+ * If we have high-resolution time-of-day routines, then we
+ * can do a bit more sanity checking.
+ */
+
+ /*
+ * A complete packet is 22 half cycles.
+ */
+ if(sc->sc_rcount <= 20) {
+#ifdef HIRESTIME
+ if((newphase == sc->sc_rphase &&
+ twchecktime(sc->sc_rtimes[sc->sc_rcount+1], HALFCYCLE/3) == 0)
+ || (newphase != sc->sc_rphase &&
+ twchecktime(sc->sc_rtimes[sc->sc_rcount], HALFCYCLE/3) == 0)) {
+ sc->sc_flags |= TW_RCV_ERROR;
+ } else {
+#endif /* HIRESTIME */
+ sc->sc_bits = (sc->sc_bits << 1)
+ | ((newphase == sc->sc_rphase) ? 0x0 : 0x1);
+ sc->sc_rcount += 2;
+#ifdef HIRESTIME
+ }
+#endif /* HIRESTIME */
+ }
+ if(sc->sc_rcount >= 22 || sc->sc_flags & TW_RCV_ERROR) {
+ if(sc->sc_rcount != 22) {
+ sc->sc_flags |= TW_RCV_ERROR;
+ pkt[0] = sc->sc_flags;
+ pkt[1] = pkt[2] = 0;
+ } else {
+ pkt[0] = sc->sc_flags;
+ pkt[1] = X10_HOUSE_INV[(sc->sc_bits & 0x1e0) >> 5];
+ pkt[2] = X10_KEY_INV[sc->sc_bits & 0x1f];
+ }
+ sc->sc_state &= ~TWS_RCVING;
+ twputpkt(sc, pkt);
+ untimeout((timeout_func_t)twabortrcv, (caddr_t)sc);
+ if(sc->sc_flags & TW_RCV_ERROR)
+ log(LOG_ERR, "TWRCV: invalid packet: (%d, %x)\n",
+ sc->sc_rcount, sc->sc_bits);
+ wakeup((caddr_t)sc);
+ }
+}
+
+#ifdef HIRESTIME
+/*
+ * Initialize an array of 22 times, starting from the current
+ * microtime and continuing for the next 21 half cycles.
+ * We use the times as a reference to make sure transmission
+ * or reception is on schedule.
+ */
+
+void twsetuptimes(int *a)
+{
+ struct timeval tv;
+ int i, t;
+
+ microtime(&tv);
+ t = tv.tv_usec;
+ for(i = 0; i < 22; i++) {
+ *a++ = t;
+ t += HALFCYCLE;
+ if(t >= 1000000) t -= 1000000;
+ }
+}
+
+/*
+ * Check the current time against a slot in a previously set up
+ * timing array, and make sure that it looks like we are still
+ * on schedule.
+ */
+
+int twchecktime(int target, int tol)
+{
+ struct timeval tv;
+ int t, d;
+
+ microtime(&tv);
+ t = tv.tv_usec;
+ d = (target - t) >= 0 ? (target - t) : (t - target);
+ if(d > 500000) d = 1000000-d;
+ if(d <= tol && d >= -tol) {
+ return(1);
+ } else {
+ log(LOG_ERR, "TWCHK: timing off by %dus (>= %dus)\n", d, tol);
+ return(0);
+ }
+}
+#endif /* HIRESTIME */
+
+#endif NTW
diff --git a/sys/i386/isa/ultra14f.c b/sys/i386/isa/ultra14f.c
index 230868b33746..d2bc4c39b171 100644
--- a/sys/i386/isa/ultra14f.c
+++ b/sys/i386/isa/ultra14f.c
@@ -19,7 +19,7 @@
* commenced: Sun Sep 27 18:14:01 PDT 1992
* slight mod to make work with 34F as well: Wed Jun 2 18:05:48 WST 1993
*
- * $Id: ultra14f.c,v 1.15 1994/01/29 10:29:14 rgrimes Exp $
+ * $Id: ultra14f.c,v 1.17 1994/03/24 02:23:00 davidg Exp $
*/
#include <sys/types.h>
@@ -465,6 +465,7 @@ uha_attach(dev)
uha->sc_link.adapter_targ = uha->our_id;
uha->sc_link.adapter = &uha_switch;
uha->sc_link.device = &uha_dev;
+ uha->sc_link.flags = SDEV_BOUNCE;
/*
* ask the adapter what subunits are present
@@ -675,7 +676,8 @@ uha_get_mscp(unit, flags)
goto gottit;
} else {
if (!(flags & SCSI_NOSLEEP)) {
- sleep(&uha->free_mscp, PRIBIO);
+ tsleep((caddr_t)&uha->free_mscp, PRIBIO,
+ "uhamscp", 0);
}
}
}
diff --git a/sys/i386/isa/vector.s b/sys/i386/isa/vector.s
index f6bb78fce97a..835da9cdf320 100644
--- a/sys/i386/isa/vector.s
+++ b/sys/i386/isa/vector.s
@@ -1,6 +1,6 @@
/*
* from: vector.s, 386BSD 0.1 unknown origin
- * $Id: vector.s,v 1.6 1994/01/10 23:15:09 ache Exp $
+ * $Id: vector.s,v 1.7 1994/04/02 07:00:50 davidg Exp $
*/
#include "i386/isa/icu.h"
@@ -12,24 +12,44 @@
#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 */
+#else
#define ENABLE_ICU1 \
movb $ICU_EOI,%al ; /* as soon as possible send EOI ... */ \
FASTER_NOP ; /* ... ASAP ... */ \
outb %al,$IO_ICU1 /* ... to clear in service bit */
-#ifdef AUTO_EOI_1
-#undef ENABLE_ICU1 /* we now use auto-EOI to reduce i/o */
-#define ENABLE_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 */ \
FASTER_NOP ; \
outb %al,$IO_ICU2 ; /* but do second icu first */ \
FASTER_NOP ; \
outb %al,$IO_ICU1 /* then first icu */
-#ifdef AUTO_EOI_2
-#undef ENABLE_ICU1_AND_2 /* data sheet says no auto-EOI on slave ... */
-#define ENABLE_ICU1_AND_2 /* ... but it works */
+#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
/*
@@ -82,39 +102,63 @@
pushl %ecx ; \
pushl %edx ; \
pushl %ds ; \
- /* pushl %es ; know compiler doesn't do string insns */ \
+ MAYBE_PUSHL_ES ; \
movl $KDSEL,%eax ; \
movl %ax,%ds ; \
- /* movl %ax,%es ; */ \
- SHOW_CLI ; /* although it interferes with "ASAP" */ \
+ MAYBE_MOVW_AX_ES ; \
+ FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \
pushl $unit ; \
call handler ; /* do the work ASAP */ \
enable_icus ; /* (re)enable ASAP (helps edge trigger?) */ \
addl $4,%esp ; \
incl _cnt+V_INTR ; /* book-keeping can wait */ \
- COUNT_EVENT(_intrcnt_actv, id_num) ; \
- SHOW_STI ; \
- /* popl %es ; */ \
+ incl _intrcnt_actv + (id_num) * 4 ; \
+ 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
+ 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,(1+ACTUALLY_PUSHED)*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 */ \
+ MEXITCOUNT ; \
+ jmp _doreti
#define INTR(unit, irq_num, id_num, mask, handler, icu, enable_icus, reg, stray) \
- pushl $0 ; /* dummy error code */ \
- pushl $T_ASTFLT ; \
+ pushl $0 ; /* dumby error code */ \
+ pushl $0 ; /* dumby trap type */ \
pushal ; \
- pushl %ds ; /* save our data and extra segments ... */ \
+ pushl %ds ; /* save our data and extra segments ... */ \
pushl %es ; \
movl $KDSEL,%eax ; /* ... and reload with kernel's own ... */ \
- movl %ax,%ds ; /* ... early in case SHOW_A_LOT is on */ \
+ movl %ax,%ds ; /* ... early for obsolete reasons */ \
movl %ax,%es ; \
- SHOW_CLI ; /* interrupt did an implicit cli */ \
movb _imen + IRQ_BYTE(irq_num),%al ; \
orb $IRQ_BIT(irq_num),%al ; \
movb %al,_imen + IRQ_BYTE(irq_num) ; \
- SHOW_IMEN ; \
FASTER_NOP ; \
outb %al,$icu+1 ; \
enable_icus ; \
@@ -123,32 +167,32 @@
testb $IRQ_BIT(irq_num),%reg ; \
jne 2f ; \
1: ; \
- COUNT_EVENT(_intrcnt_actv, id_num) ; \
+ FAKE_MCOUNT(12*4(%esp)) ; /* XXX late to avoid double count */ \
+ incl _intrcnt_actv + (id_num) * 4 ; \
movl _cpl,%eax ; \
pushl %eax ; \
pushl $unit ; \
orl mask,%eax ; \
movl %eax,_cpl ; \
- SHOW_CPL ; \
- SHOW_STI ; \
sti ; \
call handler ; \
movb _imen + IRQ_BYTE(irq_num),%al ; \
andb $~IRQ_BIT(irq_num),%al ; \
movb %al,_imen + IRQ_BYTE(irq_num) ; \
- SHOW_IMEN ; \
FASTER_NOP ; \
outb %al,$icu+1 ; \
- jmp doreti ; \
+ 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: ; \
- COUNT_EVENT(_intrcnt_pend, id_num) ; \
+ /* XXX skip mcounting here to avoid double count */ \
movl $1b,%eax ; /* register resume address */ \
/* XXX - someday do it at attach time */ \
- movl %eax,Vresume + (irq_num) * 4 ; \
+ movl %eax,ihandlers + (irq_num) * 4 ; \
orb $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num) ; \
- SHOW_IPENDING ; \
popl %es ; \
popl %ds ; \
popal ; \
@@ -191,7 +235,7 @@
.globl _V/**/name ; \
SUPERALIGN_TEXT ; \
_V/**/name: ; \
- FAST_INTR(unit, irq_num, id_num, handler, ENABLE_ICU/**/icu_enables)
+ FAST_INTR(unit, irq_num,id_num, handler, ENABLE_ICU/**/icu_enables)
#undef BUILD_VECTOR
#define BUILD_VECTOR(name, unit, irq_num, id_num, mask, handler, \
@@ -201,9 +245,10 @@ _V/**/name: ; \
.globl _V/**/name ; \
SUPERALIGN_TEXT ; \
_V/**/name: ; \
- INTR(unit,irq_num,id_num, mask, handler, IO_ICU/**/icu_num, \
+ INTR(unit,irq_num, id_num, mask, handler, IO_ICU/**/icu_num, \
ENABLE_ICU/**/icu_enables, reg,)
+MCOUNT_LABEL(bintr)
BUILD_VECTORS
/* hardware interrupt catcher (IDT 32 - 47) */
@@ -211,7 +256,7 @@ _V/**/name: ; \
#define STRAYINTR(irq_num, icu_num, icu_enables, reg) \
IDTVEC(intr/**/irq_num) ; \
- INTR(irq_num,irq_num,irq_num, _highmask, _isa_strayintr, \
+ INTR(irq_num,irq_num,irq_num, _high_imask, _isa_strayintr, \
IO_ICU/**/icu_num, ENABLE_ICU/**/icu_enables, reg,stray)
/*
@@ -241,6 +286,7 @@ IDTVEC(intr/**/irq_num) ; \
STRAYINTR(4,1,1, al)
STRAYINTR(5,1,1, al)
STRAYINTR(6,1,1, al)
+ STRAYINTR(7,1,1, al)
STRAYINTR(8,2,1_AND_2, ah)
STRAYINTR(9,2,1_AND_2, ah)
STRAYINTR(10,2,1_AND_2, ah)
@@ -249,11 +295,11 @@ IDTVEC(intr/**/irq_num) ; \
STRAYINTR(13,2,1_AND_2, ah)
STRAYINTR(14,2,1_AND_2, ah)
STRAYINTR(15,2,1_AND_2, ah)
-IDTVEC(intrdefault)
- STRAYINTR(7,1,1, al) /* XXX */
#if 0
INTRSTRAY(255, _highmask, 255) ; call _isa_strayintr ; INTREXIT2
#endif
+MCOUNT_LABEL(eintr)
+
/*
* These are the interrupt counters, I moved them here from icu.s so that
* they are with the name table. rgrimes
@@ -263,7 +309,15 @@ IDTVEC(intrdefault)
* work with vmstat.
*/
.data
-Vresume: .space 32 * 4 /* where to resume intr handler after unpend */
+ihandlers: /* addresses of interrupt handlers */
+ .space NHWI*4 /* actually resumption addresses for HWI's */
+ .long swi_tty, swi_net, 0, 0, 0, 0, 0, 0
+ .long 0, 0, 0, 0, 0, 0, swi_clock, 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 _intrcnt
_intrcnt: /* used by vmstat to calc size of table */
.globl _intrcnt_bad7
@@ -274,14 +328,8 @@ _intrcnt_bad15: .space 4 /* glitches on irq 15 */
_intrcnt_stray: .space 4 /* total count of stray interrupts */
.globl _intrcnt_actv
_intrcnt_actv: .space NR_REAL_INT_HANDLERS * 4 /* active interrupts */
- .globl _intrcnt_pend
-_intrcnt_pend: .space NR_REAL_INT_HANDLERS * 4 /* pending interrupts */
.globl _eintrcnt
_eintrcnt: /* used by vmstat to calc size of table */
- .globl _intrcnt_spl
-_intrcnt_spl: .space 32 * 4 /* XXX 32 should not be hard coded ? */
- .globl _intrcnt_show
-_intrcnt_show: .space 8 * 4 /* XXX 16 should not be hard coded ? */
/*
* Build the interrupt name table for vmstat
@@ -296,8 +344,9 @@ _intrcnt_show: .space 8 * 4 /* XXX 16 should not be hard coded ? */
.ascii "name irq" ; \
.asciz "irq_num"
/*
- * XXX - use the STRING and CONCAT macros from <sys/cdefs.h> to stringize
- * and concatenate names above and elsewhere.
+ * XXX - use the __STRING and __CONCAT macros from <sys/cdefs.h> to stringize
+ * and concatenate names above and elsewhere. Note that __CONCAT doesn't
+ * work when nested.
*/
.text
@@ -308,61 +357,4 @@ _intrnames:
BUILD_VECTOR(stray,,,,,,,,)
BUILD_VECTORS
-#undef BUILD_FAST_VECTOR
-#define BUILD_FAST_VECTOR BUILD_VECTOR
-
-#undef BUILD_VECTOR
-#define BUILD_VECTOR(name, unit, irq_num, id_num, mask, handler, \
- icu_num, icu_enables, reg) \
- .asciz "name pend"
-
- BUILD_VECTORS
_eintrnames:
-
-/*
- * now the spl names
- */
- .asciz "unpend_v"
- .asciz "doreti"
- .asciz "p0!ni"
- .asciz "!p0!ni"
- .asciz "p0ni"
- .asciz "netisr_raw"
- .asciz "netisr_ip"
- .asciz "netisr_imp"
- .asciz "netisr_ns"
- .asciz "netisr_iso"
- .asciz "softclock" /* 10 */
- .asciz "trap"
- .asciz "doreti_exit2"
- .asciz "splbio"
- .asciz "splclock"
- .asciz "splhigh"
- .asciz "splimp"
- .asciz "splnet"
- .asciz "splsoftclock"
- .asciz "spltty"
- .asciz "spl0" /* 20 */
- .asciz "netisr_raw2"
- .asciz "netisr_ip2"
- .asciz "netisr_imp2"
- .asciz "netisr_ns2"
- .asciz "netisr_iso2"
- .asciz "splx"
- .asciz "splx!0"
- .asciz "unpend_V"
- .asciz "netisr_x25"
- .asciz "netisr_hdlc"
- .asciz "spl31"
-/*
- * now the mask names
- */
- .asciz "cli"
- .asciz "cpl"
- .asciz "imen"
- .asciz "ipending"
- .asciz "sti"
- .asciz "mask5" /* mask5-mask7 are spares */
- .asciz "mask6"
- .asciz "mask7"
-
diff --git a/sys/i386/isa/wd.c b/sys/i386/isa/wd.c
index cb22340349e5..d141f040f0e0 100644
--- a/sys/i386/isa/wd.c
+++ b/sys/i386/isa/wd.c
@@ -37,7 +37,7 @@ static int wdtest = 0;
* SUCH DAMAGE.
*
* from: @(#)wd.c 7.2 (Berkeley) 5/9/91
- * $Id: wd.c,v 1.33.2.1 1994/03/07 01:42:55 rgrimes Exp $
+ * $Id: wd.c,v 1.40 1994/06/17 16:57:03 pst Exp $
*/
/* TODO:
@@ -83,11 +83,11 @@ static int wdtest = 0;
#include "syslog.h"
#include "vm/vm.h"
-#define TIMEOUT 10000 /* XXX? WDCC_DIAGNOSE can take > 1.1 sec */
-
+#define TIMEOUT 10000
#define RETRIES 5 /* number of retries before giving up */
#define RECOVERYTIME 500000 /* usec for controller to recover after err */
#define MAXTRANSFER 256 /* max size of transfer in sectors */
+#define BAD144_NO_CYL 0xffff /* XXX should be in dkbad.h; bad144.c uses -1 */
#ifdef notyet
#define wdnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */
@@ -149,6 +149,7 @@ struct disk {
struct dos_partition
dk_dospartitions[NDOSPART]; /* DOS view of disk */
struct dkbad dk_bad; /* bad sector table */
+ long dk_badsect[127]; /* 126 plus trailing -1 marker */
};
static struct disk *wddrives[NWD]; /* table of units */
@@ -159,6 +160,8 @@ static struct buf rwdbuf[NWD]; /* buffers for raw IO */
#endif
static long wdxfer[NWD]; /* count of transfers */
+
+static void bad144intern(struct disk *);
static int wdprobe(struct isa_device *dvp);
static int wdattach(struct isa_device *dvp);
static void wdustart(struct disk *du);
@@ -209,9 +212,46 @@ wdprobe(struct isa_device *dvp)
/* execute a controller only command */
if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0
- || wdwait(du, 0, TIMEOUT) != 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.)
+ */
+ 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;
+ sdh = inb(du->dk_port+ wd_sdh);
+ sdh = sdh | 0x10;
+ 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 */
+ 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);
@@ -318,13 +358,14 @@ wdstrategy(register struct buf *bp)
goto done;
}
+#if !defined(DISKLABEL_UNPROTECTED)
/* "soft" write protect check */
if ((du->dk_flags & DKFL_WRITEPROT) && (bp->b_flags & B_READ) == 0) {
bp->b_error = EROFS;
bp->b_flags |= B_ERROR;
goto done;
}
-
+#endif /* !defined(DISKLABEL_UNPROTECTED) */
/*
* Do bounds checking, adjust transfer, and set b_cylin.
*/
@@ -333,10 +374,32 @@ wdstrategy(register struct buf *bp)
du->dk_wlabel) <= 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 undersirable.
+ */
+ if( du->dk_flags & DKFL_BADSECT) {
+ int i;
+ int nsecs = howmany(bp->b_bcount, DEV_BSIZE);
+ int blkend = bp->b_pblkno + nsecs;
+ for(i=0;du->dk_badsect[i] != -1 && du->dk_badsect[i] < blkend;i++) {
+ if( du->dk_badsect[i] >= bp->b_pblkno) {
+ bp->b_flags |= B_BAD;
+ break;
+ }
+ }
+ }
+
/* queue transfer on drive, activate drive and controller if idle */
dp = &wdutab[lunit];
s = splbio();
- disksort(dp, bp);
+
+ cldisksort(dp, bp, 254*DEV_BSIZE);
+
if (dp->b_active == 0)
wdustart(du); /* start drive */
@@ -352,8 +415,10 @@ wdstrategy(register struct buf *bp)
return;
done:
+ s = splbio();
/* toss transfer, we're done early */
biodone(bp);
+ splx(s);
}
/*
@@ -440,7 +505,7 @@ loop:
}
/* calculate transfer details */
- blknum = bp->b_blkno + du->dk_skip;
+ blknum = bp->b_pblkno + du->dk_skip;
#ifdef WDDEBUG
if (du->dk_skip == 0)
printf("wd%d: wdstart: %s %d@%d; map ", lunit,
@@ -449,67 +514,49 @@ loop:
else
printf(" %d)%x", du->dk_skip, inb(du->dk_port + wd_altsts));
#endif
- if (du->dk_skip == 0)
- du->dk_bc = bp->b_bcount;
lp = &du->dk_dd;
secpertrk = lp->d_nsectors;
secpercyl = lp->d_secpercyl;
- if (wddospart(bp->b_dev))
- blknum += du->dk_dd2.d_partitions[wdpart(bp->b_dev)].p_offset;
- else
- blknum += lp->d_partitions[wdpart(bp->b_dev)].p_offset;
- cylin = blknum / secpercyl;
- head = (blknum % secpercyl) / secpertrk;
- sector = blknum % secpertrk;
- /*
- * See if the current block is in the bad block list.
- * (If we have one, and not formatting.)
- */
- if ((du->dk_flags & (DKFL_SINGLE | DKFL_BADSECT))
- == (DKFL_SINGLE | DKFL_BADSECT))
-#define BAD144_NO_CYL 0xffff /* XXX should be in dkbad.h; bad144.c uses -1 */
- for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != BAD144_NO_CYL;
- bt_ptr++) {
- if (bt_ptr->bt_cyl > cylin)
- /* Sorted list, and we passed our cylinder. quit. */
- break;
- if (bt_ptr->bt_cyl == cylin &&
- bt_ptr->bt_trksec == (head << 8) + sector) {
- /*
- * Found bad block. Calculate new block number.
- * This starts at the end of the disk (skip the
- * last track which is used for the bad block list),
- * and works backwards to the front of the disk.
- */
-#ifdef WDDEBUG
- printf("--- badblock code -> Old = %ld; ", blknum);
-#endif
+ if (du->dk_skip == 0) {
+ du->dk_bc = bp->b_bcount;
+ if (bp->b_flags & B_BAD) {
+ du->dk_flags |= DKFL_SINGLE;
+ }
+ }
+
+ if ((du->dk_flags & (DKFL_SINGLE|DKFL_BADSECT)) /* 19 Aug 92*/
+ == (DKFL_SINGLE|DKFL_BADSECT)) {
+ int i;
+ for(i=0;
+ du->dk_badsect[i] != -1 && du->dk_badsect[i] <= blknum;
+ i++) {
+
+ if( du->dk_badsect[i] == blknum) {
/*
* XXX the offset of the bad sector table ought
* to be stored in the in-core copy of the table.
*/
#define BAD144_PART 2 /* XXX scattered magic numbers */
#define BSD_PART 0 /* XXX should be 2 but bad144.c uses 0 */
- if (lp->d_partitions[BSD_PART].p_offset != 0)
- blknum = lp->d_partitions[BAD144_PART].p_offset
- + lp->d_partitions[BAD144_PART].p_size;
- else
- blknum = lp->d_secperunit;
- blknum -= lp->d_nsectors + (bt_ptr - du->dk_bad.bt_bad)
- + 1;
-
- cylin = blknum / secpercyl;
- head = (blknum % secpercyl) / secpertrk;
- sector = blknum % secpertrk;
-#ifdef WDDEBUG
- printf("new = %ld\n", blknum);
-#endif
- break;
+ if (lp->d_partitions[BSD_PART].p_offset != 0)
+ blknum = lp->d_partitions[BAD144_PART].p_offset
+ + lp->d_partitions[BAD144_PART].p_size;
+ else
+ blknum = lp->d_secperunit;
+ blknum -= lp->d_nsectors + i + 1;
+
+ break;
+ }
}
}
+
+
+ cylin = blknum / secpercyl;
+ head = (blknum % secpercyl) / secpertrk;
+ sector = blknum % secpertrk;
wdtab[ctrlr].b_active = 1; /* mark controller active */
@@ -643,7 +690,7 @@ wdintr(int unit)
return;
case 1:
wdstart(unit);
- return;
+ return;
case 2:
goto done;
}
@@ -690,6 +737,9 @@ oops:
chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short));
/* 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;
@@ -840,8 +890,10 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p)
du->dk_flags |= DKFL_BSDLABEL;
du->dk_flags &= ~DKFL_WRITEPROT;
- if (du->dk_dd.d_flags & D_BADSECT)
+ if (du->dk_dd.d_flags & D_BADSECT) {
du->dk_flags |= DKFL_BADSECT;
+ bad144intern(du);
+ }
/*
* Force WDRAW partition to be the whole disk.
@@ -1008,6 +1060,7 @@ wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector,
static int
wdsetctlr(struct disk *du)
{
+ int error = 0;
#ifdef WDDEBUG
printf("wd(%d,%d): wdsetctlr: C %lu H %lu S %lu\n",
du->dk_ctrlr, du->dk_unit,
@@ -1028,8 +1081,18 @@ wdsetctlr(struct disk *du)
}
else {
printf("(truncating to 16)\n");
- du->dk_dd.d_ntracks = 16;
+ 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
@@ -1215,8 +1278,10 @@ wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
case DIOCSBAD:
if ((flag & FWRITE) == 0)
error = EBADF;
- else
+ else {
du->dk_bad = *(struct dkbad *)addr;
+ bad144intern(du);
+ }
break;
case DIOCGDINFO:
@@ -1735,4 +1800,27 @@ wdwait(struct disk *du, u_char bits_wanted, int timeout)
return (-1);
}
+/*
+ * Internalize the bad sector table.
+ */
+void bad144intern(struct disk *du) {
+ int i;
+ if (du->dk_flags & DKFL_BADSECT) {
+ for (i = 0; i < 127; i++) {
+ du->dk_badsect[i] = -1;
+ }
+ for (i = 0; i < 126; i++) {
+ if (du->dk_bad.bt_bad[i].bt_cyl == 0xffff) {
+ break;
+ } else {
+ du->dk_badsect[i] =
+ du->dk_bad.bt_bad[i].bt_cyl * du->dk_dd.d_secpercyl +
+ (du->dk_bad.bt_bad[i].bt_trksec >> 8) * du->dk_dd.d_nsectors
++
+ (du->dk_bad.bt_bad[i].bt_trksec & 0x00ff);
+ }
+ }
+ }
+}
+
#endif /* NWDC > 0 */
diff --git a/sys/i386/isa/wt.c b/sys/i386/isa/wt.c
index 307c5062b2be..783df59433bd 100644
--- a/sys/i386/isa/wt.c
+++ b/sys/i386/isa/wt.c
@@ -19,7 +19,7 @@
* the original CMU copyright notice.
*
* Version 1.3, Thu Nov 11 12:09:13 MSK 1993
- * $Id: wt.c,v 1.5 1993/12/19 00:50:50 wollman Exp $
+ * $Id: wt.c,v 1.6 1994/06/29 06:11:20 jkh Exp $
*
*/
@@ -895,6 +895,7 @@ static int wtstatus (wtinfo_t *t)
outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */
wtpoll (t, t->BUSY, 0); /* wait for not ready */
+ DELAY(30);
outb (t->CTLPORT, t->ONLINE); /* unset request */
}
return (1);
diff --git a/sys/i386/isa/wt.c.orig b/sys/i386/isa/wt.c.orig
new file mode 100644
index 000000000000..572802a6fa9b
--- /dev/null
+++ b/sys/i386/isa/wt.c.orig
@@ -0,0 +1,902 @@
+/*
+ * Streamer tape driver for 386bsd and FreeBSD.
+ * Supports Archive and Wangtek compatible QIC-02/QIC-36 boards.
+ *
+ * Copyright (C) 1993 by:
+ * Sergey Ryzhkov <sir@kiae.su>
+ * Serge Vakulenko <vak@zebub.msk.su>
+ *
+ * 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.
+ *
+ * This driver is derived from the old 386bsd Wangtek streamer tape driver,
+ * made by Robert Baron at CMU, based on Intel sources.
+ * Authors thank Robert Baron, CMU and Intel and retain here
+ * the original CMU copyright notice.
+ *
+ * Version 1.3, Thu Nov 11 12:09:13 MSK 1993
+ * $Id: wt.c,v 1.6 1994/06/29 06:11:20 jkh Exp $
+ *
+ */
+
+/*
+ * Copyright (c) 1989 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Robert Baron
+ *
+ * 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 the
+ * rights to redistribute these changes.
+ */
+
+#include "wt.h"
+#if NWT > 0
+
+#include "sys/param.h"
+#include "systm.h"
+#include "kernel.h"
+#include "sys/buf.h"
+#include "sys/fcntl.h"
+#include "sys/malloc.h"
+#include "sys/ioctl.h"
+#include "sys/mtio.h"
+#include "vm/vm_param.h"
+#include "i386/include/pio.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/wtreg.h"
+
+/*
+ * Uncomment this to enable internal device tracing.
+ */
+#define DEBUG(s) /* printf s */
+
+#define WTPRI (PZERO+10) /* sleep priority */
+
+/*
+ * Wangtek controller ports
+ */
+#define WT_CTLPORT(base) ((base)+0) /* control, write only */
+#define WT_STATPORT(base) ((base)+0) /* status, read only */
+#define WT_CMDPORT(base) ((base)+1) /* command, write only */
+#define WT_DATAPORT(base) ((base)+1) /* data, read only */
+#define WT_NPORT 2 /* 2 i/o ports */
+
+/* status port bits */
+#define WT_BUSY 0x01 /* not ready bit define */
+#define WT_NOEXCEP 0x02 /* no exception bit define */
+#define WT_RESETMASK 0x07 /* to check after reset */
+#define WT_RESETVAL 0x05 /* state after reset */
+
+/* control port bits */
+#define WT_ONLINE 0x01 /* device selected */
+#define WT_RESET 0x02 /* reset command */
+#define WT_REQUEST 0x04 /* request command */
+#define WT_IEN 0x08 /* enable dma */
+
+/*
+ * Archive controller ports
+ */
+#define AV_DATAPORT(base) ((base)+0) /* data, read only */
+#define AV_CMDPORT(base) ((base)+0) /* command, write only */
+#define AV_STATPORT(base) ((base)+1) /* status, read only */
+#define AV_CTLPORT(base) ((base)+1) /* control, write only */
+#define AV_SDMAPORT(base) ((base)+2) /* start dma */
+#define AV_RDMAPORT(base) ((base)+3) /* reset dma */
+#define AV_NPORT 4 /* 4 i/o ports */
+
+/* status port bits */
+#define AV_BUSY 0x40 /* not ready bit define */
+#define AV_NOEXCEP 0x20 /* no exception bit define */
+#define AV_RESETMASK 0xf8 /* to check after reset */
+#define AV_RESETVAL 0x50 /* state after reset */
+
+/* control port bits */
+#define AV_RESET 0x80 /* reset command */
+#define AV_REQUEST 0x40 /* request command */
+#define AV_IEN 0x20 /* enable interrupts */
+
+enum wttype {
+ UNKNOWN = 0, /* unknown type, driver disabled */
+ ARCHIVE, /* Archive Viper SC499, SC402 etc */
+ WANGTEK, /* Wangtek */
+};
+
+typedef struct {
+ unsigned short err; /* code for error encountered */
+ unsigned short ercnt; /* number of error blocks */
+ unsigned short urcnt; /* number of underruns */
+} wtstatus_t;
+
+typedef struct {
+ enum wttype type; /* type of controller */
+ unsigned unit; /* unit number */
+ unsigned port; /* base i/o port */
+ unsigned chan; /* dma channel number, 1..3 */
+ unsigned flags; /* state of tape drive */
+ unsigned dens; /* tape density */
+ int bsize; /* tape block size */
+ void *buf; /* internal i/o buffer */
+
+ void *dmavaddr; /* virtual address of dma i/o buffer */
+ unsigned dmatotal; /* size of i/o buffer */
+ unsigned dmaflags; /* i/o direction, B_READ or B_WRITE */
+ unsigned dmacount; /* resulting length of dma i/o */
+
+ wtstatus_t error; /* status of controller */
+
+ unsigned short DATAPORT, CMDPORT, STATPORT, CTLPORT, SDMAPORT, RDMAPORT;
+ unsigned char BUSY, NOEXCEP, RESETMASK, RESETVAL;
+ unsigned char ONLINE, RESET, REQUEST, IEN;
+} wtinfo_t;
+
+wtinfo_t wttab[NWT]; /* tape info by unit number */
+
+static int wtwait (wtinfo_t *t, int catch, char *msg);
+static int wtcmd (wtinfo_t *t, int cmd);
+static int wtstart (wtinfo_t *t, unsigned mode, void *vaddr, unsigned len);
+static void wtdma (wtinfo_t *t);
+static void wtimer (caddr_t, int);
+static void wtclock (wtinfo_t *t);
+static int wtreset (wtinfo_t *t);
+static int wtsense (wtinfo_t *t, int verb, int ignor);
+static int wtstatus (wtinfo_t *t);
+static void wtrewind (wtinfo_t *t);
+static int wtreadfm (wtinfo_t *t);
+static int wtwritefm (wtinfo_t *t);
+static int wtpoll (wtinfo_t *t, int mask, int bits);
+
+/* XXX */
+extern void DELAY (int usec);
+
+/*
+ * Probe for the presence of the device.
+ */
+int wtprobe (struct isa_device *id)
+{
+ wtinfo_t *t = wttab + id->id_unit;
+
+ t->unit = id->id_unit;
+ t->chan = id->id_drq;
+ t->port = id->id_iobase;
+ if (t->chan<1 || t->chan>3) {
+ printf ("wt%d: Bad drq=%d, should be 1..3\n", t->unit, t->chan);
+ return (0);
+ }
+
+ /* Try Wangtek. */
+ t->type = WANGTEK;
+ t->CTLPORT = WT_CTLPORT (t->port); t->STATPORT = WT_STATPORT (t->port);
+ t->CMDPORT = WT_CMDPORT (t->port); t->DATAPORT = WT_DATAPORT (t->port);
+ t->SDMAPORT = 0; t->RDMAPORT = 0;
+ t->BUSY = WT_BUSY; t->NOEXCEP = WT_NOEXCEP;
+ t->RESETMASK = WT_RESETMASK; t->RESETVAL = WT_RESETVAL;
+ t->ONLINE = WT_ONLINE; t->RESET = WT_RESET;
+ t->REQUEST = WT_REQUEST; t->IEN = WT_IEN;
+ if (wtreset (t))
+ return (WT_NPORT);
+
+ /* Try Archive. */
+ t->type = ARCHIVE;
+ t->CTLPORT = AV_CTLPORT (t->port); t->STATPORT = AV_STATPORT (t->port);
+ t->CMDPORT = AV_CMDPORT (t->port); t->DATAPORT = AV_DATAPORT (t->port);
+ t->SDMAPORT = AV_SDMAPORT (t->port); t->RDMAPORT = AV_RDMAPORT (t->port);
+ t->BUSY = AV_BUSY; t->NOEXCEP = AV_NOEXCEP;
+ t->RESETMASK = AV_RESETMASK; t->RESETVAL = AV_RESETVAL;
+ t->ONLINE = 0; t->RESET = AV_RESET;
+ t->REQUEST = AV_REQUEST; t->IEN = AV_IEN;
+ if (wtreset (t))
+ return (AV_NPORT);
+
+ /* Tape controller not found. */
+ t->type = UNKNOWN;
+ return (0);
+}
+
+/*
+ * Device is found, configure it.
+ */
+int wtattach (struct isa_device *id)
+{
+ wtinfo_t *t = wttab + id->id_unit;
+
+ if (t->type == ARCHIVE) {
+ printf ("wt%d: type <Archive>\n", t->unit);
+ outb (t->RDMAPORT, 0); /* reset dma */
+ } else
+ printf ("wt%d: type <Wangtek>\n", t->unit);
+ t->flags = TPSTART; /* tape is rewound */
+ t->dens = -1; /* unknown density */
+ return (1);
+}
+
+struct isa_driver wtdriver = { wtprobe, wtattach, "wt", };
+
+int wtdump (int dev)
+{
+ /* Not implemented */
+ return (EINVAL);
+}
+
+int wtsize (int dev)
+{
+ /* Not implemented */
+ return (-1);
+}
+
+/*
+ * Open routine, called on every device open.
+ */
+int wtopen (int dev, int flag)
+{
+ int u = minor (dev) & T_UNIT;
+ wtinfo_t *t = wttab + u;
+ int error;
+
+ if (u >= NWT || t->type == UNKNOWN)
+ return (ENXIO);
+
+ /* Check that device is not in use */
+ if (t->flags & TPINUSE)
+ return (EBUSY);
+
+ /* If the tape is in rewound state, check the status and set density. */
+ if (t->flags & TPSTART) {
+ /* If rewind is going on, wait */
+ if (error = wtwait (t, PCATCH, "wtrew"))
+ return (error);
+
+ /* Check the controller status */
+ if (! wtsense (t, 0, (flag & FWRITE) ? 0 : TP_WRP)) {
+ /* Bad status, reset the controller */
+ if (! wtreset (t))
+ return (EIO);
+ if (! wtsense (t, 1, (flag & FWRITE) ? 0 : TP_WRP))
+ return (EIO);
+ }
+
+ /* Set up tape density. */
+ if (t->dens != (minor (dev) & WT_DENSEL)) {
+ int d = 0;
+
+ switch (minor (dev) & WT_DENSEL) {
+ case WT_DENSDFLT: default: break; /* default density */
+ case WT_QIC11: d = QIC_FMT11; break; /* minor 010 */
+ case WT_QIC24: d = QIC_FMT24; break; /* minor 020 */
+ case WT_QIC120: d = QIC_FMT120; break; /* minor 030 */
+ case WT_QIC150: d = QIC_FMT150; break; /* minor 040 */
+ case WT_QIC300: d = QIC_FMT300; break; /* minor 050 */
+ case WT_QIC600: d = QIC_FMT600; break; /* minor 060 */
+ }
+ if (d) {
+ /* Change tape density. */
+ if (! wtcmd (t, d))
+ return (EIO);
+ if (! wtsense (t, 1, TP_WRP | TP_ILL))
+ return (EIO);
+
+ /* Check the status of the controller. */
+ if (t->error.err & TP_ILL) {
+ printf ("wt%d: invalid tape density\n", t->unit);
+ return (ENODEV);
+ }
+ }
+ t->dens = minor (dev) & WT_DENSEL;
+ }
+ t->flags &= ~TPSTART;
+ } else if (t->dens != (minor (dev) & WT_DENSEL))
+ return (ENXIO);
+
+ t->bsize = (minor (dev) & WT_BSIZE) ? 1024 : 512;
+ t->buf = malloc (t->bsize, M_TEMP, M_WAITOK);
+ if (! t->buf)
+ return (EAGAIN);
+
+ t->flags = TPINUSE;
+ if (flag & FREAD)
+ t->flags |= TPREAD;
+ if (flag & FWRITE)
+ t->flags |= TPWRITE;
+ return (0);
+}
+
+/*
+ * Close routine, called on last device close.
+ */
+int wtclose (int dev)
+{
+ int u = minor (dev) & T_UNIT;
+ wtinfo_t *t = wttab + u;
+
+ if (u >= NWT || t->type == UNKNOWN)
+ return (ENXIO);
+
+ /* If rewind is pending, do nothing */
+ if (t->flags & TPREW)
+ goto done;
+
+ /* If seek forward is pending and no rewind on close, do nothing */
+ if (t->flags & TPRMARK) {
+ if (minor (dev) & T_NOREWIND)
+ goto done;
+
+ /* If read file mark is going on, wait */
+ wtwait (t, 0, "wtrfm");
+ }
+
+ if (t->flags & TPWANY)
+ /* Tape was written. Write file mark. */
+ wtwritefm (t);
+
+ if (! (minor (dev) & T_NOREWIND)) {
+ /* Rewind tape to beginning of tape. */
+ /* Don't wait until rewind, though. */
+ wtrewind (t);
+ goto done;
+ }
+ if ((t->flags & TPRANY) && ! (t->flags & (TPVOL | TPWANY)))
+ /* Space forward to after next file mark if no writing done. */
+ /* Don't wait for completion. */
+ wtreadfm (t);
+done:
+ t->flags &= TPREW | TPRMARK | TPSTART | TPTIMER;
+ free (t->buf, M_TEMP);
+ return (0);
+}
+
+/*
+ * Ioctl routine. Compatible with BSD ioctls.
+ * Direct QIC-02 commands ERASE and RETENSION added.
+ * There are three possible ioctls:
+ * ioctl (int fd, MTIOCGET, struct mtget *buf) -- get status
+ * ioctl (int fd, MTIOCTOP, struct mtop *buf) -- do BSD-like op
+ * ioctl (int fd, WTQICMD, int qicop) -- do QIC op
+ */
+int wtioctl (int dev, int cmd, void *arg, int mode)
+{
+ int u = minor (dev) & T_UNIT;
+ wtinfo_t *t = wttab + u;
+ int error, count, op;
+
+ if (u >= NWT || t->type == UNKNOWN)
+ return (ENXIO);
+
+ switch (cmd) {
+ default:
+ return (EINVAL);
+ case WTQICMD: /* direct QIC command */
+ op = (int) *(void**)arg;
+ switch (op) {
+ default:
+ return (EINVAL);
+ case QIC_ERASE: /* erase the whole tape */
+ if (! (t->flags & TPWRITE) || (t->flags & TPWP))
+ return (EACCES);
+ if (error = wtwait (t, PCATCH, "wterase"))
+ return (error);
+ break;
+ case QIC_RETENS: /* retension the tape */
+ if (error = wtwait (t, PCATCH, "wtretens"))
+ return (error);
+ break;
+ }
+ /* Both ERASE and RETENS operations work like REWIND. */
+ /* Simulate the rewind operation here. */
+ t->flags &= ~(TPRO | TPWO | TPVOL);
+ if (! wtcmd (t, op))
+ return (EIO);
+ t->flags |= TPSTART | TPREW;
+ if (op == QIC_ERASE)
+ t->flags |= TPWANY;
+ wtclock (t);
+ return (0);
+ case MTIOCIEOT: /* ignore EOT errors */
+ case MTIOCEEOT: /* enable EOT errors */
+ return (0);
+ case MTIOCGET:
+ ((struct mtget*)arg)->mt_type =
+ t->type == ARCHIVE ? MT_ISVIPER1 : 0x11;
+ ((struct mtget*)arg)->mt_dsreg = t->flags; /* status */
+ ((struct mtget*)arg)->mt_erreg = t->error.err; /* errors */
+ ((struct mtget*)arg)->mt_resid = 0;
+ ((struct mtget*)arg)->mt_fileno = 0; /* file */
+ ((struct mtget*)arg)->mt_blkno = 0; /* block */
+ return (0);
+ case MTIOCTOP:
+ break;
+ }
+ switch ((short) ((struct mtop*)arg)->mt_op) {
+ default:
+ case MTFSR: /* forward space record */
+ case MTBSR: /* backward space record */
+ case MTBSF: /* backward space file */
+ break;
+ case MTNOP: /* no operation, sets status only */
+ case MTCACHE: /* enable controller cache */
+ case MTNOCACHE: /* disable controller cache */
+ return (0);
+ case MTREW: /* rewind */
+ case MTOFFL: /* rewind and put the drive offline */
+ if (t->flags & TPREW) /* rewind is running */
+ return (0);
+ if (error = wtwait (t, PCATCH, "wtorew"))
+ return (error);
+ wtrewind (t);
+ return (0);
+ case MTFSF: /* forward space file */
+ for (count=((struct mtop*)arg)->mt_count; count>0; --count) {
+ if (error = wtwait (t, PCATCH, "wtorfm"))
+ return (error);
+ if (error = wtreadfm (t))
+ return (error);
+ }
+ return (0);
+ case MTWEOF: /* write an end-of-file record */
+ if (! (t->flags & TPWRITE) || (t->flags & TPWP))
+ return (EACCES);
+ if (error = wtwait (t, PCATCH, "wtowfm"))
+ return (error);
+ if (error = wtwritefm (t))
+ return (error);
+ return (0);
+ }
+ return (EINVAL);
+}
+
+/*
+ * Strategy routine.
+ */
+void wtstrategy (struct buf *bp)
+{
+ int u = minor (bp->b_dev) & T_UNIT;
+ wtinfo_t *t = wttab + u;
+ int s;
+
+ bp->b_resid = bp->b_bcount;
+ if (u >= NWT || t->type == UNKNOWN)
+ goto errxit;
+
+ /* at file marks and end of tape, we just return '0 bytes available' */
+ if (t->flags & TPVOL)
+ goto xit;
+
+ if (bp->b_flags & B_READ) {
+ /* Check read access and no previous write to this tape. */
+ if (! (t->flags & TPREAD) || (t->flags & TPWANY))
+ goto errxit;
+
+ /* For now, we assume that all data will be copied out */
+ /* If read command outstanding, just skip down */
+ if (! (t->flags & TPRO)) {
+ if (! wtsense (t, 1, TP_WRP)) /* clear status */
+ goto errxit;
+ if (! wtcmd (t, QIC_RDDATA)) { /* sed read mode */
+ wtsense (t, 1, TP_WRP);
+ goto errxit;
+ }
+ t->flags |= TPRO | TPRANY;
+ }
+ } else {
+ /* Check write access and write protection. */
+ /* No previous read from this tape allowed. */
+ if (! (t->flags & TPWRITE) || (t->flags & (TPWP | TPRANY)))
+ goto errxit;
+
+ /* If write command outstanding, just skip down */
+ if (! (t->flags & TPWO)) {
+ if (! wtsense (t, 1, 0)) /* clear status */
+ goto errxit;
+ if (! wtcmd (t, QIC_WRTDATA)) { /* set write mode */
+ wtsense (t, 1, 0);
+ goto errxit;
+ }
+ t->flags |= TPWO | TPWANY;
+ }
+ }
+
+ if (! bp->b_bcount)
+ goto xit;
+
+ t->flags &= ~TPEXCEP;
+ s = splbio ();
+ if (wtstart (t, bp->b_flags, bp->b_un.b_addr, bp->b_bcount)) {
+ wtwait (t, 0, (bp->b_flags & B_READ) ? "wtread" : "wtwrite");
+ bp->b_resid -= t->dmacount;
+ }
+ splx (s);
+
+ if (t->flags & TPEXCEP) {
+errxit: bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ }
+xit: biodone (bp);
+ return;
+}
+
+/*
+ * Interrupt routine.
+ */
+void wtintr (int u)
+{
+ wtinfo_t *t = wttab + u;
+ unsigned char s;
+
+ if (u >= NWT || t->type == UNKNOWN) {
+ DEBUG (("wtintr() -- device not configured\n"));
+ return;
+ }
+
+ s = inb (t->STATPORT); /* get status */
+ DEBUG (("wtintr() status=0x%x -- ", s));
+ if ((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP)) {
+ DEBUG (("busy\n"));
+ return; /* device is busy */
+ }
+
+ /*
+ * Check if rewind finished.
+ */
+ if (t->flags & TPREW) {
+ DEBUG (((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP) ?
+ "rewind busy?\n" : "rewind finished\n"));
+ t->flags &= ~TPREW; /* Rewind finished. */
+ wtsense (t, 1, TP_WRP);
+ wakeup ((caddr_t)t);
+ return;
+ }
+
+ /*
+ * Check if writing/reading of file mark finished.
+ */
+ if (t->flags & (TPRMARK | TPWMARK)) {
+ DEBUG (((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP) ?
+ "marker r/w busy?\n" : "marker r/w finished\n"));
+ if (! (s & t->NOEXCEP)) /* operation failed */
+ wtsense (t, 1, (t->flags & TPRMARK) ? TP_WRP : 0);
+ t->flags &= ~(TPRMARK | TPWMARK); /* operation finished */
+ wakeup ((caddr_t)t);
+ return;
+ }
+
+ /*
+ * Do we started any i/o? If no, just return.
+ */
+ if (! (t->flags & TPACTIVE)) {
+ DEBUG (("unexpected interrupt\n"));
+ return;
+ }
+ t->flags &= ~TPACTIVE;
+ t->dmacount += t->bsize; /* increment counter */
+
+ /*
+ * Clean up dma.
+ */
+ if ((t->dmaflags & B_READ) && (t->dmatotal - t->dmacount) < t->bsize) {
+ /* If reading short block, copy the internal buffer
+ * to the user memory. */
+ isa_dmadone (t->dmaflags, t->buf, t->bsize, t->chan);
+ bcopy (t->buf, t->dmavaddr, t->dmatotal - t->dmacount);
+ } else
+ isa_dmadone (t->dmaflags, t->dmavaddr, t->bsize, t->chan);
+
+ /*
+ * On exception, check for end of file and end of volume.
+ */
+ if (! (s & t->NOEXCEP)) {
+ DEBUG (("i/o exception\n"));
+ wtsense (t, 1, (t->dmaflags & B_READ) ? TP_WRP : 0);
+ if (t->error.err & (TP_EOM | TP_FIL))
+ t->flags |= TPVOL; /* end of file */
+ else
+ t->flags |= TPEXCEP; /* i/o error */
+ wakeup ((caddr_t)t);
+ return;
+ }
+
+ if (t->dmacount < t->dmatotal) { /* continue i/o */
+ t->dmavaddr += t->bsize;
+ wtdma (t);
+ DEBUG (("continue i/o, %d\n", t->dmacount));
+ return;
+ }
+ if (t->dmacount > t->dmatotal) /* short last block */
+ t->dmacount = t->dmatotal;
+ wakeup ((caddr_t)t); /* wake up user level */
+ DEBUG (("i/o finished, %d\n", t->dmacount));
+}
+
+/* start the rewind operation */
+static void wtrewind (wtinfo_t *t)
+{
+ int rwmode = (t->flags & (TPRO | TPWO));
+
+ t->flags &= ~(TPRO | TPWO | TPVOL);
+ /*
+ * Wangtek strictly follows QIC-02 standard:
+ * clearing ONLINE in read/write modes causes rewind.
+ * REWIND command is not allowed in read/write mode
+ * and gives `illegal command' error.
+ */
+ if (t->type==WANGTEK && rwmode) {
+ outb (t->CTLPORT, 0);
+ } else if (! wtcmd (t, QIC_REWIND))
+ return;
+ t->flags |= TPSTART | TPREW;
+ wtclock (t);
+}
+
+/* start the `read marker' operation */
+static int wtreadfm (wtinfo_t *t)
+{
+ t->flags &= ~(TPRO | TPWO | TPVOL);
+ if (! wtcmd (t, QIC_READFM)) {
+ wtsense (t, 1, TP_WRP);
+ return (EIO);
+ }
+ t->flags |= TPRMARK | TPRANY;
+ wtclock (t);
+ /* Don't wait for completion here. */
+ return (0);
+}
+
+/* write marker to the tape */
+static int wtwritefm (wtinfo_t *t)
+{
+ tsleep ((caddr_t)wtwritefm, WTPRI, "wtwfm", hz); /* timeout: 1 second */
+ t->flags &= ~(TPRO | TPWO);
+ if (! wtcmd (t, QIC_WRITEFM)) {
+ wtsense (t, 1, 0);
+ return (EIO);
+ }
+ t->flags |= TPWMARK | TPWANY;
+ wtclock (t);
+ return (wtwait (t, 0, "wtwfm"));
+}
+
+/* while controller status & mask == bits continue waiting */
+static int wtpoll (wtinfo_t *t, int mask, int bits)
+{
+ int s, i;
+
+ /* Poll status port, waiting for specified bits. */
+ for (i=0; i<1000; ++i) { /* up to 1 msec */
+ s = inb (t->STATPORT);
+ if ((s & mask) != bits)
+ return (s);
+ DELAY (1);
+ }
+ for (i=0; i<100; ++i) { /* up to 10 msec */
+ s = inb (t->STATPORT);
+ if ((s & mask) != bits)
+ return (s);
+ DELAY (100);
+ }
+ for (;;) { /* forever */
+ s = inb (t->STATPORT);
+ if ((s & mask) != bits)
+ return (s);
+ tsleep ((caddr_t)wtpoll, WTPRI, "wtpoll", 1); /* timeout: 1 tick */
+ }
+}
+
+/* execute QIC command */
+static int wtcmd (wtinfo_t *t, int cmd)
+{
+ int s;
+
+ DEBUG (("wtcmd() cmd=0x%x\n", cmd));
+ s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */
+ if (! (s & t->NOEXCEP)) /* error */
+ return (0);
+
+ outb (t->CMDPORT, cmd); /* output the command */
+
+ outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */
+ wtpoll (t, t->BUSY, t->BUSY); /* wait for ready */
+ outb (t->CTLPORT, t->IEN | t->ONLINE); /* reset request */
+ wtpoll (t, t->BUSY, 0); /* wait for not ready */
+ return (1);
+}
+
+/* wait for the end of i/o, seeking marker or rewind operation */
+static int wtwait (wtinfo_t *t, int catch, char *msg)
+{
+ int error;
+
+ DEBUG (("wtwait() `%s'\n", msg));
+ while (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK))
+ if (error = tsleep ((caddr_t)t, WTPRI | catch, msg, 0))
+ return (error);
+ return (0);
+}
+
+/* initialize dma for the i/o operation */
+static void wtdma (wtinfo_t *t)
+{
+ t->flags |= TPACTIVE;
+ wtclock (t);
+
+ if (t->type == ARCHIVE)
+ outb (t->SDMAPORT, 0); /* set dma */
+
+ if ((t->dmaflags & B_READ) && (t->dmatotal - t->dmacount) < t->bsize)
+ /* Reading short block. Do it through the internal buffer. */
+ isa_dmastart (t->dmaflags, t->buf, t->bsize, t->chan);
+ else
+ isa_dmastart (t->dmaflags, t->dmavaddr, t->bsize, t->chan);
+}
+
+/* start i/o operation */
+static int wtstart (wtinfo_t *t, unsigned flags, void *vaddr, unsigned len)
+{
+ int s;
+
+ DEBUG (("wtstart()\n"));
+ s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */
+ if (! (s & t->NOEXCEP)) {
+ t->flags |= TPEXCEP; /* error */
+ return (0);
+ }
+ t->flags &= ~TPEXCEP; /* clear exception flag */
+ t->dmavaddr = vaddr;
+ t->dmatotal = len;
+ t->dmacount = 0;
+ t->dmaflags = flags;
+ wtdma (t);
+ return (1);
+}
+
+/* start timer */
+static void wtclock (wtinfo_t *t)
+{
+ if (! (t->flags & TPTIMER)) {
+ t->flags |= TPTIMER;
+ /* Some controllers seem to lose dma interrupts too often.
+ * To make the tape stream we need 1 tick timeout. */
+ timeout (wtimer, (caddr_t)t, (t->flags & TPACTIVE) ? 1 : hz);
+ }
+}
+
+/*
+ * Simulate an interrupt periodically while i/o is going.
+ * This is necessary in case interrupts get eaten due to
+ * multiple devices on a single IRQ line.
+ */
+static void wtimer (caddr_t xt, int dummy)
+{
+ wtinfo_t *t = (wtinfo_t *)xt;
+ int s;
+
+ t->flags &= ~TPTIMER;
+ if (! (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK)))
+ return;
+
+ /* If i/o going, simulate interrupt. */
+ s = splbio ();
+ if ((inb (t->STATPORT) & (t->BUSY | t->NOEXCEP)) != (t->BUSY | t->NOEXCEP)) {
+ DEBUG (("wtimer() -- "));
+ wtintr (t->unit);
+ }
+ splx (s);
+
+ /* Restart timer if i/o pending. */
+ if (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK))
+ wtclock (t);
+}
+
+/* reset the controller */
+static int wtreset (wtinfo_t *t)
+{
+ /* Perform QIC-02 and QIC-36 compatible reset sequence. */
+ /* Thanks to Mikael Hybsch <micke@dynas.se>. */
+ int s, i;
+
+ outb (t->CTLPORT, t->RESET | t->ONLINE); /* send reset */
+ DELAY (30);
+ outb (t->CTLPORT, t->ONLINE); /* turn off reset */
+ DELAY (30);
+
+ /* Read the controller status. */
+ s = inb (t->STATPORT);
+ if (s == 0xff) /* no port at this address? */
+ return (0);
+
+ /* Wait 3 sec for reset to complete. Needed for QIC-36 boards? */
+ for (i=0; i<3000; ++i) {
+ if (! (s & t->BUSY) || ! (s & t->NOEXCEP))
+ break;
+ DELAY (1000);
+ s = inb (t->STATPORT);
+ }
+ return ((s & t->RESETMASK) == t->RESETVAL);
+}
+
+/* get controller status information */
+/* return 0 if user i/o request should receive an i/o error code */
+static int wtsense (wtinfo_t *t, int verb, int ignor)
+{
+ char *msg = 0;
+ int err;
+
+ DEBUG (("wtsense() ignor=0x%x\n", ignor));
+ t->flags &= ~(TPRO | TPWO);
+ if (! wtstatus (t))
+ return (0);
+ if (! (t->error.err & TP_ST0))
+ t->error.err &= ~TP_ST0MASK;
+ if (! (t->error.err & TP_ST1))
+ t->error.err &= ~TP_ST1MASK;
+ t->error.err &= ~ignor; /* ignore certain errors */
+ err = t->error.err & (TP_FIL | TP_BNL | TP_UDA | TP_EOM | TP_WRP |
+ TP_USL | TP_CNI | TP_MBD | TP_NDT | TP_ILL);
+ if (! err)
+ return (1);
+ if (! verb)
+ return (0);
+
+ /* lifted from tdriver.c from Wangtek */
+ if (err & TP_USL) msg = "Drive not online";
+ else if (err & TP_CNI) msg = "No cartridge";
+ else if ((err & TP_WRP) && !(t->flags & TPWP)) {
+ msg = "Tape is write protected";
+ t->flags |= TPWP;
+ }
+ else if (err & TP_FIL) msg = 0 /*"Filemark detected"*/;
+ else if (err & TP_EOM) msg = 0 /*"End of tape"*/;
+ else if (err & TP_BNL) msg = "Block not located";
+ else if (err & TP_UDA) msg = "Unrecoverable data error";
+ else if (err & TP_NDT) msg = "No data detected";
+ else if (err & TP_ILL) msg = "Illegal command";
+ if (msg)
+ printf ("wt%d: %s\n", t->unit, msg);
+ return (0);
+}
+
+/* get controller status information */
+static int wtstatus (wtinfo_t *t)
+{
+ char *p;
+
+ wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */
+ outb (t->CMDPORT, QIC_RDSTAT); /* send `read status' command */
+
+ outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */
+ wtpoll (t, t->BUSY, t->BUSY); /* wait for ready */
+ outb (t->CTLPORT, t->ONLINE); /* reset request */
+ wtpoll (t, t->BUSY, 0); /* wait for not ready */
+
+ p = (char*) &t->error;
+ while (p < (char*)&t->error + 6) {
+ int s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP);
+ if (! (s & t->NOEXCEP)) /* error */
+ return (0);
+
+ *p++ = inb (t->DATAPORT); /* read status byte */
+
+ outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */
+ wtpoll (t, t->BUSY, 0); /* wait for not ready */
+ outb (t->CTLPORT, t->ONLINE); /* unset request */
+ }
+ return (1);
+}
+#endif /* NWT */
diff --git a/sys/i386/netboot/Makefile b/sys/i386/netboot/Makefile
index 550cd7be44cd..b7b2b442e557 100644
--- a/sys/i386/netboot/Makefile
+++ b/sys/i386/netboot/Makefile
@@ -5,13 +5,18 @@
# -DSMALL_ROM - Compile for 8K ROMS
# -DROMSIZE - Size of EPROM - Must be set (even for .COM files)
# -DRELOC - Relocation address (usually 0x90000)
+# -DINCLUDE_WD - Include Western Digital/SMC support
+# -DINCLUDE_NE - Include NE1000/NE2000 support
+# -DNE_BASE - Base I/O address for NE1000/NE2000
+# -DWD_DEFAULT_MEM- Default memory location for WD/SMC cards
#
ROMSIZE=16384
RELOCADDR=0x90000
-CFLAGS=-O2 -DNFS -DROMSIZE=$(ROMSIZE) -DRELOC=$(RELOCADDR)
+CFLAGS= -O2 -DNFS -DINCLUDE_WD -DINCLUDE_NE -DROMSIZE=$(ROMSIZE) \
+ -DRELOC=$(RELOCADDR) -DNE_BASE=0x320 -DWD_DEFAULT_MEM=0xD0000
HDRS=netboot.h
-COBJS=main.o misc.o wd80x3.o bootmenu.o
+COBJS=main.o misc.o ether.o bootmenu.o
SSRCS=start2.S
SOBJS=start2.o
diff --git a/sys/i386/netboot/bootmenu.c b/sys/i386/netboot/bootmenu.c
index c6b7e79d99b4..d77e7efc773e 100644
--- a/sys/i386/netboot/bootmenu.c
+++ b/sys/i386/netboot/bootmenu.c
@@ -86,7 +86,7 @@ CMD_BOOTFILE - set boot filename
cmd_bootfile(p)
char *p;
{
- char *q = bootname;
+ char *q = bootfile = bootname;
if (*p) {
while(*p)
*(q++) = *(p++);
diff --git a/sys/i386/netboot/ether.c b/sys/i386/netboot/ether.c
new file mode 100644
index 000000000000..69448026c4e4
--- /dev/null
+++ b/sys/i386/netboot/ether.c
@@ -0,0 +1,479 @@
+
+/**************************************************************************
+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.
+
+
+**************************************************************************/
+
+#include "netboot.h"
+#include "ether.h"
+
+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_node_addr;
+
+/**************************************************************************
+The following two variables are used externally
+**************************************************************************/
+char packet[ETH_MAX_PACKET];
+int packetlen;
+
+/**************************************************************************
+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;
+ 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++)
+ printhb((int)(arptable[ARP_CLIENT].node[i] =
+ inb(i+eth_asic_base+WD_LAR)));
+ 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) & 0x0B));
+ 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 INCLUDE_NE
+ /******************************************************************
+ Search for NE1000/2000 if no WD/SMC cards
+ ******************************************************************/
+ if (eth_vendor != VENDOR_WD) {
+ char romdata[16], testbuf[32];
+ char test[] = "NE1000/2000 memory";
+ eth_bmem = (char *)0; /* No shared memory */
+ eth_asic_base = NE_BASE + NE_ASIC_OFFSET;
+ eth_nic_base = NE_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))) 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++)
+ printhb((int)(arptable[ARP_CLIENT].node[i] = romdata[i
+ + ((eth_flags & FLAG_16BIT) ? i : 0)]));
+ printf("\r\n");
+ }
+#endif
+ if (eth_vendor == VENDOR_NONE) return(0);
+
+ 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);
+ 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_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_bmem + 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_bmem + 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_bmem + 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
+/**************************************************************************
+NE1000/NE2000 Support Routines
+**************************************************************************/
+static inline unsigned short inw(unsigned short a)
+{
+ unsigned short d;
+ asm volatile( "inw %1, %0" : "=a" (d) : "d" (a));
+ return d;
+}
+
+static inline void outw(unsigned short a, unsigned short d)
+{
+ asm volatile( "outw %0, %1" : : "a" (d), "d" (a));
+}
+
+/**************************************************************************
+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/i386/netboot/ether.h b/sys/i386/netboot/ether.h
new file mode 100644
index 000000000000..586a35cb963b
--- /dev/null
+++ b/sys/i386/netboot/ether.h
@@ -0,0 +1,178 @@
+/**************************************************************************
+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 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_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},
+ {"SMC8013EBP", TYPE_SMC8013EBP,FLAG_16BIT, MEM_16384},
+ {NULL, 0, 0}
+};
+#endif
+
+/**************************************************************************
+NE1000/2000 definitions
+**************************************************************************/
+#ifndef NE_BASE
+#define NE_BASE 0x320
+#endif
+#define NE_ASIC_OFFSET 0x10
+#define NE_RESET 0x0F /* Used to reset card */
+#define NE_DATA 0x00 /* Used to read/write NIC mem */
+
+/**************************************************************************
+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
+
+struct ringbuffer {
+ unsigned char status;
+ unsigned char bound;
+ unsigned short len;
+};
diff --git a/sys/i386/netboot/wd80x3.c b/sys/i386/netboot/wd80x3.c
deleted file mode 100644
index a68dc44c1f18..000000000000
--- a/sys/i386/netboot/wd80x3.c
+++ /dev/null
@@ -1,240 +0,0 @@
-
-/**************************************************************************
-NETBOOT - BOOTP/TFTP Bootstrap Program
-
-Author: Martin Renters
- Date: Dec/93
-
-**************************************************************************/
-
-#include "netboot.h"
-
-unsigned short we_base;
-unsigned char *we_bmem;
-
-#define WE_LOW_BASE 0x200
-#define WE_HIGH_BASE 0x3e0
-#define WE_DEFAULT_MEM 0xD0000
-#define WE_MIN_PACKET 64
-#define WE_TXBUF_SIZE 6
-#define WE_RXBUF_END 32
-
-#define WE_MSR 0x00
-#define WE_ICR 0x01
-#define WE_IAR 0x02
-#define WE_BIO 0x03
-#define WE_IRR 0x04
-#define WE_LAAR 0x05
-#define WE_IJR 0x06
-#define WE_GP2 0x07
-#define WE_LAR 0x08
-#define WE_BID 0x0E
-#define WE_P0_COMMAND 0x10
-#define WE_P0_PSTART 0x11
-#define WE_P0_PSTOP 0x12
-#define WE_P0_BOUND 0x13
-#define WE_P0_TSR 0x14
-#define WE_P0_TPSR 0x14
-#define WE_P0_TBCR0 0x15
-#define WE_P0_TBCR1 0x16
-#define WE_P0_ISR 0x17
-#define WE_P0_RBCR0 0x1A
-#define WE_P0_RBCR1 0x1B
-#define WE_P0_RSR 0x1C
-#define WE_P0_RCR 0x1C
-#define WE_P0_TCR 0x1D
-#define WE_P0_DCR 0x1E
-#define WE_P0_IMR 0x1F
-#define WE_P1_COMMAND 0x10
-#define WE_P1_PAR0 0x11
-#define WE_P1_PAR1 0x12
-#define WE_P1_PAR2 0x13
-#define WE_P1_PAR3 0x14
-#define WE_P1_PAR4 0x15
-#define WE_P1_PAR5 0x16
-#define WE_P1_CURR 0x17
-#define WE_P1_MAR0 0x18
-
-#define WE_COMMAND_PS0 0x0 /* Page 0 select */
-#define WE_COMMAND_PS1 0x40 /* Page 1 select */
-#define WE_COMMAND_PS2 0x80 /* Page 2 select */
-#define WE_COMMAND_TXP 0x04 /* transmit packet */
-#define WE_COMMAND_STA 0x02 /* start */
-#define WE_COMMAND_STP 0x01 /* stop */
-
-#define WE_ISR_PRX 0x01 /* successful recv */
-#define WE_ISR_PTX 0x02 /* successful xmit */
-#define WE_ISR_RXE 0x04 /* receive error */
-#define WE_ISR_TXE 0x08 /* transmit error */
-#define WE_ISR_OVW 0x10 /* Overflow */
-#define WE_ISR_CNT 0x20 /* Counter overflow */
-#define WE_ISR_RST 0x80 /* reset */
-
-#define WE_RSTAT_PRX 0x01 /* successful recv */
-#define WE_RSTAT_CRC 0x02 /* CRC error */
-#define WE_RSTAT_FAE 0x04 /* Frame alignment error */
-#define WE_RSTAT_OVER 0x08 /* overflow */
-
-char packet[1600];
-int packetlen;
-int bit16;
-
-/**************************************************************************
-ETH_PROBE - Look for an adapter
-**************************************************************************/
-eth_probe()
-{
- unsigned short base;
- unsigned short chksum;
- unsigned char c;
- int i;
- for (we_base = WE_LOW_BASE; we_base <= WE_HIGH_BASE; we_base += 0x20) {
- chksum = 0;
- for (i=8; i<16; i++)
- chksum += inb(i+we_base);
- if ((chksum & 0x00FF) == 0x00FF)
- break;
- }
- if (we_base > WE_HIGH_BASE) return(0); /* No adapter found */
-
- for (i = 1; i<6; i++) /* Look for aliased registers */
- if (inb(we_base+i) != inb(we_base+i+WE_LAR)) break;
- if (i == 6) { /* Aliased */
- we_bmem = (char *)WE_DEFAULT_MEM;
- bit16 = 0;
- } else {
- we_bmem = (char *)(0x80000 | ((inb(we_base+WE_MSR) & 0x3F) << 13));
- bit16 = 1;
- }
- outb(we_base+WE_MSR, 0x80); /* Reset */
- printf("\r\nSMC80x3 base 0x%x, memory 0x%X, etheraddr ",we_base, we_bmem);
- for (i=0; i<6; i++)
- printhb((int)(arptable[ARP_CLIENT].node[i] = inb(i+we_base+WE_LAR)));
- printf("\r\n");
- outb(we_base+WE_MSR,(((unsigned)we_bmem >> 13) & 0x3F) | 0x40);
- iskey(); /* Kill some time while device resets */
- eth_reset();
-}
-
-/**************************************************************************
-ETH_RESET - Reset adapter
-**************************************************************************/
-eth_reset()
-{
- int i;
- outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0 | WE_COMMAND_STP);
- outb(we_base+WE_P0_DCR, 0x48);
- outb(we_base+WE_P0_RBCR0, 0);
- outb(we_base+WE_P0_RBCR1, 0);
- outb(we_base+WE_P0_RCR, 4); /* allow broadcast frames */
- outb(we_base+WE_P0_TCR, 0);
- outb(we_base+WE_P0_TPSR, 0);
- outb(we_base+WE_P0_PSTART, WE_TXBUF_SIZE);
- outb(we_base+WE_P0_PSTOP, WE_RXBUF_END); /* All cards have 8K */
- outb(we_base+WE_P0_BOUND, WE_TXBUF_SIZE);
- outb(we_base+WE_P0_IMR, 0);
- outb(we_base+WE_P0_ISR, 0xFF);
- if (bit16) outb(we_base+WE_LAAR, 1); /* Turn off 16bit mode */
- outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS1);
- for (i=0; i<6; i++)
- outb(we_base+WE_P1_PAR0+i, inb(we_base+WE_LAR+i));
- for (i=0; i<6; i++)
- outb(we_base+WE_P1_MAR0+i, 0xFF);
- outb(we_base+WE_P1_CURR, WE_TXBUF_SIZE+1);
- outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0 | WE_COMMAND_STA);
- 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;
- bcopy(d, we_bmem, 6); /* Copy destination */
- bcopy(arptable[ARP_CLIENT].node, we_bmem+6, ETHER_ADDR_SIZE); /* My ether addr */
- *(we_bmem+12) = t>>8; /* Type field */
- *(we_bmem+13) = t;
- bcopy(p, we_bmem+14, s);
- s += 14;
- while (s < WE_MIN_PACKET) *(we_bmem+(s++)) = 0;
- twiddle();
- outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0);
- outb(we_base+WE_P0_TPSR, 0);
- outb(we_base+WE_P0_TBCR0, s);
- outb(we_base+WE_P0_TBCR1, s>>8);
- outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0 | WE_COMMAND_TXP);
- 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 char *pkt, *p;
- rstat = inb(we_base+WE_P0_RSR);
- if (rstat & WE_RSTAT_OVER) {
- eth_reset();
- return(0);
- }
- if (!(rstat & WE_RSTAT_PRX)) return(0);
- bound = inb(we_base+WE_P0_BOUND)+1;
- if (bound == WE_RXBUF_END) bound = WE_TXBUF_SIZE;
- outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS1);
- curr = inb(we_base+WE_P1_CURR);
- outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0);
- if (curr == WE_RXBUF_END) curr=WE_TXBUF_SIZE;
- if (curr == bound) return(0);
- pkt = we_bmem + (bound << 8);
- len = *((unsigned short *)(pkt+2)) - 4; /* sub CRC */
- if (len > 1514) len = 1514;
-#ifdef DEBUG
-printf("[R%dS%dC%dB%dN%d]",len, rstat, curr,bound,*(pkt+1));
-#endif
- bound = *(pkt+1); /* New bound ptr */
- p = packet;
- if ( (*pkt & WE_RSTAT_PRX) && (len > 14) && (len < 1518)) {
- pkt += 4;
- packetlen = len;
- while (len) {
- while (len && (pkt < (we_bmem + 8192))) {
- *(p++) = *(pkt++);
- len--;
- }
- pkt = we_bmem + (WE_TXBUF_SIZE << 8);
- }
- type = (packet[12]<<8) | packet[13];
- ret = 1;
- }
- if (bound == WE_TXBUF_SIZE)
- bound = WE_RXBUF_END;
- outb(we_base+WE_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);
-}
-
diff --git a/sys/i386/pci/ncr.c b/sys/i386/pci/ncr.c
new file mode 100644
index 000000000000..0c094afcf609
--- /dev/null
+++ b/sys/i386/pci/ncr.c
@@ -0,0 +1,5507 @@
+/**************************************************************************
+**
+** $Id: ncr.c,v 2.0.0.3 94/07/24 09:02:42 wolf Exp $
+**
+** Device driver for the NCR 53C810 PCI-SCSI-Controller.
+**
+** 386bsd / FreeBSD
+**
+**-------------------------------------------------------------------------
+**
+** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany
+** <wolf@dentaro.GUN.de>
+**
+** This is a beta version - use with care.
+**
+**-------------------------------------------------------------------------
+**
+** $Log: ncr.c,v $
+** Revision 2.0.0.3 94/07/24 09:02:42 wolf
+** sstat0 used to calculate residue in int_ma.
+** log messages extended.
+**
+** Revision 2.0.0.2 94/07/22 19:04:26 wolf
+** ncr_int_ma: byte count corrected with dfifo.
+** script: dispatch and no_data changed.
+**
+** Revision 2.0.0.1 94/07/19 21:42:02 wolf
+** New debug value: DEBUG_FREEZE
+** M_REJECT log entry includes rejected message.
+** Phase change in command/status/msg phase logged.
+** Timeout exception handler locked.
+**
+** Revision 2.0 94/07/10 15:53:22 wolf
+** FreeBSD release.
+**
+** Revision 1.0 94/06/07 20:02:16 wolf
+** Beta release.
+**
+***************************************************************************
+*/
+
+#ifdef KERNEL
+#include <ncr.h>
+#else /* KERNEL */
+#define NNCR 1
+#endif /* KERNEL */
+
+#if NNCR > 0
+
+#define NCR_VERSION (2)
+
+
+/*==========================================================
+**
+** Configuration and Debugging
+**
+** May be overwritten in <i386/conf/XXXXX>
+**
+**==========================================================
+*/
+
+/*
+** Enable/Disable debug messages.
+** Can be changed at runtime too.
+*/
+
+#ifndef SCSI_NCR_DEBUG
+#define SCSI_NCR_DEBUG (0)
+#endif /* SCSI_NCR_DEBUG */
+
+/*
+** SCSI address of this device.
+** The boot routines should have set it.
+** If not, use this.
+*/
+
+#ifndef SCSI_NCR_MYADDR
+#define SCSI_NCR_MYADDR (7)
+#endif /* SCSI_NCR_MYADDR */
+
+/*
+** The maximal synchronous frequency in kHz.
+** (0=asynchronous)
+*/
+
+#ifndef SCSI_NCR_MAX_SYNC
+#define SCSI_NCR_MAX_SYNC (0)
+#endif /* SCSI_NCR_MAX_SYNC */
+
+/*
+** The maximum number of tags per logic unit.
+** Used only for disk devices that support tags.
+*/
+
+#ifndef SCSI_NCR_MAX_TAGS
+#define SCSI_NCR_MAX_TAGS (8)
+#endif /* SCSI_NCR_MAX_TAGS */
+
+/*==========================================================
+**
+** Configuration and Debugging
+**
+**==========================================================
+*/
+
+/*
+** Number of targets supported by the driver.
+** n permits target numbers 0..n-1.
+** Default is 7, meaning targets #0..#6.
+** #7 .. is myself.
+*/
+
+#define MAX_TARGET (7)
+
+/*
+** Number of logic units supported by the driver.
+** n enables logic unit numbers 0..n-1.
+** The common SCSI devices require only
+** one lun, so take 1 as the default.
+*/
+
+#define MAX_LUN (1)
+
+/*
+** The maximum number of jobs scheduled for starting.
+** There should be one slot per target, and one slot
+** for each tag of each target.
+*/
+
+#define MAX_START (20)
+
+/*
+** The maximum number of segments a transfer is split into.
+*/
+
+#define MAX_SCATTER (33)
+
+/*
+** The maximum transfer length (should be >= 64k).
+** MUST NOT be greater than (MAX_SCATTER-1) * NBPG.
+*/
+
+#define MAX_SIZE ((MAX_SCATTER-1) * NBPG)
+
+/*
+** Enable some processor/os dependent functions.
+*/
+
+#define DIRTY 1
+
+/*
+** Write disk status information to dkstat ?
+*/
+
+#define DK 1
+
+/*==========================================================
+**
+** Include files
+**
+**==========================================================
+*/
+
+#ifdef KERNEL
+#include <types.h>
+#include <param.h>
+#include <time.h>
+#include <systm.h>
+#include <malloc.h>
+#include <buf.h>
+#include <kernel.h>
+#include <vm/vm.h>
+
+#ifdef DK
+#include <dkstat.h>
+#endif /* DK */
+
+#else /* KERNEL */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#endif /* KERNEL */
+
+#include <i386/pci/ncr_reg.h>
+#include <i386/pci/pci.h>
+#include <i386/pci/pci_device.h>
+
+#ifdef __NetBSD__
+#include <sys/device.h>
+#include <i386/isa/isavar.h>
+#endif
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+
+/*==========================================================
+**
+** Debugging tags
+**
+**==========================================================
+*/
+
+#ifdef SCSI_NCR_DEBUG
+
+#define DEBUG_ALLOC (0x0001)
+#define DEBUG_PHASE (0x0002)
+#define DEBUG_POLL (0x0004)
+#define DEBUG_QUEUE (0x0008)
+#define DEBUG_RESULT (0x0010)
+#define DEBUG_SCATTER (0x0020)
+#define DEBUG_SCRIPT (0x0040)
+#define DEBUG_TINY (0x0080)
+#define DEBUG_TIMING (0x0100)
+#define DEBUG_SDTR (0x0200)
+#define DEBUG_TAGS (0x0400)
+#define DEBUG_FREEZE (0x0800)
+#define DEBUG_NODUMP (0x1000)
+
+int ncr_debug = SCSI_NCR_DEBUG;
+
+#else /* SCSI_NCR_DEBUG */
+int ncr_debug = 0;
+#endif /* SCSI_NCR_DEBUG */
+
+
+/*==========================================================
+**
+** assert ()
+**
+**==========================================================
+**
+** modified copy from 386bsd:/usr/include/sys/assert.h
+**
+**----------------------------------------------------------
+*/
+
+#define assert(expression) { \
+ if (!(expression)) { \
+ (void)printf(\
+ "assertion \"%s\" failed: file \"%s\", line %d\n", \
+ #expression, \
+ __FILE__, __LINE__); \
+ } \
+}
+
+/*==========================================================
+**
+** Access to the controller chip.
+**
+**==========================================================
+*/
+
+#define INB(r) (np->reg->r)
+#define INW(r) (np->reg->r)
+#define INL(r) (np->reg->r)
+
+#define OUTB(r, val) np->reg->r = val
+#define OUTW(r, val) np->reg->r = val
+#define OUTL(r, val) np->reg->r = val
+
+/*==========================================================
+**
+** Command control block states.
+**
+**==========================================================
+*/
+
+#define HS_IDLE (0)
+#define HS_BUSY (1)
+#define HS_NEGOTIATE (2) /* sync. data transfer */
+#define HS_DISCONNECT (3) /* Disconnected by target */
+
+#define HS_COMPLETE (4)
+#define HS_SEL_TIMEOUT (5) /* Selection timeout */
+#define HS_RESET (6) /* SCSI reset */
+#define HS_ABORTED (7) /* Transfer aborted */
+#define HS_TIMEOUT (8) /* Software timeout */
+#define HS_FAIL (9) /* SCSI or PCI bus errors */
+#define HS_UNEXPECTED (10) /* Unexpected disconnect */
+
+/*==========================================================
+**
+** Misc.
+**
+**==========================================================
+*/
+
+#define ILLEGAL_ADDR (0xefffffff)
+#define CCB_MAGIC (0xf2691ad2)
+
+/*==========================================================
+**
+** Capability bits in Inquire response byte #7.
+**
+**==========================================================
+*/
+
+#define INQ7_SYNC (0x10)
+#define INQ7_QUEUE (0x02)
+
+/*==========================================================
+**
+** OS dependencies.
+**
+**==========================================================
+*/
+
+#ifndef __FreeBSD__
+#ifndef __NetBSD__
+ #define ANCIENT
+#endif /*__NetBSD__*/
+#endif /*__FreeBSD__*/
+
+#ifdef ANCIENT
+ #define LUN lu
+ #define TARGET targ
+ #define INT32 int
+ #define U_INT32 long
+ #define TIMEOUT
+#else /* ANCIENT */
+ #define LUN sc_link->lun
+ #define TARGET sc_link->target
+#ifdef __NetBSD__
+ #define INT32 int
+ #define U_INT32 u_int
+ #define TIMEOUT (void*)
+#else /*__NetBSD__*/
+ #define INT32 int32
+ #define U_INT32 u_int32
+ #define TIMEOUT (timeout_func_t)
+#endif /*__NetBSD__*/
+#endif /* ANCIENT */
+
+/*==========================================================
+**
+** Declaration of structs.
+**
+**==========================================================
+*/
+
+struct tcb;
+struct lcb;
+struct ccb;
+struct ncb;
+struct script;
+
+typedef struct ncb * ncb_p;
+typedef struct tcb * tcb_p;
+typedef struct lcb * lcb_p;
+typedef struct ccb * ccb_p;
+
+struct link {
+ u_long l_cmd;
+ u_long l_paddr;
+};
+
+struct usrcmd {
+ u_long target;
+ u_long lun;
+ u_long data;
+ u_long cmd;
+};
+
+#define UC_SETSYNC 10
+#define UC_SETTAGS 11
+#define UC_SETDEBUG 12
+#define UC_SETORDER 13
+
+/*==========================================================
+**
+** Access to fields of structs.
+**
+**==========================================================
+*/
+
+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
+
+/*---------------------------------------
+**
+** Timestamps for profiling
+**
+**---------------------------------------
+*/
+
+struct tstamp {
+ struct timeval start;
+ struct timeval end;
+ struct timeval select;
+ struct timeval command;
+ struct timeval data;
+ struct timeval status;
+ struct timeval disconnect;
+ struct timeval reselect;
+};
+
+/*
+** profiling data (per device)
+*/
+
+struct profile {
+ u_long num_trans;
+ u_long num_bytes;
+ u_long num_disc;
+ u_long num_break;
+ u_long num_int;
+ u_long num_fly;
+ u_long ms_setup;
+ u_long ms_data;
+ u_long ms_disc;
+ u_long ms_post;
+};
+
+/*==========================================================
+**
+** Declaration of structs: TARGET control block
+**
+**==========================================================
+*/
+
+struct tcb {
+ /*
+ ** during reselection the ncr jumps to this point
+ ** with SFBR set to the encoded TARGET number
+ ** with bit 7 set.
+ ** if it's not this target, jump to the next.
+ **
+ ** JUMP IF (SFBR != #TARGET#)
+ ** @(next tcb)
+ */
+
+ struct link jump_tcb;
+
+ /*
+ ** load the actual synchronous mode
+ ** for this target to the sxfer register
+ **
+ ** SCR_COPY (1);
+ ** @(sval field of this tcb)
+ ** @(sxfer register)
+ */
+
+ ncrcmd getscr[3];
+
+ /*
+ ** if next message is "identify"
+ ** then load the message to SFBR,
+ ** else load 0 to SFBR.
+ **
+ ** CALL
+ ** <RESEL_LUN>
+ */
+
+ struct link call_lun;
+
+ /*
+ ** now look for the right lun.
+ **
+ ** JUMP
+ ** @(first ccb of this lun)
+ */
+
+ struct link jump_lcb;
+
+ /*
+ ** pointer to interrupted getcc ccb
+ */
+
+ ccb_p hold_cp;
+
+ /*
+ ** statistical data
+ */
+
+ u_long transfers;
+ u_long bytes;
+
+ /*
+ ** user settable limits for sync transfer
+ ** and tagged commands.
+ */
+
+ u_char usrsync;
+ u_char usrtags;
+
+ /*
+ ** negotiation of synch transfer and tagged commands
+ */
+
+ u_short period;
+ u_char _1;
+ u_char sval;
+ u_char minsync;
+ u_char maxoffs;
+
+ /*
+ ** inquire data
+ */
+#define MAX_INQUIRE 36
+ u_char inqdata[MAX_INQUIRE];
+
+ /*
+ ** the lcb's of this tcb
+ */
+
+ lcb_p lp[MAX_LUN];
+};
+
+/*==========================================================
+**
+** Declaration of structs: LUN control block
+**
+**==========================================================
+*/
+
+struct lcb {
+ /*
+ ** during reselection the ncr jumps to this point
+ ** with SFBR set to the "Identify" message.
+ ** if it's not this lun, jump to the next.
+ **
+ ** JUMP IF (SFBR == #LUN#)
+ ** @(next lcb of this target)
+ */
+
+ struct link jump_lcb;
+
+ /*
+ ** if next message is "simple tag",
+ ** then load the tag to SFBR,
+ ** else load 0 to SFBR.
+ **
+ ** CALL
+ ** <RESEL_TAG>
+ */
+
+ struct link call_tag;
+
+ /*
+ ** now look for the right ccb.
+ **
+ ** JUMP
+ ** @(first ccb of this lun)
+ */
+
+ struct link jump_ccb;
+
+ /*
+ ** start of the ccb chain
+ */
+
+ ccb_p next_ccb;
+
+ /*
+ ** Control of tagged queueing
+ */
+
+ u_char reqccbs;
+ u_char actccbs;
+ u_char reqlink;
+ u_char actlink;
+ u_char usetags;
+ u_char lasttag;
+};
+
+/*==========================================================
+**
+** Declaration of structs: COMMAND control block
+**
+**==========================================================
+**
+** This substructure is copied from the ccb to a
+** global address after selection (or reselection)
+** and copied back before disconnect.
+**
+** These fields are accessible to the script processor.
+**
+**----------------------------------------------------------
+*/
+
+struct head {
+ /*
+ ** Execution of a ccb starts at this point.
+ ** It's a jump to the "SELECT" label
+ ** of the script.
+ **
+ ** After successful selection the script
+ ** processor overwrites it with a jump to
+ ** the IDLE label of the script.
+ */
+
+ struct link launch;
+
+ /*
+ ** Saved data pointer.
+ ** Points to the position in the script
+ ** responsible for the actual transfer
+ ** of data.
+ ** It's written after reception of a
+ ** "SAVE_DATA_POINTER" message.
+ */
+
+ u_long savep;
+
+ /*
+ ** The virtual address of the ccb
+ ** containing this header.
+ */
+
+ ccb_p cp;
+
+ /*
+ ** space for some timestamps to gather
+ ** profiling data about devices and this driver.
+ */
+
+ struct tstamp stamp;
+
+ /*
+ ** status fields.
+ */
+
+ u_char status[8];
+
+#define host_status phys.header.status[0]
+#define scsi_status phys.header.status[1]
+#define scs2_status phys.header.status[2]
+#define sync_status phys.header.status[3]
+#define parity_errs phys.header.status[4]
+};
+
+/*==========================================================
+**
+** Declaration of structs: Data structure block
+**
+**==========================================================
+**
+** During execution of a ccb by the script processor,
+** the DSA (data structure address) register points
+** to this substructure of the ccb.
+** This substructure contains the header with
+** the script-processor-changable data and
+** data blocks for the indirect move commands.
+**
+**----------------------------------------------------------
+*/
+
+struct dsb {
+
+ /*
+ ** Header.
+ ** Has to be the first entry,
+ ** because it's jumped to by the
+ ** script processor
+ */
+
+ struct head header;
+
+ /*
+ ** Table data for Script
+ */
+
+ struct scr_tblsel select;
+ struct scr_tblmove smsg ;
+ struct scr_tblmove smsg2 ;
+ struct scr_tblmove cmd ;
+ struct scr_tblmove sense ;
+ struct scr_tblmove data [MAX_SCATTER];
+};
+
+/*==========================================================
+**
+** Declaration of structs: Command control block.
+**
+**==========================================================
+**
+** During execution of a ccb by the script processor,
+** the DSA (data structure address) register points
+** to this substructure of the ccb.
+** This substructure contains the header with
+** the script-processor-changable data and then
+** data blocks for the indirect move commands.
+**
+**----------------------------------------------------------
+*/
+
+
+struct ccb {
+ /*
+ ** during reselection the ncr jumps to this point.
+ ** If a "SIMPLE_TAG" message was received,
+ ** then SFBR is set to the tag.
+ ** else SFBR is set to 0
+ ** If looking for another tag, jump to the next ccb.
+ **
+ ** JUMP IF (SFBR != #TAG#)
+ ** @(next ccb of this lun)
+ */
+
+ struct link jump_ccb;
+
+ /*
+ ** After execution of this call, the return address
+ ** (in the TEMP register) points to the following
+ ** data structure block.
+ ** So copy it to the DSA register, and start
+ ** processing of this data structure.
+ **
+ ** CALL
+ ** <RESEL_TMP>
+ */
+
+ struct link call_tmp;
+
+ /*
+ ** This is the data structure which is
+ ** to be executed by the script processor.
+ */
+
+ struct dsb phys;
+
+ /*
+ ** If a data transfer phase is terminated too early
+ ** (after reception of a message (i.e. DISCONNECT)),
+ ** we have to prepare a mini script to transfer
+ ** the rest of the data.
+ */
+
+ u_long patch[8];
+
+ /*
+ ** The general SCSI driver provides a
+ ** pointer to a control block.
+ */
+
+ struct scsi_xfer *xfer;
+
+#ifdef ANCIENT
+ /*
+ ** We copy the SCSI command, because it
+ ** may be volatile (on the stack).
+ **
+ */
+ struct scsi_generic cmd;
+#endif /* ANCIENT */
+
+ /*
+ ** We prepare a message to be sent after selection,
+ ** and a second one to be sent after getcc selection.
+ ** Contents are IDENTIFY and SIMPLE_TAG.
+ ** And sdtr .. (if negotiating sync transfers)
+ */
+
+ u_char scsi_smsg [8];
+ u_char scsi_smsg2[8];
+
+ /*
+ ** Lock this ccb.
+ ** Flag is used while looking for a free ccb.
+ */
+
+ u_long magic;
+ u_long tlimit;
+
+ /*
+ ** All ccbs of one hostadapter are linked.
+ */
+
+ ccb_p link_ccb;
+
+ /*
+ ** All ccbs of one target/lun are linked.
+ */
+
+ ccb_p next_ccb;
+
+ /*
+ ** Tag for this transfer.
+ ** It's patched into jump_ccb.
+ ** If it's not zero, a SIMPLE_TAG
+ ** message is included in smsg.
+ */
+
+ u_char tag;
+};
+
+/*==========================================================
+**
+** Declaration of structs: NCR device descriptor
+**
+**==========================================================
+*/
+
+struct ncb {
+ /*-----------------------------------------------
+ ** Scripts ..
+ **-----------------------------------------------
+ **
+ ** During reselection the ncr jumps to this point.
+ ** The SFBR register is loaded with the encoded target id.
+ **
+ ** Jump to the first target.
+ **
+ ** JUMP
+ ** @(next tcb)
+ */
+ struct link jump_tcb;
+
+ /*-----------------------------------------------
+ ** Configuration ..
+ **-----------------------------------------------
+ **
+ ** virtual and physical addresses
+ ** of the 53c810 chip.
+ */
+ vm_offset_t vaddr;
+ vm_offset_t paddr;
+
+ /*
+ ** pointer to the chip's registers.
+ */
+ volatile
+ struct ncr_reg* reg;
+
+ /*
+ ** virtual and physical addresses
+ ** of the script.
+ */
+ struct script* v_script;
+ u_long p_script;
+
+ /*
+ ** The SCSI address of the host adapter.
+ */
+ u_char myaddr;
+
+ /*
+ ** timing parameters
+ */
+ u_char ns_async;
+ u_char ns_sync;
+ u_char rv_scntl3;
+
+#ifndef ANCIENT
+ /*-----------------------------------------------
+ ** Link to the generic SCSI driver
+ **-----------------------------------------------
+ */
+
+ struct scsi_link sc_link;
+#endif /* ANCIENT */
+
+ /*-----------------------------------------------
+ ** Job control
+ **-----------------------------------------------
+ **
+ ** Commands from user
+ */
+ struct usrcmd user;
+ u_char order;
+
+ /*
+ ** Target data
+ */
+ struct tcb target[MAX_TARGET];
+
+ /*
+ ** Start queue.
+ */
+ u_long squeue [MAX_START];
+ u_short squeueput;
+ u_short actccbs;
+
+ /*
+ ** Timeout handler
+ */
+ u_long heartbeat;
+ u_short ticks;
+ u_short latetime;
+ u_long lasttime;
+ u_short imask;
+ u_short mcount;
+
+ /*-----------------------------------------------
+ ** Debug and profiling
+ **-----------------------------------------------
+ **
+ ** register dump
+ */
+ struct ncr_reg regdump;
+ struct timeval regtime;
+
+ /*
+ ** Profiling data
+ */
+ struct profile profile;
+ u_long disc_phys;
+ u_long disc_ref;
+
+ /*-----------------------------------------------
+ ** Working areas
+ **-----------------------------------------------
+ **
+ ** The global header.
+ ** Accessible to both the host and the
+ ** script-processor.
+ */
+
+ struct head header;
+
+ /*
+ ** The global control block.
+ ** It's used only during the configuration phase.
+ ** A target control block will be created
+ ** after the first successful transfer.
+ */
+
+ struct ccb ccb;
+
+ /*
+ ** message buffers.
+ ** Should be longword aligned,
+ ** because they're written with a
+ ** COPY script command.
+ */
+
+ u_char msgout[8];
+ u_char msgin [8];
+
+ /*
+ ** Buffer for STATUS_IN phase.
+ */
+
+ u_char scratch;
+ u_char lock; /* @DEBUG@ */
+
+#ifdef __NetBSD__
+ /*-----------------------------------------------
+ ** Interrupt handler.
+ **-----------------------------------------------
+ **
+ ** Only for NetBSD
+ */
+
+ struct intrhand ihand;
+#endif
+
+};
+
+/*==========================================================
+**
+**
+** Script for NCR-Processor.
+**
+** Use ncr_script_fill() to create the variable parts.
+** Use ncr_script_bind() to bind to physical addresses.
+**
+**
+**==========================================================
+**
+** We have to know the offsets of all labels before
+** we reach them (for forward jumps).
+** Therefore we declare a struct here.
+** If you make changes inside the script,
+** DONT FORGET TO CHANGE THE LENGTHS HERE!
+**
+**----------------------------------------------------------
+*/
+
+struct script {
+ ncrcmd start [ 2];
+ ncrcmd start1 [ 10];
+ ncrcmd startpos [ 1];
+ ncrcmd tryloop [MAX_START*5+2];
+ ncrcmd trysel [ 8];
+ ncrcmd skip [ 8];
+ ncrcmd skip2 [ 3];
+ ncrcmd idle [ 2];
+ ncrcmd select [ 22];
+ ncrcmd prepare [ 4];
+ ncrcmd loadpos [ 24];
+ ncrcmd prepare2 [ 20];
+ ncrcmd setmsg [ 5];
+ ncrcmd clrack [ 6];
+ ncrcmd dispatch [ 22];
+ ncrcmd no_data [ 19];
+ ncrcmd checkatn [ 16];
+ ncrcmd command [ 15];
+ ncrcmd status [ 25];
+ ncrcmd msg_in [ 22];
+ ncrcmd msg_bad [ 6];
+ ncrcmd msg_parity [ 12];
+ ncrcmd msg_reject [ 6];
+ ncrcmd msg_extended [ 34];
+ ncrcmd msg_sdtr [ 39];
+ ncrcmd complete [ 6];
+ ncrcmd cleanup [ 12];
+ ncrcmd savepos [ 11];
+ ncrcmd signal [ 10];
+ ncrcmd save_dp [ 5];
+ ncrcmd restore_dp [ 5];
+ ncrcmd disconnect [ 21];
+ ncrcmd msg_out [ 9];
+ ncrcmd msg_out_done [ 7];
+ ncrcmd msg_out_abort [ 10];
+ ncrcmd getcc [ 4];
+ ncrcmd getcc1 [ 5];
+ ncrcmd getcc2 [ 33];
+ ncrcmd badgetcc [ 2];
+ ncrcmd reselect [ 12];
+ ncrcmd reselect2 [ 6];
+ ncrcmd resel_tmp [ 5];
+ ncrcmd resel_lun [ 18];
+ ncrcmd resel_tag [ 24];
+ ncrcmd data_in [MAX_SCATTER * 4 + 7];
+ ncrcmd data_out [MAX_SCATTER * 4 + 7];
+ ncrcmd aborttag [ 4];
+ ncrcmd abort [ 20];
+};
+
+/*==========================================================
+**
+**
+** Function Headers.
+**
+**
+**==========================================================
+*/
+
+#ifdef KERNEL
+#ifdef ANCIENT
+extern int splbio(void);
+extern void splx(int level);
+extern int wakeup(void* channel);
+extern int tsleep();
+extern int DELAY();
+extern int scsi_attachdevs();
+extern void timeout();
+extern void untimeout();
+#endif /* ANCIENT */
+
+static void ncr_alloc_ccb (ncb_p np, struct scsi_xfer * xp);
+static int ncr_attach (pcici_t config_id);
+static void ncr_complete (ncb_p np, ccb_p cp);
+static int ncr_delta (struct timeval * from, struct timeval * to);
+static void ncr_exception (ncb_p np);
+static void ncr_free_ccb (ncb_p np, ccb_p cp, int flags);
+static void ncr_getclock (ncb_p np);
+static ccb_p ncr_get_ccb (ncb_p np, u_long flags, u_long t,u_long l);
+static U_INT32 ncr_info (int unit);
+static void ncr_init (ncb_p np, char * msg, u_long code);
+static void ncr_int_ma (ncb_p np);
+static void ncr_int_sir (ncb_p np);
+static void ncr_int_sto (ncb_p np);
+static int ncr_intr (int dev);
+static void ncr_min_phys (struct buf *bp);
+static void ncr_opennings (ncb_p np, lcb_p lp, struct scsi_xfer * xp);
+static int ncb_probe (pcici_t config_id);
+static void ncb_profile (ncb_p np, ccb_p cp);
+static void ncr_script_bind (ncb_p np);
+static void ncr_script_copy (ncb_p oldnp, ncb_p np);
+static void ncr_script_fill (struct script * scr);
+static u_long ncr_scatter (struct dsb* phys,u_long vaddr,u_long datalen);
+static void ncr_setmaxtags (tcb_p tp, u_long usrtags);
+static void ncr_setsync (ncb_p np, ccb_p xxxcp, u_char sxfer);
+static void ncr_settags (tcb_p tp, lcb_p lp);
+static INT32 ncr_start (struct scsi_xfer *xp);
+static void ncr_timeout (ncb_p np);
+static void ncr_usercmd (ncb_p np);
+static void ncr_wakeup (ncb_p np, u_long code);
+
+/*==========================================================
+**
+**
+** Access to processor ports.
+**
+**
+**==========================================================
+*/
+
+#ifdef DIRTY
+#include <i386/isa/isa.h>
+#ifdef ANCIENT
+/*
+** Doch das ist alles nur geklaut ..
+** aus: 386bsd:/sys/i386/include/pio.h
+**
+** Mach Operating System
+** Copyright (c) 1990 Carnegie-Mellon University
+** All rights reserved. The CMU software License Agreement specifies
+** the terms and conditions for use and redistribution.
+*/
+
+#undef inb
+#define inb(port) \
+({ unsigned char data; \
+ __asm __volatile("inb %1, %0": "=a" (data): "d" ((u_short)(port))); \
+ data; })
+
+#undef outb
+#define outb(port, data) \
+{__asm __volatile("outb %0, %1"::"a" ((u_char)(data)), "d" ((u_short)(port)));}
+
+#define disable_intr() \
+{__asm __volatile("cli");}
+
+#define enable_intr() \
+{__asm __volatile("sti");}
+#endif /* ANCIENT */
+
+/*------------------------------------------------------------------
+**
+** getirr: get a bit vector of the pending interrupts.
+**
+** NOTE: this is HIGHLY hardware dependant :-(
+**
+**------------------------------------------------------------------
+*/
+
+
+static u_long getirr (void)
+{
+ u_long mask;
+
+ disable_intr();
+
+ outb (IO_ICU2, 0x0a);
+ mask = inb (IO_ICU2);
+ outb (IO_ICU2, 0x0b);
+
+ mask <<= 8;
+
+ outb (IO_ICU1, 0x0a);
+ mask|= inb (IO_ICU1);
+ outb (IO_ICU1, 0x0b);
+
+ enable_intr();
+
+ return (mask);
+}
+
+#else /* DIRTY */
+ #define getirr() (0)
+#endif /* DIRTY */
+#endif /* KERNEL */
+
+/*==========================================================
+**
+**
+** Global static data.
+**
+**
+**==========================================================
+*/
+
+
+static char ident[] =
+ "\n$Id: ncr.c,v 2.0.0.3 94/07/24 09:02:42 wolf Exp $\n"
+ "Copyright (c) 1994, Wolfgang Stanglmeier\n";
+
+
+int ncr_version = NCR_VERSION
+ + sizeof (struct ncb) * sizeof (struct ccb)
+ * sizeof (struct lcb) * sizeof (struct tcb);
+
+#ifdef KERNEL
+
+u_long ncr_units;
+u_long nncr=NNCR;
+struct ncb ncr0;
+ncb_p (ncrp [NNCR]) = {&ncr0};
+u_long ncr_msgout; /* @DEBUG@ */
+
+/*
+** SCSI cmd to get the SCSI sense data
+*/
+
+static u_char rs_cmd [6] =
+ { 0x03, 0, 0, 0, sizeof (struct scsi_sense_data), 0 };
+
+/*==========================================================
+**
+**
+** Global static data: auto configure
+**
+**
+**==========================================================
+*/
+
+struct pci_driver ncrdevice = {
+ ncb_probe,
+ ncr_attach,
+ 0x00011000ul,
+ "ncr",
+ "ncr 53c810 scsi",
+ ncr_intr
+};
+
+
+#ifndef ANCIENT
+struct scsi_adapter ncr_switch =
+{
+ ncr_start,
+ ncr_min_phys,
+ 0,
+ 0,
+ ncr_info,
+ "ncr",
+};
+
+struct scsi_device ncr_dev =
+{
+ NULL, /* Use default error handler */
+ NULL, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
+ "ncr",
+};
+#else /* ANCIENT */
+struct scsi_switch ncr_switch =
+{
+ ncr_start,
+ ncr_min_phys,
+ 0,
+ 0,
+ ncr_info,
+ 0,0,0
+};
+#endif /* ANCIENT */
+
+/*==========================================================
+**
+**
+** Scripts for NCR-Processor.
+**
+** Use ncr_script_bind for binding to physical addresses.
+**
+**
+**==========================================================
+**
+**
+**
+** PADDR generates a reference to another part of the script.
+** REG generates a reference to a script processor register.
+**
+**
+**----------------------------------------------------------
+*/
+
+#define PADDR(label) ((ncrcmd) &script0.label)
+
+static struct script script0 = {
+/*--------------------------< START >-----------------------*/ {
+ /*
+ ** Hook for interrupted GetConditionCode.
+ ** Will be patched to ... IFTRUE by
+ ** the interrupt handler.
+ */
+ SCR_INT ^ IFFALSE (0),
+ 1,
+}/*-------------------------< START1 >----------------------*/,{
+ /*
+ ** Hook for stalled start queue.
+ ** Will be patched to IFTRUE by the interrupt handler.
+ */
+ SCR_INT ^ IFFALSE (0),
+ 7,
+ /*
+ ** Claim to be still alive ...
+ */
+ SCR_COPY (sizeof (ncr0.heartbeat)),
+ (ncrcmd) &time.tv_sec,
+ (ncrcmd) &ncr0.heartbeat,
+ /*
+ ** Make data structure address invalid.
+ ** clear SIGP.
+ */
+ SCR_LOAD_REG (dsa, 0xff),
+ 0,
+ SCR_FROM_REG (ctest2),
+ 0,
+ /*
+ ** Then jump to a certain point in tryloop.
+ ** Due to the lack of indirect addressing the code
+ ** is self modifying here.
+ */
+ SCR_JUMP,
+}/*-------------------------< STARTPOS >--------------------*/,{
+ PADDR(tryloop),
+
+}/*-------------------------< TRYLOOP >---------------------*/,{
+/*
+** Load an entry of the start queue into dsa
+** and try to start it by jumping to TRYSEL.
+**
+** Because the size depends on the
+** #define MAX_START parameter, it is filled
+** in at runtime.
+**
+**-----------------------------------------------------------
+**
+** ##===========< I=0; i<MAX_START >===========
+** || SCR_COPY (4),
+** || (ncrcmd) &ncr0.squeue[i],
+** || REG (dsa),
+** || SCR_CALL,
+** || PADDR (trysel),
+** ##==========================================
+**
+** SCR_JUMP,
+** PADDR(tryloop),
+**
+**-----------------------------------------------------------
+*/
+0
+
+}/*-------------------------< TRYSEL >----------------------*/,{
+ /*
+ ** Now:
+ ** DSA: Address of a Data Structure
+ ** or Address of the IDLE-Label.
+ **
+ ** TEMP: Address of a script, which tries to
+ ** start the NEXT entry.
+ **
+ ** Save the TEMP register into the SCRATCHA register.
+ ** Then copy the DSA to TEMP and RETURN.
+ ** This is kind of an indirect jump.
+ ** (The script processor has NO stack, so the
+ ** CALL is actually a jump and link, and the
+ ** RETURN is an indirect jump.)
+ **
+ ** If the slot was empty, DSA contains the address
+ ** of the IDLE part of this script. The processor
+ ** jumps to IDLE and waits for a reselect.
+ ** It will wake up and try the same slot again
+ ** after the SIGP bit becomes set by the host.
+ **
+ ** If the slot was not empty, DSA contains
+ ** the address of the phys-part of a ccb.
+ ** The processor jumps to this address.
+ ** phys starts with head,
+ ** head starts with launch,
+ ** so actually the processor jumps to
+ ** the lauch part.
+ ** If the entry is scheduled to be executed,
+ ** then launch contains a jump to SELECT.
+ ** If it's not scheduled, it contains a jump to IDLE.
+ */
+ SCR_COPY (4),
+ REG (temp),
+ REG (scratcha),
+ SCR_COPY (4),
+ REG (dsa),
+ REG (temp),
+ SCR_RETURN,
+ 0
+
+}/*-------------------------< SKIP >------------------------*/,{
+ /*
+ ** This entry has been canceled.
+ ** Next time use the next slot.
+ */
+ SCR_COPY (4),
+ REG (scratcha),
+ PADDR (startpos),
+ /*
+ ** patch the launch field.
+ ** should look like an idle process.
+ */
+ SCR_COPY (4),
+ REG (dsa),
+ PADDR (skip2),
+ SCR_COPY (8),
+ PADDR (idle),
+}/*-------------------------< SKIP2 >-----------------------*/,{
+ 0,
+ SCR_JUMP,
+ PADDR(start),
+}/*-------------------------< IDLE >------------------------*/,{
+ /*
+ ** Nothing to do?
+ ** Wait for reselect.
+ */
+ SCR_JUMP,
+ PADDR(reselect),
+
+}/*-------------------------< SELECT >----------------------*/,{
+ /*
+ ** DSA contains the address of a scheduled
+ ** data structure.
+ **
+ ** SCRATCHA contains the address of the script,
+ ** which starts the next entry.
+ **
+ ** Set Initiator mode.
+ **
+ ** (Target mode is left as an exercise for the student)
+ */
+
+ SCR_CLR (SCR_TRG),
+ 0,
+ SCR_LOAD_REG (scr0, 0xff),
+ 0,
+
+ /*
+ ** And try to select this target.
+ */
+ SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select),
+ PADDR (reselect),
+
+ /*
+ ** Now there are 4 possibilities:
+ **
+ ** (1) The ncr looses arbitration.
+ ** This is ok, because it will try again,
+ ** when the bus becomes idle.
+ ** (But beware of the timeout function!)
+ **
+ ** (2) The ncr is reselected.
+ ** Then the script processor takes the jump
+ ** to the RESELECT label.
+ **
+ ** (3) The ncr completes the selection.
+ ** Then it will execute the next statement.
+ **
+ ** (4) There is a selection timeout.
+ ** Then the ncr should interrupt the host and stop.
+ ** Unfortunately, it seems to continue execution
+ ** of the script. But it will fail with an
+ ** IID-interrupt on the next WHEN.
+ */
+
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)),
+ 0,
+
+ /*
+ ** Save target id to ctest0 register
+ */
+
+ SCR_FROM_REG (sdid),
+ 0,
+ SCR_TO_REG (ctest0),
+ 0,
+ /*
+ ** Send the IDENTIFY and SIMPLE_TAG messages
+ ** (and the M_X_SDTR message)
+ */
+ SCR_MOVE_TBL ^ SCR_MSG_OUT,
+ offsetof (struct dsb, smsg),
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+ -16,
+ SCR_COPY (1),
+ REG (sfbr),
+ (ncrcmd) &ncr_msgout,
+ /*
+ ** Selection complete.
+ ** Next time use the next slot.
+ */
+ SCR_COPY (4),
+ REG (scratcha),
+ PADDR (startpos),
+}/*-------------------------< PREPARE >----------------------*/,{
+ /*
+ ** The ncr doesn't have an indirect load
+ ** or store command. So we have to
+ ** copy part of the control block to a
+ ** fixed place, where we can access it.
+ **
+ ** We patch the address part of a
+ ** COPY command with the DSA-register.
+ */
+ SCR_COPY (4),
+ REG (dsa),
+ PADDR (loadpos),
+ /*
+ ** then we do the actual copy.
+ */
+ SCR_COPY (sizeof (struct head)),
+ /*
+ ** continued after the next label ...
+ */
+
+}/*-------------------------< LOADPOS >---------------------*/,{
+ 0,
+ (ncrcmd) &ncr0.header,
+ /*
+ ** Mark this ccb as not scheduled.
+ */
+ SCR_COPY (8),
+ PADDR (idle),
+ (ncrcmd) &ncr0.header.launch,
+ /*
+ ** Set a time stamp for this selection
+ */
+ SCR_COPY (sizeof (struct timeval)),
+ (ncrcmd) &time,
+ (ncrcmd) &ncr0.header.stamp.select,
+ /*
+ ** load the savep (saved pointer) into
+ ** the TEMP register (actual pointer)
+ */
+ SCR_COPY (4),
+ (ncrcmd) &ncr0.header.savep,
+ REG (temp),
+ /*
+ ** Initialize the status registers
+ */
+ SCR_COPY (4),
+ (ncrcmd) &ncr0.header.status,
+ REG (scr0),
+ /*
+ ** Set carry according to host_status
+ */
+ SCR_CLR (SCR_CARRY),
+ 0,
+ SCR_FROM_REG (scr0),
+ 0,
+ SCR_JUMPR ^ IFFALSE (DATA (HS_NEGOTIATE)),
+ 16,
+ SCR_LOAD_REG (scr0, HS_BUSY),
+ 0,
+ SCR_SET (SCR_CARRY),
+ 0,
+
+}/*-------------------------< PREPARE2 >---------------------*/,{
+ /*
+ ** <Carry set iff SDTM message sent>
+ **
+ ** Load the synchronous mode register
+ */
+ SCR_FROM_REG (scr3),
+ 0,
+ SCR_TO_REG (sxfer),
+ 0,
+ /*
+ ** Initialize the msgout buffer with a NOOP message.
+ */
+ SCR_LOAD_REG (scratcha, M_NOOP),
+ 0,
+ SCR_COPY (1),
+ REG (scratcha),
+ (ncrcmd) &ncr0.msgout,
+ SCR_COPY (1),
+ REG (scratcha),
+ (ncrcmd) &ncr0.msgin,
+ /*
+ ** If M_X_SDTR sent, but no MSG_IN phase, ...
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+ PADDR (msg_in),
+ SCR_JUMP ^ IFFALSE (CARRYSET),
+ PADDR (dispatch),
+ /*
+ ** no answer is an answer, too.
+ */
+ SCR_INT,
+ 3,
+ SCR_JUMP,
+ PADDR (dispatch),
+
+}/*-------------------------< SETMSG >----------------------*/,{
+ SCR_COPY (1),
+ REG (scratcha),
+ (ncrcmd) &ncr0.msgout,
+ SCR_SET (SCR_ATN),
+ 0,
+}/*-------------------------< CLRACK >----------------------*/,{
+ /*
+ ** Terminate sdtr mode.
+ ** Terminate possible pending message phase.
+ */
+ SCR_FROM_REG (scr0),
+ 0,
+ SCR_LOAD_REG (scr0, HS_BUSY),
+ 0,
+ SCR_CLR (SCR_ACK | SCR_CARRY),
+ 0,
+
+}/*-----------------------< DISPATCH >----------------------*/,{
+ SCR_RETURN ^ IFTRUE (WHEN (SCR_DATA_OUT)),
+ 0,
+ SCR_RETURN ^ IFTRUE (IF (SCR_DATA_IN)),
+ 0,
+ SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)),
+ PADDR (msg_out),
+ SCR_JUMP ^ IFTRUE (IF (SCR_MSG_IN)),
+ PADDR (msg_in),
+ SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)),
+ PADDR (command),
+ SCR_JUMP ^ IFTRUE (IF (SCR_STATUS)),
+ PADDR (status),
+ /*
+ ** Discard one illegal phase byte, if required.
+ */
+ SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_OUT)),
+ 8,
+ SCR_MOVE_ABS (1) ^ SCR_ILG_OUT,
+ (ncrcmd) &ncr0.scratch,
+ SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_IN)),
+ 8,
+ SCR_MOVE_ABS (1) ^ SCR_ILG_IN,
+ (ncrcmd) &ncr0.scratch,
+ SCR_JUMP,
+ PADDR (dispatch),
+}/*-------------------------< NO_DATA >--------------------*/,{
+ /*
+ ** The target wants to tranfer too much data
+ ** or in the wrong direction.
+ ** Prepare an abort message and set ATN.
+ */
+ SCR_LOAD_REG (scratcha, M_ABORT),
+ 0,
+ SCR_COPY (1),
+ REG (scratcha),
+ (ncrcmd) &ncr0.msgout,
+ SCR_SET (SCR_ATN),
+ 0,
+ /*
+ ** Discard one data byte, if required.
+ */
+ SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_OUT)),
+ 8,
+ SCR_MOVE_ABS (1) ^ SCR_DATA_OUT,
+ (ncrcmd) &ncr0.scratch,
+ SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)),
+ 8,
+ SCR_MOVE_ABS (1) ^ SCR_DATA_IN,
+ (ncrcmd) &ncr0.scratch,
+ /*
+ ** .. and repeat as required.
+ */
+ SCR_CALL,
+ PADDR (dispatch),
+ SCR_JUMP,
+ PADDR (no_data),
+
+}/*-------------------------< CHECKATN >--------------------*/,{
+ /*
+ ** If AAP (bit 1 of scntl0 register) is set
+ ** and a parity error is detected,
+ ** the script processor asserts ATN.
+ **
+ ** The target should switch to a MSG_OUT phase
+ ** to get the message.
+ */
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFFALSE (MASK (CATN, CATN)),
+ PADDR (dispatch),
+ /*
+ ** count it
+ */
+ SCR_COPY (1),
+ (ncrcmd) &ncr0.header.status[4],
+ REG (scratcha),
+ SCR_REG_REG (scratcha, SCR_ADD, 0x01),
+ 0,
+ SCR_COPY (1),
+ REG (scratcha),
+ (ncrcmd) &ncr0.header.status[4],
+ /*
+ ** Prepare a M_ID_ERROR message
+ ** (initiator detected error).
+ ** The target should retry the transfer.
+ */
+ SCR_LOAD_REG (scratcha, M_ID_ERROR),
+ 0,
+ SCR_JUMP,
+ PADDR (setmsg),
+
+}/*-------------------------< COMMAND >--------------------*/,{
+ /*
+ ** If this is not a GETCC transfer ...
+ */
+ SCR_FROM_REG (scr1),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFTRUE (DATA (S_CHECK_COND)),
+ 28,
+ /*
+ ** ... set a timestamp ...
+ */
+ SCR_COPY (sizeof (struct timeval)),
+ (ncrcmd) &time,
+ (ncrcmd) &ncr0.header.stamp.command,
+ /*
+ ** ... and send the command
+ */
+ SCR_MOVE_TBL ^ SCR_COMMAND,
+ offsetof (struct dsb, cmd),
+ SCR_JUMP,
+ PADDR (dispatch),
+ /*
+ ** Send the GETCC command
+ */
+/*>>>*/ SCR_MOVE_ABS (6) ^ SCR_COMMAND,
+ (ncrcmd) &rs_cmd,
+ SCR_JUMP,
+ PADDR (dispatch),
+
+}/*-------------------------< STATUS >--------------------*/,{
+ /*
+ ** set the timestamp.
+ */
+ SCR_COPY (sizeof (struct timeval)),
+ (ncrcmd) &time,
+ (ncrcmd) &ncr0.header.stamp.status,
+ /*
+ ** If this is a GETCC transfer,
+ */
+ SCR_FROM_REG (scr1),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (S_CHECK_COND)),
+ 32,
+ /*
+ ** get the status
+ */
+ SCR_MOVE_ABS (1) ^ SCR_STATUS,
+ (ncrcmd) &ncr0.scratch,
+ /*
+ ** Save status to scs2_status.
+ ** Mark as complete.
+ ** And wait for disconnect.
+ */
+ SCR_TO_REG (scr2),
+ 0,
+ SCR_LOAD_REG (scr0, HS_COMPLETE),
+ 0,
+ SCR_JUMP,
+ PADDR (checkatn),
+ /*
+ ** If it was no GETCC transfer,
+ ** save the status to scsi_status.
+ */
+/*>>>*/ SCR_MOVE_ABS (1) ^ SCR_STATUS,
+ (ncrcmd) &ncr0.scratch,
+ SCR_TO_REG (scr1),
+ 0,
+ /*
+ ** if it was no check condition ...
+ */
+ SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)),
+ PADDR (checkatn),
+ /*
+ ** ... mark as complete.
+ */
+ SCR_LOAD_REG (scr0, HS_COMPLETE),
+ 0,
+ SCR_JUMP,
+ PADDR (checkatn),
+
+}/*-------------------------< MSG_IN >--------------------*/,{
+ /*
+ ** Get the first byte of the message
+ ** and save it to SCRATCHA.
+ **
+ ** The script processor doesn't negate the
+ ** ACK signal after this transfer.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ (ncrcmd) &ncr0.msgin[0],
+ /*
+ ** Check for message parity error.
+ */
+ SCR_TO_REG (scratcha),
+ 0,
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDR (msg_parity),
+ SCR_FROM_REG (scratcha),
+ 0,
+ /*
+ ** Parity was ok, handle this message.
+ */
+ SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE)),
+ PADDR (complete),
+ SCR_JUMP ^ IFTRUE (DATA (M_SAVE_DP)),
+ PADDR (save_dp),
+ SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP)),
+ PADDR (restore_dp),
+ SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)),
+ PADDR (disconnect),
+ SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
+ PADDR (msg_extended),
+ SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
+ PADDR (msg_reject),
+ /*
+ ** Rest of the messages left as
+ ** an exercise ...
+ **
+ ** Unimplemented messages:
+ ** fall through to MSG_BAD.
+ */
+}/*-------------------------< MSG_BAD >------------------*/,{
+ /*
+ ** unimplemented message - reject it.
+ */
+ SCR_INT,
+ 6,
+ SCR_LOAD_REG (scratcha, M_REJECT),
+ 0,
+ SCR_JUMP,
+ PADDR (setmsg),
+
+}/*-------------------------< MSG_PARITY >---------------*/,{
+ /*
+ ** count it
+ */
+ SCR_COPY (1),
+ (ncrcmd) &ncr0.header.status[4],
+ REG (scratcha),
+ SCR_REG_REG (scratcha, SCR_ADD, 0x01),
+ 0,
+ SCR_COPY (1),
+ REG (scratcha),
+ (ncrcmd) &ncr0.header.status[4],
+ /*
+ ** send a "message parity error" message.
+ */
+ SCR_LOAD_REG (scratcha, M_PARITY),
+ 0,
+ SCR_JUMP,
+ PADDR (setmsg),
+}/*-------------------------< MSG_REJECT >---------------*/,{
+ /*
+ ** If a M_X_SDTR message was sent,
+ ** negotiate synchronous mode.
+ */
+ SCR_INT ^ IFTRUE (CARRYSET),
+ 3,
+ /*
+ ** make host log this message
+ */
+ SCR_INT ^ IFFALSE (CARRYSET),
+ 5,
+ SCR_JUMP,
+ PADDR (clrack),
+
+}/*-------------------------< MSG_EXTENDED >-------------*/,{
+ /*
+ ** Terminate cycle
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get length.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ (ncrcmd) &ncr0.msgin[1],
+ /*
+ ** Check for message parity error.
+ */
+ SCR_TO_REG (scratcha),
+ 0,
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDR (msg_parity),
+ SCR_FROM_REG (scratcha),
+ 0,
+ /*
+ */
+ SCR_JUMP ^ IFFALSE (DATA (3)),
+ PADDR (msg_bad),
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get extended message code.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ (ncrcmd) &ncr0.msgin[2],
+ /*
+ ** Check for message parity error.
+ */
+ SCR_TO_REG (scratcha),
+ 0,
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDR (msg_parity),
+ SCR_FROM_REG (scratcha),
+ 0,
+ SCR_JUMP ^ IFTRUE (DATA (M_X_SDTR)),
+ PADDR (msg_sdtr),
+ /*
+ ** unknown extended message
+ */
+ SCR_JUMP,
+ PADDR (msg_bad)
+
+}/*-------------------------< MSG_SDTR >-----------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get period and offset
+ */
+ SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
+ (ncrcmd) &ncr0.msgin[3],
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDR (msg_parity),
+ /*
+ ** let the host do the real work.
+ */
+ SCR_INT ^ IFTRUE (CARRYSET),
+ 3,
+ SCR_INT ^ IFFALSE (CARRYSET),
+ 4,
+ /*
+ ** let the target fetch our answer.
+ */
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+
+/*<<<*/ SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+ 16,
+ SCR_INT,
+ 4,
+ SCR_JUMP,
+ PADDR (dispatch),
+ /*
+ ** Sent the M_X_SDTR
+ */
+/*>>>*/ SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
+ (ncrcmd) &ncr0.msgout,
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+ -16,
+ SCR_COPY (1),
+ REG (sfbr),
+ (ncrcmd) &ncr_msgout,
+ /*
+ ** If rejected, cancel sync transfer.
+ */
+ SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)),
+ PADDR (msg_out_done),
+ SCR_FROM_REG (sbdl),
+ 0,
+ SCR_INT ^ IFTRUE (DATA (M_REJECT)),
+ 4,
+ SCR_JUMP,
+ PADDR (msg_out_done),
+
+}/*-------------------------< COMPLETE >-----------------*/,{
+ /*
+ ** Complete message.
+ **
+ ** When we terminate the cycle by clearing ACK,
+ ** the target may disconnect immediately.
+ **
+ ** We don't want to be told of an
+ ** "unexpected disconnect",
+ ** so we disable this feature.
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ /*
+ ** Terminate cycle ...
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ ** ... and wait for the disconnect.
+ */
+ SCR_WAIT_DISC,
+ 0,
+}/*-------------------------< CLEANUP >-------------------*/,{
+ /*
+ ** dsa: Pointer to ccb
+ ** or xxxxxxFF (no ccb)
+ **
+ ** scr0: Host-Status (<>0!)
+ */
+ SCR_FROM_REG (dsa),
+ 0,
+ SCR_JUMP ^ IFTRUE (DATA (0xff)),
+ PADDR (signal),
+ /*
+ ** dsa is valid.
+ ** save the status registers
+ */
+ SCR_COPY (4),
+ REG (scr0),
+ (ncrcmd) &ncr0.header.status,
+ /*
+ ** and copy back the header to the ccb.
+ */
+ SCR_COPY (4),
+ REG (dsa),
+ PADDR (savepos),
+ SCR_COPY (sizeof (struct head)),
+ (ncrcmd) &ncr0.header,
+}/*-------------------------< SAVEPOS >---------------------*/,{
+ 0,
+
+ /*
+ ** If command resulted in "check condition"
+ ** status and is not yet completed,
+ ** try to get the condition code.
+ */
+ SCR_FROM_REG (scr0),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (0x00, 0xfc)),
+ 16,
+ SCR_FROM_REG (scr1),
+ 0,
+ SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)),
+ PADDR(getcc2),
+ /*
+ ** And make the DSA register invalid.
+ */
+/*>>>*/ SCR_LOAD_REG (dsa, 0xff), /* invalid */
+ 0,
+}/*-------------------------< SIGNAL >----------------------*/,{
+ /*
+ ** if status = queue full,
+ ** reinsert in startqueue and stall queue.
+ */
+ SCR_FROM_REG (scr1),
+ 0,
+ SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)),
+ 8,
+ /*
+ ** if job completed ...
+ */
+ SCR_FROM_REG (scr0),
+ 0,
+ /*
+ ** ... signal completion to the host
+ */
+ SCR_INT_FLY ^ IFFALSE (MASK (0x00, 0xfc)),
+ 0,
+ /*
+ ** Auf zu neuen Schandtaten!
+ */
+ SCR_JUMP,
+ PADDR(start),
+
+}/*-------------------------< SAVE_DP >------------------*/,{
+ /*
+ ** SAVE_DP message:
+ ** Copy TEMP register to SAVEP in header.
+ */
+ SCR_COPY (4),
+ REG (temp),
+ (ncrcmd) &ncr0.header.savep,
+ SCR_JUMP,
+ PADDR (clrack),
+}/*-------------------------< RESTORE_DP >---------------*/,{
+ /*
+ ** RESTORE_DP message:
+ ** Copy SAVEP in header to TEMP register.
+ */
+ SCR_COPY (4),
+ (ncrcmd) &ncr0.header.savep,
+ REG (temp),
+ SCR_JUMP,
+ PADDR (clrack),
+}/*-------------------------< DISCONNECT >---------------*/,{
+ /*
+ ** Disable the "unexpected disconnect" feature.
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ ** Wait for the disconnect.
+ */
+ SCR_WAIT_DISC,
+ 0,
+ /*
+ ** Profiling:
+ ** Set a time stamp,
+ ** and count the disconnects.
+ */
+ SCR_COPY (sizeof (struct timeval)),
+ (ncrcmd) &time,
+ (ncrcmd) &ncr0.header.stamp.disconnect,
+ SCR_COPY (4),
+ (ncrcmd) &ncr0.disc_phys,
+ REG (temp),
+ SCR_REG_REG (temp, SCR_ADD, 0x01),
+ 0,
+ SCR_COPY (4),
+ REG (temp),
+ (ncrcmd) &ncr0.disc_phys,
+ /*
+ ** Status is: DISCONNECTED.
+ */
+ SCR_LOAD_REG (scr0, HS_DISCONNECT),
+ 0,
+ SCR_JUMP,
+ PADDR (cleanup),
+
+}/*-------------------------< MSG_OUT >-------------------*/,{
+ /*
+ ** The target requests a message.
+ ** First remove ATN so the target will
+ ** not continue fetching messages.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+ (ncrcmd) &ncr0.msgout,
+ SCR_COPY (1),
+ REG (sfbr),
+ (ncrcmd) &ncr_msgout,
+ /*
+ ** If it was no ABORT message ...
+ */
+ SCR_JUMP ^ IFTRUE (DATA (M_ABORT)),
+ PADDR (msg_out_abort),
+ /*
+ ** ... wait for the next phase
+ ** if it's a message out, send it again, ...
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+ PADDR (msg_out),
+}/*-------------------------< MSG_OUT_DONE >--------------*/,{
+ /*
+ ** ... else clear the message ...
+ */
+ SCR_LOAD_REG (scratcha, M_NOOP),
+ 0,
+ SCR_COPY (4),
+ REG (scratcha),
+ (ncrcmd) &ncr0.msgout,
+ /*
+ ** ... and process the next phase
+ */
+ SCR_JUMP,
+ PADDR (dispatch),
+}/*-------------------------< MSG_OUT_ABORT >-------------*/,{
+ /*
+ ** After ABORT message,
+ **
+ ** expect an immediate disconnect, ...
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_WAIT_DISC,
+ 0,
+ /*
+ ** ... and set the status to "ABORTED"
+ */
+ SCR_LOAD_REG (scr0, HS_ABORTED),
+ 0,
+ SCR_JUMP,
+ PADDR (cleanup),
+
+}/*-------------------------< GETCC >-----------------------*/,{
+ /*
+ ** The ncr doesn't have an indirect load
+ ** or store command. So we have to
+ ** copy part of the control block to a
+ ** fixed place, where we can modify it.
+ **
+ ** We patch the address part of a COPY command
+ ** with the address of the dsa register ...
+ */
+ SCR_COPY (4),
+ REG (dsa),
+ PADDR (getcc1),
+ /*
+ ** ... then we do the actual copy.
+ */
+ SCR_COPY (sizeof (struct head)),
+}/*-------------------------< GETCC1 >----------------------*/,{
+ 0,
+ (ncrcmd) &ncr0.header,
+ /*
+ ** Initialize the status registers
+ */
+ SCR_COPY (4),
+ (ncrcmd) &ncr0.header.status,
+ REG (scr0),
+}/*-------------------------< GETCC2 >----------------------*/,{
+ /*
+ ** Get the condition code from a target.
+ **
+ ** DSA points to a data structure.
+ ** Set TEMP to the script location
+ ** that receives the condition code.
+ **
+ ** Because there is no script command
+ ** to load a longword into a register,
+ ** we use a CALL command.
+ */
+/*<<<*/ SCR_CALLR,
+ 24,
+ /*
+ ** Get the condition code.
+ */
+ SCR_MOVE_TBL ^ SCR_DATA_IN,
+ offsetof (struct dsb, sense),
+ /*
+ ** No data phase may follow!
+ */
+ SCR_CALL,
+ PADDR (checkatn),
+ SCR_JUMP,
+ PADDR (no_data),
+/*>>>*/
+
+ /*
+ ** The CALL jumps to this point.
+ ** Prepare for a RESTORE_POINTER message.
+ ** Save the TEMP register into the saved pointer.
+ */
+ SCR_COPY (4),
+ REG (temp),
+ (ncrcmd) &ncr0.header.savep,
+ /*
+ ** Load scratcha, because in case of a selection timeout,
+ ** the host will expect a new value for startpos in
+ ** the scratcha register.
+ */
+ SCR_COPY (4),
+ PADDR (startpos),
+ REG (scratcha),
+ /*
+ ** Then try to connect to the target.
+ ** If we are reselected, special treatment
+ ** of the current job is required before
+ ** accepting the reselection.
+ */
+ SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select),
+ PADDR(badgetcc),
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)),
+ 0,
+ SCR_FROM_REG (sdid),
+ 0,
+ SCR_TO_REG (ctest0),
+ 0,
+ /*
+ ** and send the IDENTIFY and a SDTM message.
+ */
+ SCR_MOVE_TBL ^ SCR_MSG_OUT,
+ offsetof (struct dsb, smsg2),
+ SCR_JUMPR ^ IFTRUE ( WHEN (SCR_MSG_OUT) ),
+ -16,
+ SCR_COPY (1),
+ REG (sfbr),
+ (ncrcmd) &ncr_msgout,
+ /*
+ ** Handle synch negotiation.
+ */
+ SCR_SET (SCR_CARRY),
+ 0,
+ SCR_JUMP,
+ PADDR (prepare2),
+
+}/*------------------------< BADGETCC >---------------------*/,{
+ SCR_INT,
+ 2,
+}/*-------------------------< RESELECT >--------------------*/,{
+ /*
+ ** make the DSA invalid.
+ */
+ SCR_LOAD_REG (dsa, 0xff),
+ 0,
+ SCR_CLR (SCR_TRG),
+ 0,
+ /*
+ ** Sleep waiting for a reselection.
+ ** If SIGP is set, special treatment.
+ **
+ ** Zu allem bereit ..
+ */
+ SCR_WAIT_RESEL,
+ PADDR(reselect2),
+ /*
+ ** ... zu nichts zu gebrauchen ?
+ **
+ ** load the target id into the SFBR
+ ** and jump to the control block.
+ **
+ ** Look at the declarations of
+ ** - struct ncb
+ ** - struct tcb
+ ** - struct lcb
+ ** - struct ccb
+ ** to understand what's going on.
+ */
+ SCR_REG_SFBR (ssid, SCR_AND, 0x87),
+ 0,
+ SCR_TO_REG (ctest0),
+ 0,
+ SCR_JUMP,
+ (ncrcmd) &ncr0.jump_tcb,
+}/*-------------------------< RESELECT2 >-------------------*/,{
+ /*
+ ** If it's not connected :(
+ ** -> interrupted by SIGP bit.
+ ** Jump to start.
+ */
+ SCR_FROM_REG (ctest2),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
+ PADDR (start),
+ SCR_JUMP,
+ PADDR (reselect),
+
+}/*-------------------------< RESEL_TMP >-------------------*/,{
+ /*
+ ** The return address in TEMP
+ ** is in fact the data structure address,
+ ** so copy it to the DSA register.
+ */
+ SCR_COPY (4),
+ REG (temp),
+ REG (dsa),
+ SCR_JUMP,
+ PADDR (prepare),
+
+}/*-------------------------< RESEL_LUN >-------------------*/,{
+ /*
+ ** come back to this point
+ ** to get an IDENTIFY message
+ ** Wait for a msg_in phase.
+ */
+/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ 48,
+ /*
+ ** message phase
+ ** It's not a sony, it's a trick:
+ ** read the data without acknowledging it.
+ */
+ SCR_FROM_REG (sbdl),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (M_IDENTIFY, 0x98)),
+ 32,
+ /*
+ ** It WAS an Identify message.
+ ** get it and ack it!
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ (ncrcmd) &ncr0.msgin,
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ ** Mask out the LUN.
+ */
+ SCR_REG_REG (sfbr, SCR_AND, 0x07),
+ 0,
+ SCR_RETURN,
+ 0,
+ /*
+ ** No message phase or no IDENTIFY message:
+ ** return 0.
+ */
+/*>>>*/ SCR_LOAD_SFBR (0),
+ 0,
+ SCR_RETURN,
+ 0,
+
+}/*-------------------------< RESEL_TAG >-------------------*/,{
+ /*
+ ** come back to this point
+ ** to get a SIMPLE_TAG message
+ ** Wait for a MSG_IN phase.
+ */
+/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ 64,
+ /*
+ ** message phase
+ ** It's a trick - read the data
+ ** without acknowledging it.
+ */
+ SCR_FROM_REG (sbdl),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (M_SIMPLE_TAG)),
+ 48,
+ /*
+ ** It WAS a SIMPLE_TAG message.
+ ** get it and ack it!
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ (ncrcmd) &ncr0.msgin,
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ ** Wait for the second byte (the tag)
+ */
+/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ 24,
+ /*
+ ** Get it and ack it!
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ (ncrcmd) &ncr0.msgin,
+ SCR_CLR (SCR_ACK|SCR_CARRY),
+ 0,
+ SCR_RETURN,
+ 0,
+ /*
+ ** No message phase or no SIMPLE_TAG message
+ ** or no second byte: return 0.
+ */
+/*>>>*/ SCR_LOAD_SFBR (0),
+ 0,
+ SCR_SET (SCR_CARRY),
+ 0,
+ SCR_RETURN,
+ 0,
+
+}/*-------------------------< DATA_IN >--------------------*/,{
+/*
+** Because the size depends on the
+** #define MAX_SCATTER parameter,
+** it is filled in at runtime.
+**
+** SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+** PADDR (no_data),
+** SCR_COPY (sizeof (struct timeval)),
+** (ncrcmd) &time,
+** (ncrcmd) &ncr0.header.stamp.data,
+** SCR_MOVE_TBL ^ SCR_DATA_IN,
+** offsetof (struct dsb, data[ 0]),
+**
+** ##===========< i=1; i<MAX_SCATTER >=========
+** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
+** || PADDR (checkatn),
+** || SCR_MOVE_TBL ^ SCR_DATA_IN,
+** || offsetof (struct dsb, data[ i]),
+** ##==========================================
+**
+** SCR_CALL,
+** PADDR (checkatn),
+** SCR_JUMP,
+** PADDR (no_data),
+*/
+0
+}/*-------------------------< DATA_OUT >-------------------*/,{
+/*
+** Because the size depends on the
+** #define MAX_SCATTER parameter,
+** it is filled in at runtime.
+**
+** SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+** PADDR (no_data),
+** SCR_COPY (sizeof (struct timeval)),
+** (ncrcmd) &time,
+** (ncrcmd) &ncr0.header.stamp.data,
+** SCR_MOVE_TBL ^ SCR_DATA_OUT,
+** offsetof (struct dsb, data[ 0]),
+**
+** ##===========< i=1; i<MAX_SCATTER >=========
+** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)),
+** || PADDR (dispatch),
+** || SCR_MOVE_TBL ^ SCR_DATA_OUT,
+** || offsetof (struct dsb, data[ i]),
+** ##==========================================
+**
+** SCR_CALL,
+** PADDR (dispatch),
+** SCR_JUMP,
+** PADDR (no_data),
+**
+**---------------------------------------------------------
+*/
+(u_long)&ident
+
+}/*-------------------------< ABORTTAG >-------------------*/,{
+ /*
+ ** Abort a bad reselection.
+ ** Set the message to ABORT vs. ABORT_TAG
+ */
+ SCR_LOAD_REG (scratcha, M_ABORT_TAG),
+ 0,
+ SCR_JUMPR ^ IFFALSE (CARRYSET),
+ 8,
+}/*-------------------------< ABORT >----------------------*/,{
+ SCR_LOAD_REG (scratcha, M_ABORT),
+ 0,
+ SCR_COPY (1),
+ REG (scratcha),
+ (ncrcmd) &ncr0.msgout,
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ ** and send it.
+ ** we expect an immediate disconnect
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+ (ncrcmd) &ncr0.msgout,
+ SCR_COPY (1),
+ REG (sfbr),
+ (ncrcmd) &ncr_msgout,
+ SCR_WAIT_DISC,
+ 0,
+ SCR_JUMP,
+ PADDR (start),
+}/*--------------------------------------------------------*/
+};
+
+/*==========================================================
+**
+**
+** Fill in #define dependent parts of the script
+**
+**
+**==========================================================
+*/
+
+void ncr_script_fill (struct script * scr)
+{
+ int i;
+ ncrcmd *p;
+
+ p = scr->tryloop;
+ for (i=0; i<MAX_START; i++) {
+ *p++ =SCR_COPY (4);
+ *p++ =(ncrcmd) &ncr0.squeue[i];
+ *p++ =REG (dsa);
+ *p++ =SCR_CALL;
+ *p++ =PADDR (trysel);
+ };
+ *p++ =SCR_JUMP;
+ *p++ =PADDR(tryloop);
+
+ assert ((u_long)p == (u_long)&scr->tryloop + sizeof (scr->tryloop));
+
+ p = scr->data_in;
+
+ *p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN));
+ *p++ =PADDR (no_data);
+ *p++ =SCR_COPY (sizeof (struct timeval));
+ *p++ =(ncrcmd) &time;
+ *p++ =(ncrcmd) &ncr0.header.stamp.data;
+ *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
+ *p++ =offsetof (struct dsb, data[ 0]);
+
+ for (i=1; i<MAX_SCATTER; i++) {
+ *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN));
+ *p++ =PADDR (checkatn);
+ *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
+ *p++ =offsetof (struct dsb, data[i]);
+ };
+
+ *p++ =SCR_CALL;
+ *p++ =PADDR (checkatn);
+ *p++ =SCR_JUMP;
+ *p++ =PADDR (no_data);
+
+ assert ((u_long)p == (u_long)&scr->data_in + sizeof (scr->data_in));
+
+ p = scr->data_out;
+
+ *p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_OUT));
+ *p++ =PADDR (no_data);
+ *p++ =SCR_COPY (sizeof (struct timeval));
+ *p++ =(ncrcmd) &time;
+ *p++ =(ncrcmd) &ncr0.header.stamp.data;
+ *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
+ *p++ =offsetof (struct dsb, data[ 0]);
+
+ for (i=1; i<MAX_SCATTER; i++) {
+ *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT));
+ *p++ =PADDR (dispatch);
+ *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
+ *p++ =offsetof (struct dsb, data[i]);
+ };
+
+ *p++ =SCR_CALL;
+ *p++ =PADDR (dispatch);
+ *p++ =SCR_JUMP;
+ *p++ =PADDR (no_data);
+
+ assert ((u_long)p == (u_long)&scr->data_out + sizeof (scr->data_out));
+}
+
+/*==========================================================
+**
+**
+** Bind the script to its physical address.
+**
+**
+**==========================================================
+*/
+
+static int ncr_unit (ncb_p np)
+{
+ int idx;
+ for (idx=0;idx<NNCR; idx++)
+ if (ncrp[idx]==np)
+ return (idx);
+ return (-1);
+}
+
+static void ncr_script_bind (ncb_p np)
+{
+ ncrcmd *p = (ncrcmd*) np->v_script;
+ ncrcmd opcode, arg, a1;
+ ncrcmd *end, *start;
+
+ start = p;
+ for (end=p+(sizeof(struct script)/4);p<end;) {
+
+ /*
+ ** If we forget to change the length
+ ** in struct script, a field will be
+ ** padded with 0. This is an illegal
+ ** command.
+ */
+
+ if (!(opcode=*p++))
+ printf ("ncr%d: ERROR0 IN SCRIPT at %d.\n",
+ ncr_unit(np), p-start-1);
+ arg=*p;
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_SCRIPT)
+ printf ("%x: <%x>\n",
+ (u_long)(p-1), opcode);
+#endif /* SCSI_NCR_DEBUG */
+ /*
+ ** We don't have to decode ALL commands
+ */
+ switch (opcode >> 28) {
+
+ case 0xc:
+ /*
+ ** COPY has TWO arguments.
+ */
+ a1 = arg;
+ if (a1 >= 256) *p = vtophys (a1);
+ else *p = np->paddr + a1;
+ arg = *++p;
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_SCRIPT)
+ printf ("%x: <%x>\n", (u_long)p, *p);
+#endif /* SCSI_NCR_DEBUG */
+ if ((a1 ^ arg) & 3)
+ printf ("ncr%d: script: ERROR1 IN SCRIPT at %d.\n",
+ ncr_unit (np), p-start-1);
+ /* pass */
+ case 0x0:
+ /*
+ ** MOVE (absolute address)
+ */
+ if (arg >= 256) *p = vtophys (arg);
+ else *p = np->paddr + arg;
+ break;
+ case 0x8:
+ /*
+ ** JUMP / CALL
+ ** dont't relocate if relative :-)
+ */
+ if (opcode & 0x00800000) break;
+ *p = vtophys (arg);
+ break;
+ /*
+ ** don't relocate 0 arguments.
+ */
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ if (arg) *p = vtophys (arg);
+ break;
+ default:
+ break;
+ };
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_SCRIPT)
+ printf ("%x: <%x>\n", (u_long)p, *p);
+#endif /* SCSI_NCR_DEBUG */
+ p++;
+ };
+}
+
+/*==========================================================
+**
+**
+** Copy and rebind a script.
+**
+**
+**==========================================================
+*/
+
+static void ncr_script_copy (ncb_p oldnp, ncb_p np)
+{
+ ncrcmd *src = (ncrcmd*) oldnp->v_script;
+ ncrcmd *end = (ncrcmd*) oldnp->v_script + sizeof (struct script)/4;
+ ncrcmd *dst = (ncrcmd*) np->v_script;
+
+ u_long oldbase = vtophys (oldnp);
+ u_long newbase = vtophys (np);
+
+ ncrcmd opcode, old, new;
+ u_long d;
+
+ printf ("ncr%d: script_copy from ncr%d..\n",
+ ncr_unit (np), ncr_unit (oldnp));
+ while (src < end) {
+
+ *dst++ = opcode = *src++;
+
+ new=old=*src++;
+
+ /*
+ ** We don't have to decode ALL commands
+ */
+ switch (opcode >> 28) {
+
+ case 0xc:
+ /*
+ ** COPY has TWO arguments.
+ */
+
+ d = old - oldnp->paddr;
+ if (d<sizeof (struct ncr_reg))
+ new = np->paddr + d;
+
+ d = old - oldnp->p_script;
+ if (d<sizeof (struct script))
+ new = np->p_script + d;
+
+ d = old - oldbase;
+ if (d<sizeof (struct ncb))
+ new = newbase + d;
+
+ *dst++ = new;
+ new=old=*src++;
+
+ /*
+ ** fall through
+ */
+
+ case 0x0:
+ /*
+ ** MOVE (absolute address)
+ */
+ d = old - oldnp->paddr;
+ if (d<sizeof (struct ncr_reg))
+ new = np->paddr + d;
+
+ d = old - oldnp->p_script;
+ if (d<sizeof (struct script))
+ new = np->p_script + d;
+
+ d = old - oldbase;
+ if (d<sizeof (struct ncb))
+ new = newbase + d;
+
+ break;
+ case 0x8:
+ /*
+ ** JUMP / CALL
+ ** dont't relocate if relative :-)
+ */
+ if (opcode & 0x00800000) break;
+ /*
+ ** fall through
+ */
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ d = old - oldnp->paddr;
+ if (d<sizeof (struct ncr_reg))
+ new = np->paddr + d;
+
+ d = old - oldnp->p_script;
+ if (d<sizeof (struct script))
+ new = np->p_script + d;
+
+ d = old - oldbase;
+ if (d<sizeof (struct ncb))
+ new = newbase + d;
+
+ default:
+ break;
+ };
+ *dst++ = new;
+ };
+ assert ((char*)src == ((char*)oldnp->v_script)+sizeof(struct script));
+ assert ((char*)dst == ((char*) np->v_script)+sizeof(struct script));
+}
+
+/*==========================================================
+**
+**
+** Auto configuration.
+**
+**
+**==========================================================
+*/
+
+/*----------------------------------------------------------
+**
+** Reduce the transfer length to the max value
+** we can transfer safely.
+**
+** Reading a block greater then MAX_SIZE from the
+** raw (character) device exercises a memory leak
+** in the vm subsystem. This is common to ALL devices.
+** We have submitted a description of this bug to
+** <FreeBSD-bugs@freefall.cdrom.com>.
+** It should be fixed in the current release.
+**
+**----------------------------------------------------------
+*/
+
+void ncr_min_phys (struct buf *bp)
+{
+ if (bp->b_bcount > MAX_SIZE) bp->b_bcount = MAX_SIZE;
+}
+
+/*----------------------------------------------------------
+**
+** Maximal number of outstanding requests per target.
+**
+**----------------------------------------------------------
+*/
+
+U_INT32 ncr_info (int unit)
+{
+ return (1); /* may be changed later */
+}
+
+/*----------------------------------------------------------
+**
+** Probe the hostadapter.
+**
+**----------------------------------------------------------
+*/
+
+static int ncb_probe(pcici_t config_id)
+{
+ if (ncr_units >= NNCR) return (-1);
+ return (ncr_units);
+}
+
+/*==========================================================
+**
+**
+** Auto configuration: attach and init a host adapter.
+**
+**
+**==========================================================
+*/
+
+#define MIN_ASYNC_PD 40
+#define MIN_SYNC_PD 20
+
+static int ncr_attach (pcici_t config_id)
+{
+ int retval;
+ ncb_p np = ncrp[ncr_units];
+
+ /*
+ ** allocate structure
+ */
+
+ if (!np) {
+ np = (ncb_p) malloc (sizeof (struct ncb),
+ M_DEVBUF, M_NOWAIT);
+ if (!np) return (0);
+ ncrp[ncr_units]=np;
+ }
+
+ /*
+ ** initialize structure.
+ */
+
+ bzero (np, sizeof (*np));
+
+ /*
+ ** Try to map the controller chip to
+ ** virtual and physical memory.
+ */
+
+ retval = pci_map_mem (config_id, 0x14, &np->vaddr, &np->paddr);
+
+ if (retval) {
+ printf ("ncr%d: pci_map_mem failed.\n",ncr_unit (np));
+ return (retval);
+ };
+
+ /*
+ ** Patch script to physical addresses
+ */
+
+ if (np==&ncr0) {
+ np->v_script = &script0;
+ np->p_script = vtophys (np->v_script);
+ ncr_script_fill (&script0);
+ ncr_script_bind (np);
+ } else {
+ /*
+ ** allocate space for script
+ */
+ np->v_script = (struct script *)
+ malloc (sizeof (struct ncb), M_DEVBUF, M_NOWAIT);
+ if (!np->v_script) return (0);
+ np->p_script = vtophys (np->v_script);
+ ncr_script_copy (&ncr0, np);
+ };
+
+ /*
+ ** init data structure
+ */
+
+ np -> jump_tcb.l_cmd = SCR_JUMP ;
+ np -> jump_tcb.l_paddr = vtophys (&np->v_script->abort);
+
+ /*
+ ** Make the controller's registers available.
+ ** Now the INB INW INL OUTB OUTW OUTL macros
+ ** can be used safely.
+ */
+
+ np->reg = (struct ncr_reg*) np->vaddr;
+
+ /*
+ ** Get SCSI addr of host adapter (set by bios?).
+ */
+
+ np->myaddr = INB(nc_scid) & 0x07;
+ if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
+
+ /*
+ ** Get the value of the chip's clock.
+ ** Find the right value for scntl3.
+ */
+
+ ncr_getclock (np);
+
+ /*
+ ** Reset chip.
+ */
+
+ OUTB (nc_istat, SRST);
+ OUTB (nc_istat, 0 );
+
+ /*
+ ** After SCSI devices have been opened, we cannot
+ ** reset the bus safely, so we do it here.
+ ** Interrupt handler does the real work.
+ */
+
+ OUTB (nc_scntl1, CRST);
+
+ /*
+ ** process the reset exception,
+ ** if interrupts are not enabled yet.
+ */
+ ncr_exception (np);
+
+#ifdef ANCIENT
+ printf ("ncr%d waiting for scsi devices to settle\n",
+ ncr_unit (np));
+ DELAY (1000000);
+#endif
+
+ /*
+ ** Now let the generic SCSI driver
+ ** look for the SCSI devices on the bus ..
+ */
+
+#ifndef ANCIENT
+#ifdef __NetBSD__
+ np->sc_link.adapter_softc = np;
+#else/*__NetBSD__*/
+ np->sc_link.adapter_unit = ncr_units;
+#endif /*__NetBSD__*/
+ np->sc_link.adapter_targ = np->myaddr;
+ np->sc_link.adapter = &ncr_switch;
+ np->sc_link.device = &ncr_dev;
+
+ scsi_attachdevs (&np->sc_link);
+#else /* ANCIENT */
+ scsi_attachdevs (ncr_units, np->myaddr, &ncr_switch);
+#endif /* ANCIENT */
+
+ /*
+ ** start the timeout daemon
+ */
+ ncr_timeout (np);
+ np->lasttime=0;
+
+ /*
+ ** Done.
+ */
+
+ ncr_units++;
+
+ return(1);
+}
+
+/*==========================================================
+**
+**
+** Process pending device interrupts.
+**
+**
+**==========================================================
+*/
+
+static int ncr_intr (int dev)
+{
+ ncb_p np;
+ int n=0;
+
+ /*
+ ** Sanity check
+ */
+
+ if (dev >= ncr_units) return (0);
+
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_TINY) printf ("[");
+#endif /* SCSI_NCR_DEBUG */
+
+ assert (dev<NNCR);
+
+ /*
+ ** Repeat until no outstanding ints
+ */
+
+ np = ncrp[dev];
+ while (INB(nc_istat) & (INTF|SIP|DIP)) {
+ ncr_exception (np);
+ n=1;
+ };
+
+ /*
+ ** Switch timeout function to slow.
+ */
+
+ if (n) np->ticks = 100;
+
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_TINY) printf ("]\n");
+#endif /* SCSI_NCR_DEBUG */
+
+ return (n);
+}
+
+/*==========================================================
+**
+**
+** Start execution of a SCSI command.
+** This is called from the generic SCSI driver.
+**
+**
+**==========================================================
+*/
+
+static INT32 ncr_start (struct scsi_xfer * xp)
+{
+#ifndef ANCIENT
+#ifdef __NetBSD__
+ ncb_p np = xp->sc_link->adapter_softc;
+#else /*__NetBSD__*/
+ ncb_p np = ncrp[xp->sc_link->adapter_unit];
+#endif/*__NetBSD__*/
+#else /* ANCIENT */
+ ncb_p np = ncrp[xp->adapter];
+#endif /* ANCIENT */
+
+ struct scsi_generic * cmd = xp->cmd;
+ ccb_p cp;
+ lcb_p lp;
+ tcb_p tp;
+
+ int i, oldspl, flags = xp->flags;
+ u_char ptr, startcode, idmsg;
+ u_long msglen, msglen2;
+
+ /*---------------------------------------------
+ **
+ ** Reset SCSI bus
+ **
+ ** Interrupt handler does the real work.
+ **
+ **---------------------------------------------
+ */
+
+ if (flags & SCSI_RESET) {
+ OUTB (nc_scntl1, CRST);
+ return(COMPLETE);
+ };
+
+ /*---------------------------------------------
+ **
+ ** Some shortcuts ...
+ **
+ **---------------------------------------------
+ */
+
+ if ((xp->TARGET == np->myaddr ) ||
+ (xp->TARGET >= MAX_TARGET) ||
+ (xp->LUN >= MAX_LUN ) ||
+ (flags & SCSI_DATA_UIO)) {
+ xp->error = XS_DRIVER_STUFFUP;
+ return(HAD_ERROR);
+ };
+
+#ifdef ANCIENT
+ /*---------------------------------------------
+ ** Ancient version of <sys/scsi/sd.c>
+ ** doesn't set the DATA_IN/DATA_OUT bits.
+ ** So we have to fix it ..
+ **---------------------------------------------
+ */
+
+ switch (cmd->opcode) {
+ case 0x1a: /* MODE_SENSE */
+ case 0x25: /* READ_CAPACITY */
+ case 0x28: /* READ_BIG (10) */
+ xp->flags |= SCSI_DATA_IN;
+ break;
+ case 0x2a: /* WRITE_BIG(10) */
+ xp->flags |= SCSI_DATA_OUT;
+ break;
+ };
+#endif /* ANCIENT */
+
+#ifdef SCSI_NCR_DEBUG
+ if(ncr_debug & DEBUG_TINY)
+ printf("ncr%d targ %d: CMD=%x F=%x L=%x ",
+ ncr_unit (np), xp->TARGET, cmd->opcode,
+ xp->flags, xp->datalen);
+#endif /* SCSI_NCR_DEBUG */
+
+ /*--------------------------------------------
+ **
+ ** Sanity checks ...
+ ** copied from Elischer's Adaptec driver.
+ **
+ **--------------------------------------------
+ */
+
+ flags = xp->flags;
+ if (!(flags & INUSE)) {
+ printf("ncr%d: ?INUSE?\n", ncr_unit (np));
+ xp->flags |= INUSE;
+ };
+
+ if(flags & ITSDONE) {
+ printf("ncr%d: ?ITSDONE?\n", ncr_unit (np));
+ xp->flags &= ~ITSDONE;
+ };
+
+ if (xp->bp)
+ flags |= (SCSI_NOSLEEP); /* just to be sure */
+
+ /*---------------------------------------------------
+ **
+ ** Assign a ccb
+ **
+ **----------------------------------------------------
+ */
+
+ if (!(cp=ncr_get_ccb (np, flags, xp->TARGET, xp->LUN))) {
+ printf ("ncr%d: no ccb.\n", ncr_unit (np));
+ xp->error = XS_DRIVER_STUFFUP;
+ return(TRY_AGAIN_LATER);
+ };
+
+ /*---------------------------------------------------
+ **
+ ** timestamp
+ **
+ **----------------------------------------------------
+ */
+
+ bzero (&cp->phys.header.stamp, sizeof (struct tstamp));
+ cp->phys.header.stamp.start = time;
+
+ /*---------------------------------------------------
+ **
+ ** sync negotiation required?
+ **
+ **----------------------------------------------------
+ */
+
+ tp = &np->target[xp->TARGET];
+
+ if ((cmd->opcode!=0x12) && (tp->period)) {
+
+ startcode = HS_BUSY;
+
+ } else if (!(tp->inqdata[7] & INQ7_SYNC)) {
+
+ tp->minsync = 255;
+ tp->maxoffs = 8 ;
+ tp->period =0xffff;
+ startcode = HS_BUSY;
+
+ } else {
+ /*
+ ** minsync unit is 4ns !
+ */
+
+ u_long minsync = tp->usrsync;
+
+ if (minsync < 25) minsync=25;
+
+ /*
+ ** if not scsi 2
+ ** don't believe FAST!
+ */
+
+ if ((minsync < 50) && (tp->inqdata[2] & 0x0f) < 2)
+ minsync=50;
+
+ /*
+ ** our limit ..
+ */
+
+ if (minsync < np->ns_sync)
+ minsync = np->ns_sync;
+
+ /*
+ ** divider limit
+ */
+
+ if (minsync > (np->ns_sync * 11) / 4)
+ minsync = 255;
+
+ tp->minsync = minsync;
+ tp->maxoffs = (minsync<255 ? 8 : 0);
+
+ startcode = HS_NEGOTIATE;
+ };
+
+ /*---------------------------------------------------
+ **
+ ** choose a new tag ...
+ **
+ **----------------------------------------------------
+ */
+
+ if ((lp = tp->lp[xp->LUN]) && (lp->usetags)) {
+ /*
+ ** assign a tag to this ccb!
+ */
+ while (!cp->tag) {
+ ccb_p cp2 = lp->next_ccb;
+ lp->lasttag = lp->lasttag % 255 + 1;
+ while (cp2 && cp2->tag != lp->lasttag)
+ cp2 = cp2->next_ccb;
+ if (cp2) continue;
+ cp->tag=lp->lasttag;
+ printf ("ncr%d targ %d lun %d: using tag #%d.\n",
+ ncr_unit (np), xp->TARGET, xp->LUN, cp->tag);
+ };
+ } else cp->tag=0;
+
+
+ /*----------------------------------------------------
+ **
+ ** Build the identify / tag / sdtr message
+ **
+ **----------------------------------------------------
+ */
+
+ idmsg = (cp==&np->ccb ? 0x80 : 0xc0) | xp->LUN;
+
+ cp -> scsi_smsg [0] = idmsg;
+ msglen=1;
+
+ if (cp->tag) {
+
+ /*
+ ** Ordered write ops, unordered read ops.
+ */
+ switch (cmd->opcode) {
+ case 0x08: /* READ_SMALL (6) */
+ case 0x28: /* READ_BIG (10) */
+ case 0xa8: /* READ_HUGE (12) */
+ cp -> scsi_smsg [msglen] = M_SIMPLE_TAG;
+ break;
+ default:
+ cp -> scsi_smsg [msglen] = M_ORDERED_TAG;
+ }
+
+ /*
+ ** can be overwritten by ncrstat
+ */
+ switch (np->order) {
+
+ case M_SIMPLE_TAG:
+ cp -> scsi_smsg [msglen] = M_SIMPLE_TAG;
+ break;
+
+ case M_ORDERED_TAG:
+ cp -> scsi_smsg [msglen] = M_ORDERED_TAG;
+ break;
+ };
+ msglen++;
+
+ cp -> scsi_smsg [msglen++] = cp -> tag;
+ }
+ if (startcode==HS_NEGOTIATE) {
+ cp -> scsi_smsg [msglen++] = M_EXTENDED;
+ cp -> scsi_smsg [msglen++] = 3;
+ cp -> scsi_smsg [msglen++] = M_X_SDTR;
+ cp -> scsi_smsg [msglen++] = np->target[xp->TARGET].minsync;
+ cp -> scsi_smsg [msglen++] = np->target[xp->TARGET].maxoffs;
+ };
+
+ /*----------------------------------------------------
+ **
+ ** Build the identify / sdtr message for getcc
+ **
+ **----------------------------------------------------
+ */
+
+ cp -> scsi_smsg2 [0] = idmsg;
+ msglen2 = 1;
+ if (np->target[xp->TARGET].inqdata[7]&INQ7_SYNC) {
+ cp -> scsi_smsg2 [1] = M_EXTENDED;
+ cp -> scsi_smsg2 [2] = 3;
+ cp -> scsi_smsg2 [3] = M_X_SDTR;
+ cp -> scsi_smsg2 [4] = np->target[xp->TARGET].minsync;
+ cp -> scsi_smsg2 [5] = np->target[xp->TARGET].maxoffs;
+ msglen2 = 6;
+ };
+
+ /*----------------------------------------------------
+ **
+ ** Build the data descriptors
+ **
+ **----------------------------------------------------
+ */
+
+ if (ncr_scatter (&cp->phys,
+ (vm_offset_t) xp->data,
+ (vm_size_t) xp->datalen)) {
+ xp->error = XS_DRIVER_STUFFUP;
+ ncr_free_ccb(np, cp, flags);
+ return(HAD_ERROR);
+ };
+
+ /*----------------------------------------------------
+ **
+ ** Set the SAVED_POINTER.
+ **
+ **----------------------------------------------------
+ */
+
+ if (flags & SCSI_DATA_IN) {
+ cp->phys.header.savep = vtophys (&np->v_script->data_in);
+ } else if (flags & SCSI_DATA_OUT) {
+ cp->phys.header.savep = vtophys (&np->v_script->data_out);
+ } else {
+ cp->phys.header.savep = vtophys (&np->v_script->no_data);
+ };
+
+ /*----------------------------------------------------
+ **
+ ** fill ccb
+ **
+ **----------------------------------------------------
+ */
+
+ /*
+ ** physical -> virtual translation
+ */
+ cp->phys.header.cp = cp;
+ /*
+ ** Generic SCSI command
+ */
+ cp->xfer = xp;
+ /*
+ ** Startqueue
+ */
+ cp->phys.header.launch.l_paddr = vtophys (&np->v_script->select);
+ cp->phys.header.launch.l_cmd = SCR_JUMP;
+ /*
+ ** select
+ */
+ cp->phys.select.sel_id = xp->TARGET;
+ cp->phys.select.sel_scntl3 = np->rv_scntl3;
+ cp->phys.select.sel_sxfer = np->target[xp->TARGET].sval;
+ /*
+ ** message
+ */
+ cp->phys.smsg.addr = vtophys (&cp->scsi_smsg );
+ cp->phys.smsg.size = msglen;
+ cp->phys.smsg2.addr = vtophys (&cp->scsi_smsg2);
+ cp->phys.smsg2.size = msglen2;
+ /*
+ ** command
+ */
+#ifdef ANCIENT
+ bcopy (cmd, &cp->cmd, sizeof (cp->cmd));
+ cp->phys.cmd.addr = vtophys (&cp->cmd);
+#else /* ANCIENT */
+ cp->phys.cmd.addr = vtophys (cmd);
+#endif /* ANCIENT */
+ cp->phys.cmd.size = xp->cmdlen;
+ /*
+ ** sense data
+ */
+ cp->phys.sense.addr = vtophys (&cp->xfer->sense);
+ cp->phys.sense.size = sizeof(struct scsi_sense_data);
+ /*
+ ** status
+ */
+ cp->scs2_status = S_ILLEGAL;
+ cp->scsi_status = S_ILLEGAL;
+ cp->sync_status = np->target[xp->TARGET].sval;
+ cp->host_status = startcode;
+ cp->parity_errs = 0;
+
+ /*----------------------------------------------------
+ **
+ ** Critical region: starting this job.
+ **
+ **----------------------------------------------------
+ */
+
+ oldspl = 0; /* for the sake of gcc */
+ if (!(flags & SCSI_NOMASK)) oldspl = splbio();
+ np->lock++;
+
+ /*
+ ** reselect pattern and activate this job.
+ */
+
+ cp->jump_ccb.l_cmd = (SCR_JUMP ^ IFFALSE (DATA (cp->tag)));
+ cp->tlimit = time.tv_sec + xp->timeout / 1000 + 2;
+ cp->magic = CCB_MAGIC;
+
+ /*
+ ** insert into startqueue.
+ */
+
+ ptr = np->squeueput + 1;
+ if (ptr >= MAX_START) ptr=0;
+ np->squeue [ptr ] = vtophys(&np->v_script->idle);
+ np->squeue [np->squeueput] = vtophys(&cp->phys);
+ np->squeueput = ptr;
+
+#ifdef SCSI_NCR_DEBUG
+ if(ncr_debug & DEBUG_QUEUE)
+ printf ("ncr%d: queuepos=%d tryoffset=%d.\n", ncr_unit (np),
+ np->squeueput, np->v_script->startpos[0]-(vtophys(&np->v_script->tryloop)));
+#endif /* SCSI_NCR_DEBUG */
+
+ /*
+ ** Script processor may be waiting for reconnect.
+ ** Wake it up.
+ */
+ OUTB (nc_istat, SIGP);
+
+ /*
+ ** If interrupts are enabled, return now.
+ ** Command is successfully queued.
+ */
+
+ np->lock--;
+ if (!(flags & SCSI_NOMASK)) {
+ splx (oldspl);
+ if (np->lasttime) {
+#ifdef SCSI_NCR_DEBUG
+ if(ncr_debug & DEBUG_TINY) printf ("Q");
+#endif /* SCSI_NCR_DEBUG */
+ return(SUCCESSFULLY_QUEUED);
+ };
+ };
+
+ /*----------------------------------------------------
+ **
+ ** Interrupts not yet enabled - have to poll.
+ **
+ **----------------------------------------------------
+ */
+
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_POLL) printf("P");
+#endif /* SCSI_NCR_DEBUG */
+
+ for (i=xp->timeout; i && !(xp->flags & ITSDONE);i--) {
+#ifdef SCSI_NCR_DEBUG
+ if ((ncr_debug & DEBUG_POLL) && (cp->host_status))
+ printf ("%c", (cp->host_status & 0xf) + '0');
+#endif /* SCSI_NCR_DEBUG */
+ DELAY (1000);
+ ncr_exception (np);
+ };
+
+ /*
+ ** Abort if command not done.
+ */
+ if (!(xp->flags & ITSDONE)) {
+ printf ("ncr%d: aborting job ...\n", ncr_unit (np));
+ OUTB (nc_istat, CABRT);
+ DELAY (100000);
+ OUTB (nc_istat, SIGP);
+ ncr_exception (np);
+ };
+
+ if (!(xp->flags & ITSDONE)) {
+ printf ("ncr%d: abortion failed at %x.\n",
+ ncr_unit (np), INL(nc_dsp));
+ ncr_init (np, "timeout", HS_TIMEOUT);
+ };
+
+ if (!(xp->flags & ITSDONE)) {
+ cp-> host_status = HS_SEL_TIMEOUT;
+ ncr_complete (np, cp);
+ };
+
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_RESULT) {
+ printf ("ncr%d: result: %x %x %x.\n",
+ ncr_unit (np), cp->host_status,
+ cp->scsi_status, cp->scs2_status);
+ };
+#endif /* SCSI_NCR_DEBUG */
+ if (!(flags & SCSI_NOMASK))
+ return (SUCCESSFULLY_QUEUED);
+ switch (xp->error) {
+ case 0 : return (COMPLETE);
+ case XS_BUSY: return (TRY_AGAIN_LATER);
+ };
+ return (HAD_ERROR);
+}
+
+/*==========================================================
+**
+**
+** Complete execution of a SCSI command.
+** Signal completion to the generic SCSI driver.
+**
+**
+**==========================================================
+*/
+
+void ncr_complete (ncb_p np, ccb_p cp)
+{
+ struct scsi_xfer * xp;
+ tcb_p tp;
+ lcb_p lp;
+
+ /*
+ ** Sanity check
+ */
+
+ if (!cp || !cp->magic || !cp->xfer) return;
+ cp->magic = 1;
+ cp->tlimit= 0;
+
+ /*
+ ** No Reselect anymore.
+ */
+ cp->jump_ccb.l_cmd = (SCR_JUMP);
+
+ /*
+ ** No starting.
+ */
+ cp->phys.header.launch.l_paddr= vtophys (&np->v_script->idle);
+
+ /*
+ ** timestamp
+ */
+ ncb_profile (np, cp);
+
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_TINY)
+ printf ("CCB=%x STAT=%x/%x/%x\n", (u_long)cp & 0xfff,
+ cp->host_status,cp->scsi_status,cp->scs2_status);
+#endif /* SCSI_NCR_DEBUG */
+
+ xp = cp->xfer;
+ cp->xfer = NULL;
+ tp = &np->target[xp->TARGET];
+ lp = tp->lp[xp->LUN];
+
+ /*
+ ** @PARITY@
+ ** Check for parity errors.
+ */
+
+ if (cp->parity_errs) {
+ printf ("ncr%d targ %d: %d parity error(s), fallback.\n",
+ ncr_unit (np), xp->TARGET, cp->parity_errs);
+ /*
+ ** fallback to asynch transfer.
+ */
+ tp->usrsync=255;
+ tp->period = 0;
+ };
+
+ /*
+ ** Check the status.
+ */
+ if ( (cp->host_status == HS_COMPLETE)
+ && (cp->scsi_status == S_GOOD)) {
+
+ /*
+ ** All went well.
+ */
+ xp->resid = 0;
+
+ /*
+ ** Try to assign a ccb to this nexus
+ */
+ ncr_alloc_ccb (np, xp);
+
+ /*
+ ** On inquire cmd (0x12) save some data.
+ */
+#ifdef ANCIENT
+ if (cp->cmd.opcode == 0x12) {
+#else /* ANCIENT */
+ if (xp->cmd->opcode == 0x12) {
+#endif /* ANCIENT */
+ bcopy ( xp->data,
+ &tp->inqdata,
+ sizeof (tp->inqdata));
+ ncr_setmaxtags (tp, tp->usrtags);
+ tp->period=0;
+ };
+
+ if (!tp->sval) {
+ printf ("ncr%d targ %d: asynchronous.\n",
+ ncr_unit (np), xp->TARGET);
+ tp->sval = 0xe0;
+ };
+
+ /*
+ ** Announce changes to the generic driver
+ */
+ if (lp) {
+ ncr_settags (tp, lp);
+ if (lp->reqlink != lp->actlink)
+ ncr_opennings (np, lp, xp);
+ };
+
+#ifdef DK
+ dk_xfer[DK] ++;
+ dk_wds [DK] += xp->datalen/64;
+ dk_wpms[DK] = 1000000;
+#endif /* DK */
+
+ tp->bytes += xp->datalen;
+ tp->transfers ++;
+
+ } else if (xp->flags & SCSI_ERR_OK) {
+
+ /*
+ ** Not correct, but errors expected.
+ */
+ xp->resid = 0;
+
+ } else if ((cp->host_status == HS_COMPLETE)
+ && (cp->scsi_status == S_CHECK_COND)
+ && (cp->scs2_status == S_GOOD)) {
+
+ /*
+ ** Check condition code
+ */
+ xp->error = XS_SENSE;
+
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & (DEBUG_RESULT|DEBUG_TINY)) {
+ u_char * p = (u_char*) & xp->sense;
+ int i;
+ printf ("\nncr%d: sense data:", ncr_unit (np));
+ for (i=0; i<14; i++) printf (" %x", *p++);
+ printf (".\n");
+ };
+#endif /* SCSI_NCR_DEBUG */
+
+ } else if ((cp->host_status == HS_COMPLETE)
+ && (cp->scsi_status == S_BUSY)) {
+
+ /*
+ ** Target is busy.
+ */
+ xp->error = XS_BUSY;
+
+ } else if ((cp->host_status == HS_SEL_TIMEOUT)
+ || (cp->host_status == HS_TIMEOUT)) {
+
+ /*
+ ** No response
+ */
+ xp->error = XS_TIMEOUT;
+
+ } else {
+
+ /*
+ ** Other protocol messes
+ */
+ printf ("ncr%d targ %d: COMMAND FAILED (%x %x %x) @%x.\n",
+ ncr_unit (np), xp->TARGET,
+ cp->host_status, cp->scsi_status, cp->scs2_status,
+ cp);
+
+ xp->error = XS_DRIVER_STUFFUP;
+ }
+
+ xp->flags |= ITSDONE;
+
+ /*
+ ** Free this ccb
+ */
+ ncr_free_ccb (np, cp, xp->flags);
+
+ /*
+ ** signal completion to generic driver.
+ */
+#ifdef ANCIENT
+ if (xp->when_done)
+ (*(xp->when_done))(xp->done_arg,xp->done_arg2);
+#else /* ANCIENT */
+ scsi_done (xp);
+#endif /* ANCIENT */
+}
+
+/*==========================================================
+**
+**
+** Signal all (or one) control block done.
+**
+**
+**==========================================================
+*/
+
+void ncr_wakeup (ncb_p np, u_long code)
+{
+ /*
+ ** Starting at the default ccb and following
+ ** the links, complete all jobs with a
+ ** host_status greater than "disconnect".
+ **
+ ** If the "code" parameter is not zero,
+ ** complete all jobs that are not IDLE.
+ */
+
+ ccb_p cp = &np->ccb;
+ while (cp) {
+ switch (cp->host_status) {
+
+ case HS_IDLE:
+ break;
+
+ case HS_DISCONNECT:
+#ifdef SCSI_NCR_DEBUG
+ if(ncr_debug & DEBUG_TINY) printf ("D");
+#endif /* SCSI_NCR_DEBUG */
+ /* fall through */
+
+ case HS_BUSY:
+ case HS_NEGOTIATE:
+ if (!code) break;
+ cp->host_status = code;
+
+ /* fall through */
+
+ default:
+ ncr_complete (np, cp);
+ break;
+ };
+ cp = cp -> link_ccb;
+ };
+}
+
+/*==========================================================
+**
+**
+** Start NCR chip.
+**
+**
+**==========================================================
+*/
+
+void ncr_init (ncb_p np, char * msg, u_long code)
+{
+ int i;
+ u_long usrsync;
+
+ /*
+ ** Reset chip.
+ */
+
+ OUTB (nc_istat, SRST );
+
+ /*
+ ** Message.
+ */
+
+ if (msg) printf ("ncr%d: restart (%s).\n", ncr_unit (np), msg);
+
+ /*
+ ** Clear Start Queue
+ */
+
+ for (i=0;i<MAX_START;i++)
+ np -> squeue [i] = vtophys (&np->v_script->idle);
+
+ /*
+ ** Start at first entry.
+ */
+
+ np->squeueput = 0;
+ np->v_script->startpos[0] = vtophys (&np->v_script->tryloop);
+ np->v_script->start [0] = SCR_INT ^ IFFALSE (0);
+
+ /*
+ ** Wakeup all pending jobs.
+ */
+
+ ncr_wakeup (np, code);
+
+ /*
+ ** Init chip.
+ */
+
+ OUTB (nc_istat, 0 ); /* Remove Reset, abort ... */
+ OUTB (nc_scntl0, 0xca ); /* full arb., ena parity, par->ATN */
+ OUTB (nc_scntl1, 0x00 ); /* odd parity, and remove CRST!! */
+ OUTB (nc_scntl3, np->rv_scntl3);/* timing prescaler */
+ OUTB (nc_scid , 0x40|np->myaddr); /* host adapter SCSI address */
+ OUTB (nc_respid, 1<<np->myaddr);/* id to respond to */
+ OUTB (nc_istat , SIGP ); /* Signal Process */
+ OUTB (nc_dmode , 0xc ); /* Burst length = 16 transfer */
+ OUTB (nc_dcntl , NOCOM ); /* no single step mode, protect SFBR*/
+ OUTB (nc_ctest4, 0x08 ); /* enable master parity checking */
+ OUTB (nc_stest3, TE ); /* TolerANT enable */
+ OUTB (nc_stime0, 0xfb ); /* HTH = 1.6sec STO = 0.1 sec. */
+
+ /*
+ ** Reinitialize usrsync.
+ ** Have to renegotiate synch mode.
+ */
+
+ usrsync = 255;
+ if (SCSI_NCR_MAX_SYNC) {
+ u_long period;
+ period =1000000/SCSI_NCR_MAX_SYNC; /* ns = 10e6 / kHz */
+ if (period <= 11 * np->ns_sync) {
+ if (period < 4 * np->ns_sync)
+ usrsync = np->ns_sync;
+ else
+ usrsync = period / 4;
+ };
+ };
+
+ for (i=0;i<MAX_TARGET;i++) {
+ tcb_p tp = &np->target[i];
+ tp->period = 0;
+ tp->sval = 0;
+ tp->usrsync = usrsync;
+ }
+
+ /*
+ ** enable ints
+ */
+
+ OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST);
+ OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID);
+
+ /*
+ ** Start script processor.
+ */
+
+ OUTL (nc_dsp, vtophys (&np->v_script->start));
+}
+
+/*==========================================================
+**
+** Switch sync mode for current job and it's target
+** @CHECKOUT@ xxxcp only for assert - may delete it.
+**
+**==========================================================
+*/
+
+static void ncr_setsync (ncb_p np, ccb_p xxxcp, u_char sxfer)
+{
+ u_short target = INB (nc_ctest0)&7;
+ u_short period;
+ tcb_p tp;
+ ccb_p cp;
+
+ assert (xxxcp);
+ if (!xxxcp) return;
+ assert (xxxcp->xfer);
+ if (!xxxcp->xfer) return;
+ assert (target==xxxcp->xfer->TARGET & 7);
+
+ tp = &np->target[target];
+ tp->period= sxfer&0xf ? ((sxfer>>5)+4) * np->ns_sync : 0xffff;
+ if (tp->sval == sxfer) return;
+ tp->sval = sxfer;
+
+ /*
+ ** Bells and whistles ;-)
+ */
+ if (sxfer & 0x0f) {
+ period = np->ns_sync * ((sxfer>>5)+4);
+ printf ("ncr%d targ %d: %s%dns (%d Mb/sec) offset %d.\n",
+ ncr_unit (np), target, period<200 ? "FAST SCSI-2 ":"",
+ period, (1000+period/2)/period, sxfer & 0x0f);
+ } else{
+ printf ("ncr%d targ %d: asynchronous.\n",
+ ncr_unit (np), target);
+ }
+
+ /*
+ ** set actual value and sync_status
+ */
+ OUTB (nc_scr3 , sxfer);
+ OUTB (nc_sxfer, sxfer);
+
+ /*
+ ** patch ALL ccbs of this target.
+ */
+ for (cp = &np->ccb; cp; cp = cp -> link_ccb) {
+ if (!cp->xfer) continue;
+ if (cp->xfer->TARGET != target) continue;
+ cp->sync_status = sxfer;
+ };
+}
+
+/*==========================================================
+**
+** Switch tagged mode for a target.
+**
+**==========================================================
+*/
+
+static void ncr_setmaxtags (tcb_p tp, u_long usrtags)
+{
+ int l;
+ tp->usrtags = usrtags;
+ for (l=0; l<MAX_LUN; l++) {
+ lcb_p lp;
+ if (!tp) break;
+ lp=tp->lp[l];
+ if (!lp) continue;
+ ncr_settags (tp, lp);
+ };
+}
+
+static void ncr_settags (tcb_p tp, lcb_p lp)
+{
+ u_char reqtags, tmp;
+
+ if ((!tp) || (!lp)) return;
+
+ /*
+ ** only devices capable of tagges commands
+ ** only disk devices
+ ** only if enabled by user ..
+ */
+ if (( tp->inqdata[7] & INQ7_QUEUE) && ((tp->inqdata[0] & 0x1f)==0x00)
+ && tp->usrtags) {
+ reqtags = tp->usrtags;
+ if (lp->actlink <= 1)
+ lp->usetags=reqtags;
+ } else {
+ reqtags = 1;
+ if (lp->actlink <= 1)
+ lp->usetags=0;
+ };
+
+ /*
+ ** don't announce more than available.
+ */
+ tmp = lp->actccbs;
+ if (tmp > reqtags) tmp = reqtags;
+ lp->reqlink = tmp;
+
+ /*
+ ** don't discard if announced.
+ */
+ tmp = lp->actlink;
+ if (tmp < reqtags) tmp = reqtags;
+ lp->reqccbs = tmp;
+}
+
+/*----------------------------------------------------
+**
+** handle user commands
+**
+**----------------------------------------------------
+*/
+
+static void ncr_usercmd (ncb_p np)
+{
+ u_char t;
+ tcb_p tp;
+
+ switch (np->user.cmd) {
+
+ case 0: return;
+
+ case UC_SETSYNC:
+ for (t=0; t<MAX_TARGET; t++) {
+ if (!((np->user.target>>t)&1)) continue;
+ tp = &np->target[t];
+ tp->usrsync = np->user.data;
+ tp->period = 0;
+ };
+ break;
+
+ case UC_SETTAGS:
+ if (np->user.data > SCSI_NCR_MAX_TAGS)
+ break;
+ for (t=0; t<MAX_TARGET; t++) {
+ if (!((np->user.target>>t)&1)) continue;
+ ncr_setmaxtags (&np->target[t], np->user.data);
+ };
+ break;
+
+ case UC_SETDEBUG:
+ ncr_debug = np->user.data;
+ break;
+
+ case UC_SETORDER:
+ np->order = np->user.data;
+ break;
+
+ }
+ np->user.cmd=0;
+}
+
+/*==========================================================
+**
+**
+** ncr timeout handler.
+**
+**
+**==========================================================
+**
+** Misused to keep the driver running when
+** interrupts are not configured correctly.
+**
+**----------------------------------------------------------
+*/
+
+static void ncr_timeout (ncb_p np)
+{
+ u_long thistime = time.tv_sec;
+ u_long step = np->ticks;
+ u_long count = 0;
+ long signed t;
+ ccb_p cp;
+
+ if (np->lasttime != thistime) {
+ np->lasttime = thistime;
+
+ ncr_usercmd (np);
+
+ /*----------------------------------------------------
+ **
+ ** handle ncr chip timeouts
+ **
+ ** Assumption:
+ ** We have a chance to arbitrate for the
+ ** SCSI bus at least every 10 seconds.
+ **
+ **----------------------------------------------------
+ */
+
+ t = thistime - np->heartbeat;
+
+ if (t<2) np->latetime=0; else np->latetime++;
+
+ if (np->latetime>2) {
+ /*
+ ** If there are no requests, the script
+ ** processor will sleep on SEL_WAIT_RESEL.
+ ** But we have to check whether it died.
+ ** Let's wake it up.
+ */
+ OUTB (nc_istat, SIGP);
+ };
+
+ if (np->latetime>10) {
+ /*
+ ** Although we tried to wakeup it,
+ ** the script processor didn't answer.
+ **
+ ** May be a target is hanging,
+ ** or another initator lets a tape device
+ ** rewind with disconnect disabled :-(
+ **
+ ** We won't accept that.
+ */
+ printf ("ncr%d: reset by timeout.\n", ncr_unit (np));
+ OUTB (nc_istat, SRST);
+ OUTB (nc_istat, 0);
+ if (INB (nc_sbcl) & CBSY)
+ OUTB (nc_scntl1, CRST);
+ ncr_init (np, NULL, HS_TIMEOUT);
+ np->heartbeat = thistime;
+ };
+
+ /*----------------------------------------------------
+ **
+ ** handle ccb timeouts
+ **
+ **----------------------------------------------------
+ */
+
+ for (cp=&np->ccb; cp; cp=cp->link_ccb) {
+ /*
+ ** look for timed out ccbs.
+ */
+ if (!cp->host_status) continue;
+ count++;
+ if (cp->tlimit > thistime) continue;
+
+ /*
+ ** Disable reselect.
+ ** Remove it from startqueue.
+ */
+ cp->jump_ccb.l_cmd = (SCR_JUMP);
+ if (cp->phys.header.launch.l_paddr ==
+ vtophys (&np->v_script->select)) {
+ printf ("ncr%d: timeout ccb=%x (skip)\n",
+ ncr_unit (np), cp);
+ cp->phys.header.launch.l_paddr
+ = vtophys (&np->v_script->skip);
+ };
+
+ switch (cp->host_status) {
+
+ case HS_BUSY:
+ case HS_NEGOTIATE:
+ /*
+ ** still in start queue ?
+ */
+ if (cp->phys.header.launch.l_paddr ==
+ vtophys (&np->v_script->skip))
+ continue;
+
+ /* fall through */
+ case HS_DISCONNECT:
+ cp->host_status=HS_TIMEOUT;
+ };
+ cp->tag = 0;
+
+ /*
+ ** wakeup this ccb.
+ */
+ {
+ int oldspl = splbio();
+ ncr_complete (np, cp);
+ splx (oldspl);
+ };
+ };
+ }
+
+ timeout (TIMEOUT ncr_timeout, (caddr_t) np, step ? step : 1);
+
+ if ((INB(nc_istat) & (INTF|SIP|DIP)) && !np->lock) {
+
+ /*
+ ** Process pending interrupts.
+ */
+
+ int oldspl = splbio ();
+ u_long imask = getirr();
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_TINY) printf ("{");
+#endif /* SCSI_NCR_DEBUG */
+ ncr_exception (np);
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_TINY) printf ("}");
+#endif /* SCSI_NCR_DEBUG */
+ imask &=~getirr();
+ splx (oldspl);
+
+ /*
+ ** automagically find int vector.
+ */
+ if (imask) {
+ if ((imask != np->imask) && (np->mcount<100))
+ np->mcount=0;
+ np->imask = imask;
+ np->mcount ++;
+ };
+
+ /*
+ ** a hint to the user :-)
+ */
+ if (np->mcount==100) {
+ if (np->imask & (np->imask-1)) {
+ printf ("ncr%d: please configure intr mask %x.\n",
+ ncr_unit (np), np->imask);
+ } else {
+#ifdef __NetBSD__
+ if (!np->ihand.ih_fun) {
+ np->ihand.ih_fun = ncr_intr;
+ np->ihand.ih_arg = (void*)ncr_unit (np);
+ np->ihand.ih_level = IPL_BIO;
+ intr_establish(ffs (np->imask)-1,
+ &np->ihand);
+ }
+#else
+ printf ("ncr%d: please configure intr %d.\n",
+ ncr_unit (np), ffs (np->imask)-1);
+#endif
+ };
+ np->mcount++;
+ };
+ };
+}
+
+/*==========================================================
+**
+**
+** ncr chip exception handler.
+**
+**
+**==========================================================
+**
+** @RECOVER@ this function is not yet complete.
+**
+** there should be better ways to handle
+** unexpected exceptions than to restart the
+** script processor.
+**
+**----------------------------------------------------------
+*/
+
+void ncr_exception (ncb_p np)
+{
+ u_char istat, dstat;
+ u_short sist;
+ u_long dsp;
+
+ /*
+ ** interrupt on the fly ?
+ */
+ while ((istat = INB (nc_istat)) & INTF) {
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_TINY) printf ("F");
+#endif /* SCSI_NCR_DEBUG */
+ OUTB (nc_istat, INTF);
+ np->profile.num_fly++;
+ ncr_wakeup (np, 0);
+ };
+
+ if (!(istat & (SIP|DIP))) return;
+ dstat = INB (nc_dstat);
+ sist = INW (nc_sist) ;
+ np->profile.num_int++;
+
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_TINY)
+ printf ("<%d|%x:%x|%x:%x>",
+ INB(nc_scr0),
+ dstat,sist,
+ INL(nc_dsp),INL(nc_dbc));
+#endif /* SCSI_NCR_DEBUG */
+
+/*==========================================================
+**
+** First the normal cases.
+**
+**==========================================================
+*/
+ /*-------------------------------------------
+ ** SCSI reset
+ **-------------------------------------------
+ */
+
+ if (sist & RST) {
+ ncr_init (np, "scsi reset", HS_RESET);
+ return;
+ };
+
+ /*-------------------------------------------
+ ** selection timeout
+ **
+ ** IID excluded from dstat mask!
+ ** (chip bug)
+ **-------------------------------------------
+ */
+
+ if ((sist & STO) &&
+ !(sist & (GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
+ !(dstat & (MDPE|BF|ABRT|SIR))) {
+ ncr_int_sto (np);
+ return;
+ };
+
+ /*-------------------------------------------
+ ** Phase mismatch.
+ **-------------------------------------------
+ */
+
+ if ((sist & MA) &&
+ !(sist & (STO|GEN|HTH|SGE|UDC|RST|PAR)) &&
+ !(dstat & (MDPE|BF|ABRT|SIR|IID))) {
+ ncr_int_ma (np);
+ return;
+ };
+
+ /*-------------------------------------------
+ ** Programmed interrupt
+ **-------------------------------------------
+ */
+
+ if ((dstat & SIR) &&
+ !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
+ !(dstat & (MDPE|BF|ABRT|IID)) &&
+ (INB(nc_dsps) <= 8)) {
+ ncr_int_sir (np);
+ return;
+ };
+
+ /*========================================
+ ** do the register dump
+ **========================================
+ */
+
+#ifdef SCSI_NCR_DEBUG
+ if (!(ncr_debug & DEBUG_NODUMP)) /* @DEBUG@ */
+#endif
+ if (time.tv_sec - np->regtime.tv_sec>10) {
+ int i;
+ np->regtime = time;
+ for (i=0; i<sizeof(np->regdump); i++)
+ ((char*)&np->regdump)[i] = ((char*)np->reg)[i];
+ np->regdump.nc_dstat = dstat;
+ np->regdump.nc_sist = sist;
+ };
+
+ printf ("ncr%d targ %d?: ERROR (%x:%x:%x) (%x/%x) @ (%x:%x).\n",
+ ncr_unit (np), INB (nc_ctest0)&7, dstat, sist,
+ INB (nc_sbcl),
+ INB (nc_sxfer),INB (nc_scr3),
+ dsp = INL (nc_dsp), INL (nc_dbc));
+
+ /*----------------------------------------
+ ** clean up the dma fifo
+ **----------------------------------------
+ */
+
+ if ((INB(nc_sstat0)&(ILF|ORF|OLF)) ||
+ (INB(nc_sstat1)&0xf0) || !(dstat & DFE)) {
+ printf ("ncr%d: have to clear fifos.\n", ncr_unit (np));
+ OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */
+ OUTB (nc_ctest3, CLF); /* clear dma fifo */
+ }
+
+ /*----------------------------------------
+ ** unexpected disconnect
+ **----------------------------------------
+ */
+
+ if ((sist & UDC) &&
+ !(sist & (STO|GEN|HTH|MA|SGE|RST|PAR)) &&
+ !(dstat & (MDPE|BF|ABRT|SIR|IID))) {
+ OUTB (nc_scr0, HS_UNEXPECTED);
+ OUTL (nc_dsp, vtophys(&np->v_script->cleanup));
+ return;
+ };
+
+ /*----------------------------------------
+ ** cannot disconnect
+ **----------------------------------------
+ */
+
+ if ((dstat & IID) &&
+ !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
+ !(dstat & (MDPE|BF|ABRT|SIR)) &&
+ ((INL(nc_dbc) & 0xf8000000) == 0x18000000)) {
+ /*
+ ** Data cycles while waiting for disconnect.
+ ** Force disconnect.
+ */
+ OUTB (nc_scntl1, 0);
+ /*
+ ** System may hang, but timeout will handle that.
+ ** In fact, timeout can handle ALL problems :-)
+ */
+ OUTB (nc_dcntl, (STD|NOCOM));
+ return;
+ };
+
+ /*----------------------------------------
+ ** single step
+ **----------------------------------------
+ */
+
+ if ((dstat & SSI) &&
+ !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
+ !(dstat & (MDPE|BF|ABRT|SIR|IID))) {
+ OUTB (nc_dcntl, (STD|NOCOM));
+ return;
+ };
+
+/*
+** @RECOVER@ HTH, SGE, ABRT.
+**
+** We should try to recover from these interrupts.
+** They may occur if there are problems with synch transfers,
+** or if targets are powerswitched while the driver is running.
+*/
+
+ if (sist & SGE) {
+ OUTB (nc_ctest3, CLF); /* clear scsi offsets */
+ }
+
+#ifdef SCSI_NCR_DEBUG
+ /*
+ ** Freeze controller to be able to read the messages.
+ */
+
+ if (ncr_debug & DEBUG_FREEZE) {
+ int i;
+ unsigned char val;
+ for (i=0; i<0x60; i++) {
+ switch (i%16) {
+
+ case 0:
+ printf ("ncr%d: reg[%d0]: ",
+ ncr_unit(np),i/16);
+ break;
+ case 4:
+ case 8:
+ case 12:
+ printf (" ");
+ break;
+ };
+ val = ((unsigned char*) np->vaddr) [i];
+ printf (" %x%x", val/16, val%16);
+ if (i%16==15) printf (".\n");
+ };
+
+ untimeout (TIMEOUT ncr_timeout, (caddr_t) np);
+
+ printf ("ncr%d: halted!\n", ncr_unit(np));
+ /*
+ ** don't restart controller ...
+ */
+ OUTB (nc_istat, SRST);
+ return;
+ };
+#endif /* SCSI_NCR_DEBUG */
+
+ /*
+ ** sorry, have to kill ALL jobs ...
+ */
+
+ ncr_init (np, "fatal error", HS_FAIL);
+}
+
+/*==========================================================
+**
+** ncr chip exception handler for selection timeout
+**
+**==========================================================
+**
+** There seems to be a bug in the 53c810.
+** Although a STO-Interupt is pending,
+** it continues executing script commands.
+** But it will fail and interrupt (IID) on
+** the next instruction where it's looking
+** for a valid phase.
+**
+**----------------------------------------------------------
+*/
+
+void ncr_int_sto (ncb_p np)
+{
+ u_long dsa, scratcha, diff;
+ ccb_p cp;
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_TINY) printf ("T");
+#endif /* SCSI_NCR_DEBUG */
+
+ /*
+ ** look for ccb and set the status.
+ */
+
+ dsa= INL (nc_dsa);
+ cp = &np->ccb;
+ while (cp && (vtophys(&cp->phys)!=dsa)) cp = cp -> link_ccb;
+
+ if (cp) {
+ cp-> host_status = HS_SEL_TIMEOUT;
+ ncr_complete (np, cp);
+ };
+
+ /*
+ ** repair start queue
+ */
+
+ scratcha = INL (nc_scratcha);
+ diff = scratcha-(np->p_script + offsetof (struct script, tryloop));
+
+ assert ((diff <= MAX_START * 20) && !(diff % 20));
+
+ if ((diff <= MAX_START * 20) && !(diff % 20)) {
+ np->v_script->startpos[0] = scratcha;
+ OUTL (nc_dsp, vtophys (&np->v_script->start));
+ return;
+ };
+ ncr_init (np, "selection timeout", HS_FAIL);
+}
+
+/*==========================================================
+**
+**
+** ncr chip exception handler for phase errors.
+**
+**
+**==========================================================
+**
+** We have to construct a new transfer descriptor,
+** to transfer the rest of the current block.
+**
+**----------------------------------------------------------
+*/
+
+static void ncr_int_ma (ncb_p np)
+{
+ u_long dbc;
+ u_long rest;
+ u_long dsa;
+ u_long dsp;
+ u_long nxtdsp;
+ u_long *vdsp;
+ u_long oadr;
+ u_long olen;
+ u_long *tblp;
+ u_long *newcmd;
+ u_char cmd;
+ u_char sbcl;
+ u_char delta;
+ u_char ss0;
+ ccb_p cp,cp2;
+
+ dsp = INL (nc_dsp);
+ dsa = INL (nc_dsa);
+ dbc = INL (nc_dbc);
+ ss0 = INB (nc_sstat0);
+ sbcl= INB (nc_sbcl);
+
+ cmd = dbc >> 24;
+ rest= dbc & 0xffffff;
+ delta=(INB (nc_dfifo) - rest) & 0x7f;
+
+ /*
+ ** The data in the dma fifo has not been transfered to
+ ** the target -> add the amount to the rest ..
+ */
+
+ if (! (INB(nc_dstat) & DFE)) rest += delta;
+ if (ss0 & OLF) rest++;
+ if (ss0 & ORF) rest++;
+
+ /*
+ ** and clear it.
+ */
+
+ OUTB (nc_ctest3, CLF ); /* clear dma fifo */
+ OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */
+
+ cp2 = np->header.cp;
+
+ cp = &np->ccb;
+ while (cp && (vtophys(&cp->phys)!=dsa)) cp = cp -> link_ccb;
+
+ assert (cp == cp2);
+
+ if (dsp == vtophys (&cp->patch[2])) {
+ vdsp = &cp->patch[0];
+ nxtdsp = vdsp[3];
+ } else if (dsp == vtophys (&cp->patch[6])) {
+ vdsp = &cp->patch[4];
+ nxtdsp = vdsp[3];
+ } else {
+ vdsp = (u_long*) ((char*)np->v_script + dsp - np->p_script -8);
+ nxtdsp = dsp;
+ };
+
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & (DEBUG_TINY|DEBUG_PHASE)) {
+ printf ("P%d%d ",cmd&7, sbcl&7);
+ printf ("RL=%d D=%d SS0=%x ",rest,delta,ss0);
+ };
+ if (ncr_debug & DEBUG_PHASE) {
+ printf ("\nCP=%x CP2=%x DSP=%x NXT=%x VDSP=%x CMD=%x ",
+ cp, cp2, dsp, nxtdsp, vdsp, cmd);
+ };
+#endif /* SCSI_NCR_DEBUG */
+
+ oadr = vdsp[1];
+
+ if (cmd & 0x10) { /* Table indirect */
+ tblp = (u_long*) ((char*) &cp->phys + oadr);
+ olen = tblp[0];
+ oadr = tblp[1];
+ } else {
+ tblp = (u_long*) 0;
+ olen = vdsp[0] & 0xffffff;
+ };
+
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_PHASE) {
+ printf ("OCMD=%x\nTBLP=%x OLEN=%x OADR=%x\n",
+ vdsp[0] >> 24, tblp, olen, oadr);
+ };
+#endif /* SCSI_NCR_DEBUG */
+ assert (cmd == (vdsp[0] >> 24));
+
+ if (cmd & 0x06) {
+
+ printf ("ncr%d: targ %d: phase change %d-%d %d@%x resid=%d.\n",
+ ncr_unit (np), INB(nc_ctest0) & 7,
+ cmd&7, sbcl&7, olen, oadr, rest);
+
+ OUTB (nc_dcntl, (STD|NOCOM));
+ return;
+ };
+
+ newcmd = cp->patch;
+ if (cp->phys.header.savep == vtophys (newcmd)) newcmd+=4;
+
+ newcmd[0] = ((cmd & 0x0f) << 24) | rest;
+ newcmd[1] = oadr + olen - rest;
+ newcmd[2] = SCR_JUMP;
+ newcmd[3] = nxtdsp;
+
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_PHASE)
+ printf ("ncr%d targ %d: newcmd[%d] %x %x %x %x.\n",
+ ncr_unit (np), INB(nc_ctest0) & 7,
+ newcmd - cp->patch,
+ newcmd[0], newcmd[1], newcmd[2], newcmd[3]);
+#endif /* SCSI_NCR_DEBUG */
+ np->profile.num_break++;
+ OUTL (nc_temp, vtophys (newcmd));
+ OUTL (nc_dsp, vtophys (&np->v_script->dispatch));
+}
+
+/*==========================================================
+**
+**
+** ncr chip exception handler for programmed interrupts.
+**
+**
+**==========================================================
+*/
+
+static void ncr_show_msg (u_char * msg)
+{
+ u_char i;
+ printf ("%x",*msg);
+ if (*msg==M_EXTENDED) {
+ for (i=1;i<8;i++) {
+ if (i-1>msg[1]) break;
+ printf ("-%x",msg[i]);
+ };
+ } else if ((*msg & 0xf0) == 0x20) {
+ printf ("-%x",msg[1]);
+ }
+}
+
+void ncr_int_sir (ncb_p np)
+{
+ u_char chg, ofs, per, fak;
+ u_char num = INB (nc_dsps);
+ ccb_p cp;
+ tcb_p tp;
+ u_long dsa;
+ u_char target = INB (nc_ctest0) & 7;
+ int i;
+#ifdef SCSI_NCR_DEBUG
+ if(ncr_debug & DEBUG_TINY) printf ("I#%d", num);
+#endif /* SCSI_NCR_DEBUG */
+
+ switch (num) {
+
+/*--------------------------------------------------------------------
+**
+** Processing of interrupted getcc selects
+**
+**--------------------------------------------------------------------
+*/
+
+ case 1: /*
+ ** Script processor is idle.
+ ** Look for interrupted "check cond"
+ */
+
+ printf ("ncr%d: int#%d",ncr_unit (np),num);
+ cp = (ccb_p) 0;
+ for (i=0; i<MAX_TARGET; i++) {
+ printf (" t%d", i);
+ tp = &np->target[i];
+ printf ("+");
+ cp = tp->hold_cp;
+ if (!cp) continue;
+ printf ("+");
+ if ((cp->host_status==HS_BUSY) &&
+ (cp->scsi_status==S_CHECK_COND) &&
+ (cp->scs2_status==S_ILLEGAL))
+ break;
+ printf ("- (remove)");
+ tp->hold_cp = cp = (ccb_p) 0;
+ };
+
+ if (cp) {
+ printf ("+ restart job ..\n");
+ OUTL (nc_dsa, vtophys (&cp->phys));
+ OUTL (nc_dsp, vtophys (&np->v_script->getcc));
+ return;
+ };
+
+ /*
+ ** no job, resume normal processing
+ */
+ printf (" -- remove trap\n");
+ np->v_script->start[0] = SCR_INT ^ IFFALSE (0);
+ break;
+
+ case 2: /*
+ ** While trying to reselect for
+ ** getting the condition code,
+ ** a target reselected us.
+ */
+
+ dsa= INL (nc_dsa);
+ printf ("ncr%d targ %d: in getcc reselect by t%d (dsa=%x).\n",
+ ncr_unit (np), target, INB(nc_ssid)&7, dsa);
+
+ /*
+ ** lookup the ccb
+ */
+ cp = &np->ccb;
+ while (cp && (vtophys(&cp->phys)!=dsa))
+ cp = cp -> link_ccb;
+ assert (cp==np->header.cp);
+
+ /*
+ ** can't happen
+ */
+
+ assert (cp);
+ if (!cp) break;
+
+ /*
+ ** Mark this job
+ */
+ cp->host_status = HS_BUSY;
+ cp->scsi_status = S_CHECK_COND;
+ cp->scs2_status = S_ILLEGAL;
+ np->target[target].hold_cp = cp;
+
+ /*
+ ** And patch code to restart it.
+ */
+ np->v_script->start[0] = SCR_INT;
+ break;
+
+/*--------------------------------------------------------------------
+**
+** Negotiation of synch mode.
+**
+** Possible cases: int msg_in[0] sxfer send goto
+** We try try to negotiate:
+** -> target doesnt't msgin (3) noop ASYNC - -
+** -> target rejected our msg (3) reject ASYNC - -
+** -> target answered (ok) (3) sdtr set - clrack
+** -> target answered (!ok) (3) sdtr ASYNC REJ--->msg_bad
+** -> any other msgin -
+**
+** Target tries to negotiate:
+** -> incoming message (4) sdtr set SDTR -
+** We sent our answer:
+** -> target doesn't msgout (4) reject* ASYNC - -
+** -> target rejected our msg (4) reject ASYNC - -
+** -> target negotiates again (4) sdtr set SDTR -
+**
+**--------------------------------------------------------------------
+*/
+ case 3:
+ case 4:
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_SDTR) {
+ printf ("ncr%d targ %d: sync msg in: ",
+ ncr_unit (np), target);
+ ncr_show_msg (np->msgin);
+ printf (".\n");
+ };
+#endif /* SCSI_NCR_DEBUG */
+ /*
+ ** @CHECKOUT@
+ */
+ dsa= INL (nc_dsa);
+ cp = &np->ccb;
+ while (cp && (vtophys(&cp->phys)!=dsa)) cp = cp -> link_ccb;
+ assert (cp==np->header.cp);
+ tp = &np->target[target];
+
+ /*
+ ** any error in negotiation:
+ ** fall back to asynch.
+ */
+
+ if ((np->msgin[0]!=M_EXTENDED) ||
+ (np->msgin[1]!=3) ||
+ (np->msgin[2]!=M_X_SDTR)) {
+ np->msgin [0] = M_NOOP;
+ ncr_setsync (np, cp, 0xe0);
+ break;
+ }
+
+ per = np->msgin[3];
+ ofs = np->msgin[4];
+
+ /*
+ ** if target sends SDTR message,
+ ** it CAN transfer synch.
+ */
+
+ if (ofs)
+ tp->inqdata[7] |= INQ7_SYNC;
+
+ /*------------------------------------------------
+ ** do actual computation.
+ **------------------------------------------------
+ */
+ chg = 0;
+
+ if (ofs==0) per=255;
+ if (per < np->ns_sync) {chg = 1; per = np->ns_sync;}
+ if (per < tp->minsync)
+ {chg = 1; per = tp->minsync;}
+ if (ofs > tp->maxoffs)
+ {chg = 1; ofs = 8;}
+ fak = (4ul * per - 1) / np->ns_sync - 3;
+
+ if (ofs && (fak>7)) {chg = 1; ofs = 0;}
+
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_SDTR)
+ printf ("ncr%d targ %d: sync: per=%d ofs=%d fak=%d chg=%d.\n",
+ ncr_unit (np), target, per, ofs, fak, chg);
+#endif /* SCSI_NCR_DEBUG */
+
+ /*
+ ** if the answer had bad values,
+ ** we will use asynch mode.
+ */
+
+ if ((num == 3) && chg) ofs = 0;
+ if (!ofs) fak=7;
+
+ /*
+ ** Set synchronous mode now.
+ */
+ ncr_setsync (np, cp, (fak<<5)|ofs);
+
+ if (num == 3) {
+ if (chg) OUTL (nc_dsp,vtophys (&np->v_script->msg_bad));
+ else OUTL (nc_dsp,vtophys (&np->v_script->clrack));
+ return;
+ };
+
+ /*------------------------------------------------
+ ** prepare an answer message
+ **------------------------------------------------
+ */
+
+ np->msgout[0] = M_EXTENDED;
+ np->msgout[1] = 3;
+ np->msgout[2] = M_X_SDTR;
+ np->msgout[3] = per;
+ np->msgout[4] = ofs;
+
+ np->msgin [0] = M_NOOP;
+
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_SDTR) {
+ printf ("ncr%d targ %d: sync msgout: ",
+ ncr_unit (np), target);
+ ncr_show_msg (np->msgin);
+ printf (".\n");
+ }
+#endif /* SCSI_NCR_DEBUG */
+ break;
+
+
+/*--------------------------------------------------------------------
+**
+** Processing of special messages
+**
+**--------------------------------------------------------------------
+*/
+
+ case 5: /*
+ ** We received a M_REJECT message.
+ */
+ printf ("ncr%d targ %d: M_REJECT received (%x:%x).\n",
+ ncr_unit (np), target, ncr_msgout, np->msgout[0]);
+ break;
+
+ case 6: /*
+ ** We received an unknown message
+ */
+ printf ("ncr%d targ %d: M_REJECT sent for ",
+ ncr_unit (np), target);
+ ncr_show_msg (np->msgin);
+ printf (".\n");
+ break;
+
+/*--------------------------------------------------------------------
+**
+** Processing of a "S_QUEUE_FULL" status.
+**
+** The current command has been rejected.
+** We have started too many commands for that target.
+**
+** If possible, reinsert at head of queue.
+** Stall queue until there are no disconnected jobs
+** (ncr REALLY idle). Then restart processing.
+**
+**--------------------------------------------------------------------
+*/
+ case 8: /*
+ ** Stall the start queue.
+ */
+
+ printf ("ncr%d targ %d: queue full.\n",
+ ncr_unit (np), target);
+ np->v_script->start1[0] = SCR_INT;
+
+ /*
+ ** Try to disable tagged transfers.
+ */
+ ncr_setmaxtags (&np->target[target], 0);
+
+ /*
+ ** @QUEUE@ reinsert current job in queue.
+ */
+
+ /* fall through */
+
+ case 7: /*
+ ** Look for a disconnected job.
+ */
+
+ cp = &np->ccb;
+ while (cp && cp->host_status != HS_DISCONNECT)
+ cp = cp -> link_ccb;
+
+ /*
+ ** if there is one, ...
+ */
+ if (cp) {
+ /*
+ ** wait for reselection
+ */
+ OUTL (nc_dsp,vtophys (&np->v_script->reselect));
+ return;
+ };
+
+ /*
+ ** else remove the interrupt.
+ */
+
+ printf ("ncr%d: queue empty.\n", ncr_unit (np));
+ np->v_script->start1[0] = SCR_INT ^ IFFALSE (0);
+ break;
+
+ };
+ OUTB (nc_dcntl, (STD|NOCOM));
+}
+
+/*==========================================================
+**
+**
+** Aquire a control block
+**
+**
+**==========================================================
+*/
+
+static ccb_p ncr_get_ccb
+ (ncb_p np, u_long flags, u_long target, u_long lun)
+{
+ lcb_p lp;
+ ccb_p cp = (ccb_p ) 0;
+
+ /*
+ ** Lun structure available ?
+ */
+
+ lp = np->target[target].lp[lun];
+ if (lp)
+ cp = lp->next_ccb;
+
+ /*
+ ** Look for free CCB
+ */
+
+ while (cp && cp->magic) cp = cp->next_ccb;
+
+ /*
+ ** if nothing available, take the default.
+ */
+
+ if (!cp) cp = &np->ccb;
+
+ /*
+ ** Wait until available.
+ */
+
+ while (cp->magic) {
+ if (flags & SCSI_NOSLEEP) break;
+ if (tsleep ((caddr_t)cp, PZERO|PCATCH, "ncr", 0))
+ break;
+ };
+
+ if (cp->magic)
+ return ((ccb_p) 0);
+
+ cp->magic = 1;
+ return (cp);
+}
+
+/*==========================================================
+**
+**
+** Release one control block
+**
+**
+**==========================================================
+*/
+
+void ncr_free_ccb (ncb_p np, ccb_p cp, int flags)
+{
+ /*
+ ** sanity
+ */
+
+ if (!cp) return;
+
+ cp -> host_status = HS_IDLE;
+ cp -> magic = 0;
+ if (cp == &np->ccb)
+ wakeup ((caddr_t) cp);
+}
+
+/*==========================================================
+**
+**
+** Allocation of resources for Targets/Luns/Tags.
+**
+**
+**==========================================================
+*/
+
+static void ncr_alloc_ccb (ncb_p np, struct scsi_xfer * xp)
+{
+ tcb_p tp;
+ lcb_p lp;
+ ccb_p cp;
+
+ u_long target;
+ u_long lun;
+
+ if (!np) return;
+ if (!xp) return;
+
+ target = xp->TARGET;
+ lun = xp->LUN;
+
+ if (target>=MAX_TARGET) return;
+ if (lun >=MAX_LUN ) return;
+
+ /*
+ ** target control block ?
+ */
+ tp=&np->target[target];
+
+ if (!tp->jump_tcb.l_cmd) {
+
+ /*
+ ** initialize it.
+ */
+ tp->jump_tcb.l_cmd = (SCR_JUMP^IFFALSE (DATA (0x80 + target)));
+ tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr;
+
+ tp->getscr[0] = SCR_COPY (1);
+ tp->getscr[1] = vtophys (&tp->sval);
+ tp->getscr[2] = np->paddr + offsetof (struct ncr_reg, nc_sxfer);
+
+ assert (( (offsetof(struct ncr_reg, nc_sxfer) ^
+ offsetof(struct tcb , sval )) &3) == 0);
+
+ tp->call_lun.l_cmd = (SCR_CALL);
+ tp->call_lun.l_paddr = vtophys (&np->v_script->resel_lun);
+
+ tp->jump_lcb.l_cmd = (SCR_JUMP);
+ tp->jump_lcb.l_paddr = vtophys (&np->v_script->abort);
+
+ np->jump_tcb.l_paddr = vtophys (&tp->jump_tcb);
+ }
+
+ /*
+ ** Logic unit control block
+ */
+ lp = tp->lp[lun];
+ if (!lp) {
+ /*
+ ** Allocate a lcb
+ */
+ lp = (lcb_p) malloc (sizeof (struct lcb), M_DEVBUF, M_NOWAIT);
+ if (!lp) return;
+
+ /*
+ ** Initialize it
+ */
+ bzero (lp, sizeof (*lp));
+ lp->jump_lcb.l_cmd = (SCR_JUMP ^ IFFALSE (DATA (lun)));
+ lp->jump_lcb.l_paddr = tp->jump_lcb.l_paddr;
+
+ lp->call_tag.l_cmd = (SCR_CALL);
+ lp->call_tag.l_paddr = vtophys (&np->v_script->resel_tag);
+
+ lp->jump_ccb.l_cmd = (SCR_JUMP);
+ lp->jump_ccb.l_paddr = vtophys (&np->v_script->aborttag);
+
+ lp->actlink = 1;
+ /*
+ ** Link into Lun-Chain
+ */
+
+ tp->jump_lcb.l_paddr = vtophys (&lp->jump_lcb);
+ tp->lp[lun] = lp;
+
+ }
+
+ /*
+ ** Limit possible number of ccbs.
+ **
+ ** If tagged command queueing is enabled,
+ ** can use more than one ccb.
+ */
+
+ if (np->actccbs >= MAX_START-2) return;
+ if (lp->actccbs && (lp->actccbs >= lp->reqccbs))
+ return;
+
+ /*
+ ** Allocate a ccb
+ */
+ cp = (ccb_p) malloc (sizeof (struct ccb), M_DEVBUF, M_NOWAIT);
+
+ if (!cp)
+ return;
+
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_ALLOC)
+ printf ("ncr%d targ %d lun %d: new ccb @%x.\n",
+ ncr_unit (np), target, lun, cp);
+#endif /* SCSI_NCR_DEBUG */
+
+ /*
+ ** Count it
+ */
+ lp->actccbs++;
+ np->actccbs++;
+
+ /*
+ ** Initialize it.
+ */
+ bzero (cp, sizeof (*cp));
+
+ /*
+ ** link in reselect chain.
+ */
+ cp->jump_ccb.l_cmd = SCR_JUMP;
+ cp->jump_ccb.l_paddr = lp->jump_ccb.l_paddr;
+ lp->jump_ccb.l_paddr = vtophys(&cp->jump_ccb);
+ cp->call_tmp.l_cmd = SCR_CALL;
+ cp->call_tmp.l_paddr = vtophys(&np->v_script->resel_tmp);
+
+ /*
+ ** link in wakeup chain
+ */
+ cp->link_ccb = np->ccb.link_ccb;
+ np->ccb.link_ccb = cp;
+
+ /*
+ ** Link into CCB-Chain
+ */
+ cp->next_ccb = lp->next_ccb;
+ lp->next_ccb = cp;
+}
+
+/*==========================================================
+**
+**
+** Announce the number of ccbs/tags to the scsi driver.
+**
+**
+**==========================================================
+*/
+
+static void ncr_opennings (ncb_p np, lcb_p lp, struct scsi_xfer * xp)
+{
+#ifndef ANCIENT
+ /*
+ ** want to reduce the number ...
+ */
+ if (lp->actlink > lp->reqlink) {
+
+ /*
+ ** Try to reduce the count.
+ ** We assumed to run at splbio ..
+ */
+ u_char diff = lp->actlink - lp->reqlink;
+
+ if (!diff) return;
+
+ if (diff > xp->sc_link->opennings)
+ diff = xp->sc_link->opennings;
+
+ /*
+ ** reduce it.
+ */
+
+ xp->sc_link->opennings -= diff;
+ lp->actlink -= diff;
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_TAGS)
+ printf ("ncr%d: actlink: diff=%d, new=%d, req=%d\n",
+ ncr_unit(np), diff, lp->actlink, lp->reqlink);
+#endif /* SCSI_NCR_DEBUG */
+ return;
+ };
+
+ /*
+ ** want to increase the number ?
+ */
+ if (lp->reqlink > lp->actlink) {
+ u_char diff = lp->reqlink - lp->actlink;
+
+ xp->sc_link->opennings += diff;
+ lp->actlink += diff;
+ wakeup ((caddr_t) xp->sc_link);
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_TAGS)
+ printf ("ncr: actlink: diff=%d, new=%d, req=%d\n",
+ ncr_unit(np), diff, lp->actlink, lp->reqlink);
+#endif
+ };
+#endif
+}
+
+/*==========================================================
+**
+**
+** Build Scatter Gather Block
+**
+**
+**==========================================================
+**
+** The transfer area may be scattered among
+** several non adjacent physical pages.
+**
+** We may use MAX_SCATTER blocks.
+**
+**----------------------------------------------------------
+*/
+
+static u_long ncr_scatter
+ (struct dsb* phys, vm_offset_t vaddr, vm_size_t datalen)
+{
+ u_long paddr, pnext;
+
+ u_short segment = 0;
+ u_long segsize, segaddr;
+ u_long size, csize = 0;
+ u_long chunk = MAX_SIZE;
+ int free;
+
+ bzero (&phys->data, sizeof (phys->data));
+ if (!datalen) return (0);
+
+ paddr = vtophys (vaddr);
+
+ /*
+ ** insert extra break points at a distance of chunk.
+ ** We try to reduce the number of interrupts due to
+ ** unexpected phase changes due to disconnects.
+ ** A typical harddisk may disconnect before ANY block.
+ ** If we want to avoid unexpected phase changes at all
+ ** we have to use a break point every 512 bytes.
+ ** Of course the number of scatter/gather blocks is
+ ** limited.
+ */
+
+ free = MAX_SCATTER - 1;
+
+ if (vaddr & (NBPG-1)) free -= datalen / NBPG;
+
+ if (free>1)
+ while ((chunk * free >= 2 * datalen) && (chunk>=1024))
+ chunk /= 2;
+
+#ifdef SCSI_NCR_DEBUG
+ if(ncr_debug & DEBUG_SCATTER)
+ printf("ncr?:\tscattering virtual=0x%x size=%d chunk=%d.\n",
+ (u_long) vaddr, (u_long) datalen, chunk);
+#endif /* SCSI_NCR_DEBUG */
+
+ /*
+ ** Build data descriptors.
+ */
+ while (datalen && (segment < MAX_SCATTER)) {
+
+ /*
+ ** this segment is empty
+ */
+ segsize = 0;
+ segaddr = paddr;
+ pnext = paddr;
+
+ if (!csize) csize = chunk;
+
+ while ((datalen) && (paddr == pnext) && (csize)) {
+
+ /*
+ ** continue this segment
+ */
+ pnext = (paddr & (~(NBPG - 1))) + NBPG;
+
+ /*
+ ** Compute max size
+ */
+
+ size = pnext - paddr; /* page size */
+ if (size > datalen) size = datalen; /* data size */
+ if (size > csize ) size = csize ; /* chunksize */
+
+ segsize += size;
+ vaddr += size;
+ csize -= size;
+ datalen -= size;
+ paddr = vtophys (vaddr);
+ };
+
+#ifdef SCSI_NCR_DEBUG
+ if(ncr_debug & DEBUG_SCATTER)
+ printf ("\tseg #%d addr=%x size=%d (rest=%d).\n",
+ segment, segaddr, segsize, datalen);
+#endif /* SCSI_NCR_DEBUG */
+
+ phys->data[segment].addr = segaddr;
+ phys->data[segment].size = segsize;
+ segment++;
+ }
+
+ if (datalen)
+ printf("ncr?: scatter/gather failed (residue=%d).\n",
+ datalen);
+
+ return (datalen);
+}
+
+/*==========================================================
+**
+**
+** Profiling the drivers and targets performance.
+**
+**
+**==========================================================
+*/
+
+/*
+** Compute the difference in milliseconds.
+**/
+
+static int ncr_delta (struct timeval * from, struct timeval * to)
+{
+ if (!from->tv_sec) return (-1);
+ if (!to ->tv_sec) return (-2);
+ return ( (to->tv_sec - from->tv_sec - 2)*1000+
+ +(to->tv_usec - from->tv_usec + 2000000)/1000);
+}
+
+#define PROFILE cp->phys.header.stamp
+static void ncb_profile (ncb_p np, ccb_p cp)
+{
+ int co, da, st, en, di, se, post,work,disc;
+ u_long diff;
+
+ PROFILE.end = time;
+
+ st = ncr_delta (&PROFILE.start,&PROFILE.status);
+ if (st<0) return; /* status not reached */
+
+ da = ncr_delta (&PROFILE.start,&PROFILE.data);
+ if (da<0) return; /* No data transfer phase */
+
+ co = ncr_delta (&PROFILE.start,&PROFILE.command);
+ if (co<0) return; /* command not executed */
+
+ en = ncr_delta (&PROFILE.start,&PROFILE.end),
+ di = ncr_delta (&PROFILE.start,&PROFILE.disconnect),
+ se = ncr_delta (&PROFILE.start,&PROFILE.select);
+ post = en - st;
+
+ /*
+ ** @PROFILE@ Disconnect time invalid if multiple disconnects
+ */
+
+ if (di>=0) disc = se-di; else disc = 0;
+
+ work = (st - co) - disc;
+
+ diff = (np->disc_phys - np->disc_ref) & 0xff;
+ np->disc_ref += diff;
+
+ np->profile.num_trans += 1;
+ if (cp->xfer)
+ np->profile.num_bytes += cp->xfer->datalen;
+ np->profile.num_disc += diff;
+ np->profile.ms_setup += co;
+ np->profile.ms_data += work;
+ np->profile.ms_disc += disc;
+ np->profile.ms_post += post;
+}
+#undef PROFILE
+
+/*==========================================================
+**
+** Determine the ncr's clock frequency.
+** This is important for the negotiation
+** of the synchronous transfer rate.
+**
+**==========================================================
+**
+** Note: we have to return the correct value.
+** THERE IS NO SAVE DEFAULT VALUE.
+**
+** We assume that all NCR based boards are delivered
+** with a 40Mhz clock. Because we have to divide
+** by an integer value greater than 3, only clock
+** frequencies of 40Mhz (/4) or 50MHz (/5) permit
+** the FAST-SCSI rate of 10MHz.
+**
+**----------------------------------------------------------
+*/
+
+#ifndef NCR_CLOCK
+# define NCR_CLOCK 40
+#endif /* NCR_CLOCK */
+
+
+static void ncr_getclock (ncb_p np)
+{
+ u_char tbl[5] = {6,2,3,4,6};
+ u_char f;
+ u_char ns_clock = (1000/NCR_CLOCK);
+
+ /*
+ ** Compute the best value for scntl3.
+ */
+
+ f = (2 * MIN_SYNC_PD - 1) / ns_clock;
+ if (!f ) f=1;
+ if (f>4) f=4;
+ np -> ns_sync = (ns_clock * tbl[f]) / 2;
+ np -> rv_scntl3 = f<<4;
+
+ f = (2 * MIN_ASYNC_PD - 1) / ns_clock;
+ if (!f ) f=1;
+ if (f>4) f=4;
+ np -> ns_async = (ns_clock * tbl[f]) / 2;
+ np -> rv_scntl3 |= f;
+#ifdef SCSI_NCR_DEBUG
+ if (ncr_debug & DEBUG_TIMING)
+ printf ("ncr%d: sclk=%d async=%d sync=%d (ns) scntl3=0x%x\n",
+ ncr_unit (np), ns_clock, np->ns_async, np->ns_sync, np->rv_scntl3);
+#endif /* SCSI_NCR_DEBUG */
+}
+
+/*=========================================================================*/
+#endif /* KERNEL */
+#endif /* NNCR */
diff --git a/sys/i386/pci/ncr_reg.h b/sys/i386/pci/ncr_reg.h
new file mode 100644
index 000000000000..917dc14ab097
--- /dev/null
+++ b/sys/i386/pci/ncr_reg.h
@@ -0,0 +1,489 @@
+/**************************************************************************
+**
+** $Id: ncr_reg.h,v 2.0.0.3 94/07/24 08:59:19 wolf Exp $
+**
+** #define the NCR 53 C 810 Register Structure
+** #define the NCR 53 C 810 Scripts Language
+**
+**-------------------------------------------------------------------------
+**
+** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany
+** <wolf@dentaro.GUN.de>
+**
+** This is a beta version - use with care.
+**
+**-------------------------------------------------------------------------
+**
+** $Log: ncr_reg.h,v $
+** Revision 2.0.0.3 94/07/24 08:59:19 wolf
+** bits of sstat0 defined.
+**
+** Revision 2.0 94/07/10 15:53:27 wolf
+** FreeBSD release.
+**
+** Revision 1.0 94/06/07 20:02:21 wolf
+** Beta release.
+**
+***************************************************************************
+*/
+
+#ifndef __NCR_REG_H__
+#define __NCR_REG_H__
+
+
+/*-----------------------------------------------------------------
+**
+** The ncr 53c810 register structure.
+**
+**-----------------------------------------------------------------
+*/
+
+struct ncr_reg {
+/*00*/ u_char nc_scntl0; /* full arb., ena parity, par->ATN */
+/*01*/ u_char nc_scntl1; /* no reset */
+ #define ISCON 0x10 /* connected to scsi */
+ #define CRST 0x08 /* force reset */
+/*02*/ u_char nc_scntl2; /* no disconnect expected */
+/*03*/ u_char nc_scntl3; /* cnf system clock dependent */
+/*04*/ u_char nc_scid; /* cnf host adapter scsi address */
+ #define RRE 0x40 /* r/w:e enable response to resel. */
+ #define SRE 0x20 /* r/w:e enable response to select */
+/*05*/ u_char nc_sxfer; /* ### Sync speed and count */
+/*06*/ u_char nc_sdid; /* ### Destination-ID */
+/*07*/ u_char nc_gpreg; /* ??? IO-Pins */
+/*08*/ u_char nc_sfbr; /* ### First byte in phase */
+/*09*/ u_char nc_socl;
+ #define CREQ 0x80 /* r/w: SCSI-REQ */
+ #define CACK 0x40 /* r/w: SCSI-ACK */
+ #define CBSY 0x20 /* r/w: SCSI-BSY */
+ #define CSEL 0x10 /* r/w: SCSI-SEL */
+ #define CATN 0x08 /* r/w: SCSI-ATN */
+ #define CMSG 0x04 /* r/w: SCSI-MSG */
+ #define CC_D 0x02 /* r/w: SCSI-C_D */
+ #define CI_O 0x01 /* r/w: SCSI-I_O */
+/*0a*/ u_char nc_ssid;
+/*0b*/ u_char nc_sbcl;
+/*0c*/ u_char nc_dstat;
+ #define DFE 0x80 /* sta: dma fifo empty */
+ #define MDPE 0x40 /* int: master data parity error */
+ #define BF 0x20 /* int: script: bus fault */
+ #define ABRT 0x10 /* int: script: command aborted */
+ #define SSI 0x08 /* int: script: single step */
+ #define SIR 0x04 /* int: script: interrupt instruct. */
+ #define IID 0x01 /* int: script: illegal instruct. */
+/*0d*/ u_char nc_sstat0;
+ #define ILF 0x80 /* sta: data in SIDL register */
+ #define ORF 0x40 /* sta: data in SODR register */
+ #define OLF 0x20 /* sta: data in SODL register */
+ #define AIP 0x10 /* sta: arbitration in progress */
+ #define LOA 0x08 /* sta: arbitration lost */
+ #define WOA 0x04 /* sta: arbitration won */
+ #define IRST 0x02 /* sta: scsi reset signal */
+ #define SDP 0x01 /* sta: scsi parity signal */
+/*0e*/ u_char nc_sstat1;
+/*0f*/ u_char nc_sstat2;
+
+/*10*/ u_long nc_dsa; /* --> Base page */
+/*14*/ u_char nc_istat; /* --> Main Command and status */
+ #define CABRT 0x80 /* cmd: abort current operation */
+ #define SRST 0x40 /* mod: reset chip */
+ #define SIGP 0x20 /* r/w: message from host to ncr */
+ #define SEM 0x10 /* r/w: message between host + ncr */
+ #define CON 0x08 /* sta: connected to scsi */
+ #define INTF 0x04 /* sta: int on the fly (reset by wr)*/
+ #define SIP 0x02 /* sta: scsi-interupt */
+ #define DIP 0x01 /* sta: host/script interupt */
+/*15*/ u_char nc_15_;
+/*16*/ u_char nc_16_;
+/*17*/ u_char nc_17_;
+/*18*/ u_char nc_ctest0;
+/*19*/ u_char nc_ctest1;
+/*1a*/ u_char nc_ctest2;
+ #define CSIGP 0x40
+/*1b*/ u_char nc_ctest3;
+ #define CLF 0x04 /* clear scsi fifo */
+/*1c*/ u_long nc_temp; /* ### Temporary stack */
+/*20*/ u_char nc_dfifo;
+/*21*/ u_char nc_ctest4;
+/*22*/ u_char nc_ctest5;
+/*23*/ u_char nc_ctest6;
+/*24*/ u_long nc_dbc; /* ### Byte count and command */
+/*28*/ u_long nc_dnad; /* ### Next command register */
+/*2c*/ u_long nc_dsp; /* --> Script Pointer */
+/*30*/ u_long nc_dsps; /* --> Script pointer save/opcode#2 */
+/*34*/ u_long nc_scratcha; /* ??? Temporary register a */
+/*38*/ u_char nc_dmode;
+/*39*/ u_char nc_dien;
+/*3a*/ u_char nc_dwt;
+/*3b*/ u_char nc_dcntl; /* --> Script execution control */
+ #define SSM 0x10 /* mod: single step mode */
+ #define STD 0x04 /* cmd: start dma mode */
+ #define NOCOM 0x01 /* cmd: protect sfbr while reselect */
+/*3c*/ u_long nc_adder;
+
+/*40*/ u_short nc_sien; /* -->: interupt enable */
+/*42*/ u_short nc_sist; /* <--: interupt status */
+ #define STO 0x0400/* sta: timeout (select) */
+ #define GEN 0x0200/* sta: timeout (general) */
+ #define HTH 0x0100/* sta: timeout (handshake) */
+ #define MA 0x80 /* sta: phase mismatch */
+ #define CMP 0x40 /* sta: arbitration complete */
+ #define SEL 0x20 /* sta: selected by another device */
+ #define RSL 0x10 /* sta: reselected by another device*/
+ #define SGE 0x08 /* sta: gross error (over/underflow)*/
+ #define UDC 0x04 /* sta: unexpected disconnect */
+ #define RST 0x02 /* sta: scsi bus reset detected */
+ #define PAR 0x01 /* sta: scsi parity error */
+/*44*/ u_char nc_slpar;
+/*45*/ u_char nc_45_;
+/*46*/ u_char nc_macntl;
+/*47*/ u_char nc_gpcntl;
+/*48*/ u_char nc_stime0; /* cmd: timeout for select&handshake*/
+/*49*/ u_char nc_stime1; /* cmd: timeout user defined */
+/*4a*/ u_char nc_respid; /* sta: Reselect-IDs */
+/*4b*/ u_char nc_4b_;
+/*4c*/ u_char nc_stest0;
+/*4d*/ u_char nc_stest1;
+/*4e*/ u_char nc_stest2;
+ #define ROF 0x40 /* reset scsi offset (after gross error!) */
+ #define EXT 0x02 /* extended filtering */
+/*4f*/ u_char nc_stest3;
+ #define TE 0x80 /* c: tolerAnt enable */
+ #define CSF 0x02 /* c: clear scsi fifo */
+/*50*/ u_char nc_sidl; /* Lowlevel: latched from scsi data */
+/*51*/ u_char nc_51_;
+/*52*/ u_char nc_52_;
+/*53*/ u_char nc_53_;
+/*54*/ u_char nc_sodl; /* Lowlevel: data out to scsi data */
+/*55*/ u_char nc_55_;
+/*56*/ u_char nc_56_;
+/*57*/ u_char nc_57_;
+/*58*/ u_char nc_sbdl; /* Lowlevel: data from scsi data */
+/*59*/ u_char nc_59_;
+/*5a*/ u_char nc_5a_;
+/*5b*/ u_char nc_5b_;
+/*5c*/ u_char nc_scr0; /* Working register B */
+/*5d*/ u_char nc_scr1; /* */
+/*5e*/ u_char nc_scr2; /* */
+/*5f*/ u_char nc_scr3; /* */
+/*60*/
+};
+
+/*-----------------------------------------------------------
+**
+** Utility macros for the script.
+**
+**-----------------------------------------------------------
+*/
+
+#define REGJ(p,r) (offsetof(struct ncr_reg, p ## r))
+#define REG(r) REGJ (nc_, r)
+
+#ifndef TARGET_MODE
+#define TARGET_MODE 0
+#endif
+
+typedef unsigned long ncrcmd;
+
+/*-----------------------------------------------------------
+**
+** SCSI phases
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_DATA_OUT 0x00000000
+#define SCR_DATA_IN 0x01000000
+#define SCR_COMMAND 0x02000000
+#define SCR_STATUS 0x03000000
+#define SCR_ILG_OUT 0x04000000
+#define SCR_ILG_IN 0x05000000
+#define SCR_MSG_OUT 0x06000000
+#define SCR_MSG_IN 0x07000000
+
+/*-----------------------------------------------------------
+**
+** Data transfer via SCSI.
+**
+**-----------------------------------------------------------
+**
+** MOVE_ABS (LEN)
+** <<start address>>
+**
+** MOVE_IND (LEN)
+** <<dnad_offset>>
+**
+** MOVE_TBL
+** <<dnad_offset>>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_MOVE_ABS(l) ((0x08000000 ^ (TARGET_MODE << 1ul)) | (l))
+#define SCR_MOVE_IND(l) ((0x28000000 ^ (TARGET_MODE << 1ul)) | (l))
+#define SCR_MOVE_TBL (0x18000000 ^ (TARGET_MODE << 1ul))
+
+struct scr_tblmove {
+ u_long size;
+ u_long addr;
+};
+
+/*-----------------------------------------------------------
+**
+** Selection
+**
+**-----------------------------------------------------------
+**
+** SEL_ABS | SCR_ID (0..7) [ | REL_JMP]
+** <<alternate_address>>
+**
+** SEL_TBL | << dnad_offset>> [ | REL_JMP]
+** <<alternate_address>>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_SEL_ABS 0x40000000
+#define SCR_SEL_ABS_ATN 0x41000000
+#define SCR_SEL_TBL 0x42000000
+#define SCR_SEL_TBL_ATN 0x43000000
+
+struct scr_tblsel {
+ u_char sel_0;
+ u_char sel_sxfer;
+ u_char sel_id;
+ u_char sel_scntl3;
+};
+
+#define SCR_JMP_REL 0x04000000
+#define SCR_ID(id) (((u_long)(id)) << 16)
+
+/*-----------------------------------------------------------
+**
+** Waiting for Disconnect or Reselect
+**
+**-----------------------------------------------------------
+**
+** WAIT_DISC
+** dummy: <<alternate_address>>
+**
+** WAIT_RESEL
+** <<alternate_address>>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_WAIT_DISC 0x48000000
+#define SCR_WAIT_RESEL 0x50000000
+
+/*-----------------------------------------------------------
+**
+** Bit Set / Reset
+**
+**-----------------------------------------------------------
+**
+** SET (flags {|.. })
+**
+** CLR (flags {|.. })
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_SET(f) (0x58000000 | (f))
+#define SCR_CLR(f) (0x60000000 | (f))
+
+#define SCR_CARRY 0x00000400
+#define SCR_TRG 0x00000200
+#define SCR_ACK 0x00000040
+#define SCR_ATN 0x00000008
+
+
+
+
+/*-----------------------------------------------------------
+**
+** Memory to memory move
+**
+**-----------------------------------------------------------
+**
+** COPY (bytecount)
+** << source_address >>
+** << destination_address >>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_COPY(n) (0xc0000000 | (n))
+
+/*-----------------------------------------------------------
+**
+** Register move and binary operations
+**
+**-----------------------------------------------------------
+**
+** SFBR_REG (reg, op, data) reg = SFBR op data
+** << 0 >>
+**
+** REG_SFBR (reg, op, data) SFBR = reg op data
+** << 0 >>
+**
+** REG_REG (reg, op, data) reg = reg op data
+** << 0 >>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_SFBR_REG(reg,op,data) \
+ (0x68000000 | (REG(reg) << 16ul) | (op) | ((data)<<8ul))
+
+#define SCR_REG_SFBR(reg,op,data) \
+ (0x70000000 | (REG(reg) << 16ul) | (op) | ((data)<<8ul))
+
+#define SCR_REG_REG(reg,op,data) \
+ (0x78000000 | (REG(reg) << 16ul) | (op) | ((data)<<8ul))
+
+#define SCR_LOAD 0x00000000
+#define SCR_SHL 0x01000000
+#define SCR_OR 0x02000000
+#define SCR_XOR 0x03000000
+#define SCR_AND 0x04000000
+#define SCR_SHR 0x05000000
+#define SCR_ADD 0x06000000
+#define SCR_ADDC 0x07000000
+
+/*-----------------------------------------------------------
+**
+** FROM_REG (reg) reg = SFBR
+** << 0 >>
+**
+** TO_REG (reg) SFBR = reg
+** << 0 >>
+**
+** LOAD_REG (reg, data) reg = <data>
+** << 0 >>
+**
+** LOAD_SFBR(data) SFBR = <data>
+** << 0 >>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_FROM_REG(reg) \
+ SCR_REG_SFBR(reg,SCR_OR,0)
+
+#define SCR_TO_REG(reg) \
+ SCR_SFBR_REG(reg,SCR_OR,0)
+
+#define SCR_LOAD_REG(reg,data) \
+ SCR_REG_REG(reg,SCR_LOAD,data)
+
+#define SCR_LOAD_SFBR(data) \
+ (SCR_REG_SFBR (gpreg, SCR_LOAD, data))
+
+/*-----------------------------------------------------------
+**
+** Waiting for Disconnect or Reselect
+**
+**-----------------------------------------------------------
+**
+** JUMP [ | IFTRUE/IFFALSE ( ... ) ]
+** <<address>>
+**
+** JUMPR [ | IFTRUE/IFFALSE ( ... ) ]
+** <<distance>>
+**
+** CALL [ | IFTRUE/IFFALSE ( ... ) ]
+** <<address>>
+**
+** CALLR [ | IFTRUE/IFFALSE ( ... ) ]
+** <<distance>>
+**
+** RETURN [ | IFTRUE/IFFALSE ( ... ) ]
+** <<dummy>>
+**
+** INT [ | IFTRUE/IFFALSE ( ... ) ]
+** <<ident>>
+**
+** INT_FLY [ | IFTRUE/IFFALSE ( ... ) ]
+** <<ident>>
+**
+** Conditions:
+** WHEN (phase)
+** IF (phase)
+** CARRY
+** DATA (data, mask)
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_JUMP 0x80080000
+#define SCR_JUMPR 0x80880000
+#define SCR_CALL 0x88080000
+#define SCR_CALLR 0x88880000
+#define SCR_RETURN 0x90080000
+#define SCR_INT 0x98080000
+#define SCR_INT_FLY 0x98180000
+
+#define IFFALSE(arg) (0x00080000 | (arg))
+#define IFTRUE(arg) (0x00000000 | (arg))
+
+#define WHEN(phase) (0x00030000 | (phase))
+#define IF(phase) (0x00020000 | (phase))
+
+#define DATA(D) (0x00040000 | ((D) & 0xff))
+#define MASK(D,M) (0x00040000 | (((M ^ 0xff) & 0xff) << 8ul)|((D) & 0xff))
+
+#define CARRYSET (0x00200000)
+
+/*-----------------------------------------------------------
+**
+** SCSI constants.
+**
+**-----------------------------------------------------------
+*/
+
+/*
+** Messages
+*/
+
+#define M_COMPLETE (0x00)
+#define M_EXTENDED (0x01)
+#define M_SAVE_DP (0x02)
+#define M_RESTORE_DP (0x03)
+#define M_DISCONNECT (0x04)
+#define M_ID_ERROR (0x05)
+#define M_ABORT (0x06)
+#define M_REJECT (0x07)
+#define M_NOOP (0x08)
+#define M_PARITY (0x09)
+#define M_LCOMPLETE (0x0a)
+#define M_FCOMPLETE (0x0b)
+#define M_RESET (0x0c)
+#define M_ABORT_TAG (0x0d)
+#define M_CLEAR_QUEUE (0x0e)
+#define M_INIT_REC (0x0f)
+#define M_REL_REC (0x10)
+#define M_TERMINATE (0x11)
+#define M_SIMPLE_TAG (0x20)
+#define M_HEAD_TAG (0x21)
+#define M_ORDERED_TAG (0x22)
+#define M_IDENTIFY (0x80)
+
+#define M_X_SDTR (0x01)
+
+/*
+** Status
+*/
+
+#define S_GOOD (0x00)
+#define S_CHECK_COND (0x02)
+#define S_COND_MET (0x04)
+#define S_BUSY (0x08)
+#define S_INT (0x10)
+#define S_INT_COND_MET (0x14)
+#define S_CONFLICT (0x18)
+#define S_TERMINATED (0x20)
+#define S_QUEUE_FULL (0x28)
+#define S_ILLEGAL (0xff)
+
+#endif /*__NCR_REG_H__*/
diff --git a/sys/i386/pci/pci.c b/sys/i386/pci/pci.c
new file mode 100644
index 000000000000..878c97c68165
--- /dev/null
+++ b/sys/i386/pci/pci.c
@@ -0,0 +1,494 @@
+/**************************************************************************
+**
+** $Id: pci.c,v 2.0.0.1 94/07/19 19:06:44 wolf Exp $
+**
+** General subroutines for the PCI bus on 80*86 systems.
+** pci_configure ()
+**
+**-------------------------------------------------------------------------
+**
+** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany
+** <wolf@dentaro.GUN.de>
+**
+** This is a beta version - use with care.
+**
+**-------------------------------------------------------------------------
+**
+** $Log: pci.c,v $
+** Revision 2.0.0.1 94/07/19 19:06:44 wolf
+** New vendor entry: MATROX
+**
+** Revision 2.0 94/07/10 15:53:29 wolf
+** FreeBSD release.
+**
+** Revision 1.0 94/06/07 20:02:19 wolf
+** Beta release.
+**
+***************************************************************************
+*/
+
+#include <pci.h>
+#if NPCI > 0
+
+/*========================================================
+**
+** Configuration
+**
+**========================================================
+*/
+
+/*
+** maximum number of devices which share one interrupt line
+*/
+
+#define PCI_MAX_DPI 1
+
+/*
+** there may be up to 255 busses on pci.
+** don't probe them all :-)
+*/
+
+#define FIRST_BUS 0
+#define LAST_BUS 0
+
+/*
+** there may be up to 32 devices per bus.
+** do probe them all ;-)
+*/
+
+#define FIRST_DEVICE 0
+#define LAST_DEVICE 31
+
+/*========================================================
+**
+** #includes and declarations
+**
+**========================================================
+*/
+
+#include <types.h>
+#include <cdefs.h>
+#include <errno.h>
+#include <param.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+
+#include <i386/isa/icu.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+
+#include <i386/pci/pci.h>
+#include <i386/pci/pci_device.h>
+#include <i386/pci/pcibios.h>
+
+
+static char ident[] =
+ "\n$Id: pci.c,v 2.0.0.1 94/07/19 19:06:44 wolf Exp $\n"
+ "Copyright (c) 1994, Wolfgang Stanglmeier\n";
+
+/*
+** Function prototypes missing in system headers
+*/
+
+extern int printf();
+extern int ffs();
+extern pmap_t pmap_kernel(void);
+
+/*
+** function prototypes
+*/
+
+int pci_map_mem
+ (pcici_t tag, u_long reg, u_long *va, vm_offset_t *pa);
+int pci_map_port (pcici_t tag, u_long reg, u_short* pa);
+void pci_configure (void);
+
+/*========================================================
+**
+** Autoconfiguration (of isa bus) (Free/386)
+**
+**========================================================
+*/
+
+/*
+** per device (interrupt) data structure.
+*/
+
+static struct {
+ u_short number;
+ u_short isanum;
+ struct {
+ int (*proc)(int dev);
+ dev_t unit;
+ } vector[PCI_MAX_DPI];
+} pcidata [NPCI];
+
+#ifndef __NetBSD__
+
+/*
+** check device ready
+*/
+static int pciprobe (struct isa_device *dev)
+{
+ if (dev->id_unit >= NPCI)
+ return (0);
+
+ if (!pci_conf_mode())
+ return (0);
+
+ return (1);
+}
+
+/*
+** initialize the driver structure
+*/
+static int pciattach (struct isa_device *isdp)
+{
+ pcidata[isdp->id_unit].number = 0;
+ pcidata[isdp->id_unit].isanum = ffs(isdp->id_irq)-1;
+ return (1);
+}
+
+/*
+** ISA driver structure
+*/
+
+struct isa_driver pcidriver = {
+ pciprobe,
+ pciattach,
+ "pci"
+};
+
+/*========================================================
+**
+** Interrupt forward from isa to pci devices.
+**
+**========================================================
+*/
+
+
+void pciintr (int unit)
+{
+ u_short i;
+ if (unit >= NPCI) return;
+
+#if defined (GENERICAH) || defined (GENERICBT) || defined (GENERICNCR) \
+ || defined (GENERICAHA) || defined (GENERICISA)
+ for (unit=0; unit < NPCI; unit++)
+#endif
+
+ for (i=0; i<pcidata[unit].number; i++) {
+ (void)(*pcidata[unit].vector[i].proc)(pcidata[unit].vector[i].unit);
+ };
+}
+
+#endif /* __NetBSD__ */
+
+/*========================================================
+**
+** Autoconfiguration of pci devices.
+**
+** This is reverse to the isa configuration.
+** (1) find a pci device.
+** (2) look for a driver.
+**
+**========================================================
+*/
+
+/*--------------------------------------------------------
+**
+** The pci devices can be mapped to any address.
+** As default we start at the last gigabyte.
+**
+**--------------------------------------------------------
+*/
+
+#ifndef PCI_PMEM_START
+#define PCI_PMEM_START 0xc0000000
+#endif
+
+static vm_offset_t pci_paddr = PCI_PMEM_START;
+
+/*---------------------------------------------------------
+**
+** pci_configure ()
+**
+**---------------------------------------------------------
+*/
+
+void pci_configure()
+{
+ u_char bus, device, reg;
+ pcici_t tag;
+ pcidi_t type;
+ u_long data;
+ int unit;
+ int intpin;
+ int pci_mode;
+
+ struct pci_driver *drp;
+ struct pci_device *dvp;
+
+ /*
+ ** check pci bus present
+ */
+
+ pci_mode = pci_conf_mode ();
+ if (!pci_mode) return;
+
+ /*
+ ** hello world ..
+ */
+
+ printf ("PCI configuration mode %d.\n", pci_mode);
+
+ for (bus=FIRST_BUS;bus<=LAST_BUS; bus++)
+ for (device=FIRST_DEVICE; device<=LAST_DEVICE; device ++) {
+ tag = pcitag (bus, device, 0);
+ type = pci_conf_read (tag, 0);
+
+ if ((!type) || (type==0xfffffffful)) continue;
+ printf ("on pci%d:%d ", bus, device);
+
+ /*
+ ** lookup device in ioconfiguration:
+ */
+
+ for (dvp = pci_devtab; drp=dvp->pd_driver; dvp++) {
+ if (drp->device_id == type) break;
+ };
+
+ if (!drp) {
+
+ /*
+ ** not found
+ */
+
+ switch (type & 0xffff) {
+
+ case 0x1002:
+ printf ("ATI TECHNOLOGIES INC");
+ break;
+ case 0x101A:
+ printf ("NCR");
+ break;
+ case 0x102B:
+ printf ("MATROX");
+ break;
+ case 0x1045:
+ printf ("OPTI");
+ break;
+ case 0x8086:
+ printf ("INTEL CORPORATION");
+ break;
+
+ default:
+ printf ("vendor=%x", type & 0xffff);
+ };
+
+ switch (type) {
+
+ case 0x04848086:
+ printf (" 82378IB pci-isa bridge");
+ break;
+ case 0x04838086:
+ printf (" 82424ZX cache dram controller");
+ break;
+ case 0x04828086:
+ printf (" 82375EB pci-eisa bridge");
+ break;
+ case 0x04A38086:
+ printf (" 82434LX pci cache memory controller");
+ break;
+ default:
+ printf (", device=%x", type >> 16);
+ };
+ printf (" [not supported]\n");
+
+ for (reg=0x10; reg<=0x20; reg+=4) {
+ data = pci_conf_read (tag, reg);
+ if (!data) continue;
+ switch (data&7) {
+
+ case 1:
+ case 5:
+ printf (" map(%x): io(%x)\n",
+ reg, data & ~3);
+ break;
+ case 0:
+ printf (" map(%x): mem32(%x)\n",
+ reg, data & ~7);
+ break;
+ case 2:
+ printf (" map(%x): mem20(%x)\n",
+ reg, data & ~7);
+ break;
+ case 4:
+ printf (" map(%x): mem64(%x)\n",
+ reg, data & ~7);
+ break;
+ };
+ };
+ continue;
+ };
+
+ /*
+ ** found it.
+ ** probe returns the device unit.
+ */
+
+ printf ("<%s>", drp -> vendor);
+
+ unit = (*drp->probe) (tag);
+
+ if (unit<0) {
+ printf (" probe failed.\n");
+ continue;
+ };
+
+ /*
+ ** install interrupts
+ */
+
+ intpin = (pci_conf_read (tag, 0x3c) >> 8) & 0xff;
+ if (intpin) {
+ printf (" irq %c", 0x60+intpin);
+ intpin--;
+ if (intpin < NPCI) {
+ u_short entry = pcidata[intpin].number;
+ if (entry < PCI_MAX_DPI) {
+ pcidata[intpin].vector[entry].proc = drp->intr;
+ pcidata[intpin].vector[entry].unit = unit;
+ entry++;
+ };
+ printf (" isa=%d [%d]",pcidata[intpin].isanum, entry);
+ pcidata[intpin].number=entry;
+ } else printf (" not installed");
+ };
+
+ /*
+ ** enable memory access
+ */
+ data = pci_conf_read (tag, 0x04) & 0xffff | 0x0002;
+ pci_conf_write (tag, (u_char) 0x04, data);
+
+ /*
+ ** attach device
+ ** may produce additional log messages,
+ ** i.e. when installing subdevices.
+ */
+
+ printf (" as %s%d\n", drp->name,unit);
+ (void) (*drp->attach) (tag);
+
+ };
+
+ printf ("pci uses physical addresses from %x to %x\n",
+ PCI_PMEM_START, pci_paddr);
+}
+
+/*-----------------------------------------------------------------------
+**
+** Map device into virtual and physical space
+**
+**-----------------------------------------------------------------------
+*/
+
+extern vm_map_t kernel_map;
+
+int pci_map_port (pcici_t tag, u_long reg, u_short* pa)
+{
+ /*
+ ** @MAPIO@ not yet implemented.
+ */
+ return (ENOSYS);
+}
+
+
+int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa)
+{
+ u_long data, result;
+ vm_size_t vsize;
+ vm_offset_t vaddr;
+
+ /*
+ ** sanity check
+ */
+
+ if (reg <= 0x10 || reg >= 0x20 || (reg & 3))
+ return (EINVAL);
+
+ /*
+ ** get size and type of memory
+ */
+
+ pci_conf_write (tag, reg, 0xfffffffful);
+ data = pci_conf_read (tag, reg);
+
+ switch (data & 0x0f) {
+
+ case 0x0: /* 32 bit non cachable */
+ break;
+
+ default: /* unknown */
+ return (EINVAL);
+ };
+
+ vsize = round_page (-(data & 0xfffffff0));
+
+ printf (" memory size=0x%x", vsize);
+
+ if (!vsize) return (EINVAL);
+
+ /*
+ ** try to map device to virtual space
+ */
+
+ vaddr = vm_map_min (kernel_map);
+
+ result = vm_map_find (kernel_map, (void*)0, (vm_offset_t) 0,
+ &vaddr, vsize, TRUE);
+
+ if (result != KERN_SUCCESS) {
+ printf (" vm_map_find failed(%d)\n", result);
+ return (ENOMEM);
+ };
+
+ /*
+ ** align physical address to virtual size
+ */
+
+ if (data = pci_paddr % vsize)
+ pci_paddr += vsize - data;
+
+ /*
+ ** display values.
+ */
+
+ printf (" virtual=0x%x physical=0x%x\n", vaddr, pci_paddr);
+
+ *va = vaddr;
+ *pa = pci_paddr;
+
+ /*
+ ** set device address
+ */
+
+ pci_conf_write (tag, reg, pci_paddr);
+
+ /*
+ ** map physical
+ */
+
+ while (vsize >= NBPG) {
+ pmap_enter (pmap_kernel(), vaddr, pci_paddr,
+ VM_PROT_READ|VM_PROT_WRITE, TRUE);
+ vaddr += NBPG;
+ pci_paddr += NBPG;
+ vsize -= NBPG;
+ };
+
+ return (0);
+}
+#endif
diff --git a/sys/i386/pci/pci.h b/sys/i386/pci/pci.h
new file mode 100644
index 000000000000..703144d1eb8f
--- /dev/null
+++ b/sys/i386/pci/pci.h
@@ -0,0 +1,58 @@
+/**************************************************************************
+**
+** $Id: pci.h,v 2.0 94/07/10 15:53:30 wolf Rel $
+**
+** #define for pci bus device drivers
+**
+**-------------------------------------------------------------------------
+**
+** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany
+** <wolf@dentaro.GUN.de>
+**
+** This is a beta version - use with care.
+**
+**-------------------------------------------------------------------------
+**
+** $Log: pci.h,v $
+** Revision 2.0 94/07/10 15:53:30 wolf
+** FreeBSD release.
+**
+** Revision 1.0 94/06/07 20:02:21 wolf
+** Beta release.
+**
+***************************************************************************
+*/
+
+#ifndef __PCI_H__
+#define __PCI_H__
+
+/*
+** main pci initialization function.
+** called at boot time from autoconf.c
+*/
+
+void pci_configure(void);
+
+/*
+** pci configuration id
+**
+** is constructed from: bus, device & function numbers.
+*/
+
+typedef union {
+ u_long cfg1;
+ struct {
+ u_char enable;
+ u_char forward;
+ u_short port;
+ } cfg2;
+ } pcici_t;
+
+/*
+** Each pci device has an unique device id.
+** It is used to find a matching driver.
+*/
+
+typedef u_long pcidi_t;
+
+#endif /*__PCI_H__*/
diff --git a/sys/i386/pci/pci_config.c b/sys/i386/pci/pci_config.c
new file mode 100644
index 000000000000..d1e2332191f2
--- /dev/null
+++ b/sys/i386/pci/pci_config.c
@@ -0,0 +1,45 @@
+/**************************************************************************
+**
+** $Id: pci_config.c,v 2.0 94/07/10 15:53:30 wolf Rel $
+**
+** @PCI@ this should be part of "ioconf.c".
+**
+** The config-utility should build it!
+** When struct pci_driver has become stable
+** I'll extend the config utility.
+**
+**-------------------------------------------------------------------------
+**
+** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany
+** <wolf@dentaro.GUN.de>
+**
+** This is a beta version - use with care.
+**
+**-------------------------------------------------------------------------
+**
+** $Log: pci_config.c,v $
+** Revision 2.0 94/07/10 15:53:30 wolf
+** FreeBSD release.
+**
+** Revision 1.0 94/06/07 20:04:37 wolf
+** Beta release.
+**
+***************************************************************************
+*/
+
+#include "types.h"
+#include "i386/pci/pci.h"
+#include "i386/pci/pci_device.h"
+
+#include "ncr.h"
+#if NNCR>0
+extern struct pci_driver ncrdevice;
+#endif
+
+struct pci_device pci_devtab[] = {
+
+#if NNCR>0
+ {&ncrdevice},
+#endif
+ {0}
+};
diff --git a/sys/i386/pci/pci_device.h b/sys/i386/pci/pci_device.h
new file mode 100644
index 000000000000..5fc33216a5a5
--- /dev/null
+++ b/sys/i386/pci/pci_device.h
@@ -0,0 +1,88 @@
+/**************************************************************************
+**
+** $Id: pci_device.h,v 2.0 94/07/10 15:53:31 wolf Rel $
+**
+** #define for pci based device drivers
+**
+**-------------------------------------------------------------------------
+**
+** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany
+** <wolf@dentaro.GUN.de>
+**
+** This is a beta version - use with care.
+**
+**-------------------------------------------------------------------------
+**
+** $Log: pci_device.h,v $
+** Revision 2.0 94/07/10 15:53:31 wolf
+** FreeBSD release.
+**
+** Revision 1.0 94/06/07 20:02:22 wolf
+** Beta release.
+**
+***************************************************************************
+*/
+
+#ifndef __PCI_DEVICE_H__
+#define __PCI_DEVICE_H__
+
+/*------------------------------------------------------------
+**
+** Per driver structure.
+**
+**------------------------------------------------------------
+*/
+
+struct pci_driver {
+ int (*probe )(pcici_t pci_ident); /* test whether device is present */
+ int (*attach)(pcici_t pci_ident); /* setup driver for a device */
+ pcidi_t device_id; /* device pci id */
+ char *name; /* device name */
+ char *vendor; /* device long name */
+ int (*intr)(int); /* interupt handler */
+};
+
+/*-----------------------------------------------------------
+**
+** Per device structure.
+**
+** It is initialized by the config utility and should live in
+** "ioconf.c". At the moment there is only one field.
+**
+** This is a first attempt to include the pci bus to 386bsd.
+** So this structure may grow ..
+**
+**-----------------------------------------------------------
+*/
+
+struct pci_device {
+ struct pci_driver * pd_driver;
+};
+
+/*-----------------------------------------------------------
+**
+** This table should be generated in file "ioconf.c"
+** by the config program.
+** It is used at boot time by the configuration function
+** pci_configure()
+**
+**-----------------------------------------------------------
+*/
+
+extern struct pci_device pci_devtab[];
+
+/*-----------------------------------------------------------
+**
+** This functions may be used by drivers to map devices
+** to virtual and physical addresses. The va and pa
+** addresses are "in/out" parameters. If they are 0
+** on entry, the mapping function assigns an address.
+**
+**-----------------------------------------------------------
+*/
+
+int pci_map_mem (pcici_t tag, u_long entry, u_long * va, u_long * pa);
+
+int pci_map_port(pcici_t tag, u_long entry, u_short * pa);
+
+#endif /*__PCI_DEVICE_H__*/
diff --git a/sys/i386/pci/pcibios.c b/sys/i386/pci/pcibios.c
new file mode 100644
index 000000000000..049d6e77a057
--- /dev/null
+++ b/sys/i386/pci/pcibios.c
@@ -0,0 +1,261 @@
+/**************************************************************************
+**
+** $Id: pcibios.c,v 2.0 94/07/10 15:53:31 wolf Rel $
+**
+** #define for pci-bus bios functions.
+**
+**-------------------------------------------------------------------------
+**
+** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany
+** <wolf@dentaro.GUN.de>
+**
+** This is a beta version - use with care.
+**
+**-------------------------------------------------------------------------
+**
+** $Log: pcibios.c,v $
+** Revision 2.0 94/07/10 15:53:31 wolf
+** FreeBSD release.
+**
+** Revision 1.0 94/06/07 20:02:20 wolf
+** Beta release.
+**
+***************************************************************************
+*/
+
+
+#include "types.h"
+#include "i386/isa/isa.h"
+#include "i386/pci/pci.h"
+#include "i386/pci/pcibios.h"
+
+
+extern int printf();
+
+static char pci_mode;
+
+static char ident[] =
+ "\n$Id: pcibios.c,v 2.0 94/07/10 15:53:31 wolf Rel $\n"
+ "Copyright (c) 1994, Wolfgang Stanglmeier\n";
+
+
+/*--------------------------------------------------------------------
+**
+** Port access
+**
+**--------------------------------------------------------------------
+**
+** @FREEBSD@ inl() and outl() functions are not defined
+*/
+
+#define DIRTY
+
+#ifdef DIRTY
+
+#undef inl
+#define inl(port) \
+({ u_long data; \
+ __asm __volatile("inl %1, %0": "=a" (data): "d" ((u_short)(port))); \
+ data; })
+
+
+#undef outl
+#define outl(port, data) \
+{__asm __volatile("outl %0, %1"::"a" ((u_long)(data)), "d" ((u_short)(port)));}
+
+
+#undef inb
+#define inb(port) \
+({ u_char data; \
+ __asm __volatile("inb %1, %0": "=a" (data): "d" ((u_short)(port))); \
+ data; })
+
+
+#undef outb
+#define outb(port, data) \
+{__asm __volatile("outb %0, %1"::"a" ((u_char)(data)), "d" ((u_short)(port)));}
+
+#endif
+
+/*--------------------------------------------------------------------
+**
+** Determine configuration mode
+**
+**--------------------------------------------------------------------
+*/
+
+
+#define CONF1_ENABLE 0x80000000ul
+#define CONF1_ADDR_PORT 0x0cf8
+#define CONF1_DATA_PORT 0x0cfc
+
+
+#define CONF2_ENABLE_PORT 0x0cf8
+#define CONF2_FORWARD_PORT 0x0cfa
+
+
+int pci_conf_mode (void)
+{
+ u_long result, oldval;
+
+ /*---------------------------------------
+ ** Configuration mode 1 ?
+ **---------------------------------------
+ */
+
+ oldval = inl (CONF1_ADDR_PORT);
+ outl (CONF1_ADDR_PORT, CONF1_ENABLE);
+ result = inl (CONF1_ADDR_PORT);
+ outl (CONF1_ADDR_PORT, oldval);
+
+ if (result == CONF1_ENABLE) {
+ pci_mode = 1;
+ return (1);
+ };
+
+ /*---------------------------------------
+ ** Configuration mode 2 ?
+ **---------------------------------------
+ */
+
+ outb (CONF2_ENABLE_PORT, 0);
+ outb (CONF2_FORWARD_PORT, 0);
+ if (!inb (CONF2_ENABLE_PORT) && !inb (CONF2_FORWARD_PORT)) {
+ pci_mode = 2;
+ return (2);
+ };
+
+ /*---------------------------------------
+ ** No PCI bus available.
+ **---------------------------------------
+ */
+ return (0);
+}
+
+/*--------------------------------------------------------------------
+**
+** Build a pcitag from bus, device and function number
+**
+**--------------------------------------------------------------------
+*/
+
+
+pcici_t pcitag (unsigned char bus,
+ unsigned char device,
+ unsigned char func)
+{
+ pcici_t tag;
+
+ tag.cfg1 = 0;
+ if (device >= 32) return tag;
+ if (func >= 8) return tag;
+
+ switch (pci_mode) {
+
+ case 1:
+ tag.cfg1 = CONF1_ENABLE
+ | (((u_long) bus ) << 16ul)
+ | (((u_long) device) << 11ul)
+ | (((u_long) func ) << 8ul);
+ break;
+ case 2:
+ if (device >= 16) break;
+ tag.cfg2.port = 0xc000 | (device << 8ul);
+ tag.cfg2.enable = 0xf1 | (func << 1ul);
+ tag.cfg2.forward = bus;
+ break;
+ };
+ return tag;
+}
+
+/*--------------------------------------------------------------------
+**
+** Read register from configuration space.
+**
+**--------------------------------------------------------------------
+*/
+
+
+u_long pci_conf_read (pcici_t tag, u_long reg)
+{
+ u_long addr, data = 0;
+
+ if (!tag.cfg1) return (0xfffffffful);
+
+ switch (pci_mode) {
+
+ 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.
+**
+**--------------------------------------------------------------------
+*/
+
+
+void pci_conf_write (pcici_t tag, u_long reg, u_long data)
+{
+ u_long addr;
+
+ if (!tag.cfg1) return;
+
+ switch (pci_mode) {
+
+ 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;
+ };
+}
diff --git a/sys/i386/pci/pcibios.h b/sys/i386/pci/pcibios.h
new file mode 100644
index 000000000000..5e82d2f27b34
--- /dev/null
+++ b/sys/i386/pci/pcibios.h
@@ -0,0 +1,53 @@
+/**************************************************************************
+**
+** $Id: pcibios.h,v 2.0 94/07/10 15:53:32 wolf Rel $
+**
+** #define for pci-bus bios functions.
+**
+**-------------------------------------------------------------------------
+**
+** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany
+** <wolf@dentaro.GUN.de>
+**
+** This is a beta version - use with care.
+**
+**-------------------------------------------------------------------------
+**
+** $Log: pcibios.h,v $
+** Revision 2.0 94/07/10 15:53:32 wolf
+** FreeBSD release.
+**
+** Revision 1.0 94/06/07 20:02:23 wolf
+** Beta release.
+**
+***************************************************************************
+*/
+
+#ifndef __PCIBIOS_H__
+#define __PCIBIOS_H__
+
+/*
+** the availability of a pci bus.
+** configuration mode (1 or 2)
+** 0 if no pci bus found.
+*/
+
+int pci_conf_mode (void);
+
+/*
+** get a "ticket" for accessing a pci device
+** configuration space.
+*/
+
+pcici_t pcitag (unsigned char bus,
+ unsigned char device,
+ unsigned char func);
+
+/*
+** read or write the configuration space.
+*/
+
+u_long pci_conf_read (pcici_t tag, u_long reg );
+void pci_conf_write (pcici_t tag, u_long reg, u_long data);
+
+#endif
diff --git a/sys/isofs/isofs_rrip.c b/sys/isofs/isofs_rrip.c
index 2ed6d50bc297..5568f024e566 100644
--- a/sys/isofs/isofs_rrip.c
+++ b/sys/isofs/isofs_rrip.c
@@ -28,7 +28,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: isofs_rrip.c,v 1.5 1993/12/19 00:51:05 wollman Exp $
+ * $Id: isofs_rrip.c,v 1.6 1994/06/13 20:19:35 jkh Exp $
*/
#include "param.h"
@@ -193,7 +193,8 @@ struct timeval *pu;
if (year < 0) {
crtime = 0;
} else {
- int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
+ static int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
+
days = year * 365;
if (year > 2)
days += (year+2) / 4;
@@ -211,7 +212,7 @@ struct timeval *pu;
/* timezone offset is unreliable on some disks */
if (-48 <= tz && tz <= 52)
- crtime += tz * 15 * 60;
+ crtime -= tz * 15 * 60;
}
pu->tv_sec = crtime;
pu->tv_usec = 0;
diff --git a/sys/isofs/isofs_vfsops.c b/sys/isofs/isofs_vfsops.c
index 77bf963fb34b..bc9b337b87e8 100644
--- a/sys/isofs/isofs_vfsops.c
+++ b/sys/isofs/isofs_vfsops.c
@@ -1,5 +1,5 @@
/*
- * $Id: isofs_vfsops.c,v 1.5 1993/12/19 00:51:07 wollman Exp $
+ * $Id: isofs_vfsops.c,v 1.6 1994/06/02 06:48:34 swallace Exp $
*/
#include "param.h"
@@ -521,7 +521,7 @@ isofs_fhtovp(mp, fhp, vpp)
return (EINVAL);
if (ifhp->ifid_offset + sizeof (struct iso_directory_record)
- >= imp->im_bsize)
+ > imp->im_bsize)
return (EINVAL);
if (error = bread (imp->im_devvp,
@@ -534,7 +534,7 @@ isofs_fhtovp(mp, fhp, vpp)
dirp = (struct iso_directory_record *)
(bp->b_un.b_addr + ifhp->ifid_offset);
- if (ifhp->ifid_offset + isonum_711 (dirp->length) >= imp->im_bsize) {
+ if (ifhp->ifid_offset + isonum_711 (dirp->length) > imp->im_bsize) {
brelse (bp);
return (EINVAL);
}
diff --git a/sys/isofs/isofs_vnops.c b/sys/isofs/isofs_vnops.c
index b3034881b8d2..bcb5e99e6c00 100644
--- a/sys/isofs/isofs_vnops.c
+++ b/sys/isofs/isofs_vnops.c
@@ -1,5 +1,5 @@
/*
- * $Id: isofs_vnops.c,v 1.4 1993/12/19 00:51:08 wollman Exp $
+ * $Id: isofs_vnops.c,v 1.6 1994/06/12 04:05:29 davidg Exp $
*/
#include "param.h"
#include "systm.h"
@@ -82,10 +82,13 @@ isofs_getattr(vp, vap, cred, p)
vap->va_fsid = ip->i_dev;
vap->va_fileid = ip->i_number;
- if (vp->v_type == VDIR)
- vap->va_nlink = 2;
- else
- vap->va_nlink = 1;
+ /*
+ * This should be set properly if a RR filesystem, but this is
+ * the safest value for now. Note that previously this was conditionally
+ * set to 2 if the file is a directory, but this causes problems
+ * with find.
+ */
+ vap->va_nlink = 1;
vap->va_mode = ip->inode.iso_mode;
vap->va_uid = ip->inode.iso_uid;
@@ -162,8 +165,6 @@ isofs_read(vp, uio, ioflag, cred)
}
error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
- if (n + on == imp->im_bsize || uio->uio_offset == ip->i_size)
- bp->b_flags |= B_AGE;
brelse(bp);
} while (error == 0 && uio->uio_resid > 0 && n != 0);
return (error);
diff --git a/sys/kern/imgact_aout.c b/sys/kern/imgact_aout.c
index 8bfb19cfe28d..8837764c2735 100644
--- a/sys/kern/imgact_aout.c
+++ b/sys/kern/imgact_aout.c
@@ -28,7 +28,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: imgact_aout.c,v 1.3 1993/12/30 01:39:29 davidg Exp $
+ * $Id: imgact_aout.c,v 1.4 1994/03/17 22:21:02 davidg Exp $
*/
#include "param.h"
@@ -151,9 +151,7 @@ exec_aout_imgact(iparams)
&vmaddr,
a_out->a_data,
VM_PROT_READ | VM_PROT_WRITE | (a_out->a_text ? 0 : VM_PROT_EXECUTE),
- VM_PROT_READ | VM_PROT_WRITE | (a_out->a_text ? 0 : VM_PROT_EXECUTE),
- MAP_FILE | MAP_PRIVATE | MAP_FIXED,
- iparams->vnodep,
+ VM_PROT_ALL, MAP_FILE | MAP_PRIVATE | MAP_FIXED, iparams->vnodep,
file_offset + a_out->a_text);
if (error)
return (error);
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c
index ce2f1bf8065f..9624bb3482b3 100644
--- a/sys/kern/init_main.c
+++ b/sys/kern/init_main.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)init_main.c 7.41 (Berkeley) 5/15/91
- * $Id: init_main.c,v 1.14 1994/01/14 16:24:45 davidg Exp $
+ * $Id: init_main.c,v 1.18 1994/06/16 02:55:28 davidg Exp $
*/
#include "param.h"
@@ -77,7 +77,7 @@ struct filedesc0 filedesc0;
struct plimit limit0;
struct vmspace vmspace0;
struct proc *curproc = &proc0;
-struct proc *initproc, *pageproc;
+struct proc *initproc, *pageproc, *pagescanproc, *updateproc;
int cmask = CMASK;
extern struct user *proc0paddr;
@@ -86,8 +86,6 @@ extern int (*mountroot)();
struct vnode *rootvp, *swapdev_vp;
int boothowto;
-struct proc *updateproc;
-
#if __GNUC__ >= 2
void __main() {}
#endif
@@ -96,6 +94,10 @@ void __main() {}
* This table is filled in by the linker with functions that need to be
* called to initialize various pseudo-devices and whatnot.
*/
+
+static void dummyinit() {}
+TEXT_SET(pseudo_set, dummyinit);
+
typedef void (*pseudo_func_t)(void);
extern const struct linker_set pseudo_set;
static const pseudo_func_t *pseudos =
@@ -351,7 +353,7 @@ main()
* Start up pageout daemon (process 2).
*/
if (fork(p, (void *) NULL, rval))
- panic("fork pager");
+ panic("failed fork pageout daemon");
if (rval[1]) {
/*
* Now in process 2.
@@ -364,11 +366,28 @@ main()
/*NOTREACHED*/
}
+#if 0
+ /*
+ * Start page scanner daemon (process 3).
+ */
+ if (fork(p, (void *) NULL, rval))
+ panic("failed fork page scanner daemon");
+ if (rval[1]) {
+ p = curproc;
+ pagescanproc = p;
+ p->p_flag |= SLOAD|SSYS;
+ bcopy("pagescan", p->p_comm, sizeof("pagescan"));
+ vm_pagescan();
+ /*NOTREACHED*/
+ }
+#endif
+
/*
- * Start update daemon (process 3).
+ * Start update daemon (process 4).
*/
+#ifndef LAPTOP
if (fork(p, (void *) NULL, rval))
- panic("fork update");
+ panic("failed fork update daemon");
if (rval[1]) {
p = curproc;
updateproc = p;
@@ -377,6 +396,7 @@ main()
vfs_update();
/*NOTREACHED*/
}
+#endif
/*
* enter scheduling loop
diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c
index 97056b0c3bbc..55a5b1e3263f 100644
--- a/sys/kern/init_sysent.c
+++ b/sys/kern/init_sysent.c
@@ -2,7 +2,7 @@
* System call switch table.
*
* DO NOT EDIT-- this file is automatically generated.
- * created from $Id: syscalls.master,v 1.8 1994/01/31 10:27:25 davidg Exp $
+ * created from $Id: syscalls.master,v 1.9 1994/03/15 01:58:33 wollman Exp $
*/
#include "param.h"
@@ -174,6 +174,8 @@ int msgsys();
int shmsys();
#else
#endif
+int ntp_gettime();
+int ntp_adjtime();
#ifdef MACHVMCOMPAT
int svm_allocate();
int svm_deallocate();
@@ -453,8 +455,8 @@ struct sysent sysent[] = {
0, nosys, /* 172 = nosys */
0, nosys, /* 173 = nosys */
0, nosys, /* 174 = nosys */
- 0, nosys, /* 175 = nosys */
- 0, nosys, /* 176 = nosys */
+ 1, ntp_gettime, /* 175 = ntp_gettime */
+ 1, ntp_adjtime, /* 176 = ntp_adjtime */
#ifdef MACHVMCOMPAT
4, svm_allocate, /* 177 = vm_allocate */
3, svm_deallocate, /* 178 = vm_deallocate */
diff --git a/sys/kern/kern_acct.c b/sys/kern/kern_acct.c
index 6d2c0e6536c7..386886e9926a 100644
--- a/sys/kern/kern_acct.c
+++ b/sys/kern/kern_acct.c
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)kern_acct.c 7.18 (Berkeley) 5/11/91
- * $Id: kern_acct.c,v 1.9.2.1 1994/05/04 07:54:32 rgrimes Exp $
+ * $Id: kern_acct.c,v 1.10 1994/05/04 08:26:46 rgrimes Exp $
*/
#include "param.h"
diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c
index 44a334e00ebc..8df606e4dc72 100644
--- a/sys/kern/kern_clock.c
+++ b/sys/kern/kern_clock.c
@@ -31,9 +31,27 @@
* SUCH DAMAGE.
*
* from: @(#)kern_clock.c 7.16 (Berkeley) 5/9/91
- * $Id: kern_clock.c,v 1.11 1993/12/19 00:51:20 wollman Exp $
+ * $Id: kern_clock.c,v 1.16 1994/04/21 20:39:30 wollman Exp $
*/
+/* Portions of this software are covered by the following: */
+/******************************************************************************
+ * *
+ * Copyright (c) David L. Mills 1993, 1994 *
+ * *
+ * 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 University of Delaware not be used in *
+ * advertising or publicity pertaining to distribution of the software *
+ * without specific, written prior permission. The University of Delaware *
+ * makes no representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied warranty. *
+ * *
+ *****************************************************************************/
+
+
#include "param.h"
#include "systm.h"
#include "dkstat.h"
@@ -42,6 +60,7 @@
#include "proc.h"
#include "signalvar.h"
#include "resourcevar.h"
+#include "timex.h"
#include "machine/cpu.h"
@@ -93,6 +112,238 @@ int ncallout;
}
/*
+ * Phase-lock loop (PLL) definitions
+ *
+ * The following variables are read and set by the ntp_adjtime() system
+ * call.
+ *
+ * time_state shows the state of the system clock, with values defined
+ * in the timex.h header file.
+ *
+ * time_status shows the status of the system clock, with bits defined
+ * in the timex.h header file.
+ *
+ * time_offset is used by the PLL to adjust the system time in small
+ * increments.
+ *
+ * time_constant determines the bandwidth or "stiffness" of the PLL.
+ *
+ * time_tolerance determines maximum frequency error or tolerance of the
+ * CPU clock oscillator and is a property of the architecture; however,
+ * in principle it could change as result of the presence of external
+ * discipline signals, for instance.
+ *
+ * time_precision is usually equal to the kernel tick variable; however,
+ * in cases where a precision clock counter or external clock is
+ * available, the resolution can be much less than this and depend on
+ * whether the external clock is working or not.
+ *
+ * time_maxerror is initialized by a ntp_adjtime() call and increased by
+ * the kernel once each second to reflect the maximum error
+ * bound growth.
+ *
+ * time_esterror is set and read by the ntp_adjtime() call, but
+ * otherwise not used by the kernel.
+ */
+int time_status = STA_UNSYNC; /* clock status bits */
+int time_state = TIME_OK; /* clock state */
+long time_offset = 0; /* time offset (us) */
+long time_constant = 0; /* pll time constant */
+long time_tolerance = MAXFREQ; /* frequency tolerance (scaled ppm) */
+long time_precision = 1; /* clock precision (us) */
+long time_maxerror = MAXPHASE; /* maximum error (us) */
+long time_esterror = MAXPHASE; /* estimated error (us) */
+
+/*
+ * The following variables establish the state of the PLL and the
+ * residual time and frequency offset of the local clock. The scale
+ * factors are defined in the timex.h header file.
+ *
+ * time_phase and time_freq are the phase increment and the frequency
+ * increment, respectively, of the kernel time variable at each tick of
+ * the clock.
+ *
+ * time_freq is set via ntp_adjtime() from a value stored in a file when
+ * the synchronization daemon is first started. Its value is retrieved
+ * via ntp_adjtime() and written to the file about once per hour by the
+ * daemon.
+ *
+ * time_adj is the adjustment added to the value of tick at each timer
+ * interrupt and is recomputed at each timer interrupt.
+ *
+ * time_reftime is the second's portion of the system time on the last
+ * call to ntp_adjtime(). It is used to adjust the time_freq variable
+ * and to increase the time_maxerror as the time since last update
+ * increases.
+ */
+long time_phase = 0; /* phase offset (scaled us) */
+long time_freq = 0; /* frequency offset (scaled ppm) */
+long time_adj = 0; /* tick adjust (scaled 1 / hz) */
+long time_reftime = 0; /* time at last adjustment (s) */
+
+#ifdef PPS_SYNC
+/*
+ * The following variables are used only if the if the kernel PPS
+ * discipline code is configured (PPS_SYNC). The scale factors are
+ * defined in the timex.h header file.
+ *
+ * pps_time contains the time at each calibration interval, as read by
+ * microtime().
+ *
+ * pps_offset is the time offset produced by the time median filter
+ * pps_tf[], while pps_jitter is the dispersion measured by this
+ * filter.
+ *
+ * pps_freq is the frequency offset produced by the frequency median
+ * filter pps_ff[], while pps_stabil is the dispersion measured by
+ * this filter.
+ *
+ * pps_usec is latched from a high resolution counter or external clock
+ * at pps_time. Here we want the hardware counter contents only, not the
+ * contents plus the time_tv.usec as usual.
+ *
+ * pps_valid counts the number of seconds since the last PPS update. It
+ * is used as a watchdog timer to disable the PPS discipline should the
+ * PPS signal be lost.
+ *
+ * pps_glitch counts the number of seconds since the beginning of an
+ * offset burst more than tick/2 from current nominal offset. It is used
+ * mainly to suppress error bursts due to priority conflicts between the
+ * PPS interrupt and timer interrupt.
+ *
+ * pps_count counts the seconds of the calibration interval, the
+ * duration of which is pps_shift in powers of two.
+ *
+ * pps_intcnt counts the calibration intervals for use in the interval-
+ * adaptation algorithm. It's just too complicated for words.
+ */
+struct timeval pps_time; /* kernel time at last interval */
+long pps_offset = 0; /* pps time offset (us) */
+long pps_jitter = MAXTIME; /* pps time dispersion (jitter) (us) */
+long pps_tf[] = {0, 0, 0}; /* pps time offset median filter (us) */
+long pps_freq = 0; /* frequency offset (scaled ppm) */
+long pps_stabil = MAXFREQ; /* frequency dispersion (scaled ppm) */
+long pps_ff[] = {0, 0, 0}; /* frequency offset median filter */
+long pps_usec = 0; /* microsec counter at last interval */
+long pps_valid = PPS_VALID; /* pps signal watchdog counter */
+int pps_glitch = 0; /* pps signal glitch counter */
+int pps_count = 0; /* calibration interval counter (s) */
+int pps_shift = PPS_SHIFT; /* interval duration (s) (shift) */
+int pps_intcnt = 0; /* intervals at current duration */
+
+/*
+ * PPS signal quality monitors
+ *
+ * pps_jitcnt counts the seconds that have been discarded because the
+ * jitter measured by the time median filter exceeds the limit MAXTIME
+ * (100 us).
+ *
+ * pps_calcnt counts the frequency calibration intervals, which are
+ * variable from 4 s to 256 s.
+ *
+ * pps_errcnt counts the calibration intervals which have been discarded
+ * because the wander exceeds the limit MAXFREQ (100 ppm) or where the
+ * calibration interval jitter exceeds two ticks.
+ *
+ * pps_stbcnt counts the calibration intervals that have been discarded
+ * because the frequency wander exceeds the limit MAXFREQ / 4 (25 us).
+ */
+long pps_jitcnt = 0; /* jitter limit exceeded */
+long pps_calcnt = 0; /* calibration intervals */
+long pps_errcnt = 0; /* calibration errors */
+long pps_stbcnt = 0; /* stability limit exceeded */
+#endif /* PPS_SYNC */
+
+/* XXX none of this stuff works under FreeBSD */
+#ifdef EXT_CLOCK
+/*
+ * External clock definitions
+ *
+ * The following definitions and declarations are used only if an
+ * external clock (HIGHBALL or TPRO) is configured on the system.
+ */
+#define CLOCK_INTERVAL 30 /* CPU clock update interval (s) */
+
+/*
+ * The clock_count variable is set to CLOCK_INTERVAL at each PPS
+ * interrupt and decremented once each second.
+ */
+int clock_count = 0; /* CPU clock counter */
+
+#ifdef HIGHBALL
+/*
+ * The clock_offset and clock_cpu variables are used by the HIGHBALL
+ * interface. The clock_offset variable defines the offset between
+ * system time and the HIGBALL counters. The clock_cpu variable contains
+ * the offset between the system clock and the HIGHBALL clock for use in
+ * disciplining the kernel time variable.
+ */
+extern struct timeval clock_offset; /* Highball clock offset */
+long clock_cpu = 0; /* CPU clock adjust */
+#endif /* HIGHBALL */
+#endif /* EXT_CLOCK */
+
+/*
+ * hardupdate() - local clock update
+ *
+ * This routine is called by ntp_adjtime() to update the local clock
+ * phase and frequency. This is used to implement an adaptive-parameter,
+ * first-order, type-II phase-lock loop. The code computes new time and
+ * frequency offsets each time it is called. The hardclock() routine
+ * amortizes these offsets at each tick interrupt. If the kernel PPS
+ * discipline code is configured (PPS_SYNC), the PPS signal itself
+ * determines the new time offset, instead of the calling argument.
+ * Presumably, calls to ntp_adjtime() occur only when the caller
+ * believes the local clock is valid within some bound (+-128 ms with
+ * NTP). If the caller's time is far different than the PPS time, an
+ * argument will ensue, and it's not clear who will lose.
+ *
+ * For default SHIFT_UPDATE = 12, the offset is limited to +-512 ms, the
+ * maximum interval between updates is 4096 s and the maximum frequency
+ * offset is +-31.25 ms/s.
+ *
+ * Note: splclock() is in effect.
+ */
+void
+hardupdate(offset)
+ long offset;
+{
+ long ltemp, mtemp;
+
+ if (!(time_status & STA_PLL) && !(time_status & STA_PPSTIME))
+ return;
+ ltemp = offset;
+#ifdef PPS_SYNC
+ if (time_status & STA_PPSTIME && time_status & STA_PPSSIGNAL)
+ ltemp = pps_offset;
+#endif /* PPS_SYNC */
+ if (ltemp > MAXPHASE)
+ time_offset = MAXPHASE << SHIFT_UPDATE;
+ else if (ltemp < -MAXPHASE)
+ time_offset = -(MAXPHASE << SHIFT_UPDATE);
+ else
+ time_offset = ltemp << SHIFT_UPDATE;
+ mtemp = time.tv_sec - time_reftime;
+ time_reftime = time.tv_sec;
+ if (mtemp > MAXSEC)
+ mtemp = 0;
+
+ /* ugly multiply should be replaced */
+ if (ltemp < 0)
+ time_freq -= (-ltemp * mtemp) >> (time_constant +
+ time_constant + SHIFT_KF - SHIFT_USEC);
+ else
+ time_freq += (ltemp * mtemp) >> (time_constant +
+ time_constant + SHIFT_KF - SHIFT_USEC);
+ if (time_freq > time_tolerance)
+ time_freq = time_tolerance;
+ else if (time_freq < -time_tolerance)
+ time_freq = -time_tolerance;
+}
+
+
+
+/*
* The hz hardware interval timer.
* We update the events relating to real time.
* If this timer is also being used to gather statistics,
@@ -111,6 +362,7 @@ hardclock(frame)
int needsoft = 0;
extern int tickdelta;
extern long timedelta;
+ long ltemp, time_update = 0;
/*
* Update real-time timeout queue.
@@ -234,49 +486,164 @@ hardclock(frame)
* so we don't keep the relatively high clock interrupt
* priority any longer than necessary.
*/
- if (timedelta == 0)
- BUMPTIME(&time, tick)
- else {
- register delta;
-
- if (timedelta < 0) {
- delta = tick - tickdelta;
- timedelta += tickdelta;
+ {
+ int time_update;
+ if (timedelta == 0) {
+ time_update = tick;
} else {
- delta = tick + tickdelta;
- timedelta -= tickdelta;
+ if (timedelta < 0) {
+ time_update = tick - tickdelta;
+ timedelta += tickdelta;
+ } else {
+ time_update = tick + tickdelta;
+ timedelta -= tickdelta;
+ }
+ }
+ /*
+ * Compute the phase adjustment. If the low-order bits
+ * (time_phase) of the update overflow, bump the high-order bits
+ * (time_update).
+ */
+ time_phase += time_adj;
+ if (time_phase <= -FINEUSEC) {
+ ltemp = -time_phase >> SHIFT_SCALE;
+ time_phase += ltemp << SHIFT_SCALE;
+ time_update -= ltemp;
+ }
+ else if (time_phase >= FINEUSEC) {
+ ltemp = time_phase >> SHIFT_SCALE;
+ time_phase -= ltemp << SHIFT_SCALE;
+ time_update += ltemp;
+ }
+
+ time.tv_usec += time_update;
+ /*
+ * On rollover of the second the phase adjustment to be used for
+ * the next second is calculated. Also, the maximum error is
+ * increased by the tolerance. If the PPS frequency discipline
+ * code is present, the phase is increased to compensate for the
+ * CPU clock oscillator frequency error.
+ *
+ * With SHIFT_SCALE = 23, the maximum frequency adjustment is
+ * +-256 us per tick, or 25.6 ms/s at a clock frequency of 100
+ * Hz. The time contribution is shifted right a minimum of two
+ * bits, while the frequency contribution is a right shift.
+ * Thus, overflow is prevented if the frequency contribution is
+ * limited to half the maximum or 15.625 ms/s.
+ */
+ if (time.tv_usec >= 1000000) {
+ time.tv_usec -= 1000000;
+ time.tv_sec++;
+ time_maxerror += time_tolerance >> SHIFT_USEC;
+ if (time_offset < 0) {
+ ltemp = -time_offset >>
+ (SHIFT_KG + time_constant);
+ time_offset += ltemp;
+ time_adj = -ltemp <<
+ (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
+ } else {
+ ltemp = time_offset >>
+ (SHIFT_KG + time_constant);
+ time_offset -= ltemp;
+ time_adj = ltemp <<
+ (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
+ }
+#ifdef PPS_SYNC
+ /*
+ * Gnaw on the watchdog counter and update the frequency
+ * computed by the pll and the PPS signal.
+ */
+ pps_valid++;
+ if (pps_valid == PPS_VALID) {
+ pps_jitter = MAXTIME;
+ pps_stabil = MAXFREQ;
+ time_status &= ~(STA_PPSSIGNAL | STA_PPSJITTER |
+ STA_PPSWANDER | STA_PPSERROR);
+ }
+ ltemp = time_freq + pps_freq;
+#else
+ ltemp = time_freq;
+#endif /* PPS_SYNC */
+ if (ltemp < 0)
+ time_adj -= -ltemp >>
+ (SHIFT_USEC + SHIFT_HZ - SHIFT_SCALE);
+ else
+ time_adj += ltemp >>
+ (SHIFT_USEC + SHIFT_HZ - SHIFT_SCALE);
+
+ /*
+ * When the CPU clock oscillator frequency is not a
+ * power of two in Hz, the SHIFT_HZ is only an
+ * approximate scale factor. In the SunOS kernel, this
+ * results in a PLL gain factor of 1/1.28 = 0.78 what it
+ * should be. In the following code the overall gain is
+ * increased by a factor of 1.25, which results in a
+ * residual error less than 3 percent.
+ */
+ /* Same thing applies for FreeBSD --GAW */
+ if (hz == 100) {
+ if (time_adj < 0)
+ time_adj -= -time_adj >> 2;
+ else
+ time_adj += time_adj >> 2;
+ }
+
+ /* XXX - this is really bogus, but can't be fixed until
+ xntpd's idea of the system clock is fixed to know how
+ the user wants leap seconds handled; in the mean time,
+ we assume that users of NTP are running without proper
+ leap second support (this is now the default anyway) */
+ /*
+ * Leap second processing. If in leap-insert state at
+ * the end of the day, the system clock is set back one
+ * second; if in leap-delete state, the system clock is
+ * set ahead one second. The microtime() routine or
+ * external clock driver will insure that reported time
+ * is always monotonic. The ugly divides should be
+ * replaced.
+ */
+ switch (time_state) {
+
+ case TIME_OK:
+ if (time_status & STA_INS)
+ time_state = TIME_INS;
+ else if (time_status & STA_DEL)
+ time_state = TIME_DEL;
+ break;
+
+ case TIME_INS:
+ if (time.tv_sec % 86400 == 0) {
+ time.tv_sec--;
+ time_state = TIME_OOP;
+ }
+ break;
+
+ case TIME_DEL:
+ if ((time.tv_sec + 1) % 86400 == 0) {
+ time.tv_sec++;
+ time_state = TIME_WAIT;
+ }
+ break;
+
+ case TIME_OOP:
+ time_state = TIME_WAIT;
+ break;
+
+ case TIME_WAIT:
+ if (!(time_status & (STA_INS | STA_DEL)))
+ time_state = TIME_OK;
+ }
}
- BUMPTIME(&time, delta);
}
-#ifdef DCFCLK
- /*
- * This is lousy, but until I can get the $&^%&^(!!! signal onto one
- * of the interrupt's I'll have to poll it. No, it will not work if
- * you attempt -DHZ=1000, things break.
- * But keep the NDCFCLK low, to avoid waste of cycles...
- * phk@data.fls.dk
- */
- dcfclk_worker();
-#endif
if (needsoft) {
-#if 0
-/*
- * XXX - hardclock runs at splhigh, so the splsoftclock is useless and
- * softclock runs at splhigh as well if we do this. It is not much of
- * an optimization, since the "software interrupt" is done with a call
- * from doreti, and the overhead of checking there is sometimes less
- * than checking here. Moreover, the whole %$$%$^ frame is passed by
- * value here.
- */
if (CLKF_BASEPRI(&frame)) {
/*
* Save the overhead of a software interrupt;
* it will happen as soon as we return, so do it now.
*/
(void) splsoftclock();
- softclock(frame);
+ softclock(CLKF_USERMODE(&frame));
} else
-#endif
setsoftclock();
}
}
@@ -322,7 +689,7 @@ gatherstats(framep)
cpstate = CP_SYS;
if (curproc == NULL && CLKF_BASEPRI(framep))
cpstate = CP_IDLE;
-#ifdef GPROF
+#if defined(GPROF) && !defined(GUPROF)
s = (u_long) CLKF_PC(framep) - (u_long) s_lowpc;
if (profiling < 2 && s < s_textsize)
kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
@@ -343,10 +710,9 @@ gatherstats(framep)
* Software priority level clock interrupt.
* Run periodic events from timeout queue.
*/
-/*ARGSUSED*/
void
-softclock(frame)
- clockframe frame;
+softclock(usermode)
+ int usermode;
{
for (;;) {
@@ -377,11 +743,11 @@ softclock(frame)
* If trapped user-mode and profiling, give it
* a profiling tick.
*/
- if (CLKF_USERMODE(&frame)) {
+ if (usermode) {
register struct proc *p = curproc;
if (p->p_stats->p_prof.pr_scale)
- profile_tick(p, &frame);
+ profile_tick(p, unused was &frame);
/*
* Check to see if process has accumulated
* more than 10 minutes of user time. If so
@@ -515,3 +881,167 @@ hzto(tv)
ticks = CLOCK_T_MAX;
return (ticks);
}
+
+#ifdef PPS_SYNC
+/*
+ * hardpps() - discipline CPU clock oscillator to external pps signal
+ *
+ * This routine is called at each PPS interrupt in order to discipline
+ * the CPU clock oscillator to the PPS signal. It integrates successive
+ * phase differences between the two oscillators and calculates the
+ * frequency offset. This is used in hardclock() to discipline the CPU
+ * clock oscillator so that intrinsic frequency error is cancelled out.
+ * The code requires the caller to capture the time and hardware
+ * counter value at the designated PPS signal transition.
+ */
+void
+hardpps(tvp, usec)
+ struct timeval *tvp; /* time at PPS */
+ long usec; /* hardware counter at PPS */
+{
+ long u_usec, v_usec, bigtick;
+ long cal_sec, cal_usec;
+
+ /*
+ * During the calibration interval adjust the starting time when
+ * the tick overflows. At the end of the interval compute the
+ * duration of the interval and the difference of the hardware
+ * counters at the beginning and end of the interval. This code
+ * is deliciously complicated by the fact valid differences may
+ * exceed the value of tick when using long calibration
+ * intervals and small ticks. Note that the counter can be
+ * greater than tick if caught at just the wrong instant, but
+ * the values returned and used here are correct.
+ */
+ bigtick = (long)tick << SHIFT_USEC;
+ pps_usec -= ntp_pll.ybar;
+ if (pps_usec >= bigtick)
+ pps_usec -= bigtick;
+ if (pps_usec < 0)
+ pps_usec += bigtick;
+ pps_time.tv_sec++;
+ pps_count++;
+ if (pps_count < (1 << pps_shift))
+ return;
+ pps_count = 0;
+ ntp_pll.calcnt++;
+ u_usec = usec << SHIFT_USEC;
+ v_usec = pps_usec - u_usec;
+ if (v_usec >= bigtick >> 1)
+ v_usec -= bigtick;
+ if (v_usec < -(bigtick >> 1))
+ v_usec += bigtick;
+ if (v_usec < 0)
+ v_usec = -(-v_usec >> ntp_pll.shift);
+ else
+ v_usec = v_usec >> ntp_pll.shift;
+ pps_usec = u_usec;
+ cal_sec = tvp->tv_sec;
+ cal_usec = tvp->tv_usec;
+ cal_sec -= pps_time.tv_sec;
+ cal_usec -= pps_time.tv_usec;
+ if (cal_usec < 0) {
+ cal_usec += 1000000;
+ cal_sec--;
+ }
+ pps_time = *tvp;
+
+ /*
+ * Check for lost interrupts, noise, excessive jitter and
+ * excessive frequency error. The number of timer ticks during
+ * the interval may vary +-1 tick. Add to this a margin of one
+ * tick for the PPS signal jitter and maximum frequency
+ * deviation. If the limits are exceeded, the calibration
+ * interval is reset to the minimum and we start over.
+ */
+ u_usec = (long)tick << 1;
+ if (!((cal_sec == -1 && cal_usec > (1000000 - u_usec))
+ || (cal_sec == 0 && cal_usec < u_usec))
+ || v_usec > ntp_pll.tolerance || v_usec < -ntp_pll.tolerance) {
+ ntp_pll.jitcnt++;
+ ntp_pll.shift = NTP_PLL.SHIFT;
+ pps_dispinc = PPS_DISPINC;
+ ntp_pll.intcnt = 0;
+ return;
+ }
+
+ /*
+ * A three-stage median filter is used to help deglitch the pps
+ * signal. The median sample becomes the offset estimate; the
+ * difference between the other two samples becomes the
+ * dispersion estimate.
+ */
+ pps_mf[2] = pps_mf[1];
+ pps_mf[1] = pps_mf[0];
+ pps_mf[0] = v_usec;
+ if (pps_mf[0] > pps_mf[1]) {
+ if (pps_mf[1] > pps_mf[2]) {
+ u_usec = pps_mf[1]; /* 0 1 2 */
+ v_usec = pps_mf[0] - pps_mf[2];
+ } else if (pps_mf[2] > pps_mf[0]) {
+ u_usec = pps_mf[0]; /* 2 0 1 */
+ v_usec = pps_mf[2] - pps_mf[1];
+ } else {
+ u_usec = pps_mf[2]; /* 0 2 1 */
+ v_usec = pps_mf[0] - pps_mf[1];
+ }
+ } else {
+ if (pps_mf[1] < pps_mf[2]) {
+ u_usec = pps_mf[1]; /* 2 1 0 */
+ v_usec = pps_mf[2] - pps_mf[0];
+ } else if (pps_mf[2] < pps_mf[0]) {
+ u_usec = pps_mf[0]; /* 1 0 2 */
+ v_usec = pps_mf[1] - pps_mf[2];
+ } else {
+ u_usec = pps_mf[2]; /* 1 2 0 */
+ v_usec = pps_mf[1] - pps_mf[0];
+ }
+ }
+
+ /*
+ * Here the dispersion average is updated. If it is less than
+ * the threshold pps_dispmax, the frequency average is updated
+ * as well, but clamped to the tolerance.
+ */
+ v_usec = (v_usec >> 1) - ntp_pll.disp;
+ if (v_usec < 0)
+ ntp_pll.disp -= -v_usec >> PPS_AVG;
+ else
+ ntp_pll.disp += v_usec >> PPS_AVG;
+ if (ntp_pll.disp > pps_dispmax) {
+ ntp_pll.discnt++;
+ return;
+ }
+ if (u_usec < 0) {
+ ntp_pll.ybar -= -u_usec >> PPS_AVG;
+ if (ntp_pll.ybar < -ntp_pll.tolerance)
+ ntp_pll.ybar = -ntp_pll.tolerance;
+ u_usec = -u_usec;
+ } else {
+ ntp_pll.ybar += u_usec >> PPS_AVG;
+ if (ntp_pll.ybar > ntp_pll.tolerance)
+ ntp_pll.ybar = ntp_pll.tolerance;
+ }
+
+ /*
+ * Here the calibration interval is adjusted. If the maximum
+ * time difference is greater than tick/4, reduce the interval
+ * by half. If this is not the case for four consecutive
+ * intervals, double the interval.
+ */
+ if (u_usec << ntp_pll.shift > bigtick >> 2) {
+ ntp_pll.intcnt = 0;
+ if (ntp_pll.shift > NTP_PLL.SHIFT) {
+ ntp_pll.shift--;
+ pps_dispinc <<= 1;
+ }
+ } else if (ntp_pll.intcnt >= 4) {
+ ntp_pll.intcnt = 0;
+ if (ntp_pll.shift < NTP_PLL.SHIFTMAX) {
+ ntp_pll.shift++;
+ pps_dispinc >>= 1;
+ }
+ } else
+ ntp_pll.intcnt++;
+}
+#endif /* PPS_SYNC */
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index d28faac16cbe..7ad2e2946f6a 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)kern_descrip.c 7.28 (Berkeley) 6/25/91
- * $Id: kern_descrip.c,v 1.6.2.1 1994/05/04 07:54:36 rgrimes Exp $
+ * $Id: kern_descrip.c,v 1.8 1994/05/04 08:26:49 rgrimes Exp $
*/
#include "param.h"
@@ -100,10 +100,12 @@ dup(p, uap, retval)
struct file *fp;
int fd, error;
+#if 0
/*
* XXX Compatibility
*/
if (uap->i &~ 077) { uap->i &= 077; return (dup2(p, uap, retval)); }
+#endif
if ((unsigned)uap->i >= fdp->fd_nfiles ||
(fp = fdp->fd_ofiles[uap->i]) == NULL)
diff --git a/sys/kern/kern_execve.c b/sys/kern/kern_execve.c
index 5e0aa6565d3d..f72948400f1e 100644
--- a/sys/kern/kern_execve.c
+++ b/sys/kern/kern_execve.c
@@ -28,7 +28,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: kern_execve.c,v 1.15.2.2 1994/03/24 08:57:16 rgrimes Exp $
+ * $Id: kern_execve.c,v 1.20 1994/03/26 12:24:27 davidg Exp $
*/
#include "param.h"
@@ -78,9 +78,6 @@ execve(p, uap, retval)
char *stringbase, *stringp;
int *stack_base;
int error, resid, len, i;
-#if 0
- char image_header[256];
-#endif
struct image_params image_params, *iparams;
struct vnode *vnodep;
struct vattr attr;
@@ -147,33 +144,13 @@ interpret:
if (error)
goto exec_fail_dealloc;
-#if 0
- /*
- * Read the image header from the file.
- */
- error = vn_rdwr(UIO_READ,
- vnodep,
- image_header,
- sizeof(image_header),
- 0,
- UIO_SYSSPACE, IO_NODELOCKED,
- p->p_ucred,
- &resid,
- p);
- if (error)
- goto exec_fail_dealloc;
-
- /* Clear out junk in image_header if a partial read (small file) */
- if (resid)
- bzero(image_header + (sizeof(image_header) - resid), resid);
-#endif
/*
* Map the image header (first page) of the file into
* kernel address space
*/
error = vm_mmap(kernel_map, /* map */
(vm_offset_t *)&image_header, /* address */
- NBPG, /* size */
+ PAGE_SIZE, /* size */
VM_PROT_READ, /* protection */
VM_PROT_READ, /* max protection */
MAP_FILE, /* flags */
@@ -208,7 +185,7 @@ interpret:
vput(ndp->ni_vp);
FREE(ndp->ni_pnbuf, M_NAMEI);
if (vm_deallocate(kernel_map,
- (vm_offset_t)image_header, NBPG))
+ (vm_offset_t)image_header, PAGE_SIZE))
panic("execve: header dealloc failed (1)");
/* set new name to that of the interpreter */
@@ -231,14 +208,15 @@ interpret:
stack_base = exec_copyout_strings(iparams);
p->p_vmspace->vm_minsaddr = (char *)stack_base;
-
/*
* Stuff argument count as first item on stack
*/
*(--stack_base) = iparams->argc;
- /* close files on exec, fixup signals */
+ /* close files on exec */
fdcloseexec(p);
+
+ /* reset caught signals */
execsigs(p);
/* name this process - nameiexec(p, ndp) */
@@ -269,12 +247,12 @@ interpret:
vrele(p->p_tracep);
p->p_tracep = 0;
}
- if ((attr.va_mode&VSUID) && (p->p_flag & STRC) == 0) {
+ if ((attr.va_mode & VSUID) && (p->p_flag & STRC) == 0) {
p->p_ucred = crcopy(p->p_ucred);
p->p_ucred->cr_uid = attr.va_uid;
p->p_flag |= SUGID;
}
- if ((attr.va_mode&VSGID) && (p->p_flag & STRC) == 0) {
+ if ((attr.va_mode & VSGID) && (p->p_flag & STRC) == 0) {
p->p_ucred = crcopy(p->p_ucred);
p->p_ucred->cr_groups[0] = attr.va_gid;
p->p_flag |= SUGID;
@@ -305,10 +283,9 @@ interpret:
/*
* free various allocated resources
*/
- if (vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase,
- ARG_MAX))
+ if (vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase, ARG_MAX))
panic("execve: string buffer dealloc failed (1)");
- if (vm_deallocate(kernel_map, (vm_offset_t)image_header, NBPG))
+ if (vm_deallocate(kernel_map, (vm_offset_t)image_header, PAGE_SIZE))
panic("execve: header dealloc failed (2)");
vput(ndp->ni_vp);
FREE(ndp->ni_pnbuf, M_NAMEI);
@@ -322,7 +299,7 @@ exec_fail_dealloc:
panic("execve: string buffer dealloc failed (2)");
if (iparams->image_header && iparams->image_header != (char *)-1)
if (vm_deallocate(kernel_map,
- (vm_offset_t)iparams->image_header, NBPG))
+ (vm_offset_t)iparams->image_header, PAGE_SIZE))
panic("execve: header dealloc failed (3)");
vput(ndp->ni_vp);
FREE(ndp->ni_pnbuf, M_NAMEI);
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index e1deb4d855fe..a6cf4c732390 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)kern_exit.c 7.35 (Berkeley) 6/27/91
- * $Id: kern_exit.c,v 1.14 1994/01/29 04:04:23 davidg Exp $
+ * $Id: kern_exit.c,v 1.15 1994/03/14 21:54:13 davidg Exp $
*/
#include "param.h"
@@ -353,6 +353,9 @@ loop:
continue;
nfound++;
if (p->p_stat == SZOMB) {
+ /* charge childs cpu usage to parent */
+ if( curproc->p_pid != 1)
+ curproc->p_cpu += p->p_cpu;
retval[0] = p->p_pid;
#ifdef COMPAT_43
if (uap->compat)
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index 5ca1d910f937..b9088766dd8b 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)kern_fork.c 7.29 (Berkeley) 5/15/91
- * $Id: kern_fork.c,v 1.6.2.1 1994/05/04 07:54:38 rgrimes Exp $
+ * $Id: kern_fork.c,v 1.8 1994/05/04 08:26:54 rgrimes Exp $
*/
#include "param.h"
@@ -246,6 +246,11 @@ again:
#endif
/*
+ * set priority of child to be that of parent
+ */
+ p2->p_cpu = p1->p_cpu;
+
+ /*
* This begins the section where we must prevent the parent
* from being swapped.
*/
@@ -268,6 +273,9 @@ again:
p2->p_stats->p_start = time;
(void) spl0();
p2->p_acflag = AFORK;
+/*
+ vm_map_init_pmap(&p2->p_vmspace->vm_map);
+*/
return (0);
}
diff --git a/sys/kern/kern_malloc.c b/sys/kern/kern_malloc.c
index 6841827446d0..ce3243a30367 100644
--- a/sys/kern/kern_malloc.c
+++ b/sys/kern/kern_malloc.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)kern_malloc.c 7.25 (Berkeley) 5/8/91
- * $Id: kern_malloc.c,v 1.8 1994/02/10 08:04:07 davidg Exp $
+ * $Id: kern_malloc.c,v 1.9 1994/04/13 00:54:59 ache Exp $
*/
#include "param.h"
@@ -259,3 +259,75 @@ kmeminit()
kmemstats[M_MBUF].ks_limit = (vm_page_count * NBPG) / 16;
#endif
}
+
+void *
+contigmalloc(size, type, flags, maxpa, alignmask, boundarymask)
+ unsigned long size;
+ int type;
+ int flags;
+ unsigned long maxpa; /* e.g. 16M - 1 for isa dma */
+ unsigned long alignmask; /* e.g. 1M - 1 for M boundary */
+ unsigned long boundarymask; /* e.g. 64K - 1 for 8-bit isa dma */
+{
+ unsigned long skipsize;
+ void *skipva;
+
+ size = round_page(size);
+ if (size == 0 || size > boundarymask + 1)
+ return (NULL);
+
+ /*
+ * Attempt to push the physical address to a suitable boundary by
+ * skipping some memory. We could be cleverer here. E.g., mallocate
+ * lots of single pages and then free the ones that we hope to use.
+ * flags == M_WAIT is likely to hang the system.
+ */
+ for (skipsize = 0, skipva = NULL; ; skipsize += NBPG) {
+ unsigned long off;
+ unsigned long pa;
+ unsigned long prevpa;
+ void *va;
+
+ if (skipsize != 0) {
+ skipva = malloc(skipsize, type, flags);
+ if (skipva == NULL) {
+#ifdef DEBUG
+ printf("contigmalloc: skipva NULL on try %d\n",
+ 1 + skipsize / NBPG);
+#endif
+ return (NULL);
+ }
+ }
+ va = malloc(size, type, flags);
+ if (skipsize != 0)
+ free(skipva, type);
+ if (va == NULL) {
+#ifdef DEBUG
+ printf("contigmalloc: va NULL on try %d\n",
+ 1 + skipsize / NBPG);
+#endif
+ return (NULL);
+ }
+ for (off = 0, prevpa = 0; off < size; off += NBPG, prevpa = pa)
+ {
+ pa = pmap_extract(pmap_kernel(), (vm_offset_t)va + off);
+ if (pa + NBPG - 1 > maxpa
+ || off == 0 && pa & alignmask
+ || off != 0
+ && (pa != prevpa + NBPG
+ || (pa & boundarymask) == 0))
+ goto fail;
+ }
+#ifdef DEBUG
+ printf("contigmalloc: success at va %lx pa %lx on try %d\n",
+ (unsigned long)va,
+ pmap_extract(pmap_kernel(), (unsigned long)va),
+ 1 + skipsize / NBPG);
+#endif
+ return (va);
+fail:
+ free(va, type);
+ }
+}
+
+
diff --git a/sys/kern/kern_ntptime.c b/sys/kern/kern_ntptime.c
new file mode 100644
index 000000000000..9e50a42df282
--- /dev/null
+++ b/sys/kern/kern_ntptime.c
@@ -0,0 +1,267 @@
+/******************************************************************************
+ * *
+ * Copyright (c) David L. Mills 1993, 1994 *
+ * *
+ * 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 University of Delaware not be used in *
+ * advertising or publicity pertaining to distribution of the software *
+ * without specific, written prior permission. The University of Delaware *
+ * makes no representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied warranty. *
+ * *
+ ******************************************************************************/
+
+/*
+ * Modification history kern_ntptime.c
+ *
+ * 24 Mar 94 David L. Mills
+ * Revised syscall interface to include new variables for PPS
+ * time discipline.
+ *
+ * 14 Feb 94 David L. Mills
+ * Added code for external clock
+ *
+ * 28 Nov 93 David L. Mills
+ * Revised frequency scaling to conform with adjusted parameters
+ *
+ * 17 Sep 93 David L. Mills
+ * Created file
+ */
+/*
+ * ntp_gettime(), ntp_adjtime() - precision time interface for SunOS
+ * 4.1.1 and 4.1.3
+ *
+ * These routines consitute the Network Time Protocol (NTP) interfaces
+ * for user and daemon application programs. The ntp_gettime() routine
+ * provides the time, maximum error (synch distance) and estimated error
+ * (dispersion) to client user application programs. The ntp_adjtime()
+ * routine is used by the NTP daemon to adjust the system clock to an
+ * externally derived time. The time offset and related variables set by
+ * this routine are used by hardclock() to adjust the phase and
+ * frequency of the phase-lock loop which controls the system clock.
+ */
+#include "param.h"
+#include "systm.h"
+#include "kernel.h"
+#include "proc.h"
+#include "timex.h"
+
+/*
+ * The following variables are used by the hardclock() routine in the
+ * kern_clock.c module and are described in that module.
+ */
+extern struct timeval time; /* kernel time variable */
+extern int time_state; /* clock state */
+extern int time_status; /* clock status bits */
+extern long time_offset; /* time adjustment (us) */
+extern long time_freq; /* frequency offset (scaled ppm) */
+extern long time_maxerror; /* maximum error (us) */
+extern long time_esterror; /* estimated error (us) */
+extern long time_constant; /* pll time constant */
+extern long time_precision; /* clock precision (us) */
+extern long time_tolerance; /* frequency tolerance (scaled ppm) */
+
+#ifdef PPS_SYNC
+/*
+ * The following variables are used only if the PPS signal discipline
+ * is configured in the kernel.
+ */
+extern int pps_shift; /* interval duration (s) (shift) */
+extern long pps_freq; /* pps frequency offset (scaled ppm) */
+extern long pps_jitter; /* pps jitter (us) */
+extern long pps_stabil; /* pps stability (scaled ppm) */
+extern long pps_jitcnt; /* jitter limit exceeded */
+extern long pps_calcnt; /* calibration intervals */
+extern long pps_errcnt; /* calibration errors */
+extern long pps_stbcnt; /* stability limit exceeded */
+#endif /* PPS_SYNC */
+
+/*
+ * ntp_gettime() - NTP user application interface
+ */
+struct ntp_gettime_args {
+ struct ntptimeval *tp;
+};
+
+int
+ntp_gettime(struct proc *p, struct ntp_gettime_args *uap, int *retval)
+{
+ struct timeval atv;
+ struct ntptimeval ntv;
+ int s;
+ int error = 0;
+
+ if (uap->tp) {
+ s = splclock();
+#ifdef EXT_CLOCK
+ /*
+ * The microtime() external clock routine returns a
+ * status code. If less than zero, we declare an error
+ * in the clock status word and return the kernel
+ * (software) time variable. While there are other
+ * places that call microtime(), this is the only place
+ * that matters from an application point of view.
+ */
+ if (microtime(&atv) < 0) {
+ time_status |= STA_CLOCKERR;
+ ntv.time = time;
+ } else
+ time_status &= ~STA_CLOCKERR;
+#else /* EXT_CLOCK */
+ microtime(&atv);
+#endif /* EXT_CLOCK */
+ ntv.time = atv;
+ ntv.maxerror = time_maxerror;
+ ntv.esterror = time_esterror;
+ (void) splx(s);
+
+ error = copyout((caddr_t)&ntv, (caddr_t)uap->tp,
+ sizeof (ntv));
+ }
+ if (!error) {
+ *retval = time_state;
+
+ /*
+ * Status word error decode. If any of these conditions
+ * occur, an error is returned, instead of the status
+ * word. Most applications will care only about the fact
+ * the system clock may not be trusted, not about the
+ * details.
+ *
+ * Hardware or software error
+ */
+ if (time_status & (STA_UNSYNC | STA_CLOCKERR))
+ *retval = TIME_ERROR;
+
+ /*
+ * PPS signal lost when either time or frequency
+ * synchronization requested
+ */
+ if (time_status & (STA_PPSFREQ | STA_PPSTIME) &&
+ !(time_status & STA_PPSSIGNAL))
+ *retval = TIME_ERROR;
+
+ /*
+ * PPS jitter exceeded when time synchronization
+ * requested
+ */
+ if (time_status & STA_PPSTIME &&
+ time_status & STA_PPSJITTER)
+ *retval = TIME_ERROR;
+
+ /*
+ * PPS wander exceeded or calibration error when
+ * frequency synchronization requested
+ */
+ if (time_status & STA_PPSFREQ &&
+ time_status & (STA_PPSWANDER | STA_PPSERROR))
+ *retval = TIME_ERROR;
+ }
+ return error;
+}
+
+/*
+ * ntp_adjtime() - NTP daemon application interface
+ */
+struct ntp_adjtime_args {
+ struct timex *tp;
+};
+
+int
+ntp_adjtime(struct proc *p, struct ntp_adjtime_args *uap, int *retval)
+{
+ struct timex ntv;
+ int modes;
+ int s;
+ int error;
+
+ error = copyin((caddr_t)uap->tp, (caddr_t)&ntv, sizeof(ntv));
+ if (error)
+ return error;
+
+ /*
+ * Update selected clock variables - only the superuser can
+ * change anything. Note that there is no error checking here on
+ * the assumption the superuser should know what it is doing.
+ */
+ modes = ntv.modes;
+ if ((modes != 0)
+ && (error = suser(p->p_cred->pc_ucred, &p->p_acflag)))
+ return error;
+
+ s = splclock();
+ if (modes & MOD_FREQUENCY)
+#ifdef PPS_SYNC
+ time_freq = ntv.freq - pps_freq;
+#else /* PPS_SYNC */
+ time_freq = ntv.freq;
+#endif /* PPS_SYNC */
+ if (modes & MOD_MAXERROR)
+ time_maxerror = ntv.maxerror;
+ if (modes & MOD_ESTERROR)
+ time_esterror = ntv.esterror;
+ if (modes & MOD_STATUS) {
+ time_status &= STA_RONLY;
+ time_status |= ntv.status & ~STA_RONLY;
+ }
+ if (modes & MOD_TIMECONST)
+ time_constant = ntv.constant;
+ if (modes & MOD_OFFSET)
+ hardupdate(ntv.offset);
+
+ /*
+ * Retrieve all clock variables
+ */
+ if (time_offset < 0)
+ ntv.offset = -(-time_offset >> SHIFT_UPDATE);
+ else
+ ntv.offset = time_offset >> SHIFT_UPDATE;
+#ifdef PPS_SYNC
+ ntv.freq = time_freq + pps_freq;
+#else /* PPS_SYNC */
+ ntv.freq = time_freq;
+#endif /* PPS_SYNC */
+ ntv.maxerror = time_maxerror;
+ ntv.esterror = time_esterror;
+ ntv.status = time_status;
+ ntv.constant = time_constant;
+ ntv.precision = time_precision;
+ ntv.tolerance = time_tolerance;
+#ifdef PPS_SYNC
+ ntv.shift = pps_shift;
+ ntv.ppsfreq = pps_freq;
+ ntv.jitter = pps_jitter >> PPS_AVG;
+ ntv.stabil = pps_stabil;
+ ntv.calcnt = pps_calcnt;
+ ntv.errcnt = pps_errcnt;
+ ntv.jitcnt = pps_jitcnt;
+ ntv.stbcnt = pps_stbcnt;
+#endif /* PPS_SYNC */
+ (void)splx(s);
+
+ error = copyout((caddr_t)&ntv, (caddr_t)uap->tp, sizeof(ntv));
+ if (!error) {
+ /*
+ * Status word error decode. See comments in
+ * ntp_gettime() routine.
+ */
+ retval[0] = time_state;
+ if (time_status & (STA_UNSYNC | STA_CLOCKERR))
+ retval[0] = TIME_ERROR;
+ if (time_status & (STA_PPSFREQ | STA_PPSTIME) &&
+ !(time_status & STA_PPSSIGNAL))
+ retval[0] = TIME_ERROR;
+ if (time_status & STA_PPSTIME &&
+ time_status & STA_PPSJITTER)
+ retval[0] = TIME_ERROR;
+ if (time_status & STA_PPSFREQ &&
+ time_status & (STA_PPSWANDER | STA_PPSERROR))
+ retval[0] = TIME_ERROR;
+ }
+ return error;
+}
+
+
diff --git a/sys/kern/kern_physio.c b/sys/kern/kern_physio.c
index cf76f80ae202..bc02348f73a9 100644
--- a/sys/kern/kern_physio.c
+++ b/sys/kern/kern_physio.c
@@ -45,7 +45,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: kern_physio.c,v 1.1.2.2 1994/05/01 18:59:48 rgrimes Exp $
+ * $Id: kern_physio.c,v 1.7 1994/04/25 23:48:29 davidg Exp $
*/
#include "param.h"
@@ -56,8 +56,11 @@
#include "malloc.h"
#include "vnode.h"
#include "vm/vm.h"
+#include "vm/vm_page.h"
#include "specdev.h"
+#define HOLD_WORKS_FOR_SHARING
+
/*
* Driver interface to do "raw" I/O in the address space of a
* user process directly for read and write operations..
@@ -79,6 +82,13 @@ rawwrite(dev, uio)
(caddr_t) (u_long) dev, uio));
}
+static void
+physwakeup(bp)
+ struct buf *bp;
+{
+ wakeup((caddr_t) bp);
+ bp->b_flags &= ~B_CALL;
+}
int physio(strat, dev, bp, off, rw, base, len, p)
d_strategy_t strat;
@@ -92,7 +102,7 @@ int physio(strat, dev, bp, off, rw, base, len, p)
int amttodo = *len;
int error, amtdone;
vm_prot_t ftype;
- vm_offset_t v, lastv;
+ vm_offset_t v, lastv, pa;
caddr_t adr;
int oldflags;
int s;
@@ -122,7 +132,6 @@ int physio(strat, dev, bp, off, rw, base, len, p)
splx(s);
}
- bp->b_flags = B_BUSY | B_PHYS | rw;
bp->b_proc = p;
bp->b_dev = dev;
bp->b_error = 0;
@@ -131,17 +140,25 @@ int physio(strat, dev, bp, off, rw, base, len, p)
/* iteratively do I/O on as large a chunk as possible */
do {
- bp->b_flags &= ~B_DONE;
+ bp->b_flags = B_BUSY | B_PHYS | B_CALL | rw;
+ bp->b_iodone = physwakeup;
bp->b_un.b_addr = base;
- /* XXX limit */
+ /*
+ * Notice that b_bufsize is more owned by the buffer
+ * allocating entity, while b_bcount might be modified
+ * by the called I/O routines. So after I/O is complete
+ * the only thing guaranteed to be unchanged is
+ * b_bufsize.
+ */
bp->b_bcount = min (256*1024, amttodo);
+ bp->b_bufsize = bp->b_bcount;
/* first, check if accessible */
- if (rw == B_READ && !useracc(base, bp->b_bcount, B_WRITE)) {
+ if (rw == B_READ && !useracc(base, bp->b_bufsize, B_WRITE)) {
error = EFAULT;
goto errrtn;
}
- if (rw == B_WRITE && !useracc(base, bp->b_bcount, B_READ)) {
+ if (rw == B_WRITE && !useracc(base, bp->b_bufsize, B_READ)) {
error = EFAULT;
goto errrtn;
}
@@ -153,16 +170,17 @@ int physio(strat, dev, bp, off, rw, base, len, p)
ftype = VM_PROT_READ;
lastv = 0;
- for (adr = (caddr_t)trunc_page(base); adr < base + bp->b_bcount;
+ for (adr = (caddr_t)trunc_page(base); adr < base + bp->b_bufsize;
adr += NBPG) {
/*
- * make sure that the pde is valid and wired
+ * make sure that the pde is valid and held
*/
v = trunc_page(((vm_offset_t)vtopte(adr)));
if (v != lastv) {
- vm_map_pageable(&p->p_vmspace->vm_map, v,
- round_page(v+1), FALSE);
+ vm_fault_quick(v, VM_PROT_READ);
+ pa = pmap_extract(&p->p_vmspace->vm_pmap, v);
+ vm_page_hold(PHYS_TO_VM_PAGE(pa));
lastv = v;
}
@@ -170,65 +188,48 @@ int physio(strat, dev, bp, off, rw, base, len, p)
* do the vm_fault if needed, do the copy-on-write thing when
* reading stuff off device into memory.
*/
- if (ftype & VM_PROT_WRITE) {
- /*
- * properly handle copy-on-write
- */
-#if 0
- *(volatile int *) adr += 0;
-#endif
- vm_fault(&curproc->p_vmspace->vm_map,
- (vm_offset_t) adr,
- VM_PROT_READ|VM_PROT_WRITE, FALSE);
- }
-#if 0
- else {
- /*
- * this clause is not really necessary because
- * vslock does a vm_map_pageable FALSE.
- * It is not optimally efficient to reference the
- * page with the possiblity of it being paged out, but
- * if this page is faulted here, it will be placed on the
- * active queue, with the probability of it being paged
- * out being very very low. This is here primarily for
- * "symmetry".
- */
- *(volatile int *) adr;
- }
-#endif
- }
+ vm_fault_quick(adr, ftype);
+ pa = pmap_extract(&p->p_vmspace->vm_pmap, (vm_offset_t) adr);
/*
- * if the process has been blocked by the wiring of the page table pages
- * above or faults of other pages, then the vm_map_pageable contained in the
- * vslock will fault the pages back in if they have been paged out since
- * being referenced in the loop above. (vm_map_pageable calls vm_fault_wire
- * which calls vm_fault to get the pages if needed.)
+ * hold the data page
*/
+ vm_page_hold(PHYS_TO_VM_PAGE(pa));
+ }
- /* lock in core (perform vm_map_pageable, FALSE) */
- vslock (base, bp->b_bcount);
+ vmapbuf(bp);
/* perform transfer */
- physstrat(bp, strat, PRIBIO);
+ (*strat)(bp);
- /* unlock (perform vm_map_pageable, TRUE) */
- vsunlock (base, bp->b_bcount, 0);
+ /* pageout daemon doesn't wait for pushed pages */
+ s = splbio();
+ while ((bp->b_flags & B_DONE) == 0)
+ tsleep((caddr_t)bp, PRIBIO, "physstr", 0);
+ splx(s);
- lastv = 0;
+ vunmapbuf(bp);
/*
- * unwire the pde
+ * unhold the pde, and data pages
*/
- for (adr = (caddr_t)trunc_page(base); adr < base + bp->b_bcount;
+ lastv = 0;
+ for (adr = (caddr_t)trunc_page(base); adr < base + bp->b_bufsize;
adr += NBPG) {
v = trunc_page(((vm_offset_t)vtopte(adr)));
if (v != lastv) {
- vm_map_pageable(&p->p_vmspace->vm_map, v, round_page(v+1), TRUE);
+ pa = pmap_extract(&p->p_vmspace->vm_pmap, v);
+ vm_page_unhold(PHYS_TO_VM_PAGE(pa));
lastv = v;
}
+ pa = pmap_extract(&p->p_vmspace->vm_pmap, (vm_offset_t) adr);
+ vm_page_unhold(PHYS_TO_VM_PAGE(pa));
}
+ /*
+ * in this case, we need to use b_bcount instead of
+ * b_bufsize.
+ */
amtdone = bp->b_bcount - bp->b_resid;
amttodo -= amtdone;
base += amtdone;
diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c
index 2d1d7db1ce4e..48ea1cc33d06 100644
--- a/sys/kern/kern_prot.c
+++ b/sys/kern/kern_prot.c
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)kern_prot.c 7.21 (Berkeley) 5/3/91
- * $Id: kern_prot.c,v 1.5.2.2 1994/05/04 07:54:41 rgrimes Exp $
+ * $Id: kern_prot.c,v 1.7 1994/05/04 08:26:59 rgrimes Exp $
*/
/*
diff --git a/sys/kern/kern_resource.c b/sys/kern/kern_resource.c
index 941ead222a34..caaf6a5953a5 100644
--- a/sys/kern/kern_resource.c
+++ b/sys/kern/kern_resource.c
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)kern_resource.c 7.13 (Berkeley) 5/9/91
- * $Id: kern_resource.c,v 1.7.2.1 1994/05/04 07:54:43 rgrimes Exp $
+ * $Id: kern_resource.c,v 1.8 1994/05/04 08:27:01 rgrimes Exp $
*/
#include "param.h"
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index 8450e1c2f588..f57966f8fbb6 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)kern_sig.c 7.35 (Berkeley) 6/28/91
- * $Id: kern_sig.c,v 1.9 1993/12/19 00:51:30 wollman Exp $
+ * $Id: kern_sig.c,v 1.12 1994/05/25 19:49:38 csgr Exp $
*/
#define SIGPROP /* include signal properties table */
@@ -43,6 +43,7 @@
#include "mount.h"
#include "filedesc.h"
#include "proc.h"
+#include "ucred.h"
#include "systm.h"
#include "timeb.h"
#include "times.h"
@@ -52,6 +53,7 @@
#include "kernel.h"
#include "wait.h"
#include "ktrace.h"
+#include "syslog.h"
#include "machine/cpu.h"
@@ -1044,6 +1046,14 @@ sigexit(p, sig)
p->p_acflag |= AXSIG;
if (sigprop[sig] & SA_CORE) {
p->p_sigacts->ps_sig = sig;
+ /*
+ * Log signals which would cause core dumps
+ * (Log as LOG_INFO to appease those who don't want
+ * these messages.)
+ * XXX : Todo, as well as euid, write out ruid too
+ */
+ log(LOG_INFO, "pid %d: %s: uid %d: exited on signal %d\n",
+ p->p_pid, p->p_comm, p->p_ucred->cr_uid, sig);
if (coredump(p) == 0)
sig |= WCOREFLAG;
}
diff --git a/sys/kern/kern_subr.c b/sys/kern/kern_subr.c
index bb860d2ca6bd..47af2d75b472 100644
--- a/sys/kern/kern_subr.c
+++ b/sys/kern/kern_subr.c
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)kern_subr.c 7.7 (Berkeley) 4/15/91
- * $Id: kern_subr.c,v 1.5.2.1 1994/05/04 07:54:46 rgrimes Exp $
+ * $Id: kern_subr.c,v 1.6 1994/05/04 08:27:05 rgrimes Exp $
*/
#include "param.h"
diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c
index 554d38b004c4..67bd7e4786d8 100644
--- a/sys/kern/kern_synch.c
+++ b/sys/kern/kern_synch.c
@@ -32,7 +32,7 @@
* SUCH DAMAGE.
*
* from: @(#)kern_synch.c 7.18 (Berkeley) 6/27/91
- * $Id: kern_synch.c,v 1.4 1994/02/11 21:14:28 guido Exp $
+ * $Id: kern_synch.c,v 1.5 1994/03/20 00:33:06 wollman Exp $
*/
#include "param.h"
@@ -381,7 +381,7 @@ endtsleep(arg1, dummy)
splx(s);
}
-#if 1 /* XXX this should go away... */
+#if 0 /* This is obsolete, use tsleep(). */
/*
* Short-term, non-interruptable sleep.
*/
diff --git a/sys/kern/kern_time.c b/sys/kern/kern_time.c
index a0c4a117bb2f..3be34ee22d8e 100644
--- a/sys/kern/kern_time.c
+++ b/sys/kern/kern_time.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)kern_time.c 7.15 (Berkeley) 3/17/91
- * $Id: kern_time.c,v 1.4 1993/11/25 01:33:14 wollman Exp $
+ * $Id: kern_time.c,v 1.5 1994/06/22 05:52:47 jkh Exp $
*/
#include "param.h"
@@ -103,7 +103,9 @@ settimeofday(p, uap, retval)
return (error);
/* WHAT DO WE DO ABOUT PENDING REAL-TIME TIMEOUTS??? */
boottime.tv_sec += atv.tv_sec - time.tv_sec;
- s = splhigh(); time = atv; splx(s);
+ s = splclock();
+ time.tv_sec = atv.tv_sec; /* XXX avoid skew in tv_usec */
+ splx(s);
resettodr();
}
if (uap->tzp && (error = copyin((caddr_t)uap->tzp, (caddr_t)&atz,
diff --git a/sys/kern/makesyscalls.sh b/sys/kern/makesyscalls.sh
index 77f61280c1bd..60826dfc4106 100644
--- a/sys/kern/makesyscalls.sh
+++ b/sys/kern/makesyscalls.sh
@@ -1,6 +1,6 @@
#! /bin/sh -
# from: @(#)makesyscalls.sh 7.6 (Berkeley) 4/20/91
-# $Id: makesyscalls.sh,v 1.3 1993/11/07 21:22:30 wollman Exp $
+# $Id: makesyscalls.sh,v 1.4 1994/06/01 21:13:53 phk Exp $
set -e
@@ -162,7 +162,7 @@ awk < $1 "
printf("\n#else /* %s */\n", compat) > syscompat
printf("#define compat(n, name) 0, nosys\n") > syscompat
printf("#endif /* %s */\n\n", compat) > syscompat
- printf("#endif /* _SYS_SYSCALL_H_ */") > syshdr
+ printf("#endif /* _SYS_SYSCALL_H_ */\n") > syshdr
printf("};\n\n") > sysent
printf("int\tnsysent = sizeof(sysent) / sizeof(sysent[0]);\n") > sysent
diff --git a/sys/kern/spec_vnops.c b/sys/kern/spec_vnops.c
index dd7a7c8be283..13ea715bef69 100644
--- a/sys/kern/spec_vnops.c
+++ b/sys/kern/spec_vnops.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)spec_vnops.c 7.37 (Berkeley) 5/30/91
- * $Id: spec_vnops.c,v 1.3 1993/11/25 01:33:15 wollman Exp $
+ * $Id: spec_vnops.c,v 1.4 1994/05/30 03:20:08 ache Exp $
*/
#include "param.h"
@@ -137,7 +137,9 @@ spec_open(vp, mode, cred, p)
if ((u_int)maj >= nchrdev)
return (ENXIO);
VOP_UNLOCK(vp);
+ vp->v_opencount++;
error = (*cdevsw[maj].d_open)(dev, mode, S_IFCHR, p);
+ --vp->v_opencount;
VOP_LOCK(vp);
return (error);
diff --git a/sys/kern/subr_prf.c b/sys/kern/subr_prf.c
index 6d4a61735965..afe2ddc99f36 100644
--- a/sys/kern/subr_prf.c
+++ b/sys/kern/subr_prf.c
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)subr_prf.c 7.30 (Berkeley) 6/29/91
- * $Id: subr_prf.c,v 1.6.2.1 1994/05/04 07:54:49 rgrimes Exp $
+ * $Id: subr_prf.c,v 1.8 1994/05/04 08:27:06 rgrimes Exp $
*/
#include "param.h"
@@ -87,7 +87,6 @@ static void logpri __P((int level));
static void putchar __P((int ch, int flags, struct tty *tp));
static char *ksprintn __P((u_long num, int base, int *len));
void kprintf __P((const char *fmt, int flags, struct tty *tp, va_list));
-volatile void boot(int bootopt);
/*
* Variable panicstr contains argument to first call to panic; used
@@ -106,14 +105,8 @@ int msgbufmapped;
* and then reboots. If we are called twice, then we avoid trying to sync
* the disks as this often leads to recursive panics.
*/
-#ifdef __STDC__
-volatile void
-panic(const char *msg)
-#else
void
-panic(msg)
- char *msg;
-#endif
+panic(const char *msg)
{
int bootopt = RB_AUTOBOOT | RB_DUMP;
diff --git a/sys/kern/subr_rand.c b/sys/kern/subr_rand.c
new file mode 100644
index 000000000000..09f36a7d630a
--- /dev/null
+++ b/sys/kern/subr_rand.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1993 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 Computer Systems
+ * Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory 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.
+ */
+
+/*
+ * LBL BSD kernel random number generator (used by statclock).
+ *
+ * Written by Steve McCanne & Chris Torek (mccanne@ee.lbl.gov,
+ * torek@ee.lbl.gov), November, 1992.
+ *
+ * This implementation is based on ``Two Fast Implementations of
+ * the "Minimal Standard" Random Number Generator", David G. Carta,
+ * Communications of the ACM, Jan 1990, Vol 33 No 1.
+ */
+
+#ifdef sparc
+asm("\
+ .global _kernrand ;\
+_kernrand: ;\
+ sethi %hi(16807), %o1 ;\
+ wr %o1, %lo(16807), %y ;\
+ set 0xffff, %o4 ;\
+ andcc %g0, 0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %o0, %o2 ;\
+ mulscc %o2, %g0, %o2 ;\
+ rd %y, %o3 ;\
+ srl %o2, 16, %o1 ;\
+ and %o4, %o2, %o0 ;\
+ sll %o0, 15, %o0 ;\
+ srl %o3, 17, %o3 ;\
+ or %o3, %o0, %o0 ;\
+ addcc %o0, %o1, %o0 ;\
+ bl 1f ;\
+ sethi %hi(0x7fffffff), %o1 ;\
+ retl ;\
+ nop ;\
+1: ;\
+ or %o1, %lo(0x7fffffff), %o1 ;\
+ add %o0, 1, %o0 ;\
+ retl ;\
+ and %o1, %o0, %o0 ");
+#else
+long
+kernrand(x)
+ register long x;
+{
+ register long hi, lo, t;
+
+ hi = x / 127773;
+ lo = x % 127773;
+ t = 16807 * lo - 2836 * hi;
+ if (t > 0)
+ return (t);
+ else
+ return (t + 0x7fffffff);
+}
+#endif
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
index b129f4f4d5bd..43f036c62568 100644
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)sys_process.c 7.22 (Berkeley) 5/11/91
- * $Id: sys_process.c,v 1.9.2.2 1994/05/03 20:44:01 rgrimes Exp $
+ * $Id: sys_process.c,v 1.11 1994/04/02 23:03:01 jkh Exp $
*/
#include "param.h"
diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c
index 2f624acebcf7..1a65f4f70a44 100644
--- a/sys/kern/syscalls.c
+++ b/sys/kern/syscalls.c
@@ -2,7 +2,7 @@
* System call names.
*
* DO NOT EDIT-- this file is automatically generated.
- * created from $Id: syscalls.master,v 1.8 1994/01/31 10:27:25 davidg Exp $
+ * created from $Id: syscalls.master,v 1.9 1994/03/15 01:58:33 wollman Exp $
*/
char *syscallnames[] = {
@@ -218,8 +218,8 @@ char *syscallnames[] = {
"#172", /* 172 = nosys */
"#173", /* 173 = nosys */
"#174", /* 174 = nosys */
- "#175", /* 175 = nosys */
- "#176", /* 176 = nosys */
+ "ntp_gettime", /* 175 = ntp_gettime */
+ "ntp_adjtime", /* 176 = ntp_adjtime */
#ifdef MACHVMCOMPAT
"vm_allocate", /* 177 = vm_allocate */
"vm_deallocate", /* 178 = vm_deallocate */
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
index a43845ebd1a4..db30a9330979 100644
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -1,4 +1,4 @@
- $Id: syscalls.master,v 1.8 1994/01/31 10:27:25 davidg Exp $
+ $Id: syscalls.master,v 1.9 1994/03/15 01:58:33 wollman Exp $
; from: @(#)syscalls.master 7.26 (Berkeley) 3/25/91
; System call name/number master file.
; Processed to created init_sysent.c, syscalls.c and syscall.h.
@@ -243,8 +243,8 @@
172 UNIMPL 0 nosys
173 UNIMPL 0 nosys
174 UNIMPL 0 nosys
-175 UNIMPL 0 nosys
-176 UNIMPL 0 nosys
+175 STD 1 ntp_gettime
+176 STD 1 ntp_adjtime
#ifdef MACHVMCOMPAT
177 STD 4 svm_allocate vm_allocate
178 STD 3 svm_deallocate vm_deallocate
diff --git a/sys/kern/sysv_msg.c b/sys/kern/sysv_msg.c
index f861f53ece52..ff442d05b2cb 100644
--- a/sys/kern/sysv_msg.c
+++ b/sys/kern/sysv_msg.c
@@ -149,7 +149,7 @@ struct msgctl_args {
struct msqid_ds *user_msqptr;
};
-int
+static int
msgctl(p, uap, retval)
struct proc *p;
register struct msgctl_args *uap;
@@ -309,7 +309,7 @@ struct msgget_args {
int msgflg;
};
-int
+static int
msgget(p, uap, retval)
struct proc *p;
register struct msgget_args *uap;
@@ -766,7 +766,7 @@ struct msgrcv_args {
int msgflg;
};
-int
+static int
msgrcv(p, uap, retval)
struct proc *p;
register struct msgrcv_args *uap;
diff --git a/sys/kern/sysv_sem.c b/sys/kern/sysv_sem.c
index 6344018d9514..dbada0d09aad 100644
--- a/sys/kern/sysv_sem.c
+++ b/sys/kern/sysv_sem.c
@@ -62,7 +62,7 @@ semsys(p, uap, retval)
{
while ( semlock_holder != NULL && semlock_holder != p ) {
/* printf("semaphore facility locked - sleeping ...\n"); */
- sleep( (caddr_t)&semlock_holder, (PZERO - 4) );
+ tsleep( (caddr_t)&semlock_holder, (PZERO - 4), "semsys", 0 );
}
if (uap->which >= sizeof(semcalls)/sizeof(semcalls[0]))
@@ -89,7 +89,7 @@ struct semconfig_args {
semconfig_ctl_t flag;
};
-int
+static int
semconfig(p, uap, retval)
struct proc *p;
struct semconfig_args *uap;
@@ -324,7 +324,7 @@ struct semctl_args {
union semun *arg;
};
-int
+static int
semctl(p, uap, retval)
struct proc *p;
register struct semctl_args *uap;
@@ -556,7 +556,7 @@ struct semget_args {
int semflg;
};
-int
+static int
semget(p, uap, retval)
struct proc *p;
register struct semget_args *uap;
@@ -676,7 +676,7 @@ struct semop_args {
int nsops;
};
-int
+static int
semop(p, uap, retval)
struct proc *p;
register struct semop_args *uap;
@@ -1007,7 +1007,7 @@ semexit(p)
#ifdef SEM_DEBUG
printf("semaphore facility locked - sleeping ...\n");
#endif
- sleep( (caddr_t)&semlock_holder, (PZERO - 4) );
+ tsleep( (caddr_t)&semlock_holder, (PZERO - 4), "semexit", 0 );
}
did_something = 0;
diff --git a/sys/kern/sysv_shm.c b/sys/kern/sysv_shm.c
index 00f123f529c5..3c7a45749583 100644
--- a/sys/kern/sysv_shm.c
+++ b/sys/kern/sysv_shm.c
@@ -37,7 +37,7 @@
*
* from: Utah $Hdr: uipc_shm.c 1.9 89/08/14$
* from: @(#)sysv_shm.c 7.15 (Berkeley) 5/13/91
- * $Id: sysv_shm.c,v 1.9 1994/01/21 09:56:31 davidg Exp $
+ * $Id: sysv_shm.c,v 1.12 1994/03/08 14:08:14 ats Exp $
*/
/*
@@ -69,7 +69,7 @@
struct shmid_ds *shmsegs;
struct shminfo shminfo;
-int shmat(), shmctl(), shmdt(), shmget(); /* XXX */
+static int shmat(), shmctl(), shmdt(), shmget(); /* XXX */
int (*shmcalls[])() = { shmat, shmctl, shmdt, shmget }; /* XXX */
int shmtot = 0;
@@ -90,7 +90,7 @@ struct shmhandle {
caddr_t shmh_id;
};
-static int ipcaccess(struct ipc_perm *, int, struct ucred *);
+extern int ipcaccess(struct ipc_perm *, int, struct ucred *);
static void shmufree(struct proc *, struct shmdesc *);
static void shmfree(struct shmid_ds *);
static int shmvalid(int);
@@ -146,7 +146,7 @@ struct shmget_args {
int shmflg;
};
-int
+static int
shmget(p, uap, retval)
struct proc *p;
register struct shmget_args *uap;
@@ -244,7 +244,7 @@ struct shmctl_args {
};
/* ARGSUSED */
-int
+static int
shmctl(p, uap, retval)
struct proc *p;
register struct shmctl_args *uap;
@@ -316,7 +316,7 @@ struct shmat_args {
int shmflg;
};
-int
+static int
shmat(p, uap, retval)
struct proc *p;
register struct shmat_args *uap;
@@ -402,7 +402,7 @@ struct shmdt_args {
};
/* ARGSUSED */
-int
+static int
shmdt(p, uap, retval)
struct proc *p;
struct shmdt_args *uap;
diff --git a/sys/kern/tty.c b/sys/kern/tty.c
index 519b910ccfdb..146aa1545efe 100644
--- a/sys/kern/tty.c
+++ b/sys/kern/tty.c
@@ -32,7 +32,22 @@
* SUCH DAMAGE.
*
* from: @(#)tty.c 7.44 (Berkeley) 5/28/91
- * $Id: tty.c,v 1.19.2.1 1994/03/24 08:19:44 rgrimes Exp $
+ * $Id: tty.c,v 1.32 1994/05/30 21:53:17 ache Exp $
+ */
+
+/*-
+ * TODO:
+ * o Fix races for sending the start char in ttyflush().
+ * o Handle inter-byte timeout for "MIN > 0, TIME > 0" in ttselect().
+ * With luck, there will be MIN chars before select() returns().
+ * o Handle CLOCAL consistently for ptys. Perhaps disallow setting it.
+ * o Don't allow input in TS_ZOMBIE case. It would be visible through
+ * FIONREAD.
+ * o Do the new sio locking stuff here and use it to avoid special
+ * case for EXTPROC?
+ * o Lock PENDIN too?
+ * o Move EXTPROC and/or PENDIN to t_state?
+ * o Wrap most of ttioctl in spltty/splx.
*/
#include "param.h"
@@ -47,6 +62,7 @@
#include "dkstat.h"
#include "uio.h"
#include "kernel.h"
+#include "malloc.h"
#include "vnode.h"
#include "syslog.h"
#include "signalvar.h"
@@ -64,11 +80,12 @@
#define I_LOW_WATER ((TTYHOG - 2 * 256) * 7 / 8) /* XXX */
/* XXX RB_LEN() is too slow. */
-#define INPUT_LEN(tp) (RB_LEN(&(tp)->t_can) + RB_LEN(&(tp)->t_raw))
+#define INPUT_LEN(tp) (RB_LEN((tp)->t_can) + RB_LEN((tp)->t_raw))
#undef MAX_INPUT /* XXX wrong in <sys/syslimits.h> */
#define MAX_INPUT TTYHOG
static int proc_compare __P((struct proc *p1, struct proc *p2));
+static int ttnread(struct tty *tp);
static void ttyblock __P((struct tty *tp));
static void ttyecho __P((int c, struct tty *tp));
static int ttyoutput __P((int c, register struct tty *tp));
@@ -79,14 +96,6 @@ static void ttyrub __P((int c, struct tty *tp));
static void ttyrubo __P((struct tty *tp, int cnt));
static void ttyunblock __P((struct tty *tp));
-/* symbolic sleep message strings */
-const char ttyin[] = "ttyin";
-const char ttyout[] = "ttyout";
-const char ttopen[] = "ttyopn";
-const char ttclos[] = "ttycls";
-const char ttybg[] = "ttybg";
-const char ttybuf[] = "ttybuf";
-
/*
* Table giving parity for characters and indicating
* character classes to tty driver. The 8th bit
@@ -172,11 +181,19 @@ extern int nldisp;
(c) == cc[VEOL] || (c) == cc[VEOL2]) && (c) != _POSIX_VDISABLE)
void
+termioschars(t)
+ struct termios *t;
+{
+
+ bcopy(ttydefchars, t->c_cc, sizeof t->c_cc);
+}
+
+void
ttychars(tp)
struct tty *tp;
{
- bcopy(ttydefchars, tp->t_cc, sizeof(ttydefchars));
+ termioschars(&tp->t_termios);
}
/*
@@ -202,36 +219,18 @@ ttywait(tp)
{
int error = 0, s = spltty();
- while ((RB_LEN(&tp->t_out) || tp->t_state&TS_BUSY) &&
- (tp->t_state&TS_CARR_ON || tp->t_cflag&CLOCAL) &&
- tp->t_oproc) {
- /*
- * XXX temporary fix for deadlock.
- *
- * If two processes wait for output to drain from the same
- * tty, and the amount of output to drain is > 0 and
- * <= tp->t_lowat, then the processes will take turns
- * uselessly waking each other up until the output drains,
- * with cpl higher than spltty() throughout.
- *
- * The sleep address and TS_ASLEEP flag ought to be different
- * for the different events (output done) and (output almost
- * done).
- */
- tp->t_lowat = 0;
-
+ while ((RB_LEN(tp->t_out) || tp->t_state&TS_BUSY) &&
+ CAN_DO_IO(tp) && tp->t_oproc) {
(*tp->t_oproc)(tp);
- if ((RB_LEN(&tp->t_out) || tp->t_state&TS_BUSY) &&
- (tp->t_state&TS_CARR_ON || tp->t_cflag&CLOCAL)) {
- tp->t_state |= TS_ASLEEP;
- if (error = ttysleep(tp, (caddr_t)&tp->t_out,
+ if ((RB_LEN(tp->t_out) || tp->t_state&TS_BUSY) &&
+ CAN_DO_IO(tp)) {
+ tp->t_state |= TS_SO_OCOMPLETE;
+ if (error = ttysleep(tp, TSA_OCOMPLETE(tp),
TTOPRI | PCATCH, "ttywai", 0))
break;
} else
break;
}
- if (tp->t_lowat == 0)
- ttsetwater(tp);
splx(s);
return (error);
}
@@ -257,21 +256,17 @@ ttyflush(tp, rw)
tp->t_state &= ~TS_TTSTOP;
(*cdevsw[major(tp->t_dev)].d_stop)(tp, rw);
if (rw & FREAD) {
- flushq(&tp->t_can);
- flushq(&tp->t_raw);
+ flushq(tp->t_can);
+ flushq(tp->t_raw);
+ tp->t_lflag &= ~PENDIN;
tp->t_rocount = 0;
tp->t_rocol = 0;
tp->t_state &= ~TS_LOCAL;
ttwakeup(tp);
}
if (rw & FWRITE) {
- flushq(&tp->t_out);
- wakeup((caddr_t)&tp->t_out);
- if (tp->t_wsel) {
- selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
- tp->t_wsel = 0;
- tp->t_state &= ~TS_WCOLL;
- }
+ flushq(tp->t_out);
+ ttwwakeup(tp);
}
if (rw & FREAD) {
if (tp->t_state & (TS_TBLOCK | TS_HW_IFLOW)
@@ -286,16 +281,19 @@ ttyflush(tp, rw)
* is still tricky because we don't want to add a
* new obstruction to draining the output queue.
*/
- out_cc = RB_LEN(&tp->t_out);
+ out_cc = RB_LEN(tp->t_out);
t_state = tp->t_state;
ttyunblock(tp);
tp->t_state &= ~TS_TBLOCK;
- if (t_state & TS_TBLOCK && RB_LEN(&tp->t_out) != 0)
- ttysleep(tp, (caddr_t)&tp->t_out, TTIPRI,
+ if (t_state & TS_TBLOCK && RB_LEN(tp->t_out) != 0) {
+ tp->t_state |= TS_SO_OCOMPLETE;
+ ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI,
"ttyfls", hz / 10);
- if (out_cc == 0 && RB_LEN(&tp->t_out) != 0) {
+ }
+ if (out_cc != 0 && RB_LEN(tp->t_out) != 0) {
(*cdevsw[major(tp->t_dev)].d_stop)(tp, FWRITE);
- flushq(&tp->t_out);
+ flushq(tp->t_out);
+ ttwwakeup(tp);
}
}
}
@@ -314,7 +312,7 @@ ttyblock(tp)
if ((tp->t_state & TS_TBLOCK) == 0
&& tp->t_cc[VSTOP] != _POSIX_VDISABLE
- && putc(tp->t_cc[VSTOP], &tp->t_out) == 0)
+ && putc(tp->t_cc[VSTOP], tp->t_out) == 0)
tp->t_state |= TS_TBLOCK;
if (tp->t_cflag & CDTR_IFLOW)
tp->t_state |= TS_DTR_IFLOW;
@@ -334,7 +332,7 @@ ttyunblock(tp)
if (tp->t_state & TS_TBLOCK
&& tp->t_cc[VSTART] != _POSIX_VDISABLE
- && putc(tp->t_cc[VSTART], &tp->t_out) == 0)
+ && putc(tp->t_cc[VSTART], tp->t_out) == 0)
tp->t_state &= ~TS_TBLOCK;
tp->t_state &= ~TS_HW_IFLOW;
ttstart(tp);
@@ -413,7 +411,7 @@ ttioctl(tp, com, data, flag)
(p->p_sigmask & sigmask(SIGTTOU)) == 0) {
pgsignal(p->p_pgrp, SIGTTOU, 1);
if (error = ttysleep(tp, (caddr_t)&lbolt,
- TTOPRI | PCATCH, ttybg, 0))
+ TTOPRI | PCATCH, "ttybg1", 0))
return (error);
}
break;
@@ -490,11 +488,15 @@ ttioctl(tp, com, data, flag)
/* return number of characters immediately available */
case FIONREAD:
+ s = spltty();
*(off_t *)data = ttnread(tp);
+ splx(s);
break;
case TIOCOUTQ:
- *(int *)data = RB_LEN(&tp->t_out);
+ s = spltty();
+ *(int *)data = RB_LEN(tp->t_out);
+ splx(s);
break;
case TIOCSTOP:
@@ -555,47 +557,27 @@ ttioctl(tp, com, data, flag)
if (tp->t_param && (error = (*tp->t_param)(tp, t))) {
splx(s);
return (error);
- } else {
- /*
- * XXX doubtful. We mostly check both CLOCAL
- * and TS_CARR_ON before doing anything, and
- * changing TS_ISOPEN here just give another
- * flag to worry about, and is probably
- * inconsistent with not changing TS_ISOPEN
- * when carrier drops or CLOCAL rises. OTOH
- * we should maintain a flag to keep track
- * of the combination of CLOCAL and TS_CARR_ON.
- * This could be just TS_CARR_ON (if we don't
- * need to
- *
- * XXX ttselect() doesn't worry about
- * TS_ISOPEN, so it is inconsistent with
- * ttread() after TS_ISOPEN gets cleared here.
- */
- if ((tp->t_state&TS_CARR_ON) == 0 &&
- (tp->t_cflag&CLOCAL) &&
- (t->c_cflag&CLOCAL) == 0) {
- tp->t_state &= ~TS_ISOPEN;
- tp->t_state |= TS_WOPEN;
- ttwakeup(tp);
- }
- tp->t_cflag = t->c_cflag;
- tp->t_ispeed = t->c_ispeed;
- tp->t_ospeed = t->c_ospeed;
}
+ if (t->c_cflag & CLOCAL && !(tp->t_cflag & CLOCAL)) {
+ wakeup(TSA_CARR_ON(tp));
+ ttwakeup(tp);
+ ttwwakeup(tp);
+ }
+ tp->t_cflag = t->c_cflag;
+ tp->t_ispeed = t->c_ispeed;
+ tp->t_ospeed = t->c_ospeed;
ttsetwater(tp);
}
- if (com != TIOCSETAF) {
- if ((t->c_lflag&ICANON) != (tp->t_lflag&ICANON))
- if (t->c_lflag&ICANON) {
- tp->t_lflag |= PENDIN;
- ttwakeup(tp);
- }
+ if ((t->c_lflag&ICANON) != (tp->t_lflag&ICANON) &&
+ com != TIOCSETAF) {
+ if (t->c_lflag&ICANON)
+ t->c_lflag |= PENDIN;
else {
- catb(&tp->t_raw, &tp->t_can);
- catb(&tp->t_can, &tp->t_raw);
+ catb(tp->t_raw, tp->t_can);
+ catb(tp->t_can, tp->t_raw);
}
- }
+ ttwakeup(tp);
+ }
tp->t_iflag = t->c_iflag;
tp->t_oflag = t->c_oflag;
/*
@@ -606,6 +588,9 @@ ttioctl(tp, com, data, flag)
else
t->c_lflag &= ~EXTPROC;
tp->t_lflag = t->c_lflag;
+ if (t->c_cc[VMIN] != tp->t_cc[VMIN] ||
+ t->c_cc[VTIME] != tp->t_cc[VTIME])
+ ttwakeup(tp);
bcopy(t->c_cc, tp->t_cc, sizeof(t->c_cc));
splx(s);
break;
@@ -640,7 +625,13 @@ ttioctl(tp, com, data, flag)
case TIOCSPGRP: {
register struct pgrp *pgrp = pgfind(*(int *)data);
+#ifdef broken_for_F_SETOWN
+ if (!suser(p->p_ucred, &p->p_acflag))
+ ;
+ else if (!isctty(p, tp))
+#else
if (!isctty(p, tp))
+#endif
return (ENOTTY);
else if (pgrp == NULL || pgrp->pg_session != p->p_session)
return (EPERM);
@@ -668,9 +659,7 @@ ttioctl(tp, com, data, flag)
case TIOCCONS:
if (*(int *)data) {
- if (constty && constty != tp &&
- (constty->t_state & (TS_CARR_ON|TS_ISOPEN)) ==
- (TS_CARR_ON|TS_ISOPEN))
+ if (constty && constty != tp && CAN_DO_IO(constty))
return (EBUSY);
#ifndef UCONSOLE
if (error = suser(p->p_ucred, &p->p_acflag))
@@ -696,19 +685,21 @@ ttioctl(tp, com, data, flag)
return (0);
}
-int
+/*
+ * Call at spltty().
+ */
+static int
ttnread(tp)
struct tty *tp;
{
int nread = 0;
- /* XXX races. */
if (tp->t_lflag & PENDIN)
ttypend(tp);
- nread = RB_LEN(&tp->t_can);
+ nread = RB_LEN(tp->t_can);
if ((tp->t_lflag & ICANON) == 0) {
- nread += RB_LEN(&tp->t_raw);
- if (nread < tp->t_cc[VMIN])
+ nread += RB_LEN(tp->t_raw);
+ if (nread < tp->t_cc[VMIN] && tp->t_cc[VTIME] == 0)
nread = 0;
}
return (nread);
@@ -720,17 +711,14 @@ ttselect(dev, rw, p)
int rw;
struct proc *p;
{
- register struct tty *tp = &cdevsw[major(dev)].d_ttys[minor(dev)];
- int nread;
+ register struct tty *tp = cdevsw[major(dev)].d_ttys[minor(dev)];
int s = spltty();
struct proc *selp;
switch (rw) {
case FREAD:
- nread = ttnread(tp);
- if (nread > 0 ||
- ((tp->t_cflag&CLOCAL) == 0 && (tp->t_state&TS_CARR_ON) == 0))
+ if (ttnread(tp) > 0 || tp->t_state & TS_ZOMBIE)
goto win;
if (tp->t_rsel && (selp = pfind(tp->t_rsel)) && selp->p_wchan == (caddr_t)&selwait)
tp->t_state |= TS_RCOLL;
@@ -739,7 +727,8 @@ ttselect(dev, rw, p)
break;
case FWRITE:
- if (RB_LEN(&tp->t_out) <= tp->t_lowat)
+ if (RB_LEN(tp->t_out) <= tp->t_lowat && CAN_DO_IO(tp)
+ || tp->t_state & TS_ZOMBIE)
goto win;
if (tp->t_wsel && (selp = pfind(tp->t_wsel)) && selp->p_wchan == (caddr_t)&selwait)
tp->t_state |= TS_WCOLL;
@@ -766,12 +755,11 @@ ttyopen(dev, tp, dummy)
tp->t_dev = dev;
- tp->t_state &= ~TS_WOPEN;
if ((tp->t_state & TS_ISOPEN) == 0) {
tp->t_state |= TS_ISOPEN;
- initrb(&tp->t_raw);
- initrb(&tp->t_can);
- initrb(&tp->t_out);
+ initrb(tp->t_raw);
+ initrb(tp->t_can);
+ initrb(tp->t_out);
bzero((caddr_t)&tp->t_winsize, sizeof(tp->t_winsize));
}
return (0);
@@ -793,7 +781,7 @@ ttylclose(tp, flag)
}
/*
- * Handle close() on a tty line: flush and set to initial state,
+ * Handle close() on a tty line: set to initial state,
* bumping generation number so that pending read/write calls
* can detect recycling of the tty.
*/
@@ -803,7 +791,6 @@ ttyclose(tp)
{
if (constty == tp)
constty = NULL;
- ttyflush(tp, FREAD|FWRITE);
tp->t_session = NULL;
tp->t_pgrp = NULL;
tp->t_state = 0;
@@ -822,7 +809,7 @@ ttymodem(tp, flag)
int flag;
{
- if ((tp->t_state&TS_WOPEN) == 0 && (tp->t_lflag&MDMBUF)) {
+ if (tp->t_state & TS_CARR_ON && tp->t_lflag & MDMBUF) {
/*
* MDMBUF: do flow control according to carrier flag
*/
@@ -839,6 +826,7 @@ ttymodem(tp, flag)
*/
tp->t_state &= ~TS_CARR_ON;
if (tp->t_state&TS_ISOPEN && (tp->t_cflag&CLOCAL) == 0) {
+ tp->t_state |= TS_ZOMBIE;
if (tp->t_session && tp->t_session->s_leader)
psignal(tp->t_session->s_leader, SIGHUP);
ttyflush(tp, FREAD|FWRITE);
@@ -849,30 +837,9 @@ ttymodem(tp, flag)
* Carrier now on.
*/
tp->t_state |= TS_CARR_ON;
+ wakeup(TSA_CARR_ON(tp));
ttwakeup(tp);
- }
- return (1);
-}
-
-/*
- * Default modem control routine (for other line disciplines).
- * Return argument flag, to turn off device on carrier drop.
- */
-int
-nullmodem(tp, flag)
- register struct tty *tp;
- int flag;
-{
-
- if (flag)
- tp->t_state |= TS_CARR_ON;
- else {
- tp->t_state &= ~TS_CARR_ON;
- if ((tp->t_cflag&CLOCAL) == 0) {
- if (tp->t_session && tp->t_session->s_leader)
- psignal(tp->t_session->s_leader, SIGHUP);
- return (0);
- }
+ ttwwakeup(tp);
}
return (1);
}
@@ -889,12 +856,12 @@ ttypend(tp)
tp->t_lflag &= ~PENDIN;
tp->t_state |= TS_TYPEN;
- hd = tp->t_raw.rb_hd;
- tl = tp->t_raw.rb_tl;
- flushq(&tp->t_raw);
+ hd = tp->t_raw->rb_hd;
+ tl = tp->t_raw->rb_tl;
+ flushq(tp->t_raw);
while (hd != tl) {
ttyinput(*hd, tp);
- hd = RB_SUCC(&tp->t_raw, hd);
+ hd = RB_SUCC(tp->t_raw, hd);
}
tp->t_state &= ~TS_TYPEN;
}
@@ -937,7 +904,7 @@ ttyinput(c, tp)
if ((iflag & IXOFF && (tp->t_state & TS_TBLOCK) == 0
|| tp->t_cflag & TS_HW_IFLOW && (tp->t_state & TS_HW_IFLOW) == 0)
&& INPUT_LEN(tp) > I_HIGH_WATER - 3
- && ((lflag & ICANON) == 0 || RB_LEN(&tp->t_can) != 0))
+ && ((lflag & ICANON) == 0 || RB_LEN(tp->t_can) != 0))
ttyblock(tp);
/*
* Handle exceptional conditions (break, parity, framing).
@@ -959,9 +926,9 @@ ttyinput(c, tp)
parmrk:
if (INPUT_LEN(tp) > MAX_INPUT - 3)
goto input_overflow;
- putc(0377|TTY_QUOTE, &tp->t_raw);
- putc(0|TTY_QUOTE, &tp->t_raw);
- putc(c|TTY_QUOTE, &tp->t_raw);
+ putc(0377|TTY_QUOTE, tp->t_raw);
+ putc(0|TTY_QUOTE, tp->t_raw);
+ putc(c|TTY_QUOTE, tp->t_raw);
goto endcase;
} else
c = 0;
@@ -1074,23 +1041,23 @@ parmrk:
* erase (^H / ^?)
*/
if (CCEQ(cc[VERASE], c)) {
- if (RB_LEN(&tp->t_raw))
- ttyrub(unputc(&tp->t_raw), tp);
+ if (RB_LEN(tp->t_raw))
+ ttyrub(unputc(tp->t_raw), tp);
goto endcase;
}
/*
* kill (^U)
*/
if (CCEQ(cc[VKILL], c)) {
- if (lflag&ECHOKE && RB_LEN(&tp->t_raw) == tp->t_rocount &&
+ if (lflag&ECHOKE && RB_LEN(tp->t_raw) == tp->t_rocount &&
(lflag&ECHOPRT) == 0) {
- while (RB_LEN(&tp->t_raw))
- ttyrub(unputc(&tp->t_raw), tp);
+ while (RB_LEN(tp->t_raw))
+ ttyrub(unputc(tp->t_raw), tp);
} else {
ttyecho(c, tp);
if (lflag&ECHOK || lflag&ECHOKE)
ttyecho('\n', tp);
- while (getc(&tp->t_raw) > 0)
+ while (getc(tp->t_raw) > 0)
;
tp->t_rocount = 0;
}
@@ -1106,7 +1073,7 @@ parmrk:
/*
* erase whitespace
*/
- while ((c = unputc(&tp->t_raw)) == ' ' || c == '\t')
+ while ((c = unputc(tp->t_raw)) == ' ' || c == '\t')
ttyrub(c, tp);
if (c == -1)
goto endcase;
@@ -1115,14 +1082,14 @@ parmrk:
* next chars type (for ALTWERASE)
*/
ttyrub(c, tp);
- c = unputc(&tp->t_raw);
+ c = unputc(tp->t_raw);
if (c == -1)
goto endcase;
/*
* Handle one-letter word cases.
*/
if (c == ' ' || c == '\t') {
- putc(c, &tp->t_raw);
+ putc(c, tp->t_raw);
goto endcase;
}
ctype = ISALPHA(c);
@@ -1131,13 +1098,13 @@ parmrk:
*/
do {
ttyrub(c, tp);
- c = unputc(&tp->t_raw);
+ c = unputc(tp->t_raw);
if (c == -1)
goto endcase;
} while (c != ' ' && c != '\t' &&
((lflag & ALTWERASE) == 0
|| ISALPHA(c) == ctype));
- (void) putc(c, &tp->t_raw);
+ (void) putc(c, tp->t_raw);
goto endcase;
}
/*
@@ -1163,7 +1130,7 @@ parmrk:
if (INPUT_LEN(tp) >= MAX_INPUT) {
input_overflow:
if (iflag&IMAXBEL) {
- if (RB_LEN(&tp->t_out) < tp->t_hiwat)
+ if (RB_LEN(tp->t_out) < tp->t_hiwat)
(void) ttyoutput(CTRL('g'), tp);
} else
ttyflush(tp, FREAD);
@@ -1173,7 +1140,7 @@ input_overflow:
* Put data char in q for user and
* wakeup on seeing a line delimiter.
*/
- if (putc(c, &tp->t_raw) >= 0) {
+ if (putc(c, tp->t_raw) >= 0) {
if ((lflag&ICANON) == 0) {
ttwakeup(tp);
ttyecho(c, tp);
@@ -1181,7 +1148,7 @@ input_overflow:
}
if (ttbreakc(c)) {
tp->t_rocount = 0;
- catb(&tp->t_raw, &tp->t_can);
+ catb(tp->t_raw, tp->t_can);
ttwakeup(tp);
} else if (tp->t_rocount++ == 0)
tp->t_rocol = tp->t_col;
@@ -1236,7 +1203,7 @@ ttyoutput(c, tp)
if ((oflag&OPOST) == 0) {
if (tp->t_lflag&FLUSHO)
return (-1);
- if (putc(c, &tp->t_out))
+ if (putc(c, tp->t_out))
return (c);
tk_nout++;
tp->t_outcc++;
@@ -1261,17 +1228,17 @@ ttyoutput(c, tp)
#ifdef was
c -= b_to_q(" ", c, &tp->t_outq);
#else
- i = imin(c, RB_CONTIGPUT(&tp->t_out));
- bcopy(" ", tp->t_out.rb_tl, i);
- tp->t_out.rb_tl =
- RB_ROLLOVER(&tp->t_out, tp->t_out.rb_tl+i);
- i = imin(c - i, RB_CONTIGPUT(&tp->t_out));
+ i = imin(c, RB_CONTIGPUT(tp->t_out));
+ bcopy(" ", tp->t_out->rb_tl, i);
+ tp->t_out->rb_tl =
+ RB_ROLLOVER(tp->t_out, tp->t_out->rb_tl+i);
+ i = imin(c - i, RB_CONTIGPUT(tp->t_out));
/* off end and still have space? */
if (i) {
- bcopy(" ", tp->t_out.rb_tl, i);
- tp->t_out.rb_tl =
- RB_ROLLOVER(&tp->t_out, tp->t_out.rb_tl+i);
+ bcopy(" ", tp->t_out->rb_tl, i);
+ tp->t_out->rb_tl =
+ RB_ROLLOVER(tp->t_out, tp->t_out->rb_tl+i);
}
#endif
tk_nout += c;
@@ -1291,7 +1258,7 @@ ttyoutput(c, tp)
*/
if (c == '\n' && (tp->t_oflag&ONLCR) && ttyoutput('\r', tp) >= 0)
return (c);
- if ((tp->t_lflag&FLUSHO) == 0 && putc(c, &tp->t_out))
+ if ((tp->t_lflag&FLUSHO) == 0 && putc(c, tp->t_out))
return (c);
col = tp->t_col;
@@ -1342,8 +1309,8 @@ ttread(tp, uio, flag)
long slp = 0; /* XXX this should be renamed `timo'. */
loop:
- lflag = tp->t_lflag;
s = spltty();
+ lflag = tp->t_lflag;
/*
* take pending input first
*/
@@ -1351,6 +1318,7 @@ loop:
ttypend(tp);
splx(s); /* reduce latency */
s = spltty();
+ lflag = tp->t_lflag; /* XXX ttypend() clobbers it */
}
/*
@@ -1364,18 +1332,32 @@ loop:
return (EIO);
pgsignal(p->p_pgrp, SIGTTIN, 1);
if (error = ttysleep(tp, (caddr_t)&lbolt, TTIPRI | PCATCH,
- ttybg, 0))
+ "ttybg2", 0))
return (error);
goto loop;
}
+ if (tp->t_state & TS_ZOMBIE) {
+ splx(s);
+ return (0); /* EOF */
+ }
+
/*
* If canonical, use the canonical queue,
* else use the raw queue.
*/
- qp = lflag&ICANON ? &tp->t_can : &tp->t_raw;
+ qp = lflag&ICANON ? tp->t_can : tp->t_raw;
rblen = RB_LEN(qp);
-
+ if (flag & IO_NDELAY) {
+ if (rblen > 0)
+ goto read;
+ if ((lflag & ICANON) == 0 && cc[VMIN] == 0) {
+ splx(s);
+ return (0);
+ }
+ splx(s);
+ return (EWOULDBLOCK);
+ }
if ((lflag & ICANON) == 0) {
int m = cc[VMIN];
long t = cc[VTIME];
@@ -1460,45 +1442,24 @@ loop:
goto sleep;
}
if (rblen <= 0) {
- int carrier;
-
sleep:
/*
- * If there is no input, sleep on rawq
- * awaiting hardware receipt and notification.
- * If we have data, we don't need to check for carrier.
+ * There is no input, or not enough input and we can block.
*/
- carrier = (tp->t_state&TS_CARR_ON) || (tp->t_cflag&CLOCAL);
- if (!carrier && tp->t_state&TS_ISOPEN) {
- splx(s);
- return (0); /* EOF */
- }
- if (flag & IO_NDELAY) {
- splx(s);
- return (EWOULDBLOCK);
- }
- if (slp) {
- /*
- * Use plain wakeup() not ttwakeup().
- * XXX why not use the timeout built into tsleep?
- */
- timeout((timeout_func_t)wakeup, (caddr_t)qp, (int)slp);
- }
- error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH,
- carrier ? ttyin : ttopen, 0);
- if (slp) {
- slp = 0;
- untimeout((timeout_func_t)wakeup, (caddr_t)qp);
- }
+ error = ttysleep(tp, TSA_HUP_OR_INPUT(tp), TTIPRI | PCATCH,
+ CAN_DO_IO(tp) ? "ttyin" : "ttyhup", (int)slp);
splx(s);
- if (error)
+ if (error == EWOULDBLOCK)
+ error = 0;
+ else if (error)
return (error);
/*
- * XXX what happens if ICANON, MIN or TIME changes or
- * another process eats some input while we are asleep
- * (not just here)? It would be safest to detect changes
- * and reset our state variables (has_stime and last_cc).
+ * XXX what happens if another process eats some input
+ * while we are asleep (not just here)? It would be
+ * safest to detect changes and reset our state variables
+ * (has_stime and last_cc).
*/
+ slp = 0;
goto loop;
}
@@ -1548,7 +1509,8 @@ slowcase:
pgsignal(tp->t_pgrp, SIGTSTP, 1);
if (first) {
if (error = ttysleep(tp, (caddr_t)&lbolt,
- TTIPRI | PCATCH, ttybg, 0))
+ TTIPRI | PCATCH, "ttybg3",
+ 0))
break;
goto loop;
}
@@ -1612,17 +1574,17 @@ ttycheckoutq(tp, wait)
oldsig = curproc->p_sig;
else
oldsig = 0;
- if (RB_LEN(&tp->t_out) > hiwat + 200)
- while (RB_LEN(&tp->t_out) > hiwat) {
+ if (RB_LEN(tp->t_out) > hiwat + 200)
+ while (RB_LEN(tp->t_out) > hiwat) {
ttstart(tp);
+ if (RB_LEN(tp->t_out) <= hiwat)
+ break;
if (wait == 0 || (curproc && curproc->p_sig != oldsig)) {
splx(s);
return (0);
}
- timeout((timeout_func_t)wakeup, (caddr_t)&tp->t_out,
- hz); /* XXX */
- tp->t_state |= TS_ASLEEP;
- tsleep((caddr_t)&tp->t_out, PZERO - 1, "ttchout", 0);
+ tp->t_state |= TS_SO_OLOWAT;
+ tsleep(TSA_OLOWAT(tp), PZERO - 1, "ttchout", hz);
}
splx(s);
return (1);
@@ -1648,20 +1610,20 @@ ttwrite(tp, uio, flag)
error = 0;
loop:
s = spltty();
- if ((tp->t_state&TS_CARR_ON) == 0 && (tp->t_cflag&CLOCAL) == 0) {
- if (tp->t_state&TS_ISOPEN) {
- splx(s);
- return (EIO);
- } else if (flag & IO_NDELAY) {
+ if (tp->t_state & TS_ZOMBIE) {
+ splx(s);
+ if (uio->uio_resid == cnt)
+ error = EIO;
+ goto out;
+ }
+ if (!CAN_DO_IO(tp)) {
+ if (flag & IO_NDELAY) {
splx(s);
error = EWOULDBLOCK;
goto out;
} else {
- /*
- * sleep awaiting carrier
- */
- error = ttysleep(tp, (caddr_t)&tp->t_raw,
- TTIPRI | PCATCH,ttopen, 0);
+ error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH,
+ "ttydcd", 0);
splx(s);
if (error)
goto out;
@@ -1679,7 +1641,7 @@ loop:
p->p_pgrp->pg_jobc) {
pgsignal(p->p_pgrp, SIGTTOU, 1);
if (error = ttysleep(tp, (caddr_t)&lbolt, TTIPRI | PCATCH,
- ttybg, 0))
+ "ttybg4", 0))
goto out;
goto loop;
}
@@ -1704,7 +1666,7 @@ loop:
* to fix this is messy because of all the gotos.
*/
s = spltty();
- if (RB_LEN(&tp->t_out) > hiwat) {
+ if (RB_LEN(tp->t_out) > hiwat) {
splx(s);
goto ovhiwat;
}
@@ -1748,14 +1710,14 @@ loop:
ttstart(tp);
if (error = ttysleep(tp,
(caddr_t)&lbolt,
- TTOPRI | PCATCH, ttybuf, 0))
+ TTOPRI | PCATCH, "ttybf1", 0))
break;
goto loop;
}
cp++, cc--;
s = spltty();
if ((tp->t_lflag&FLUSHO) ||
- RB_LEN(&tp->t_out) > hiwat) {
+ RB_LEN(tp->t_out) > hiwat) {
splx(s);
goto ovhiwat;
}
@@ -1778,18 +1740,18 @@ loop:
#else
i = ce;
s = spltty();
- ce = imin(ce, RB_CONTIGPUT(&tp->t_out));
- bcopy(cp, tp->t_out.rb_tl, ce);
- tp->t_out.rb_tl = RB_ROLLOVER(&tp->t_out,
- tp->t_out.rb_tl + ce);
+ ce = imin(ce, RB_CONTIGPUT(tp->t_out));
+ bcopy(cp, tp->t_out->rb_tl, ce);
+ tp->t_out->rb_tl = RB_ROLLOVER(tp->t_out,
+ tp->t_out->rb_tl + ce);
i -= ce;
if (i > 0) {
int ii;
- ii = imin(i, RB_CONTIGPUT(&tp->t_out));
- bcopy(cp + ce, tp->t_out.rb_tl, ii);
- tp->t_out.rb_tl = RB_ROLLOVER(&tp->t_out,
- tp->t_out.rb_tl + ii);
+ ii = imin(i, RB_CONTIGPUT(tp->t_out));
+ bcopy(cp + ce, tp->t_out->rb_tl, ii);
+ tp->t_out->rb_tl = RB_ROLLOVER(tp->t_out,
+ tp->t_out->rb_tl + ii);
i -= ii;
ce += ii;
}
@@ -1801,14 +1763,15 @@ loop:
if (i > 0) {
ttstart(tp);
s = spltty();
- if (RB_CONTIGPUT(&tp->t_out) > 0) {
+ if (RB_CONTIGPUT(tp->t_out) > 0) {
splx(s);
goto loop; /* synchronous/fast */
}
/* out of space, wait a bit */
- tp->t_state |= TS_ASLEEP;
- if (error = ttysleep(tp, (caddr_t)&tp->t_out,
- TTOPRI | PCATCH, ttybuf, 0)) {
+ tp->t_state |= TS_SO_OLOWAT;
+ if (error = ttysleep(tp, TSA_OLOWAT(tp),
+ TTOPRI | PCATCH, "ttybf2",
+ 0)) {
splx(s);
break;
}
@@ -1816,7 +1779,7 @@ loop:
goto loop;
}
s = spltty();
- if (tp->t_lflag&FLUSHO || RB_LEN(&tp->t_out) > hiwat) {
+ if (tp->t_lflag&FLUSHO || RB_LEN(tp->t_out) > hiwat) {
splx(s);
break;
}
@@ -1841,7 +1804,7 @@ ovhiwat:
* This can only occur if FLUSHO is set in t_lflag,
* or if ttstart/oproc is synchronous (or very fast).
*/
- if (RB_LEN(&tp->t_out) <= hiwat) {
+ if (RB_LEN(tp->t_out) <= hiwat) {
splx(s);
goto loop;
}
@@ -1852,8 +1815,8 @@ ovhiwat:
return (EWOULDBLOCK);
return (0);
}
- tp->t_state |= TS_ASLEEP;
- error = ttysleep(tp, (caddr_t)&tp->t_out, TTOPRI | PCATCH, ttyout, 0);
+ tp->t_state |= TS_SO_OLOWAT;
+ error = ttysleep(tp, TSA_OLOWAT(tp), TTOPRI | PCATCH, "ttyout", 0);
splx(s);
if (error)
goto out;
@@ -1904,7 +1867,7 @@ ttyrub(c, tp)
case TAB: {
int c;
- if (tp->t_rocount < RB_LEN(&tp->t_raw)) {
+ if (tp->t_rocount < RB_LEN(tp->t_raw)) {
ttyretype(tp);
return;
}
@@ -1913,9 +1876,9 @@ ttyrub(c, tp)
tp->t_state |= TS_CNTTB;
tp->t_lflag |= FLUSHO;
tp->t_col = tp->t_rocol;
- cp = tp->t_raw.rb_hd;
- for (c = nextc(&cp, &tp->t_raw); c ;
- c = nextc(&cp, &tp->t_raw))
+ cp = tp->t_raw->rb_hd;
+ for (c = nextc(&cp, tp->t_raw); c ;
+ c = nextc(&cp, tp->t_raw))
ttyecho(c, tp);
tp->t_lflag &= ~FLUSHO;
tp->t_state &= ~TS_CNTTB;
@@ -1979,16 +1942,16 @@ ttyretype(tp)
(void) ttyoutput('\n', tp);
s = spltty();
- cp = tp->t_can.rb_hd;
- for (c = nextc(&cp, &tp->t_can); c ; c = nextc(&cp, &tp->t_can))
+ cp = tp->t_can->rb_hd;
+ for (c = nextc(&cp, tp->t_can); c ; c = nextc(&cp, tp->t_can))
ttyecho(c, tp);
- cp = tp->t_raw.rb_hd;
- for (c = nextc(&cp, &tp->t_raw); c ; c = nextc(&cp, &tp->t_raw))
+ cp = tp->t_raw->rb_hd;
+ for (c = nextc(&cp, tp->t_raw); c ; c = nextc(&cp, tp->t_raw))
ttyecho(c, tp);
tp->t_state &= ~TS_ERASE;
splx(s);
- tp->t_rocount = RB_LEN(&tp->t_raw);
+ tp->t_rocount = RB_LEN(tp->t_raw);
tp->t_rocol = 0;
}
@@ -2049,7 +2012,31 @@ ttwakeup(tp)
}
if (tp->t_state & TS_ASYNC)
pgsignal(tp->t_pgrp, SIGIO, 1);
- wakeup((caddr_t)&tp->t_raw);
+ wakeup(TSA_HUP_OR_INPUT(tp));
+}
+
+/*
+ * Wake up any writers on a tty.
+ */
+void
+ttwwakeup(tp)
+ register struct tty *tp;
+{
+ if (RB_LEN(tp->t_out) <= tp->t_lowat) {
+ if (tp->t_state & TS_SO_OCOMPLETE && RB_LEN(tp->t_out) == 0) {
+ tp->t_state &= ~TS_SO_OCOMPLETE;
+ wakeup(TSA_OCOMPLETE(tp));
+ }
+ if (tp->t_state & TS_SO_OLOWAT) {
+ tp->t_state &= ~TS_SO_OLOWAT;
+ wakeup(TSA_OLOWAT(tp));
+ }
+ if (tp->t_wsel) {
+ selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
+ tp->t_wsel = 0;
+ tp->t_state &= ~TS_WCOLL;
+ }
+ }
}
/*
@@ -2099,7 +2086,7 @@ ttyinfo(tp)
{
register struct proc *p, *pick;
struct timeval utime, stime;
- int loadtmp;
+ int loadtmp, tmp;
if (ttycheckoutq(tp,0) == 0)
return;
@@ -2152,8 +2139,13 @@ ttyinfo(tp)
* Lock out clock if process is running; get user/system
* cpu time.
*/
+ tmp = 0;
+ if (curproc == pick)
+ tmp = splclock();
utime = pick->p_utime;
stime = pick->p_stime;
+ if (curproc == pick)
+ splx(tmp);
ttyprintf(tp, " cmd: %s %d [%s] ", comm, pid, wmesg);
@@ -2258,7 +2250,7 @@ tputchar(c, tp)
{
register s = spltty();
- if ((tp->t_state & (TS_CARR_ON|TS_ISOPEN)) == (TS_CARR_ON|TS_ISOPEN)) {
+ if (CAN_DO_IO(tp)) {
if (c == '\n')
(void) ttyoutput('\r', tp);
(void) ttyoutput(c, tp);
@@ -2293,3 +2285,56 @@ ttysleep(tp, chan, pri, wmesg, timo)
return (ERESTART);
return (0);
}
+
+/*
+ * Allocate a tty structure and its associated buffers.
+ */
+struct tty *
+ttymalloc(itp)
+ struct tty *itp;
+{
+ struct tty *tp;
+
+#ifndef broken
+ /*
+ * Note that the itp input is not necessary when we can dealloc
+ * the struct tty.
+ */
+ if(itp == NULL) {
+ MALLOC(tp, struct tty *, sizeof(struct tty), M_TTYS, M_WAITOK);
+ bzero(tp, sizeof *tp);
+ } else {
+ tp = itp;
+ }
+#endif
+ if(tp->t_raw == NULL) {
+ MALLOC(tp->t_raw, struct ringb *, sizeof(struct ringb), M_TTYS, M_WAITOK);
+ bzero(tp->t_raw, sizeof *tp->t_raw);
+ }
+ if(tp->t_can == NULL) {
+ MALLOC(tp->t_can, struct ringb *, sizeof(struct ringb), M_TTYS, M_WAITOK);
+ bzero(tp->t_can, sizeof *tp->t_can);
+ }
+ if(tp->t_out == NULL) {
+ MALLOC(tp->t_out, struct ringb *, sizeof(struct ringb), M_TTYS, M_WAITOK);
+ bzero(tp->t_out, sizeof *tp->t_out);
+ }
+ return(tp);
+}
+
+/*
+ * Free a tty structure and its buffers.
+ */
+void
+ttyfree(tp)
+struct tty *tp;
+{
+ FREE(tp->t_raw, M_TTYS);
+ FREE(tp->t_can, M_TTYS);
+ FREE(tp->t_out, M_TTYS);
+ tp->t_raw = tp->t_can = tp->t_out = NULL;
+#ifdef broken /* session holds a ref to the tty; can't deallocate */
+ /* also set tp to NULL when this isn't broken anymore */
+ FREE(tp, M_TTYS);
+#endif
+}
diff --git a/sys/kern/tty_conf.c b/sys/kern/tty_conf.c
index 6b9ad4917460..ce7016415a5c 100644
--- a/sys/kern/tty_conf.c
+++ b/sys/kern/tty_conf.c
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)tty_conf.c 7.6 (Berkeley) 5/9/91
- * $Id: tty_conf.c,v 1.6.2.1 1994/05/04 07:54:52 rgrimes Exp $
+ * $Id: tty_conf.c,v 1.9 1994/05/30 03:23:14 ache Exp $
*/
#include "param.h"
@@ -85,14 +85,14 @@ struct linesw linesw[] =
#if NTB > 0
tbopen, tbclose, tbread, IE(enodev), tbioctl,
- tbinput, enodev, nullop, ttstart, nullmodem, /* 3- TABLDISC */
+ tbinput, enodev, nullop, ttstart, ttymodem, /* 3- TABLDISC */
#else
IE(enodev), VE(enodev), IE(enodev), IE(enodev), IE(enodev),
VE(enodev), IE(enodev), IE(enodev), VE(enodev), IE(enodev),
#endif
#if NSL > 0
slopen, VE(slclose), IE(enodev), IE(enodev), sltioctl,
- VE(slinput), enodev, nullop, VE(slstart), nullmodem, /* 4- SLIPDISC */
+ VE(slinput), enodev, nullop, VE(slstart), ttymodem, /* 4- SLIPDISC */
#else
IE(enodev), VE(enodev), IE(enodev), IE(enodev), IE(enodev),
VE(enodev), IE(enodev), IE(enodev), VE(enodev), IE(enodev),
diff --git a/sys/kern/tty_pty.c b/sys/kern/tty_pty.c
index 0e99e9329e0a..8039fa952b94 100644
--- a/sys/kern/tty_pty.c
+++ b/sys/kern/tty_pty.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)tty_pty.c 7.21 (Berkeley) 5/30/91
- * $Id: tty_pty.c,v 1.9 1994/01/29 04:04:26 davidg Exp $
+ * $Id: tty_pty.c,v 1.14 1994/05/30 03:22:34 ache Exp $
*/
/*
@@ -66,7 +66,7 @@ static void ptcwakeup(struct tty *, int);
* pts == /dev/tty[pqrs]?
* ptc == /dev/pty[pqrs]?
*/
-struct tty pt_tty[NPTY];
+struct tty *pt_tty[NPTY];
struct pt_ioctl {
int pt_flags;
pid_t pt_selr, pt_selw;
@@ -75,13 +75,15 @@ struct pt_ioctl {
} pt_ioctl[NPTY];
int npty = NPTY; /* for pstat -t */
-#define PF_RCOLL 0x01
-#define PF_WCOLL 0x02
-#define PF_PKT 0x08 /* packet mode */
-#define PF_STOPPED 0x10 /* user told stopped */
-#define PF_REMOTE 0x20 /* remote and flow controlled input */
-#define PF_NOSTOP 0x40
-#define PF_UCNTL 0x80 /* user control mode */
+#define PF_RCOLL 0x0001
+#define PF_WCOLL 0x0002
+#define PF_PKT 0x0008 /* packet mode */
+#define PF_STOPPED 0x0010 /* user told stopped */
+#define PF_REMOTE 0x0020 /* remote and flow controlled input */
+#define PF_NOSTOP 0x0040
+#define PF_UCNTL 0x0080 /* user control mode */
+#define PF_COPEN 0x0100 /* master open */
+#define PF_SOPEN 0x0200 /* slave open */
/*ARGSUSED*/
int
@@ -99,9 +101,8 @@ ptsopen(dev, flag, devtype, p)
#endif
if (minor(dev) >= NPTY)
return (ENXIO);
- tp = &pt_tty[minor(dev)];
+ tp = pt_tty[minor(dev)] = ttymalloc(pt_tty[minor(dev)]);
if ((tp->t_state & TS_ISOPEN) == 0) {
- tp->t_state |= TS_WOPEN;
ttychars(tp); /* Set up default chars */
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
@@ -114,15 +115,17 @@ ptsopen(dev, flag, devtype, p)
if (tp->t_oproc) /* Ctrlr still around. */
tp->t_state |= TS_CARR_ON;
while ((tp->t_state & TS_CARR_ON) == 0) {
- tp->t_state |= TS_WOPEN;
if (flag&FNONBLOCK)
break;
- if (error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH,
- ttopen, 0))
+ if (error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH,
+ "ptsopn", 0))
return (error);
}
error = (*linesw[tp->t_line].l_open)(dev, tp, flag);
- ptcwakeup(tp, FREAD|FWRITE);
+ if (error == 0) {
+ ptcwakeup(tp, FREAD|FWRITE);
+ pt_ioctl[minor(dev)].pt_flags |= PF_SOPEN;
+ }
return (error);
}
@@ -134,10 +137,17 @@ ptsclose(dev, flag, mode, p)
{
register struct tty *tp;
- tp = &pt_tty[minor(dev)];
+ tp = pt_tty[minor(dev)];
+ ptcwakeup(tp, FREAD|FWRITE);
(*linesw[tp->t_line].l_close)(tp, flag);
ttyclose(tp);
- ptcwakeup(tp, FREAD|FWRITE);
+ pt_ioctl[minor(dev)].pt_flags &= ~PF_SOPEN;
+ if ((pt_ioctl[minor(dev)].pt_flags & PF_COPEN) == 0) {
+ ttyfree(tp);
+#ifdef broken /* session holds a ref to the tty; can't deallocate */
+ pt_tty[minor(dev)] = (struct tty *)NULL;
+#endif
+ }
return(0);
}
@@ -148,7 +158,7 @@ ptsread(dev, uio, flag)
int flag;
{
struct proc *p = curproc;
- register struct tty *tp = &pt_tty[minor(dev)];
+ register struct tty *tp = pt_tty[minor(dev)];
register struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
int error = 0;
@@ -162,25 +172,25 @@ again:
return (EIO);
pgsignal(p->p_pgrp, SIGTTIN, 1);
if (error = ttysleep(tp, (caddr_t)&lbolt,
- TTIPRI | PCATCH, ttybg, 0))
+ TTIPRI | PCATCH, "ptsbg", 0))
return (error);
}
- if (RB_LEN(&tp->t_can) == 0) {
+ if (RB_LEN(tp->t_can) == 0) {
if (flag & IO_NDELAY)
return (EWOULDBLOCK);
- if (error = ttysleep(tp, (caddr_t)&tp->t_can,
- TTIPRI | PCATCH, ttyin, 0))
+ if (error = ttysleep(tp, (caddr_t)tp->t_can,
+ TTIPRI | PCATCH, "ptsin", 0))
return (error);
goto again;
}
- while (RB_LEN(&tp->t_can) > 1 && uio->uio_resid > 0)
- if (ureadc(getc(&tp->t_can), uio) < 0) {
+ while (RB_LEN(tp->t_can) > 1 && uio->uio_resid > 0)
+ if (ureadc(getc(tp->t_can), uio) < 0) {
error = EFAULT;
break;
}
- if (RB_LEN(&tp->t_can) == 1)
- (void) getc(&tp->t_can);
- if (RB_LEN(&tp->t_can))
+ if (RB_LEN(tp->t_can) == 1)
+ (void) getc(tp->t_can);
+ if (RB_LEN(tp->t_can))
return (error);
} else
if (tp->t_oproc)
@@ -202,7 +212,7 @@ ptswrite(dev, uio, flag)
{
register struct tty *tp;
- tp = &pt_tty[minor(dev)];
+ tp = pt_tty[minor(dev)];
if (tp->t_oproc == 0)
return (EIO);
return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
@@ -240,7 +250,7 @@ ptcwakeup(tp, flag)
pti->pt_selr = 0;
pti->pt_flags &= ~PF_RCOLL;
}
- wakeup((caddr_t)&tp->t_out.rb_tl);
+ wakeup(TSA_PTC_READ(tp));
}
if (flag & FWRITE) {
if (pti->pt_selw) {
@@ -248,7 +258,7 @@ ptcwakeup(tp, flag)
pti->pt_selw = 0;
pti->pt_flags &= ~PF_WCOLL;
}
- wakeup((caddr_t)&tp->t_raw.rb_hd);
+ wakeup(TSA_PTC_WRITE(tp));
}
}
@@ -268,14 +278,15 @@ ptcopen(dev, flag, devtype, p)
if (minor(dev) >= NPTY)
return (ENXIO);
- tp = &pt_tty[minor(dev)];
+ tp = pt_tty[minor(dev)] = ttymalloc(pt_tty[minor(dev)]);
if (tp->t_oproc)
return (EIO);
tp->t_oproc = ptsstart;
(void)(*linesw[tp->t_line].l_modem)(tp, 1);
tp->t_lflag &= ~EXTPROC;
pti = &pt_ioctl[minor(dev)];
- pti->pt_flags = 0;
+ pti->pt_flags &= PF_SOPEN;
+ pti->pt_flags |= PF_COPEN;
pti->pt_send = 0;
pti->pt_ucntl = 0;
return (0);
@@ -289,7 +300,7 @@ ptcclose(dev)
{
register struct tty *tp;
- tp = &pt_tty[minor(dev)];
+ tp = pt_tty[minor(dev)];
(void)(*linesw[tp->t_line].l_modem)(tp, 0);
tp->t_state &= ~TS_CARR_ON;
tp->t_oproc = 0; /* mark closed */
@@ -298,6 +309,13 @@ ptcclose(dev)
if (constty==tp)
constty = 0;
+ pt_ioctl[minor(dev)].pt_flags &= ~PF_COPEN;
+ if ((pt_ioctl[minor(dev)].pt_flags & PF_SOPEN) == 0) {
+ ttyfree(tp);
+#ifdef broken /* session holds a ref to the tty; can't deallocate */
+ pt_tty[minor(dev)] = (struct tty *)NULL;
+#endif
+ }
return (0);
}
@@ -307,7 +325,7 @@ ptcread(dev, uio, flag)
struct uio *uio;
int flag;
{
- register struct tty *tp = &pt_tty[minor(dev)];
+ register struct tty *tp = pt_tty[minor(dev)];
struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
char buf[BUFSIZ];
int error = 0, cc;
@@ -339,15 +357,15 @@ ptcread(dev, uio, flag)
pti->pt_ucntl = 0;
return (0);
}
- if (RB_LEN(&tp->t_out) && (tp->t_state&TS_TTSTOP) == 0)
+ if (RB_LEN(tp->t_out) && (tp->t_state&TS_TTSTOP) == 0)
break;
}
if ((tp->t_state&TS_CARR_ON) == 0)
return (0); /* EOF */
if (flag & IO_NDELAY)
return (EWOULDBLOCK);
- if (error = tsleep((caddr_t)&tp->t_out.rb_tl, TTIPRI | PCATCH,
- ttyin, 0))
+ if (error = tsleep(TSA_PTC_READ(tp), TTIPRI | PCATCH,
+ "ptcin", 0))
return (error);
}
if (pti->pt_flags & (PF_PKT|PF_UCNTL))
@@ -356,28 +374,19 @@ ptcread(dev, uio, flag)
#ifdef was
cc = q_to_b(&tp->t_outq, buf, MIN(uio->uio_resid, BUFSIZ));
#else
- cc = min(MIN(uio->uio_resid, BUFSIZ), RB_CONTIGGET(&tp->t_out));
+ cc = min(MIN(uio->uio_resid, BUFSIZ), RB_CONTIGGET(tp->t_out));
if (cc) {
- bcopy(tp->t_out.rb_hd, buf, cc);
- tp->t_out.rb_hd =
- RB_ROLLOVER(&tp->t_out, tp->t_out.rb_hd+cc);
+ bcopy(tp->t_out->rb_hd, buf, cc);
+ tp->t_out->rb_hd =
+ RB_ROLLOVER(tp->t_out, tp->t_out->rb_hd+cc);
}
#endif
if (cc <= 0)
break;
error = uiomove(buf, cc, uio);
}
- if (RB_LEN(&tp->t_out) <= tp->t_lowat) {
- if (tp->t_state&TS_ASLEEP) {
- tp->t_state &= ~TS_ASLEEP;
- wakeup((caddr_t)&tp->t_out);
- }
- if (tp->t_wsel) {
- selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
- tp->t_wsel = 0;
- tp->t_state &= ~TS_WCOLL;
- }
- }
+ if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel)
+ ttwwakeup(tp);
return (error);
}
@@ -411,7 +420,7 @@ ptcselect(dev, rw, p)
int rw;
struct proc *p;
{
- register struct tty *tp = &pt_tty[minor(dev)];
+ register struct tty *tp = pt_tty[minor(dev)];
struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
struct proc *prev;
int s;
@@ -426,7 +435,7 @@ ptcselect(dev, rw, p)
*/
s = spltty();
if ((tp->t_state&TS_ISOPEN) &&
- RB_LEN(&tp->t_out) && (tp->t_state&TS_TTSTOP) == 0) {
+ RB_LEN(tp->t_out) && (tp->t_state&TS_TTSTOP) == 0) {
splx(s);
return (1);
}
@@ -448,12 +457,12 @@ ptcselect(dev, rw, p)
case FWRITE:
if (tp->t_state&TS_ISOPEN) {
if (pti->pt_flags & PF_REMOTE) {
- if (RB_LEN(&tp->t_can) == 0)
+ if (RB_LEN(tp->t_can) == 0)
return (1);
} else {
- if (RB_LEN(&tp->t_raw) + RB_LEN(&tp->t_can) < TTYHOG-2)
+ if (RB_LEN(tp->t_raw) + RB_LEN(tp->t_can) < TTYHOG-2)
return (1);
- if (RB_LEN(&tp->t_can) == 0 && (tp->t_iflag&ICANON))
+ if (RB_LEN(tp->t_can) == 0 && (tp->t_iflag&ICANON))
return (1);
}
}
@@ -473,7 +482,7 @@ ptcwrite(dev, uio, flag)
register struct uio *uio;
int flag;
{
- register struct tty *tp = &pt_tty[minor(dev)];
+ register struct tty *tp = pt_tty[minor(dev)];
register u_char *cp = 0;
register int cc = 0;
u_char locbuf[BUFSIZ];
@@ -485,12 +494,12 @@ again:
if ((tp->t_state&TS_ISOPEN) == 0)
goto block;
if (pti->pt_flags & PF_REMOTE) {
- if (RB_LEN(&tp->t_can))
+ if (RB_LEN(tp->t_can))
goto block;
- while (uio->uio_resid > 0 && RB_LEN(&tp->t_can) < TTYHOG - 1) {
+ while (uio->uio_resid > 0 && RB_LEN(tp->t_can) < TTYHOG - 1) {
if (cc == 0) {
cc = min(uio->uio_resid, BUFSIZ);
- cc = min(cc, RB_CONTIGPUT(&tp->t_can));
+ cc = min(cc, RB_CONTIGPUT(tp->t_can));
cp = locbuf;
error = uiomove((caddr_t)cp, cc, uio);
if (error)
@@ -504,16 +513,16 @@ again:
(void) b_to_q((char *)cp, cc, &tp->t_canq);
#else
if (cc) {
- bcopy(cp, tp->t_can.rb_tl, cc);
- tp->t_can.rb_tl =
- RB_ROLLOVER(&tp->t_can, tp->t_can.rb_tl+cc);
+ bcopy(cp, tp->t_can->rb_tl, cc);
+ tp->t_can->rb_tl =
+ RB_ROLLOVER(tp->t_can, tp->t_can->rb_tl+cc);
}
#endif
cc = 0;
}
- (void) putc(0, &tp->t_can);
+ (void) putc(0, tp->t_can);
ttwakeup(tp);
- wakeup((caddr_t)&tp->t_can);
+ wakeup((caddr_t)tp->t_can);
return (0);
}
while (uio->uio_resid > 0) {
@@ -528,9 +537,9 @@ again:
return (EIO);
}
while (cc > 0) {
- if ((RB_LEN(&tp->t_raw) + RB_LEN(&tp->t_can)) >= TTYHOG - 2 &&
- (RB_LEN(&tp->t_can) > 0 || !(tp->t_iflag&ICANON))) {
- wakeup((caddr_t)&tp->t_raw);
+ if ((RB_LEN(tp->t_raw) + RB_LEN(tp->t_can)) >= TTYHOG - 2 &&
+ (RB_LEN(tp->t_can) > 0 || !(tp->t_iflag&ICANON))) {
+ wakeup(TSA_HUP_OR_INPUT(tp));
goto block;
}
(*linesw[tp->t_line].l_rint)(*cp++, tp);
@@ -554,8 +563,8 @@ block:
return (EWOULDBLOCK);
return (0);
}
- if (error = tsleep((caddr_t)&tp->t_raw.rb_hd, TTOPRI | PCATCH,
- ttyout, 0)) {
+ if (error = tsleep(TSA_PTC_WRITE(tp), TTOPRI | PCATCH,
+ "ptcout", 0)) {
/* adjust for data copied in but not written */
uio->uio_resid += cc;
return (error);
@@ -571,7 +580,7 @@ ptyioctl(dev, cmd, data, flag)
dev_t dev;
int flag;
{
- register struct tty *tp = &pt_tty[minor(dev)];
+ register struct tty *tp = pt_tty[minor(dev)];
register struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
register u_char *cc = tp->t_cc;
int stop, error;
@@ -648,7 +657,7 @@ ptyioctl(dev, cmd, data, flag)
case TIOCSETA:
case TIOCSETAW:
case TIOCSETAF:
- while (getc(&tp->t_out) >= 0)
+ while (getc(tp->t_out) >= 0)
;
break;
diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c
index c0cf7a4c68cd..b735b0269666 100644
--- a/sys/kern/uipc_mbuf.c
+++ b/sys/kern/uipc_mbuf.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)uipc_mbuf.c 7.19 (Berkeley) 4/20/91
- * $Id: uipc_mbuf.c,v 1.6 1993/12/19 00:51:44 wollman Exp $
+ * $Id: uipc_mbuf.c,v 1.9 1994/06/11 23:06:35 paul Exp $
*/
#include "param.h"
@@ -312,7 +312,8 @@ m_copym(m, off0, len, wait)
n->m_len = MIN(len, m->m_len - off);
if (m->m_flags & M_EXT) {
n->m_data = m->m_data + off;
- mclrefcnt[mtocl(m->m_ext.ext_buf)]++;
+ if (m->m_ext.ext_free == (void (*)())0)
+ mclrefcnt[mtocl(m->m_ext.ext_buf)]++;
n->m_ext = m->m_ext;
n->m_flags |= M_EXT;
} else
@@ -441,8 +442,8 @@ m_adj(mp, req_len)
}
if (m->m_len >= len) {
m->m_len -= len;
- if ((mp = m)->m_flags & M_PKTHDR)
- m->m_pkthdr.len -= len;
+ if (mp->m_flags & M_PKTHDR)
+ mp->m_pkthdr.len -= len;
return;
}
count -= len;
@@ -639,7 +640,8 @@ extpacket:
if (m -> m_flags & M_EXT) {
n -> m_flags |= M_EXT;
n -> m_ext = m -> m_ext;
- mclrefcnt[mtocl (m -> m_ext.ext_buf)]++;
+ if (m->m_ext.ext_free == (void (*)())0)
+ mclrefcnt[mtocl (m -> m_ext.ext_buf)]++;
n -> m_data = m -> m_data + len;
} else {
bcopy (mtod (m, caddr_t) + len, mtod (n, caddr_t), remain);
@@ -724,11 +726,6 @@ m_compress(in, out)
(*out)->m_act = 0;
while (in) {
- if (in->m_flags & M_EXT) {
-#ifdef DEBUG
- ASSERT(in->m_len == 0);
-#endif
- }
if ( in->m_len == 0) {
in = in->m_next;
continue;
diff --git a/sys/kern/vfs__bio.c b/sys/kern/vfs__bio.c
index 2ccf2e6126b7..6868618e9293 100644
--- a/sys/kern/vfs__bio.c
+++ b/sys/kern/vfs__bio.c
@@ -45,7 +45,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: vfs__bio.c,v 1.15 1994/01/31 05:57:45 davidg Exp $
+ * $Id: vfs__bio.c,v 1.21 1994/05/29 18:06:31 ats Exp $
*/
#include "param.h"
@@ -73,7 +73,13 @@ struct buf bswlist; /* head of free swap header list */
struct buf *bclnlist; /* head of cleaned page list */
static struct buf *getnewbuf(int);
-extern vm_map_t buffer_map;
+extern vm_map_t buffer_map, io_map;
+
+/*
+ * Internel update daemon, process 3
+ * The variable vfs_update_wakeup allows for internal syncs.
+ */
+int vfs_update_wakeup;
/*
* Initialize buffer headers and related structures.
@@ -353,18 +359,8 @@ start:
&& bfreelist[BQ_EMPTY].av_forw != (struct buf *)bfreelist+BQ_EMPTY) {
caddr_t addr;
-/*#define notyet*/
-#ifndef notyet
- if ((addr = malloc (sz, M_IOBUF, M_WAITOK)) == 0) goto tryfree;
-#else /* notyet */
- /* get new memory buffer */
- if (round_page(sz) == sz)
- addr = (caddr_t) kmem_alloc_wired_wait(buffer_map, sz);
- else
- addr = (caddr_t) malloc (sz, M_IOBUF, M_WAITOK);
- /*if ((addr = malloc (sz, M_IOBUF, M_NOWAIT)) == 0) goto tryfree;*/
- bzero(addr, sz);
-#endif /* notyet */
+ if ((addr = malloc (sz, M_IOBUF, M_NOWAIT)) == 0)
+ goto tryfree;
freebufspace -= sz;
allocbufspace += sz;
@@ -476,7 +472,14 @@ loop:
/* if (bp->b_bufsize != size) allocbuf(bp, size); */
} else {
- if ((bp = getnewbuf(size)) == 0) goto loop;
+ if ((bp = getnewbuf(size)) == 0)
+ goto loop;
+ if ( incore(vp, blkno)) {
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ goto loop;
+ }
+
bp->b_blkno = bp->b_lblkno = blkno;
bgetvp(vp, bp);
bh = BUFHASH(vp, blkno);
@@ -521,27 +524,13 @@ allocbuf(register struct buf *bp, int size)
caddr_t newcontents;
/* get new memory buffer */
-#ifndef notyet
newcontents = (caddr_t) malloc (size, M_IOBUF, M_WAITOK);
-#else /* notyet */
- if (round_page(size) == size)
- newcontents = (caddr_t) kmem_alloc_wired_wait(buffer_map, size);
- else
- newcontents = (caddr_t) malloc (size, M_IOBUF, M_WAITOK);
-#endif /* notyet */
/* copy the old into the new, up to the maximum that will fit */
bcopy (bp->b_un.b_addr, newcontents, min(bp->b_bufsize, size));
/* return old contents to free heap */
-#ifndef notyet
free (bp->b_un.b_addr, M_IOBUF);
-#else /* notyet */
- if (round_page(bp->b_bufsize) == bp->b_bufsize)
- kmem_free_wakeup(buffer_map, bp->b_un.b_addr, bp->b_bufsize);
- else
- free (bp->b_un.b_addr, M_IOBUF);
-#endif /* notyet */
/* adjust buffer cache's idea of memory allocated to buffer contents */
freebufspace -= size - bp->b_bufsize;
@@ -593,6 +582,39 @@ biowait(register struct buf *bp)
void
biodone(register struct buf *bp)
{
+ int s;
+ s = splbio();
+ if (bp->b_flags & B_CLUSTER) {
+ struct buf *tbp;
+ bp->b_resid = bp->b_bcount;
+ while ( tbp = bp->b_clusterf) {
+ bp->b_clusterf = tbp->av_forw;
+ bp->b_resid -= tbp->b_bcount;
+ tbp->b_resid = 0;
+ if( bp->b_resid <= 0) {
+ tbp->b_error = bp->b_error;
+ tbp->b_flags |= (bp->b_flags & B_ERROR);
+ tbp->b_resid = -bp->b_resid;
+ bp->b_resid = 0;
+ }
+/*
+ printf("rdc (%d,%d,%d) ", tbp->b_blkno, tbp->b_bcount, tbp->b_resid);
+*/
+
+ biodone(tbp);
+ }
+#ifndef NOBOUNCE
+ vm_bounce_kva_free( bp->b_un.b_addr, bp->b_bufsize, 0);
+#endif
+ relpbuf(bp);
+ splx(s);
+ return;
+ }
+
+#ifndef NOBOUNCE
+ if (bp->b_flags & B_BOUNCE)
+ vm_bounce_free(bp);
+#endif
bp->b_flags |= B_DONE;
if ((bp->b_flags & B_READ) == 0) {
@@ -603,6 +625,7 @@ biodone(register struct buf *bp)
if (bp->b_flags & B_CALL) {
bp->b_flags &= ~B_CALL;
(*bp->b_iodone)(bp);
+ splx(s);
return;
}
@@ -618,19 +641,21 @@ biodone(register struct buf *bp)
bp->b_flags &= ~B_WANTED;
wakeup((caddr_t) bp);
}
+ splx(s);
}
-/*
- * Internel update daemon, process 3
- * The variable vfs_update_wakeup allows for internal syncs.
- */
-int vfs_update_wakeup;
+#ifndef UPDATE_INTERVAL
+int vfs_update_interval = 30;
+#else
+int vfs_update_interval = UPDATE_INTERVAL;
+#endif
void
vfs_update() {
(void) spl0();
while(1) {
- tsleep((caddr_t)&vfs_update_wakeup, PRIBIO, "update", hz*30);
+ tsleep((caddr_t)&vfs_update_wakeup, PRIBIO, "update",
+ hz * vfs_update_interval);
vfs_update_wakeup = 0;
sync(curproc, NULL, NULL);
}
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
index f35002a9460f..37a406d6651e 100644
--- a/sys/kern/vfs_lookup.c
+++ b/sys/kern/vfs_lookup.c
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)vfs_lookup.c 7.32 (Berkeley) 5/21/91
- * $Id: vfs_lookup.c,v 1.5.2.1 1994/05/04 07:54:56 rgrimes Exp $
+ * $Id: vfs_lookup.c,v 1.9 1994/06/02 06:53:37 ache Exp $
*/
#include "param.h"
@@ -106,6 +106,13 @@ namei(ndp, p)
else
error = copyinstr(ndp->ni_dirp, ndp->ni_pnbuf,
MAXPATHLEN, (u_int *)&ndp->ni_pathlen);
+#if 0
+ /*
+ * Don't allow empty pathname.
+ */
+ if (!error && *ndp->ni_pnbuf == '\0')
+ error = ENOENT;
+#endif
if (error) {
free(ndp->ni_pnbuf, M_NAMEI);
ndp->ni_vp = NULL;
@@ -284,10 +291,10 @@ dirloop:
* responsibility for freeing the pathname buffer.
*/
ndp->ni_hash = 0;
- for (cp = ndp->ni_ptr; *cp != 0 && *cp != '/'; cp++)
+ for (cp = ndp->ni_ptr; *cp != '\0' && *cp != '/'; cp++)
ndp->ni_hash += (unsigned char)*cp;
ndp->ni_namelen = cp - ndp->ni_ptr;
- if (ndp->ni_namelen >= NAME_MAX) {
+ if (ndp->ni_namelen > NAME_MAX) {
error = ENAMETOOLONG;
goto bad;
}
@@ -367,7 +374,7 @@ dirloop:
printf("not found\n");
#endif
if (flag == LOOKUP || flag == DELETE ||
- error != ENOENT || *cp != 0)
+ error != ENOENT || *cp != '\0')
goto bad;
/*
* If creating and at end of pathname, then can consider
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
index 024c5679000b..5525022e9811 100644
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)vfs_subr.c 7.60 (Berkeley) 6/21/91
- * $Id: vfs_subr.c,v 1.7.2.2 1994/05/04 07:55:00 rgrimes Exp $
+ * $Id: vfs_subr.c,v 1.13 1994/06/14 03:41:00 davidg Exp $
*/
/*
@@ -57,6 +57,9 @@
#include "buf.h"
#include "errno.h"
#include "malloc.h"
+#include "vm/vm.h"
+#include "vm/vm_object.h"
+#include "vm/vm_pager.h"
static void insmntque(struct vnode *, struct mount *);
@@ -441,6 +444,8 @@ vinvalbuf(vp, save)
register struct buf *bp;
struct buf *nbp, *blist;
int s, dirty = 0;
+ vm_pager_t pager;
+ vm_object_t object;
for (;;) {
if (blist = vp->v_dirtyblkhd)
@@ -473,6 +478,20 @@ vinvalbuf(vp, save)
brelse(bp);
}
}
+
+ pager = (vm_pager_t)vp->v_vmdata;
+ if (pager != NULL) {
+ object = vm_object_lookup(pager);
+ if (object) {
+ vm_object_lock(object);
+ if (save)
+ vm_object_page_clean(object, 0, 0);
+ vm_object_page_remove(object, 0, object->size);
+ vm_object_unlock(object);
+ vm_object_deallocate(object);
+ }
+ }
+
if (vp->v_dirtyblkhd || vp->v_cleanblkhd)
panic("vinvalbuf: flush failed");
return (dirty);
@@ -645,6 +664,7 @@ loop:
nvp->v_hashchain = vpp;
nvp->v_specnext = *vpp;
nvp->v_specflags = 0;
+ nvp->v_opencount = 0;
*vpp = nvp;
if (vp != NULL) {
nvp->v_flag |= VALIASED;
@@ -1068,6 +1088,7 @@ vfinddev(dev, type, vpp)
/*
* Calculate the total number of references to a special device.
+ * Not counting sleeping openers.
*/
int
vcount(vp)
@@ -1089,7 +1110,7 @@ loop:
vgone(vq);
goto loop;
}
- count += vq->v_usecount;
+ count += vq->v_usecount - vq->v_opencount;
}
return (count);
}
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 396bd6326f98..b218ab6dd380 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)vfs_syscalls.c 7.74 (Berkeley) 6/21/91
- * $Id: vfs_syscalls.c,v 1.10 1994/01/19 21:09:13 jtc Exp $
+ * $Id: vfs_syscalls.c,v 1.17 1994/05/26 05:32:05 ache Exp $
*/
#include "param.h"
@@ -841,9 +841,10 @@ link(p, uap, retval)
if (error = namei(ndp, p))
return (error);
vp = ndp->ni_vp;
- if (vp->v_type == VDIR &&
- (error = suser(p->p_ucred, &p->p_acflag)))
+ if (vp->v_type == VDIR) {
+ error = EPERM;
goto out1;
+ }
ndp->ni_nameiop = CREATE | LOCKPARENT;
ndp->ni_dirp = (caddr_t)uap->linkname;
if (error = namei(ndp, p))
diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c
index b273dc8b3604..a9c03b99d6ad 100644
--- a/sys/kern/vfs_vnops.c
+++ b/sys/kern/vfs_vnops.c
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)vfs_vnops.c 7.33 (Berkeley) 6/27/91
- * $Id: vfs_vnops.c,v 1.4.2.2 1994/05/04 07:55:04 rgrimes Exp $
+ * $Id: vfs_vnops.c,v 1.7 1994/05/04 08:27:20 rgrimes Exp $
*/
#include "param.h"
@@ -388,6 +388,7 @@ vn_ioctl(fp, com, data, p)
default:
return (ENOTTY);
+ case VPROC:
case VFIFO:
case VCHR:
case VBLK:
diff --git a/sys/net/if.c b/sys/net/if.c
index fc51448e9ef5..42a7a5de4a70 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)if.c 7.14 (Berkeley) 4/20/91
- * $Id: if.c,v 1.7 1993/12/19 00:52:00 wollman Exp $
+ * $Id: if.c,v 1.9 1994/06/10 11:10:24 ache Exp $
*/
#include "param.h"
@@ -204,7 +204,7 @@ ifa_ifwithdstaddr(addr)
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != addr->sa_family)
continue;
- if (equal(addr, ifa->ifa_dstaddr))
+ if (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr))
return (ifa);
}
return ((struct ifaddr *)0);
@@ -506,7 +506,8 @@ ifioctl(so, cmd, data, p)
return (EOPNOTSUPP);
#ifndef COMPAT_43
return ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
- cmd, data, ifp));
+ (struct mbuf *)cmd, (struct mbuf *)data,
+ (struct mbuf *)ifp, (struct mbuf *)0));
#else
{
int ocmd = cmd;
diff --git a/sys/net/if.h b/sys/net/if.h
index e1d7f316b04a..b434f795a59f 100644
--- a/sys/net/if.h
+++ b/sys/net/if.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)if.h 7.11 (Berkeley) 3/19/91
- * $Id: if.h,v 1.11 1993/12/19 00:52:02 wollman Exp $
+ * $Id: if.h,v 1.12 1994/05/17 22:30:53 jkh Exp $
*/
#ifndef _NET_IF_H_
@@ -81,7 +81,7 @@ struct ifnet {
char *if_name; /* name, e.g. ``en'' or ``lo'' */
short if_unit; /* sub-unit for lower level driver */
u_short if_mtu; /* maximum transmission unit */
- short if_flags; /* up/down, broadcast, etc. */
+ u_int if_flags; /* up/down, broadcast, etc. */
short if_timer; /* time 'til if_watchdog called */
int if_metric; /* routing metric (external only) */
struct ifaddr *if_addrlist; /* linked list of addresses per if */
@@ -143,12 +143,18 @@ struct ifnet {
#define IFF_LLC2 0x4000 /* IEEE 802.2 LLC class 2 */
#define IFF_ALTPHYS 0x8000 /* alternative physical connection */
#define IFF_MULTICAST 0x10000 /* i'face supports multicast */
+#ifdef notdef
#define IFF_VIRTUAL 0x20000 /* i'face is really a VIF */
/* flags set internally only: */
#define IFF_CANTCHANGE \
(IFF_BROADCAST|IFF_POINTOPOINT|IFF_RUNNING|IFF_OACTIVE|IFF_SIMPLEX\
|IFF_MULTICAST|IFF_VIRTUAL)
+#else
+#define IFF_CANTCHANGE \
+ (IFF_BROADCAST|IFF_POINTOPOINT|IFF_RUNNING|IFF_OACTIVE|IFF_SIMPLEX\
+ |IFF_MULTICAST)
+#endif
/*
* Output queues (ifp->if_snd) and internetwork datagram level (pup level 1)
@@ -222,7 +228,7 @@ struct ifreq {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
- short ifru_flags;
+ u_int ifru_flags;
int ifru_metric;
caddr_t ifru_data;
} ifr_ifru;
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c
index 348d51dd148f..9d7e897c32cb 100644
--- a/sys/net/if_ethersubr.c
+++ b/sys/net/if_ethersubr.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1982, 1989 Regents of the University of California.
+ * Copyright (c) 1982, 1989, 1993 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)if_ethersubr.c 7.13 (Berkeley) 4/20/91
- * $Id: if_ethersubr.c,v 1.4 1993/11/25 01:34:02 wollman Exp $
+ * $Id: if_ethersubr.c,v 1.5 1994/05/17 22:30:54 jkh Exp $
*/
#include "param.h"
@@ -110,22 +110,12 @@ ether_output(ifp, m0, dst, rt)
idst = ((struct sockaddr_in *)dst)->sin_addr;
if (!arpresolve(ac, m, &idst, edst, &usetrailers))
return (0); /* if not yet resolved */
- if ((ifp->if_flags & IFF_SIMPLEX) && (*edst & 1))
+ /* If broadcasting on a simplex interface, loopback a copy */
+ if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
mcopy = m_copy(m, 0, (int)M_COPYALL);
off = m->m_pkthdr.len - m->m_len;
- if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
- (m->m_flags & M_EXT) == 0 &&
- m->m_data >= m->m_pktdat + 2 * sizeof (u_short)) {
- type = ETHERTYPE_TRAIL + (off>>9);
- m->m_data -= 2 * sizeof (u_short);
- m->m_len += 2 * sizeof (u_short);
- len += 2 * sizeof (u_short);
- *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
- *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
- goto gottrailertype;
- }
type = ETHERTYPE_IP;
- goto gottype;
+ break;
#endif
#ifdef NS
case AF_NS:
@@ -134,9 +124,10 @@ ether_output(ifp, m0, dst, rt)
(caddr_t)edst, sizeof (edst));
if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, sizeof(edst)))
return (looutput(ifp, m, dst, rt));
- if ((ifp->if_flags & IFF_SIMPLEX) && (*edst & 1))
+ /* If broadcasting on a simplex interface, loopback a copy */
+ if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
mcopy = m_copy(m, 0, (int)M_COPYALL);
- goto gottype;
+ break;
#endif
#ifdef ISO
case AF_ISO: {
@@ -165,7 +156,8 @@ ether_output(ifp, m0, dst, rt)
(char *)edst, &snpalen)) > 0)
goto bad; /* Not Resolved */
iso_resolved:
- if ((ifp->if_flags & IFF_SIMPLEX) && (*edst & 1) &&
+ /* If broadcasting on a simplex interface, loopback a copy */
+ if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX)
(mcopy = m_copy(m, 0, (int)M_COPYALL))) {
M_PREPEND(mcopy, sizeof (*eh), M_DONTWAIT);
if (mcopy) {
@@ -191,7 +183,8 @@ ether_output(ifp, m0, dst, rt)
printf("%x ", edst[i] & 0xff);
printf("\n");
ENDDEBUG
- } goto gottype;
+ }
+ break;
#endif ISO
#ifdef RMP
case AF_RMP:
@@ -208,7 +201,7 @@ ether_output(ifp, m0, dst, rt)
eh = (struct ether_header *)dst->sa_data;
bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
type = eh->ether_type;
- goto gottype;
+ break;
default:
printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
@@ -217,18 +210,6 @@ ether_output(ifp, m0, dst, rt)
goto bad;
}
-gottrailertype:
- /*
- * Packet to be sent as trailer: move first packet
- * (control information) to end of chain.
- */
- while (m->m_next)
- m = m->m_next;
- m->m_next = m0;
- m = m0->m_next;
- m0->m_next = 0;
-
-gottype:
if (mcopy)
(void) looutput(ifp, mcopy, dst, rt);
/*
@@ -266,7 +247,7 @@ gottype:
(*ifp->if_start)(ifp);
splx(s);
ifp->if_obytes += len + sizeof (struct ether_header);
- if (edst[0] & 1)
+ if (m->m_flags & M_MCAST)
ifp->if_omcasts++;
return (error);
@@ -293,11 +274,13 @@ ether_input(ifp, eh, m)
ifp->if_lastchange = time;
ifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh);
+ if (eh->ether_dhost[0] & 1) {
if (bcmp((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
sizeof(etherbroadcastaddr)) == 0)
m->m_flags |= M_BCAST;
- else if (eh->ether_dhost[0] & 1)
+ else
m->m_flags |= M_MCAST;
+ }
if (m->m_flags & (M_BCAST|M_MCAST))
ifp->if_imcasts++;
@@ -421,3 +404,178 @@ ether_sprintf(ap)
*--cp = 0;
return (etherbuf);
}
+
+#ifdef MULTICAST
+u_char ether_ipmulticast_min[6] = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 };
+u_char ether_ipmulticast_max[6] = { 0x01, 0x00, 0x5e, 0x7f, 0xff, 0xff };
+
+/* XXX */
+#undef ac
+/*
+ * Add an Ethernet multicast address or range of addresses to the list for a
+ * given interface.
+ */
+int
+ether_addmulti(ifr, ac)
+ struct ifreq *ifr;
+ register struct arpcom *ac;
+{
+ register struct ether_multi *enm;
+ struct sockaddr_in *sin;
+ u_char addrlo[6];
+ u_char addrhi[6];
+ int s = splimp();
+
+ switch (ifr->ifr_addr.sa_family) {
+
+ case AF_UNSPEC:
+ bcopy(ifr->ifr_addr.sa_data, addrlo, 6);
+ bcopy(addrlo, addrhi, 6);
+ break;
+
+#ifdef INET
+ case AF_INET:
+ sin = (struct sockaddr_in *)&(ifr->ifr_addr);
+ if (sin->sin_addr.s_addr == INADDR_ANY) {
+ /*
+ * An IP address of INADDR_ANY means listen to all
+ * of the Ethernet multicast addresses used for IP.
+ * (This is for the sake of IP multicast routers.)
+ */
+ bcopy(ether_ipmulticast_min, addrlo, 6);
+ bcopy(ether_ipmulticast_max, addrhi, 6);
+ }
+ else {
+ ETHER_MAP_IP_MULTICAST(&sin->sin_addr, addrlo);
+ bcopy(addrlo, addrhi, 6);
+ }
+ break;
+#endif
+
+ default:
+ splx(s);
+ return (EAFNOSUPPORT);
+ }
+
+ /*
+ * Verify that we have valid Ethernet multicast addresses.
+ */
+ if ((addrlo[0] & 0x01) != 1 || (addrhi[0] & 0x01) != 1) {
+ splx(s);
+ return (EINVAL);
+ }
+ /*
+ * See if the address range is already in the list.
+ */
+ ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm);
+ if (enm != NULL) {
+ /*
+ * Found it; just increment the reference count.
+ */
+ ++enm->enm_refcount;
+ splx(s);
+ return (0);
+ }
+ /*
+ * New address or range; malloc a new multicast record
+ * and link it into the interface's multicast list.
+ */
+ enm = (struct ether_multi *)malloc(sizeof(*enm), M_IFMADDR, M_NOWAIT);
+ if (enm == NULL) {
+ splx(s);
+ return (ENOBUFS);
+ }
+ bcopy(addrlo, enm->enm_addrlo, 6);
+ bcopy(addrhi, enm->enm_addrhi, 6);
+ enm->enm_ac = ac;
+ enm->enm_refcount = 1;
+ enm->enm_next = ac->ac_multiaddrs;
+ ac->ac_multiaddrs = enm;
+ ac->ac_multicnt++;
+ splx(s);
+ /*
+ * Return ENETRESET to inform the driver that the list has changed
+ * and its reception filter should be adjusted accordingly.
+ */
+ return (ENETRESET);
+}
+
+/*
+ * Delete a multicast address record.
+ */
+int
+ether_delmulti(ifr, ac)
+ struct ifreq *ifr;
+ register struct arpcom *ac;
+{
+ register struct ether_multi *enm;
+ register struct ether_multi **p;
+ struct sockaddr_in *sin;
+ u_char addrlo[6];
+ u_char addrhi[6];
+ int s = splimp();
+
+ switch (ifr->ifr_addr.sa_family) {
+
+ case AF_UNSPEC:
+ bcopy(ifr->ifr_addr.sa_data, addrlo, 6);
+ bcopy(addrlo, addrhi, 6);
+ break;
+
+#ifdef INET
+ case AF_INET:
+ sin = (struct sockaddr_in *)&(ifr->ifr_addr);
+ if (sin->sin_addr.s_addr == INADDR_ANY) {
+ /*
+ * An IP address of INADDR_ANY means stop listening
+ * to the range of Ethernet multicast addresses used
+ * for IP.
+ */
+ bcopy(ether_ipmulticast_min, addrlo, 6);
+ bcopy(ether_ipmulticast_max, addrhi, 6);
+ }
+ else {
+ ETHER_MAP_IP_MULTICAST(&sin->sin_addr, addrlo);
+ bcopy(addrlo, addrhi, 6);
+ }
+ break;
+#endif
+
+ default:
+ splx(s);
+ return (EAFNOSUPPORT);
+ }
+
+ /*
+ * Look up the address in our list.
+ */
+ ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm);
+ if (enm == NULL) {
+ splx(s);
+ return (ENXIO);
+ }
+ if (--enm->enm_refcount != 0) {
+ /*
+ * Still some claims to this record.
+ */
+ splx(s);
+ return (0);
+ }
+ /*
+ * No remaining claims to this record; unlink and free it.
+ */
+ for (p = &enm->enm_ac->ac_multiaddrs;
+ *p != enm;
+ p = &(*p)->enm_next)
+ continue;
+ *p = (*p)->enm_next;
+ free(enm, M_IFMADDR);
+ ac->ac_multicnt--;
+ splx(s);
+ /*
+ * Return ENETRESET to inform the driver that the list has changed
+ * and its reception filter should be adjusted accordingly.
+ */
+ return (ENETRESET);
+}
+#endif
diff --git a/sys/net/if_loop.c b/sys/net/if_loop.c
index 163712a66fd1..d9f5028b7e1d 100644
--- a/sys/net/if_loop.c
+++ b/sys/net/if_loop.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)if_loop.c 7.13 (Berkeley) 4/26/91
- * $Id: if_loop.c,v 1.6 1993/12/20 19:31:29 wollman Exp $
+ * $Id: if_loop.c,v 1.7 1994/05/17 22:30:55 jkh Exp $
*/
/*
@@ -95,7 +95,11 @@ loattach(void)
ifp->if_name = "lo";
ifp->if_mtu = LOMTU;
+#ifdef MULTICAST
+ ifp->if_flags = IFF_LOOPBACK|IFF_MULTICAST;
+#else
ifp->if_flags = IFF_LOOPBACK;
+#endif
ifp->if_ioctl = loioctl;
ifp->if_output = looutput;
ifp->if_type = IFT_LOOP;
@@ -216,6 +220,9 @@ loioctl(ifp, cmd, data)
caddr_t data;
{
register struct ifaddr *ifa;
+#ifdef MULTICAST
+ register struct ifreq *ifr;
+#endif
int error = 0;
switch (cmd) {
@@ -230,6 +237,27 @@ loioctl(ifp, cmd, data)
*/
break;
+#ifdef MULTICAST
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ ifr = (struct ifreq *)data;
+ if (ifr == 0) {
+ error = EAFNOSUPPORT; /* XXX */
+ break;
+ }
+ switch (ifr->ifr_addr.sa_family) {
+
+#ifdef INET
+ case AF_INET:
+ break;
+#endif
+
+ default:
+ error = EAFNOSUPPORT;
+ break;
+ }
+ break;
+#endif
default:
error = EINVAL;
}
diff --git a/sys/net/if_ppp.c b/sys/net/if_ppp.c
index efda46dbe54a..18beb9a345cd 100644
--- a/sys/net/if_ppp.c
+++ b/sys/net/if_ppp.c
@@ -70,7 +70,7 @@
*/
/*
- * $Id: if_ppp.c,v 1.7 1993/12/20 19:31:30 wollman Exp $
+ * $Id: if_ppp.c,v 1.13 1994/05/30 03:37:47 ache Exp $
* From: if_ppp.c,v 1.22 1993/08/31 23:20:40 paulus Exp
* From: if_ppp.c,v 1.21 1993/08/29 11:22:37 paulus Exp
* From: if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp
@@ -92,6 +92,7 @@
#include "kernel.h"
#include "conf.h"
#include "dkstat.h"
+#include "pppdefs.h"
#include "if.h"
#include "if_types.h"
@@ -117,11 +118,11 @@
#ifndef RB_LEN
/* NetBSD, 4.3-Reno or similar */
#define CCOUNT(q) ((q)->c_cc)
+#define TSA_HUP_OR_INPUT(tp) ((caddr_t)&tp->t_rawq)
#else
/* 386BSD, Jolitz-style ring buffers */
#define t_outq t_out
-#define t_rawq t_raw
#define t_canq t_can
#define CCOUNT(q) (RB_LEN(q))
#endif
@@ -249,7 +250,7 @@ pppopen(dev, tp)
if (sc->sc_ttyp == NULL)
break;
if (nppp >= NPPP)
- return ENXIO;
+ return (ENXIO);
sc->sc_flags = 0;
sc->sc_ilen = 0;
@@ -344,33 +345,40 @@ pppread(tp, uio, flag)
{
register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
struct mbuf *m, *m0;
- register int s;
- int error = 0;
+ int s;
+ int error;
- if ((tp->t_state & TS_CARR_ON)==0)
- return (EIO);
s = splimp();
- while (sc->sc_inq.ifq_head == NULL && tp->t_line == PPPDISC) {
+ for (;;) {
+ if (tp->t_line != PPPDISC) {
+ splx(s);
+ return (ENXIO);
+ }
+ if (!CAN_DO_IO(tp)) {
+ splx(s);
+ return (EIO);
+ }
+ if (sc->sc_inq.ifq_head != NULL)
+ break;
if (tp->t_state & TS_ASYNC) {
splx(s);
return (EWOULDBLOCK);
}
- error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI|PCATCH, ttyin, 0);
- if (error)
- return error;
- }
- if (tp->t_line != PPPDISC) {
- splx(s);
- return (-1);
+ error = ttysleep(tp, TSA_HUP_OR_INPUT(tp), TTIPRI | PCATCH, "pppin", 0);
+ if (error) {
+ splx(s);
+ return (error);
+ }
}
/* Pull place-holder byte out of canonical queue */
- getc(&tp->t_canq);
+ getc(tp->t_canq);
/* Get the packet from the input queue */
IF_DEQUEUE(&sc->sc_inq, m0);
splx(s);
+ error = 0;
for (m = m0; m && uio->uio_resid; m = m->m_next)
if (error = uiomove(mtod(m, u_char *), m->m_len, uio))
break;
@@ -391,19 +399,33 @@ pppwrite(tp, uio, flag)
struct mbuf *m, *m0, **mp;
struct sockaddr dst;
struct ppp_header *ph1, *ph2;
- int len, error;
+ int len, error, s;
- if ((tp->t_state & TS_CARR_ON)==0)
- return (EIO);
if (tp->t_line != PPPDISC)
- return (EINVAL);
+ return (ENXIO);
+
+ /*
+ * XXX the locking is probably too strong, and/or the order of checking
+ * for errors requires an inconvenient number of splx()'s. pppoutput()
+ * does its own locking, but we must hold at least an spltty() lock to
+ * preserve the validity of the test of TS_CARR_ON in CAN_DO_IO().
+ */
+ s = splimp();
+
+ if (!CAN_DO_IO(tp)) {
+ splx(s);
+ return (EIO);
+ }
if (uio->uio_resid > sc->sc_if.if_mtu + PPP_HEADER_LEN ||
- uio->uio_resid < PPP_HEADER_LEN)
+ uio->uio_resid < PPP_HEADER_LEN) {
+ splx(s);
return (EMSGSIZE);
+ }
for (mp = &m0; uio->uio_resid; mp = &m->m_next) {
MGET(m, M_WAIT, MT_DATA);
if ((*mp = m) == NULL) {
m_freem(m0);
+ splx(s);
return (ENOBUFS);
}
if (uio->uio_resid >= MCLBYTES / 2)
@@ -411,6 +433,7 @@ pppwrite(tp, uio, flag)
len = MIN(M_TRAILINGSPACE(m), uio->uio_resid);
if (error = uiomove(mtod(m, u_char *), len, uio)) {
m_freem(m0);
+ splx(s);
return (error);
}
m->m_len = len;
@@ -421,7 +444,9 @@ pppwrite(tp, uio, flag)
*ph1 = *ph2;
m0->m_data += PPP_HEADER_LEN;
m0->m_len -= PPP_HEADER_LEN;
- return (pppoutput(&sc->sc_if, m0, &dst, 0));
+ error = pppoutput(&sc->sc_if, m0, &dst, 0);
+ splx(s);
+ return (error);
}
/*
@@ -589,7 +614,7 @@ pppoutput(ifp, m0, dst, rt)
error = ENETDOWN; /* sort of */
goto bad;
}
- if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0) {
+ if (!CAN_DO_IO(sc->sc_ttyp)) {
error = EHOSTUNREACH;
goto bad;
}
@@ -711,15 +736,14 @@ pppstart(tp)
for (;;) {
/*
- * If there is more in the output queue, just send it now.
+ * Call output process whether or not there is any output.
* We are being called in lieu of ttstart and must do what
* it would.
*/
- if (CCOUNT(&tp->t_outq) != 0 && tp->t_oproc != NULL) {
- (*tp->t_oproc)(tp);
- if (CCOUNT(&tp->t_outq) > PPP_HIWAT)
- return;
- }
+ (*tp->t_oproc)(tp);
+ if (CCOUNT(tp->t_outq) > PPP_HIWAT)
+ return;
+
/*
* This happens briefly when the line shuts down.
*/
@@ -809,9 +833,9 @@ pppstart(tp)
* will flush any accumulated garbage. We do this whenever
* the line may have been idle for some time.
*/
- if (CCOUNT(&tp->t_outq) == 0) {
+ if (CCOUNT(tp->t_outq) == 0) {
++sc->sc_bytessent;
- (void) putc(PPP_FLAG, &tp->t_outq);
+ (void) putc(PPP_FLAG, tp->t_outq);
}
/* Calculate the FCS for the first mbuf's worth. */
@@ -834,7 +858,7 @@ pppstart(tp)
if (n) {
#ifndef RB_LEN
/* NetBSD (0.9 or later), 4.3-Reno or similar. */
- ndone = n - b_to_q(start, n, &tp->t_outq);
+ ndone = n - b_to_q(start, n, tp->t_outq);
#else
#ifdef NetBSD
/* NetBSD with 2-byte ring buffer entries */
@@ -843,12 +867,12 @@ pppstart(tp)
/* 386BSD, FreeBSD */
int cc, nleft;
for (nleft = n; nleft > 0; nleft -= cc) {
- if ((cc = RB_CONTIGPUT(&tp->t_out)) == 0)
+ if ((cc = RB_CONTIGPUT(tp->t_out)) == 0)
break;
cc = min (cc, nleft);
- bcopy((char *)start + n - nleft, tp->t_out.rb_tl, cc);
- tp->t_out.rb_tl = RB_ROLLOVER(&tp->t_out,
- tp->t_out.rb_tl + cc);
+ bcopy((char *)start + n - nleft, tp->t_out->rb_tl, cc);
+ tp->t_out->rb_tl = RB_ROLLOVER(tp->t_out,
+ tp->t_out->rb_tl + cc);
}
ndone = n - nleft;
#endif /* NetBSD */
@@ -866,10 +890,10 @@ pppstart(tp)
* Put it out in a different form.
*/
if (len) {
- if (putc(PPP_ESCAPE, &tp->t_outq))
+ if (putc(PPP_ESCAPE, tp->t_outq))
break;
- if (putc(*start ^ PPP_TRANS, &tp->t_outq)) {
- (void) unputc(&tp->t_outq);
+ if (putc(*start ^ PPP_TRANS, tp->t_outq)) {
+ (void) unputc(tp->t_outq);
break;
}
sc->sc_bytessent += 2;
@@ -912,10 +936,10 @@ pppstart(tp)
* don't all fit, back out.
*/
for (q = endseq; q < p; ++q)
- if (putc(*q, &tp->t_outq)) {
+ if (putc(*q, tp->t_outq)) {
done = 0;
for (; q > endseq; --q)
- unputc(&tp->t_outq);
+ unputc(tp->t_outq);
break;
}
}
@@ -1213,7 +1237,7 @@ pppinput(c, tp)
* Some other protocol - place on input queue for read().
* Put a placeholder byte in canq for ttselect()/ttnread().
*/
- putc(0, &tp->t_canq);
+ putc(0, tp->t_canq);
ttwakeup(tp);
inq = &sc->sc_inq;
break;
diff --git a/sys/net/if_sl.c b/sys/net/if_sl.c
index 223281337a53..60376b28e1bf 100644
--- a/sys/net/if_sl.c
+++ b/sys/net/if_sl.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)if_sl.c 7.22 (Berkeley) 4/20/91
- * $Id: if_sl.c,v 1.7 1993/12/20 19:31:32 wollman Exp $
+ * $Id: if_sl.c,v 1.12 1994/06/07 13:02:36 davidg Exp $
*/
/*
@@ -65,7 +65,7 @@
* interrupts and network activity; thus, splimp must be >= spltty.
*/
-/* $Id: if_sl.c,v 1.7 1993/12/20 19:31:32 wollman Exp $ */
+/* $Id: if_sl.c,v 1.12 1994/06/07 13:02:36 davidg Exp $ */
/* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */
#include "sl.h"
@@ -170,8 +170,10 @@
#ifndef SLRMTU
#define SLRMTU 296 /* for good latency */
#endif
-#else
+#else /* (not) experimental */
+#ifndef SLMTU
#define SLMTU 296
+#endif
#define SLRMTU SLMTU
#endif
@@ -445,7 +447,7 @@ sloutput(ifp, m, dst, rt)
}
IF_ENQUEUE(ifq, m);
sc->sc_if.if_lastchange = time;
- if (RB_LEN(&sc->sc_ttyp->t_out) == 0)
+ if (RB_LEN(sc->sc_ttyp->t_out) == 0)
slstart(sc->sc_ttyp);
splx(s);
return (0);
@@ -478,7 +480,7 @@ slstart(tp)
* it would.
*/
(*tp->t_oproc)(tp);
- if (RB_LEN(&tp->t_out) > SLIP_HIWAT)
+ if (RB_LEN(tp->t_out) > SLIP_HIWAT)
return;
/*
@@ -495,7 +497,7 @@ slstart(tp)
* of RBSZ in tty.h also has to be upped to be at least
* SLMTU*2.
*/
- if (min(RBSZ, 4 * SLMTU + 4) - RB_LEN(&tp->t_out) < 2 * SLMTU + 2)
+ if (min(RBSZ, 4 * SLMTU + 4) - RB_LEN(tp->t_out) < 2 * SLMTU + 2)
return;
/*
@@ -558,9 +560,9 @@ slstart(tp)
* will flush any accumulated garbage. We do this whenever
* the line may have been idle for some time.
*/
- if (RB_LEN(&tp->t_out) == 0) {
+ if (RB_LEN(tp->t_out) == 0) {
++sc->sc_bytessent;
- (void) putc(FRAME_END, &tp->t_out);
+ (void) putc(FRAME_END, tp->t_out);
}
while (m) {
@@ -589,7 +591,7 @@ slstart(tp)
* into the tty output queue.
*/
sc->sc_bytessent += rb_write(
- &tp->t_out,
+ tp->t_out,
(char *) bp,
cp - bp);
}
@@ -599,12 +601,12 @@ slstart(tp)
* Put it out in a different form.
*/
if (cp < ep) {
- if (putc(FRAME_ESCAPE, &tp->t_out))
+ if (putc(FRAME_ESCAPE, tp->t_out))
break;
if (putc(*cp++ == FRAME_ESCAPE ?
TRANS_FRAME_ESCAPE : TRANS_FRAME_END,
- &tp->t_out)) {
- (void) unputc(&tp->t_out);
+ tp->t_out)) {
+ (void) unputc(tp->t_out);
break;
}
sc->sc_bytessent += 2;
@@ -614,7 +616,7 @@ slstart(tp)
m = m2;
}
- if (putc(FRAME_END, &tp->t_out)) {
+ if (putc(FRAME_END, tp->t_out)) {
/*
* Not enough room. Remove a char to make room
* and end the packet normally.
@@ -622,8 +624,8 @@ slstart(tp)
* a day) you probably do not have enough clists
* and you should increase "nclist" in param.c.
*/
- (void) unputc(&tp->t_out);
- (void) putc(FRAME_END, &tp->t_out);
+ (void) unputc(tp->t_out);
+ (void) putc(FRAME_END, tp->t_out);
sc->sc_if.if_collisions++;
} else {
++sc->sc_bytessent;
@@ -869,6 +871,9 @@ slioctl(ifp, cmd, data)
caddr_t data;
{
register struct ifaddr *ifa = (struct ifaddr *)data;
+#ifdef MULTICAST
+ register struct ifreq *ifr;
+#endif
int s = splimp(), error = 0;
switch (cmd) {
@@ -890,6 +895,25 @@ slioctl(ifp, cmd, data)
error = EAFNOSUPPORT;
break;
+#ifdef MULTICAST
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ ifr = (struct ifreq *)data;
+ if (ifr == 0) {
+ error = EAFNOSUPPORT; /* XXX */
+ break;
+ }
+ switch (ifr->ifr_addr.sa_family) {
+#ifdef INET
+ case AF_INET:
+ break;
+#endif
+ default:
+ error = EAFNOSUPPORT;
+ break;
+ }
+ break;
+#endif
default:
error = EINVAL;
}
diff --git a/sys/net/netisr.h b/sys/net/netisr.h
index b543791db6fb..372388b69ded 100644
--- a/sys/net/netisr.h
+++ b/sys/net/netisr.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)netisr.h 7.8 (Berkeley) 5/7/91
- * $Id: netisr.h,v 1.5 1993/12/20 14:58:31 wollman Exp $
+ * $Id: netisr.h,v 1.6 1994/04/02 07:01:00 davidg Exp $
*/
#ifndef _NET_NETISR_H_
@@ -65,20 +65,9 @@
#define schednetisr(anisr) { netisr |= 1<<(anisr); setsoftnet(); }
-#ifdef i386
-/* XXX Temporary -- soon to vanish - wfj */
-#define NETISR_SCLK 11 /* softclock */
-#define NETISR_AST 12 /* ast -- resched */
-
-#undef schednetisr
-#define schednetisr(anisr) {\
- netisr |= 1<<(anisr); \
-}
-#endif /* i386 */
-
#ifndef LOCORE
#ifdef KERNEL
-extern int netisr; /* scheduling bits for network */
+extern volatile unsigned netisr; /* scheduling bits for network */
#endif
#endif
#endif /* _NET_NETISR_H_ */
diff --git a/sys/net/ppp.h b/sys/net/pppdefs.h
index c2d4a79c4baa..a0b5bc5d5b4f 100644
--- a/sys/net/ppp.h
+++ b/sys/net/pppdefs.h
@@ -1,5 +1,5 @@
/*
- * ppp.h - PPP global declarations.
+ * pppdefs.h - PPP global declarations.
*
* Copyright (c) 1989 Carnegie Mellon University.
* All rights reserved.
@@ -16,14 +16,13 @@
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
- * from: unknown
- * $Id: ppp.h,v 1.2 1993/10/16 17:43:29 rgrimes Exp $
+ * $Id: pppdefs.h,v 1.1 1994/04/01 10:13:22 jkh Exp $
*/
#ifndef __PPP_H__
#define __PPP_H__
-#define NPPP 1 /* One PPP interface supported (per process) */
+#define _NPPP 1 /* One PPP interface supported (per process) */
/*
* Data Link Layer header = Address, Control, Protocol.
@@ -34,6 +33,7 @@
#define IPCP 0x8021 /* IP Control Protocol */
#define UPAP 0xc023 /* User/Password Authentication Protocol */
#define CHAP 0xc223 /* Crytpographic Handshake Protocol */
+#define LQR 0xc025 /* Link Quality Report protocol */
#define IP_VJ_COMP 0x002d /* VJ TCP compressed IP packet */
#define DLLHEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
#define MTU 1500 /* Default MTU */
diff --git a/sys/net/raw_cb.h b/sys/net/raw_cb.h
index 5d64b60d9d9a..0eda56573de6 100644
--- a/sys/net/raw_cb.h
+++ b/sys/net/raw_cb.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)raw_cb.h 7.6 (Berkeley) 6/28/90
- * $Id: raw_cb.h,v 1.4 1993/12/19 00:52:05 wollman Exp $
+ * $Id: raw_cb.h,v 1.5 1994/05/17 22:30:57 jkh Exp $
*/
#ifndef _NET_RAW_CB_H_
@@ -48,6 +48,7 @@ struct rawcb {
struct sockaddr *rcb_faddr; /* destination address */
struct sockaddr *rcb_laddr; /* socket's address */
struct sockproto rcb_proto; /* protocol family, protocol */
+ struct mbuf *rcb_moptions; /* proto specific multicast options */
};
#define sotorawcb(so) ((struct rawcb *)(so)->so_pcb)
diff --git a/sys/net/route.c b/sys/net/route.c
index 5cf0cb72d556..ccb1cf63d097 100644
--- a/sys/net/route.c
+++ b/sys/net/route.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)route.c 7.22 (Berkeley) 6/27/91
- * $Id: route.c,v 1.5.2.1 1994/03/24 07:42:12 rgrimes Exp $
+ * $Id: route.c,v 1.6 1994/03/22 02:02:25 davidg Exp $
*/
#include "param.h"
diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c
index ebad56b12dcf..47efad34eaed 100644
--- a/sys/netinet/if_ether.c
+++ b/sys/netinet/if_ether.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)if_ether.c 7.13 (Berkeley) 10/31/90
- * $Id: if_ether.c,v 1.4 1993/11/25 01:34:57 wollman Exp $
+ * $Id: if_ether.c,v 1.5 1994/05/17 22:31:00 jkh Exp $
*/
/*
@@ -197,6 +197,12 @@ arpresolve(ac, m, destip, desten, usetrailers)
sizeof(etherbroadcastaddr));
return (1);
}
+#ifdef MULTICAST
+ if (m->m_flags & M_MCAST) { /* multicast */
+ ETHER_MAP_IP_MULTICAST(destip, desten);
+ return(1);
+ }
+#endif
lna = in_lnaof(*destip);
/* if for us, use software loopback driver if up */
for (ia = in_ifaddr; ia; ia = ia->ia_next)
diff --git a/sys/netinet/if_ether.h b/sys/netinet/if_ether.h
index 787b561e6fdf..fdf595924c43 100644
--- a/sys/netinet/if_ether.h
+++ b/sys/netinet/if_ether.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)if_ether.h 7.5 (Berkeley) 6/28/90
- * $Id: if_ether.h,v 1.4 1993/11/25 01:35:01 wollman Exp $
+ * $Id: if_ether.h,v 1.5 1994/05/17 22:31:02 jkh Exp $
*/
#ifndef _NETINET_IF_ETHER_H_
@@ -61,6 +61,25 @@ struct ether_header {
#define ETHERMTU 1500
#define ETHERMIN (60-14)
+#ifdef KERNEL
+/*
+ * Macro to map an IP multicast address to an Ethernet multicast address.
+ * The high-order 25 bites of the Ethernet address are staticall assigned,
+ * and the low-rder 23 bites are taken from the low end of the IP address.
+ */
+#define ETHER_MAP_IP_MULTICAST(ipaddr, enaddr) \
+ /* struct in_addr *ipaddr; */ \
+ /* u_char enaddr[6]; */ \
+{ \
+ (enaddr)[0] = 0x01; \
+ (enaddr)[1] = 0x00; \
+ (enaddr)[2] = 0x5e; \
+ (enaddr)[3] = ((u_char *)ipaddr)[1] & 0x7f; \
+ (enaddr)[4] = ((u_char *)ipaddr)[2]; \
+ (enaddr)[5] = ((u_char *)ipaddr)[3]; \
+}
+#endif
+
/*
* Ethernet Address Resolution Protocol.
*
@@ -91,8 +110,11 @@ struct arpcom {
struct ifnet ac_if; /* network-visible interface */
u_char ac_enaddr[6]; /* ethernet hardware address */
struct in_addr ac_ipaddr; /* copy of ip address- XXX */
+ struct ether_multi *ac_multiaddrs; /* list of ether m'cast addrs */
+ int ac_multicnt; /* length of ac_multiaddrs list */
};
+
/*
* Internet to ethernet address resolution table.
*/
@@ -106,10 +128,84 @@ struct arptab {
#ifdef KERNEL
extern u_char etherbroadcastaddr[6]; /* defined in net/if_ethersubr.c */
+#if defined(ISO) && !defined(MULTICAST)
+#define MULTICAST 1
+#endif
+#ifdef MULTICAST
+u_char ether_ipmulticast_min[6];
+u_char ether_ipmulticast_max[6];
+#endif
struct arptab *arptnew();
extern void ether_input(struct ifnet *, struct ether_header *, struct mbuf *);
extern char *ether_sprintf(u_char *);
-#endif
+#ifdef MULTICAST
+/*
+ * Ethernet multicast address structure. There is one of these for each
+ * multicast address or range of multicast addresses that we are supposed
+ * to listen to on a particular interface. They are kept in a linked list,
+ * rooted in the interface's arpcom structure. (This really has nothing to
+ * do with ARP, or with the Internet address family, but this appears to be
+ * the minimally-disrupting place to put it.)
+ */
+struct ether_multi {
+ u_char enm_addrlo[6]; /* low or only address of range */
+ u_char enm_addrhi[6]; /* high or only address of range */
+ struct arpcom *enm_ac; /* back point to arpcom */
+ u_int enm_refcount; /* no. claims to this addr/range */
+ struct ether_multi *enm_next; /* ptr to next ether_multi */
+} ;
+
+/*
+ * Structure used by macros below to remember position when stepping through
+ * all of the ether_multi records.
+ */
+struct ether_multistep {
+ struct ether_multi *e_enm;
+};
+
+/*
+ * Macro for looking up the ether_multi record for a given range of Ethernet
+ * multicast addresses connected to a given arpcom structure. If no matching
+ * record is found, "enm" returns NULL.
+ */
+#define ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm) \
+ /* u_char addrlo[6]; */ \
+ /* u_char addrhi[6]; */ \
+ /* struct arpcom *ac; */ \
+ /* struct ether_multi *enm; */ \
+{ \
+ for ((enm) = (ac)->ac_multiaddrs; \
+ (enm) != NULL && \
+ (bcmp((enm)->enm_addrlo, (addrlo), 6) != 0 || \
+ bcmp((enm)->enm_addrhi, (addrhi), 6) != 0); \
+ (enm) = (enm)->enm_next); \
+}
+
+/*
+ * Macro to step through all of the ether_multi records, one at a time.
+ * The current position is remembered in "step", which the caller must
+ * provide. ETHER_FIRST_MULTI(), below, must be called to initialize "step"
+ * and get the first record. Both macros return a NULL "enm" when there
+ * are no remaining records.
+ */
+#define ETHER_NEXT_MULTI(step, enm) \
+ /* struct ether_multistep step; */ \
+ /* struct ether_multi *enm; */ \
+{ \
+ if (((enm) = (step).e_enm) != NULL) \
+ (step).e_enm = (enm)->enm_next; \
+}
+
+#define ETHER_FIRST_MULTI(step, ac, enm) \
+ /* struct ether_multistep step; */ \
+ /* struct arpcom *ac; */ \
+ /* struct ether_multi *enm; */ \
+{ \
+ (step).e_enm = (ac)->ac_multiaddrs; \
+ ETHER_NEXT_MULTI((step), (enm)); \
+}
+#endif /* _MULTICAST */
+#endif /* KERNEL */
#endif /* _NETINET_IF_ETHER_H_ */
diff --git a/sys/netinet/igmp.c b/sys/netinet/igmp.c
new file mode 100644
index 000000000000..d3bdd933730a
--- /dev/null
+++ b/sys/netinet/igmp.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 1988 Stephen Deering.
+ * Copyright (c) 1992, 1993 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * 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.
+ *
+ * @(#)igmp.c 7.2 (Berkeley) 10/11/92
+ */
+
+/* Internet Group Management Protocol (IGMP) routines. */
+
+#ifdef MULTICAST
+
+#include "param.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "protosw.h"
+
+#include "net/if.h"
+#include "net/route.h"
+
+#include "in.h"
+#include "in_var.h"
+#include "in_systm.h"
+#include "ip.h"
+#include "ip_var.h"
+#include "igmp.h"
+#include "igmp_var.h"
+#include "machine/cpufunc.h"
+
+extern struct ifnet loif;
+
+static int igmp_timers_are_running = 0;
+static u_long igmp_all_hosts_group;
+
+static void igmp_sendreport __P((struct in_multi *));
+
+void
+igmp_init()
+{
+ /*
+ * To avoid byte-swapping the same value over and over again.
+ */
+ igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP);
+}
+
+void
+igmp_input(m, iphlen)
+ register struct mbuf *m;
+ register int iphlen;
+{
+ register struct igmp *igmp;
+ register struct ip *ip;
+ register int igmplen;
+ register struct ifnet *ifp = m->m_pkthdr.rcvif;
+ register int minlen;
+ register struct in_multi *inm;
+ register struct in_ifaddr *ia;
+ struct in_multistep step;
+
+ ++igmpstat.igps_rcv_total;
+
+ ip = mtod(m, struct ip *);
+ igmplen = ip->ip_len;
+
+ /*
+ * Validate lengths
+ */
+ if (igmplen < IGMP_MINLEN) {
+ ++igmpstat.igps_rcv_tooshort;
+ m_freem(m);
+ return;
+ }
+ minlen = iphlen + IGMP_MINLEN;
+ if ((m->m_flags & M_EXT || m->m_len < minlen) &&
+ (m = m_pullup(m, minlen)) == 0) {
+ ++igmpstat.igps_rcv_tooshort;
+ return;
+ }
+
+ /*
+ * Validate checksum
+ */
+ m->m_data += iphlen;
+ m->m_len -= iphlen;
+ igmp = mtod(m, struct igmp *);
+ if (in_cksum(m, igmplen)) {
+ ++igmpstat.igps_rcv_badsum;
+ m_freem(m);
+ return;
+ }
+ m->m_data -= iphlen;
+ m->m_len += iphlen;
+ ip = mtod(m, struct ip *);
+
+ switch (igmp->igmp_type) {
+
+ case IGMP_HOST_MEMBERSHIP_QUERY:
+ ++igmpstat.igps_rcv_queries;
+
+ if (ifp == &loif)
+ break;
+
+ if (ip->ip_dst.s_addr != igmp_all_hosts_group) {
+ ++igmpstat.igps_rcv_badqueries;
+ m_freem(m);
+ return;
+ }
+
+ /*
+ * Start the timers in all of our membership records for
+ * the interface on which the query arrived, except those
+ * that are already running and those that belong to the
+ * "all-hosts" group.
+ */
+ IN_FIRST_MULTI(step, inm);
+ while (inm != NULL) {
+ if (inm->inm_ifp == ifp && inm->inm_timer == 0 &&
+ inm->inm_addr.s_addr != igmp_all_hosts_group) {
+ inm->inm_timer =
+ IGMP_RANDOM_DELAY(inm->inm_addr);
+ igmp_timers_are_running = 1;
+ }
+ IN_NEXT_MULTI(step, inm);
+ }
+
+ break;
+
+ case IGMP_HOST_MEMBERSHIP_REPORT:
+ ++igmpstat.igps_rcv_reports;
+
+ if (ifp == &loif)
+ break;
+
+ if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) ||
+ igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
+ ++igmpstat.igps_rcv_badreports;
+ m_freem(m);
+ return;
+ }
+
+ /*
+ * KLUDGE: if the IP source address of the report has an
+ * unspecified (i.e., zero) subnet number, as is allowed for
+ * a booting host, replace it with the correct subnet number
+ * so that a process-level multicast routing demon can
+ * determine which subnet it arrived from. This is necessary
+ * to compensate for the lack of any way for a process to
+ * determine the arrival interface of an incoming packet.
+ */
+ if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) {
+ IFP_TO_IA(ifp, ia);
+ if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
+ }
+
+ /*
+ * If we belong to the group being reported, stop
+ * our timer for that group.
+ */
+ IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
+ if (inm != NULL) {
+ inm->inm_timer = 0;
+ ++igmpstat.igps_rcv_ourreports;
+ }
+
+ break;
+ }
+
+ /*
+ * Pass all valid IGMP packets up to any process(es) listening
+ * on a raw IGMP socket.
+ */
+ rip_input(m);
+}
+
+void
+igmp_joingroup(inm)
+ struct in_multi *inm;
+{
+ register int s = splnet();
+
+ if (inm->inm_addr.s_addr == igmp_all_hosts_group ||
+ inm->inm_ifp == &loif)
+ inm->inm_timer = 0;
+ else {
+ igmp_sendreport(inm);
+ inm->inm_timer = IGMP_RANDOM_DELAY(inm->inm_addr);
+ igmp_timers_are_running = 1;
+ }
+ splx(s);
+}
+
+void
+igmp_leavegroup(inm)
+ struct in_multi *inm;
+{
+ /*
+ * No action required on leaving a group.
+ */
+}
+
+void
+igmp_fasttimo()
+{
+ register struct in_multi *inm;
+ register int s;
+ struct in_multistep step;
+
+ /*
+ * Quick check to see if any work needs to be done, in order
+ * to minimize the overhead of fasttimo processing.
+ */
+ if (!igmp_timers_are_running)
+ return;
+
+ s = splnet();
+ igmp_timers_are_running = 0;
+ IN_FIRST_MULTI(step, inm);
+ while (inm != NULL) {
+ if (inm->inm_timer == 0) {
+ /* do nothing */
+ } else if (--inm->inm_timer == 0) {
+ igmp_sendreport(inm);
+ } else {
+ igmp_timers_are_running = 1;
+ }
+ IN_NEXT_MULTI(step, inm);
+ }
+ splx(s);
+}
+
+static void
+igmp_sendreport(inm)
+ register struct in_multi *inm;
+{
+ register struct mbuf *m;
+ register struct igmp *igmp;
+ register struct ip *ip;
+ register struct ip_moptions *imo;
+ struct ip_moptions simo;
+ extern struct socket *ip_mrouter;
+
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (m == NULL)
+ return;
+ /*
+ * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
+ * is smaller than mbuf size returned by MGETHDR.
+ */
+ m->m_data += max_linkhdr;
+ m->m_len = sizeof(struct ip) + IGMP_MINLEN;
+ m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
+
+ ip = mtod(m, struct ip *);
+ ip->ip_tos = 0;
+ ip->ip_len = sizeof(struct ip) + IGMP_MINLEN;
+ ip->ip_off = 0;
+ ip->ip_p = IPPROTO_IGMP;
+ ip->ip_src.s_addr = INADDR_ANY;
+ ip->ip_dst = inm->inm_addr;
+
+ m->m_data += sizeof(struct ip);
+ m->m_len -= sizeof(struct ip);
+ igmp = mtod(m, struct igmp *);
+ igmp->igmp_type = IGMP_HOST_MEMBERSHIP_REPORT;
+ igmp->igmp_code = 0;
+ igmp->igmp_group = inm->inm_addr;
+ igmp->igmp_cksum = 0;
+ igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
+ m->m_data -= sizeof(struct ip);
+ m->m_len += sizeof(struct ip);
+
+ imo = &simo;
+ bzero((caddr_t)imo, sizeof(*imo));
+ imo->imo_multicast_ifp = inm->inm_ifp;
+ imo->imo_multicast_ttl = 1;
+ /*
+ * Request loopback of the report if we are acting as a multicast
+ * router, so that the process-level routing demon can hear it.
+ */
+#ifdef MROUTING
+ imo->imo_multicast_loop = (ip_mrouter != NULL);
+#else
+ imo->imo_multicast_loop = 0;
+#endif
+
+ ip_output(m, NULL, NULL, IP_MULTICASTOPTS, imo);
+
+ ++igmpstat.igps_snd_reports;
+}
+#endif
diff --git a/sys/netinet/igmp.h b/sys/netinet/igmp.h
new file mode 100644
index 000000000000..176941914de7
--- /dev/null
+++ b/sys/netinet/igmp.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1988 Stephen Deering.
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * 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.
+ *
+ * @(#)igmp.h 7.1 (Berkeley) 7/8/92
+ */
+
+/* Internet Group Management Protocol (IGMP) definitions. */
+
+/*
+ * IGMP packet format.
+ */
+struct igmp {
+ u_char igmp_type; /* version & type of IGMP message */
+ u_char igmp_code; /* unused, should be zero */
+ u_short igmp_cksum; /* IP-style checksum */
+ struct in_addr igmp_group; /* group address being reported */
+}; /* (zero for queries) */
+
+#define IGMP_MINLEN 8
+
+#define IGMP_HOST_MEMBERSHIP_QUERY 0x11 /* message types, incl. version */
+#define IGMP_HOST_MEMBERSHIP_REPORT 0x12
+#define IGMP_DVMRP 0x13 /* for experimental multicast */
+ /* routing protocol */
+
+#define IGMP_MAX_HOST_REPORT_DELAY 10 /* max delay for response to */
+ /* query (in seconds) */
diff --git a/sys/netinet/igmp_var.h b/sys/netinet/igmp_var.h
new file mode 100644
index 000000000000..01e967f99ea2
--- /dev/null
+++ b/sys/netinet/igmp_var.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1988 Stephen Deering.
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * 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.
+ *
+ * @(#)igmp_var.h 7.1 (Berkeley) 7/8/92
+ */
+
+/*
+ * Internet Group Management Protocol (IGMP),
+ * implementation-specific definitions.
+ *
+ * Written by Steve Deering, Stanford, May 1988.
+ *
+ * MULTICAST 1.1
+ */
+
+struct igmpstat {
+ u_int igps_rcv_total; /* total IGMP messages received */
+ u_int igps_rcv_tooshort; /* received with too few bytes */
+ u_int igps_rcv_badsum; /* received with bad checksum */
+ u_int igps_rcv_queries; /* received membership queries */
+ u_int igps_rcv_badqueries; /* received invalid queries */
+ u_int igps_rcv_reports; /* received membership reports */
+ u_int igps_rcv_badreports; /* received invalid reports */
+ u_int igps_rcv_ourreports; /* received reports for our groups */
+ u_int igps_snd_reports; /* sent membership reports */
+};
+
+#ifdef KERNEL
+struct igmpstat igmpstat;
+
+/*
+ * Macro to compute a random timer value between 1 and (IGMP_MAX_REPORTING_
+ * DELAY * countdown frequency). We assume that the routine random()
+ * is defined somewhere (and that it returns a positive number).
+ */
+#define IGMP_RANDOM_DELAY(multiaddr) \
+ /* struct in_addr multiaddr; */ \
+ (random() % (IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ) + 1)
+
+
+void igmp_init __P(());
+void igmp_input __P((struct mbuf *, int));
+void igmp_joingroup __P((struct in_multi *));
+void igmp_leavegroup __P((struct in_multi *));
+void igmp_fasttimo __P(());
+#endif
+
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
index 2c662e2d6336..46f423b71bbd 100644
--- a/sys/netinet/in.c
+++ b/sys/netinet/in.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)in.c 7.17 (Berkeley) 4/20/91
- * $Id: in.c,v 1.8 1994/01/15 14:29:21 davidg Exp $
+ * $Id: in.c,v 1.9 1994/05/17 22:31:03 jkh Exp $
*/
#include "param.h"
@@ -93,6 +93,10 @@ in_netof(in)
net = i & IN_CLASSB_NET;
else if (IN_CLASSC(i))
net = i & IN_CLASSC_NET;
+#ifdef MULTICAST
+ else if (IN_CLASSD(i))
+ net = i & IN_CLASSD_NET;
+#endif
else
return (0);
@@ -174,6 +178,11 @@ in_lnaof(in)
} else if (IN_CLASSC(i)) {
net = i & IN_CLASSC_NET;
host = i & IN_CLASSC_HOST;
+#ifdef MULTICAST
+ } else if (IN_CLASSD(i)) {
+ net = i & IN_CLASSD_NET;
+ host = i & IN_CLASSD_HOST;
+#endif
} else
return (i);
@@ -557,6 +566,18 @@ in_ifinit(ifp, ia, sin, scrub)
}
if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0)
ia->ia_flags |= IFA_ROUTE;
+#ifdef MULTICAST
+ /*
+ * If the interface supports multicast, join the "all hosts"
+ * multicast group on that interface.
+ */
+ if (ifp->if_flags & IFF_MULTICAST) {
+ struct in_addr addr;
+
+ addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+ in_addmulti(&addr, ifp);
+ }
+#endif
return (error);
}
@@ -603,4 +624,113 @@ in_broadcast(in)
return (1);
return (0);
}
+
+#ifdef MULTICAST
+/*
+ * Add an address to the list of IP multicast addresses for a given interface.
+ */
+struct in_multi *
+in_addmulti(ap, ifp)
+ register struct in_addr *ap;
+ register struct ifnet *ifp;
+{
+ register struct in_multi *inm;
+ struct ifreq ifr;
+ struct in_ifaddr *ia;
+ int s = splnet();
+
+ /*
+ * See if address already in list.
+ */
+ IN_LOOKUP_MULTI(*ap, ifp, inm);
+ if (inm != NULL) {
+ /*
+ * Found it; just increment the reference count.
+ */
+ ++inm->inm_refcount;
+ }
+ else {
+ /*
+ * New address; allocate a new multicast record
+ * and link it into the interface's multicast list.
+ */
+ inm = (struct in_multi *)malloc(sizeof(*inm),
+ M_IPMADDR, M_NOWAIT);
+ if (inm == NULL) {
+ splx(s);
+ return (NULL);
+ }
+ inm->inm_addr = *ap;
+ inm->inm_ifp = ifp;
+ inm->inm_refcount = 1;
+ IFP_TO_IA(ifp, ia);
+ if (ia == NULL) {
+ free(inm, M_IPMADDR);
+ splx(s);
+ return (NULL);
+ }
+ inm->inm_ia = ia;
+ inm->inm_next = ia->ia_multiaddrs;
+ ia->ia_multiaddrs = inm;
+ /*
+ * Ask the network driver to update its multicast reception
+ * filter appropriately for the new address.
+ */
+ ((struct sockaddr_in *)&ifr.ifr_addr)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr = *ap;
+ if (ifp->if_ioctl == NULL ||
+ (*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0) {
+ ia->ia_multiaddrs = inm->inm_next;
+ free(inm, M_IPMADDR);
+ splx(s);
+ return (NULL);
+ }
+ /*
+ * Let IGMP know that we have joined a new IP multicast group.
+ */
+ igmp_joingroup(inm);
+ }
+ splx(s);
+ return (inm);
+}
+
+/*
+ * Delete a multicast address record.
+ */
+int
+in_delmulti(inm)
+ register struct in_multi *inm;
+{
+ register struct in_multi **p;
+ struct ifreq ifr;
+ int s = splnet();
+
+ if (--inm->inm_refcount == 0) {
+ /*
+ * No remaining claims to this record; let IGMP know that
+ * we are leaving the multicast group.
+ */
+ igmp_leavegroup(inm);
+ /*
+ * Unlink from list.
+ */
+ for (p = &inm->inm_ia->ia_multiaddrs;
+ *p != inm;
+ p = &(*p)->inm_next)
+ continue;
+ *p = (*p)->inm_next;
+ /*
+ * Notify the network driver to update its multicast reception
+ * filter.
+ */
+ ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_family = AF_INET;
+ ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_addr =
+ inm->inm_addr;
+ (*inm->inm_ifp->if_ioctl)(inm->inm_ifp, SIOCDELMULTI,
+ (caddr_t)&ifr);
+ free(inm, M_IPMADDR);
+ }
+ splx(s);
+}
+#endif
#endif
diff --git a/sys/netinet/in.h b/sys/netinet/in.h
index 48f24b39ede4..96a9f88cea77 100644
--- a/sys/netinet/in.h
+++ b/sys/netinet/in.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)in.h 7.11 (Berkeley) 4/20/91
- * $Id: in.h,v 1.4 1993/12/19 00:52:35 wollman Exp $
+ * $Id: in.h,v 1.8 1994/05/26 22:42:14 jkh Exp $
*/
#ifndef _NETINET_IN_H_
@@ -47,6 +47,7 @@
*/
#define IPPROTO_IP 0 /* dummy for IP */
#define IPPROTO_ICMP 1 /* control message protocol */
+#define IPPROTO_IGMP 2 /* ground control protocol */
#define IPPROTO_GGP 3 /* gateway^2 (deprecated) */
#define IPPROTO_TCP 6 /* tcp */
#define IPPROTO_EGP 8 /* exterior gateway protocol */
@@ -100,20 +101,37 @@ struct in_addr {
#define IN_CLASSC_HOST 0x000000ffUL
#define IN_CLASSD(i) (((u_long)(i) & 0xf0000000UL) == 0xe0000000UL)
+#define IN_CLASSD_NET 0xf0000000UL /* These ones aren't really */
+#define IN_CLASSD_NSHIFT 28 /* net and host fields, bit */
+#define IN_CLASSD_HOST 0x0fffffffUL /* routing needn't know. */
#define IN_MULTICAST(i) IN_CLASSD(i)
#define IN_EXPERIMENTAL(i) (((u_long)(i) & 0xe0000000UL) == 0xe0000000UL)
#define IN_BADCLASS(i) (((u_long)(i) & 0xf0000000UL) == 0xf0000000UL)
#define INADDR_ANY 0x00000000UL
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK 0x7f000001UL
+#endif
#define INADDR_BROADCAST 0xffffffffUL /* must be masked */
#ifndef KERNEL
#define INADDR_NONE 0xffffffffUL /* -1 return */
#endif
+#define INADDR_UNSPEC_GROUP 0xe0000000UL /* 224.0.0.0 */
+#define INADDR_ALLHOSTS_GROUP 0xe0000001UL /* 244.0.0.1 */
+#define INADDR_MAX_LOCAL_GROUP 0xe00000ffUL /* 244.0.0.255 */
+
#define IN_LOOPBACKNET 127 /* official! */
/*
+ * Define a macro to stuff the loopback address into an Internet address.
+ */
+#define IN_SET_LOOPBACK_ADDR(a) { \
+ (a)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); \
+ (a)->sin_family = AF_INET; }
+
+/*
* Socket address, internet style.
*/
struct sockaddr_in {
@@ -148,6 +166,23 @@ struct ip_opts {
#define IP_RECVRETOPTS 6 /* bool; receive IP options for response */
#define IP_RECVDSTADDR 7 /* bool; receive IP dst addr w/datagram */
#define IP_RETOPTS 8 /* ip_opts; set/get IP per-packet options */
+#define IP_MULTICAST_IF 9 /* set/get IP multicast interfcae */
+#define IP_MULTICAST_TTL 10 /* set/get IP multicast timetolive */
+#define IP_MULTICAST_LOOP 11 /* set/get IP m'cast loopback */
+#define IP_ADD_MEMBERSHIP 12 /* add an IP group membership */
+#define IP_DROP_MEMBERSHIP 13 /* drop an IP group membership */
+
+#define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */
+#define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sens if a member */
+#define IP_MAX_MEMBERSHIPS 20 /* per socket; must fit in one mbuf */
+
+/*
+ * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.
+ */
+struct ip_mreq {
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_interface; /* local IP address of interface */
+} ;
#ifdef KERNEL
/* From in.c: */
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index cc404e6f1510..af5d742f1bac 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)in_pcb.c 7.14 (Berkeley) 4/20/91
- * $Id: in_pcb.c,v 1.5 1993/12/19 00:52:37 wollman Exp $
+ * $Id: in_pcb.c,v 1.6 1994/05/17 22:31:05 jkh Exp $
*/
#include "param.h"
@@ -51,6 +51,9 @@
#include "ip.h"
#include "in_pcb.h"
#include "in_var.h"
+#ifdef MULTICAST
+#include "ip_var.h"
+#endif
int
in_pcballoc(so, head)
@@ -70,6 +73,53 @@ in_pcballoc(so, head)
so->so_pcb = (caddr_t)inp;
return (0);
}
+/*
+ * return 1 if there's a pcb whose addresses 'confict' with the
+ * supplied addresses. Only exact matches (address with address
+ * or wildcard with wildcard) are considered to be in conflict
+ * since in_pcblookup will resolve anything else via 'best match'.
+ */
+int
+in_pcbconflict(head, faddr, laddr, fport, lport)
+ register struct inpcb *head;
+ register u_long faddr, laddr;
+ register u_short fport, lport;
+{
+ register struct inpcb *inp = head;
+
+ while ((inp = inp->inp_next) != head)
+ if (inp->inp_lport == lport &&
+ (fport == 0 || inp->inp_fport == fport) &&
+ (faddr == 0 || inp->inp_faddr.s_addr == faddr) &&
+ (laddr == 0 || inp->inp_laddr.s_addr == laddr))
+ return (1);
+ return (0);
+}
+
+/*
+ * Chose a unique (non-conflicting) local port for the inpcb list
+ * starting at 'head'. (A 'rover' is kept in the lport field of
+ * the list head to make N calls to this routine O(N^2) instead of
+ * O(N^3)). The port will always be
+ * IPPORT_RESERVED <= lport <= IPPORT_USERRESERVED
+ */
+u_short
+in_uniqueport(head, faddr, laddr, fport)
+ register struct inpcb *head;
+ register u_long faddr, laddr;
+ register u_short fport;
+{
+ register u_short lport = head->inp_lport;
+
+ do {
+ ++lport;
+ if (lport < IPPORT_RESERVED || lport > IPPORT_USERRESERVED)
+ lport = IPPORT_RESERVED;
+ } while (in_pcbconflict(head, faddr, laddr, fport, htons(lport)));
+ head->inp_lport = lport;
+ return (htons(lport));
+}
+
int
in_pcbbind(inp, nam)
@@ -107,6 +157,7 @@ in_pcbbind(inp, nam)
if (aport < IPPORT_RESERVED && (so->so_state & SS_PRIV) == 0)
return (EACCES);
/* even GROSSER, but this is the Internet */
+#ifdef notdef
if ((so->so_options & SO_REUSEADDR) == 0 &&
((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
(so->so_options & SO_ACCEPTCONN) == 0))
@@ -114,10 +165,16 @@ in_pcbbind(inp, nam)
if (in_pcblookup(head,
zeroin_addr, 0, sin->sin_addr, lport, wild))
return (EADDRINUSE);
+#else
+ if ((inp->inp_socket->so_options & SO_REUSEADDR) == 0 &&
+ in_pcbconflict(head, 0, sin->sin_addr.s_addr, 0, lport))
+ return (EADDRINUSE);
+#endif
}
inp->inp_laddr = sin->sin_addr;
noname:
if (lport == 0)
+#ifdef notdef
do {
if (head->inp_lport++ < IPPORT_RESERVED ||
head->inp_lport > IPPORT_USERRESERVED)
@@ -125,6 +182,10 @@ noname:
lport = htons(head->inp_lport);
} while (in_pcblookup(head,
zeroin_addr, 0, inp->inp_laddr, lport, 0));
+#else
+ lport = in_uniqueport(head, inp->inp_faddr.s_addr,
+ inp->inp_laddr, inp->inp_fport);
+#endif
inp->inp_lport = lport;
return (0);
}
@@ -148,8 +209,13 @@ in_pcbconnect(inp, nam)
return (EINVAL);
if (sin->sin_family != AF_INET)
return (EAFNOSUPPORT);
+#ifdef MULTICAST
+ if (sin->sin_port == 0 && !IN_MULTICAST(sin->sin_addr.s_addr))
+ return(EADDRNOTAVAIL);
+#else
if (sin->sin_port == 0)
return (EADDRNOTAVAIL);
+#endif
if (in_ifaddr) {
/*
* If the destination address is INADDR_ANY,
@@ -217,6 +283,28 @@ in_pcbconnect(inp, nam)
if (ia == 0)
return (EADDRNOTAVAIL);
}
+#ifdef MULTICAST
+ /*
+ * If the destination address is multicast and an outgoing
+ * interface has been set as a multicast option, use the
+ * address of that interface as our source address.
+ */
+ if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
+ inp->inp_moptions != NULL) {
+ struct ip_moptions *imo;
+ struct ifnet *ifp;
+
+ imo = inp->inp_moptions;
+ if (imo->imo_multicast_ifp != NULL) {
+ ifp = imo->imo_multicast_ifp;
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+ }
+#endif
ifaddr = (struct sockaddr_in *)&ia->ia_addr;
}
if (in_pcblookup(inp->inp_head,
@@ -269,6 +357,9 @@ in_pcbdetach(inp)
(void)m_free(inp->inp_options);
if (inp->inp_route.ro_rt)
rtfree(inp->inp_route.ro_rt);
+#ifdef MULTICAST
+ ip_freemoptions(inp->inp_moptions);
+#endif
remque(inp);
(void) m_free(dtom(inp));
}
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h
index e4adb614e6d4..dd4527e768d1 100644
--- a/sys/netinet/in_pcb.h
+++ b/sys/netinet/in_pcb.h
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1982, 1986, 1990 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1990, 1993
+ * 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
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)in_pcb.h 7.6 (Berkeley) 6/28/90
- * $Id: in_pcb.h,v 1.5 1993/11/25 01:35:06 wollman Exp $
+ * $Id: in_pcb.h,v 1.6 1994/05/17 22:31:06 jkh Exp $
*/
#ifndef _NETINET_IN_PCB_H_
@@ -67,6 +67,7 @@ struct inpcb {
/* function to call when MTU may have
* changed */
#endif /* MTUDISC */
+ struct ip_moptions *inp_moptions; /* IP multicast options */
};
/* flags in inp_flags: */
diff --git a/sys/netinet/in_pcb.old.c b/sys/netinet/in_pcb.old.c
new file mode 100644
index 000000000000..1946b9edb34a
--- /dev/null
+++ b/sys/netinet/in_pcb.old.c
@@ -0,0 +1,503 @@
+/*
+ * Copyright (c) 1982, 1986, 1991 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: @(#)in_pcb.c 7.14 (Berkeley) 4/20/91
+ * $Id: in_pcb.old.c,v 1.1 1994/05/17 23:16:40 jkh Exp $
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "malloc.h"
+#include "mbuf.h"
+#include "protosw.h"
+#include "socket.h"
+#include "socketvar.h"
+#include "ioctl.h"
+
+#include "../net/if.h"
+#include "../net/route.h"
+
+#include "in.h"
+#include "in_systm.h"
+#include "ip.h"
+#include "in_pcb.h"
+#include "in_var.h"
+#ifdef MULTICAST
+#include "ip_var.h"
+#endif
+
+int
+in_pcballoc(so, head)
+ struct socket *so;
+ struct inpcb *head;
+{
+ struct mbuf *m;
+ register struct inpcb *inp;
+
+ m = m_getclr(M_DONTWAIT, MT_PCB);
+ if (m == NULL)
+ return (ENOBUFS);
+ inp = mtod(m, struct inpcb *);
+ inp->inp_head = head;
+ inp->inp_socket = so;
+ insque(inp, head);
+ so->so_pcb = (caddr_t)inp;
+ return (0);
+}
+
+int
+in_pcbbind(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct socket *so = inp->inp_socket;
+ register struct inpcb *head = inp->inp_head;
+ register struct sockaddr_in *sin;
+ u_short lport = 0;
+
+ if (in_ifaddr == 0)
+ return (EADDRNOTAVAIL);
+ if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
+ return (EINVAL);
+ if (nam == 0)
+ goto noname;
+ sin = mtod(nam, struct sockaddr_in *);
+ if (nam->m_len != sizeof (*sin))
+ return (EINVAL);
+ if (sin->sin_addr.s_addr != INADDR_ANY) {
+ int tport = sin->sin_port;
+
+ sin->sin_port = 0; /* yech... */
+ if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
+ return (EADDRNOTAVAIL);
+ sin->sin_port = tport;
+ }
+ lport = sin->sin_port;
+ if (lport) {
+ u_short aport = ntohs(lport);
+ int wild = 0;
+
+ /* GROSS */
+ if (aport < IPPORT_RESERVED && (so->so_state & SS_PRIV) == 0)
+ return (EACCES);
+ /* even GROSSER, but this is the Internet */
+ if ((so->so_options & SO_REUSEADDR) == 0 &&
+ ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
+ (so->so_options & SO_ACCEPTCONN) == 0))
+ wild = INPLOOKUP_WILDCARD;
+ if (in_pcblookup(head,
+ zeroin_addr, 0, sin->sin_addr, lport, wild))
+ return (EADDRINUSE);
+ }
+ inp->inp_laddr = sin->sin_addr;
+noname:
+ if (lport == 0)
+ do {
+ if (head->inp_lport++ < IPPORT_RESERVED ||
+ head->inp_lport > IPPORT_USERRESERVED)
+ head->inp_lport = IPPORT_RESERVED;
+ lport = htons(head->inp_lport);
+ } while (in_pcblookup(head,
+ zeroin_addr, 0, inp->inp_laddr, lport, 0));
+ inp->inp_lport = lport;
+ return (0);
+}
+
+/*
+ * Connect from a socket to a specified address.
+ * Both address and port must be specified in argument sin.
+ * If don't have a local address for this socket yet,
+ * then pick one.
+ */
+int
+in_pcbconnect(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ struct in_ifaddr *ia;
+ struct sockaddr_in *ifaddr = 0;
+ register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
+
+ if (nam->m_len != sizeof (*sin))
+ return (EINVAL);
+ if (sin->sin_family != AF_INET)
+ return (EAFNOSUPPORT);
+#ifdef MULTICAST
+ if (sin->sin_port == 0 && !IN_MULTICAST(sin->sin_addr.s_addr))
+ return (EADDRNOTAVAIL);
+#else
+ if (sin->sin_port == 0)
+ return (EADDRNOTAVAIL);
+#endif
+ if (in_ifaddr) {
+ /*
+ * If the destination address is INADDR_ANY,
+ * use the primary local address.
+ * If the supplied address is INADDR_BROADCAST,
+ * and the primary interface supports broadcast,
+ * choose the broadcast address for that interface.
+ */
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+ if (sin->sin_addr.s_addr == INADDR_ANY)
+ sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
+ else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
+ (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
+ sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
+ }
+ if (inp->inp_laddr.s_addr == INADDR_ANY) {
+ register struct route *ro;
+ struct ifnet *ifp;
+
+ ia = (struct in_ifaddr *)0;
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ */
+ ro = &inp->inp_route;
+ if (ro->ro_rt &&
+ (satosin(&ro->ro_dst)->sin_addr.s_addr !=
+ sin->sin_addr.s_addr ||
+ inp->inp_socket->so_options & SO_DONTROUTE)) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
+ (ro->ro_rt == (struct rtentry *)0 ||
+ ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
+ /* No route yet, so try to acquire one */
+ ro->ro_dst.sa_family = AF_INET;
+ ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
+ ((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
+ sin->sin_addr;
+ rtalloc(ro);
+ }
+ /*
+ * If we found a route, use the address
+ * corresponding to the outgoing interface
+ * unless it is the loopback (in case a route
+ * to our address on another net goes to loopback).
+ */
+ if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp) &&
+ (ifp->if_flags & IFF_LOOPBACK) == 0)
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0) {
+ int fport = sin->sin_port;
+
+ sin->sin_port = 0;
+ ia = (struct in_ifaddr *)
+ ifa_ifwithdstaddr((struct sockaddr *)sin);
+ sin->sin_port = fport;
+ if (ia == 0)
+ ia = in_iaonnetof(in_netof(sin->sin_addr));
+ if (ia == 0)
+ ia = in_ifaddr;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+#ifdef MULTICAST
+ /*
+ * If the destination address is multicast and an outgoing
+ * interface has been set as a multicast option, use the
+ * address of that interface as our source address.
+ */
+ if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
+ inp->inp_moptions != NULL) {
+ struct ip_moptions *imo;
+ struct ifnet *ifp;
+
+ imo = inp->inp_moptions;
+ if (imo->imo_multicast_ifp != NULL) {
+ ifp = imo->imo_multicast_ifp;
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+ }
+#endif
+ ifaddr = (struct sockaddr_in *)&ia->ia_addr;
+ }
+ if (in_pcblookup(inp->inp_head,
+ sin->sin_addr,
+ sin->sin_port,
+ inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
+ inp->inp_lport,
+ 0))
+ return (EADDRINUSE);
+ if (inp->inp_laddr.s_addr == INADDR_ANY) {
+ if (inp->inp_lport == 0)
+ (void)in_pcbbind(inp, (struct mbuf *)0);
+ inp->inp_laddr = ifaddr->sin_addr;
+ }
+ inp->inp_faddr = sin->sin_addr;
+ inp->inp_fport = sin->sin_port;
+#ifdef MTUDISC
+ /*
+ * If the upper layer asked for PMTU discovery services, see
+ * if we can get an idea of what the MTU should be...
+ */
+ in_pcbmtu(inp);
+#endif /* MTUDISC */
+ return (0);
+}
+
+void
+in_pcbdisconnect(inp)
+ struct inpcb *inp;
+{
+
+ inp->inp_faddr.s_addr = INADDR_ANY;
+ inp->inp_fport = 0;
+#ifdef MTUDISC
+ inp->inp_flags &= ~INP_MTUDISCOVERED;
+#endif
+ if (inp->inp_socket->so_state & SS_NOFDREF)
+ in_pcbdetach(inp);
+}
+
+void
+in_pcbdetach(inp)
+ struct inpcb *inp;
+{
+ struct socket *so = inp->inp_socket;
+
+ so->so_pcb = 0;
+ sofree(so);
+ if (inp->inp_options)
+ (void)m_free(inp->inp_options);
+ if (inp->inp_route.ro_rt)
+ rtfree(inp->inp_route.ro_rt);
+#ifdef MULTICAST
+ ip_freemoptions(inp->inp_moptions);
+#endif
+ remque(inp);
+ (void) m_free(dtom(inp));
+}
+
+void
+in_setsockaddr(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *sin;
+
+ nam->m_len = sizeof (*sin);
+ sin = mtod(nam, struct sockaddr_in *);
+ bzero((caddr_t)sin, sizeof (*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_port = inp->inp_lport;
+ sin->sin_addr = inp->inp_laddr;
+}
+
+void
+in_setpeeraddr(inp, nam)
+ struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *sin;
+
+ nam->m_len = sizeof (*sin);
+ sin = mtod(nam, struct sockaddr_in *);
+ bzero((caddr_t)sin, sizeof (*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_port = inp->inp_fport;
+ sin->sin_addr = inp->inp_faddr;
+}
+
+/*
+ * Pass some notification to all connections of a protocol
+ * associated with address dst. The local address and/or port numbers
+ * may be specified to limit the search. The "usual action" will be
+ * taken, depending on the ctlinput cmd. The caller must filter any
+ * cmds that are uninteresting (e.g., no error in the map).
+ * Call the protocol specific routine (if any) to report
+ * any errors for each matching socket.
+ *
+ * Must be called at splnet.
+ */
+void
+in_pcbnotify(head, dst, fport, laddr, lport, cmd, notify)
+ struct inpcb *head;
+ struct sockaddr *dst;
+ u_short fport, lport;
+ struct in_addr laddr;
+ int cmd;
+ void (*notify)(struct inpcb *, int);
+{
+ register struct inpcb *inp, *oinp;
+ struct in_addr faddr;
+ int errno;
+
+ if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
+ return;
+ faddr = ((struct sockaddr_in *)dst)->sin_addr;
+ if (faddr.s_addr == INADDR_ANY)
+ return;
+
+ /*
+ * Redirects go to all references to the destination,
+ * and use in_rtchange to invalidate the route cache.
+ * Dead host indications: notify all references to the destination.
+ * MTU change indications: same thing.
+ * Otherwise, if we have knowledge of the local port and address,
+ * deliver only to that socket.
+ */
+ if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD
+ || cmd == PRC_MTUCHANGED) {
+ fport = 0;
+ lport = 0;
+ laddr.s_addr = 0;
+ if (cmd != PRC_HOSTDEAD && cmd != PRC_MTUCHANGED)
+ notify = in_rtchange;
+ }
+ errno = inetctlerrmap[cmd];
+ for (inp = head->inp_next; inp != head;) {
+ if (inp->inp_faddr.s_addr != faddr.s_addr ||
+ inp->inp_socket == 0 ||
+ (lport && inp->inp_lport != lport) ||
+ (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
+ (fport && inp->inp_fport != fport)) {
+ inp = inp->inp_next;
+ continue;
+ }
+ oinp = inp;
+ inp = inp->inp_next;
+ if (notify)
+ (*notify)(oinp, errno);
+ }
+}
+
+/*
+ * Check for alternatives when higher level complains
+ * about service problems. For now, invalidate cached
+ * routing information. If the route was created dynamically
+ * (by a redirect), time to try a default gateway again.
+ */
+void
+in_losing(inp)
+ struct inpcb *inp;
+{
+ register struct rtentry *rt;
+
+ if ((rt = inp->inp_route.ro_rt)) {
+ rt_missmsg(RTM_LOSING, &inp->inp_route.ro_dst,
+ rt->rt_gateway, (struct sockaddr *)rt_mask(rt),
+ (struct sockaddr *)0, rt->rt_flags, 0);
+ if (rt->rt_flags & RTF_DYNAMIC)
+ (void) rtrequest(RTM_DELETE, rt_key(rt),
+ rt->rt_gateway, rt_mask(rt), rt->rt_flags,
+ (struct rtentry **)0);
+ inp->inp_route.ro_rt = 0;
+ rtfree(rt);
+
+#ifdef MTUDISC
+ /*
+ * When doing MTU discovery, we want to find out as
+ * quickly as possible what the MTU of the new route is.
+ */
+ in_pcbmtu(inp);
+#endif /* MTUDISC */
+ }
+}
+
+/*
+ * After a routing change, flush old routing
+ * and allocate a (hopefully) better one.
+ */
+void
+in_rtchange(inp, errno)
+ register struct inpcb *inp;
+ int errno;
+{
+ if (inp->inp_route.ro_rt) {
+ rtfree(inp->inp_route.ro_rt);
+ inp->inp_route.ro_rt = 0;
+#ifdef MTUDISC
+ /*
+ * A new route can be allocated the next time
+ * output is attempted, but make sure to let
+ * MTU discovery know about it.
+ */
+ in_pcbmtu(inp);
+#endif /* MTUDISC */
+ }
+}
+
+struct inpcb *
+in_pcblookup(head, faddr, fport, laddr, lport, flags)
+ struct inpcb *head;
+ struct in_addr faddr, laddr;
+ u_short fport, lport;
+ int flags;
+{
+ register struct inpcb *inp, *match = 0;
+ int matchwild = 3, wildcard;
+
+ for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
+ if (inp->inp_lport != lport)
+ continue;
+ wildcard = 0;
+ if (inp->inp_laddr.s_addr != INADDR_ANY) {
+ if (laddr.s_addr == INADDR_ANY)
+ wildcard++;
+ else if (inp->inp_laddr.s_addr != laddr.s_addr)
+ continue;
+ } else {
+ if (laddr.s_addr != INADDR_ANY)
+ wildcard++;
+ }
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ if (faddr.s_addr == INADDR_ANY)
+ wildcard++;
+ else if (inp->inp_faddr.s_addr != faddr.s_addr ||
+ inp->inp_fport != fport)
+ continue;
+ } else {
+ if (faddr.s_addr != INADDR_ANY)
+ wildcard++;
+ }
+ if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
+ continue;
+ if (wildcard < matchwild) {
+ match = inp;
+ matchwild = wildcard;
+ if (matchwild == 0)
+ break;
+ }
+ }
+ return (match);
+}
diff --git a/sys/netinet/in_pcb.orig.c b/sys/netinet/in_pcb.orig.c
new file mode 100644
index 000000000000..badedf559fda
--- /dev/null
+++ b/sys/netinet/in_pcb.orig.c
@@ -0,0 +1,498 @@
+/*
+ * Copyright (c) 1982, 1986, 1991 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: @(#)in_pcb.c 7.14 (Berkeley) 4/20/91
+ * $Id: in_pcb.orig.c,v 1.1 1994/05/17 23:16:41 jkh Exp $
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "malloc.h"
+#include "mbuf.h"
+#include "protosw.h"
+#include "socket.h"
+#include "socketvar.h"
+#include "ioctl.h"
+
+#include "../net/if.h"
+#include "../net/route.h"
+
+#include "in.h"
+#include "in_systm.h"
+#include "ip.h"
+#include "in_pcb.h"
+#include "in_var.h"
+#ifdef MULTICAST
+#include "ip_var.h"
+#endif
+
+int
+in_pcballoc(so, head)
+ struct socket *so;
+ struct inpcb *head;
+{
+ struct mbuf *m;
+ register struct inpcb *inp;
+
+ m = m_getclr(M_DONTWAIT, MT_PCB);
+ if (m == NULL)
+ return (ENOBUFS);
+ inp = mtod(m, struct inpcb *);
+ inp->inp_head = head;
+ inp->inp_socket = so;
+ insque(inp, head);
+ so->so_pcb = (caddr_t)inp;
+ return (0);
+}
+
+int
+in_pcbbind(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct socket *so = inp->inp_socket;
+ register struct inpcb *head = inp->inp_head;
+ register struct sockaddr_in *sin;
+ u_short lport = 0;
+
+ if (in_ifaddr == 0)
+ return (EADDRNOTAVAIL);
+ if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
+ return (EINVAL);
+ if (nam == 0)
+ goto noname;
+ sin = mtod(nam, struct sockaddr_in *);
+ if (nam->m_len != sizeof (*sin))
+ return (EINVAL);
+ if (sin->sin_addr.s_addr != INADDR_ANY) {
+ int tport = sin->sin_port;
+
+ sin->sin_port = 0; /* yech... */
+ if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
+ return (EADDRNOTAVAIL);
+ sin->sin_port = tport;
+ }
+ lport = sin->sin_port;
+ if (lport) {
+ u_short aport = ntohs(lport);
+ int wild = 0;
+
+ /* GROSS */
+ if (aport < IPPORT_RESERVED && (so->so_state & SS_PRIV) == 0)
+ return (EACCES);
+ /* even GROSSER, but this is the Internet */
+ if ((so->so_options & SO_REUSEADDR) == 0 &&
+ ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
+ (so->so_options & SO_ACCEPTCONN) == 0))
+ wild = INPLOOKUP_WILDCARD;
+ if (in_pcblookup(head,
+ zeroin_addr, 0, sin->sin_addr, lport, wild))
+ return (EADDRINUSE);
+ }
+ inp->inp_laddr = sin->sin_addr;
+noname:
+ if (lport == 0)
+ do {
+ if (head->inp_lport++ < IPPORT_RESERVED ||
+ head->inp_lport > IPPORT_USERRESERVED)
+ head->inp_lport = IPPORT_RESERVED;
+ lport = htons(head->inp_lport);
+ } while (in_pcblookup(head,
+ zeroin_addr, 0, inp->inp_laddr, lport, 0));
+ inp->inp_lport = lport;
+ return (0);
+}
+
+/*
+ * Connect from a socket to a specified address.
+ * Both address and port must be specified in argument sin.
+ * If don't have a local address for this socket yet,
+ * then pick one.
+ */
+int
+in_pcbconnect(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ struct in_ifaddr *ia;
+ struct sockaddr_in *ifaddr = 0;
+ register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
+
+ if (nam->m_len != sizeof (*sin))
+ return (EINVAL);
+ if (sin->sin_family != AF_INET)
+ return (EAFNOSUPPORT);
+ if (sin->sin_port == 0)
+ return (EADDRNOTAVAIL);
+ if (in_ifaddr) {
+ /*
+ * If the destination address is INADDR_ANY,
+ * use the primary local address.
+ * If the supplied address is INADDR_BROADCAST,
+ * and the primary interface supports broadcast,
+ * choose the broadcast address for that interface.
+ */
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+ if (sin->sin_addr.s_addr == INADDR_ANY)
+ sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
+ else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
+ (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
+ sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
+ }
+ if (inp->inp_laddr.s_addr == INADDR_ANY) {
+ register struct route *ro;
+ struct ifnet *ifp;
+
+ ia = (struct in_ifaddr *)0;
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ */
+ ro = &inp->inp_route;
+ if (ro->ro_rt &&
+ (satosin(&ro->ro_dst)->sin_addr.s_addr !=
+ sin->sin_addr.s_addr ||
+ inp->inp_socket->so_options & SO_DONTROUTE)) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
+ (ro->ro_rt == (struct rtentry *)0 ||
+ ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
+ /* No route yet, so try to acquire one */
+ ro->ro_dst.sa_family = AF_INET;
+ ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
+ ((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
+ sin->sin_addr;
+ rtalloc(ro);
+ }
+ /*
+ * If we found a route, use the address
+ * corresponding to the outgoing interface
+ * unless it is the loopback (in case a route
+ * to our address on another net goes to loopback).
+ */
+ if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp) &&
+ (ifp->if_flags & IFF_LOOPBACK) == 0)
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0) {
+ int fport = sin->sin_port;
+
+ sin->sin_port = 0;
+ ia = (struct in_ifaddr *)
+ ifa_ifwithdstaddr((struct sockaddr *)sin);
+ sin->sin_port = fport;
+ if (ia == 0)
+ ia = in_iaonnetof(in_netof(sin->sin_addr));
+ if (ia == 0)
+ ia = in_ifaddr;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+#ifdef MULTICAST
+ /*
+ * If the destination address is multicast and an outgoing
+ * interface has been set as a multicast option, use the
+ * address of that interface as our source address.
+ */
+ if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
+ inp->inp_moptions != NULL) {
+ struct ip_moptions *imo;
+ struct ifnet *ifp;
+
+ imo = inp->inp_moptions;
+ if (imo->imo_multicast_ifp != NULL) {
+ ifp = imo->imo_multicast_ifp;
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+ }
+#endif
+ ifaddr = (struct sockaddr_in *)&ia->ia_addr;
+ }
+ if (in_pcblookup(inp->inp_head,
+ sin->sin_addr,
+ sin->sin_port,
+ inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
+ inp->inp_lport,
+ 0))
+ return (EADDRINUSE);
+ if (inp->inp_laddr.s_addr == INADDR_ANY) {
+ if (inp->inp_lport == 0)
+ (void)in_pcbbind(inp, (struct mbuf *)0);
+ inp->inp_laddr = ifaddr->sin_addr;
+ }
+ inp->inp_faddr = sin->sin_addr;
+ inp->inp_fport = sin->sin_port;
+#ifdef MTUDISC
+ /*
+ * If the upper layer asked for PMTU discovery services, see
+ * if we can get an idea of what the MTU should be...
+ */
+ in_pcbmtu(inp);
+#endif /* MTUDISC */
+ return (0);
+}
+
+void
+in_pcbdisconnect(inp)
+ struct inpcb *inp;
+{
+
+ inp->inp_faddr.s_addr = INADDR_ANY;
+ inp->inp_fport = 0;
+#ifdef MTUDISC
+ inp->inp_flags &= ~INP_MTUDISCOVERED;
+#endif
+ if (inp->inp_socket->so_state & SS_NOFDREF)
+ in_pcbdetach(inp);
+}
+
+void
+in_pcbdetach(inp)
+ struct inpcb *inp;
+{
+ struct socket *so = inp->inp_socket;
+
+ so->so_pcb = 0;
+ sofree(so);
+ if (inp->inp_options)
+ (void)m_free(inp->inp_options);
+ if (inp->inp_route.ro_rt)
+ rtfree(inp->inp_route.ro_rt);
+#ifdef MULTICAST
+ ip_freemoptions(inp->inp_moptions);
+#endif
+ remque(inp);
+ (void) m_free(dtom(inp));
+}
+
+void
+in_setsockaddr(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *sin;
+
+ nam->m_len = sizeof (*sin);
+ sin = mtod(nam, struct sockaddr_in *);
+ bzero((caddr_t)sin, sizeof (*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_port = inp->inp_lport;
+ sin->sin_addr = inp->inp_laddr;
+}
+
+void
+in_setpeeraddr(inp, nam)
+ struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *sin;
+
+ nam->m_len = sizeof (*sin);
+ sin = mtod(nam, struct sockaddr_in *);
+ bzero((caddr_t)sin, sizeof (*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_port = inp->inp_fport;
+ sin->sin_addr = inp->inp_faddr;
+}
+
+/*
+ * Pass some notification to all connections of a protocol
+ * associated with address dst. The local address and/or port numbers
+ * may be specified to limit the search. The "usual action" will be
+ * taken, depending on the ctlinput cmd. The caller must filter any
+ * cmds that are uninteresting (e.g., no error in the map).
+ * Call the protocol specific routine (if any) to report
+ * any errors for each matching socket.
+ *
+ * Must be called at splnet.
+ */
+void
+in_pcbnotify(head, dst, fport, laddr, lport, cmd, notify)
+ struct inpcb *head;
+ struct sockaddr *dst;
+ u_short fport, lport;
+ struct in_addr laddr;
+ int cmd;
+ void (*notify)(struct inpcb *, int);
+{
+ register struct inpcb *inp, *oinp;
+ struct in_addr faddr;
+ int errno;
+
+ if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
+ return;
+ faddr = ((struct sockaddr_in *)dst)->sin_addr;
+ if (faddr.s_addr == INADDR_ANY)
+ return;
+
+ /*
+ * Redirects go to all references to the destination,
+ * and use in_rtchange to invalidate the route cache.
+ * Dead host indications: notify all references to the destination.
+ * MTU change indications: same thing.
+ * Otherwise, if we have knowledge of the local port and address,
+ * deliver only to that socket.
+ */
+ if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD
+ || cmd == PRC_MTUCHANGED) {
+ fport = 0;
+ lport = 0;
+ laddr.s_addr = 0;
+ if (cmd != PRC_HOSTDEAD && cmd != PRC_MTUCHANGED)
+ notify = in_rtchange;
+ }
+ errno = inetctlerrmap[cmd];
+ for (inp = head->inp_next; inp != head;) {
+ if (inp->inp_faddr.s_addr != faddr.s_addr ||
+ inp->inp_socket == 0 ||
+ (lport && inp->inp_lport != lport) ||
+ (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
+ (fport && inp->inp_fport != fport)) {
+ inp = inp->inp_next;
+ continue;
+ }
+ oinp = inp;
+ inp = inp->inp_next;
+ if (notify)
+ (*notify)(oinp, errno);
+ }
+}
+
+/*
+ * Check for alternatives when higher level complains
+ * about service problems. For now, invalidate cached
+ * routing information. If the route was created dynamically
+ * (by a redirect), time to try a default gateway again.
+ */
+void
+in_losing(inp)
+ struct inpcb *inp;
+{
+ register struct rtentry *rt;
+
+ if ((rt = inp->inp_route.ro_rt)) {
+ rt_missmsg(RTM_LOSING, &inp->inp_route.ro_dst,
+ rt->rt_gateway, (struct sockaddr *)rt_mask(rt),
+ (struct sockaddr *)0, rt->rt_flags, 0);
+ if (rt->rt_flags & RTF_DYNAMIC)
+ (void) rtrequest(RTM_DELETE, rt_key(rt),
+ rt->rt_gateway, rt_mask(rt), rt->rt_flags,
+ (struct rtentry **)0);
+ inp->inp_route.ro_rt = 0;
+ rtfree(rt);
+
+#ifdef MTUDISC
+ /*
+ * When doing MTU discovery, we want to find out as
+ * quickly as possible what the MTU of the new route is.
+ */
+ in_pcbmtu(inp);
+#endif /* MTUDISC */
+ }
+}
+
+/*
+ * After a routing change, flush old routing
+ * and allocate a (hopefully) better one.
+ */
+void
+in_rtchange(inp, errno)
+ register struct inpcb *inp;
+ int errno;
+{
+ if (inp->inp_route.ro_rt) {
+ rtfree(inp->inp_route.ro_rt);
+ inp->inp_route.ro_rt = 0;
+#ifdef MTUDISC
+ /*
+ * A new route can be allocated the next time
+ * output is attempted, but make sure to let
+ * MTU discovery know about it.
+ */
+ in_pcbmtu(inp);
+#endif /* MTUDISC */
+ }
+}
+
+struct inpcb *
+in_pcblookup(head, faddr, fport, laddr, lport, flags)
+ struct inpcb *head;
+ struct in_addr faddr, laddr;
+ u_short fport, lport;
+ int flags;
+{
+ register struct inpcb *inp, *match = 0;
+ int matchwild = 3, wildcard;
+
+ for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
+ if (inp->inp_lport != lport)
+ continue;
+ wildcard = 0;
+ if (inp->inp_laddr.s_addr != INADDR_ANY) {
+ if (laddr.s_addr == INADDR_ANY)
+ wildcard++;
+ else if (inp->inp_laddr.s_addr != laddr.s_addr)
+ continue;
+ } else {
+ if (laddr.s_addr != INADDR_ANY)
+ wildcard++;
+ }
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ if (faddr.s_addr == INADDR_ANY)
+ wildcard++;
+ else if (inp->inp_faddr.s_addr != faddr.s_addr ||
+ inp->inp_fport != fport)
+ continue;
+ } else {
+ if (faddr.s_addr != INADDR_ANY)
+ wildcard++;
+ }
+ if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
+ continue;
+ if (wildcard < matchwild) {
+ match = inp;
+ matchwild = wildcard;
+ if (matchwild == 0)
+ break;
+ }
+ }
+ return (match);
+}
diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c
index dc147f090b51..c94cdd0a101b 100644
--- a/sys/netinet/in_proto.c
+++ b/sys/netinet/in_proto.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)in_proto.c 7.5 (Berkeley) 6/28/90
- * $Id: in_proto.c,v 1.3 1993/12/19 00:52:38 wollman Exp $
+ * $Id: in_proto.c,v 1.4 1994/05/17 22:31:07 jkh Exp $
*/
#include "param.h"
@@ -55,6 +55,9 @@
#include "udp.h"
#include "udp_var.h" /* UDP prototypes */
+#include "igmp.h"
+#include "igmp_var.h" /* IGMP prototypes */
+
#include "tcp.h"
#include "tcp_fsm.h"
#include "tcp_seq.h"
@@ -160,6 +163,13 @@ struct in_protosw inetsw[] = {
eonprotoinit, 0, 0, 0,
},
#endif
+#ifdef MULTICAST
+{ SOCK_RAW, &inetdomain, IPPROTO_IGMP, PR_ATOMIC|PR_ADDR,
+ igmp_input, rip_output, 0, rip_ctloutput,
+ rip_usrreq,
+ igmp_init, igmp_fasttimo, 0, 0,
+},
+#endif
#ifdef NSIP
{ SOCK_RAW, &inetdomain, IPPROTO_IDP, PR_ATOMIC|PR_ADDR,
idpip_input, rip_output, nsip_ctlinput, 0,
diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h
index 9e03a296adb4..70d7a8185f01 100644
--- a/sys/netinet/in_var.h
+++ b/sys/netinet/in_var.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)in_var.h 7.6 (Berkeley) 6/28/90
- * $Id: in_var.h,v 1.6 1993/12/19 21:43:26 wollman Exp $
+ * $Id: in_var.h,v 1.7 1994/05/17 22:31:08 jkh Exp $
*/
#ifndef _NETINET_IN_VAR_H_
@@ -58,6 +58,7 @@ struct in_ifaddr {
struct sockaddr_in ia_dstaddr; /* reserve space for broadcast addr */
#define ia_broadaddr ia_dstaddr
struct sockaddr_in ia_sockmask; /* reserve space for general netmask */
+ struct in_multi *ia_multiaddrs; /* list of multicast addresses */
};
struct in_aliasreq {
@@ -144,5 +145,119 @@ extern int in_routemtu(struct route *);
extern void in_mtureduce(struct in_addr, unsigned);
extern void in_mtutimer(caddr_t, int);
#endif /* MTUDISC */
+
+/*
+ * Macro for finding the interface (ifnet structure) corresponding to one
+ * of our IP addresses.
+ */
+#define INADDR_TO_IFP(addr, ifp) \
+ /* struct in_addr addr; */ \
+ /* struct ifnet *ifp; */ \
+{ \
+ register struct in_ifaddr *ia; \
+\
+ for (ia = in_ifaddr; \
+ ia != NULL && IA_SIN(ia)->sin_addr.s_addr != (addr).s_addr; \
+ ia = ia->ia_next) \
+ continue; \
+ (ifp) = (ia == NULL) ? NULL : ia->ia_ifp; \
+}
+
+/*
+ * Macro for finding the internet address structure (in_ifaddr) corresponding
+ * to a given interface (ifnet structure).
+ */
+#define IFP_TO_IA(ifp, ia) \
+ /* struct ifnet *ifp; */ \
+ /* struct in_ifaddr *ia; */ \
+{ \
+ for ((ia) = in_ifaddr; \
+ (ia) != NULL && (ia)->ia_ifp != (ifp); \
+ (ia) = (ia)->ia_next) \
+ continue; \
+}
+#endif
+
+/*
+ * Internet multicast address structure. There is one of these for each IP
+ * multicast group to which this host belongs on a given network interface.
+ * They are kept in a linked list, rooted in the interface's in_ifaddr
+ * structure.
+ */
+struct in_multi {
+ struct in_addr inm_addr; /* IP multicast address */
+ struct ifnet *inm_ifp; /* back pointer to ifnet */
+ struct in_ifaddr *inm_ia; /* back pointer to in_ifaddr */
+ u_int inm_refcount; /* no. membership claims by sockets */
+ u_int inm_timer; /* IGMP membership report timer */
+ struct in_multi *inm_next; /* ptr to next multicast address */
+};
+
+#ifdef KERNEL
+/*
+ * Structure used by macros below to remember position when stepping through
+ * all of the in_multi records.
+ */
+struct in_multistep {
+ struct in_ifaddr *i_ia;
+ struct in_multi *i_inm;
+};
+
+/*
+ * Macro for looking up the in_multi record for a given IP multicast address
+ * on a given interface. If no matching record is found, "inm" returns NULL.
+ */
+#define IN_LOOKUP_MULTI(addr, ifp, inm) \
+ /* struct in_addr addr; */ \
+ /* struct ifnet *ifp; */ \
+ /* struct in_multi *inm; */ \
+{ \
+ register struct in_ifaddr *ia; \
+\
+ IFP_TO_IA((ifp), ia); \
+ if (ia == NULL) \
+ (inm) = NULL; \
+ else \
+ for ((inm) = ia->ia_multiaddrs; \
+ (inm) != NULL && (inm)->inm_addr.s_addr != (addr).s_addr; \
+ (inm) = inm->inm_next) \
+ continue; \
+}
+
+/*
+ * Macro to step through all of the in_multi records, one at a time.
+ * The current position is remembered in "step", which the caller must
+ * provide. IN_FIRST_MULTI(), below, must be called to initialize "step"
+ * and get the first record. Both macros return a NULL "inm" when there
+ * are no remaining records.
+ */
+#define IN_NEXT_MULTI(step, inm) \
+ /* struct in_multistep step; */ \
+ /* struct in_multi *inm; */ \
+{ \
+ if (((inm) = (step).i_inm) != NULL) \
+ (step).i_inm = (inm)->inm_next; \
+ else \
+ while ((step).i_ia != NULL) { \
+ (inm) = (step).i_ia->ia_multiaddrs; \
+ (step).i_ia = (step).i_ia->ia_next; \
+ if ((inm) != NULL) { \
+ (step).i_inm = (inm)->inm_next; \
+ break; \
+ } \
+ } \
+}
+
+#define IN_FIRST_MULTI(step, inm) \
+ /* struct in_multistep step; */ \
+ /* struct in_multi *inm; */ \
+{ \
+ (step).i_ia = in_ifaddr; \
+ (step).i_inm = NULL; \
+ IN_NEXT_MULTI((step), (inm)); \
+}
+
+struct in_multi *in_addmulti __P((struct in_addr *, struct ifnet *));
+int in_delmulti __P((struct in_multi *));
#endif /* KERNEL */
#endif /* _NETINET_IN_VAR_H_ */
diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c
index ed9b199e1db4..c192ef2d908b 100644
--- a/sys/netinet/ip_icmp.c
+++ b/sys/netinet/ip_icmp.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)ip_icmp.c 7.15 (Berkeley) 4/20/91
- * $Id: ip_icmp.c,v 1.6 1993/12/19 00:52:42 wollman Exp $
+ * $Id: ip_icmp.c,v 1.7 1994/05/17 22:31:09 jkh Exp $
*/
#include "param.h"
@@ -156,6 +156,11 @@ icmp_error(n, type, code, dest, mtu)
icmpstat.icps_oldicmp++;
goto freeit;
}
+#ifdef MULTICAST
+ /* Don't send error in response to a multicast or broadcast packet */
+ if(n->m_flags & (M_MCAST | M_BCAST))
+ goto freeit;
+#endif
/*
* First, formulate icmp message
diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c
index 6c45aed63234..426e757824d5 100644
--- a/sys/netinet/ip_input.c
+++ b/sys/netinet/ip_input.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * 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
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)ip_input.c 7.19 (Berkeley) 5/25/91
- * $Id: ip_input.c,v 1.8 1994/01/04 17:47:13 ache Exp $
+ * $Id: ip_input.c,v 1.9 1994/05/17 22:31:10 jkh Exp $
*/
#include "param.h"
@@ -240,6 +240,53 @@ next:
goto ours;
}
}
+#ifdef MULTICAST
+ if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
+ struct in_multi *inm;
+#ifdef MROUTING
+ extern struct socket *ip_mrouter;
+
+ if (ip_mrouter) {
+ /*
+ * If we are acting as a multicast router, all
+ * incoming multicast packets are passed to the
+ * kernel-level multicast forwarding function.
+ * The packet is returned (relatively) intact; if
+ * ip_mforward() returns a non-zero value, the packet
+ * must be discarded, else it may be accepted below.
+ *
+ * (The IP ident field is put in the same byte order
+ * as expected when ip_mforward() is called from
+ * ip_output().)
+ */
+ ip->ip_id = htons(ip->ip_id);
+ if (ip_mforward(ip, m->m_pkthdr.rcvif, m) != 0) {
+ m_freem(m);
+ goto next;
+ }
+ ip->ip_id = ntohs(ip->ip_id);
+
+ /*
+ * The process-level routing demon needs to receive
+ * all multicast IGMP packets, whether or not this
+ * host belongs to their destination groups.
+ */
+ if (ip->ip_p == IPPROTO_IGMP)
+ goto ours;
+ }
+#endif
+ /*
+ * See if we belong to the destination multicast group on the
+ * arrival interface.
+ */
+ IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
+ if (inm == NULL) {
+ m_freem(m);
+ goto next;
+ }
+ goto ours;
+ }
+#endif
if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
goto ours;
if (ip->ip_dst.s_addr == INADDR_ANY)
diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c
new file mode 100644
index 000000000000..7ce8361ea3b9
--- /dev/null
+++ b/sys/netinet/ip_mroute.c
@@ -0,0 +1,1056 @@
+/*
+ * Copyright (c) 1989 Stephen Deering
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * 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.
+ *
+ * @(#)ip_mroute.c 7.4 (Berkeley) 11/19/92
+ */
+
+/*
+ * Procedures for the kernel part of DVMRP,
+ * a Distance-Vector Multicast Routing Protocol.
+ * (See RFC-1075.)
+ *
+ * Written by David Waitzman, BBN Labs, August 1988.
+ * Modified by Steve Deering, Stanford, February 1989.
+ *
+ * MROUTING 1.1
+ */
+
+#ifndef MROUTING
+int ip_mrtproto; /* for netstat only */
+#else
+
+#include "param.h"
+#include "errno.h"
+#include "ioctl.h"
+#include "malloc.h"
+#include "mbuf.h"
+#include "protosw.h"
+#include "socket.h"
+#include "socketvar.h"
+#include "time.h"
+
+#ifndef __FreeBSD__
+#include "net/af.h"
+#else
+#include "systm.h"
+#endif
+#include "net/if.h"
+#include "net/route.h"
+#include "net/raw_cb.h"
+
+#include "in.h"
+#include "in_systm.h"
+#include "ip.h"
+#include "in_pcb.h"
+#include "in_var.h"
+#include "ip_var.h"
+
+#include "igmp.h"
+#include "igmp_var.h"
+#include "ip_mroute.h"
+
+/* Static forwards */
+static int ip_mrouter_init __P((struct socket *));
+static int add_vif __P((struct vifctl *));
+static int del_vif __P((vifi_t *vifip));
+static int add_lgrp __P((struct lgrplctl *));
+static int del_lgrp __P((struct lgrplctl *));
+static int grplst_member __P((struct vif *, struct in_addr));
+static u_long nethash __P((u_long in));
+static int add_mrt __P((struct mrtctl *));
+static int del_mrt __P((struct in_addr *));
+static struct mrt *mrtfind __P((u_long));
+static void phyint_send __P((struct ip *, struct vif *, struct mbuf *));
+static void srcrt_send __P((struct ip *, struct vif *, struct mbuf *));
+static void encap_send __P((struct ip *, struct vif *, struct mbuf *));
+static void multiencap_decap __P((struct mbuf *, int hlen));
+
+#define INSIZ sizeof(struct in_addr)
+#define same(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), INSIZ) == 0)
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+
+/*
+ * Globals. All but ip_mrouter and ip_mrtproto could be static,
+ * except for netstat or debugging purposes.
+ */
+struct socket *ip_mrouter = NULL;
+int ip_mrtproto = IGMP_DVMRP; /* for netstat only */
+
+struct mrt *mrttable[MRTHASHSIZ];
+struct vif viftable[MAXVIFS];
+struct mrtstat mrtstat;
+
+/*
+ * 'Interfaces' associated with decapsulator (so we can tell
+ * packets that went through it from ones that get reflected
+ * by a broken gateway). These interfaces are never linked into
+ * the system ifnet list & no routes point to them. I.e., packets
+ * can't be sent this way. They only exist as a placeholder for
+ * multicast source verification.
+ */
+struct ifnet multicast_decap_if[MAXVIFS];
+
+#define ENCAP_TTL 64
+#define ENCAP_PROTO 4
+
+/* prototype IP hdr for encapsulated packets */
+struct ip multicast_encap_iphdr = {
+#if defined(ultrix) || defined(i386)
+ sizeof(struct ip) >> 2, IPVERSION,
+#else
+ IPVERSION, sizeof(struct ip) >> 2,
+#endif
+ 0, /* tos */
+ sizeof(struct ip), /* total length */
+ 0, /* id */
+ 0, /* frag offset */
+ ENCAP_TTL, ENCAP_PROTO,
+ 0, /* checksum */
+};
+
+/*
+ * Private variables.
+ */
+static vifi_t numvifs = 0;
+static struct mrt *cached_mrt = NULL;
+static u_long cached_origin;
+static u_long cached_originmask;
+
+static void (*encap_oldrawip)();
+
+/*
+ * one-back cache used by multiencap_decap to locate a tunnel's vif
+ * given a datagram's src ip address.
+ */
+static u_long last_encap_src;
+static struct vif *last_encap_vif;
+
+/*
+ * A simple hash function: returns MRTHASHMOD of the low-order octet of
+ * the argument's network or subnet number.
+ */
+static u_long
+nethash(n)
+ u_long n;
+{
+ struct in_addr in;
+
+ in.s_addr = n;
+ n = in_netof(in);
+ while ((n & 0xff) == 0)
+ n >>= 8;
+ return (MRTHASHMOD(n));
+}
+
+/*
+ * this is a direct-mapped cache used to speed the mapping from a
+ * datagram source address to the associated multicast route. Note
+ * that unlike mrttable, the hash is on IP address, not IP net number.
+ */
+#define MSRCHASHSIZ 1024
+#define MSRCHASH(a) ((((a) >> 20) ^ ((a) >> 10) ^ (a)) & (MSRCHASHSIZ - 1))
+struct mrt *mrtsrchash[MSRCHASHSIZ];
+
+/*
+ * Find a route for a given origin IP address.
+ */
+#define MRTFIND(o, rt) { \
+ register u_int _mrhash = o; \
+ _mrhash = MSRCHASH(_mrhash); \
+ ++mrtstat.mrts_mrt_lookups; \
+ rt = mrtsrchash[_mrhash]; \
+ if (rt == NULL || \
+ (o & rt->mrt_originmask.s_addr) != rt->mrt_origin.s_addr) \
+ if ((rt = mrtfind(o)) != NULL) \
+ mrtsrchash[_mrhash] = rt; \
+}
+
+static struct mrt *
+mrtfind(origin)
+ u_long origin;
+{
+ register struct mrt *rt;
+ register u_int hash;
+
+ mrtstat.mrts_mrt_misses++;
+
+ hash = nethash(origin);
+ for (rt = mrttable[hash]; rt; rt = rt->mrt_next) {
+ if ((origin & rt->mrt_originmask.s_addr) ==
+ rt->mrt_origin.s_addr)
+ return (rt);
+ }
+ return (NULL);
+}
+
+/*
+ * Handle DVMRP setsockopt commands to modify the multicast routing tables.
+ */
+int
+ip_mrouter_cmd(cmd, so, m)
+ register int cmd;
+ register struct socket *so;
+ register struct mbuf *m;
+{
+ register int error = 0;
+
+ if (cmd != DVMRP_INIT && so != ip_mrouter)
+ error = EACCES;
+ else switch (cmd) {
+
+ case DVMRP_INIT:
+ error = ip_mrouter_init(so);
+ break;
+
+ case DVMRP_DONE:
+ error = ip_mrouter_done();
+ break;
+
+ case DVMRP_ADD_VIF:
+ if (m == NULL || m->m_len < sizeof(struct vifctl))
+ error = EINVAL;
+ else
+ error = add_vif(mtod(m, struct vifctl *));
+ break;
+
+ case DVMRP_DEL_VIF:
+ if (m == NULL || m->m_len < sizeof(short))
+ error = EINVAL;
+ else
+ error = del_vif(mtod(m, vifi_t *));
+ break;
+
+ case DVMRP_ADD_LGRP:
+ if (m == NULL || m->m_len < sizeof(struct lgrplctl))
+ error = EINVAL;
+ else
+ error = add_lgrp(mtod(m, struct lgrplctl *));
+ break;
+
+ case DVMRP_DEL_LGRP:
+ if (m == NULL || m->m_len < sizeof(struct lgrplctl))
+ error = EINVAL;
+ else
+ error = del_lgrp(mtod(m, struct lgrplctl *));
+ break;
+
+ case DVMRP_ADD_MRT:
+ if (m == NULL || m->m_len < sizeof(struct mrtctl))
+ error = EINVAL;
+ else
+ error = add_mrt(mtod(m, struct mrtctl *));
+ break;
+
+ case DVMRP_DEL_MRT:
+ if (m == NULL || m->m_len < sizeof(struct in_addr))
+ error = EINVAL;
+ else
+ error = del_mrt(mtod(m, struct in_addr *));
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+ return (error);
+}
+
+/*
+ * Enable multicast routing
+ */
+static int
+ip_mrouter_init(so)
+ register struct socket *so;
+{
+ if (so->so_type != SOCK_RAW ||
+ so->so_proto->pr_protocol != IPPROTO_IGMP)
+ return (EOPNOTSUPP);
+
+ if (ip_mrouter != NULL)
+ return (EADDRINUSE);
+
+ ip_mrouter = so;
+
+ return (0);
+}
+
+/*
+ * Disable multicast routing
+ */
+int
+ip_mrouter_done()
+{
+ register vifi_t vifi;
+ register int i;
+ register struct ifnet *ifp;
+ register int s;
+ struct ifreq ifr;
+
+ s = splnet();
+
+ /*
+ * For each phyint in use, free its local group list and
+ * disable promiscuous reception of all IP multicasts.
+ */
+ for (vifi = 0; vifi < numvifs; vifi++) {
+ if (viftable[vifi].v_lcl_addr.s_addr != 0 &&
+ !(viftable[vifi].v_flags & VIFF_TUNNEL)) {
+ if (viftable[vifi].v_lcl_grps)
+ free(viftable[vifi].v_lcl_grps, M_MRTABLE);
+ satosin(&ifr.ifr_addr)->sin_family = AF_INET;
+ satosin(&ifr.ifr_addr)->sin_addr.s_addr = INADDR_ANY;
+ ifp = viftable[vifi].v_ifp;
+ (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr);
+ }
+ }
+ bzero((caddr_t)viftable, sizeof(viftable));
+ numvifs = 0;
+
+ /*
+ * Free any multicast route entries.
+ */
+ for (i = 0; i < MRTHASHSIZ; i++)
+ if (mrttable[i])
+ free(mrttable[i], M_MRTABLE);
+ bzero((caddr_t)mrttable, sizeof(mrttable));
+ bzero((caddr_t)mrtsrchash, sizeof(mrtsrchash));
+
+ ip_mrouter = NULL;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Add a vif to the vif table
+ */
+static int
+add_vif(vifcp)
+ register struct vifctl *vifcp;
+{
+ register struct vif *vifp = viftable + vifcp->vifc_vifi;
+ register struct ifaddr *ifa;
+ register struct ifnet *ifp;
+ struct ifreq ifr;
+ register int error, s;
+ static struct sockaddr_in sin = { sizeof(sin), AF_INET };
+
+ if (vifcp->vifc_vifi >= MAXVIFS)
+ return (EINVAL);
+ if (vifp->v_lcl_addr.s_addr != 0)
+ return (EADDRINUSE);
+
+ /* Find the interface with an address in AF_INET family */
+ sin.sin_addr = vifcp->vifc_lcl_addr;
+ ifa = ifa_ifwithaddr((struct sockaddr *)&sin);
+ if (ifa == 0)
+ return (EADDRNOTAVAIL);
+ ifp = ifa->ifa_ifp;
+
+ if (vifcp->vifc_flags & VIFF_TUNNEL) {
+ if ((vifcp->vifc_flags & VIFF_SRCRT) == 0) {
+ /*
+ * An encapsulating tunnel is wanted. If we
+ * haven't done so already, put our decap routine
+ * in front of raw_input so we have a chance to
+ * decapsulate incoming packets. Then set the
+ * arrival 'interface' to be the decapsulator.
+ */
+ if (encap_oldrawip == 0) {
+ register int pr = ip_protox[ENCAP_PROTO];
+
+ encap_oldrawip = inetsw[pr].pr_input;
+ inetsw[pr].pr_input = multiencap_decap;
+ for (s = 0; s < MAXVIFS; ++s) {
+ multicast_decap_if[s].if_name =
+ "mdecap";
+ multicast_decap_if[s].if_unit = s;
+ }
+ }
+ ifp = &multicast_decap_if[vifcp->vifc_vifi];
+ } else {
+ ifp = 0;
+ }
+ } else {
+ /* Make sure the interface supports multicast */
+ if ((ifp->if_flags & IFF_MULTICAST) == 0)
+ return EOPNOTSUPP;
+
+ /*
+ * Enable promiscuous reception of all
+ * IP multicasts from the if
+ */
+ ((struct sockaddr_in *)&ifr.ifr_addr)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
+ INADDR_ANY;
+ s = splnet();
+ error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr);
+ splx(s);
+ if (error)
+ return error;
+ }
+
+ s = splnet();
+ vifp->v_flags = vifcp->vifc_flags;
+ vifp->v_threshold = vifcp->vifc_threshold;
+ vifp->v_lcl_addr = vifcp->vifc_lcl_addr;
+ vifp->v_ifp = ifa->ifa_ifp;
+ vifp->v_rmt_addr = vifcp->vifc_rmt_addr;
+ splx(s);
+
+ /* Adjust numvifs up if the vifi is higher than numvifs */
+ if (numvifs <= vifcp->vifc_vifi)
+ numvifs = vifcp->vifc_vifi + 1;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Delete a vif from the vif table
+ */
+static int
+del_vif(vifip)
+ register vifi_t *vifip;
+{
+ register struct vif *vifp = viftable + *vifip;
+ register struct ifnet *ifp;
+ register int i, s;
+ struct ifreq ifr;
+
+ if (*vifip >= numvifs)
+ return (EINVAL);
+ if (vifp->v_lcl_addr.s_addr == 0)
+ return (EADDRNOTAVAIL);
+
+ s = splnet();
+
+ if (!(vifp->v_flags & VIFF_TUNNEL)) {
+ if (vifp->v_lcl_grps)
+ free(vifp->v_lcl_grps, M_MRTABLE);
+ satosin(&ifr.ifr_addr)->sin_family = AF_INET;
+ satosin(&ifr.ifr_addr)->sin_addr.s_addr = INADDR_ANY;
+ ifp = vifp->v_ifp;
+ (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr);
+ }
+ if (vifp == last_encap_vif) {
+ last_encap_vif = 0;
+ last_encap_src = 0;
+ }
+ bzero((caddr_t)vifp, sizeof (*vifp));
+
+ /* Adjust numvifs down */
+ for (i = numvifs - 1; i >= 0; i--)
+ if (viftable[i].v_lcl_addr.s_addr != 0)
+ break;
+ numvifs = i + 1;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Add the multicast group in the lgrpctl to the list of local multicast
+ * group memberships associated with the vif indexed by gcp->lgc_vifi.
+ */
+static int
+add_lgrp(gcp)
+ register struct lgrplctl *gcp;
+{
+ register struct vif *vifp;
+ register int s;
+
+ if (gcp->lgc_vifi >= numvifs)
+ return (EINVAL);
+
+ vifp = viftable + gcp->lgc_vifi;
+ if (vifp->v_lcl_addr.s_addr == 0 || (vifp->v_flags & VIFF_TUNNEL))
+ return (EADDRNOTAVAIL);
+
+ /* If not enough space in existing list, allocate a larger one */
+ s = splnet();
+ if (vifp->v_lcl_grps_n + 1 >= vifp->v_lcl_grps_max) {
+ register int num;
+ register struct in_addr *ip;
+
+ num = vifp->v_lcl_grps_max;
+ if (num <= 0)
+ num = 32; /* initial number */
+ else
+ num += num; /* double last number */
+ ip = (struct in_addr *)malloc(num * sizeof(*ip),
+ M_MRTABLE, M_NOWAIT);
+ if (ip == NULL) {
+ splx(s);
+ return (ENOBUFS);
+ }
+
+ bzero((caddr_t)ip, num * sizeof(*ip)); /* XXX paranoid */
+ bcopy((caddr_t)vifp->v_lcl_grps, (caddr_t)ip,
+ vifp->v_lcl_grps_n * sizeof(*ip));
+
+ vifp->v_lcl_grps_max = num;
+ if (vifp->v_lcl_grps)
+ free(vifp->v_lcl_grps, M_MRTABLE);
+ vifp->v_lcl_grps = ip;
+ }
+
+ vifp->v_lcl_grps[vifp->v_lcl_grps_n++] = gcp->lgc_gaddr;
+
+ if (gcp->lgc_gaddr.s_addr == vifp->v_cached_group)
+ vifp->v_cached_result = 1;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Delete the the local multicast group associated with the vif
+ * indexed by gcp->lgc_vifi.
+ */
+
+static int
+del_lgrp(gcp)
+ register struct lgrplctl *gcp;
+{
+ register struct vif *vifp;
+ register int i, error, s;
+
+ if (gcp->lgc_vifi >= numvifs)
+ return (EINVAL);
+ vifp = viftable + gcp->lgc_vifi;
+ if (vifp->v_lcl_addr.s_addr == 0 || (vifp->v_flags & VIFF_TUNNEL))
+ return (EADDRNOTAVAIL);
+
+ s = splnet();
+
+ if (gcp->lgc_gaddr.s_addr == vifp->v_cached_group)
+ vifp->v_cached_result = 0;
+
+ error = EADDRNOTAVAIL;
+ for (i = 0; i < vifp->v_lcl_grps_n; ++i)
+ if (same(&gcp->lgc_gaddr, &vifp->v_lcl_grps[i])) {
+ error = 0;
+ --vifp->v_lcl_grps_n;
+ for (; i < vifp->v_lcl_grps_n; ++i)
+ vifp->v_lcl_grps[i] = vifp->v_lcl_grps[i + 1];
+ error = 0;
+ break;
+ }
+
+ splx(s);
+ return (error);
+}
+
+/*
+ * Return 1 if gaddr is a member of the local group list for vifp.
+ */
+static int
+grplst_member(vifp, gaddr)
+ register struct vif *vifp;
+ struct in_addr gaddr;
+{
+ register int i, s;
+ register u_long addr;
+
+ mrtstat.mrts_grp_lookups++;
+
+ addr = gaddr.s_addr;
+ if (addr == vifp->v_cached_group)
+ return (vifp->v_cached_result);
+
+ mrtstat.mrts_grp_misses++;
+
+ for (i = 0; i < vifp->v_lcl_grps_n; ++i)
+ if (addr == vifp->v_lcl_grps[i].s_addr) {
+ s = splnet();
+ vifp->v_cached_group = addr;
+ vifp->v_cached_result = 1;
+ splx(s);
+ return (1);
+ }
+ s = splnet();
+ vifp->v_cached_group = addr;
+ vifp->v_cached_result = 0;
+ splx(s);
+ return (0);
+}
+
+/*
+ * Add an mrt entry
+ */
+static int
+add_mrt(mrtcp)
+ register struct mrtctl *mrtcp;
+{
+ struct mrt *rt;
+ u_long hash;
+ int s;
+
+ if (rt = mrtfind(mrtcp->mrtc_origin.s_addr)) {
+ /* Just update the route */
+ s = splnet();
+ rt->mrt_parent = mrtcp->mrtc_parent;
+ VIFM_COPY(mrtcp->mrtc_children, rt->mrt_children);
+ VIFM_COPY(mrtcp->mrtc_leaves, rt->mrt_leaves);
+ splx(s);
+ return (0);
+ }
+
+ s = splnet();
+
+ rt = (struct mrt *)malloc(sizeof(*rt), M_MRTABLE, M_NOWAIT);
+ if (rt == NULL) {
+ splx(s);
+ return (ENOBUFS);
+ }
+
+ /*
+ * insert new entry at head of hash chain
+ */
+ rt->mrt_origin = mrtcp->mrtc_origin;
+ rt->mrt_originmask = mrtcp->mrtc_originmask;
+ rt->mrt_parent = mrtcp->mrtc_parent;
+ VIFM_COPY(mrtcp->mrtc_children, rt->mrt_children);
+ VIFM_COPY(mrtcp->mrtc_leaves, rt->mrt_leaves);
+ /* link into table */
+ hash = nethash(mrtcp->mrtc_origin.s_addr);
+ rt->mrt_next = mrttable[hash];
+ mrttable[hash] = rt;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Delete an mrt entry
+ */
+static int
+del_mrt(origin)
+ register struct in_addr *origin;
+{
+ register struct mrt *rt, *prev_rt;
+ register u_long hash = nethash(origin->s_addr);
+ register struct mrt **cmrt, **cmrtend;
+ register int s;
+
+ for (prev_rt = rt = mrttable[hash]; rt; prev_rt = rt, rt = rt->mrt_next)
+ if (origin->s_addr == rt->mrt_origin.s_addr)
+ break;
+ if (!rt)
+ return (ESRCH);
+
+ s = splnet();
+
+ cmrt = mrtsrchash;
+ cmrtend = cmrt + MSRCHASHSIZ;
+ for ( ; cmrt < cmrtend; ++cmrt)
+ if (*cmrt == rt)
+ *cmrt = 0;
+
+ if (prev_rt == rt)
+ mrttable[hash] = rt->mrt_next;
+ else
+ prev_rt->mrt_next = rt->mrt_next;
+ free(rt, M_MRTABLE);
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * IP multicast forwarding function. This function assumes that the packet
+ * pointed to by "ip" has arrived on (or is about to be sent to) the interface
+ * pointed to by "ifp", and the packet is to be relayed to other networks
+ * that have members of the packet's destination IP multicast group.
+ *
+ * The packet is returned unscathed to the caller, unless it is tunneled
+ * or erroneous, in which case a non-zero return value tells the caller to
+ * discard it.
+ */
+
+#define IP_HDR_LEN 20 /* # bytes of fixed IP header (excluding options) */
+#define TUNNEL_LEN 12 /* # bytes of IP option for tunnel encapsulation */
+
+int
+ip_mforward(ip, ifp, m)
+ register struct ip *ip;
+ register struct ifnet *ifp;
+ register struct mbuf *m;
+{
+ register struct mrt *rt;
+ register struct vif *vifp;
+ register int vifi;
+ register u_char *ipoptions;
+ u_long tunnel_src;
+
+ if (ip->ip_hl < (IP_HDR_LEN + TUNNEL_LEN) >> 2 ||
+ (ipoptions = (u_char *)(ip + 1))[1] != IPOPT_LSRR ) {
+ /*
+ * Packet arrived via a physical interface.
+ */
+ tunnel_src = 0;
+ } else {
+ /*
+ * Packet arrived through a tunnel.
+ *
+ * A tunneled packet has a single NOP option and a
+ * two-element loose-source-and-record-route (LSRR)
+ * option immediately following the fixed-size part of
+ * the IP header. At this point in processing, the IP
+ * header should contain the following IP addresses:
+ *
+ * original source - in the source address field
+ * destination group - in the destination address field
+ * remote tunnel end-point - in the first element of LSRR
+ * one of this host's addrs - in the second element of LSRR
+ *
+ * NOTE: RFC-1075 would have the original source and
+ * remote tunnel end-point addresses swapped. However,
+ * that could cause delivery of ICMP error messages to
+ * innocent applications on intermediate routing
+ * hosts! Therefore, we hereby change the spec.
+ */
+
+ /*
+ * Verify that the tunnel options are well-formed.
+ */
+ if (ipoptions[0] != IPOPT_NOP ||
+ ipoptions[2] != 11 || /* LSRR option length */
+ ipoptions[3] != 12 || /* LSRR address pointer */
+ (tunnel_src = *(u_long *)(&ipoptions[4])) == 0) {
+ mrtstat.mrts_bad_tunnel++;
+ return (1);
+ }
+
+ /*
+ * Delete the tunnel options from the packet.
+ */
+ ovbcopy((caddr_t)(ipoptions + TUNNEL_LEN), (caddr_t)ipoptions,
+ (unsigned)(m->m_len - (IP_HDR_LEN + TUNNEL_LEN)));
+ m->m_len -= TUNNEL_LEN;
+ ip->ip_len -= TUNNEL_LEN;
+ ip->ip_hl -= TUNNEL_LEN >> 2;
+ }
+
+ /*
+ * Don't forward a packet with time-to-live of zero or one,
+ * or a packet destined to a local-only group.
+ */
+ if (ip->ip_ttl <= 1 ||
+ ntohl(ip->ip_dst.s_addr) <= INADDR_MAX_LOCAL_GROUP)
+ return ((int)tunnel_src);
+
+ /*
+ * Don't forward if we don't have a route for the packet's origin.
+ */
+ MRTFIND(ip->ip_src.s_addr, rt)
+ if (rt == NULL) {
+ mrtstat.mrts_no_route++;
+ return ((int)tunnel_src);
+ }
+
+ /*
+ * Don't forward if it didn't arrive from the
+ * parent vif for its origin.
+ *
+ * Notes: v_ifp is zero for src route tunnels, multicast_decap_if
+ * for encapsulated tunnels and a real ifnet for non-tunnels so
+ * the first part of the if catches wrong physical interface or
+ * tunnel type; v_rmt_addr is zero for non-tunneled packets so
+ * the 2nd part catches both packets that arrive via a tunnel
+ * that shouldn't and packets that arrive via the wrong tunnel.
+ */
+ vifi = rt->mrt_parent;
+ if (viftable[vifi].v_ifp != ifp ||
+ (ifp == 0 && viftable[vifi].v_rmt_addr.s_addr != tunnel_src)) {
+ /* came in the wrong interface */
+ ++mrtstat.mrts_wrong_if;
+ return (int)tunnel_src;
+ }
+
+ /*
+ * For each vif, decide if a copy of the packet should be forwarded.
+ * Forward if:
+ * - the ttl exceeds the vif's threshold AND
+ * - the vif is a child in the origin's route AND
+ * - ( the vif is not a leaf in the origin's route OR
+ * the destination group has members on the vif )
+ *
+ * (This might be speeded up with some sort of cache -- someday.)
+ */
+ for (vifp = viftable, vifi = 0; vifi < numvifs; vifp++, vifi++) {
+ if (ip->ip_ttl > vifp->v_threshold &&
+ VIFM_ISSET(vifi, rt->mrt_children) &&
+ (!VIFM_ISSET(vifi, rt->mrt_leaves) ||
+ grplst_member(vifp, ip->ip_dst))) {
+ if (vifp->v_flags & VIFF_SRCRT)
+ srcrt_send(ip, vifp, m);
+ else if (vifp->v_flags & VIFF_TUNNEL)
+ encap_send(ip, vifp, m);
+ else
+ phyint_send(ip, vifp, m);
+ }
+ }
+ return ((int)tunnel_src);
+}
+
+static void
+phyint_send(ip, vifp, m)
+ register struct ip *ip;
+ register struct vif *vifp;
+ register struct mbuf *m;
+{
+ register struct mbuf *mb_copy;
+ register struct ip_moptions *imo;
+ register int error;
+ struct ip_moptions simo;
+
+ mb_copy = m_copy(m, 0, M_COPYALL);
+ if (mb_copy == NULL)
+ return;
+
+ imo = &simo;
+ imo->imo_multicast_ifp = vifp->v_ifp;
+ imo->imo_multicast_ttl = ip->ip_ttl - 1;
+ imo->imo_multicast_loop = 1;
+
+ error = ip_output(mb_copy, NULL, NULL,
+ IP_FORWARDING|IP_MULTICASTOPTS, imo);
+}
+
+static void
+srcrt_send(ip, vifp, m)
+ register struct ip *ip;
+ register struct vif *vifp;
+ register struct mbuf *m;
+{
+ register struct mbuf *mb_copy, *mb_opts;
+ register struct ip *ip_copy;
+ register int error;
+ register u_char *cp;
+
+ /*
+ * Make sure that adding the tunnel options won't exceed the
+ * maximum allowed number of option bytes.
+ */
+ if (ip->ip_hl > (60 - TUNNEL_LEN) >> 2) {
+ mrtstat.mrts_cant_tunnel++;
+ return;
+ }
+
+ mb_copy = m_copy(m, 0, M_COPYALL);
+ if (mb_copy == NULL)
+ return;
+ ip_copy = mtod(mb_copy, struct ip *);
+ ip_copy->ip_ttl--;
+ ip_copy->ip_dst = vifp->v_rmt_addr; /* remote tunnel end-point */
+ /*
+ * Adjust the ip header length to account for the tunnel options.
+ */
+ ip_copy->ip_hl += TUNNEL_LEN >> 2;
+ ip_copy->ip_len += TUNNEL_LEN;
+ MGETHDR(mb_opts, M_DONTWAIT, MT_HEADER);
+ if (mb_opts == NULL) {
+ m_freem(mb_copy);
+ return;
+ }
+ /*
+ * 'Delete' the base ip header from the mb_copy chain
+ */
+ mb_copy->m_len -= IP_HDR_LEN;
+ mb_copy->m_data += IP_HDR_LEN;
+ /*
+ * Make mb_opts be the new head of the packet chain.
+ * Any options of the packet were left in the old packet chain head
+ */
+ mb_opts->m_next = mb_copy;
+ mb_opts->m_len = IP_HDR_LEN + TUNNEL_LEN;
+ mb_opts->m_data += MSIZE - mb_opts->m_len;
+ /*
+ * Copy the base ip header from the mb_copy chain to the new head mbuf
+ */
+ bcopy((caddr_t)ip_copy, mtod(mb_opts, caddr_t), IP_HDR_LEN);
+ /*
+ * Add the NOP and LSRR after the base ip header
+ */
+ cp = mtod(mb_opts, u_char *) + IP_HDR_LEN;
+ *cp++ = IPOPT_NOP;
+ *cp++ = IPOPT_LSRR;
+ *cp++ = 11; /* LSRR option length */
+ *cp++ = 8; /* LSSR pointer to second element */
+ *(u_long*)cp = vifp->v_lcl_addr.s_addr; /* local tunnel end-point */
+ cp += 4;
+ *(u_long*)cp = ip->ip_dst.s_addr; /* destination group */
+
+ error = ip_output(mb_opts, NULL, NULL, IP_FORWARDING, NULL);
+}
+
+static void
+encap_send(ip, vifp, m)
+ register struct ip *ip;
+ register struct vif *vifp;
+ register struct mbuf *m;
+{
+ register struct mbuf *mb_copy;
+ register struct ip *ip_copy;
+ register int i, len = ip->ip_len;
+
+ /*
+ * copy the old packet & pullup it's IP header into the
+ * new mbuf so we can modify it. Try to fill the new
+ * mbuf since if we don't the ethernet driver will.
+ */
+ MGETHDR(mb_copy, M_DONTWAIT, MT_HEADER);
+ if (mb_copy == NULL)
+ return;
+ mb_copy->m_data += 16;
+ mb_copy->m_len = sizeof(multicast_encap_iphdr);
+ if ((mb_copy->m_next = m_copy(m, 0, M_COPYALL)) == NULL) {
+ m_freem(mb_copy);
+ return;
+ }
+ i = MHLEN - 16;
+ if (i > len)
+ i = len;
+ mb_copy = m_pullup(mb_copy, i);
+ if (mb_copy == NULL)
+ return;
+
+ /*
+ * fill in the encapsulating IP header.
+ */
+ ip_copy = mtod(mb_copy, struct ip *);
+ *ip_copy = multicast_encap_iphdr;
+ ip_copy->ip_id = htons(ip_id++);
+ ip_copy->ip_len += len;
+ ip_copy->ip_src = vifp->v_lcl_addr;
+ ip_copy->ip_dst = vifp->v_rmt_addr;
+
+ /*
+ * turn the encapsulated IP header back into a valid one.
+ */
+ ip = (struct ip *)((caddr_t)ip_copy + sizeof(multicast_encap_iphdr));
+ --ip->ip_ttl;
+ HTONS(ip->ip_len);
+ HTONS(ip->ip_off);
+ ip->ip_sum = 0;
+#if defined(LBL) && !defined(ultrix) && !defined(i386)
+ ip->ip_sum = ~oc_cksum((caddr_t)ip, ip->ip_hl << 2, 0);
+#else
+ mb_copy->m_data += sizeof(multicast_encap_iphdr);
+ ip->ip_sum = in_cksum(mb_copy, ip->ip_hl << 2);
+ mb_copy->m_data -= sizeof(multicast_encap_iphdr);
+#endif
+ ip_output(mb_copy, (struct mbuf *)0, (struct route *)0,
+ IP_FORWARDING, (struct mbuf *)0);
+}
+
+/*
+ * De-encapsulate a packet and feed it back through ip input (this
+ * routine is called whenever IP gets a packet with proto type
+ * ENCAP_PROTO and a local destination address).
+ */
+static void
+multiencap_decap(m, hlen)
+ register struct mbuf *m;
+ int hlen;
+{
+ struct ifnet *ifp;
+ register struct ip *ip = mtod(m, struct ip *);
+ register int s;
+ register struct ifqueue *ifq;
+ register struct vif *vifp;
+
+ if (ip->ip_p != ENCAP_PROTO) {
+ (*encap_oldrawip)(m, hlen);
+ return;
+ }
+ /*
+ * dump the packet if it's not to a multicast destination or if
+ * we don't have an encapsulating tunnel with the source.
+ * Note: This code assumes that the remote site IP address
+ * uniquely identifies the tunnel (i.e., that this site has
+ * at most one tunnel with the remote site).
+ */
+ if (! IN_MULTICAST(ntohl(((struct ip *)((char *)ip + hlen))->ip_dst.s_addr))) {
+ ++mrtstat.mrts_bad_tunnel;
+ m_freem(m);
+ return;
+ }
+ if (ip->ip_src.s_addr != last_encap_src) {
+ register struct vif *vife;
+
+ vifp = viftable;
+ vife = vifp + numvifs;
+ last_encap_src = ip->ip_src.s_addr;
+ last_encap_vif = 0;
+ for ( ; vifp < vife; ++vifp)
+ if (vifp->v_rmt_addr.s_addr == ip->ip_src.s_addr) {
+ if ((vifp->v_flags & (VIFF_TUNNEL|VIFF_SRCRT))
+ == VIFF_TUNNEL)
+ last_encap_vif = vifp;
+ break;
+ }
+ }
+ if ((vifp = last_encap_vif) == 0) {
+ mrtstat.mrts_cant_tunnel++; /*XXX*/
+ m_freem(m);
+ return;
+ }
+ ifp = vifp->v_ifp;
+ m->m_data += hlen;
+ m->m_len -= hlen;
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len -= hlen;
+ ifq = &ipintrq;
+ s = splimp();
+ if (IF_QFULL(ifq)) {
+ IF_DROP(ifq);
+ m_freem(m);
+ } else {
+ IF_ENQUEUE(ifq, m);
+ /*
+ * normally we would need a "schednetisr(NETISR_IP)"
+ * here but we were called by ip_input and it is going
+ * to loop back & try to dequeue the packet we just
+ * queued as soon as we return so we avoid the
+ * unnecessary software interrrupt.
+ */
+ }
+ splx(s);
+}
+#endif
diff --git a/sys/netinet/ip_mroute.h b/sys/netinet/ip_mroute.h
new file mode 100644
index 000000000000..4b3c753998df
--- /dev/null
+++ b/sys/netinet/ip_mroute.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 1989 Stephen Deering.
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * 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.
+ *
+ * @(#)ip_mroute.h 7.2 (Berkeley) 7/8/92
+ */
+
+/*
+ * Definitions for the kernel part of DVMRP,
+ * a Distance-Vector Multicast Routing Protocol.
+ * (See RFC-1075.)
+ *
+ * Written by David Waitzman, BBN Labs, August 1988.
+ * Modified by Steve Deering, Stanford, February 1989.
+ *
+ * MROUTING 1.0
+ */
+
+
+/*
+ * DVMRP-specific setsockopt commands.
+ */
+#define DVMRP_INIT 100
+#define DVMRP_DONE 101
+#define DVMRP_ADD_VIF 102
+#define DVMRP_DEL_VIF 103
+#define DVMRP_ADD_LGRP 104
+#define DVMRP_DEL_LGRP 105
+#define DVMRP_ADD_MRT 106
+#define DVMRP_DEL_MRT 107
+
+
+/*
+ * Types and macros for handling bitmaps with one bit per virtual interface.
+ */
+#define MAXVIFS 32
+typedef u_long vifbitmap_t;
+typedef u_short vifi_t; /* type of a vif index */
+
+#define VIFM_SET(n, m) ((m) |= (1 << (n)))
+#define VIFM_CLR(n, m) ((m) &= ~(1 << (n)))
+#define VIFM_ISSET(n, m) ((m) & (1 << (n)))
+#define VIFM_CLRALL(m) ((m) = 0x00000000)
+#define VIFM_COPY(mfrom, mto) ((mto) = (mfrom))
+#define VIFM_SAME(m1, m2) ((m1) == (m2))
+
+
+/*
+ * Agument structure for DVMRP_ADD_VIF.
+ * (DVMRP_DEL_VIF takes a single vifi_t argument.)
+ */
+struct vifctl {
+ vifi_t vifc_vifi; /* the index of the vif to be added */
+ u_char vifc_flags; /* VIFF_ flags defined below */
+ u_char vifc_threshold; /* min ttl required to forward on vif */
+ struct in_addr vifc_lcl_addr; /* local interface address */
+ struct in_addr vifc_rmt_addr; /* remote address (tunnels only) */
+};
+
+#define VIFF_TUNNEL 0x1 /* vif represents a tunnel end-point */
+#define VIFF_SRCRT 0x2 /* tunnel uses IP src routing */
+
+
+/*
+ * Argument structure for DVMRP_ADD_LGRP and DVMRP_DEL_LGRP.
+ */
+struct lgrplctl {
+ vifi_t lgc_vifi;
+ struct in_addr lgc_gaddr;
+};
+
+
+/*
+ * Argument structure for DVMRP_ADD_MRT.
+ * (DVMRP_DEL_MRT takes a single struct in_addr argument, containing origin.)
+ */
+struct mrtctl {
+ struct in_addr mrtc_origin; /* subnet origin of multicasts */
+ struct in_addr mrtc_originmask; /* subnet mask for origin */
+ vifi_t mrtc_parent; /* incoming vif */
+ vifbitmap_t mrtc_children; /* outgoing children vifs */
+ vifbitmap_t mrtc_leaves; /* subset of outgoing children vifs */
+};
+
+
+#ifdef KERNEL
+
+/*
+ * The kernel's virtual-interface structure.
+ */
+struct vif {
+ u_char v_flags; /* VIFF_ flags defined above */
+ u_char v_threshold; /* min ttl required to forward on vif */
+ struct in_addr v_lcl_addr; /* local interface address */
+ struct in_addr v_rmt_addr; /* remote address (tunnels only) */
+ struct ifnet *v_ifp; /* pointer to interface */
+ struct in_addr *v_lcl_grps; /* list of local grps (phyints only) */
+ int v_lcl_grps_max; /* malloc'ed number of v_lcl_grps */
+ int v_lcl_grps_n; /* used number of v_lcl_grps */
+ u_long v_cached_group; /* last grp looked-up (phyints only) */
+ int v_cached_result; /* last look-up result (phyints only) */
+};
+
+/*
+ * The kernel's multicast route structure.
+ */
+struct mrt {
+ struct in_addr mrt_origin; /* subnet origin of multicasts */
+ struct in_addr mrt_originmask; /* subnet mask for origin */
+ vifi_t mrt_parent; /* incoming vif */
+ vifbitmap_t mrt_children; /* outgoing children vifs */
+ vifbitmap_t mrt_leaves; /* subset of outgoing children vifs */
+ struct mrt *mrt_next; /* forward link */
+};
+
+
+#define MRTHASHSIZ 256
+#if (MRTHASHSIZ & (MRTHASHSIZ - 1)) == 0 /* from sys:route.h */
+#define MRTHASHMOD(h) ((h) & (MRTHASHSIZ - 1))
+#else
+#define MRTHASHMOD(h) ((h) % MRTHASHSIZ)
+#endif
+
+/*
+ * The kernel's multicast routing statistics.
+ */
+struct mrtstat {
+ u_long mrts_mrt_lookups; /* # multicast route lookups */
+ u_long mrts_mrt_misses; /* # multicast route cache misses */
+ u_long mrts_grp_lookups; /* # group address lookups */
+ u_long mrts_grp_misses; /* # group address cache misses */
+ u_long mrts_no_route; /* no route for packet's origin */
+ u_long mrts_bad_tunnel; /* malformed tunnel options */
+ u_long mrts_cant_tunnel; /* no room for tunnel options */
+ u_long mrts_wrong_if; /* arrived on wrong interface */
+};
+
+int ip_mrouter_cmd __P((int, struct socket *, struct mbuf *));
+int ip_mrouter_done __P(());
+
+#endif /* KERNEL */
+
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index e9b459e870b8..f1b9975133a8 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * 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
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)ip_output.c 7.23 (Berkeley) 11/12/90
- * $Id: ip_output.c,v 1.5 1993/12/19 00:52:45 wollman Exp $
+ * $Id: ip_output.c,v 1.7 1994/05/26 22:42:15 jkh Exp $
*/
#include "param.h"
@@ -58,6 +58,7 @@
#endif
struct mbuf *ip_insertoptions();
+void ip_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in *));
/*
* IP output. The packet in mbuf chain m contains a skeletal IP
@@ -66,11 +67,18 @@ struct mbuf *ip_insertoptions();
* The mbuf opt, if present, will not be freed.
*/
int
-ip_output(m0, opt, ro, flags)
+ip_output(m0, opt, ro, flags
+#ifdef MULTICAST
+ , imo
+#endif
+ )
struct mbuf *m0;
struct mbuf *opt;
struct route *ro;
int flags;
+#ifdef MULTICAST
+ struct ip_moptions *imo;
+#endif
{
register struct ip *ip, *mhip;
register struct ifnet *ifp;
@@ -152,6 +160,97 @@ ip_output(m0, opt, ro, flags)
if (ro->ro_rt->rt_flags & RTF_GATEWAY)
dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway;
}
+#ifdef MULTICAST
+ if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
+ struct in_multi *inm;
+ extern struct ifnet loif;
+ extern struct socket *ip_mrouter;
+
+ m->m_flags |= M_MCAST;
+ /*
+ * IP destination address is multicast. Make sure "dst"
+ * still points to the address in "ro". (It may have been
+ * changed to point to a gateway address, above.)
+ */
+ dst = (struct sockaddr_in *)&ro->ro_dst;
+ /*
+ * See if the caller provided any multicast options
+ */
+ if ((flags & IP_MULTICASTOPTS) && imo != NULL) {
+ ip->ip_ttl = imo->imo_multicast_ttl;
+ if (imo->imo_multicast_ifp != NULL)
+ ifp = imo->imo_multicast_ifp;
+ } else {
+ imo = NULL;
+ ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL;
+ }
+ /*
+ * Confirm that the outgoing interface supports multicast.
+ */
+ if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+ error = ENETUNREACH;
+ goto bad;
+ }
+ /*
+ * If source address not specified yet, use address
+ * of outgoing interface.
+ */
+ if (ip->ip_src.s_addr == INADDR_ANY) {
+ register struct in_ifaddr *ia;
+
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp) {
+ ip->ip_src = IA_SIN(ia)->sin_addr;
+ break;
+ }
+ }
+
+ IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm);
+ if (inm != NULL &&
+ (imo == NULL || imo->imo_multicast_loop)) {
+ /*
+ * If we belong to the destination multicast group
+ * on the outgoing interface, and the caller did not
+ * forbid loopback, loop back a copy.
+ */
+ ip_mloopback(ifp, m, dst);
+ }
+#ifdef MROUTING
+ else if (ip_mrouter && (flags & IP_FORWARDING) == 0) {
+ /*
+ * If we are acting as a multicast router, perform
+ * multicast forwarding as if the packet had just
+ * arrived on the interface to which we are about
+ * to send. The multicast forwarding function
+ * recursively calls this function, using the
+ * IP_FORWARDING flag to prevent infinite recursion.
+ *
+ * Multicasts that are looped back by ip_mloopback(),
+ * above, will be forwarded by the ip_input() routine,
+ * if necessary.
+ */
+ if (ip_mforward(ip, ifp, m) != 0) {
+ m_freem(m);
+ goto done;
+ }
+ }
+#endif
+ /*
+ * Multicasts with a time-to-live of zero may be looped-
+ * back, above, but must not be transmitted on a network.
+ * Also, multicasts addressed to the loopback interface
+ * are not sent -- the above call to ip_mloopback() will
+ * loop back a copy if this host actually belongs to the
+ * destination group on the loopback interface.
+ */
+ if (ip->ip_ttl == 0 || ifp == &loif) {
+ m_freem(m);
+ goto done;
+ }
+
+ goto sendit;
+ }
+#endif
#ifndef notdef
/*
* If source address not specified yet, use address
@@ -192,7 +291,9 @@ ip_output(m0, opt, ro, flags)
}
m->m_flags |= M_BCAST;
}
-
+#ifdef MULTICAST
+sendit:
+#endif
/*
* If small enough for interface, can just send directly.
*/
@@ -413,7 +514,24 @@ ip_ctloutput(op, so, level, optname, mp)
case IP_RECVRETOPTS:
case IP_RECVDSTADDR:
if (m->m_len != sizeof(int))
+#if defined(MULTICAST) && defined(OLD_VAT_COMPAT)
+ {
+ optname += IP_RETOPTS-IP_OPTIONS;
+ switch(optname) {
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ if (m->m_len == sizeof(char))
+ goto multicast_setopt;
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ if (m->m_len == sizeof(struct ip_mreq))
+ goto multicast_setopt;
+ }
+ error = EINVAL;
+ }
+#else
error = EINVAL;
+#endif
else {
optval = *mtod(m, int *);
switch (optname) {
@@ -446,7 +564,25 @@ ip_ctloutput(op, so, level, optname, mp)
}
break;
#undef OPTSET
-
+#ifdef MULTICAST
+#ifdef OLD_VAT_COMPAT
+ case IP_HDRINCL:
+ if (m->m_len != sizeof(struct in_addr)) {
+ error = EINVAL;
+ break;
+ }
+ optname = IP_MULTICAST_IF;
+ /* FALLTHRU */
+multicast_setopt:
+#endif
+ case IP_MULTICAST_IF:
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ error = ip_setmoptions(optname, &inp->inp_moptions, m);
+ break;
+#endif
default:
error = EINVAL;
break;
@@ -501,7 +637,15 @@ ip_ctloutput(op, so, level, optname, mp)
}
*mtod(m, int *) = optval;
break;
-
+#ifdef MULTICAST
+ case IP_MULTICAST_IF:
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ error = ip_getmoptions(optname, inp->inp_moptions, mp);
+ break;
+#endif
default:
error = EINVAL;
break;
@@ -617,3 +761,339 @@ bad:
(void)m_free(m);
return (EINVAL);
}
+
+#ifdef MULTICAST
+/*
+ * Set the IP multicast options in response to user setsockopt().
+ */
+int
+ip_setmoptions(optname, imop, m)
+ int optname;
+ struct ip_moptions **imop;
+ struct mbuf *m;
+{
+ register int error = 0;
+ u_char loop;
+ register int i;
+ struct in_addr addr;
+ register struct ip_mreq *mreq;
+ register struct ifnet *ifp;
+ register struct ip_moptions *imo = *imop;
+ struct route ro;
+ register struct sockaddr_in *dst;
+
+ if (imo == NULL) {
+ /*
+ * No multicast option buffer attached to the pcb;
+ * allocate one and initialize to default values.
+ */
+ imo = (struct ip_moptions*)malloc(sizeof(*imo), M_IPMOPTS,
+ M_WAITOK);
+
+ if (imo == NULL)
+ return (ENOBUFS);
+ *imop = imo;
+ imo->imo_multicast_ifp = NULL;
+ imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
+ imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
+ imo->imo_num_memberships = 0;
+ }
+
+ switch (optname) {
+
+ case IP_MULTICAST_IF:
+ /*
+ * Select the interface for outgoing multicast packets.
+ */
+ if (m == NULL || m->m_len != sizeof(struct in_addr)) {
+ error = EINVAL;
+ break;
+ }
+ addr = *(mtod(m, struct in_addr *));
+ /*
+ * INADDR_ANY is used to remove a previous selection.
+ * When no interface is selected, a default one is
+ * chosen every time a multicast packet is sent.
+ */
+ if (addr.s_addr == INADDR_ANY) {
+ imo->imo_multicast_ifp = NULL;
+ break;
+ }
+ /*
+ * The selected interface is identified by its local
+ * IP address. Find the interface and confirm that
+ * it supports multicasting.
+ */
+ INADDR_TO_IFP(addr, ifp);
+ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ imo->imo_multicast_ifp = ifp;
+ break;
+
+ case IP_MULTICAST_TTL:
+ /*
+ * Set the IP time-to-live for outgoing multicast packets.
+ */
+ if (m == NULL || m->m_len != 1) {
+ error = EINVAL;
+ break;
+ }
+ imo->imo_multicast_ttl = *(mtod(m, u_char *));
+ break;
+
+ case IP_MULTICAST_LOOP:
+ /*
+ * Set the loopback flag for outgoing multicast packets.
+ * Must be zero or one.
+ */
+ if (m == NULL || m->m_len != 1 ||
+ (loop = *(mtod(m, u_char *))) > 1) {
+ error = EINVAL;
+ break;
+ }
+ imo->imo_multicast_loop = loop;
+ break;
+
+ case IP_ADD_MEMBERSHIP:
+ /*
+ * Add a multicast group membership.
+ * Group must be a valid IP multicast address.
+ */
+ if (m == NULL || m->m_len != sizeof(struct ip_mreq)) {
+ error = EINVAL;
+ break;
+ }
+ mreq = mtod(m, struct ip_mreq *);
+ if (!IN_MULTICAST(ntohl(mreq->imr_multiaddr.s_addr))) {
+ error = EINVAL;
+ break;
+ }
+ /*
+ * If no interface address was provided, use the interface of
+ * the route to the given multicast address.
+ */
+ if (mreq->imr_interface.s_addr == INADDR_ANY) {
+ ro.ro_rt = NULL;
+ dst = (struct sockaddr_in *)&ro.ro_dst;
+ dst->sin_len = sizeof(*dst);
+ dst->sin_family = AF_INET;
+ dst->sin_addr = mreq->imr_multiaddr;
+ rtalloc(&ro);
+ if (ro.ro_rt == NULL) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ ifp = ro.ro_rt->rt_ifp;
+ rtfree(ro.ro_rt);
+ }
+ else {
+ INADDR_TO_IFP(mreq->imr_interface, ifp);
+ }
+ /*
+ * See if we found an interface, and confirm that it
+ * supports multicast.
+ */
+ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ /*
+ * See if the membership already exists or if all the
+ * membership slots are full.
+ */
+ for (i = 0; i < imo->imo_num_memberships; ++i) {
+ if (imo->imo_membership[i]->inm_ifp == ifp &&
+ imo->imo_membership[i]->inm_addr.s_addr
+ == mreq->imr_multiaddr.s_addr)
+ break;
+ }
+ if (i < imo->imo_num_memberships) {
+ error = EADDRINUSE;
+ break;
+ }
+ if (i == IP_MAX_MEMBERSHIPS) {
+ error = ETOOMANYREFS;
+ break;
+ }
+ /*
+ * Everything looks good; add a new record to the multicast
+ * address list for the given interface.
+ */
+ if ((imo->imo_membership[i] =
+ in_addmulti(&mreq->imr_multiaddr, ifp)) == NULL) {
+ error = ENOBUFS;
+ break;
+ }
+ ++imo->imo_num_memberships;
+ break;
+
+ case IP_DROP_MEMBERSHIP:
+ /*
+ * Drop a multicast group membership.
+ * Group must be a valid IP multicast address.
+ */
+ if (m == NULL || m->m_len != sizeof(struct ip_mreq)) {
+ error = EINVAL;
+ break;
+ }
+ mreq = mtod(m, struct ip_mreq *);
+ if (!IN_MULTICAST(ntohl(mreq->imr_multiaddr.s_addr))) {
+ error = EINVAL;
+ break;
+ }
+ /*
+ * If an interface address was specified, get a pointer
+ * to its ifnet structure.
+ */
+ if (mreq->imr_interface.s_addr == INADDR_ANY)
+ ifp = NULL;
+ else {
+ INADDR_TO_IFP(mreq->imr_interface, ifp);
+ if (ifp == NULL) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ }
+ /*
+ * Find the membership in the membership array.
+ */
+ for (i = 0; i < imo->imo_num_memberships; ++i) {
+ if ((ifp == NULL ||
+ imo->imo_membership[i]->inm_ifp == ifp) &&
+ imo->imo_membership[i]->inm_addr.s_addr ==
+ mreq->imr_multiaddr.s_addr)
+ break;
+ }
+ if (i == imo->imo_num_memberships) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ /*
+ * Give up the multicast address record to which the
+ * membership points.
+ */
+ in_delmulti(imo->imo_membership[i]);
+ /*
+ * Remove the gap in the membership array.
+ */
+ for (++i; i < imo->imo_num_memberships; ++i)
+ imo->imo_membership[i-1] = imo->imo_membership[i];
+ --imo->imo_num_memberships;
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ /*
+ * If all options have default values, no need to keep the mbuf.
+ */
+ if (imo->imo_multicast_ifp == NULL &&
+ imo->imo_multicast_ttl == IP_DEFAULT_MULTICAST_TTL &&
+ imo->imo_multicast_loop == IP_DEFAULT_MULTICAST_LOOP &&
+ imo->imo_num_memberships == 0) {
+ free(*imop, M_IPMOPTS);
+ *imop = NULL;
+ }
+
+ return (error);
+}
+
+/*
+ * Return the IP multicast options in response to user getsockopt().
+ */
+int
+ip_getmoptions(optname, imo, mp)
+ int optname;
+ register struct ip_moptions *imo;
+ register struct mbuf **mp;
+{
+ u_char *ttl;
+ u_char *loop;
+ struct in_addr *addr;
+ struct in_ifaddr *ia;
+
+ *mp = m_get(M_WAIT, MT_SOOPTS);
+
+ switch (optname) {
+
+ case IP_MULTICAST_IF:
+ addr = mtod(*mp, struct in_addr *);
+ (*mp)->m_len = sizeof(struct in_addr);
+ if (imo == NULL || imo->imo_multicast_ifp == NULL)
+ addr->s_addr = INADDR_ANY;
+ else {
+ IFP_TO_IA(imo->imo_multicast_ifp, ia);
+ addr->s_addr = (ia == NULL) ? INADDR_ANY
+ : IA_SIN(ia)->sin_addr.s_addr;
+ }
+ return (0);
+
+ case IP_MULTICAST_TTL:
+ ttl = mtod(*mp, u_char *);
+ (*mp)->m_len = 1;
+ *ttl = (imo == NULL) ? IP_DEFAULT_MULTICAST_TTL
+ : imo->imo_multicast_ttl;
+ return (0);
+
+ case IP_MULTICAST_LOOP:
+ loop = mtod(*mp, u_char *);
+ (*mp)->m_len = 1;
+ *loop = (imo == NULL) ? IP_DEFAULT_MULTICAST_LOOP
+ : imo->imo_multicast_loop;
+ return (0);
+
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
+/*
+ * Discard the IP multicast options.
+ */
+void
+ip_freemoptions(imo)
+ register struct ip_moptions *imo;
+{
+ register int i;
+
+ if (imo != NULL) {
+ for (i = 0; i < imo->imo_num_memberships; ++i)
+ in_delmulti(imo->imo_membership[i]);
+ free(imo, M_IPMOPTS);
+ }
+}
+
+/*
+ * Routine called from ip_output() to loop back a copy of an IP multicast
+ * packet to the input queue of a specified interface. Note that this
+ * calls the output routine of the loopback "driver", but with an interface
+ * pointer that might NOT be &loif -- easier than replicating that code here.
+ */
+void
+ip_mloopback(ifp, m, dst)
+ struct ifnet *ifp;
+ register struct mbuf *m;
+ register struct sockaddr_in *dst;
+{
+ register struct ip *ip;
+ struct mbuf *copym;
+
+ copym = m_copy(m, 0, M_COPYALL);
+ if (copym != NULL) {
+ /*
+ * We don't bother to fragment if the IP length is greater
+ * than the interface's MTU. Can this possibly matter?
+ */
+ ip = mtod(copym, struct ip *);
+ ip->ip_len = htons((u_short)ip->ip_len);
+ ip->ip_off = htons((u_short)ip->ip_off);
+ ip->ip_sum = 0;
+ ip->ip_sum = in_cksum(copym, ip->ip_hl << 2);
+ (void) looutput(ifp, copym, (struct sockaddr *)dst, 0);
+ }
+}
+#endif
diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h
index 47d76fe01b5a..9452d8c9a5d5 100644
--- a/sys/netinet/ip_var.h
+++ b/sys/netinet/ip_var.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1982, 1986 Regents of the University of California.
+ * Copyright (c) 1982, 1986, 1993 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)ip_var.h 7.7 (Berkeley) 6/28/90
- * $Id: ip_var.h,v 1.3 1993/11/07 17:48:00 wollman Exp $
+ * $Id: ip_var.h,v 1.4 1994/05/17 22:31:12 jkh Exp $
*/
#ifndef _NETINET_IP_VAR_H_
@@ -103,6 +103,18 @@ struct ipoption {
char ipopt_list[MAX_IPOPTLEN]; /* options proper */
};
+/*
+ * Structure attached to inpcb.ip_moptions and
+ * passed to ip_output when IP multicast options are in use.
+ */
+struct ip_moptions {
+ struct ifnet *imo_multicast_ifp; /* ifp for outgoing multicasts */
+ u_char imo_multicast_ttl; /* TTL for outgoing multicasts */
+ u_char imo_multicast_loop; /* 1 => hear sends if a member */
+ u_short imo_num_memberships; /* no. memberships this socket */
+ struct in_multi *imo_membership[IP_MAX_MEMBERSHIPS];
+};
+
struct ipstat {
long ips_total; /* total packets received */
long ips_badsum; /* checksum bad */
@@ -129,6 +141,7 @@ struct ipstat {
#ifdef KERNEL
/* flags passed to ip_output as last parameter */
#define IP_FORWARDING 0x1 /* most of ip header exists */
+#define IP_MULTICASTOPTS 0x2 /* multicast opts present */
#define IP_ROUTETOIF SO_DONTROUTE /* bypass routing tables */
#define IP_ALLOWBROADCAST SO_BROADCAST /* can send broadcast packets */
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
index 1618339176ae..a98cc1a71efe 100644
--- a/sys/netinet/raw_ip.c
+++ b/sys/netinet/raw_ip.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * 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
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)raw_ip.c 7.8 (Berkeley) 7/25/90
- * $Id: raw_ip.c,v 1.4 1993/12/19 00:52:46 wollman Exp $
+ * $Id: raw_ip.c,v 1.5 1994/05/17 22:31:13 jkh Exp $
*/
#include "param.h"
@@ -118,7 +118,11 @@ rip_output(m, so)
return (ip_output(m,
(rp->rinp_flags & RINPF_HDRINCL)? (struct mbuf *)0: rp->rinp_options,
&rp->rinp_route,
- (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST));
+ (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST
+#ifdef MULTICAST
+ | IP_MULTICASTOPTS, rp->rinp_rcb.rcb_moptions
+#endif
+ ));
}
/*
@@ -154,10 +158,27 @@ rip_ctloutput(op, so, level, optname, m)
else
rp->rinp_flags &= ~RINPF_HDRINCL;
break;
-
+#ifdef MULTICAST
+ case IP_MULTICAST_IF:
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ error = ip_setmoptions(optname,
+ &rp->rinp_rcb.rcb_moptions, *m);
+ break;
+ default:
+#ifdef MROUTING
+ error = ip_mrouter_cmd(optname, so, *m);
+#else
+ error = EINVAL;
+#endif
+ break;
+#else
default:
error = EINVAL;
break;
+#endif
}
break;
@@ -178,7 +199,16 @@ rip_ctloutput(op, so, level, optname, m)
(*m)->m_len = sizeof (int);
*mtod(*m, int *) = rp->rinp_flags & RINPF_HDRINCL;
break;
-
+#ifdef MULTICAST
+ case IP_MULTICAST_IF:
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ error = ip_getmoptions(optname,
+ rp->rinp_rcb.rcb_moptions, m);
+ break;
+#endif
default:
error = EINVAL;
m_freem(*m);
@@ -201,7 +231,9 @@ rip_usrreq(so, req, m, nam, control)
{
register int error = 0;
register struct raw_inpcb *rp = sotorawinpcb(so);
-
+#if defined(MULTICAST) && defined(MROUTING)
+ extern struct socket *ip_mrouter;
+#endif
switch (req) {
case PRU_ATTACH:
@@ -217,8 +249,16 @@ rip_usrreq(so, req, m, nam, control)
case PRU_DETACH:
if (rp == 0)
panic("rip_detach");
+#if defined(MULTICAST) && defined(MROUTING)
+ if (so == ip_mrouter)
+ ip_mrouter_done();
+#endif
if (rp->rinp_options)
m_freem(rp->rinp_options);
+#ifdef MULTICAST
+ if (rp->rinp_rcb.rcb_moptions)
+ ip_freemoptions(rp->rinp_rcb.rcb_moptions);
+#endif
if (rp->rinp_route.ro_rt)
RTFREE(rp->rinp_route.ro_rt);
if (rp->rinp_rcb.rcb_laddr)
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index b6f377690856..e258f5f6d022 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)tcp_usrreq.c 7.15 (Berkeley) 6/28/90
- * $Id: tcp_usrreq.c,v 1.5 1994/01/24 05:12:36 davidg Exp $
+ * $Id: tcp_usrreq.c,v 1.6 1994/05/22 23:18:40 ache Exp $
*/
#include "param.h"
@@ -351,12 +351,15 @@ tcp_ctloutput(op, so, level, optname, mp)
{
int error = 0;
struct inpcb *inp = sotoinpcb(so);
- register struct tcpcb *tp = intotcpcb(inp);
+ register struct tcpcb *tp;
register struct mbuf *m;
if (level != IPPROTO_TCP)
return (ip_ctloutput(op, so, level, optname, mp));
+ if (!inp || !(tp = intotcpcb(inp)))
+ return (EINVAL);
+
switch (op) {
case PRCO_SETOPT:
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index 44e244c31c95..12b5b3ece29e 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * 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
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)udp_usrreq.c 7.20 (Berkeley) 4/20/91
- * $Id: udp_usrreq.c,v 1.7 1994/02/07 19:53:25 ache Exp $
+ * $Id: udp_usrreq.c,v 1.8 1994/05/17 22:31:14 jkh Exp $
*/
#include "param.h"
@@ -145,6 +145,93 @@ udp_input(m, iphlen)
return;
}
}
+#ifdef MULTICAST
+ if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
+ in_broadcast(ip->ip_dst)) {
+ struct socket *last;
+ /*
+ * Deliver a multicast or broadcast datagram to *all* sockets
+ * for which the local and remote addresses and ports match
+ * those of the incoming datagram. This allows more than
+ * one process to receive multi/broadcasts on the same port.
+ * (This really ought to be done for unicast datagrams as
+ * well, but that would cause problems with existing
+ * applications that open both address-specific sockets and
+ * a wildcard socket listening to the same port -- they would
+ * end up receiving duplicates of every unicast datagram.
+ * Those applications open the multiple sockets to overcome an
+ * inadequacy of the UDP socket interface, but for backwards
+ * compatibility we avoid the problem here rather than
+ * fixing the interface. Maybe 4.4BSD will remedy this?)
+ */
+
+ /*
+ * Construct sockaddr format source address.
+ */
+ udp_in.sin_port = uh->uh_sport;
+ udp_in.sin_addr = ip->ip_src;
+ m->m_len -= sizeof (struct udpiphdr);
+ m->m_data += sizeof (struct udpiphdr);
+ /*
+ * Locate pcb(s) for datagram.
+ * (Algorithm copied from raw_intr().)
+ */
+ last = NULL;
+ for (inp = udb.inp_next; inp != &udb; inp = inp->inp_next) {
+ if (inp->inp_lport != uh->uh_dport)
+ continue;
+ if (inp->inp_laddr.s_addr != INADDR_ANY) {
+ if (inp->inp_laddr.s_addr !=
+ ip->ip_dst.s_addr)
+ continue;
+ }
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ if (inp->inp_faddr.s_addr !=
+ ip->ip_src.s_addr ||
+ inp->inp_fport != uh->uh_sport)
+ continue;
+ }
+
+ if (last != NULL) {
+ struct mbuf *n;
+
+ if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
+ if (sbappendaddr(&last->so_rcv,
+ (struct sockaddr *)&udp_in,
+ n, (struct mbuf *)0) == 0)
+ m_freem(n);
+ else
+ sorwakeup(last);
+ }
+ }
+ last = inp->inp_socket;
+ /*
+ * Don't look for additional matches if this one
+ * does not have the SO_REUSEADDR socket option set.
+ * This heuristic avoids searching through all pcbs
+ * in the common case of a non-shared port. It
+ * assumes that an application will never clear
+ * the SO_REUSEADDR option after setting it.
+ */
+ if ((last->so_options & SO_REUSEADDR) == 0)
+ break;
+ }
+
+ if (last == NULL) {
+ /*
+ * No matching pcb found; discard datagram.
+ * (No need to send an ICMP Port Unreachable
+ * for a broadcast or multicast datgram.)
+ */
+ goto bad;
+ }
+ if (sbappendaddr(&last->so_rcv, (struct sockaddr *)&udp_in,
+ m, (struct mbuf *)0) == 0)
+ goto bad;
+ sorwakeup(last);
+ return;
+ }
+#endif
/*
* Locate pcb for datagram.
@@ -163,10 +250,13 @@ udp_input(m, iphlen)
if (inp == 0) {
/* don't send ICMP response for broadcast packet */
udpstat.udps_noport++;
- if (m->m_flags & M_BCAST) {
+#ifndef MULTICAST
+ /* XXX why don't we do this with MULTICAST? */
+ if (m->m_flags & (M_BCAST | M_MCAST)) {
udpstat.udps_noportbcast++;
goto bad;
}
+#endif
*ip = save_ip;
ip->ip_len += iphlen;
{
@@ -355,7 +445,11 @@ udp_output(inp, m, addr, control)
((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */
udpstat.udps_opackets++;
error = ip_output(m, inp->inp_options, &inp->inp_route,
- inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST));
+ inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)
+#ifdef MULTICAST
+ | IP_MULTICASTOPTS, inp->inp_moptions
+#endif
+ );
if (addr) {
in_pcbdisconnect(inp);
diff --git a/sys/nfs/nfs_bio.c b/sys/nfs/nfs_bio.c
index 3a422dc184d4..e10649d08b3d 100644
--- a/sys/nfs/nfs_bio.c
+++ b/sys/nfs/nfs_bio.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* From: @(#)nfs_bio.c 7.19 (Berkeley) 4/16/91
- * $Id: nfs_bio.c,v 1.5 1994/02/06 22:20:09 davidg Exp $
+ * $Id: nfs_bio.c,v 1.6 1994/06/12 04:05:39 davidg Exp $
*/
#include "param.h"
@@ -175,8 +175,6 @@ nfs_bioread(vp, uio, ioflag, cred)
error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
switch (vp->v_type) {
case VREG:
- if (n+on == biosize || uio->uio_offset == np->n_size)
- bp->b_flags |= B_AGE;
break;
case VLNK:
n = 0;
@@ -298,12 +296,10 @@ again:
brelse(bp);
return (error);
}
+ bp->b_proc = (struct proc *)0;
if ((n+on) == biosize) {
- bp->b_flags |= B_AGE;
- bp->b_proc = (struct proc *)0;
bawrite(bp);
} else {
- bp->b_proc = (struct proc *)0;
bdwrite(bp);
}
} while (error == 0 && uio->uio_resid > 0 && n != 0);
diff --git a/sys/nfs/nfs_serv.c b/sys/nfs/nfs_serv.c
index 6205a268448e..20f7015c1f26 100644
--- a/sys/nfs/nfs_serv.c
+++ b/sys/nfs/nfs_serv.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* From: @(#)nfs_serv.c 7.40 (Berkeley) 5/15/91
- * $Id: nfs_serv.c,v 1.5 1993/12/19 00:54:12 wollman Exp $
+ * $Id: nfs_serv.c,v 1.6 1994/06/11 23:33:53 karl Exp $
*/
/*
@@ -667,10 +667,16 @@ nfsrv_create(mrep, md, dpos, cred, xid, mrq, repstat, p)
vput(nd.ni_dvp);
VOP_ABORTOP(&nd);
vap->va_size = fxdr_unsigned(long, *(tl+3)); /* 28 Aug 92*/
-/* 08 Sep 92*/ if (vap->va_size != -1 && (error = VOP_SETATTR(vp, vap, cred, p))) {
- vput(vp);
- nfsm_reply(0);
- }
+ if (vap->va_size != -1) {
+ if (error = nfsrv_access(vp, VWRITE, cred, p)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ if (error = VOP_SETATTR(vp, vap, cred, p)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ }
}
bzero((caddr_t)fhp, sizeof(nfh));
fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
diff --git a/sys/nfs/nfs_socket.c b/sys/nfs/nfs_socket.c
index 41b386d70dfc..f3c3c880785b 100644
--- a/sys/nfs/nfs_socket.c
+++ b/sys/nfs/nfs_socket.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* From: @(#)nfs_socket.c 7.23 (Berkeley) 4/20/91
- * $Id: nfs_socket.c,v 1.4.2.1 1994/05/03 21:04:53 rgrimes Exp $
+ * $Id: nfs_socket.c,v 1.5 1994/05/03 17:49:16 davidg Exp $
*/
/*
diff --git a/sys/nfs/nfs_subs.c b/sys/nfs/nfs_subs.c
index 14e011882160..d512bde223bf 100644
--- a/sys/nfs/nfs_subs.c
+++ b/sys/nfs/nfs_subs.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* From: @(#)nfs_subs.c 7.41 (Berkeley) 5/15/91
- * $Id: nfs_subs.c,v 1.5 1993/12/19 00:54:15 wollman Exp $
+ * $Id: nfs_subs.c,v 1.6 1994/04/14 07:50:11 davidg Exp $
*/
/*
@@ -262,6 +262,12 @@ nfsm_mbuftouio(mrep, uiop, siz, dpos)
return (error);
}
+void nfsm_nullfree()
+{
+ /* Nothing to do here */
+}
+
+
/*
* copies a uio scatter/gather list to an mbuf chain...
*/
@@ -278,7 +284,7 @@ nfsm_uiotombuf(uiop, mq, siz, bpos)
int uiosiz, clflg, rem;
char *cp;
- if (siz > MLEN) /* or should it >= MCLBYTES ?? */
+ if (siz > MLEN && uiop->uio_segflg != UIO_SYSSPACE)
clflg = 1;
else
clflg = 0;
@@ -292,31 +298,43 @@ nfsm_uiotombuf(uiop, mq, siz, bpos)
if (left > siz)
left = siz;
uiosiz = left;
- while (left > 0) {
+ if (uiop->uio_segflg == UIO_SYSSPACE) {
MGET(mp, M_WAIT, MT_DATA);
- if (clflg)
- MCLGET(mp, M_WAIT);
- mp->m_len = NFSMSIZ(mp);
+ mp->m_flags |= M_EXT;
+ mp->m_data = mp->m_ext.ext_buf = uiocp;
+ mp->m_len = mp->m_ext.ext_size = uiosiz;
+ mp->m_ext.ext_free = nfsm_nullfree;
mp2->m_next = mp;
mp2 = mp;
- xfer = (left > mp->m_len) ? mp->m_len : left;
+ uiop->uio_offset += uiosiz;
+ uiop->uio_resid -= uiosiz;
+ } else {
+ while (left > 0) {
+ MGET(mp, M_WAIT, MT_DATA);
+ if (clflg)
+ MCLGET(mp, M_WAIT);
+ mp->m_len = NFSMSIZ(mp);
+ mp2->m_next = mp;
+ mp2 = mp;
+ xfer = (left > mp->m_len) ? mp->m_len : left;
#ifdef notdef
- /* Not Yet.. */
- if (uiop->uio_iov->iov_op != NULL)
- (*(uiop->uio_iov->iov_op))
- (uiocp, mtod(mp, caddr_t), xfer);
- else
+ /* Not Yet.. */
+ if (uiop->uio_iov->iov_op != NULL)
+ (*(uiop->uio_iov->iov_op))
+ (uiocp, mtod(mp, caddr_t), xfer);
+ else
+ if (uiop->uio_segflg == UIO_SYSSPACE)
+ bcopy(uiocp, mtod(mp, caddr_t), xfer);
+ else
#endif
- if (uiop->uio_segflg == UIO_SYSSPACE)
- bcopy(uiocp, mtod(mp, caddr_t), xfer);
- else
copyin(uiocp, mtod(mp, caddr_t), xfer);
- len = mp->m_len;
- mp->m_len = xfer;
- left -= xfer;
- uiocp += xfer;
- uiop->uio_offset += xfer;
- uiop->uio_resid -= xfer;
+ len = mp->m_len;
+ mp->m_len = xfer;
+ left -= xfer;
+ uiocp += xfer;
+ uiop->uio_offset += xfer;
+ uiop->uio_resid -= xfer;
+ }
}
if (uiop->uio_iov->iov_len <= siz) {
uiop->uio_iovcnt--;
diff --git a/sys/nfs/nfs_vnops.c b/sys/nfs/nfs_vnops.c
index b5548e751576..0066b77b13b6 100644
--- a/sys/nfs/nfs_vnops.c
+++ b/sys/nfs/nfs_vnops.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* From: @(#)nfs_vnops.c 7.60 (Berkeley) 5/24/91
- * $Id: nfs_vnops.c,v 1.7 1994/01/31 23:40:50 martin Exp $
+ * $Id: nfs_vnops.c,v 1.8 1994/06/14 03:41:10 davidg Exp $
*/
/*
@@ -278,9 +278,19 @@ nfs_open(vp, mode, cred, p)
register enum vtype vtyp;
vtyp = vp->v_type;
- if (vtyp == VREG || vtyp == VDIR || vtyp == VLNK)
+ if (vtyp == VREG || vtyp == VDIR || vtyp == VLNK) {
+ struct nfsnode *np;
+ struct vattr vattr;
+ np = VTONFS(vp);
+ if (nfs_dogetattr(vp, &vattr, cred, 1, p) == 0) {
+ if (np->n_mtime != vattr.va_mtime.tv_sec) {
+ np->n_direofoffset = 0;
+ vinvalbuf(vp, TRUE);
+ np->n_mtime = vattr.va_mtime.tv_sec;
+ }
+ }
return (0);
- else
+ } else
return (EACCES);
}
diff --git a/sys/pcfs/pcfs_conv.c b/sys/pcfs/pcfs_conv.c
index 66fbba06216f..4fb706f0d81e 100644
--- a/sys/pcfs/pcfs_conv.c
+++ b/sys/pcfs/pcfs_conv.c
@@ -15,7 +15,7 @@
*
* October 1992
*
- * $Id: pcfs_conv.c,v 1.4 1993/12/19 00:54:27 wollman Exp $
+ * $Id: pcfs_conv.c,v 1.5 1994/04/07 00:30:35 ache Exp $
*/
/*
@@ -297,11 +297,11 @@ unix2dosfn(un, dn, unlen)
* The filenames "." and ".." are handled specially,
* since they don't follow dos filename rules.
*/
- if (un[0] == '.' && un[1] == '\0') {
+ if (un[0] == '.' && unlen == 1) {
dn[0] = '.';
return;
}
- if (un[0] == '.' && un[1] == '.' && un[2] == '\0') {
+ if (un[0] == '.' && un[1] == '.' && unlen == 2) {
dn[0] = '.';
dn[1] = '.';
return;
diff --git a/sys/pcfs/pcfs_fat.c b/sys/pcfs/pcfs_fat.c
index 3e66739e2618..dadcd6add6a7 100644
--- a/sys/pcfs/pcfs_fat.c
+++ b/sys/pcfs/pcfs_fat.c
@@ -15,7 +15,7 @@
*
* October 1992
*
- * $Id: pcfs_fat.c,v 1.3 1993/11/25 01:37:11 wollman Exp $
+ * $Id: pcfs_fat.c,v 1.4 1994/06/22 05:52:53 jkh Exp $
*/
/*
@@ -340,8 +340,14 @@ printf("updateotherfats(pmp %08x, bp %08x, fatbn %d)\n",
*/
-extern inline void
-usemap_alloc (struct pcfsmount *pmp, u_long cn)
+#ifndef __GNUC__
+#define inline
+#endif
+
+static inline void
+usemap_alloc (pmp, cn)
+ struct pcfsmount *pmp;
+ u_long cn;
{
pmp->pm_inusemap[cn / 8] |= 1 << (cn % 8);
pmp->pm_freeclustercount--;
@@ -349,8 +355,10 @@ usemap_alloc (struct pcfsmount *pmp, u_long cn)
pmp->pm_lookhere = cn + 1;
}
-extern inline void
-usemap_free (struct pcfsmount *pmp, u_long cn)
+static inline void
+usemap_free (pmp, cn)
+ struct pcfsmount *pmp;
+ u_long cn;
{
pmp->pm_freeclustercount++;
pmp->pm_inusemap[cn / 8] &= ~(1 << (cn % 8));
diff --git a/sys/pcfs/pcfs_vfsops.c b/sys/pcfs/pcfs_vfsops.c
index 2fac8237dfe3..4d698e74c92d 100644
--- a/sys/pcfs/pcfs_vfsops.c
+++ b/sys/pcfs/pcfs_vfsops.c
@@ -15,7 +15,7 @@
*
* October 1992
*
- * $Id: pcfs_vfsops.c,v 1.5 1993/12/19 02:07:58 ache Exp $
+ * $Id: pcfs_vfsops.c,v 1.6 1994/05/21 01:25:30 sean Exp $
*/
#include "param.h"
@@ -367,8 +367,7 @@ mountpcfs(devvp, mp, p)
* Finish up.
*/
pmp->pm_ronly = ronly;
- if (ronly == 0)
- pmp->pm_fmod = 1;
+ pmp->pm_fmod = !ronly;
mp->mnt_data = (qaddr_t)pmp;
mp->mnt_stat.f_fsid.val[0] = (long)dev;
mp->mnt_stat.f_fsid.val[1] = MOUNT_MSDOS;
diff --git a/sys/pcfs/pcfs_vnops.c b/sys/pcfs/pcfs_vnops.c
index b3975f6face3..15c023472f01 100644
--- a/sys/pcfs/pcfs_vnops.c
+++ b/sys/pcfs/pcfs_vnops.c
@@ -15,7 +15,7 @@
*
* October 1992
*
- * $Id: pcfs_vnops.c,v 1.4 1993/12/19 00:54:32 wollman Exp $
+ * $Id: pcfs_vnops.c,v 1.7 1994/06/12 04:05:44 davidg Exp $
*/
#include "param.h"
@@ -95,6 +95,8 @@ printf("pcfs_create(ndp %08x, vap %08x, p %08x\n", ndp, vap, p);
(union dostime *)&ndirp->deTime);
unix2dosfn((u_char *)ndp->ni_ptr, ndirp->deName, ndp->ni_namelen);
ndirp->deAttributes = (vap->va_mode & VWRITE) ? 0 : ATTR_READONLY;
+ if (vap->va_mode & VEXEC)
+ ndirp->deAttributes |= ATTR_HIDDEN;
ndirp->deStartCluster = 0;
ndirp->deFileSize = 0;
ndirent.de_pmp = pdep->de_pmp;
@@ -219,7 +221,11 @@ pcfs_getattr(vp, vap, cred, p)
cn = (cn << 16) | (dep->de_diroffset & 0xffff);
}
vap->va_fileid = cn;
- vap->va_mode = (dep->de_Attributes & ATTR_READONLY) ? 0555 : 0777;
+ vap->va_mode = (dep->de_Attributes & ATTR_READONLY) ? 0444 : 0666;
+ if ( dep->de_Attributes & ATTR_HIDDEN
+ || dep->de_Attributes & ATTR_DIRECTORY
+ )
+ vap->va_mode |= 0111;
if (dep->de_Attributes & ATTR_DIRECTORY)
vap->va_mode |= S_IFDIR;
vap->va_nlink = 1;
@@ -300,7 +306,11 @@ printf(" va_uid %x, va_gid %x, va_atime.tv_sec %x\n",
* write bit to set the readonly attribute.
*/
if (vap->va_mode != (u_short)VNOVAL) {
- /* We ignore the read and execute bits */
+ /* We ignore the read bits */
+ if (vap->va_mode & VEXEC && vp->v_type != VDIR)
+ dep->de_Attributes |= ATTR_HIDDEN;
+ else
+ dep->de_Attributes &= ~ATTR_HIDDEN;
if (vap->va_mode & VWRITE)
dep->de_Attributes &= ~ATTR_READONLY;
else
@@ -393,17 +403,6 @@ pcfs_read(vp, uio, ioflag, cred)
return error;
}
error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
-/*
- * If we have read everything from this block or
- * have read to end of file then we are done with
- * this block. Mark it to say the buffer can be reused if
- * need be.
- */
-#if 0
- if (n + on == pmp->pm_bpcluster ||
- uio->uio_offset == dep->de_FileSize)
- bp->b_flags |= B_AGE;
-#endif
brelse(bp);
} while (error == 0 && uio->uio_resid > 0 && n != 0);
return error;
@@ -558,7 +557,6 @@ printf("pcfs_write(): diroff %d, dirclust %d, startcluster %d\n",
(void) bwrite(bp);
else
if (n + croffset == pmp->pm_bpcluster) {
- bp->b_flags |= B_AGE;
bawrite(bp);
} else
bdwrite(bp);
@@ -1450,17 +1448,6 @@ printf("pcfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n",
uio);
}
-/*
- * If we have read everything from this block or
- * have read to end of file then we are done with
- * this block. Mark it to say the buffer can be reused if
- * need be.
- */
-#if 0
- if (n + on == pmp->pm_bpcluster ||
- (uio->uio_offset-bias) == dep->de_FileSize)
- bp->b_flags |= B_AGE;
-#endif
brelse(bp);
} while (error == 0 && uio->uio_resid > 0 && n != 0);
out:;
@@ -1550,7 +1537,7 @@ pcfs_bmap(vp, bn, vpp, bnp)
*vpp = dep->de_devvp;
if (bnp == NULL)
return 0;
- return pcbmap(dep, bn << (pmp->pm_cnshift - pmp->pm_bnshift), bnp, 0);
+ return pcbmap(dep, bn, bnp, 0);
}
int
diff --git a/sys/procfs/procfs_subr.c b/sys/procfs/procfs_subr.c
index 94f5e8e5e253..6639f6498c8b 100644
--- a/sys/procfs/procfs_subr.c
+++ b/sys/procfs/procfs_subr.c
@@ -27,7 +27,7 @@
* (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: procfs_subr.c,v 1.4 1994/01/14 16:25:04 davidg Exp $
+ * $Id: procfs_subr.c,v 1.5 1994/03/07 11:38:57 davidg Exp $
*/
#include "param.h"
#include "systm.h"
@@ -40,9 +40,9 @@
#include "file.h"
#include "resourcevar.h"
#include "vm/vm.h"
-#include "vm/vm_page.h"
#include "vm/vm_kern.h"
#include "vm/vm_user.h"
+#include "vm/vm_page.h"
#include "kinfo.h"
#include "kinfo_proc.h"
#include "machine/pmap.h"
@@ -53,6 +53,164 @@
#include "machine/vmparam.h"
/*
+ * Get process address map (PIOCGVMINFO)
+ */
+int
+pfs_vminfo(procp, pfsp, pmapp)
+struct proc *procp;
+struct nfsnode *pfsp;
+struct procvminfo *pmapp;
+{
+ int error = 0;
+ vm_map_t map;
+ vm_map_entry_t entry;
+ struct procvminfo prmap;
+
+ map = &procp->p_vmspace->vm_map;
+ if( procp != curproc)
+ vm_map_lock(map);
+ entry = map->header.next;
+
+ while (entry != &map->header) {
+ if (entry->is_a_map) {
+ vm_map_t submap = entry->object.share_map;
+ vm_map_entry_t subentry;
+
+ if( procp != curproc)
+ vm_map_lock(submap);
+ subentry = submap->header.next;
+ while (subentry != &submap->header) {
+ prmap.entrytype = PFS_PRMAP;
+ prmap.u.pm.vaddr = subentry->start;
+ prmap.u.pm.size = subentry->end - subentry->start;
+ prmap.u.pm.offset = subentry->offset;
+ prmap.u.pm.prot = subentry->protection;
+ error = copyout(&prmap, pmapp, sizeof(struct procvminfo));
+ if (error)
+ break;
+ pmapp++;
+ subentry = subentry->next;
+ }
+ if( procp != curproc)
+ vm_map_unlock(submap);
+ if (error)
+ break;
+ }
+
+ prmap.entrytype = PFS_PRMAP;
+ prmap.u.pm.vaddr = entry->start;
+ prmap.u.pm.size = entry->end - entry->start;
+ prmap.u.pm.offset = entry->offset;
+ prmap.u.pm.prot = entry->protection;
+ error = copyout(&prmap, pmapp, sizeof(struct procvminfo));
+ if (error)
+ break;
+ pmapp++;
+ if( !entry->is_a_map && !entry->is_sub_map) {
+ vm_object_t obj;
+ vm_offset_t off = entry->offset;
+ obj = entry->object.vm_object;
+ while( obj) {
+ vm_page_t pdata, p;
+ vm_offset_t addr, pmapent, *procpmapent;
+ struct vm_page zeropage;
+ prmap.entrytype = PFS_OBJINFO;
+ prmap.u.oi.ref_count = obj->ref_count;
+ prmap.u.oi.rss_map = obj->resident_page_count;
+ prmap.u.oi.persist = obj->can_persist;
+ prmap.u.oi.internal = obj->internal;
+ prmap.u.oi.offset = off;
+ prmap.u.oi.size = obj->size;
+ error = copyout(&prmap, pmapp, sizeof(struct procvminfo));
+ if (error)
+ break;
+ pmapp++;
+ pdata = (vm_page_t) pmapp;
+ bzero(&zeropage, sizeof zeropage);
+ for(addr=0; addr < obj->size; addr += NBPG) {
+ p = vm_page_lookup( obj, addr);
+ if( !p) {
+ p = &zeropage;
+ }
+ error = copyout( p, pdata++, sizeof( struct vm_page));
+ if( error)
+ goto errorfin;
+
+ }
+ procpmapent = (vm_offset_t *) pdata;
+
+ for(addr=0; addr < obj->size; addr += NBPG) {
+ pmapent = pmap_extract( vm_map_pmap( map), addr + entry->start);
+ error = copyout( &pmapent, procpmapent++, sizeof( vm_offset_t));
+ if( error)
+ goto errorfin;
+ }
+
+ pmapp = (struct procvminfo *) procpmapent;
+
+ if( obj->shadow) {
+ off += obj->shadow_offset;
+ obj = obj->shadow;
+ } else {
+ break;
+ }
+ }
+ }
+
+ entry = entry->next;
+ }
+errorfin:
+
+ if( procp != curproc)
+ vm_map_unlock(map);
+ if( !error) {
+ bzero(&prmap, sizeof prmap);
+ prmap.entrytype = PFS_END;
+ error = copyout(&prmap, pmapp, sizeof(struct procvminfo));
+ }
+
+ return error;
+}
+
+/*
+ * Count number of VM entries of process (PIOCGNVMINFO)
+ */
+int
+pfs_vminfo_nentries(procp, pfsp)
+struct proc *procp;
+struct nfsnode *pfsp;
+{
+ int count = 0;
+ vm_map_t map;
+ vm_map_entry_t entry;
+
+ map = &procp->p_vmspace->vm_map;
+ if( procp != curproc)
+ vm_map_lock(map);
+ entry = map->header.next;
+
+ while (entry != &map->header) {
+ if (entry->is_a_map)
+ count += entry->object.share_map->nentries;
+ else if( !entry->is_a_map && !entry->is_sub_map) {
+ vm_object_t obj;
+ obj = entry->object.vm_object;
+ while( obj) {
+ count += 2*sizeof( struct procvminfo) +
+ (obj->size / NBPG) * (sizeof (struct vm_page) + sizeof(vm_offset_t));
+ obj = obj->shadow;
+ }
+ } else {
+ count += sizeof( struct procvminfo);
+ }
+ entry = entry->next;
+ }
+
+ if( procp != curproc)
+ vm_map_unlock(map);
+ return count;
+}
+/*
* Get process address map (PIOCGMAP)
*/
int
@@ -67,7 +225,7 @@ struct procmap *pmapp;
struct procmap prmap;
map = &procp->p_vmspace->vm_map;
- vm_map_lock(map);
+ if( procp != curproc) vm_map_lock(map);
entry = map->header.next;
while (entry != &map->header) {
@@ -75,7 +233,7 @@ struct procmap *pmapp;
vm_map_t submap = entry->object.share_map;
vm_map_entry_t subentry;
- vm_map_lock(submap);
+ if( procp != curproc) vm_map_lock(submap);
subentry = submap->header.next;
while (subentry != &submap->header) {
prmap.vaddr = subentry->start;
@@ -88,7 +246,7 @@ struct procmap *pmapp;
pmapp++;
subentry = subentry->next;
}
- vm_map_unlock(submap);
+ if( procp != curproc) vm_map_unlock(submap);
if (error)
break;
}
@@ -103,7 +261,7 @@ struct procmap *pmapp;
entry = entry->next;
}
- vm_map_unlock(map);
+ if( procp != curproc) vm_map_unlock(map);
return error;
}
@@ -120,7 +278,7 @@ struct nfsnode *pfsp;
vm_map_entry_t entry;
map = &procp->p_vmspace->vm_map;
- vm_map_lock(map);
+ if( procp != curproc) vm_map_lock(map);
entry = map->header.next;
while (entry != &map->header) {
@@ -131,7 +289,7 @@ struct nfsnode *pfsp;
entry = entry->next;
}
- vm_map_unlock(map);
+ if( procp != curproc) vm_map_unlock(map);
return count;
}
diff --git a/sys/procfs/procfs_vnops.c b/sys/procfs/procfs_vnops.c
index 90d524cb474f..8c93a1481c85 100644
--- a/sys/procfs/procfs_vnops.c
+++ b/sys/procfs/procfs_vnops.c
@@ -27,7 +27,7 @@
* (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: procfs_vnops.c,v 1.6 1994/01/31 04:19:20 davidg Exp $
+ * $Id: procfs_vnops.c,v 1.7 1994/03/07 11:38:59 davidg Exp $
*/
/*
@@ -46,6 +46,7 @@
#include "namei.h"
#include "resourcevar.h"
#include "vm/vm.h"
+#include "vm/vm_page.h"
#include "kinfo.h"
#include "kinfo_proc.h"
@@ -213,6 +214,13 @@ pfs_ioctl(vp, com, data, fflag, cred, p)
error = pfs_vmmap(procp, pfsp, *(struct procmap *)data);
break;
+ case PIOCGNVMINFO:
+ *(int *)data = pfs_vminfo_nentries(procp, pfsp);
+ break;
+
+ case PIOCGVMINFO:
+ error = pfs_vminfo(procp, pfsp, *(struct procvminfo *)data);
+ break;
default:
error = EIO;
break;
diff --git a/sys/scsi/cd.c b/sys/scsi/cd.c
index e2c174e84b1a..31c0f48d6fc1 100644
--- a/sys/scsi/cd.c
+++ b/sys/scsi/cd.c
@@ -14,7 +14,7 @@
*
* Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
*
- * $Id: cd.c,v 1.16 1994/02/05 09:08:46 swallace Exp $
+ * $Id: cd.c,v 1.18 1994/04/20 07:06:51 davidg Exp $
*/
#define SPLCD splbio
@@ -415,11 +415,22 @@ cdstrategy(bp)
if (bounds_check_with_label(bp, &cd->disklabel, 1) <= 0)
goto done;
/* otherwise, process transfer request */
+ } else {
+ bp->b_pblkno = bp->b_blkno;
+ bp->b_resid = 0;
}
opri = SPLCD();
dp = &cd->buf_queue;
/*
+ * Use a bounce buffer if necessary
+ */
+#ifndef NOBOUNCE
+ if (cd->sc_link->flags & SDEV_BOUNCE)
+ vm_bounce_alloc(bp);
+#endif
+
+ /*
* Place it in the queue of disk activities for this disk
*/
disksort(dp, bp);
diff --git a/sys/scsi/scsi_base.c b/sys/scsi/scsi_base.c
index 53bf097d63bf..fbd02cab57b3 100644
--- a/sys/scsi/scsi_base.c
+++ b/sys/scsi/scsi_base.c
@@ -8,7 +8,7 @@
* file.
*
* Written by Julian Elischer (julian@dialix.oz.au)
- * $Id: scsi_base.c,v 1.6 1994/02/07 02:15:01 rgrimes Exp $
+ * $Id: scsi_base.c,v 1.8 1994/05/19 22:21:05 jkh Exp $
*/
#define SPLSD splbio
@@ -293,7 +293,7 @@ scsi_start_unit(sc_link, flags)
0,
0,
2,
- 6000,
+ 10000,
NULL,
flags));
}
@@ -321,7 +321,7 @@ scsi_stop_unit(sc_link, eject, flags)
0,
0,
2,
- 6000,
+ 10000,
NULL,
flags));
}
@@ -455,7 +455,11 @@ scsi_scsi_cmd(sc_link, scsi_cmd, cmdlen, data_addr, datalen,
retval = EFAULT;
goto bad;
}
- xs->data = malloc(datalen, M_TEMP, M_WAITOK);
+#ifdef NOBOUNCE
+ xs->data = malloc(datalen, M_TEMP, M_WAITOK);
+#else
+ xs->data = (caddr_t) vm_bounce_kva_alloc( (datalen + PAGE_SIZE - 1)/PAGE_SIZE);
+#endif
/* I think waiting is ok *//*XXX */
switch ((int)(flags & (SCSI_DATA_IN | SCSI_DATA_OUT))) {
case 0:
@@ -538,7 +542,11 @@ retry:
bcopy(xs->data, data_addr, datalen);
break;
}
+#ifdef NOBOUNCE
free(xs->data, M_TEMP);
+#else
+ vm_bounce_kva_alloc_free(xs->data, (datalen + PAGE_SIZE - 1)/PAGE_SIZE, 0);
+#endif
}
/*
* we have finished with the xfer stuct, free it and
diff --git a/sys/scsi/scsi_ioctl.c b/sys/scsi/scsi_ioctl.c
index 08d8a3ab5db3..a52b3a5eebd6 100644
--- a/sys/scsi/scsi_ioctl.c
+++ b/sys/scsi/scsi_ioctl.c
@@ -199,7 +199,7 @@ void scsistrategy(struct buf *bp)
s = splbio();
while(!(bp->b_flags & B_DONE))
{
- sleep(bp,PRIBIO);
+ tsleep((caddr_t)bp, PRIBIO, "scsistrat", 0);
}
splx(s);
SC_DEBUG(sc_link,SDEV_DB3,("back from sleep\n"));
diff --git a/sys/scsi/scsiconf.h b/sys/scsi/scsiconf.h
index ed5a12e9887e..1c3de17b71d8 100644
--- a/sys/scsi/scsiconf.h
+++ b/sys/scsi/scsiconf.h
@@ -14,7 +14,7 @@
*
* Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
*
- * $Id: scsiconf.h,v 1.8 1993/12/19 00:54:55 wollman Exp $
+ * $Id: scsiconf.h,v 1.9 1994/03/23 09:15:55 davidg Exp $
*/
#ifndef SCSI_SCSICONF_H
#define SCSI_SCSICONF_H 1
@@ -134,6 +134,7 @@ struct scsi_link
#define SDEV_MEDIA_LOADED 0x01 /* device figures are still valid */
#define SDEV_WAITING 0x02 /* a process is waiting for this */
#define SDEV_OPEN 0x04 /* at least 1 open session */
+#define SDEV_BOUNCE 0x08 /* unit requires DMA bounce buffer */
#define SDEV_DBX 0xF0 /* debuging flags (scsi_debug.h) */
/*
diff --git a/sys/scsi/sd.c b/sys/scsi/sd.c
index 20d10e376f3e..37eab6f09e28 100644
--- a/sys/scsi/sd.c
+++ b/sys/scsi/sd.c
@@ -14,7 +14,7 @@
*
* Ported to run under 386BSD by Julian Elischer (julian@dialix.oz.au) Sept 1992
*
- * $Id: sd.c,v 1.18.2.2 1994/03/16 04:03:48 rgrimes Exp $
+ * $Id: sd.c,v 1.27 1994/06/22 05:52:59 jkh Exp $
*/
#define SPLSD splbio
@@ -415,14 +415,25 @@ sdstrategy(bp)
if (bounds_check_with_label(bp, &sd->disklabel, sd->wlabel) <= 0)
goto done;
/* otherwise, process transfer request */
+ } else {
+ bp->b_pblkno = bp->b_blkno;
+ bp->b_resid = 0;
}
opri = SPLSD();
dp = &sd->buf_queue;
+ /*
+ * Use a bounce buffer if necessary
+ */
+#ifndef NOBOUNCE
+ if (sd->sc_link->flags & SDEV_BOUNCE)
+ vm_bounce_alloc(bp);
+#endif
+
/*
* Place it in the queue of disk activities for this disk
*/
- disksort(dp, bp);
+ cldisksort(dp, bp, 64*1024);
/*
* Tell the device to get going on the transfer if it's
@@ -883,10 +894,13 @@ sd_get_parms(unit, flags)
}
else {
/* set it to something reasonable */
- sectors = 32;
disk_parms->heads = 64;
disk_parms->cyls = sectors / (64 * 32);
+ sectors = 32;
}
+ /* keep secsiz sane too - we may divide by it later */
+ if(disk_parms->secsiz == 0)
+ disk_parms->secsiz = SECSIZE;
disk_parms->sectors = sectors; /* dubious on SCSI *//*XXX */
}
sd->sc_link->flags |= SDEV_MEDIA_LOADED;
diff --git a/sys/scsi/st.c b/sys/scsi/st.c
index 2bc6e494e013..4a6674718bf3 100644
--- a/sys/scsi/st.c
+++ b/sys/scsi/st.c
@@ -21,13 +21,13 @@
* 16 Feb 93 Julian Elischer ADDED for SCSI system
* 1.15 is the last version to support MACH and OSF/1
*/
-/* $Revision: 1.15 $ */
+/* $Revision: 1.17 $ */
/*
* Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
* major changes by Julian Elischer (julian@jules.dialix.oz.au) May 1993
*
- * $Id: st.c,v 1.15 1994/01/29 10:30:41 rgrimes Exp $
+ * $Id: st.c,v 1.17 1994/06/22 05:53:02 jkh Exp $
*/
/*
@@ -307,6 +307,7 @@ stattach(sc_link)
* the drive. We cannot use interrupts yet, so the
* request must specify this.
*/
+ st_rd_blk_lim(unit, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT);
if (st_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) {
printf("st%d: drive offline\n", unit);
} else {
@@ -315,7 +316,7 @@ stattach(sc_link)
if (st->media_blksiz) {
printf("%d-byte", st->media_blksiz);
} else {
- printf("variable");
+ printf("variable(%d->%d)",st->blkmin,st->blkmax);
}
printf(" blocks, write-%s\n",
(st->flags & ST_READONLY) ? "protected" : "enabled");
@@ -324,6 +325,13 @@ stattach(sc_link)
}
}
/*
+ * Forget if we've loaded the media,
+ * because sometimes things are unstable at boot time.
+ * We'll get it all again at the first open.
+ */
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+
+ /*
* Set up the buf queue for this device
*/
st->buf_queue = 0;
@@ -911,6 +919,14 @@ ststrategy(bp)
stminphys(bp);
opri = splbio();
+ /*
+ * Use a bounce buffer if necessary
+ */
+#ifndef NOBOUNCE
+ if (st->sc_link->flags & SDEV_BOUNCE)
+ vm_bounce_alloc(bp);
+#endif
+
/*
* Place it in the queue of activities for this tape
* at the end (a bit silly because we only have on user..
diff --git a/sys/sys/acct.h b/sys/sys/acct.h
index a152907c8840..46ef5c3548b8 100644
--- a/sys/sys/acct.h
+++ b/sys/sys/acct.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)acct.h 7.3 (Berkeley) 2/15/91
- * $Id: acct.h,v 1.3.2.1 1994/05/04 07:57:00 rgrimes Exp $
+ * $Id: acct.h,v 1.4 1994/05/04 08:30:10 rgrimes Exp $
*/
#ifndef _SYS_ACCT_H_
diff --git a/sys/sys/buf.h b/sys/sys/buf.h
index ab5685893f3f..502d20fa2a61 100644
--- a/sys/sys/buf.h
+++ b/sys/sys/buf.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)buf.h 7.11 (Berkeley) 5/9/90
- * $Id: buf.h,v 1.7 1993/12/22 12:51:48 davidg Exp $
+ * $Id: buf.h,v 1.10 1994/04/20 07:07:01 davidg Exp $
*/
#ifndef _SYS_BUF_H_
@@ -79,6 +79,7 @@ struct buf
struct buf *b_forw, *b_back; /* hash chain (2 way street) */
struct buf *av_forw, *av_back; /* position on free list if not BUSY */
struct buf *b_blockf, **b_blockb;/* associated vnode */
+ struct buf *b_clusterf, *b_clusterl;/* cluster list */
#define b_actf av_forw /* alternate names for driver queue */
#define b_actl av_back /* head - isn't history wonderful */
long b_bcount; /* transfer count */
@@ -96,6 +97,7 @@ struct buf
daddr_t *b_daddr; /* indirect block */
} b_un;
daddr_t b_lblkno; /* logical block number */
+ daddr_t b_pblkno; /* physical block number */
daddr_t b_blkno; /* block # on device */
long b_resid; /* words not transferred after error */
#define b_errcnt b_resid /* while i/o in progress: # retries */
@@ -107,6 +109,7 @@ struct buf
struct ucred *b_wcred; /* ref to write credendtials */
int b_dirtyoff; /* offset in buffer of dirty region */
int b_dirtyend; /* offset of end of dirty region */
+ caddr_t b_savekva; /* saved kva for transfer while bouncing */
caddr_t b_saveaddr; /* original b_addr for PHYSIO */
void * b_driver1; /* for private use by the driver */
void * b_driver2; /* for private use by the driver */
@@ -186,7 +189,7 @@ extern int physio(void (*)(struct buf *), int, struct buf *, int, int,
#define B_VMPAGE 0x000800 /* buffer from virtual memory */
#define B_MALLOC 0x001000 /* buffer from malloc space */
#define B_DIRTY 0x002000 /* dirty page to be pushed out async */
-#define B_PGIN 0x004000 /* pagein op, so swap() can count it */
+#define B_CLUSTER 0x004000 /* pagein op, so swap() can count it */
#define B_CACHE 0x008000 /* did bread find us in the cache ? */
#define B_INVAL 0x010000 /* does not contain valid info */
#define B_LOCKED 0x020000 /* locked in core (not reusable) */
@@ -200,6 +203,7 @@ extern int physio(void (*)(struct buf *), int, struct buf *, int, int,
#define B_DRIVER2 0x2000000 /* bits for the driver to use */
#define B_DRIVER4 0x3000000 /* bits for the driver to use */
#define B_DRIVER8 0x4000000 /* bits for the driver to use */
+#define B_BOUNCE 0x8000000 /* bounce buffer flag */
/*
* Insq/Remq for the buffer hash lists.
diff --git a/sys/sys/callout.h b/sys/sys/callout.h
index d23e152c0f76..cdc1b8e94586 100644
--- a/sys/sys/callout.h
+++ b/sys/sys/callout.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)callout.h 7.2 (Berkeley) 2/15/91
- * $Id: callout.h,v 1.4.2.1 1994/05/04 07:57:02 rgrimes Exp $
+ * $Id: callout.h,v 1.5 1994/05/04 08:30:18 rgrimes Exp $
*/
#ifndef _SYS_CALLOUT_H_
diff --git a/sys/sys/conf.h b/sys/sys/conf.h
index 0dcfe9796ad5..7280962373ca 100644
--- a/sys/sys/conf.h
+++ b/sys/sys/conf.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)conf.h 7.9 (Berkeley) 5/5/91
- * $Id: conf.h,v 1.4.2.1 1994/05/04 07:57:05 rgrimes Exp $
+ * $Id: conf.h,v 1.6 1994/05/04 08:30:22 rgrimes Exp $
*/
#ifndef _SYS_CONF_H_
@@ -88,7 +88,7 @@ struct cdevsw {
d_ioctl_t *d_ioctl;
d_stop_t *d_stop;
d_reset_t *d_reset;
- struct tty *d_ttys;
+ struct tty **d_ttys;
d_select_t *d_select;
d_mmap_t *d_mmap;
d_strategy_t *d_strategy;
diff --git a/sys/sys/disklabel.h b/sys/sys/disklabel.h
index 32da62f94e28..0db4d3ffb79f 100644
--- a/sys/sys/disklabel.h
+++ b/sys/sys/disklabel.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)disklabel.h 7.19 (Berkeley) 5/7/91
- * $Id: disklabel.h,v 1.6 1993/12/19 00:55:13 wollman Exp $
+ * $Id: disklabel.h,v 1.7 1994/04/05 03:23:38 davidg Exp $
*/
#ifndef _SYS_DISKLABEL_H_
@@ -355,6 +355,7 @@ char *readdisklabel(int, d_strategy_t *, struct disklabel *,
struct dos_partition *, struct dkbad *, struct buf **);
void disksort(struct buf *, struct buf *);
+void cldisksort(struct buf *, struct buf *, vm_offset_t);
int writedisklabel(int, d_strategy_t *, struct disklabel *,
struct dos_partition *);
diff --git a/sys/sys/dkstat.h b/sys/sys/dkstat.h
index bbaffb27332d..2e374e584d8b 100644
--- a/sys/sys/dkstat.h
+++ b/sys/sys/dkstat.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)dkstat.h 7.5 (Berkeley) 2/15/91
- * $Id: dkstat.h,v 1.3.2.1 1994/05/04 07:57:07 rgrimes Exp $
+ * $Id: dkstat.h,v 1.4 1994/05/04 08:30:25 rgrimes Exp $
*/
#ifndef _SYS_DKSTAT_H_
diff --git a/sys/sys/errno.h b/sys/sys/errno.h
index fe26b1b732bd..1842041c86d2 100644
--- a/sys/sys/errno.h
+++ b/sys/sys/errno.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)errno.h 7.13 (Berkeley) 2/19/91
- * $Id: errno.h,v 1.3.2.1 1994/05/04 07:57:09 rgrimes Exp $
+ * $Id: errno.h,v 1.4 1994/05/04 08:30:30 rgrimes Exp $
*/
#ifndef _SYS_ERRNO_H_
diff --git a/sys/sys/exec.h b/sys/sys/exec.h
index 783bea504e2a..e3e94005b7de 100644
--- a/sys/sys/exec.h
+++ b/sys/sys/exec.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)exec.h 7.5 (Berkeley) 2/15/91
- * $Id: exec.h,v 1.4.2.1 1994/05/04 07:57:10 rgrimes Exp $
+ * $Id: exec.h,v 1.6 1994/05/04 08:30:34 rgrimes Exp $
*/
#ifndef _EXEC_H_
@@ -66,7 +66,7 @@ unsigned long a_drsize; /* data relocation size */
#define OMAGIC 0407 /* old impure format */
#define NMAGIC 0410 /* read-only text */
#define ZMAGIC 0413 /* demand load format */
-#define QMAGIC 0314 /* "compact" demand load format -- DEPRICATE */
+#define QMAGIC 0314 /* "compact" demand load format */
/* a_mid */
#define MID_ZERO 0 /* unknown - implementation dependent */
diff --git a/sys/sys/fcntl.h b/sys/sys/fcntl.h
index 6e3acd7d8550..0990a7e4af62 100644
--- a/sys/sys/fcntl.h
+++ b/sys/sys/fcntl.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)fcntl.h 5.14 (Berkeley) 7/1/91
- * $Id: fcntl.h,v 1.2.4.1 1994/05/04 07:57:14 rgrimes Exp $
+ * $Id: fcntl.h,v 1.3 1994/05/04 08:30:37 rgrimes Exp $
*/
#ifndef _FCNTL_H_
diff --git a/sys/sys/ftape.h b/sys/sys/ftape.h
index 67fd33d0803b..79a644276ad0 100644
--- a/sys/sys/ftape.h
+++ b/sys/sys/ftape.h
@@ -40,6 +40,9 @@
#define QCV_ECCBLKS 3 /* Blocks ecc eats */
#define QCV_NFMT 3 /* Number of tape formats */
#define QCV_NLEN 5 /* Number of tape lengths */
+#define QCV_HDRMAGIC 0xaa55aa55 /* Magic for header segment */
+#define QCV_FSMAGIC 0x33cc33cc /* Magic for fileset */
+
#define UCHAR unsigned char
#define USHORT unsigned short
#define ULONG unsigned long
@@ -92,6 +95,8 @@ typedef struct qic_hwinfo {
#define QIOCONFIG _IOR('q', 15, int) /* Get tape config */
#define QIOGEOM _IOR('q', 16, QIC_Geom) /* Get geometry */
#define QIOHWINFO _IOR('q', 17, QIC_HWInfo) /* Get hardware inf */
+#define QIOSENDHDR _IOW('q', 18, QIC_Segment) /* Send header */
+#define QIORECVHDR _IOWR('q', 19, QIC_Segment) /* Receive header */
/* QIC drive status bits. */
#define QS_READY 0x01 /* Drive ready */
diff --git a/sys/sys/ioctl.h b/sys/sys/ioctl.h
index 57d812b27ea6..103cd12f39bc 100644
--- a/sys/sys/ioctl.h
+++ b/sys/sys/ioctl.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)ioctl.h 7.19 (Berkeley) 6/26/91
- * $Id: ioctl.h,v 1.6.2.1 1994/05/04 07:57:16 rgrimes Exp $
+ * $Id: ioctl.h,v 1.10 1994/05/30 03:29:53 ache Exp $
*/
#ifndef _IOCTL_H_
@@ -154,10 +154,10 @@ struct ttysize {
#define TIOCEXT _IOW('t', 96, int) /* pty: external processing */
#define TIOCSIG _IO('t', 95) /* pty: generate signal */
#define TIOCDRAIN _IO('t', 94) /* wait till output drained */
-#define TIOCMSBIDIR _IOW('t', 93, int) /* modem: set bidir cap. */
-#define TIOCMGBIDIR _IOR('t', 92, int) /* modem: get bidir cap. */
#define TIOCMSDTRWAIT _IOW('t', 91, int) /* modem: set wait on close */
#define TIOCMGDTRWAIT _IOR('t', 90, int) /* modem: get wait on close */
+#define TIOCTIMESTAMP _IOR('t', 89, struct timeval) /* get timestamp of
+ last interrupt for xntp. */
#define TTYDISC 0 /* termios tty line discipline */
#define NETLDISC 1 /* line discip for berk net */
@@ -220,6 +220,9 @@ struct ttysize {
#define SIOCSIFASYNCMAP _IOW('i', 125, struct ifreq) /* set ppp asyncmap */
#define SIOCGIFASYNCMAP _IOWR('i',124, struct ifreq) /* get ppp asyncmap */
+#define SIOCADDMULTI _IOW('i', 49, struct ifreq) /* add m'cast addr */
+#define SIOCDELMULTI _IOW('i', 50, struct ifreq) /* del m'cast addr */
+
#ifndef KERNEL
#include <sys/cdefs.h>
diff --git a/sys/sys/ioctl_compat.h b/sys/sys/ioctl_compat.h
index f9e05b6a1be5..92bbc6e3a4eb 100644
--- a/sys/sys/ioctl_compat.h
+++ b/sys/sys/ioctl_compat.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)ioctl_compat.h 7.4 (Berkeley) 2/5/91
- * $Id: ioctl_compat.h,v 1.3.2.1 1994/05/04 07:57:20 rgrimes Exp $
+ * $Id: ioctl_compat.h,v 1.4 1994/05/04 08:30:45 rgrimes Exp $
*/
#ifndef _IOCTL_COMPAT_H_
diff --git a/sys/sys/ipc.h b/sys/sys/ipc.h
index 8083cf60a390..afa286111614 100644
--- a/sys/sys/ipc.h
+++ b/sys/sys/ipc.h
@@ -43,7 +43,7 @@
* SUCH DAMAGE.
*
* from: @(#)ipc.h 7.2 (Berkeley) 2/5/91
- * $Id: ipc.h,v 1.3.2.1 1994/05/04 07:57:23 rgrimes Exp $
+ * $Id: ipc.h,v 1.4 1994/05/04 08:30:48 rgrimes Exp $
*/
/*
diff --git a/sys/sys/kernel.h b/sys/sys/kernel.h
index d1bb596d0997..5b90b62db202 100644
--- a/sys/sys/kernel.h
+++ b/sys/sys/kernel.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)kernel.h 7.4 (Berkeley) 2/15/91
- * $Id: kernel.h,v 1.8.2.1 1994/05/04 07:57:25 rgrimes Exp $
+ * $Id: kernel.h,v 1.11 1994/05/04 08:30:51 rgrimes Exp $
*/
#ifndef _SYS_KERNEL_H_
@@ -78,7 +78,12 @@ extern char *s_lowpc;
extern const char *panicstr; /* panic message */
extern const char version[]; /* system version */
-extern const char copyright[]; /* system copyright */
+extern const char ostype[]; /* operating system type */
+extern const char osrelease[]; /* operating system release */
+extern const int osbuild; /* operating system build number */
+extern const char osconfig[]; /* operating system configuration name */
+extern const char machine[]; /* machine type */
+extern const char *cpu_model; /* CPU model */
extern int nblkdev; /* number of entries in bdevsw */
extern int nchrdev; /* number of entries in cdevsw */
@@ -109,7 +114,7 @@ extern u_char curpri; /* priority of current process */
* are collected by the linker into a `struct linker_set' as defined below.
*
* NB: the constants defined below must match those defined in
- * /usr/src/gnu/ld/ld.h. Since their calculation requires arithmetic, we
+ * ld/ld.h. Since their calculation requires arithmetic, we
* can't name them symbolically (e.g., 23 is N_SETT | N_EXT).
*/
#define MAKE_SET(set, sym, type) \
diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h
index e1fbf7752fc4..d9d45921f9c3 100644
--- a/sys/sys/malloc.h
+++ b/sys/sys/malloc.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)malloc.h 7.25 (Berkeley) 5/15/91
- * $Id: malloc.h,v 1.4 1993/11/07 17:52:43 wollman Exp $
+ * $Id: malloc.h,v 1.7 1994/05/17 22:31:21 jkh Exp $
*/
#ifndef _MALLOC_H_
@@ -91,6 +91,11 @@
#define M_LOCKF 40 /* Byte-range locking structures */
#define M_PROC 41 /* Proc structures */
#define M_SUBPROC 42 /* Proc sub-structures */
+#define M_IPMOPTS 43 /* Internet multicast options */
+#define M_IPMADDR 44 /* Internet multicast address */
+#define M_IFMADDR 45 /* link-level multicast address */
+#define M_MRTABLE 46 /* multicast routing tables */
+#define M_TTYS 47 /* allocated tty structures */
#define M_ISOFSMNT 48 /* isofs mount structures */
#define M_TEMP 49 /* misc temporary data buffers */
#define M_PCFSMNT 50 /* PCFS mount structure */
@@ -141,8 +146,12 @@
"file desc", /* 39 M_FILEDESC */ \
"lockf", /* 40 M_LOCKF */ \
"proc", /* 41 M_PROC */ \
- "subproc", /* 42 M_PROC */ \
- 0, 0, 0, 0, 0, \
+ "subproc", /* 42 M_SUBPROC */ \
+ "mcast opts", /* 43 M_IPMOPTS */ \
+ "ip mcast", /* 44 M_IPMADDR */ \
+ "if mcast", /* 45 M_IFMADDR */ \
+ "mcast route", /* 46 M_MRTABLE */ \
+ "ttys", /* 47 M_TTYS */ \
"isofs mount", /* 48 M_ISOFSMNT */ \
"temp", /* 49 M_TEMP */ \
"PCFS mount", /* 50 M_PCFSMNT */ \
@@ -270,5 +279,7 @@ extern char *kmembase;
extern struct kmembuckets bucket[];
extern void *malloc __P((unsigned long size, int type, int flags));
extern void free __P((void *addr, int type));
+extern void *contigmalloc __P((unsigned long size, int type, int flags, unsigned long maxpa,
+ unsigned long alignmask, unsigned long boundarymask));
#endif /* KERNEL */
#endif /* !_MALLOC_H_ */
diff --git a/sys/sys/map.h b/sys/sys/map.h
index b0a4dc2ce8ce..e1ac4e5eefa0 100644
--- a/sys/sys/map.h
+++ b/sys/sys/map.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)map.h 7.5 (Berkeley) 5/7/91
- * $Id: map.h,v 1.2.2.1 1994/05/04 07:57:29 rgrimes Exp $
+ * $Id: map.h,v 1.3 1994/05/04 08:30:54 rgrimes Exp $
*/
#ifndef _SYS_MAP_H_
diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h
index ea36d4cced01..e36d6357fca0 100644
--- a/sys/sys/mbuf.h
+++ b/sys/sys/mbuf.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)mbuf.h 7.14 (Berkeley) 12/5/90
- * $Id: mbuf.h,v 1.6 1993/12/19 00:55:19 wollman Exp $
+ * $Id: mbuf.h,v 1.10 1994/06/01 03:06:27 davidg Exp $
*/
#ifndef _SYS_MBUF_H_
@@ -65,7 +65,7 @@
#define mtod(m,t) ((t)((m)->m_data))
#define dtom(x) ((struct mbuf *)((int)(x) & ~(MSIZE-1)))
#define mtocl(x) (((u_int)(x) - (u_int)mbutl) >> MCLSHIFT)
-#define cltom(x) ((caddr_t)((u_int)mbutl + ((u_int)(x) >> MCLSHIFT)))
+#define cltom(x) ((caddr_t)((u_int)mbutl + ((u_int)(x) << MCLSHIFT)))
/* header at beginning of each mbuf: */
struct m_hdr {
@@ -158,8 +158,21 @@ struct mbuf {
* allocates an mbuf and initializes it to contain a packet header
* and internal data.
*/
+
+
+struct mbuf *mbuffree;
+int mbuffreecnt;
#define MGET(m, how, type) { \
- MALLOC((m), struct mbuf *, MSIZE, mbtypes[type], (how)); \
+ int s = splimp(); \
+ if( mbuffree == 0) { \
+ splx(s); \
+ MALLOC((m), struct mbuf *, MSIZE, mbtypes[type], (how)); \
+ } else { \
+ --mbuffreecnt; \
+ (m) = mbuffree; \
+ mbuffree = (m)->m_next; \
+ splx(s); \
+ } \
if (m) { \
(m)->m_type = (type); \
mbstat.m_mtypes[type]++; \
@@ -172,7 +185,16 @@ struct mbuf {
}
#define MGETHDR(m, how, type) { \
- MALLOC((m), struct mbuf *, MSIZE, mbtypes[type], (how)); \
+ disable_intr(); \
+ if( mbuffree == 0) { \
+ enable_intr(); \
+ MALLOC((m), struct mbuf *, MSIZE, mbtypes[type], (how)); \
+ } else { \
+ --mbuffreecnt; \
+ (m) = mbuffree; \
+ mbuffree = (m)->m_next; \
+ enable_intr(); \
+ } \
if (m) { \
(m)->m_type = (type); \
mbstat.m_mtypes[type]++; \
@@ -219,6 +241,7 @@ union mcluster {
(m)->m_data = (m)->m_ext.ext_buf; \
(m)->m_flags |= M_EXT; \
(m)->m_ext.ext_size = MCLBYTES; \
+ (m)->m_ext.ext_free = (void (*)())0; \
} \
}
@@ -237,8 +260,7 @@ union mcluster {
* Free a single mbuf and associated external storage.
* Place the successor, if any, in n.
*/
-#ifdef notyet
-#define MFREE(m, n) \
+#define MFREE(m, nn) \
{ mbstat.m_mtypes[(m)->m_type]--; \
if ((m)->m_flags & M_EXT) { \
if ((m)->m_ext.ext_free) \
@@ -247,19 +269,17 @@ union mcluster {
else \
MCLFREE((m)->m_ext.ext_buf); \
} \
- (n) = (m)->m_next; \
- FREE((m), mbtypes[(m)->m_type]); \
- }
-#else /* notyet */
-#define MFREE(m, nn) \
- { mbstat.m_mtypes[(m)->m_type]--; \
- if ((m)->m_flags & M_EXT) { \
- MCLFREE((m)->m_ext.ext_buf); \
- } \
(nn) = (m)->m_next; \
- FREE((m), mbtypes[(m)->m_type]); \
+ if( mbuffreecnt < 256) { \
+ ++mbuffreecnt; \
+ disable_intr(); \
+ (m)->m_next = mbuffree; \
+ mbuffree = (m); \
+ enable_intr(); \
+ } else { \
+ FREE((m), mbtypes[(m)->m_type]); \
+ } \
}
-#endif
/*
* Copy mbuf pkthdr from from to to.
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
index bdbfa9901518..889fb58a9771 100644
--- a/sys/sys/mount.h
+++ b/sys/sys/mount.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)mount.h 7.22 (Berkeley) 6/3/91
- * $Id: mount.h,v 1.10 1993/12/19 22:54:02 alm Exp $
+ * $Id: mount.h,v 1.11 1994/03/07 11:39:02 davidg Exp $
*/
#ifndef _SYS_MOUNT_H_
@@ -86,7 +86,8 @@ struct statfs {
#define MOUNT_MSDOS 4 /* MSDOS Filesystem */
#define MOUNT_ISOFS 5 /* iso9660 cdrom */
#define MOUNT_PROCFS 6 /* proc filesystem */
-#define MOUNT_MAXTYPE 6
+#define MOUNT_DEVFS 7 /* device filesystem */
+#define MOUNT_MAXTYPE 7
/*
* Structure per mounted file system.
diff --git a/sys/sys/namei.h b/sys/sys/namei.h
index 11320d89a9c5..0e48cb227752 100644
--- a/sys/sys/namei.h
+++ b/sys/sys/namei.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)namei.h 7.15 (Berkeley) 5/15/91
- * $Id: namei.h,v 1.3 1993/11/07 17:52:53 wollman Exp $
+ * $Id: namei.h,v 1.5 1994/05/26 04:29:54 ache Exp $
*/
#ifndef _NAMEI_H_
diff --git a/sys/sys/param.h b/sys/sys/param.h
index 727689567734..bf27fbb53196 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)param.h 7.23 (Berkeley) 5/6/91
- * $Id: param.h,v 1.3.4.1 1994/05/04 07:57:31 rgrimes Exp $
+ * $Id: param.h,v 1.4 1994/05/04 08:30:58 rgrimes Exp $
*/
#ifndef _SYS_PARAM_H_
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index dcee5115b656..83ec20800764 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)proc.h 7.28 (Berkeley) 5/30/91
- * $Id: proc.h,v 1.6.2.1 1994/03/24 08:38:44 rgrimes Exp $
+ * $Id: proc.h,v 1.7 1994/03/15 02:48:51 wollman Exp $
*/
#ifndef _PROC_H_
diff --git a/sys/sys/procfs.h b/sys/sys/procfs.h
index 368226781adc..520734071385 100644
--- a/sys/sys/procfs.h
+++ b/sys/sys/procfs.h
@@ -27,7 +27,7 @@
* (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: procfs.h,v 1.1 1993/12/12 12:27:03 davidg Exp $
+ * $Id: procfs.h,v 1.2 1994/03/07 11:39:04 davidg Exp $
*/
#ifndef _SYS_PROCFS_H_
#define _SYS_PROCFS_H_
@@ -44,6 +44,26 @@ struct vmfd { /* Mapped file descriptor */
int fd; /* OUT */
};
+enum vmet { PFS_END, PFS_PRMAP, PFS_OBJINFO };
+
+struct objinfo {
+ int ref_count; /* ref_count for object */
+ int rss_map; /* rss within the map entry */
+ int persist; /* persist flag */
+ int internal; /* internal flag */
+ vm_offset_t offset; /* offset into object */
+ vm_offset_t size; /* total size of object */
+ struct vm_page pages[0]; /* pages */
+};
+
+struct procvminfo { /* Full proc VM info */
+ enum vmet entrytype; /* type of entry */
+ union {
+ struct procmap pm; /* proc map entry */
+ struct objinfo oi; /* object info entry */
+ } u;
+};
+
typedef unsigned long fltset_t;
#define PIOCGPINFO _IOR('P', 0, struct kinfo_proc)
@@ -54,5 +74,7 @@ typedef unsigned long fltset_t;
#define PIOCGNMAP _IOR('P', 5, int)
#define PIOCGMAP _IO ('P', 6)
#define PIOCGMAPFD _IOWR('P', 7, struct vmfd)
+#define PIOCGNVMINFO _IOR('P', 8, int)
+#define PIOCGVMINFO _IO ('P', 9)
#endif /* _SYS_PROCFS_H_ */
diff --git a/sys/sys/signal.h b/sys/sys/signal.h
index 348ccb28c632..6a4676f58e9c 100644
--- a/sys/sys/signal.h
+++ b/sys/sys/signal.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)signal.h 7.16 (Berkeley) 3/17/91
- * $Id: signal.h,v 1.7.2.1 1994/05/04 07:57:33 rgrimes Exp $
+ * $Id: signal.h,v 1.8 1994/05/04 08:31:02 rgrimes Exp $
*/
#ifndef _SIGNAL_H_
diff --git a/sys/sys/specdev.h b/sys/sys/specdev.h
index 119cc5388a63..15c8788854b3 100644
--- a/sys/sys/specdev.h
+++ b/sys/sys/specdev.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)specdev.h 7.4 (Berkeley) 4/19/91
- * $Id: specdev.h,v 1.4 1993/11/25 01:38:04 wollman Exp $
+ * $Id: specdev.h,v 1.5 1994/05/30 03:30:29 ache Exp $
*/
#ifndef _SYS_SPECDEV_H_
@@ -47,6 +47,7 @@ struct specinfo {
struct vnode *si_specnext;
long si_flags;
dev_t si_rdev;
+ short si_opencount; /* count of sleeping openers */
};
/*
* Exported shorthand
@@ -55,6 +56,7 @@ struct specinfo {
#define v_hashchain v_specinfo->si_hashchain
#define v_specnext v_specinfo->si_specnext
#define v_specflags v_specinfo->si_flags
+#define v_opencount v_specinfo->si_opencount
/*
* Flags for specinfo
diff --git a/sys/sys/stat.h b/sys/sys/stat.h
index fe4f8f19321d..0c661950ed21 100644
--- a/sys/sys/stat.h
+++ b/sys/sys/stat.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)stat.h 7.11 (Berkeley) 3/3/91
- * $Id: stat.h,v 1.4.4.1 1994/05/04 07:57:35 rgrimes Exp $
+ * $Id: stat.h,v 1.5 1994/05/04 08:31:05 rgrimes Exp $
*/
#ifndef _SYS_STAT_H_
diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h
index 491ccd5d6f1a..6b2c63a13ece 100644
--- a/sys/sys/syscall.h
+++ b/sys/sys/syscall.h
@@ -4,7 +4,7 @@
* System call numbers.
*
* DO NOT EDIT-- this file is automatically generated.
- * created from $Id: syscalls.master,v 1.8 1994/01/31 10:27:25 davidg Exp $
+ * created from $Id: syscalls.master,v 1.9 1994/03/15 01:58:33 wollman Exp $
*/
#define SYS_exit 1
@@ -169,6 +169,8 @@
#define SYS_semsys 169
#define SYS_msgsys 170
#define SYS_shmsys 171
+#define SYS_ntp_gettime 175
+#define SYS_ntp_adjtime 176
#define SYS_vm_allocate 177
#define SYS_vm_deallocate 178
#define SYS_vm_inherit 179
@@ -176,4 +178,4 @@
#define SYS_setgid 181
#define SYS_setegid 182
#define SYS_seteuid 183
-#endif /* _SYS_SYSCALL_H_ */ \ No newline at end of file
+#endif /* _SYS_SYSCALL_H_ */
diff --git a/sys/sys/systm.h b/sys/sys/systm.h
index b7ca8af23b5c..fce5b1755c89 100644
--- a/sys/sys/systm.h
+++ b/sys/sys/systm.h
@@ -38,15 +38,24 @@
* SUCH DAMAGE.
*
* from: @(#)systm.h 7.17 (Berkeley) 5/25/91
- * $Id: systm.h,v 1.9.2.1 1994/05/04 07:57:37 rgrimes Exp $
+ * $Id: systm.h,v 1.11 1994/05/04 08:31:08 rgrimes Exp $
*/
#ifndef _SYS_SYSTM_H_
#define _SYS_SYSTM_H_
-#include "sys/param.h"
+#include "sys/param.h" /* XXX */
#include "sys/sysent.h" /* XXX */
+/*
+ * Machine-dependent function declarations.
+ * These must be first in case a machine-dependent function is static
+ * [inline]. ANSI C's linkage scope rules require the static version
+ * to be visible first. However, if the machine-dependent functions
+ * were actually macros, they would have to be defined last.
+ */
+#include <machine/cpufunc.h>
+
/* Initialize the world */
void startrtclock __P((void));
void consinit __P((void));
@@ -78,17 +87,18 @@ void selwakeup __P((int /*pid_t*/, int));
extern int selwait; /* select timeout address */
-/* SPL Levels */
-extern int splbio(void);
-extern int splclock(void);
-extern int splhigh(void);
-extern int splimp(void);
-extern int splnet(void);
-extern int splsoftclock(void);
-extern int spltty(void);
-extern int splnone(void);
-extern int splx(int);
-#define spl0 splnone
+/* Interrupt masking. */
+void spl0 __P((void));
+int splbio __P((void));
+int splclock __P((void));
+int splhigh __P((void));
+int splimp __P((void));
+int splnet __P((void));
+#define splnone spl0 /* XXX traditional; the reverse is better */
+int splsoftclock __P((void));
+int splsofttty __P((void));
+int spltty __P((void));
+void splx __P((int));
/* Scheduling */
@@ -181,7 +191,6 @@ void boot __P((int));
/* string functions */
-size_t strlen __P((const char *));
int strcmp __P((const char *, const char *));
char *strncpy __P((char *, const char *, int));
char *strcat __P((char *, const char *));
@@ -195,13 +204,8 @@ int bcmp __P((const void *str1, const void *str2, u_int len));
int scanc __P((unsigned size, u_char *cp, u_char *table, int mask));
int skpc __P((int, u_int, u_char *));
int locc __P((int, unsigned, u_char *));
-int ffs __P((long));
/* Debugger entry points */
void Debugger __P((const char *));
-/*
- * Machine-dependent function declarations.
- */
-#include <machine/cpufunc.h>
#endif /* _SYS_SYSTM_H_ */
diff --git a/sys/sys/termios.h b/sys/sys/termios.h
index d8be611142d3..a49f7e7d3a6f 100644
--- a/sys/sys/termios.h
+++ b/sys/sys/termios.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)termios.h 7.22 (Berkeley) 5/7/91
- * $Id: termios.h,v 1.8 1994/01/31 07:38:18 ache Exp $
+ * $Id: termios.h,v 1.9 1994/05/30 03:31:04 ache Exp $
*/
/*
@@ -181,7 +181,7 @@
typedef unsigned long tcflag_t;
typedef unsigned char cc_t;
-typedef long speed_t;
+typedef long speed_t; /* XXX should be unsigned long */
struct termios {
tcflag_t c_iflag; /* input flags */
@@ -189,8 +189,8 @@ struct termios {
tcflag_t c_cflag; /* control flags */
tcflag_t c_lflag; /* local flags */
cc_t c_cc[NCCS]; /* control chars */
- long c_ispeed; /* input speed */
- long c_ospeed; /* output speed */
+ speed_t c_ispeed; /* input speed */
+ speed_t c_ospeed; /* output speed */
};
/*
diff --git a/sys/sys/timeb.h b/sys/sys/timeb.h
index 1ada939b0cf7..e846ee2e08a4 100644
--- a/sys/sys/timeb.h
+++ b/sys/sys/timeb.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)timeb.h 7.2 (Berkeley) 5/5/91
- * $Id: timeb.h,v 1.3.2.1 1994/05/04 07:57:40 rgrimes Exp $
+ * $Id: timeb.h,v 1.4 1994/05/04 08:31:12 rgrimes Exp $
*/
#ifndef _SYS_TIMEB_H_
diff --git a/sys/sys/times.h b/sys/sys/times.h
index 29a73f82e5c2..2bb24632ed9c 100644
--- a/sys/sys/times.h
+++ b/sys/sys/times.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)times.h 7.6 (Berkeley) 3/9/91
- * $Id: times.h,v 1.3.2.1 1994/05/04 07:57:42 rgrimes Exp $
+ * $Id: times.h,v 1.4 1994/05/04 08:31:16 rgrimes Exp $
*/
#ifndef _SYS_TIMES_H_
diff --git a/sys/sys/timex.h b/sys/sys/timex.h
new file mode 100644
index 000000000000..8390ceddca6c
--- /dev/null
+++ b/sys/sys/timex.h
@@ -0,0 +1,290 @@
+/******************************************************************************
+ * *
+ * Copyright (c) David L. Mills 1993, 1994 *
+ * *
+ * 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 University of Delaware not be used in *
+ * advertising or publicity pertaining to distribution of the software *
+ * without specific, written prior permission. The University of Delaware *
+ * makes no representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied warranty. *
+ * *
+ ******************************************************************************/
+
+/*
+ * Modification history timex.h
+ *
+ * 19 Mar 94 David L. Mills
+ * Moved defines from kernel routines to header file and added new
+ * defines for PPS phase-lock loop.
+ *
+ * 20 Feb 94 David L. Mills
+ * Revised status codes and structures for external clock and PPS
+ * signal discipline.
+ *
+ * 28 Nov 93 David L. Mills
+ * Adjusted parameters to improve stability and increase poll
+ * interval.
+ *
+ * 17 Sep 93 David L. Mills
+ * Created file
+ */
+/*
+ * This header file defines the Network Time Protocol (NTP) interfaces
+ * for user and daemon application programs. These are implemented using
+ * private syscalls and data structures and require specific kernel
+ * support.
+ *
+ * NAME
+ * ntp_gettime - NTP user application interface
+ *
+ * SYNOPSIS
+ * #include <sys/timex.h>
+ *
+ * int syscall(SYS_ntp_gettime, tptr)
+ *
+ * int SYS_ntp_gettime defined in syscall.h header file
+ * struct ntptimeval *tptr pointer to ntptimeval structure
+ *
+ * NAME
+ * ntp_adjtime - NTP daemon application interface
+ *
+ * SYNOPSIS
+ * #include <sys/timex.h>
+ *
+ * int syscall(SYS_ntp_adjtime, mode, tptr)
+ *
+ * int SYS_ntp_adjtime defined in syscall.h header file
+ * struct timex *tptr pointer to timex structure
+ *
+ */
+#ifndef _SYS_TIMEX_H_
+#define _SYS_TIMEX_H_ 1
+
+#ifndef MSDOS /* Microsoft specific */
+#include <sys/syscall.h>
+#endif /* MSDOS */
+
+/*
+ * The following defines establish the engineering parameters of the
+ * phase-lock loop (PLL) model used in the kernel implementation. These
+ * parameters have been carefully chosen by analysis for good stability
+ * and wide dynamic range.
+ *
+ * The hz variable is defined in the kernel build environment. It
+ * establishes the timer interrupt frequency, 100 Hz for the SunOS
+ * kernel, 256 Hz for the Ultrix kernel and 1024 Hz for the OSF/1
+ * kernel. SHIFT_HZ expresses the same value as the nearest power of two
+ * in order to avoid hardware multiply operations.
+ *
+ * SHIFT_KG and SHIFT_KF establish the damping of the PLL and are chosen
+ * for a slightly underdamped convergence characteristic.
+ *
+ * MAXTC establishes the maximum time constant of the PLL. With the
+ * SHIFT_KG and SHIFT_KF values given and a time constant range from
+ * zero to MAXTC, the PLL will converge in 15 minutes to 16 hours,
+ * respectively.
+ */
+#define SHIFT_HZ 7 /* log2(hz) */
+#define SHIFT_KG 6 /* phase factor (shift) */
+#define SHIFT_KF 16 /* frequency factor (shift) */
+#define MAXTC 6 /* maximum time constant (shift) */
+
+/*
+ * The following defines establish the scaling of the various variables
+ * used by the PLL. They are chosen to allow the greatest precision
+ * possible without overflow of a 32-bit word.
+ *
+ * SHIFT_SCALE defines the scaling (shift) of the time_phase variable,
+ * which serves as a an extension to the low-order bits of the system
+ * clock variable time.tv_usec.
+ *
+ * SHIFT_UPDATE defines the scaling (shift) of the time_offset variable,
+ * which represents the current time offset with respect to standard
+ * time.
+ *
+ * SHIFT_USEC defines the scaling (shift) of the time_freq and
+ * time_tolerance variables, which represent the current frequency
+ * offset and maximum frequency tolerance.
+ *
+ * FINEUSEC is 1 us in SHIFT_UPDATE units of the time_phase variable.
+ */
+#define SHIFT_SCALE 23 /* phase scale (shift) */
+#define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* time offset scale (shift) */
+#define SHIFT_USEC 16 /* frequency offset scale (shift) */
+#define FINEUSEC (1L << SHIFT_SCALE) /* 1 us in phase units */
+
+/*
+ * The following defines establish the performance envelope of the PLL.
+ * They insure it operates within predefined limits, in order to satisfy
+ * correctness assertions. An excursion which exceeds these bounds is
+ * clamped to the bound and operation proceeds accordingly. In practice,
+ * this can occur only if something has failed or is operating out of
+ * tolerance, but otherwise the PLL continues to operate in a stable
+ * mode.
+ *
+ * MAXPHASE must be set greater than or equal to CLOCK.MAX (128 ms), as
+ * defined in the NTP specification. CLOCK.MAX establishes the maximum
+ * time offset allowed before the system time is reset, rather than
+ * incrementally adjusted. Here, the maximum offset is clamped to
+ * MAXPHASE only in order to prevent overflow errors due to defective
+ * protocol implementations.
+ *
+ * MAXFREQ is the maximum frequency tolerance of the CPU clock
+ * oscillator plus the maximum slew rate allowed by the protocol. It
+ * should be set to at least the frequency tolerance of the oscillator
+ * plus 100 ppm for vernier frequency adjustments. If the kernel
+ * PPS discipline code is configured (PPS_SYNC), the oscillator time and
+ * frequency are disciplined to an external source, presumably with
+ * negligible time and frequency error relative to UTC, and MAXFREQ can
+ * be reduced.
+ *
+ * MAXTIME is the maximum jitter tolerance of the PPS signal if the
+ * kernel PPS discipline code is configured (PPS_SYNC).
+ *
+ * MINSEC and MAXSEC define the lower and upper bounds on the interval
+ * between protocol updates.
+ */
+#define MAXPHASE 128000L /* max phase error (us) */
+#ifdef PPS_SYNC
+#define MAXFREQ (100L << SHIFT_USEC) /* max freq error (100 ppm) */
+#define MAXTIME (200L << PPS_AVG) /* max PPS error (jitter) (200 us) */
+#else
+#define MAXFREQ (200L << SHIFT_USEC) /* max freq error (200 ppm) */
+#endif /* PPS_SYNC */
+#define MINSEC 16L /* min interval between updates (s) */
+#define MAXSEC 1200L /* max interval between updates (s) */
+
+#ifdef PPS_SYNC
+/*
+ * The following defines are used only if a pulse-per-second (PPS)
+ * signal is available and connected via a modem control lead, such as
+ * produced by the optional ppsclock feature incorporated in the Sun
+ * asynch driver. They establish the design parameters of the frequency-
+ * lock loop used to discipline the CPU clock oscillator to the PPS
+ * signal.
+ *
+ * PPS_AVG is the averaging factor for the frequency loop, as well as
+ * the time and frequency dispersion.
+ *
+ * PPS_SHIFT and PPS_SHIFTMAX specify the minimum and maximum
+ * calibration intervals, respectively, in seconds as a power of two.
+ *
+ * PPS_VALID is the maximum interval before the PPS signal is considered
+ * invalid and protocol updates used directly instead.
+ *
+ * MAXGLITCH is the maximum interval before a time offset of more than
+ * MAXTIME is believed.
+ */
+#define PPS_AVG 2 /* pps averaging constant (shift) */
+#define PPS_SHIFT 2 /* min interval duration (s) (shift) */
+#define PPS_SHIFTMAX 8 /* max interval duration (s) (shift) */
+#define PPS_VALID 120 /* pps signal watchdog max (s) */
+#define MAXGLITCH 30 /* pps signal glitch max (s) */
+#endif /* PPS_SYNC */
+
+/*
+ * The following defines and structures define the user interface for
+ * the ntp_gettime() and ntp_adjtime() system calls.
+ *
+ * Control mode codes (timex.modes)
+ */
+#define MOD_OFFSET 0x0001 /* set time offset */
+#define MOD_FREQUENCY 0x0002 /* set frequency offset */
+#define MOD_MAXERROR 0x0004 /* set maximum time error */
+#define MOD_ESTERROR 0x0008 /* set estimated time error */
+#define MOD_STATUS 0x0010 /* set clock status bits */
+#define MOD_TIMECONST 0x0020 /* set pll time constant */
+#define MOD_CLKB 0x4000 /* set clock B */
+#define MOD_CLKA 0x8000 /* set clock A */
+
+/*
+ * Status codes (timex.status)
+ */
+#define STA_PLL 0x0001 /* enable PLL updates (rw) */
+#define STA_PPSFREQ 0x0002 /* enable PPS freq discipline (rw) */
+#define STA_PPSTIME 0x0004 /* enable PPS time discipline (rw) */
+
+#define STA_INS 0x0010 /* insert leap (rw) */
+#define STA_DEL 0x0020 /* delete leap (rw) */
+#define STA_UNSYNC 0x0040 /* clock unsynchronized (rw) */
+
+#define STA_PPSSIGNAL 0x0100 /* PPS signal present (ro) */
+#define STA_PPSJITTER 0x0200 /* PPS signal jitter exceeded (ro) */
+#define STA_PPSWANDER 0x0400 /* PPS signal wander exceeded (ro) */
+#define STA_PPSERROR 0x0800 /* PPS signal calibration error (ro) */
+
+#define STA_CLOCKERR 0x1000 /* clock hardware fault (ro) */
+
+#define STA_RONLY (STA_PPSSIGNAL | STA_PPSJITTER | STA_PPSWANDER | \
+ STA_PPSERROR | STA_CLOCKERR) /* read-only bits */
+
+/*
+ * Clock states (time_state)
+ */
+#define TIME_OK 0 /* no leap second warning */
+#define TIME_INS 1 /* insert leap second warning */
+#define TIME_DEL 2 /* delete leap second warning */
+#define TIME_OOP 3 /* leap second in progress */
+#define TIME_WAIT 4 /* leap second has occured */
+#define TIME_ERROR 5 /* clock not synchronized */
+
+/*
+ * NTP user interface (ntp_gettime()) - used to read kernel clock values
+ *
+ * Note: maximum error = NTP synch distance = dispersion + delay / 2;
+ * estimated error = NTP dispersion.
+ */
+struct ntptimeval {
+ struct timeval time; /* current time (ro) */
+ long maxerror; /* maximum error (us) (ro) */
+ long esterror; /* estimated error (us) (ro) */
+};
+
+/*
+ * NTP daemon interface - (ntp_adjtime()) used to discipline CPU clock
+ * oscillator
+ */
+struct timex {
+ unsigned int modes; /* clock mode bits (wo) */
+ long offset; /* time offset (us) (rw) */
+ long freq; /* frequency offset (scaled ppm) (rw) */
+ long maxerror; /* maximum error (us) (rw) */
+ long esterror; /* estimated error (us) (rw) */
+ int status; /* clock status bits (rw) */
+ long constant; /* pll time constant (rw) */
+ long precision; /* clock precision (us) (ro) */
+ long tolerance; /* clock frequency tolerance (scaled
+ * ppm) (ro) */
+ /*
+ * The following read-only structure members are implemented
+ * only if the PPS signal discipline is configured in the
+ * kernel.
+ */
+ long ppsfreq; /* pps frequency (scaled ppm) (ro) */
+ long jitter; /* pps jitter (us) (ro) */
+ int shift; /* interval duration (s) (shift) (ro) */
+ long stabil; /* pps stability (scaled ppm) (ro) */
+ long jitcnt; /* jitter limit exceeded (ro) */
+ long calcnt; /* calibration intervals (ro) */
+ long errcnt; /* calibration errors (ro) */
+ long stbcnt; /* stability limit exceeded (ro) */
+
+};
+#ifdef __FreeBSD__
+
+#ifndef KERNEL
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+extern int ntp_gettime __P((struct ntptimeval *));
+extern int ntp_adjtime __P((struct timex *));
+__END_DECLS
+
+#endif /* not KERNEL */
+
+#endif /* __FreeBSD__ */
+#endif /* _SYS_TIMEX_H_ */
diff --git a/sys/sys/tty.h b/sys/sys/tty.h
index 9c8f11dfc621..9b0417b74e2f 100644
--- a/sys/sys/tty.h
+++ b/sys/sys/tty.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)tty.h 7.10 (Berkeley) 6/26/91
- * $Id: tty.h,v 1.8 1994/01/28 23:15:17 ache Exp $
+ * $Id: tty.h,v 1.12 1994/05/30 21:56:22 ache Exp $
*/
#ifndef _SYS_TTY_H_
@@ -42,8 +42,11 @@
/*
* Ring buffers provide a contiguous, dense storage for
* character data used by the tty driver.
+ *
+ * Make sizeof(struct ringb) be such that it fits exactly in
+ * a malloc bucket.
*/
-#define RBSZ 1024
+#define RBSZ 2040
struct ringb {
char *rb_hd; /* head of buffer segment to be read */
@@ -117,9 +120,9 @@ struct tty {
#if 0
int t_mask; /* interrupt mask */
#endif
- struct ringb t_raw; /* ring buffers */
- struct ringb t_can;
- struct ringb t_out;
+ struct ringb *t_raw; /* ring buffers */
+ struct ringb *t_can;
+ struct ringb *t_out;
};
#define TTIPRI 25 /* sleep priority for tty reads */
@@ -127,30 +130,29 @@ struct tty {
#define TTMASK 15
#define OBUFSIZ 100
-#define TTYHOG 1024
+#define TTYHOG RBSZ
#ifdef KERNEL
#define TTMAXHIWAT (RBSZ/2) /* XXX */
#define TTMINHIWAT 128
#define TTMAXLOWAT 256
#define TTMINLOWAT 32
-extern struct ttychars ttydefaults;
#endif /* KERNEL */
/* internal state bits */
#define TS_TIMEOUT 0x000001UL /* delay timeout in progress */
-#define TS_WOPEN 0x000002UL /* waiting for open to complete */
#define TS_ISOPEN 0x000004UL /* device is open */
#define TS_FLUSH 0x000008UL /* outq has been flushed during DMA */
#define TS_CARR_ON 0x000010UL /* software copy of carrier-present */
#define TS_BUSY 0x000020UL /* output in progress */
-#define TS_ASLEEP 0x000040UL /* wakeup when output done */
+#define TS_SO_OLOWAT 0x000040UL /* wake up when output <= low water */
#define TS_XCLUDE 0x000080UL /* exclusive-use flag against open */
#define TS_TTSTOP 0x000100UL /* output stopped by ctl-s */
-/* was TS_HUPCLS 0x000200UL * hang up upon last close */
+#define TS_ZOMBIE 0x000200UL /* carrier dropped */
#define TS_TBLOCK 0x000400UL /* tandem queue blocked */
#define TS_RCOLL 0x000800UL /* collision in read select */
#define TS_WCOLL 0x001000UL /* collision in write select */
+#define TS_SO_OCOMPLETE 0x002000UL /* wake up when output complete */
#define TS_ASYNC 0x004000UL /* tty in async i/o mode */
/* state for intra-line fancy editing work */
#define TS_BKSL 0x010000UL /* state for lowercase \ work */
@@ -166,6 +168,13 @@ extern struct ttychars ttydefaults;
#define TS_HW_IFLOW (TS_DTR_IFLOW | TS_RTS_IFLOW)
#define TS_LOCAL (TS_BKSL|TS_ERASE|TS_LNCH|TS_TYPEN|TS_CNTTB)
+/*
+ * XXX maintain a single flag to keep track of this combination and fix all
+ * the places that check TS_CARR_ON without checking CLOCAL or TS_ZOMBIE.
+ */
+#define CAN_DO_IO(tp) (((tp)->t_state & (TS_CARR_ON | TS_ZOMBIE)) \
+ == TS_CARR_ON || (tp)->t_cflag & CLOCAL)
+
/* define partab character types */
#define ORDINARY 0
#define CONTROL 1
@@ -206,21 +215,31 @@ struct speedtab {
#define DMBIC 2
#define DMGET 3
-#ifdef KERNEL
-/* symbolic sleep message strings */
-extern const char ttyin[], ttyout[], ttopen[], ttclos[], ttybg[], ttybuf[];
+/*
+ * Sleep addresses.
+ */
+#define TSA_CARR_ON(tp) ((caddr_t)(tp) + 0)
+#define TSA_HUP_OR_INPUT(tp) ((caddr_t)(tp) + 1)
+#define TSA_OCOMPLETE(tp) ((caddr_t)(tp) + 2)
+#define TSA_OLOWAT(tp) ((caddr_t)(tp) + 3)
+#define TSA_PTC_READ(tp) ((caddr_t)(tp) + 4)
+#define TSA_PTC_WRITE(tp) ((caddr_t)(tp) + 5)
+#ifdef KERNEL
+struct proc;
struct uio;
/* From tty.c: */
+extern void termioschars(struct termios *);
extern void ttychars(struct tty *);
-extern int ttwflush(struct tty *);
+extern int ttywflush(struct tty *);
extern int ttywait(struct tty *);
extern void ttyflush(struct tty *, int);
extern void ttstart(struct tty *);
+#if 0 /* XXX not used */
extern void ttrstrt(struct tty *);
+#endif
extern int ttioctl(struct tty *, int, caddr_t, int);
-extern int ttnread(struct tty *);
extern int ttselect(int /*dev_t*/, int, struct proc *);
extern int ttyopen(int /*dev_t*/, struct tty *, int);
extern void ttylclose(struct tty *, int);
@@ -232,17 +251,22 @@ extern int ttread(struct tty *, struct uio *, int);
extern int ttycheckoutq(struct tty *, int);
extern int ttwrite(struct tty *, struct uio *, int);
extern void ttwakeup(struct tty *);
+extern void ttwwakeup(struct tty *);
extern int ttspeedtab(int, struct speedtab *);
extern void ttsetwater(struct tty *);
extern void ttyinfo(struct tty *);
extern int tputchar(int, struct tty *);
extern int ttysleep(struct tty *, caddr_t, int, const char *, int);
+extern struct tty *ttymalloc(struct tty *);
+extern void ttyfree(struct tty *);
/* From tty_ring.c: */
extern int putc(int, struct ringb *);
extern int getc(struct ringb *);
extern int nextc(char **, struct ringb *);
+#if 0 /* XXX not used */
extern int ungetc(int, struct ringb *);
+#endif
extern int unputc(struct ringb *);
extern void initrb(struct ringb *);
extern void catb(struct ringb *, struct ringb *);
@@ -252,4 +276,5 @@ extern size_t rb_write(struct ringb *, char *, size_t);
extern int ttcompat(struct tty *, int, caddr_t, int);
#endif /* KERNEL */
+
#endif /* _SYS_TTY_H_ */
diff --git a/sys/sys/ttydefaults.h b/sys/sys/ttydefaults.h
index 2ce0c9990ea3..cce2c86353db 100644
--- a/sys/sys/ttydefaults.h
+++ b/sys/sys/ttydefaults.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)ttydefaults.h 7.9 (Berkeley) 5/9/91
- * $Id: ttydefaults.h,v 1.3.2.1 1994/05/04 07:57:44 rgrimes Exp $
+ * $Id: ttydefaults.h,v 1.5 1994/05/30 03:32:26 ache Exp $
*/
/*
@@ -61,10 +61,10 @@
*/
#define CTRL(x) (x&037)
#define CEOF CTRL('d')
-#define CEOL ((unsigned)'\377') /* XXX avoid _POSIX_VDISABLE */
+#define CEOL 0xFF /* XXX avoid _POSIX_VDISABLE */
#define CERASE 0177
#define CINTR CTRL('c')
-#define CSTATUS ((unsigned)'\377') /* XXX avoid _POSIX_VDISABLE */
+#define CSTATUS 0xFF /* XXX avoid _POSIX_VDISABLE */
#define CKILL CTRL('u')
#define CMIN 1
#define CQUIT 034 /* FS, ^\ */
@@ -90,7 +90,7 @@
* #define TTYDEFCHARS to include an array of default control characters.
*/
#ifdef TTYDEFCHARS
-cc_t ttydefchars[NCCS] = {
+static cc_t ttydefchars[NCCS] = {
CEOF, CEOL, CEOL, CERASE, CWERASE, CKILL, CREPRINT,
_POSIX_VDISABLE, CINTR, CQUIT, CSUSP, CDSUSP, CSTART, CSTOP, CLNEXT,
CDISCARD, CMIN, CTIME, CSTATUS, _POSIX_VDISABLE
diff --git a/sys/sys/types.h b/sys/sys/types.h
index 53aa680a38e2..c69398ba373d 100644
--- a/sys/sys/types.h
+++ b/sys/sys/types.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)types.h 7.17 (Berkeley) 5/6/91
- * $Id: types.h,v 1.8.2.1 1994/05/04 07:57:47 rgrimes Exp $
+ * $Id: types.h,v 1.12 1994/05/04 08:31:24 rgrimes Exp $
*/
#ifndef _TYPES_H_
diff --git a/sys/sys/unistd.h b/sys/sys/unistd.h
index fb12c87c9f26..76b4c56d8c4b 100644
--- a/sys/sys/unistd.h
+++ b/sys/sys/unistd.h
@@ -31,22 +31,22 @@
* SUCH DAMAGE.
*
* from: @(#)unistd.h 5.14 (Berkeley) 4/1/91
- * $Id: unistd.h,v 1.4 1994/01/31 07:38:20 ache Exp $
+ * $Id: unistd.h,v 1.7 1994/05/07 20:05:24 wollman Exp $
*/
#ifndef _SYS_UNISTD_H_
#define _SYS_UNISTD_H_
/* compile-time symbolic constants */
-#define _POSIX_JOB_CONTROL /* implementation supports job control */
-#ifdef _NOTYET
-#define _POSIX_SAVED_IDS /* saved set-user-ID and set-group-ID */
+#define _POSIX_JOB_CONTROL 1 /* implementation supports job control */
+#if 0
+#define _POSIX_SAVED_IDS 1 /* saved set-user-ID and set-group-ID */
#endif
#define _POSIX_VERSION 198808L
/* execution-time symbolic constants */
-#define _POSIX_CHOWN_RESTRICTED /* chown requires appropriate privileges */
-#define _POSIX_NO_TRUNC /* too-long path components generate errors */
+#define _POSIX_CHOWN_RESTRICTED 0 /* chown requires appropriate privileges */
+#define _POSIX_NO_TRUNC 0 /* too-long path components generate errors */
/* may disable terminal special characters */
#define _POSIX_VDISABLE 0xFF
diff --git a/sys/ufs/dir.h b/sys/ufs/dir.h
index 963ead42dec9..00e91e9d0d8f 100644
--- a/sys/ufs/dir.h
+++ b/sys/ufs/dir.h
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)dir.h 7.10 (Berkeley) 3/25/91
- * $Id: dir.h,v 1.2.4.1 1994/05/04 07:59:08 rgrimes Exp $
+ * $Id: dir.h,v 1.3 1994/05/04 08:33:04 rgrimes Exp $
*/
#ifndef _DIR_H_
diff --git a/sys/ufs/ufs_disksubr.c b/sys/ufs/ufs_disksubr.c
index e80b67e371b1..5c155992e592 100644
--- a/sys/ufs/ufs_disksubr.c
+++ b/sys/ufs/ufs_disksubr.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91
- * $Id: ufs_disksubr.c,v 1.5 1993/12/19 00:55:43 wollman Exp $
+ * $Id: ufs_disksubr.c,v 1.8 1994/06/07 01:21:39 phk Exp $
*/
#include "param.h"
@@ -493,6 +493,7 @@ bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel)
int maxsz = p->p_size,
sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT;
+#if !defined(DISKLABEL_UNPROTECTED)
/* overwriting disk label ? */
/* XXX should also protect bootstrap in first 8K */
if (bp->b_blkno + p->p_offset <= LABELSECTOR + labelsect &&
@@ -503,6 +504,7 @@ bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel)
bp->b_error = EROFS;
goto bad;
}
+#endif /* !defined(DISKLABEL_UNPROTECTED) */
#if defined(DOSBBSECTOR) && defined(notyet)
/* overwriting master boot record? */
@@ -530,7 +532,8 @@ bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel)
}
/* calculate cylinder for disksort to order transfers with */
- bp->b_cylin = (bp->b_blkno + p->p_offset) / lp->d_secpercyl;
+ bp->b_pblkno = bp->b_blkno + p->p_offset;
+ bp->b_cylin = bp->b_pblkno / lp->d_secpercyl;
return(1);
bad:
diff --git a/sys/ufs/ufs_lookup.c b/sys/ufs/ufs_lookup.c
index 06c1cb27314d..aa4c13ebfb0c 100644
--- a/sys/ufs/ufs_lookup.c
+++ b/sys/ufs/ufs_lookup.c
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)ufs_lookup.c 7.33 (Berkeley) 5/19/91
- * $Id: ufs_lookup.c,v 1.6.2.1 1994/05/04 07:59:10 rgrimes Exp $
+ * $Id: ufs_lookup.c,v 1.7 1994/05/04 08:33:11 rgrimes Exp $
*/
#include "param.h"
diff --git a/sys/ufs/ufs_vfsops.c b/sys/ufs/ufs_vfsops.c
index 5eaaf387e572..d7d5de227260 100644
--- a/sys/ufs/ufs_vfsops.c
+++ b/sys/ufs/ufs_vfsops.c
@@ -38,7 +38,7 @@
* SUCH DAMAGE.
*
* from: @(#)ufs_vfsops.c 7.56 (Berkeley) 6/28/91
- * $Id: ufs_vfsops.c,v 1.6.2.1 1994/05/04 07:59:13 rgrimes Exp $
+ * $Id: ufs_vfsops.c,v 1.7 1994/05/04 08:33:15 rgrimes Exp $
*/
#include "param.h"
diff --git a/sys/ufs/ufs_vnops.c b/sys/ufs/ufs_vnops.c
index 096cb1f3a338..39fad0e55885 100644
--- a/sys/ufs/ufs_vnops.c
+++ b/sys/ufs/ufs_vnops.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)ufs_vnops.c 7.64 (Berkeley) 5/16/91
- * $Id: ufs_vnops.c,v 1.14 1994/01/19 21:09:26 jtc Exp $
+ * $Id: ufs_vnops.c,v 1.16 1994/06/12 04:05:53 davidg Exp $
*/
#include "param.h"
@@ -524,10 +524,6 @@ ufs_read(vp, uio, ioflag, cred)
return (error);
}
error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
-#if OMIT /* 20 Aug 92*/
- if (n + on == fs->fs_bsize || uio->uio_offset == ip->i_size)
- bp->b_flags |= B_AGE;
-#endif /* OMIT*/
brelse(bp);
} while (error == 0 && uio->uio_resid > 0 && n != 0);
return (error);
@@ -617,7 +613,6 @@ ufs_write(vp, uio, ioflag, cred)
if (ioflag & IO_SYNC)
(void) bwrite(bp);
else if (n + on == fs->fs_bsize) {
- bp->b_flags |= B_AGE;
bawrite(bp);
} else
bdwrite(bp);
diff --git a/sys/vm/device_pager.c b/sys/vm/device_pager.c
index 01ce7305d5a1..12c113615ce4 100644
--- a/sys/vm/device_pager.c
+++ b/sys/vm/device_pager.c
@@ -86,6 +86,7 @@ struct pagerops devicepagerops = {
dev_pager_getpage,
0,
dev_pager_putpage,
+ 0,
dev_pager_haspage
};
diff --git a/sys/vm/queue.h b/sys/vm/queue.h
index 8eaa42a0328e..7010951bbfc3 100644
--- a/sys/vm/queue.h
+++ b/sys/vm/queue.h
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* @(#)queue.h 7.3 (Berkeley) 4/21/91
- * $Id: queue.h,v 1.2 1993/10/16 16:20:18 rgrimes Exp $
+ * $Id: queue.h,v 1.3 1994/04/14 07:50:17 davidg Exp $
*/
/*
@@ -111,6 +111,30 @@ typedef struct queue_entry *queue_entry_t;
} \
}
+#define queue_enter_head(head, elt, type, field) { \
+ if (queue_empty((head))) { \
+ (head)->next = (queue_entry_t) elt; \
+ (head)->prev = (queue_entry_t) elt; \
+ (elt)->field.next = head; \
+ (elt)->field.prev = head; \
+ } else { \
+ register queue_entry_t next = (head)->next; \
+ (elt)->field.prev = head; \
+ (elt)->field.next = next; \
+ (head)->next = (queue_entry_t)(elt); \
+ ((type)next)->field.prev = (queue_entry_t)(elt);\
+ } \
+}
+
+/* insert 'item' after 'position' using field 'field' */
+/* XXX might be broken - BEWARE */
+#define queue_insert(position, item, type, field) { \
+ ((type) position->field.next)->field.prev = (queue_entry_t)(item); \
+ (item)->field.next = (position)->field.next; \
+ (position)->field.next = (queue_entry_t)(item); \
+ (item)->field.prev = (queue_entry_t) position; \
+}
+
#define queue_field(head, thing, type, field) \
(((head) == (thing)) ? (head) : &((type)(thing))->field)
diff --git a/sys/vm/swap_pager.c b/sys/vm/swap_pager.c
index bf3f38f9ce8d..e7c350b01502 100644
--- a/sys/vm/swap_pager.c
+++ b/sys/vm/swap_pager.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 1994 John S. Dyson
* Copyright (c) 1990 University of Utah.
* Copyright (c) 1991 The Regents of the University of California.
* All rights reserved.
@@ -38,7 +39,7 @@
* from: Utah $Hdr: swap_pager.c 1.4 91/04/30$
* from: @(#)swap_pager.c 7.4 (Berkeley) 5/7/91
*
- * $Id: swap_pager.c,v 1.17.2.1 1994/03/07 02:07:06 rgrimes Exp $
+ * $Id: swap_pager.c,v 1.27 1994/05/25 11:06:48 davidg Exp $
*/
/*
@@ -68,7 +69,7 @@
#include "vm_map.h"
#ifndef NPENDINGIO
-#define NPENDINGIO 96
+#define NPENDINGIO 16
#endif
extern int nswbuf;
@@ -79,6 +80,9 @@ extern int hz;
int swap_pager_full;
extern vm_map_t pager_map;
extern int vm_pageout_pages_needed;
+extern int vm_swap_size;
+
+#define MAX_PAGEOUT_CLUSTER 8
struct swpagerclean {
queue_head_t spc_list;
@@ -86,7 +90,9 @@ struct swpagerclean {
struct buf *spc_bp;
sw_pager_t spc_swp;
vm_offset_t spc_kva;
- vm_page_t spc_m;
+ vm_offset_t spc_altkva;
+ int spc_count;
+ vm_page_t spc_m[MAX_PAGEOUT_CLUSTER];
} swcleanlist [NPENDINGIO] ;
typedef struct swpagerclean *swp_clean_t;
@@ -105,11 +111,14 @@ queue_head_t swap_pager_list; /* list of "named" anon regions */
queue_head_t swap_pager_un_list; /* list of "unnamed" anon pagers */
#define SWAP_FREE_NEEDED 0x1 /* need a swap block */
int swap_pager_needflags;
+struct rlist *swapfrag;
static queue_head_t *swp_qs[]={
&swap_pager_list, &swap_pager_un_list, (queue_head_t *) 0
};
+int swap_pager_putmulti();
+
struct pagerops swappagerops = {
swap_pager_init,
swap_pager_alloc,
@@ -117,6 +126,7 @@ struct pagerops swappagerops = {
swap_pager_getpage,
swap_pager_getmulti,
swap_pager_putpage,
+ swap_pager_putmulti,
swap_pager_haspage
};
@@ -132,10 +142,18 @@ extern int vm_page_count;
struct buf * getpbuf() ;
void relpbuf(struct buf *bp) ;
+static inline void swapsizecheck() {
+ if( vm_swap_size < 128*btodb(NBPG)) {
+ if( swap_pager_full)
+ printf("swap_pager: out of space\n");
+ swap_pager_full = 1;
+ } else if( vm_swap_size > 192*btodb(NBPG))
+ swap_pager_full = 0;
+}
+
void
swap_pager_init()
{
- register int i;
extern int dmmin, dmmax;
dfltpagerops = &swappagerops;
@@ -189,7 +207,7 @@ swap_pager_alloc(handle, size, prot, offset)
if (!spc->spc_kva) {
break;
}
- spc->spc_bp = malloc(sizeof( *bp), M_TEMP,
+ spc->spc_bp = malloc( sizeof( *bp), M_TEMP,
M_NOWAIT);
if (!spc->spc_bp) {
kmem_free_wakeup(pager_map, spc->spc_kva, NBPG);
@@ -199,6 +217,8 @@ swap_pager_alloc(handle, size, prot, offset)
queue_enter(&swap_pager_free, spc, swp_clean_t, spc_list);
}
require_swap_init = 0;
+ if( size == 0)
+ return(NULL);
}
/*
@@ -219,8 +239,9 @@ swap_pager_alloc(handle, size, prot, offset)
}
}
- if (swap_pager_full)
+ if (swap_pager_full) {
return(NULL);
+ }
/*
* Pager doesn't exist, allocate swap management resources
@@ -246,10 +267,10 @@ swap_pager_alloc(handle, size, prot, offset)
free((caddr_t)pager, M_VMPAGER);
return(NULL);
}
- bzero((caddr_t)swp->sw_blocks,
- swp->sw_nblocks * sizeof(*swp->sw_blocks));
for (i = 0; i < swp->sw_nblocks; i++) {
+ swp->sw_blocks[i].swb_valid = 0;
+ swp->sw_blocks[i].swb_locked = 0;
for (j = 0; j < SWB_NPAGES; j++)
swp->sw_blocks[i].swb_block[j] = SWB_EMPTY;
}
@@ -336,30 +357,87 @@ swap_pager_setvalid(swp, offset, valid)
}
/*
+ * this routine allocates swap space with a fragmentation
+ * minimization policy.
+ */
+int
+swap_pager_getswapspace( unsigned amount, unsigned *rtval) {
+ unsigned tmpalloc;
+ unsigned nblocksfrag = btodb(SWB_NPAGES*NBPG);
+ if( amount < nblocksfrag) {
+ if( rlist_alloc(&swapfrag, amount, rtval))
+ return 1;
+ if( !rlist_alloc(&swapmap, nblocksfrag, &tmpalloc))
+ return 0;
+ rlist_free( &swapfrag, tmpalloc+amount, tmpalloc + nblocksfrag - 1);
+ *rtval = tmpalloc;
+ return 1;
+ }
+ if( !rlist_alloc(&swapmap, amount, rtval))
+ return 0;
+ else
+ return 1;
+}
+
+/*
+ * this routine frees swap space with a fragmentation
+ * minimization policy.
+ */
+void
+swap_pager_freeswapspace( unsigned from, unsigned to) {
+ unsigned nblocksfrag = btodb(SWB_NPAGES*NBPG);
+ unsigned tmpalloc;
+ if( ((to + 1) - from) >= nblocksfrag) {
+ while( (from + nblocksfrag) <= to + 1) {
+ rlist_free(&swapmap, from, from + nblocksfrag - 1);
+ from += nblocksfrag;
+ }
+ }
+ if( from >= to)
+ return;
+ rlist_free(&swapfrag, from, to);
+ while( rlist_alloc(&swapfrag, nblocksfrag, &tmpalloc)) {
+ rlist_free(&swapmap, tmpalloc, tmpalloc + nblocksfrag-1);
+ }
+}
+/*
* this routine frees swap blocks from a specified pager
*/
void
-swap_pager_freespace(pager, start, size)
- vm_pager_t pager;
+_swap_pager_freespace(swp, start, size)
+ sw_pager_t swp;
vm_offset_t start;
vm_offset_t size;
{
- sw_pager_t swp = (sw_pager_t) pager->pg_data;
vm_offset_t i;
int s;
s = splbio();
for (i = start; i < round_page(start + size - 1); i += NBPG) {
- int *addr = swap_pager_diskaddr(swp, i, 0);
+ int valid;
+ int *addr = swap_pager_diskaddr(swp, i, &valid);
if (addr && *addr != SWB_EMPTY) {
- rlist_free(&swapmap, *addr, *addr + btodb(NBPG) - 1);
+ swap_pager_freeswapspace(*addr, *addr+btodb(NBPG) - 1);
+ if( valid) {
+ vm_swap_size += btodb(NBPG);
+ swap_pager_setvalid(swp, i, 0);
+ }
*addr = SWB_EMPTY;
- swap_pager_full = 0;
}
}
+ swapsizecheck();
splx(s);
}
+void
+swap_pager_freespace(pager, start, size)
+ vm_pager_t pager;
+ vm_offset_t start;
+ vm_offset_t size;
+{
+ _swap_pager_freespace((sw_pager_t) pager->pg_data, start, size);
+}
+
/*
* swap_pager_reclaim frees up over-allocated space from all pagers
* this eliminates internal fragmentation due to allocation of space
@@ -410,6 +488,8 @@ swap_pager_reclaim()
swp = (sw_pager_t) p->pg_data;
for (i = 0; i < swp->sw_nblocks; i++) {
sw_blk_t swb = &swp->sw_blocks[i];
+ if( swb->swb_locked)
+ continue;
for (j = 0; j < SWB_NPAGES; j++) {
if (swb->swb_block[j] != SWB_EMPTY &&
(swb->swb_valid & (1 << j)) == 0) {
@@ -430,9 +510,9 @@ rfinished:
* free the blocks that have been added to the reclaim list
*/
for (i = 0; i < reclaimcount; i++) {
- rlist_free(&swapmap, reclaims[i], reclaims[i] + btodb(NBPG) - 1);
+ swap_pager_freeswapspace(reclaims[i], reclaims[i]+btodb(NBPG) - 1);
+ swapsizecheck();
wakeup((caddr_t) &in_reclaim);
- swap_pager_full = 0;
}
splx(s);
@@ -480,7 +560,7 @@ swap_pager_copy(srcpager, srcoffset, dstpager, dstoffset, offset)
/*
* clean all of the pages that are currently active and finished
*/
- (void) swap_pager_clean(NULL, B_WRITE);
+ (void) swap_pager_clean();
s = splbio();
/*
@@ -488,11 +568,14 @@ swap_pager_copy(srcpager, srcoffset, dstpager, dstoffset, offset)
* (release allocated space)
*/
for (i = 0; i < offset + srcoffset; i += NBPG) {
- int *addr = swap_pager_diskaddr(srcswp, i, 0);
+ int valid;
+ int *addr = swap_pager_diskaddr(srcswp, i, &valid);
if (addr && *addr != SWB_EMPTY) {
- rlist_free(&swapmap, *addr, *addr + btodb(NBPG) - 1);
+ swap_pager_freeswapspace(*addr, *addr+btodb(NBPG) - 1);
+ if( valid)
+ vm_swap_size += btodb(NBPG);
+ swapsizecheck();
*addr = SWB_EMPTY;
- swap_pager_full = 0;
}
}
/*
@@ -518,23 +601,24 @@ swap_pager_copy(srcpager, srcoffset, dstpager, dstoffset, offset)
* source block without copying.
*/
if (!dstvalid && dstaddrp && *dstaddrp != SWB_EMPTY) {
- rlist_free(&swapmap, *dstaddrp, *dstaddrp + btodb(NBPG) - 1);
+ swap_pager_freeswapspace(*dstaddrp, *dstaddrp+btodb(NBPG) - 1);
*dstaddrp = SWB_EMPTY;
- swap_pager_full = 0;
}
if (dstaddrp && *dstaddrp == SWB_EMPTY) {
*dstaddrp = *srcaddrp;
*srcaddrp = SWB_EMPTY;
swap_pager_setvalid(dstswp, i + dstoffset, 1);
- }
- }
+ vm_swap_size -= btodb(NBPG);
+ }
+ }
/*
* if the source is not empty at this point, then deallocate the space.
*/
if (*srcaddrp != SWB_EMPTY) {
- rlist_free(&swapmap, *srcaddrp, *srcaddrp + btodb(NBPG) - 1);
+ swap_pager_freeswapspace(*srcaddrp, *srcaddrp+btodb(NBPG) - 1);
+ if( srcvalid)
+ vm_swap_size += btodb(NBPG);
*srcaddrp = SWB_EMPTY;
- swap_pager_full = 0;
}
}
}
@@ -543,14 +627,17 @@ swap_pager_copy(srcpager, srcoffset, dstpager, dstoffset, offset)
* deallocate the rest of the source object
*/
for (i = dstswp->sw_osize + offset + srcoffset; i < srcswp->sw_osize; i += NBPG) {
- int *srcaddrp = swap_pager_diskaddr(srcswp, i, 0);
+ int valid;
+ int *srcaddrp = swap_pager_diskaddr(srcswp, i, &valid);
if (srcaddrp && *srcaddrp != SWB_EMPTY) {
- rlist_free(&swapmap, *srcaddrp, *srcaddrp + btodb(NBPG) - 1);
+ swap_pager_freeswapspace(*srcaddrp, *srcaddrp+btodb(NBPG) - 1);
+ if( valid)
+ vm_swap_size += btodb(NBPG);
*srcaddrp = SWB_EMPTY;
- swap_pager_full = 0;
}
}
+ swapsizecheck();
splx(s);
free((caddr_t)srcswp->sw_blocks, M_VMPGDATA);
@@ -595,7 +682,7 @@ swap_pager_dealloc(pager)
splx(s);
- (void) swap_pager_clean(NULL, B_WRITE);
+ (void) swap_pager_clean();
/*
* Free left over swap blocks
@@ -604,13 +691,15 @@ swap_pager_dealloc(pager)
for (i = 0, bp = swp->sw_blocks; i < swp->sw_nblocks; i++, bp++) {
for (j = 0; j < SWB_NPAGES; j++)
if (bp->swb_block[j] != SWB_EMPTY) {
- rlist_free(&swapmap, (unsigned)bp->swb_block[j],
+ swap_pager_freeswapspace((unsigned)bp->swb_block[j],
(unsigned)bp->swb_block[j] + btodb(NBPG) - 1);
+ if( bp->swb_valid & (1<<j))
+ vm_swap_size += btodb(NBPG);
bp->swb_block[j] = SWB_EMPTY;
- swap_pager_full = 0;
}
}
splx(s);
+ swapsizecheck();
/*
* Free swap management resources
@@ -633,7 +722,9 @@ swap_pager_getmulti(pager, m, count, reqpage, sync)
int reqpage;
boolean_t sync;
{
- return swap_pager_io((sw_pager_t) pager->pg_data, m, count, reqpage, B_READ);
+ if( reqpage >= count)
+ panic("swap_pager_getmulti: reqpage >= count\n");
+ return swap_pager_input((sw_pager_t) pager->pg_data, m, count, reqpage);
}
/*
@@ -648,7 +739,29 @@ swap_pager_getpage(pager, m, sync)
vm_page_t marray[1];
marray[0] = m;
- return swap_pager_io((sw_pager_t)pager->pg_data, marray, 1, 0, B_READ);
+ return swap_pager_input((sw_pager_t)pager->pg_data, marray, 1, 0);
+}
+
+int
+swap_pager_putmulti(pager, m, c, sync, rtvals)
+ vm_pager_t pager;
+ vm_page_t *m;
+ int c;
+ boolean_t sync;
+ int *rtvals;
+{
+ int flags;
+
+ if (pager == NULL) {
+ (void) swap_pager_clean();
+ return VM_PAGER_OK;
+ }
+
+ flags = B_WRITE;
+ if (!sync)
+ flags |= B_ASYNC;
+
+ return swap_pager_output((sw_pager_t)pager->pg_data, m, c, flags, rtvals);
}
/*
@@ -662,10 +775,11 @@ swap_pager_putpage(pager, m, sync)
{
int flags;
vm_page_t marray[1];
+ int rtvals[1];
if (pager == NULL) {
- (void) swap_pager_clean(NULL, B_WRITE);
+ (void) swap_pager_clean();
return VM_PAGER_OK;
}
@@ -673,11 +787,14 @@ swap_pager_putpage(pager, m, sync)
flags = B_WRITE;
if (!sync)
flags |= B_ASYNC;
- return(swap_pager_io((sw_pager_t)pager->pg_data, marray, 1, 0, flags));
+
+ swap_pager_output((sw_pager_t)pager->pg_data, marray, 1, flags, rtvals);
+
+ return rtvals[0];
}
static inline int
-swap_pager_block_index(swp, offset)
+const swap_pager_block_index(swp, offset)
sw_pager_t swp;
vm_offset_t offset;
{
@@ -685,11 +802,11 @@ swap_pager_block_index(swp, offset)
}
static inline int
-swap_pager_block_offset(swp, offset)
+const swap_pager_block_offset(swp, offset)
sw_pager_t swp;
vm_offset_t offset;
{
- return (offset % (SWB_NPAGES*NBPG));
+ return ((offset % (NBPG*SWB_NPAGES)) / NBPG);
}
/*
@@ -755,8 +872,6 @@ swap_pager_ridpages(m, count, reqpage)
int reqpage;
{
int i;
- int s;
-
for (i = 0; i < count; i++)
if (i != reqpage)
swap_pager_freepage(m[i]);
@@ -774,35 +889,33 @@ swap_pager_iodone1(bp)
bp->b_flags |= B_DONE;
bp->b_flags &= ~B_ASYNC;
wakeup((caddr_t)bp);
+/*
if ((bp->b_flags & B_READ) == 0)
vwakeup(bp);
+*/
}
-/*
- * Scaled down version of swap().
- * BOGUS: lower level IO routines expect a KVA so we have to map our
- * provided physical page into the KVA to keep them happy.
- */
+
+
int
-swap_pager_io(swp, m, count, reqpage, flags)
+swap_pager_input(swp, m, count, reqpage)
register sw_pager_t swp;
vm_page_t *m;
int count, reqpage;
- int flags;
{
register struct buf *bp;
- register sw_blk_t swb;
+ sw_blk_t swb[count];
register int s;
- int i, ix;
+ int i;
boolean_t rv;
- vm_offset_t kva, off;
+ vm_offset_t kva, off[count];
swp_clean_t spc;
- int cluster;
vm_offset_t paging_offset;
vm_object_t object;
- int reqaddr, mydskregion;
- extern int dmmin, dmmax;
+ int reqaddr[count];
- spc = NULL;
+ int first, last;
+ int failed;
+ int reqdskregion;
object = m[reqpage]->object;
paging_offset = object->paging_offset;
@@ -812,89 +925,89 @@ swap_pager_io(swp, m, count, reqpage, flags)
* following shadow chains looking for the top level object
* with the page.
*/
- off = m[reqpage]->offset + paging_offset;
- ix = swap_pager_block_index(swp, off);
- if (swp->sw_blocks == NULL || ix >= swp->sw_nblocks) {
- /* printf("swap pager: out of range\n"); */
+ if (swp->sw_blocks == NULL) {
swap_pager_ridpages(m, count, reqpage);
return(VM_PAGER_FAIL);
}
+
+ for(i = 0; i < count; i++) {
+ vm_offset_t foff = m[i]->offset + paging_offset;
+ int ix = swap_pager_block_index(swp, foff);
+ if (ix >= swp->sw_nblocks) {
+ int j;
+ if( i <= reqpage) {
+ swap_pager_ridpages(m, count, reqpage);
+ return(VM_PAGER_FAIL);
+ }
+ for(j = i; j < count; j++) {
+ swap_pager_freepage(m[j]);
+ }
+ count = i;
+ break;
+ }
+ swb[i] = &swp->sw_blocks[ix];
+ off[i] = swap_pager_block_offset(swp, foff);
+ reqaddr[i] = swb[i]->swb_block[off[i]];
+ }
- swb = &swp->sw_blocks[ix];
- off = swap_pager_block_offset(swp, off) / NBPG;
- reqaddr = swb->swb_block[off];
-
- /* make sure that our I/O request is contiguous */
- if (flags & B_READ) {
- int first = 0, last = count;
- int failed = 0;
- int reqdskregion = reqaddr / dmmax;
- int valid;
+ /* make sure that our required input request is existant */
- if (reqaddr == SWB_EMPTY ||
- (swb->swb_valid & (1 << off)) == 0) {
- swap_pager_ridpages(m, count, reqpage);
- return(VM_PAGER_FAIL);
- }
-
- /*
- * search backwards for the first contiguous page to transfer
- */
- for (i = reqpage - 1; i >= 0; --i) {
- int *tmpaddr = swap_pager_diskaddr(swp,
- m[i]->offset + paging_offset,&valid);
- if (tmpaddr == 0 || failed || !valid ||
- *tmpaddr != reqaddr + btodb((i - reqpage) * NBPG)) {
+ if (reqaddr[reqpage] == SWB_EMPTY ||
+ (swb[reqpage]->swb_valid & (1 << off[reqpage])) == 0) {
+ swap_pager_ridpages(m, count, reqpage);
+ return(VM_PAGER_FAIL);
+ }
+
+
+ reqdskregion = reqaddr[reqpage] / dmmax;
+
+ /*
+ * search backwards for the first contiguous page to transfer
+ */
+ failed = 0;
+ first = 0;
+ for (i = reqpage - 1; i >= 0; --i) {
+ if ( failed || (reqaddr[i] == SWB_EMPTY) ||
+ (swb[i]->swb_valid & (1 << off[i])) == 0 ||
+ (reqaddr[i] != (reqaddr[reqpage] + (i - reqpage) * btodb(NBPG))) ||
+ ((reqaddr[i] / dmmax) != reqdskregion)) {
failed = 1;
swap_pager_freepage(m[i]);
- m[i] = 0;
if (first == 0)
first = i + 1;
- } else {
- mydskregion = *tmpaddr / dmmax;
- if (mydskregion != reqdskregion) {
- failed = 1;
- swap_pager_freepage(m[i]);
- m[i] = 0;
- first = i + 1;
- }
- }
- }
- /*
- * search forwards for the last contiguous page to transfer
- */
- failed = 0;
- for (i = reqpage + 1; i < count; i++) {
- int *tmpaddr = swap_pager_diskaddr(swp, m[i]->offset + paging_offset,&valid);
- if (tmpaddr == 0 || failed || !valid ||
- *tmpaddr != reqaddr + btodb((i - reqpage) * NBPG) ) {
+ }
+ }
+ /*
+ * search forwards for the last contiguous page to transfer
+ */
+ failed = 0;
+ last = count;
+ for (i = reqpage + 1; i < count; i++) {
+ if ( failed || (reqaddr[i] == SWB_EMPTY) ||
+ (swb[i]->swb_valid & (1 << off[i])) == 0 ||
+ (reqaddr[i] != (reqaddr[reqpage] + (i - reqpage) * btodb(NBPG))) ||
+ ((reqaddr[i] / dmmax) != reqdskregion)) {
failed = 1;
swap_pager_freepage(m[i]);
- m[i] = 0;
if (last == count)
last = i;
- } else {
- mydskregion = *tmpaddr / dmmax;
- if (mydskregion != reqdskregion) {
- failed = 1;
- swap_pager_freepage(m[i]);
- m[i] = 0;
- if (last == count)
- last = i;
- }
- }
- }
- count = last;
- if (first != 0) {
- for (i = first; i < count; i++) {
- m[i - first] = m[i];
- }
- count -= first;
- reqpage -= first;
+ }
+ }
+
+ count = last;
+ if (first != 0) {
+ for (i = first; i < count; i++) {
+ m[i-first] = m[i];
+ reqaddr[i-first] = reqaddr[i];
+ off[i-first] = off[i];
}
+ count -= first;
+ reqpage -= first;
}
+ ++swb[reqpage]->swb_locked;
+
/*
* at this point:
* "m" is a pointer to the array of vm_page_t for paging I/O
@@ -903,24 +1016,6 @@ swap_pager_io(swp, m, count, reqpage, flags)
* "reqpage" is the index into "m" for the page actually faulted
*/
- /*
- * For reads (pageins) and synchronous writes, we clean up
- * all completed async pageouts.
- */
- if ((flags & B_ASYNC) == 0) {
- swap_pager_clean(NULL, flags);
- }
- /*
- * For async writes (pageouts), we cleanup completed pageouts so
- * that all available resources are freed. Also tells us if this
- * page is already being cleaned. If it is, or no resources
- * are available, we try again later.
- */
- else if (swap_pager_clean(m[reqpage], B_WRITE)) {
- swap_pager_ridpages(m, count, reqpage);
- return VM_PAGER_TRYAGAIN;
- }
-
spc = NULL; /* we might not use an spc data structure */
kva = 0;
@@ -929,44 +1024,38 @@ swap_pager_io(swp, m, count, reqpage, flags)
* but for transfers == 1 page, the swap_pager_free list contains
* entries that have pre-allocated kva's (for efficiency).
*/
- if ((flags & B_READ) && count > 1) {
+ if (count > 1) {
kva = kmem_alloc_pageable(pager_map, count*NBPG);
}
-
+
if (!kva) {
/*
* if a kva has not been allocated, we can only do a one page transfer,
- * so we free the other pages that might have been allocated by vm_fault.
+ * so we free the other pages that might have been allocated by
+ * vm_fault.
*/
- for (i = 0; i < count; i++) {
- if (i != reqpage) {
- swap_pager_freepage(m[i]);
- m[i] = 0;
- }
- }
- count = 1;
+ swap_pager_ridpages(m, count, reqpage);
m[0] = m[reqpage];
+ reqaddr[0] = reqaddr[reqpage];
+
+ count = 1;
reqpage = 0;
/*
* get a swap pager clean data structure, block until we get it
*/
if (queue_empty(&swap_pager_free)) {
-/*
- if ((flags & (B_ASYNC|B_READ)) == B_ASYNC)
- return VM_PAGER_TRYAGAIN;
-*/
s = splbio();
if( curproc == pageproc)
- (void) swap_pager_clean(NULL, B_WRITE);
+ (void) swap_pager_clean();
else
wakeup((caddr_t) &vm_pages_needed);
while (queue_empty(&swap_pager_free)) {
swap_pager_needflags |= SWAP_FREE_NEEDED;
tsleep((caddr_t)&swap_pager_free,
PVM, "swpfre", 0);
- if (curproc == pageproc)
- (void) swap_pager_clean(NULL, B_WRITE);
+ if( curproc == pageproc)
+ (void) swap_pager_clean();
else
wakeup((caddr_t) &vm_pages_needed);
}
@@ -978,35 +1067,214 @@ swap_pager_io(swp, m, count, reqpage, flags)
/*
- * Determine swap block and allocate as necessary.
- * We try to get SWB_NPAGES first, but then we punt and try
- * to get one page. If that fails, we look at the allocation
- * data structures to find unused but allocated pages in other
- * pagers allocations.
+ * map our page(s) into kva for input
*/
- if (reqaddr == SWB_EMPTY) {
- int blk;
- int tries;
- int ntoget;
+ for (i = 0; i < count; i++) {
+ pmap_kenter( kva + NBPG * i, VM_PAGE_TO_PHYS(m[i]));
+ }
+ pmap_update();
+
- tries = 0;
- s = splbio();
+ /*
+ * Get a swap buffer header and perform the IO
+ */
+ if( spc) {
+ bp = spc->spc_bp;
+ bzero(bp, sizeof *bp);
+ bp->b_spc = spc;
+ } else {
+ bp = getpbuf();
+ }
+
+ s = splbio();
+ bp->b_flags = B_BUSY | B_READ | B_CALL;
+ bp->b_iodone = swap_pager_iodone1;
+ bp->b_proc = &proc0; /* XXX (but without B_PHYS set this is ok) */
+ bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred;
+ bp->b_un.b_addr = (caddr_t) kva;
+ bp->b_blkno = reqaddr[0];
+ bp->b_bcount = NBPG*count;
+ bp->b_bufsize = NBPG*count;
+
+ VHOLD(swapdev_vp);
+ bp->b_vp = swapdev_vp;
+ if (swapdev_vp->v_type == VBLK)
+ bp->b_dev = swapdev_vp->v_rdev;
+
+ swp->sw_piip++;
+
+ /*
+ * perform the I/O
+ */
+ VOP_STRATEGY(bp);
+
+ /*
+ * wait for the sync I/O to complete
+ */
+ while ((bp->b_flags & B_DONE) == 0) {
+ tsleep((caddr_t)bp, PVM, "swread", 0);
+ }
+ rv = (bp->b_flags & B_ERROR) ? VM_PAGER_FAIL : VM_PAGER_OK;
+ bp->b_flags &= ~(B_BUSY|B_WANTED|B_PHYS|B_DIRTY|B_CALL|B_DONE);
+
+ --swp->sw_piip;
+ if (swp->sw_piip == 0)
+ wakeup((caddr_t) swp);
+
+ if (bp->b_vp)
+ brelvp(bp);
+
+ splx(s);
+ --swb[reqpage]->swb_locked;
+
+ /*
+ * remove the mapping for kernel virtual
+ */
+ pmap_remove(vm_map_pmap(pager_map), kva, kva + count * NBPG);
+
+ if (spc) {
/*
- * if any other pages have been allocated in this block, we
- * only try to get one page.
+ * if we have used an spc, we need to free it.
*/
- for (i = 0; i < SWB_NPAGES; i++) {
- if (swb->swb_block[i] != SWB_EMPTY)
- break;
+ queue_enter(&swap_pager_free, spc, swp_clean_t, spc_list);
+ if (swap_pager_needflags & SWAP_FREE_NEEDED) {
+ swap_pager_needflags &= ~SWAP_FREE_NEEDED;
+ wakeup((caddr_t)&swap_pager_free);
}
+ } else {
+ /*
+ * free the kernel virtual addresses
+ */
+ kmem_free_wakeup(pager_map, kva, count * NBPG);
+ /*
+ * release the physical I/O buffer
+ */
+ relpbuf(bp);
+ /*
+ * finish up input if everything is ok
+ */
+ if( rv == VM_PAGER_OK) {
+ for (i = 0; i < count; i++) {
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m[i]));
+ m[i]->flags |= PG_CLEAN;
+ m[i]->flags &= ~PG_LAUNDRY;
+ if (i != reqpage) {
+ /*
+ * whether or not to leave the page activated
+ * is up in the air, but we should put the page
+ * on a page queue somewhere. (it already is in
+ * the object).
+ * After some emperical results, it is best
+ * to deactivate the readahead pages.
+ */
+ vm_page_deactivate(m[i]);
+
+ /*
+ * just in case someone was asking for this
+ * page we now tell them that it is ok to use
+ */
+ m[i]->flags &= ~PG_FAKE;
+ PAGE_WAKEUP(m[i]);
+ }
+ }
+ if( swap_pager_full) {
+ _swap_pager_freespace( swp, m[0]->offset+paging_offset, count*NBPG);
+ }
+ } else {
+ swap_pager_ridpages(m, count, reqpage);
+ }
+ }
+ return(rv);
+}
+
+int
+swap_pager_output(swp, m, count, flags, rtvals)
+ register sw_pager_t swp;
+ vm_page_t *m;
+ int count;
+ int flags;
+ int *rtvals;
+{
+ register struct buf *bp;
+ sw_blk_t swb[count];
+ register int s;
+ int i, j, ix;
+ boolean_t rv;
+ vm_offset_t kva, off, foff;
+ swp_clean_t spc;
+ vm_offset_t paging_offset;
+ vm_object_t object;
+ int reqaddr[count];
+ int failed;
- ntoget = (i == SWB_NPAGES) ? SWB_NPAGES : 1;
+/*
+ if( count > 1)
+ printf("off: 0x%x, count: %d\n", m[0]->offset, count);
+*/
+ spc = NULL;
+
+ object = m[0]->object;
+ paging_offset = object->paging_offset;
+
+ failed = 0;
+ for(j=0;j<count;j++) {
+ foff = m[j]->offset + paging_offset;
+ ix = swap_pager_block_index(swp, foff);
+ swb[j] = 0;
+ if( swp->sw_blocks == NULL || ix >= swp->sw_nblocks) {
+ rtvals[j] = VM_PAGER_FAIL;
+ failed = 1;
+ continue;
+ } else {
+ rtvals[j] = VM_PAGER_OK;
+ }
+ swb[j] = &swp->sw_blocks[ix];
+ ++swb[j]->swb_locked;
+ if( failed) {
+ rtvals[j] = VM_PAGER_FAIL;
+ continue;
+ }
+ off = swap_pager_block_offset(swp, foff);
+ reqaddr[j] = swb[j]->swb_block[off];
+ if( reqaddr[j] == SWB_EMPTY) {
+ int blk;
+ int tries;
+ int ntoget;
+ tries = 0;
+ s = splbio();
+
+ /*
+ * if any other pages have been allocated in this block, we
+ * only try to get one page.
+ */
+ for (i = 0; i < SWB_NPAGES; i++) {
+ if (swb[j]->swb_block[i] != SWB_EMPTY)
+ break;
+ }
+
+
+ ntoget = (i == SWB_NPAGES) ? SWB_NPAGES : 1;
+ /*
+ * this code is alittle conservative, but works
+ * (the intent of this code is to allocate small chunks
+ * for small objects)
+ */
+ if( (m[j]->offset == 0) && (ntoget*NBPG > object->size)) {
+ ntoget = (object->size + (NBPG-1))/NBPG;
+ }
+
retrygetspace:
- if (ntoget == SWB_NPAGES &&
- rlist_alloc(&swapmap, btodb(ntoget * NBPG),&blk)) {
- for (i = 0; i < ntoget; i++)
- swb->swb_block[i] = blk + btodb(NBPG) * i;
- } else if (!rlist_alloc(&swapmap, btodb(NBPG), &swb->swb_block[off])) {
+ if (!swap_pager_full && ntoget > 1 &&
+ swap_pager_getswapspace(ntoget * btodb(NBPG), &blk)) {
+
+ for (i = 0; i < ntoget; i++) {
+ swb[j]->swb_block[i] = blk + btodb(NBPG) * i;
+ swb[j]->swb_valid = 0;
+ }
+
+ reqaddr[j] = swb[j]->swb_block[off];
+ } else if (!swap_pager_getswapspace(btodb(NBPG),
+ &swb[j]->swb_block[off])) {
/*
* if the allocation has failed, we try to reclaim space and
* retry.
@@ -1015,74 +1283,183 @@ retrygetspace:
swap_pager_reclaim();
goto retrygetspace;
}
- /*
- * here on swap space full.
- */
- if (spc)
- queue_enter(&swap_pager_free, spc, swp_clean_t, spc_list);
- if (swap_pager_full == 0)
- printf("swap_pager: out of swap space !!!\n");
- swap_pager_full = 1;
- swap_pager_ridpages(m, count, reqpage);
- splx(s);
- return(VM_PAGER_TRYAGAIN);
+ rtvals[j] = VM_PAGER_TRYAGAIN;
+ failed = 1;
+ } else {
+ reqaddr[j] = swb[j]->swb_block[off];
+ swb[j]->swb_valid &= ~(1<<off);
+ }
+ splx(s);
+ }
+ }
+
+ /*
+ * search forwards for the last contiguous page to transfer
+ */
+ failed = 0;
+ for (i = 0; i < count; i++) {
+ if( failed || (reqaddr[i] != reqaddr[0] + i*btodb(NBPG)) ||
+ (reqaddr[i] / dmmax) != (reqaddr[0] / dmmax) ||
+ (rtvals[i] != VM_PAGER_OK)) {
+ failed = 1;
+ if( rtvals[i] == VM_PAGER_OK)
+ rtvals[i] = VM_PAGER_TRYAGAIN;
+ }
+ }
+
+ for(i = 0; i < count; i++) {
+ if( rtvals[i] != VM_PAGER_OK) {
+ if( swb[i])
+ --swb[i]->swb_locked;
+ }
+ }
+
+ for(i = 0; i < count; i++)
+ if( rtvals[i] != VM_PAGER_OK)
+ break;
+
+ if( i == 0) {
+ return VM_PAGER_TRYAGAIN;
+ }
+
+ count = i;
+ for(i=0;i<count;i++) {
+ if( reqaddr[i] == SWB_EMPTY)
+ printf("I/O to empty block????\n");
+ }
+
+ /*
+ */
+
+ /*
+ * For synchronous writes, we clean up
+ * all completed async pageouts.
+ */
+ if ((flags & B_ASYNC) == 0) {
+ swap_pager_clean();
+ }
+
+ kva = 0;
+
+ /*
+ * we allocate a new kva for transfers > 1 page
+ * but for transfers == 1 page, the swap_pager_free list contains
+ * entries that have pre-allocated kva's (for efficiency).
+ */
+ if ( count > 1) {
+ kva = kmem_alloc_pageable(pager_map, count*NBPG);
+ if( !kva) {
+ for (i = 0; i < count; i++) {
+ if( swb[i])
+ --swb[i]->swb_locked;
+ rtvals[i] = VM_PAGER_TRYAGAIN;
+ }
+ return VM_PAGER_TRYAGAIN;
+ }
+ }
+
+ /*
+ * get a swap pager clean data structure, block until we get it
+ */
+ if (queue_empty(&swap_pager_free)) {
+/*
+ if (flags & B_ASYNC) {
+ for(i=0;i<count;i++) {
+ rtvals[i] = VM_PAGER_TRYAGAIN;
+ if( swb[i])
+ --swb[i]->swb_locked;
+ }
+ return VM_PAGER_TRYAGAIN;
+ }
+*/
+
+ s = splbio();
+ if( curproc == pageproc)
+ (void) swap_pager_clean();
+ else
+ wakeup((caddr_t) &vm_pages_needed);
+ while (queue_empty(&swap_pager_free)) {
+ swap_pager_needflags |= SWAP_FREE_NEEDED;
+ tsleep((caddr_t)&swap_pager_free,
+ PVM, "swpfre", 0);
+ if( curproc == pageproc)
+ (void) swap_pager_clean();
+ else
+ wakeup((caddr_t) &vm_pages_needed);
}
splx(s);
- swap_pager_full = 0;
+ }
+
+ queue_remove_first(&swap_pager_free, spc, swp_clean_t, spc_list);
+ if( !kva) {
+ kva = spc->spc_kva;
+ spc->spc_altkva = 0;
+ } else {
+ spc->spc_altkva = kva;
}
/*
* map our page(s) into kva for I/O
*/
for (i = 0; i < count; i++) {
- pmap_enter(vm_map_pmap(pager_map), kva + NBPG * i,
- VM_PAGE_TO_PHYS(m[i]), VM_PROT_ALL, TRUE);
+ pmap_kenter( kva + NBPG * i, VM_PAGE_TO_PHYS(m[i]));
}
-
+ pmap_update();
/*
* get the base I/O offset into the swap file
*/
- off = swap_pager_block_offset(swp, m[0]->offset + paging_offset) / NBPG;
-
-#ifdef DEBUG
- if (flags & B_READ && count > 1)
- printf("obj: 0x%x off: 0x%x poff: 0x%x off: 0x%x, sz: %d blk: %d op: %s\n",
- object, m[0]->offset, paging_offset, off, count, swb->swb_block[off], flags&B_READ?"r":"w");
-#endif
+ for(i=0;i<count;i++) {
+ foff = m[i]->offset + paging_offset;
+ off = swap_pager_block_offset(swp, foff);
+ /*
+ * if we are setting the valid bit anew,
+ * then diminish the swap free space
+ */
+ if( (swb[i]->swb_valid & (1 << off)) == 0)
+ vm_swap_size -= btodb(NBPG);
+
+ /*
+ * set the valid bit
+ */
+ swb[i]->swb_valid |= (1 << off);
+ /*
+ * and unlock the data structure
+ */
+ --swb[i]->swb_locked;
+ }
s = splbio();
/*
* Get a swap buffer header and perform the IO
*/
- if (spc) {
- bp = spc->spc_bp;
- bzero(bp, sizeof *bp);
- bp->b_spc = spc;
- } else {
- bp = getpbuf();
- }
- bp->b_flags = B_BUSY | (flags & B_READ);
+ bp = spc->spc_bp;
+ bzero(bp, sizeof *bp);
+ bp->b_spc = spc;
+
+ bp->b_flags = B_BUSY;
bp->b_proc = &proc0; /* XXX (but without B_PHYS set this is ok) */
bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred;
bp->b_un.b_addr = (caddr_t) kva;
- bp->b_blkno = swb->swb_block[off];
+ bp->b_blkno = reqaddr[0];
VHOLD(swapdev_vp);
bp->b_vp = swapdev_vp;
if (swapdev_vp->v_type == VBLK)
bp->b_dev = swapdev_vp->v_rdev;
bp->b_bcount = NBPG*count;
- if ((bp->b_flags & B_READ) == 0)
- swapdev_vp->v_numoutput++;
+ bp->b_bufsize = NBPG*count;
+ swapdev_vp->v_numoutput++;
/*
* If this is an async write we set up additional buffer fields
* and place a "cleaning" entry on the inuse queue.
*/
- if ((flags & (B_READ|B_ASYNC)) == B_ASYNC) {
+ if ( flags & B_ASYNC ) {
spc->spc_flags = 0;
spc->spc_swp = swp;
- spc->spc_m = m[reqpage];
+ for(i=0;i<count;i++)
+ spc->spc_m[i] = m[i];
+ spc->spc_count = count;
/*
* the completion routine for async writes
*/
@@ -1092,27 +1469,8 @@ retrygetspace:
bp->b_dirtyend = bp->b_bcount;
swp->sw_poip++;
queue_enter(&swap_pager_inuse, spc, swp_clean_t, spc_list);
- /*
- * we remember that we have used a block for paging.
- */
- swb->swb_valid |= (1 << off);
} else {
- /*
- * here for sync write or any read
- */
- if ((flags & B_READ) == 0) {
- /*
- * if we are writing, we remember that we have
- * actually used a block for paging.
- */
- swb->swb_valid |= (1 << off);
- swp->sw_poip++;
- } else {
- swp->sw_piip++;
- }
- /*
- * the completion routine for reads and sync writes
- */
+ swp->sw_poip++;
bp->b_flags |= B_CALL;
bp->b_iodone = swap_pager_iodone1;
}
@@ -1122,40 +1480,31 @@ retrygetspace:
VOP_STRATEGY(bp);
if ((flags & (B_READ|B_ASYNC)) == B_ASYNC ) {
if ((bp->b_flags & B_DONE) == B_DONE) {
- swap_pager_clean(NULL, flags);
+ swap_pager_clean();
}
splx(s);
- return(VM_PAGER_PEND);
+ for(i=0;i<count;i++) {
+ rtvals[i] = VM_PAGER_PEND;
+ }
+ return VM_PAGER_PEND;
}
/*
* wait for the sync I/O to complete
*/
while ((bp->b_flags & B_DONE) == 0) {
- tsleep((caddr_t)bp, PVM, (flags & B_READ)?"swread":"swwrt", 0);
+ tsleep((caddr_t)bp, PVM, "swwrt", 0);
}
rv = (bp->b_flags & B_ERROR) ? VM_PAGER_FAIL : VM_PAGER_OK;
bp->b_flags &= ~(B_BUSY|B_WANTED|B_PHYS|B_DIRTY|B_CALL|B_DONE);
- if (bp->b_flags & B_READ) {
- --swp->sw_piip;
- if (swp->sw_piip == 0)
- wakeup((caddr_t) swp);
- } else {
- --swp->sw_poip;
- if (swp->sw_poip == 0)
- wakeup((caddr_t) swp);
- }
+ --swp->sw_poip;
+ if (swp->sw_poip == 0)
+ wakeup((caddr_t) swp);
if (bp->b_vp)
brelvp(bp);
- /*
- * release the physical I/O buffer
- */
- if (!spc)
- relpbuf(bp);
-
splx(s);
/*
@@ -1167,59 +1516,42 @@ retrygetspace:
* if we have written the page, then indicate that the page
* is clean.
*/
- if ((flags & B_READ) == 0 && rv == VM_PAGER_OK) {
- m[reqpage]->flags |= PG_CLEAN;
- pmap_clear_modify(VM_PAGE_TO_PHYS(m[reqpage]));
- /*
- * optimization, if a page has been read during the
- * pageout process, we activate it.
- */
- if ( (m[reqpage]->flags & PG_ACTIVE) == 0 &&
- pmap_is_referenced(VM_PAGE_TO_PHYS(m[reqpage])))
- vm_page_activate(m[reqpage]);
- }
-
- if (spc) {
- /*
- * if we have used an spc, we need to free it.
- */
- queue_enter(&swap_pager_free, spc, swp_clean_t, spc_list);
- } else {
- for (i = 0; i < count; i++) {
- pmap_clear_modify(VM_PAGE_TO_PHYS(m[i]));
- m[i]->flags |= PG_CLEAN;
- m[i]->flags &= ~PG_LAUNDRY;
- if (i != reqpage) {
+ if (rv == VM_PAGER_OK) {
+ for(i=0;i<count;i++) {
+ if( rtvals[i] == VM_PAGER_OK) {
+ m[i]->flags |= PG_CLEAN;
+ m[i]->flags &= ~PG_LAUNDRY;
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m[i]));
/*
- * whether or not to leave the page activated
- * is up in the air, but we should put the page
- * on a page queue somewhere. (it already is in
- * the object).
- * After some emperical results, it is best
- * to deactivate the readahead pages.
+ * optimization, if a page has been read during the
+ * pageout process, we activate it.
*/
- vm_page_deactivate(m[i]);
-
- /*
- * just in case someone was asking for this
- * page we now tell them that it is ok to use
- */
- m[i]->flags &= ~PG_FAKE;
- PAGE_WAKEUP(m[i]);
+ if ( (m[i]->flags & PG_ACTIVE) == 0 &&
+ pmap_is_referenced(VM_PAGE_TO_PHYS(m[i])))
+ vm_page_activate(m[i]);
}
}
-/*
- * and free the kernel virtual addresses
- */
+ } else {
+ for(i=0;i<count;i++) {
+ rtvals[i] = rv;
+ m[i]->flags |= PG_LAUNDRY;
+ }
+ }
+
+ if( spc->spc_altkva)
kmem_free_wakeup(pager_map, kva, count * NBPG);
+
+ queue_enter(&swap_pager_free, spc, swp_clean_t, spc_list);
+ if (swap_pager_needflags & SWAP_FREE_NEEDED) {
+ swap_pager_needflags &= ~SWAP_FREE_NEEDED;
+ wakeup((caddr_t)&swap_pager_free);
}
+
return(rv);
}
boolean_t
-swap_pager_clean(m, rw)
- vm_page_t m;
- int rw;
+swap_pager_clean()
{
register swp_clean_t spc, tspc;
register int s;
@@ -1235,7 +1567,13 @@ swap_pager_clean(m, rw)
*/
spc = (swp_clean_t) queue_first(&swap_pager_done);
while (!queue_end(&swap_pager_done, (queue_entry_t)spc)) {
- pmap_remove(vm_map_pmap(pager_map), spc->spc_kva, ((vm_offset_t) spc->spc_kva) + NBPG);
+ if( spc->spc_altkva) {
+ pmap_remove(vm_map_pmap(pager_map), spc->spc_altkva, spc->spc_altkva + spc->spc_count * NBPG);
+ kmem_free_wakeup(pager_map, spc->spc_altkva, spc->spc_count * NBPG);
+ spc->spc_altkva = 0;
+ } else {
+ pmap_remove(vm_map_pmap(pager_map), spc->spc_kva, spc->spc_kva + NBPG);
+ }
swap_pager_finish(spc);
queue_remove(&swap_pager_done, spc, swp_clean_t, spc_list);
goto doclean;
@@ -1258,6 +1596,10 @@ doclean:
}
spc->spc_flags = 0;
queue_enter(&swap_pager_free, spc, swp_clean_t, spc_list);
+ if (swap_pager_needflags & SWAP_FREE_NEEDED) {
+ swap_pager_needflags &= ~SWAP_FREE_NEEDED;
+ wakeup((caddr_t)&swap_pager_free);
+ }
++cleandone;
splx(s);
}
@@ -1269,11 +1611,10 @@ void
swap_pager_finish(spc)
register swp_clean_t spc;
{
- vm_page_t m = spc->spc_m;
- vm_object_t object = m->object;
- extern int vm_pageout_free_min;
+ vm_object_t object = spc->spc_m[0]->object;
+ int i;
- if (--object->paging_in_progress == 0)
+ if ((object->paging_in_progress -= spc->spc_count) == 0)
thread_wakeup((int) object);
/*
@@ -1282,36 +1623,27 @@ swap_pager_finish(spc)
* (XXX could get stuck doing this, should give up after awhile)
*/
if (spc->spc_flags & SPC_ERROR) {
- printf("swap_pager_finish: clean of page %x failed\n",
- VM_PAGE_TO_PHYS(m));
- m->flags |= PG_LAUNDRY;
+ for(i=0;i<spc->spc_count;i++) {
+ printf("swap_pager_finish: clean of page %x failed\n",
+ VM_PAGE_TO_PHYS(spc->spc_m[i]));
+ spc->spc_m[i]->flags |= PG_LAUNDRY;
+ }
} else {
- pmap_clear_modify(VM_PAGE_TO_PHYS(m));
- m->flags |= PG_CLEAN;
+ for(i=0;i<spc->spc_count;i++) {
+ pmap_clear_modify(VM_PAGE_TO_PHYS(spc->spc_m[i]));
+ spc->spc_m[i]->flags |= PG_CLEAN;
+ }
}
- /*
- * if a page has been read during pageout, then
- * we activate the page.
- */
- if ((m->flags & PG_ACTIVE) == 0 &&
- pmap_is_referenced(VM_PAGE_TO_PHYS(m)))
- vm_page_activate(m);
- /*
- * we wakeup any processes that are waiting on
- * this page.
- */
- PAGE_WAKEUP(m);
- /*
- * if we need memory desperately, then free it now
- */
- if (vm_page_free_count < vm_page_free_reserved &&
- (m->flags & PG_CLEAN) && m->wire_count == 0) {
- pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_NONE);
- vm_page_free(m);
+ for(i=0;i<spc->spc_count;i++) {
+ /*
+ * we wakeup any processes that are waiting on
+ * these pages.
+ */
+ PAGE_WAKEUP(spc->spc_m[i]);
}
- --nswiodone;
+ nswiodone -= spc->spc_count;
return;
}
@@ -1324,7 +1656,6 @@ swap_pager_iodone(bp)
register struct buf *bp;
{
register swp_clean_t spc;
- daddr_t blk;
int s;
s = splbio();
@@ -1337,15 +1668,17 @@ swap_pager_iodone(bp)
bp->b_error, bp->b_blkno, bp->b_bcount);
}
+/*
if ((bp->b_flags & B_READ) == 0)
vwakeup(bp);
+*/
bp->b_flags &= ~(B_BUSY|B_WANTED|B_PHYS|B_DIRTY|B_ASYNC);
if (bp->b_vp) {
brelvp(bp);
}
- nswiodone++;
+ nswiodone += spc->spc_count;
if (--spc->spc_swp->sw_poip == 0) {
wakeup((caddr_t)spc->spc_swp);
}
@@ -1393,6 +1726,27 @@ getpbuf() {
}
/*
+ * allocate a physical buffer, if one is available
+ */
+struct buf *
+trypbuf() {
+ int s;
+ struct buf *bp;
+
+ s = splbio();
+ if( bswlist.av_forw == NULL) {
+ splx(s);
+ return NULL;
+ }
+ bp = bswlist.av_forw;
+ bswlist.av_forw = bp->av_forw;
+ splx(s);
+
+ bzero(bp, sizeof *bp);
+ return bp;
+}
+
+/*
* release a physical buffer
*/
void
@@ -1416,8 +1770,8 @@ relpbuf(bp)
*/
int
swap_pager_ready() {
- if( queue_empty( &swap_pager_free))
- return 0;
- else
+ if( !queue_empty( &swap_pager_free))
return 1;
+ else
+ return 0;
}
diff --git a/sys/vm/swap_pager.h b/sys/vm/swap_pager.h
index e505e436d1ce..853edd5d1b16 100644
--- a/sys/vm/swap_pager.h
+++ b/sys/vm/swap_pager.h
@@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* from: @(#)swap_pager.h 7.1 (Berkeley) 12/5/90
- * $Id: swap_pager.h,v 1.7 1994/01/17 09:33:25 davidg Exp $
+ * $Id: swap_pager.h,v 1.9 1994/03/14 21:54:23 davidg Exp $
*/
/*
@@ -48,7 +48,7 @@
#define _SWAP_PAGER_ 1
/*
- * SWB_NPAGES can be set to any value from 1 to 32 pages per allocation,
+ * SWB_NPAGES can be set to any value from 1 to 16 pages per allocation,
* however, due to the allocation spilling into non-swap pager backed memory,
* suggest keeping SWB_NPAGES small (1-4). If high performance is manditory
* perhaps up to 8 pages might be in order????
@@ -57,7 +57,8 @@
*/
#define SWB_NPAGES 8
struct swblock {
- unsigned int swb_valid; /* bitmask for valid pages */
+ unsigned short swb_valid; /* bitmask for valid pages */
+ unsigned short swb_locked; /* block locked */
int swb_block[SWB_NPAGES]; /* unfortunately int instead of daddr_t */
};
typedef struct swblock *sw_blk_t;
@@ -89,7 +90,7 @@ boolean_t swap_pager_getmulti(vm_pager_t, vm_page_t *, int, int, boolean_t);
boolean_t swap_pager_haspage(vm_pager_t, vm_offset_t);
int swap_pager_io(sw_pager_t, vm_page_t *, int, int, int);
void swap_pager_iodone(struct buf *);
-boolean_t swap_pager_clean(vm_page_t, int);
+boolean_t swap_pager_clean();
extern struct pagerops swappagerops;
diff --git a/sys/vm/vm_fault.c b/sys/vm/vm_fault.c
index c7254bce4c51..1cf99e068e9e 100644
--- a/sys/vm/vm_fault.c
+++ b/sys/vm/vm_fault.c
@@ -66,7 +66,7 @@
* rights to redistribute these changes.
*/
/*
- * $Id: vm_fault.c,v 1.14.2.1 1994/03/24 07:20:29 rgrimes Exp $
+ * $Id: vm_fault.c,v 1.18 1994/05/25 11:06:49 davidg Exp $
*/
/*
@@ -82,9 +82,9 @@
#include "resource.h"
#include "resourcevar.h"
-#define VM_FAULT_READ_AHEAD 3
+#define VM_FAULT_READ_AHEAD 4
#define VM_FAULT_READ_AHEAD_MIN 1
-#define VM_FAULT_READ_BEHIND 2
+#define VM_FAULT_READ_BEHIND 3
#define VM_FAULT_READ (VM_FAULT_READ_AHEAD+VM_FAULT_READ_BEHIND+1)
extern int swap_pager_full;
extern int vm_pageout_proc_limit;
@@ -133,6 +133,7 @@ vm_fault(map, vaddr, fault_type, change_wiring)
vm_page_t marray[VM_FAULT_READ];
int reqpage;
int spl;
+ int hardfault=0;
vm_stat.faults++; /* needs lock XXX */
/*
@@ -284,13 +285,12 @@ vm_fault(map, vaddr, fault_type, change_wiring)
*/
vm_page_lock_queues();
- spl = vm_disable_intr();
+ spl = splimp();
if (m->flags & PG_INACTIVE) {
queue_remove(&vm_page_queue_inactive, m,
vm_page_t, pageq);
m->flags &= ~PG_INACTIVE;
vm_page_inactive_count--;
- vm_stat.reactivations++;
}
if (m->flags & PG_ACTIVE) {
@@ -299,7 +299,7 @@ vm_fault(map, vaddr, fault_type, change_wiring)
m->flags &= ~PG_ACTIVE;
vm_page_active_count--;
}
- vm_set_intr(spl);
+ splx(spl);
vm_page_unlock_queues();
/*
@@ -328,10 +328,11 @@ vm_fault(map, vaddr, fault_type, change_wiring)
(object->pager && object->pager->pg_type == PG_SWAP &&
!vm_pager_has_page(object->pager, offset+object->paging_offset)))) {
if (vaddr < VM_MAXUSER_ADDRESS && curproc && curproc->p_pid >= 48) /* XXX */ {
- UNLOCK_AND_DEALLOCATE;
printf("Process %d killed by vm_fault -- out of swap\n", curproc->p_pid);
psignal(curproc, SIGKILL);
- return KERN_RESOURCE_SHORTAGE;
+ curproc->p_cpu = 0;
+ curproc->p_nice = PRIO_MIN;
+ setpri(curproc);
}
}
@@ -403,6 +404,7 @@ vm_fault(map, vaddr, fault_type, change_wiring)
vm_stat.pageins++;
m->flags &= ~PG_FAKE;
pmap_clear_modify(VM_PAGE_TO_PHYS(m));
+ hardfault++;
break;
}
@@ -895,8 +897,16 @@ vm_fault(map, vaddr, fault_type, change_wiring)
}
else {
vm_page_activate(m);
- vm_pageout_deact_bump(m);
}
+
+ if( curproc && curproc->p_stats) {
+ if (hardfault) {
+ curproc->p_stats->p_ru.ru_majflt++;
+ } else {
+ curproc->p_stats->p_ru.ru_minflt++;
+ }
+ }
+
vm_page_unlock_queues();
/*
diff --git a/sys/vm/vm_glue.c b/sys/vm/vm_glue.c
index bd2fd07a5445..482f968f652a 100644
--- a/sys/vm/vm_glue.c
+++ b/sys/vm/vm_glue.c
@@ -75,6 +75,7 @@
#include "vm_page.h"
#include "vm_kern.h"
#include "machine/stdarg.h"
+#include "machine/vmparam.h"
extern char kstack[];
int avefree = 0; /* XXX */
@@ -115,19 +116,16 @@ useracc(addr, len, rw)
vm_prot_t prot = rw == B_READ ? VM_PROT_READ : VM_PROT_WRITE;
/*
- * XXX - specially disallow access to user page tables - they are
- * in the map.
- *
- * XXX - don't specially disallow access to the user area - treat
- * it as incorrectly as elsewhere.
+ * XXX - check separately to disallow access to user area and user
+ * page tables - they are in the map.
*
* XXX - VM_MAXUSER_ADDRESS is an end address, not a max. It was
- * only used (as an end address) in trap.c. Use it as an end
- * address here too.
+ * once only used (as an end address) in trap.c. Use it as an end
+ * address here too. This bogusness has spread. I just fixed
+ * where it was used as a max in vm_mmap.c.
*/
- if ((vm_offset_t) addr >= VM_MAXUSER_ADDRESS
- || (vm_offset_t) addr + len > VM_MAXUSER_ADDRESS
- || (vm_offset_t) addr + len <= (vm_offset_t) addr) {
+ if ((vm_offset_t) addr + len > /* XXX */ VM_MAXUSER_ADDRESS
+ || (vm_offset_t) addr + len < (vm_offset_t) addr) {
printf("address wrap\n");
return (FALSE);
}
@@ -213,7 +211,6 @@ vm_fork(p1, p2, isvfork)
* Allocate a wired-down (for now) pcb and kernel stack for the process
*/
- /* addr = UPT_MIN_ADDRESS - UPAGES*NBPG; */
addr = (vm_offset_t) kstack;
vp = &p2->p_vmspace->vm_map;
@@ -281,23 +278,24 @@ void
vm_init_limits(p)
register struct proc *p;
{
- int tmp;
+ int rss_limit;
/*
* Set up the initial limits on process VM.
- * Set the maximum resident set size to be all
- * of (reasonably) available memory. This causes
- * any single, large process to start random page
- * replacement once it fills memory.
+ * Set the maximum resident set size to be half
+ * of (reasonably) available memory. Since this
+ * is a soft limit, it comes into effect only
+ * when the system is out of memory - half of
+ * main memory helps to favor smaller processes,
+ * and reduces thrashing of the object cache.
*/
p->p_rlimit[RLIMIT_STACK].rlim_cur = DFLSSIZ;
p->p_rlimit[RLIMIT_STACK].rlim_max = MAXSSIZ;
p->p_rlimit[RLIMIT_DATA].rlim_cur = DFLDSIZ;
p->p_rlimit[RLIMIT_DATA].rlim_max = MAXDSIZ;
- tmp = ((2 * vm_page_free_count) / 3) - 32;
- if (vm_page_free_count < 512)
- tmp = vm_page_free_count;
- p->p_rlimit[RLIMIT_RSS].rlim_cur = ptoa(tmp);
+ /* limit the limit to no less than 128K */
+ rss_limit = max(vm_page_free_count / 2, 32);
+ p->p_rlimit[RLIMIT_RSS].rlim_cur = ptoa(rss_limit);
p->p_rlimit[RLIMIT_RSS].rlim_max = RLIM_INFINITY;
}
@@ -425,7 +423,7 @@ noswap:
(void) splhigh();
if (((vm_page_free_count + vm_page_inactive_count) >=
(vm_page_inactive_target + vm_page_free_reserved)) ||
- (vm_page_free_count >= vm_page_free_min)) {
+ (vm_page_free_count > vm_page_free_reserved)) {
spl0();
faultin(p);
p->p_time = 0;
@@ -485,8 +483,6 @@ swapout_threads()
continue;
switch (p->p_stat) {
case SRUN:
- if (p->p_pri < PUSER)
- continue;
if ((tpri = p->p_time + p->p_nice * 8) > outpri2) {
outp2 = p;
outpri2 = tpri;
@@ -495,7 +491,7 @@ swapout_threads()
case SSLEEP:
case SSTOP:
- if (p->p_pri <= PRIBIO)
+ if (p->p_pri <= PVM)
continue;
if (p->p_slptime > maxslp) {
swapout(p);
@@ -511,12 +507,12 @@ swapout_threads()
* If we didn't get rid of any real duds, toss out the next most
* likely sleeping/stopped or running candidate. We only do this
* if we are real low on memory since we don't gain much by doing
- * it (UPAGES pages).
+ * it (UPAGES+1 pages).
*/
if (didswap == 0 && (swapinreq &&
- vm_page_free_count <= vm_pageout_free_min)) {
+ (vm_page_free_count + vm_page_inactive_count) <= (vm_page_free_min + vm_page_inactive_target))) {
if ((p = outp) == 0 &&
- (vm_page_free_count <= vm_pageout_free_min))
+ (vm_page_free_count <= vm_page_free_reserved))
p = outp2;
#ifdef DEBUG
if (swapdebug & SDB_SWAPOUT)
@@ -552,6 +548,8 @@ swapout(p)
p->p_slptime, vm_page_free_count);
#endif
+ ++p->p_stats->p_ru.ru_nswap;
+
(void) splhigh();
p->p_flag &= ~SLOAD;
diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c
index e6759a756844..0a753040bf4e 100644
--- a/sys/vm/vm_mmap.c
+++ b/sys/vm/vm_mmap.c
@@ -37,7 +37,7 @@
*
* from: Utah $Hdr: vm_mmap.c 1.3 90/01/21$
* from: @(#)vm_mmap.c 7.5 (Berkeley) 6/28/91
- * $Id: vm_mmap.c,v 1.21 1994/01/31 04:20:26 davidg Exp $
+ * $Id: vm_mmap.c,v 1.23 1994/06/22 05:53:10 jkh Exp $
*/
/*
@@ -59,6 +59,7 @@
#include "vm_prot.h"
#include "vm_statistics.h"
#include "vm_user.h"
+#include "vm_page.h"
static boolean_t vm_map_is_allocated(vm_map_t, vm_offset_t, vm_offset_t,
boolean_t);
@@ -181,9 +182,7 @@ smmap(p, uap, retval)
/*
* Check address range for validity
*/
- if (addr + size >= VM_MAXUSER_ADDRESS)
- return(EINVAL);
- if (addr > addr + size)
+ if (addr + size > /* XXX */ VM_MAXUSER_ADDRESS || addr + size < addr)
return(EINVAL);
/*
@@ -255,8 +254,8 @@ smmap(p, uap, retval)
handle = NULL;
}
- error = vm_mmap(&p->p_vmspace->vm_map, &addr, size, prot, maxprot,
- flags, handle, (vm_offset_t)uap->pos);
+ error = vm_mmap(&p->p_vmspace->vm_map, &addr, size, prot,
+ maxprot, flags, handle, (vm_offset_t)uap->pos);
if (error == 0)
*retval = (int) addr;
return(error);
@@ -363,9 +362,7 @@ munmap(p, uap, retval)
size = (vm_size_t) round_page(uap->len);
if (size == 0)
return(0);
- if (addr + size >= VM_MAXUSER_ADDRESS)
- return(EINVAL);
- if (addr >= addr + size)
+ if (addr + size > /* XXX */ VM_MAXUSER_ADDRESS || addr + size < addr)
return(EINVAL);
if (!vm_map_is_allocated(&p->p_vmspace->vm_map, addr, addr+size,
FALSE))
@@ -475,6 +472,7 @@ mincore(p, uap, retval)
return (EOPNOTSUPP);
}
+void pmap_object_init_pt();
/*
* Internal version of mmap.
* Currently used by mmap, exec, and sys5 shared memory.
@@ -493,9 +491,9 @@ vm_mmap(map, addr, size, prot, maxprot, flags, handle, foff)
caddr_t handle; /* XXX should be vp */
vm_offset_t foff;
{
- register vm_pager_t pager;
+ register vm_pager_t pager = 0;
boolean_t fitit;
- vm_object_t object;
+ vm_object_t object = 0;
struct vnode *vp = 0;
int type;
int rv = KERN_SUCCESS;
@@ -508,7 +506,10 @@ vm_mmap(map, addr, size, prot, maxprot, flags, handle, foff)
*addr = round_page(*addr);
} else {
fitit = FALSE;
- (void) vm_deallocate(map, *addr, size);
+ /*
+ * Defer deallocating address space until
+ * after a reference is gained to the object
+ */
}
/*
@@ -516,48 +517,64 @@ vm_mmap(map, addr, size, prot, maxprot, flags, handle, foff)
* gain a reference to ensure continued existance of the object.
* (XXX the exception is to appease the pageout daemon)
*/
- if ((flags & MAP_TYPE) == MAP_ANON)
- type = PG_DFLT;
- else {
- vp = (struct vnode *)handle;
- if (vp->v_type == VCHR) {
- type = PG_DEVICE;
- handle = (caddr_t)(u_long)vp->v_rdev;
- } else
- type = PG_VNODE;
- }
- pager = vm_pager_allocate(type, handle, size, prot, foff);
- if (pager == NULL)
- return (type == PG_DEVICE ? EINVAL : ENOMEM);
+ if ((((flags & MAP_TYPE) != MAP_ANON) || (handle != NULL))) {
+ if ((flags & MAP_TYPE) == MAP_ANON)
+ type = PG_DFLT;
+ else {
+ vp = (struct vnode *)handle;
+ if (vp->v_type == VCHR) {
+ type = PG_DEVICE;
+ handle = (caddr_t)(u_long)vp->v_rdev;
+ } else
+ type = PG_VNODE;
+ }
+ pager = vm_pager_allocate(type, handle, size, prot, foff);
+ if (pager == NULL) {
+ /* on failure, don't leave anything mapped */
+ if (!fitit)
+ (void) vm_deallocate(map, *addr, size);
+ return (type == PG_DEVICE ? EINVAL : ENOMEM);
+ }
/*
* Find object and release extra reference gained by lookup
*/
- object = vm_object_lookup(pager);
- vm_object_deallocate(object);
+ object = vm_object_lookup(pager);
+ vm_object_deallocate(object);
+ }
+
+ /* blow away anything that might be mapped here already */
+ if (!fitit)
+ (void) vm_deallocate(map, *addr, size);
/*
* Anonymous memory.
*/
- if ((flags & MAP_TYPE) == MAP_ANON) {
- rv = vm_allocate_with_pager(map, addr, size, fitit,
+ if (((flags & MAP_TYPE) == MAP_ANON)) {
+ if (handle != NULL) {
+ rv = vm_allocate_with_pager(map, addr, size, fitit,
pager, (vm_offset_t)foff, TRUE);
- if (rv != KERN_SUCCESS) {
- if (handle == NULL)
- vm_pager_deallocate(pager);
- else
+ if (rv != KERN_SUCCESS) {
vm_object_deallocate(object);
- goto out;
- }
+ goto out;
+ }
+#if 1
/*
* Don't cache anonymous objects.
* Loses the reference gained by vm_pager_allocate.
*/
- (void) pager_cache(object, FALSE);
+ (void) pager_cache(object, FALSE);
+#endif
#ifdef DEBUG
- if (mmapdebug & MDB_MAPIT)
- printf("vm_mmap(%d): ANON *addr %x size %x pager %x\n",
- curproc->p_pid, *addr, size, pager);
+ if (mmapdebug & MDB_MAPIT)
+ printf("vm_mmap(%d): ANON *addr %x size %x pager %x\n",
+ curproc->p_pid, *addr, size, pager);
#endif
+ } else {
+ rv = vm_map_find(map, NULL, (vm_offset_t) 0, addr, size, fitit);
+ if(rv != KERN_SUCCESS) {
+ goto out;
+ }
+ }
}
/*
* Must be type MAP_FILE.
@@ -610,6 +627,9 @@ vm_mmap(map, addr, size, prot, maxprot, flags, handle, foff)
pager_cache(object, FALSE);
else
vm_object_deallocate(object);
+
+ if( map->pmap)
+ pmap_object_init_pt(map->pmap, *addr, object, foff, size);
}
/*
* Copy-on-write of file. Two flavors.
@@ -683,6 +703,8 @@ vm_mmap(map, addr, size, prot, maxprot, flags, handle, foff)
*/
vm_object_pmap_copy(object, (vm_offset_t)foff,
(vm_offset_t)foff+size);
+ if( map->pmap)
+ pmap_object_init_pt(map->pmap, *addr, object, foff, size);
vm_object_deallocate(object);
vm_map_deallocate(tmap);
if (rv != KERN_SUCCESS)
@@ -866,6 +888,7 @@ vm_allocate_with_pager(map, addr, size, fitit, pager, poffset, internal)
vm_stat.lookups++;
if (object == NULL) {
object = vm_object_allocate(size);
+ /* don't put internal objects in the hash table */
if (!internal)
vm_object_enter(object, pager);
} else
diff --git a/sys/vm/vm_object.c b/sys/vm/vm_object.c
index 5c463f64d6c5..3dea7601b5ba 100644
--- a/sys/vm/vm_object.c
+++ b/sys/vm/vm_object.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)vm_object.c 7.4 (Berkeley) 5/7/91
- * $Id: vm_object.c,v 1.21.2.1 1994/03/07 02:22:13 rgrimes Exp $
+ * $Id: vm_object.c,v 1.25 1994/04/14 07:50:21 davidg Exp $
*
*
* Copyright (c) 1987, 1990 Carnegie-Mellon University.
@@ -401,7 +401,7 @@ vm_object_terminate(object)
VM_PAGE_CHECK(p);
vm_page_lock_queues();
- s = vm_disable_intr();
+ s = splimp();
if (p->flags & PG_ACTIVE) {
queue_remove(&vm_page_queue_active, p, vm_page_t,
pageq);
@@ -415,7 +415,7 @@ vm_object_terminate(object)
p->flags &= ~PG_INACTIVE;
vm_page_inactive_count--;
}
- vm_set_intr(s);
+ splx(s);
vm_page_unlock_queues();
p = (vm_page_t) queue_next(&p->listq);
}
@@ -514,15 +514,7 @@ again:
vm_page_deactivate(p);
if ((p->flags & PG_CLEAN) == 0) {
- p->flags |= PG_BUSY;
- object->paging_in_progress++;
- vm_object_unlock(object);
- (void) vm_pager_put(object->pager, p, TRUE);
- vm_object_lock(object);
- object->paging_in_progress--;
- if (object->paging_in_progress == 0)
- wakeup((caddr_t) object);
- PAGE_WAKEUP(p);
+ vm_pageout_clean(p,1);
goto again;
}
}
@@ -551,7 +543,7 @@ vm_object_deactivate_pages(object)
next = (vm_page_t) queue_next(&p->listq);
vm_page_lock_queues();
if ((p->flags & (PG_INACTIVE|PG_BUSY)) == 0 &&
- p->wire_count == 0)
+ (p->wire_count == 0 && p->hold_count == 0))
vm_page_deactivate(p); /* optimisation from mach 3.0 -
* andrew@werple.apana.org.au,
* Feb '93
@@ -658,14 +650,14 @@ vm_object_pmap_copy(object, start, end)
register vm_page_t p;
vm_offset_t amount;
+ if (object == NULL)
+ return;
+
start = trunc_page(start);
end = round_page(end);
amount = ((end - start) + PAGE_SIZE - 1) / PAGE_SIZE;
- if (object == NULL)
- return;
-
vm_object_lock(object);
p = (vm_page_t) queue_first(&object->memq);
while (!queue_end(&object->memq, (queue_entry_t) p)) {
diff --git a/sys/vm/vm_object.h b/sys/vm/vm_object.h
index 60e7677b27ed..b1b82bcda8e9 100644
--- a/sys/vm/vm_object.h
+++ b/sys/vm/vm_object.h
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)vm_object.h 7.3 (Berkeley) 4/21/91
- * $Id: vm_object.h,v 1.6 1994/01/14 16:27:25 davidg Exp $
+ * $Id: vm_object.h,v 1.7 1994/03/14 21:54:27 davidg Exp $
*/
/*
@@ -100,7 +100,8 @@ struct vm_object {
/* Paging (in or out) - don't
collapse or destroy */
/* boolean_t */ can_persist:1, /* allow to persist */
- /* boolean_t */ internal:1; /* internally created object */
+ /* boolean_t */ internal:1, /* internally created object */
+ read_only:1; /* entire obj is read only */
queue_chain_t cached_list; /* for persistence */
};
diff --git a/sys/vm/vm_page.c b/sys/vm/vm_page.c
index 31a99382a7d6..409438163be6 100644
--- a/sys/vm/vm_page.c
+++ b/sys/vm/vm_page.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)vm_page.c 7.4 (Berkeley) 5/7/91
- * $Id: vm_page.c,v 1.12 1994/02/09 07:03:10 davidg Exp $
+ * $Id: vm_page.c,v 1.18 1994/06/17 13:29:13 davidg Exp $
*/
/*
@@ -311,6 +311,7 @@ vm_page_startup(starta, enda, vaddr)
m->flags = 0;
m->object = 0;
m->phys_addr = pa;
+ m->hold_count = 0;
queue_enter(&vm_page_queue_free, m, vm_page_t, pageq);
pa += PAGE_SIZE;
}
@@ -469,7 +470,7 @@ vm_page_lookup(object, offset)
*/
bucket = &vm_page_buckets[vm_page_hash(object, offset)];
- spl = vm_disable_intr();
+ spl = splimp();
simple_lock(&bucket_lock);
mem = (vm_page_t) queue_first(bucket);
@@ -477,14 +478,14 @@ vm_page_lookup(object, offset)
VM_PAGE_CHECK(mem);
if ((mem->object == object) && (mem->offset == offset)) {
simple_unlock(&bucket_lock);
- vm_set_intr(spl);
+ splx(spl);
return(mem);
}
mem = (vm_page_t) queue_next(&mem->hashq);
}
simple_unlock(&bucket_lock);
- vm_set_intr(spl);
+ splx(spl);
return(NULL);
}
@@ -508,10 +509,10 @@ vm_page_rename(mem, new_object, new_offset)
vm_page_lock_queues(); /* keep page from moving out from
under pageout daemon */
- spl = vm_disable_intr();
+ spl = splimp();
vm_page_remove(mem);
vm_page_insert(mem, new_object, new_offset);
- vm_set_intr(spl);
+ splx(spl);
vm_page_unlock_queues();
}
@@ -531,7 +532,7 @@ vm_page_alloc(object, offset)
register vm_page_t mem;
int spl;
- spl = vm_disable_intr();
+ spl = splimp();
simple_lock(&vm_page_queue_free_lock);
if ( object != kernel_object &&
object != kmem_object &&
@@ -539,7 +540,7 @@ vm_page_alloc(object, offset)
vm_page_free_count < vm_page_free_reserved) {
simple_unlock(&vm_page_queue_free_lock);
- vm_set_intr(spl);
+ splx(spl);
/*
* this wakeup seems unnecessary, but there is code that
* might just check to see if there are free pages, and
@@ -552,7 +553,7 @@ vm_page_alloc(object, offset)
}
if (queue_empty(&vm_page_queue_free)) {
simple_unlock(&vm_page_queue_free_lock);
- vm_set_intr(spl);
+ splx(spl);
/*
* comment above re: wakeups applies here too...
*/
@@ -569,8 +570,9 @@ vm_page_alloc(object, offset)
mem->flags = PG_BUSY|PG_CLEAN|PG_FAKE;
vm_page_insert(mem, object, offset);
mem->wire_count = 0;
- mem->deact = 0;
- vm_set_intr(spl);
+ mem->hold_count = 0;
+ mem->act_count = 0;
+ splx(spl);
/*
* don't wakeup too often, so we wakeup the pageout daemon when
@@ -597,10 +599,9 @@ vm_page_free(mem)
{
int spl;
- spl = vm_disable_intr();
+ spl = splimp();
vm_page_remove(mem);
- mem->deact = 0;
if (mem->flags & PG_ACTIVE) {
queue_remove(&vm_page_queue_active, mem, vm_page_t, pageq);
mem->flags &= ~PG_ACTIVE;
@@ -624,7 +625,7 @@ vm_page_free(mem)
vm_page_free_count++;
simple_unlock(&vm_page_queue_free_lock);
- vm_set_intr(spl);
+ splx(spl);
/*
* if pageout daemon needs pages, then tell it that there
@@ -650,7 +651,7 @@ vm_page_free(mem)
}
} else {
- vm_set_intr(spl);
+ splx(spl);
}
wakeup((caddr_t) mem);
}
@@ -670,7 +671,7 @@ vm_page_wire(mem)
{
int spl;
VM_PAGE_CHECK(mem);
- spl = vm_disable_intr();
+ spl = splimp();
if (mem->wire_count == 0) {
if (mem->flags & PG_ACTIVE) {
@@ -688,7 +689,7 @@ vm_page_wire(mem)
vm_page_wire_count++;
}
mem->wire_count++;
- vm_set_intr(spl);
+ splx(spl);
}
/*
@@ -706,7 +707,7 @@ vm_page_unwire(mem)
int spl;
VM_PAGE_CHECK(mem);
- spl = vm_disable_intr();
+ spl = splimp();
if (mem->wire_count != 0)
mem->wire_count--;
if (mem->wire_count == 0) {
@@ -714,9 +715,8 @@ vm_page_unwire(mem)
vm_page_active_count++;
mem->flags |= PG_ACTIVE;
vm_page_wire_count--;
- vm_pageout_deact_bump(mem);
}
- vm_set_intr(spl);
+ splx(spl);
}
/*
@@ -745,9 +745,8 @@ vm_page_deactivate(m)
* Paul Mackerras (paulus@cs.anu.edu.au) 9-Jan-93.
*/
- spl = splhigh();
- m->deact = 0;
- if (!(m->flags & PG_INACTIVE) && m->wire_count == 0) {
+ spl = splimp();
+ if (!(m->flags & PG_INACTIVE) && m->wire_count == 0 && m->hold_count == 0) {
pmap_clear_reference(VM_PAGE_TO_PHYS(m));
if (m->flags & PG_ACTIVE) {
queue_remove(&vm_page_queue_active, m, vm_page_t, pageq);
@@ -757,7 +756,13 @@ vm_page_deactivate(m)
queue_enter(&vm_page_queue_inactive, m, vm_page_t, pageq);
m->flags |= PG_INACTIVE;
vm_page_inactive_count++;
+#define NOT_DEACTIVATE_PROTECTS
+#ifndef NOT_DEACTIVATE_PROTECTS
pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_NONE);
+#else
+ if (pmap_is_modified(VM_PAGE_TO_PHYS(m)))
+ m->flags &= ~PG_CLEAN;
+#endif
if ((m->flags & PG_CLEAN) == 0)
m->flags |= PG_LAUNDRY;
}
@@ -789,32 +794,42 @@ void
vm_page_activate(m)
register vm_page_t m;
{
- int spl;
+ int spl, target, shortage, maxscan;
+ vm_page_t actm, next;
+
VM_PAGE_CHECK(m);
- vm_pageout_deact_bump(m);
+ spl = splimp();
- spl = vm_disable_intr();
+ if (m->wire_count) {
+ splx(spl);
+ return;
+ }
+
+ if ((m->flags & (PG_INACTIVE|PG_ACTIVE)) ==
+ (PG_INACTIVE|PG_ACTIVE)) {
+ panic("vm_page_activate: on both queues?");
+ }
if (m->flags & PG_INACTIVE) {
- queue_remove(&vm_page_queue_inactive, m, vm_page_t,
- pageq);
+ queue_remove(&vm_page_queue_inactive, m, vm_page_t, pageq);
vm_page_inactive_count--;
m->flags &= ~PG_INACTIVE;
+ vm_stat.reactivations++;
}
- if (m->wire_count == 0) {
- if (m->flags & PG_ACTIVE)
- panic("vm_page_activate: already active");
-
- m->flags |= PG_ACTIVE;
- queue_enter(&vm_page_queue_active, m, vm_page_t, pageq);
- queue_remove(&m->object->memq, m, vm_page_t, listq);
- queue_enter(&m->object->memq, m, vm_page_t, listq);
- vm_page_active_count++;
- }
+ if (m->flags & PG_ACTIVE)
+ panic("vm_page_activate: already active");
+
+ m->flags |= PG_ACTIVE;
+ queue_enter(&vm_page_queue_active, m, vm_page_t, pageq);
+ queue_remove(&m->object->memq, m, vm_page_t, listq);
+ queue_enter(&m->object->memq, m, vm_page_t, listq);
+ vm_page_active_count++;
+ /* m->act_count = 10; */
+ m->act_count = 1;
- vm_set_intr(spl);
+ splx(spl);
}
/*
diff --git a/sys/vm/vm_page.h b/sys/vm/vm_page.h
index 33ada305990b..5a3be88adf23 100644
--- a/sys/vm/vm_page.h
+++ b/sys/vm/vm_page.h
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)vm_page.h 7.3 (Berkeley) 4/21/91
- * $Id: vm_page.h,v 1.8 1994/01/31 04:21:19 davidg Exp $
+ * $Id: vm_page.h,v 1.14 1994/04/14 07:50:24 davidg Exp $
*/
/*
@@ -71,6 +71,9 @@
#ifndef _VM_PAGE_
#define _VM_PAGE_
+#ifdef KERNEL
+#include <systm.h>
+#endif
/*
* Management of resident (logical) pages.
*
@@ -121,18 +124,14 @@ struct vm_page {
unsigned int wire_count; /* how many wired down maps use me? */
unsigned short flags; /* bit encoded flags */
- unsigned short deact; /* deactivation count */
+ unsigned short act_count; /* active count */
+ int hold_count; /* page hold count -- don't pageout */
vm_offset_t phys_addr; /* physical address of page */
};
typedef struct vm_page *vm_page_t;
-#define DEACT_START 5
-#define DEACT_DELAY 2
-#define DEACT_CLEAN 1
-#define DEACT_FREE 0
-
#if VM_PAGE_DEBUG
#define VM_PAGE_CHECK(mem) { \
if ((((unsigned int) mem) < ((unsigned int) &vm_page_array[0])) || \
@@ -226,10 +225,10 @@ void vm_page_replace();
boolean_t vm_page_zero_fill();
void vm_page_copy();
-
+#if 0
void vm_page_wire();
void vm_page_unwire();
-
+#endif
/*
* Functions implemented as macros
@@ -272,12 +271,25 @@ extern vm_offset_t pmap_phys_ddress(int);
/*
- * these macros are *MUCH* faster on a 386/486 type machine
- * eventually they need to be implemented correctly and put
- * somewhere in the machine dependant stuff.
+ * Keep page from being freed by the page daemon
+ * much of the same effect as wiring, except much lower
+ * overhead and should be used only for *very* temporary
+ * holding ("wiring").
*/
-#define vm_disable_intr() (disable_intr(), 0)
-#define vm_set_intr(spl) enable_intr()
+static inline void
+vm_page_hold(mem)
+ vm_page_t mem;
+{
+ mem->hold_count++;
+}
+
+static inline void
+vm_page_unhold(mem)
+ vm_page_t mem;
+{
+ if( --mem->hold_count < 0)
+ panic("vm_page_unhold: hold count < 0!!!");
+}
#endif /* KERNEL */
#endif /* _VM_PAGE_ */
diff --git a/sys/vm/vm_pageout.c b/sys/vm/vm_pageout.c
index 229a4090922c..c6817768c2bb 100644
--- a/sys/vm/vm_pageout.c
+++ b/sys/vm/vm_pageout.c
@@ -65,7 +65,7 @@
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*
- * $Id: vm_pageout.c,v 1.13 1994/02/10 08:08:37 davidg Exp $
+ * $Id: vm_pageout.c,v 1.24 1994/06/17 13:29:15 davidg Exp $
*/
/*
@@ -85,22 +85,35 @@
extern vm_map_t kmem_map;
int vm_pages_needed; /* Event on which pageout daemon sleeps */
+int vm_pagescanner; /* Event on which pagescanner sleeps */
int vm_pageout_free_min = 0; /* Stop pageout to wait for pagers at this free level */
int vm_pageout_pages_needed = 0; /* flag saying that the pageout daemon needs pages */
int vm_page_pagesfreed;
+extern int vm_page_count;
extern int npendingio;
extern int hz;
int vm_pageout_proc_limit;
extern int nswiodone;
+extern int swap_pager_full;
+extern int swap_pager_ready();
#define MAXREF 32767
-#define DEACT_MAX (DEACT_START * 4)
-#define MINSCAN 512 /* minimum number of pages to scan in active queue */
- /* set the "clock" hands to be (MINSCAN * 4096) Bytes */
-static int minscan;
-void vm_pageout_deact_bump(vm_page_t m) ;
+
+#define MAXSCAN 512 /* maximum number of pages to scan in active queue */
+ /* set the "clock" hands to be (MAXSCAN * 4096) Bytes */
+#define ACT_DECLINE 1
+#define ACT_ADVANCE 3
+#define ACT_MAX 300
+
+#define LOWATER ((2048*1024)/NBPG)
+
+#define VM_PAGEOUT_PAGE_COUNT 8
+int vm_pageout_page_count = VM_PAGEOUT_PAGE_COUNT;
+static vm_offset_t vm_space_needed;
+int vm_pageout_req_do_stats;
+int vm_pageout_do_stats;
/*
@@ -108,9 +121,9 @@ void vm_pageout_deact_bump(vm_page_t m) ;
* cleans a vm_page
*/
int
-vm_pageout_clean(m, wait)
+vm_pageout_clean(m, sync)
register vm_page_t m;
- int wait;
+ int sync;
{
/*
* Clean the page and remove it from the
@@ -130,7 +143,12 @@ vm_pageout_clean(m, wait)
register vm_object_t object;
register vm_pager_t pager;
- int pageout_status;
+ int pageout_status[VM_PAGEOUT_PAGE_COUNT];
+ vm_page_t ms[VM_PAGEOUT_PAGE_COUNT];
+ int pageout_count;
+ int anyok=0;
+ int i;
+ vm_offset_t offset = m->offset;
object = m->object;
if (!object) {
@@ -153,37 +171,60 @@ vm_pageout_clean(m, wait)
vm_page_free_count < vm_pageout_free_min)
return 0;
-collapseagain:
if (!object->pager &&
object->shadow &&
object->shadow->paging_in_progress)
return 0;
- if (object->shadow) {
- vm_offset_t offset = m->offset;
- vm_object_collapse(object);
- if (!vm_page_lookup(object, offset))
+ if( !sync) {
+ if (object->shadow) {
+ vm_object_collapse(object);
+ if (!vm_page_lookup(object, offset))
+ return 0;
+ }
+
+ if ((m->flags & PG_BUSY) || (m->hold_count != 0)) {
return 0;
+ }
}
-waitagain:
- if (!wait && (m->flags & PG_BUSY)) {
- return 0;
- } else if (m->flags & PG_BUSY) {
- int s = splhigh();
- m->flags |= PG_WANTED;
- tsleep((caddr_t)m, PVM, "clnslp", 0);
- splx(s);
- goto waitagain;
- }
+ pageout_count = 1;
+ ms[0] = m;
- m->flags |= PG_BUSY;
+ if( pager = object->pager) {
+ for(i=1;i<vm_pageout_page_count;i++) {
+ if( ms[i] = vm_page_lookup( object, offset+i*NBPG)) {
+ if( ((ms[i]->flags & (PG_CLEAN|PG_INACTIVE|PG_BUSY)) == PG_INACTIVE)
+ && (ms[i]->wire_count == 0)
+ && (ms[i]->hold_count == 0))
+ pageout_count++;
+ else
+ break;
+ } else
+ break;
+ }
+ for(i=0;i<pageout_count;i++) {
+ ms[i]->flags |= PG_BUSY;
+ pmap_page_protect(VM_PAGE_TO_PHYS(ms[i]), VM_PROT_READ);
+ }
+ object->paging_in_progress += pageout_count;
+ vm_stat.pageouts += pageout_count;
+ } else {
+
+ m->flags |= PG_BUSY;
- pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_READ);
+ pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_READ);
- vm_stat.pageouts++;
+ vm_stat.pageouts++;
- object->paging_in_progress++;
+ object->paging_in_progress++;
+
+ pager = vm_pager_allocate(PG_DFLT, (caddr_t)0,
+ object->size, VM_PROT_ALL, 0);
+ if (pager != NULL) {
+ vm_object_setpager(object, pager, 0, FALSE);
+ }
+ }
/*
* If there is no pager for the page,
@@ -194,160 +235,83 @@ waitagain:
* later.
*/
- if ((pager = object->pager) == NULL) {
- pager = vm_pager_allocate(PG_DFLT, (caddr_t)0,
- object->size, VM_PROT_ALL, 0);
- if (pager != NULL) {
- vm_object_setpager(object, pager, 0, FALSE);
- }
- }
if ((pager && pager->pg_type == PG_SWAP) ||
vm_page_free_count >= vm_pageout_free_min) {
- pageout_status = pager ?
- vm_pager_put(pager, m, (((object == kernel_object) || wait) ? TRUE: FALSE)) :
- VM_PAGER_FAIL;
- } else
- pageout_status = VM_PAGER_FAIL;
-
- switch (pageout_status) {
- case VM_PAGER_OK:
- m->flags &= ~PG_LAUNDRY;
- break;
- case VM_PAGER_PEND:
- m->flags &= ~PG_LAUNDRY;
- break;
- case VM_PAGER_BAD:
- /*
- * Page outside of range of object.
- * Right now we essentially lose the
- * changes by pretending it worked.
- */
- m->flags &= ~PG_LAUNDRY;
- m->flags |= PG_CLEAN;
- pmap_clear_modify(VM_PAGE_TO_PHYS(m));
- break;
- case VM_PAGER_FAIL:
- /*
- * If page couldn't be paged out, then
- * reactivate the page so it doesn't
- * clog the inactive list. (We will
- * try paging out it again later).
- */
- if ((m->flags & PG_ACTIVE) == 0)
- vm_page_activate(m);
- break;
- case VM_PAGER_TRYAGAIN:
- break;
- }
-
-
- /*
- * If the operation is still going, leave
- * the page busy to block all other accesses.
- * Also, leave the paging in progress
- * indicator set so that we don't attempt an
- * object collapse.
- */
- if (pageout_status != VM_PAGER_PEND) {
- if ((m->flags & PG_ACTIVE) == 0 &&
- pmap_is_referenced(VM_PAGE_TO_PHYS(m))) {
- vm_page_activate(m);
+ if( pageout_count == 1) {
+ pageout_status[0] = pager ?
+ vm_pager_put(pager, m,
+ ((sync || (object == kernel_object)) ? TRUE: FALSE)) :
+ VM_PAGER_FAIL;
+ } else {
+ if( !pager) {
+ for(i=0;i<pageout_count;i++)
+ pageout_status[i] = VM_PAGER_FAIL;
+ } else {
+ vm_pager_putmulti(pager, ms, pageout_count,
+ ((sync || (object == kernel_object)) ? TRUE : FALSE),
+ pageout_status);
+ }
}
- PAGE_WAKEUP(m);
- if (--object->paging_in_progress == 0)
- wakeup((caddr_t) object);
+
+ } else {
+ for(i=0;i<pageout_count;i++)
+ pageout_status[i] = VM_PAGER_FAIL;
}
- return (pageout_status == VM_PAGER_PEND ||
- pageout_status == VM_PAGER_OK) ? 1 : 0;
-}
-int
-vm_fault_object_deactivate_pages(map, object, dummy)
- vm_map_t map;
- vm_object_t object;
- int dummy;
-{
- register vm_page_t p, next;
- int rcount;
- int s;
- int dcount;
- int count;
-
- dcount = 0;
- /*
- * deactivate the pages in the objects shadow
- */
+ for(i=0;i<pageout_count;i++) {
+ switch (pageout_status[i]) {
+ case VM_PAGER_OK:
+ ms[i]->flags &= ~PG_LAUNDRY;
+ ++anyok;
+ break;
+ case VM_PAGER_PEND:
+ ms[i]->flags &= ~PG_LAUNDRY;
+ ++anyok;
+ break;
+ case VM_PAGER_BAD:
+ /*
+ * Page outside of range of object.
+ * Right now we essentially lose the
+ * changes by pretending it worked.
+ */
+ ms[i]->flags &= ~PG_LAUNDRY;
+ ms[i]->flags |= PG_CLEAN;
+ pmap_clear_modify(VM_PAGE_TO_PHYS(ms[i]));
+ break;
+ case VM_PAGER_FAIL:
+ /*
+ * If page couldn't be paged out, then
+ * reactivate the page so it doesn't
+ * clog the inactive list. (We will
+ * try paging out it again later).
+ */
+ if (ms[i]->flags & PG_INACTIVE)
+ vm_page_activate(ms[i]);
+ break;
+ case VM_PAGER_TRYAGAIN:
+ break;
+ }
- if (object->shadow)
- dcount += vm_fault_object_deactivate_pages(map, object->shadow, 0);
- /*
- * scan the objects memory queue and remove 20% of the active pages
- */
- rcount = object->resident_page_count;
- count = rcount;
- if (count == 0)
- return dcount;
-#define MINOBJWRITE 10
-#define OBJDIVISOR 5
- if (count > MINOBJWRITE) {
- count = MINOBJWRITE + ((count - MINOBJWRITE) / OBJDIVISOR);
- }
- p = (vm_page_t) queue_first(&object->memq);
- while ((rcount-- > 0) && !queue_end(&object->memq, (queue_entry_t) p) ) {
- next = (vm_page_t) queue_next(&p->listq);
- vm_page_lock_queues();
/*
- * if a page is active, not wired and is in the processes pmap,
- * then deactivate the page.
+ * If the operation is still going, leave
+ * the page busy to block all other accesses.
+ * Also, leave the paging in progress
+ * indicator set so that we don't attempt an
+ * object collapse.
*/
- if ((p->flags & (PG_ACTIVE|PG_BUSY)) == PG_ACTIVE &&
- p->wire_count == 0 &&
- pmap_page_exists(vm_map_pmap(map), VM_PAGE_TO_PHYS(p))) {
- if (!pmap_is_referenced(VM_PAGE_TO_PHYS(p))) {
- vm_page_deactivate(p);
- if ((p->flags & PG_CLEAN) == 0) {
- vm_pageout_clean(p, 0);
- }
- ++dcount;
- if (--count <= 0) {
- vm_page_unlock_queues();
- s = splbio();
- while (object->paging_in_progress) {
- tsleep((caddr_t) object,PVM,"vmfobw",0);
- }
- splx(s);
- return dcount;
- }
- } else {
- vm_pageout_deact_bump(p);
- pmap_clear_reference(VM_PAGE_TO_PHYS(p));
- queue_remove(&object->memq, p, vm_page_t, listq);
- queue_enter(&object->memq, p, vm_page_t, listq);
- queue_remove(&vm_page_queue_active, p, vm_page_t, pageq);
- queue_enter(&vm_page_queue_active, p, vm_page_t, pageq);
+ if (pageout_status[i] != VM_PAGER_PEND) {
+ PAGE_WAKEUP(ms[i]);
+ if (--object->paging_in_progress == 0)
+ wakeup((caddr_t) object);
+ if (pmap_is_referenced(VM_PAGE_TO_PHYS(ms[i]))) {
+ pmap_clear_reference(VM_PAGE_TO_PHYS(ms[i]));
+ if( ms[i]->flags & PG_INACTIVE)
+ vm_page_activate(ms[i]);
}
- /*
- * if a page is inactive and has been modified, clean it now
- */
- } else if ((p->flags & (PG_INACTIVE|PG_BUSY)) == PG_INACTIVE) {
- if ((p->flags & PG_CLEAN) &&
- pmap_is_modified(VM_PAGE_TO_PHYS(p)))
- p->flags &= ~PG_CLEAN;
-
- if ((p->flags & PG_CLEAN) == 0)
- vm_pageout_clean(p, 0);
}
-
- vm_page_unlock_queues();
- p = next;
- }
- s = splbio();
- while (object->paging_in_progress) {
- tsleep((caddr_t)object,PVM,"vmfobw",0);
}
- splx(s);
- return dcount;
+ return anyok;
}
/*
@@ -376,7 +340,11 @@ vm_pageout_object_deactivate_pages(map, object, count)
count = 1;
if (object->shadow) {
- dcount += vm_pageout_object_deactivate_pages(map, object->shadow, count);
+ int scount = count;
+ if( object->shadow->ref_count > 1)
+ scount /= object->shadow->ref_count;
+ if( scount)
+ dcount += vm_pageout_object_deactivate_pages(map, object->shadow, scount);
}
if (object->paging_in_progress)
@@ -396,15 +364,28 @@ vm_pageout_object_deactivate_pages(map, object, count)
*/
if ((p->flags & (PG_ACTIVE|PG_BUSY)) == PG_ACTIVE &&
p->wire_count == 0 &&
+ p->hold_count == 0 &&
pmap_page_exists(vm_map_pmap(map), VM_PAGE_TO_PHYS(p))) {
if (!pmap_is_referenced(VM_PAGE_TO_PHYS(p))) {
- if (object->ref_count <= 1)
+ p->act_count -= min(p->act_count, ACT_DECLINE);
+ /*
+ * if the page act_count is zero -- then we deactivate
+ */
+ if (!p->act_count) {
vm_page_deactivate(p);
- else
- vm_page_pageout_deactivate(p);
- if (((p->flags & PG_INACTIVE)) &&
- (p->flags & PG_CLEAN) == 0)
- vm_pageout_clean(p, 0);
+ pmap_page_protect(VM_PAGE_TO_PHYS(p),
+ VM_PROT_NONE);
+ /*
+ * else if on the next go-around we will deactivate the page
+ * we need to place the page on the end of the queue to age
+ * the other pages in memory.
+ */
+ } else {
+ queue_remove(&vm_page_queue_active, p, vm_page_t, pageq);
+ queue_enter(&vm_page_queue_active, p, vm_page_t, pageq);
+ queue_remove(&object->memq, p, vm_page_t, listq);
+ queue_enter(&object->memq, p, vm_page_t, listq);
+ }
/*
* see if we are done yet
*/
@@ -419,23 +400,18 @@ vm_pageout_object_deactivate_pages(map, object, count)
}
} else {
- vm_pageout_deact_bump(p);
+ /*
+ * Move the page to the bottom of the queue.
+ */
pmap_clear_reference(VM_PAGE_TO_PHYS(p));
+ if (p->act_count < ACT_MAX)
+ p->act_count += ACT_ADVANCE;
+
queue_remove(&object->memq, p, vm_page_t, listq);
queue_enter(&object->memq, p, vm_page_t, listq);
queue_remove(&vm_page_queue_active, p, vm_page_t, pageq);
queue_enter(&vm_page_queue_active, p, vm_page_t, pageq);
}
- /*
- * if a page is inactive and has been modified, clean it now
- */
- } else if ((p->flags & (PG_INACTIVE|PG_BUSY)) == PG_INACTIVE) {
- if ((p->flags & PG_CLEAN) &&
- pmap_is_modified(VM_PAGE_TO_PHYS(p)))
- p->flags &= ~PG_CLEAN;
-
- if ((p->flags & PG_CLEAN) == 0)
- vm_pageout_clean(p, 0);
}
vm_page_unlock_queues();
@@ -488,50 +464,28 @@ vm_pageout_map_deactivate_pages(map, entry, count, freeer)
return;
}
-void
-vm_fault_free_pages(p)
- struct proc *p;
-{
- int overage = 1;
- vm_pageout_map_deactivate_pages(&p->p_vmspace->vm_map,
- (vm_map_entry_t) 0, &overage, vm_fault_object_deactivate_pages);
-}
-
/*
* vm_pageout_scan does the dirty work for the pageout daemon.
*/
-void
+int
vm_pageout_scan()
{
vm_page_t m;
int page_shortage, maxscan, maxlaunder;
- int pages_freed, free, nproc, nbusy;
+ int pages_freed, free, nproc;
+ int desired_free;
vm_page_t next;
struct proc *p;
vm_object_t object;
int s;
+ int force_wakeup = 0;
+morefree:
/*
- * deactivate objects with ref_counts == 0
- */
- object = (vm_object_t) queue_first(&vm_object_list);
- while (!queue_end(&vm_object_list, (queue_entry_t) object)) {
- if (object->ref_count == 0)
- vm_object_deactivate_pages(object);
- object = (vm_object_t) queue_next(&object->object_list);
- }
-
-rerun:
-#if 1
- /*
- * next scan the processes for exceeding their rlimits or if process
+ * scan the processes for exceeding their rlimits or if process
* is swapped out -- deactivate pages
*/
-rescanproc1a:
- for (p = allproc; p != NULL; p = p->p_nxt)
- p->p_flag &= ~SPAGEDAEMON;
-
rescanproc1:
for (p = allproc; p != NULL; p = p->p_nxt) {
vm_offset_t size;
@@ -572,22 +526,17 @@ rescanproc1:
overage = (size - limit) / NBPG;
vm_pageout_map_deactivate_pages(&p->p_vmspace->vm_map,
(vm_map_entry_t) 0, &overage, vm_pageout_object_deactivate_pages);
- p->p_flag |= SPAGEDAEMON;
- goto rescanproc1;
}
- p->p_flag |= SPAGEDAEMON;
+
}
-#if 0
if (((vm_page_free_count + vm_page_inactive_count) >=
(vm_page_inactive_target + vm_page_free_target)) &&
(vm_page_free_count >= vm_page_free_target))
- return;
-#endif
-
-#endif
+ return force_wakeup;
pages_freed = 0;
+ desired_free = vm_page_free_target;
/*
* Start scanning the inactive queue for pages we can free.
@@ -597,26 +546,37 @@ rescanproc1:
*/
maxlaunder = (vm_page_free_target - vm_page_free_count);
-rescan:
- m = (vm_page_t) queue_first(&vm_page_queue_inactive);
maxscan = vm_page_inactive_count;
+rescan1:
+ m = (vm_page_t) queue_first(&vm_page_queue_inactive);
while (maxscan-- > 0) {
vm_page_t next;
-
if (queue_end(&vm_page_queue_inactive, (queue_entry_t) m)
- || (vm_page_free_count >= vm_page_free_target)) {
+ || (vm_page_free_count >= desired_free)) {
break;
}
next = (vm_page_t) queue_next(&m->pageq);
+ if( (m->flags & PG_INACTIVE) == 0) {
+ printf("vm_pageout_scan: page not inactive?");
+ continue;
+ }
+
+ /*
+ * activate held pages
+ */
+ if (m->hold_count != 0) {
+ vm_page_activate(m);
+ m = next;
+ continue;
+ }
+
/*
* dont mess with busy pages
*/
if (m->flags & PG_BUSY) {
- queue_remove(&vm_page_queue_inactive, m, vm_page_t, pageq);
- queue_enter(&vm_page_queue_inactive, m, vm_page_t, pageq);
m = next;
continue;
}
@@ -628,34 +588,25 @@ rescan:
* vm system.
*/
if (m->flags & PG_CLEAN) {
- if ((vm_page_free_count > vm_pageout_free_min)
+ if ((vm_page_free_count > vm_pageout_free_min) /* XXX */
&& pmap_is_referenced(VM_PAGE_TO_PHYS(m))) {
vm_page_activate(m);
- ++vm_stat.reactivations;
- m = next;
- continue;
- }
- else {
+ } else if (!m->act_count) {
pmap_page_protect(VM_PAGE_TO_PHYS(m),
VM_PROT_NONE);
vm_page_free(m);
++pages_freed;
- m = next;
- continue;
+ } else {
+ m->act_count -= min(m->act_count, ACT_DECLINE);
}
} else if ((m->flags & PG_LAUNDRY) && maxlaunder > 0) {
- /*
- * if a page has been used even if it is in the laundry,
- * activate it.
- */
-
+ int written;
if (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) {
+ pmap_clear_reference(VM_PAGE_TO_PHYS(m));
vm_page_activate(m);
- m->flags &= ~PG_LAUNDRY;
m = next;
continue;
}
-
/*
* If a page is dirty, then it is either
* being washed (but not yet cleaned)
@@ -664,17 +615,18 @@ rescan:
* cleaning operation.
*/
- if (vm_pageout_clean(m,0)) {
- --maxlaunder;
- /*
- * if the next page has been re-activated, start scanning again
- */
- if ((next->flags & PG_INACTIVE) == 0)
- goto rescan;
+ if (written = vm_pageout_clean(m,0)) {
+ maxlaunder -= written;
}
- } else if (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) {
+ /*
+ * if the next page has been re-activated, start scanning again
+ */
+ if (!next || (next->flags & PG_INACTIVE) == 0)
+ goto rescan1;
+ } else if (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) {
+ pmap_clear_reference(VM_PAGE_TO_PHYS(m));
vm_page_activate(m);
- }
+ }
m = next;
}
@@ -682,42 +634,11 @@ rescan:
* now check malloc area or swap processes out if we are in low
* memory conditions
*/
- free = vm_page_free_count;
- if (free <= vm_page_free_min) {
- /*
- * Be sure the pmap system is updated so
- * we can scan the inactive queue.
- */
- pmap_update();
-
+ if (vm_page_free_count < vm_page_free_min) {
/*
* swap out inactive processes
*/
swapout_threads();
-
-#if 0
- /*
- * see if malloc has anything for us
- */
- if (free <= vm_page_free_reserved)
- malloc_gc();
-#endif
- }
-
-skipfree:
- /*
- * If we did not free any pages, but we need to do so, we grow the
- * inactive target. But as we successfully free pages, then we
- * shrink the inactive target.
- */
- if (pages_freed == 0 && vm_page_free_count < vm_page_free_min) {
- vm_page_inactive_target += (vm_page_free_min - vm_page_free_count);
- if (vm_page_inactive_target > vm_page_free_target*5)
- vm_page_inactive_target = vm_page_free_target*5;
- } else if (pages_freed > 0) {
- vm_page_inactive_target -= vm_page_free_min/2;
- if (vm_page_inactive_target < vm_page_free_target*2)
- vm_page_inactive_target = vm_page_free_target*2;
}
/*
@@ -726,35 +647,27 @@ skipfree:
* to inactive.
*/
-restart_inactivate_all:
-
- page_shortage = vm_page_inactive_target - vm_page_inactive_count;
- page_shortage -= vm_page_free_count;
+ page_shortage = vm_page_inactive_target -
+ (vm_page_free_count + vm_page_inactive_count);
if (page_shortage <= 0) {
- if (pages_freed == 0 &&
- ((vm_page_free_count + vm_page_inactive_count) <
+ if (pages_freed == 0) {
+ if( vm_page_free_count < vm_page_free_min) {
+ page_shortage = vm_page_free_min - vm_page_free_count;
+ } else if(((vm_page_free_count + vm_page_inactive_count) <
(vm_page_free_min + vm_page_inactive_target))) {
- page_shortage = 1;
- } else {
- page_shortage = 0;
+ page_shortage = 1;
+ } else {
+ page_shortage = 0;
+ }
}
+
}
- maxscan = vm_page_active_count;
-
- /*
- * deactivate pages that are active, but have not been used
- * for a while.
- */
-restart_inactivate:
m = (vm_page_t) queue_first(&vm_page_queue_active);
- while (maxscan-- > 0) {
+ maxscan = vm_page_active_count;
+ while (maxscan-- && (page_shortage > 0)) {
- if (page_shortage <= 0 &&
- maxscan < (vm_page_active_count - minscan) )
- break;
-
if (queue_end(&vm_page_queue_active, (queue_entry_t) m)) {
break;
}
@@ -762,109 +675,156 @@ restart_inactivate:
next = (vm_page_t) queue_next(&m->pageq);
/*
- * dont mess with pages that are busy
+ * Don't deactivate pages that are busy.
*/
- if (m->flags & PG_BUSY) {
+ if ((m->flags & PG_BUSY) || (m->hold_count != 0)) {
m = next;
continue;
}
- /*
- * Move some more pages from active to inactive.
- */
+ if (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) {
+ pmap_clear_reference(VM_PAGE_TO_PHYS(m));
+ if (m->act_count < ACT_MAX)
+ m->act_count += ACT_ADVANCE;
+ queue_remove(&vm_page_queue_active, m, vm_page_t, pageq);
+ queue_enter(&vm_page_queue_active, m, vm_page_t, pageq);
+ queue_remove(&m->object->memq, m, vm_page_t, listq);
+ queue_enter(&m->object->memq, m, vm_page_t, listq);
+ } else {
+ m->act_count -= min(m->act_count, ACT_DECLINE);
- /*
- * see if there are any pages that are able to be deactivated
- */
- /*
- * the referenced bit is the one that say that the page
- * has been used.
- */
- if (!pmap_is_referenced(VM_PAGE_TO_PHYS(m))) {
/*
- * if the page has not been referenced, call the
- * vm_page_pageout_deactivate routine. It might
- * not deactivate the page every time. There is
- * a policy associated with it.
+ * if the page act_count is zero -- then we deactivate
*/
- if (page_shortage > 0) {
- if (vm_page_pageout_deactivate(m)) {
- /*
- * if the page was really deactivated, then
- * decrement the page_shortage
- */
- if ((m->flags & PG_ACTIVE) == 0) {
- --page_shortage;
- }
- }
- }
- } else {
+ if (!m->act_count) {
+ vm_page_deactivate(m);
+ --page_shortage;
/*
- * if the page was recently referenced, set our
- * deactivate count and clear reference for a future
- * check for deactivation.
+ * else if on the next go-around we will deactivate the page
+ * we need to place the page on the end of the queue to age
+ * the other pages in memory.
*/
- vm_pageout_deact_bump(m);
- if (page_shortage > 0 || m->deact >= (DEACT_MAX/2))
- pmap_clear_reference(VM_PAGE_TO_PHYS(m));
- queue_remove(&m->object->memq, m, vm_page_t, listq);
- queue_enter(&m->object->memq, m, vm_page_t, listq);
- queue_remove(&vm_page_queue_active, m, vm_page_t, pageq);
- queue_enter(&vm_page_queue_active, m, vm_page_t, pageq);
+ } else {
+ queue_remove(&vm_page_queue_active, m, vm_page_t, pageq);
+ queue_enter(&vm_page_queue_active, m, vm_page_t, pageq);
+ queue_remove(&m->object->memq, m, vm_page_t, listq);
+ queue_enter(&m->object->memq, m, vm_page_t, listq);
+ }
}
+
m = next;
}
- vm_page_pagesfreed += pages_freed;
-}
+ /*
+ * if we have not freed any pages and we are desparate for memory
+ * then we keep trying until we get some (any) memory.
+ */
-/*
- * this code maintains a dynamic reference count per page
- */
-void
-vm_pageout_deact_bump(vm_page_t m) {
- if( m->deact >= DEACT_START) {
- m->deact += 1;
- if( m->deact > DEACT_MAX)
- m->deact = DEACT_MAX;
- } else {
- m->deact += DEACT_START;
+ if( !force_wakeup && (swap_pager_full || !force_wakeup ||
+ (pages_freed == 0 && (vm_page_free_count < vm_page_free_min)))){
+ vm_pager_sync();
+ force_wakeup = 1;
+ goto morefree;
}
+ vm_page_pagesfreed += pages_freed;
+ return force_wakeup;
}
-/*
- * optionally do a deactivate if the deactivate has been done
- * enough to justify it.
- */
-int
-vm_page_pageout_deactivate(m)
- vm_page_t m;
+void
+vm_pagescan()
{
+ int maxscan, pages_scanned, pages_referenced, nextscan, scantick = hz/20;
+ int m_ref, next_ref;
+ vm_page_t m, next;
+
+ (void) splnone();
+
+ nextscan = scantick;
- switch (m->deact) {
-case DEACT_FREE:
- vm_page_deactivate(m);
- return 1;
-case DEACT_CLEAN:
- break;
-case DEACT_DELAY:
- vm_page_makefault(m);
-case DEACT_START:
- break;
+scanloop:
+
+ pages_scanned = 0;
+ pages_referenced = 0;
+ maxscan = min(vm_page_active_count, MAXSCAN);
+
+ /*
+ * Gather statistics on page usage.
+ */
+ m = (vm_page_t) queue_first(&vm_page_queue_active);
+ while (maxscan-- > 0) {
+
+ if (queue_end(&vm_page_queue_active, (queue_entry_t) m)) {
+ break;
+ }
+
+ ++pages_scanned;
+
+ next = (vm_page_t) queue_next(&m->pageq);
+
+ /*
+ * Dont mess with pages that are busy.
+ */
+ if ((m->flags & PG_BUSY) || (m->hold_count != 0)) {
+ m = next;
+ continue;
+ }
+
+ /*
+ * Advance pages that have been referenced, decline pages that
+ * have not.
+ */
+ if (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) {
+ pmap_clear_reference(VM_PAGE_TO_PHYS(m));
+ pages_referenced++;
+ if (m->act_count < ACT_MAX)
+ m->act_count += ACT_ADVANCE;
+ queue_remove(&vm_page_queue_active, m, vm_page_t, pageq);
+ queue_enter(&vm_page_queue_active, m, vm_page_t, pageq);
+ queue_remove(&m->object->memq, m, vm_page_t, listq);
+ queue_enter(&m->object->memq, m, vm_page_t, listq);
+ } else {
+ m->act_count -= min(m->act_count, ACT_DECLINE);
+ /*
+ * if the page act_count is zero, and we are low on mem -- then we deactivate
+ */
+ if (!m->act_count &&
+ (vm_page_free_count+vm_page_inactive_count < vm_page_free_target+vm_page_inactive_target )) {
+ vm_page_deactivate(m);
+ /*
+ * else if on the next go-around we will deactivate the page
+ * we need to place the page on the end of the queue to age
+ * the other pages in memory.
+ */
+ } else {
+ queue_remove(&vm_page_queue_active, m, vm_page_t, pageq);
+ queue_enter(&vm_page_queue_active, m, vm_page_t, pageq);
+ queue_remove(&m->object->memq, m, vm_page_t, listq);
+ queue_enter(&m->object->memq, m, vm_page_t, listq);
+ }
+ }
+ m = next;
}
- --m->deact;
- return 0;
+
+ if (pages_referenced) {
+ nextscan = (pages_scanned / pages_referenced) * scantick;
+ nextscan = max(nextscan, scantick);
+ nextscan = min(nextscan, hz);
+ } else
+ nextscan = hz;
+ tsleep((caddr_t) &vm_pagescanner, PVM, "scanw", nextscan);
+
+ goto scanloop;
}
/*
* vm_pageout is the high level pageout daemon.
*/
-
void
vm_pageout()
{
extern npendingio, swiopend;
extern int vm_page_count;
+ static nowakeup;
(void) spl0();
/*
@@ -872,49 +832,42 @@ vm_pageout()
*/
vmretry:
- vm_page_free_min = npendingio/3;
-#ifdef VSMALL
- vm_page_free_min = 8;
-#endif
+ vm_page_free_min = 12;
vm_page_free_reserved = 8;
if (vm_page_free_min < 8)
vm_page_free_min = 8;
if (vm_page_free_min > 32)
vm_page_free_min = 32;
- vm_pageout_free_min = 3;
+ vm_pageout_free_min = 4;
vm_page_free_target = 2*vm_page_free_min + vm_page_free_reserved;
- vm_page_inactive_target = 3*vm_page_free_min + vm_page_free_reserved;
+ vm_page_inactive_target = vm_page_free_count / 12;
vm_page_free_min += vm_page_free_reserved;
- minscan = MINSCAN;
- if (minscan > vm_page_count/3)
- minscan = vm_page_count/3;
+
+ (void) swap_pager_alloc(0, 0, 0, 0);
/*
* The pageout daemon is never done, so loop
* forever.
*/
-
-
while (TRUE) {
+ int force_wakeup;
- splhigh();
- if (vm_page_free_count > vm_page_free_min) {
- wakeup((caddr_t) &vm_page_free_count);
- tsleep((caddr_t) &vm_pages_needed, PVM, "psleep", 0);
- } else {
- if (nswiodone) {
- spl0();
- goto dosync;
- }
- tsleep((caddr_t) &vm_pages_needed, PVM, "pslp1", 5);
- }
- spl0();
-
+ tsleep((caddr_t) &vm_pages_needed, PVM, "psleep", 0);
+
vm_pager_sync();
- vm_pageout_scan();
- dosync:
+ /*
+ * The force wakeup hack added to eliminate delays and potiential
+ * deadlock. It was possible for the page daemon to indefintely
+ * postpone waking up a process that it might be waiting for memory
+ * on. The putmulti stuff seems to have aggravated the situation.
+ */
+ force_wakeup = vm_pageout_scan();
vm_pager_sync();
+ if( force_wakeup)
+ wakeup( (caddr_t) &vm_page_free_count);
+ vm_pageout_do_stats = 0;
cnt.v_scan++;
wakeup((caddr_t) kmem_map);
}
}
+
diff --git a/sys/vm/vm_pageout.h b/sys/vm/vm_pageout.h
index d975d8006fd3..a80605967ec8 100644
--- a/sys/vm/vm_pageout.h
+++ b/sys/vm/vm_pageout.h
@@ -86,6 +86,8 @@ simple_lock_data_t vm_pages_needed_lock;
inline static void vm_wait() {
extern struct proc *curproc, *pageproc;
extern int vm_pageout_pages_needed;
+ int s;
+ s = splhigh();
if (curproc == pageproc) {
vm_pageout_pages_needed = 1;
tsleep((caddr_t) &vm_pageout_pages_needed, PSWP, "vmwait", 0);
@@ -94,6 +96,7 @@ inline static void vm_wait() {
wakeup((caddr_t) &vm_pages_needed);
tsleep((caddr_t) &vm_page_free_count, PVM, "vmwait", 0);
}
+ splx(s);
}
diff --git a/sys/vm/vm_pager.c b/sys/vm/vm_pager.c
index d31be45a430f..f1138e1db3f3 100644
--- a/sys/vm/vm_pager.c
+++ b/sys/vm/vm_pager.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)vm_pager.c 7.4 (Berkeley) 5/7/91
- * $Id: vm_pager.c,v 1.10 1994/01/31 04:21:43 davidg Exp $
+ * $Id: vm_pager.c,v 1.11 1994/03/07 11:39:16 davidg Exp $
*/
/*
@@ -167,6 +167,26 @@ vm_pager_getmulti(pager, m, count, reqpage, sync)
}
int
+vm_pager_putmulti(pager, m, count, sync, rtvals)
+ vm_pager_t pager;
+ vm_page_t *m;
+ int count;
+ boolean_t sync;
+ int *rtvals;
+{
+ int i;
+
+ if( pager->pg_ops->pgo_putmulti)
+ return(VM_PAGER_PUT_MULTI(pager, m, count, sync, rtvals));
+ else {
+ for(i=0;i<count;i++) {
+ rtvals[i] = VM_PAGER_PUT( pager, m[i], sync);
+ }
+ return 1;
+ }
+}
+
+int
vm_pager_get(pager, m, sync)
vm_pager_t pager;
vm_page_t m;
diff --git a/sys/vm/vm_pager.h b/sys/vm/vm_pager.h
index 699881ab51a3..2cf52674fab2 100644
--- a/sys/vm/vm_pager.h
+++ b/sys/vm/vm_pager.h
@@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* from: @(#)vm_pager.h 7.2 (Berkeley) 4/20/91
- * $Id: vm_pager.h,v 1.6 1994/01/31 04:21:50 davidg Exp $
+ * $Id: vm_pager.h,v 1.7 1994/03/07 11:39:17 davidg Exp $
*/
/*
@@ -69,6 +69,7 @@ struct pagerops {
int (*pgo_getpage)(); /* get (read) page */
int (*pgo_getmulti)(); /* get (read) multiple pages */
int (*pgo_putpage)(); /* put (write) page */
+ int (*pgo_putmulti)(); /* get (read) multiple pages */
boolean_t (*pgo_haspage)(); /* does pager have page? */
};
@@ -91,6 +92,7 @@ struct pagerops {
#define VM_PAGER_GET(pg, m, s) (*(pg)->pg_ops->pgo_getpage)(pg, m, s)
#define VM_PAGER_GET_MULTI(pg, m, c, r, s) (*(pg)->pg_ops->pgo_getmulti)(pg, m, c, r, s)
#define VM_PAGER_PUT(pg, m, s) (*(pg)->pg_ops->pgo_putpage)(pg, m, s)
+#define VM_PAGER_PUT_MULTI(pg, m, c, s, rtval) (*(pg)->pg_ops->pgo_putmulti)(pg, m, c, s, rtval)
#define VM_PAGER_HASPAGE(pg, o) (*(pg)->pg_ops->pgo_haspage)(pg, o)
#ifdef KERNEL
diff --git a/sys/vm/vm_swap.c b/sys/vm/vm_swap.c
index 0a8a57457101..462948c8703b 100644
--- a/sys/vm/vm_swap.c
+++ b/sys/vm/vm_swap.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)vm_swap.c 7.18 (Berkeley) 5/6/91
- * $Id: vm_swap.c,v 1.7 1993/12/19 00:56:15 wollman Exp $
+ * $Id: vm_swap.c,v 1.8 1994/03/14 21:54:32 davidg Exp $
*/
#include "param.h"
@@ -48,6 +48,7 @@
#include "kernel.h"
static int swfree(struct proc *, int);
+int vm_swap_size;
/*
* Indirect driver for multi-controller paging.
@@ -264,6 +265,7 @@ swfree(p, index)
if (blk > dmmax)
blk = dmmax;
rlist_free(&swapmap, vsbase, vsbase + blk - 1);
+ vm_swap_size += blk;
}
return (0);
}
diff --git a/sys/vm/vm_unix.c b/sys/vm/vm_unix.c
index 169bf376357b..f1127e2b07de 100644
--- a/sys/vm/vm_unix.c
+++ b/sys/vm/vm_unix.c
@@ -37,7 +37,7 @@
*
* from: Utah $Hdr: vm_unix.c 1.1 89/11/07$
* from: @(#)vm_unix.c 7.2 (Berkeley) 4/20/91
- * $Id: vm_unix.c,v 1.5 1993/12/12 12:27:26 davidg Exp $
+ * $Id: vm_unix.c,v 1.6 1994/03/14 21:54:33 davidg Exp $
*/
/*
@@ -50,6 +50,8 @@
#include "vm.h"
+extern int swap_pager_full;
+
struct obreak_args {
char *nsiz;
};
@@ -73,9 +75,11 @@ obreak(p, uap, retval)
old = round_page(old + ctob(vm->vm_dsize));
diff = new - old;
if (diff > 0) {
+ if (swap_pager_full) {
+ return(ENOMEM);
+ }
rv = vm_allocate(&vm->vm_map, &old, diff, FALSE);
if (rv != KERN_SUCCESS) {
- uprintf("sbrk: grow failed, return = %d\n", rv);
return(ENOMEM);
}
vm->vm_dsize += btoc(diff);
@@ -83,7 +87,6 @@ obreak(p, uap, retval)
diff = -diff;
rv = vm_deallocate(&vm->vm_map, new, diff);
if (rv != KERN_SUCCESS) {
- uprintf("sbrk: shrink failed, return = %d\n", rv);
return(ENOMEM);
}
vm->vm_dsize -= btoc(diff);
diff --git a/sys/vm/vnode_pager.c b/sys/vm/vnode_pager.c
index c35971ba67e0..d44e12e22ac6 100644
--- a/sys/vm/vnode_pager.c
+++ b/sys/vm/vnode_pager.c
@@ -2,7 +2,7 @@
* Copyright (c) 1990 University of Utah.
* Copyright (c) 1991 The Regents of the University of California.
* All rights reserved.
- * Copyright (c) 1993 John S. Dyson
+ * Copyright (c) 1993,1994 John S. Dyson
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
@@ -37,7 +37,7 @@
* SUCH DAMAGE.
*
* from: @(#)vnode_pager.c 7.5 (Berkeley) 4/20/91
- * $Id: vnode_pager.c,v 1.11.2.3 1994/04/18 04:57:49 rgrimes Exp $
+ * $Id: vnode_pager.c,v 1.21 1994/06/22 05:53:12 jkh Exp $
*/
/*
@@ -57,7 +57,7 @@
*
* 1) Supports multiple - block reads
* 2) Bypasses buffer cache for reads
- *
+ *
* TODO:
*
* 1) Totally bypass buffer cache for reads
@@ -86,6 +86,8 @@
#include "buf.h"
#include "specdev.h"
+int vnode_pager_putmulti();
+
struct pagerops vnodepagerops = {
vnode_pager_init,
vnode_pager_alloc,
@@ -93,35 +95,24 @@ struct pagerops vnodepagerops = {
vnode_pager_getpage,
vnode_pager_getmulti,
vnode_pager_putpage,
+ vnode_pager_putmulti,
vnode_pager_haspage
};
-static int vnode_pager_io(vn_pager_t vnp, vm_page_t *m, int count, int reqpage,
- enum uio_rw rw);
-struct buf * getpbuf() ;
-void relpbuf(struct buf *bp) ;
+static int vnode_pager_input(vn_pager_t vnp, vm_page_t * m, int count, int reqpage);
+static int vnode_pager_output(vn_pager_t vnp, vm_page_t * m, int count, int *rtvals);
+struct buf * getpbuf();
+void relpbuf(struct buf * bp);
extern vm_map_t pager_map;
-queue_head_t vnode_pager_list; /* list of managed vnodes */
+queue_head_t vnode_pager_list; /* list of managed vnodes */
-#ifdef DEBUG
-int vpagerdebug = 0x00;
-#define VDB_FOLLOW 0x01
-#define VDB_INIT 0x02
-#define VDB_IO 0x04
-#define VDB_FAIL 0x08
-#define VDB_ALLOC 0x10
-#define VDB_SIZE 0x20
-#endif
+#define MAXBP (NBPG/DEV_BSIZE);
void
vnode_pager_init()
{
-#ifdef DEBUG
- if (vpagerdebug & VDB_FOLLOW)
- printf("vnode_pager_init()\n");
-#endif
queue_init(&vnode_pager_list);
}
@@ -143,34 +134,32 @@ vnode_pager_alloc(handle, size, prot, offset)
struct vnode *vp;
struct proc *p = curproc; /* XXX */
-#ifdef DEBUG
- if (vpagerdebug & (VDB_FOLLOW|VDB_ALLOC))
- printf("vnode_pager_alloc(%x, %x, %x)\n", handle, size, prot);
-#endif
/*
* Pageout to vnode, no can do yet.
*/
if (handle == NULL)
- return(NULL);
+ return (NULL);
/*
- * Vnodes keep a pointer to any associated pager so no need to
- * lookup with vm_pager_lookup.
+ * Vnodes keep a pointer to any associated pager so no need to lookup
+ * with vm_pager_lookup.
*/
- vp = (struct vnode *)handle;
- pager = (vm_pager_t)vp->v_vmdata;
+ vp = (struct vnode *) handle;
+ pager = (vm_pager_t) vp->v_vmdata;
if (pager == NULL) {
+
/*
* Allocate pager structures
*/
- pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, M_WAITOK);
+ pager = (vm_pager_t) malloc(sizeof *pager, M_VMPAGER, M_WAITOK);
if (pager == NULL)
- return(NULL);
- vnp = (vn_pager_t)malloc(sizeof *vnp, M_VMPGDATA, M_WAITOK);
+ return (NULL);
+ vnp = (vn_pager_t) malloc(sizeof *vnp, M_VMPGDATA, M_WAITOK);
if (vnp == NULL) {
- free((caddr_t)pager, M_VMPAGER);
- return(NULL);
+ free((caddr_t) pager, M_VMPAGER);
+ return (NULL);
}
+
/*
* And an object of the appropriate size
*/
@@ -179,10 +168,11 @@ vnode_pager_alloc(handle, size, prot, offset)
vm_object_enter(object, pager);
vm_object_setpager(object, pager, 0, TRUE);
} else {
- free((caddr_t)vnp, M_VMPGDATA);
- free((caddr_t)pager, M_VMPAGER);
- return(NULL);
+ free((caddr_t) vnp, M_VMPGDATA);
+ free((caddr_t) pager, M_VMPAGER);
+ return (NULL);
}
+
/*
* Hold a reference to the vnode and initialize pager data.
*/
@@ -190,42 +180,32 @@ vnode_pager_alloc(handle, size, prot, offset)
vnp->vnp_flags = 0;
vnp->vnp_vp = vp;
vnp->vnp_size = vattr.va_size;
+
queue_enter(&vnode_pager_list, pager, vm_pager_t, pg_list);
pager->pg_handle = handle;
pager->pg_type = PG_VNODE;
pager->pg_ops = &vnodepagerops;
- pager->pg_data = (caddr_t)vnp;
- vp->v_vmdata = (caddr_t)pager;
+ pager->pg_data = (caddr_t) vnp;
+ vp->v_vmdata = (caddr_t) pager;
} else {
+
/*
- * vm_object_lookup() will remove the object from the
- * cache if found and also gain a reference to the object.
+ * vm_object_lookup() will remove the object from the cache if
+ * found and also gain a reference to the object.
*/
object = vm_object_lookup(pager);
-#ifdef DEBUG
- vnp = (vn_pager_t)pager->pg_data;
-#endif
}
-#ifdef DEBUG
- if (vpagerdebug & VDB_ALLOC)
- printf("vnode_pager_setup: vp %x sz %x pager %x object %x\n",
- vp, vnp->vnp_size, pager, object);
-#endif
- return(pager);
+ return (pager);
}
void
vnode_pager_dealloc(pager)
vm_pager_t pager;
{
- register vn_pager_t vnp = (vn_pager_t)pager->pg_data;
+ register vn_pager_t vnp = (vn_pager_t) pager->pg_data;
register struct vnode *vp;
- struct proc *p = curproc; /* XXX */
+ struct proc *p = curproc; /* XXX */
-#ifdef DEBUG
- if (vpagerdebug & VDB_FOLLOW)
- printf("vnode_pager_dealloc(%x)\n", pager);
-#endif
if (vp = vnp->vnp_vp) {
vp->v_vmdata = NULL;
vp->v_flag &= ~VTEXT;
@@ -236,22 +216,21 @@ vnode_pager_dealloc(pager)
vrele(vp);
}
queue_remove(&vnode_pager_list, pager, vm_pager_t, pg_list);
- free((caddr_t)vnp, M_VMPGDATA);
- free((caddr_t)pager, M_VMPAGER);
+ free((caddr_t) vnp, M_VMPGDATA);
+ free((caddr_t) pager, M_VMPAGER);
}
int
vnode_pager_getmulti(pager, m, count, reqpage, sync)
vm_pager_t pager;
vm_page_t *m;
- int count;
- int reqpage;
+ int count;
+ int reqpage;
boolean_t sync;
{
-
- return vnode_pager_io((vn_pager_t) pager->pg_data, m, count, reqpage, UIO_READ);
-}
+ return vnode_pager_input((vn_pager_t) pager->pg_data, m, count, reqpage);
+}
int
vnode_pager_getpage(pager, m, sync)
@@ -260,17 +239,14 @@ vnode_pager_getpage(pager, m, sync)
boolean_t sync;
{
- int err;
+ int err;
vm_page_t marray[1];
-#ifdef DEBUG
- if (vpagerdebug & VDB_FOLLOW)
- printf("vnode_pager_getpage(%x, %x)\n", pager, m);
-#endif
+
if (pager == NULL)
return FALSE;
marray[0] = m;
- return vnode_pager_io((vn_pager_t)pager->pg_data, marray, 1, 0, UIO_READ);
+ return vnode_pager_input((vn_pager_t) pager->pg_data, marray, 1, 0);
}
boolean_t
@@ -279,69 +255,61 @@ vnode_pager_putpage(pager, m, sync)
vm_page_t m;
boolean_t sync;
{
- int err;
+ int err;
vm_page_t marray[1];
+ int rtvals[1];
-#ifdef DEBUG
- if (vpagerdebug & VDB_FOLLOW)
- printf("vnode_pager_putpage(%x, %x)\n", pager, m);
-#endif
if (pager == NULL)
return FALSE;
marray[0] = m;
- err = vnode_pager_io((vn_pager_t)pager->pg_data, marray, 1, 0, UIO_WRITE);
- return err;
+ vnode_pager_output((vn_pager_t) pager->pg_data, marray, 1, rtvals);
+ return rtvals[0];
+}
+
+int
+vnode_pager_putmulti(pager, m, c, sync, rtvals)
+ vm_pager_t pager;
+ vm_page_t *m;
+ int c;
+ boolean_t sync;
+ int *rtvals;
+{
+ return vnode_pager_output((vn_pager_t) pager->pg_data, m, c, rtvals);
}
+
boolean_t
vnode_pager_haspage(pager, offset)
vm_pager_t pager;
vm_offset_t offset;
{
- register vn_pager_t vnp = (vn_pager_t)pager->pg_data;
+ register vn_pager_t vnp = (vn_pager_t) pager->pg_data;
daddr_t bn;
- int err;
-
-#ifdef DEBUG
- if (vpagerdebug & VDB_FOLLOW)
- printf("vnode_pager_haspage(%x, %x)\n", pager, offset);
-#endif
+ int err;
/*
* Offset beyond end of file, do not have the page
*/
if (offset >= vnp->vnp_size) {
-#ifdef DEBUG
- if (vpagerdebug & (VDB_FAIL|VDB_SIZE))
- printf("vnode_pager_haspage: pg %x, off %x, size %x\n",
- pager, offset, vnp->vnp_size);
-#endif
- return(FALSE);
+ return (FALSE);
}
/*
- * Read the index to find the disk block to read
- * from. If there is no block, report that we don't
- * have this data.
- *
+ * Read the index to find the disk block to read from. If there is no
+ * block, report that we don't have this data.
+ *
* Assumes that the vnode has whole page or nothing.
*/
err = VOP_BMAP(vnp->vnp_vp,
offset / vnp->vnp_vp->v_mount->mnt_stat.f_bsize,
- (struct vnode **)0, &bn);
+ (struct vnode **) 0, &bn);
if (err) {
-#ifdef DEBUG
- if (vpagerdebug & VDB_FAIL)
- printf("vnode_pager_haspage: BMAP err %d, pg %x, off %x\n",
- err, pager, offset);
-#endif
- return(TRUE);
+ return (TRUE);
}
- return((long)bn < 0 ? FALSE : TRUE);
+ return ((long) bn < 0 ? FALSE : TRUE);
}
/*
- * (XXX)
* Lets the VM system know about a change in size for a file.
* If this vnode is mapped into some address space (i.e. we have a pager
* for it) we adjust our own internal size and flush any cached pages in
@@ -353,7 +321,7 @@ vnode_pager_haspage(pager, offset)
void
vnode_pager_setsize(vp, nsize)
struct vnode *vp;
- u_long nsize;
+ u_long nsize;
{
register vn_pager_t vnp;
register vm_object_t object;
@@ -364,42 +332,73 @@ vnode_pager_setsize(vp, nsize)
*/
if (vp == NULL || vp->v_type != VREG || vp->v_vmdata == NULL)
return;
+
/*
* Hasn't changed size
*/
- pager = (vm_pager_t)vp->v_vmdata;
- vnp = (vn_pager_t)pager->pg_data;
+ pager = (vm_pager_t) vp->v_vmdata;
+ vnp = (vn_pager_t) pager->pg_data;
if (nsize == vnp->vnp_size)
return;
+
/*
- * No object.
- * This can happen during object termination since
- * vm_object_page_clean is called after the object
- * has been removed from the hash table, and clean
- * may cause vnode write operations which can wind
- * up back here.
+ * No object. This can happen during object termination since
+ * vm_object_page_clean is called after the object has been removed
+ * from the hash table, and clean may cause vnode write operations
+ * which can wind up back here.
*/
object = vm_object_lookup(pager);
if (object == NULL)
return;
-#ifdef DEBUG
- if (vpagerdebug & (VDB_FOLLOW|VDB_SIZE))
- printf("vnode_pager_setsize: vp %x obj %x osz %d nsz %d\n",
- vp, object, vnp->vnp_size, nsize);
-#endif
-
/*
- * File has shrunk.
- * Toss any cached pages beyond the new EOF.
+ * File has shrunk. Toss any cached pages beyond the new EOF.
*/
- if (round_page(nsize) < round_page(vnp->vnp_size)) {
+ if (nsize < vnp->vnp_size) {
vm_object_lock(object);
vm_object_page_remove(object,
- (vm_offset_t)round_page(nsize), round_page(vnp->vnp_size));
+ round_page((vm_offset_t) nsize), vnp->vnp_size);
vm_object_unlock(object);
+
+ /*
+ * this gets rid of garbage at the end of a page that is now
+ * only partially backed by the vnode...
+ */
+ if (nsize & PAGE_MASK) {
+ vm_offset_t kva;
+ vm_page_t m;
+
+ m = vm_page_lookup(object, trunc_page((vm_offset_t) nsize));
+ if (m) {
+ kva = vm_pager_map_page(m);
+ bzero((caddr_t) kva + (nsize & PAGE_MASK),
+ round_page(nsize) - nsize);
+ vm_pager_unmap_page(kva);
+ }
+ }
+ } else {
+
+ /*
+ * this allows the filesystem and VM cache to stay in sync if
+ * the VM page hasn't been modified... After the page is
+ * removed -- it will be faulted back in from the filesystem
+ * cache.
+ */
+ if (vnp->vnp_size & PAGE_MASK) {
+ vm_page_t m;
+
+ m = vm_page_lookup(object, trunc_page(vnp->vnp_size));
+ if (m && (m->flags & PG_CLEAN)) {
+ vm_object_lock(object);
+ vm_object_page_remove(object,
+ vnp->vnp_size, vnp->vnp_size);
+ vm_object_unlock(object);
+ }
+ }
}
- vnp->vnp_size = (vm_offset_t)nsize;
+ vnp->vnp_size = (vm_offset_t) nsize;
+ object->size = round_page(nsize);
+
vm_object_deallocate(object);
}
@@ -411,14 +410,15 @@ vnode_pager_umount(mp)
struct vnode *vp;
pager = (vm_pager_t) queue_first(&vnode_pager_list);
- while (!queue_end(&vnode_pager_list, (queue_entry_t)pager)) {
+ while (!queue_end(&vnode_pager_list, (queue_entry_t) pager)) {
+
/*
- * Save the next pointer now since uncaching may
- * terminate the object and render pager invalid
+ * Save the next pointer now since uncaching may terminate the
+ * object and render pager invalid
*/
- vp = ((vn_pager_t)pager->pg_data)->vnp_vp;
+ vp = ((vn_pager_t) pager->pg_data)->vnp_vp;
npager = (vm_pager_t) queue_next(&pager->pg_list);
- if (mp == (struct mount *)0 || vp->v_mount == mp)
+ if (mp == (struct mount *) 0 || vp->v_mount == mp)
(void) vnode_pager_uncache(vp);
pager = npager;
}
@@ -441,21 +441,22 @@ vnode_pager_uncache(vp)
/*
* Not a mapped vnode
*/
- pager = (vm_pager_t)vp->v_vmdata;
+ pager = (vm_pager_t) vp->v_vmdata;
if (pager == NULL)
return (TRUE);
+
/*
- * Unlock the vnode if it is currently locked.
- * We do this since uncaching the object may result
- * in its destruction which may initiate paging
- * activity which may necessitate locking the vnode.
+ * Unlock the vnode if it is currently locked. We do this since
+ * uncaching the object may result in its destruction which may
+ * initiate paging activity which may necessitate locking the vnode.
*/
locked = VOP_ISLOCKED(vp);
if (locked)
VOP_UNLOCK(vp);
+
/*
- * Must use vm_object_lookup() as it actually removes
- * the object from the cache list.
+ * Must use vm_object_lookup() as it actually removes the object from
+ * the cache list.
*/
object = vm_object_lookup(pager);
if (object) {
@@ -465,7 +466,7 @@ vnode_pager_uncache(vp)
uncached = TRUE;
if (locked)
VOP_LOCK(vp);
- return(uncached);
+ return (uncached);
}
@@ -486,20 +487,23 @@ vnode_pager_addr(vp, address)
struct vnode *vp;
vm_offset_t address;
{
- int rtaddress;
- int bsize;
+ int rtaddress;
+ int bsize;
vm_offset_t block;
struct vnode *rtvp;
- int err;
- int vblock, voffset;
+ int err;
+ int vblock, voffset;
bsize = vp->v_mount->mnt_stat.f_bsize;
vblock = address / bsize;
voffset = address % bsize;
- err = VOP_BMAP(vp,vblock,&rtvp,&block);
+ err = VOP_BMAP(vp, vblock, &rtvp, &block);
- rtaddress = block * DEV_BSIZE + voffset;
+ if (err)
+ rtaddress = -1;
+ else
+ rtaddress = block * DEV_BSIZE + voffset;
return rtaddress;
}
@@ -512,411 +516,540 @@ vnode_pager_iodone(bp)
struct buf *bp;
{
bp->b_flags |= B_DONE;
- wakeup((caddr_t)bp);
+ wakeup((caddr_t) bp);
}
/*
- * vnode_pager_io:
- * Perform read or write operation for vnode_paging
- *
- * args:
- * vnp -- pointer to vnode pager data structure
- * containing size and vnode pointer, etc
- *
- * m -- pointer to array of vm_page_t entries to
- * do I/O to. It is not necessary to fill any
- * pages except for the reqpage entry. If a
- * page is not filled, it needs to be removed
- * from its object...
- *
- * count -- number of pages for I/O
- *
- * reqpage -- fault requested page for I/O
- * (index into vm_page_t entries above)
- *
- * rw -- UIO_READ or UIO_WRITE
- *
- * NOTICE!!!! direct writes look like that they are close to being
- * implemented. They are not really, several things need
- * to be done to make it work (subtile things.) Hack at
- * your own risk (direct writes are scarey).
- *
- * ANOTHER NOTICE!!!!
- * we currently only support direct I/O to filesystems whose
- * contiguously allocated blocksize is at least a vm page.
- * changes will be made in the future to support more flexibility.
+ * small block file system vnode pager input
+ */
+int
+vnode_pager_input_smlfs(vnp, m)
+ vn_pager_t vnp;
+ vm_page_t m;
+{
+ int i;
+ int s;
+ vm_offset_t paging_offset;
+ struct vnode *dp, *vp;
+ struct buf *bp;
+ vm_offset_t mapsize;
+ vm_offset_t foff;
+ vm_offset_t kva;
+ int fileaddr;
+ int block;
+ vm_offset_t bsize;
+ int error = 0;
+
+ paging_offset = m->object->paging_offset;
+ vp = vnp->vnp_vp;
+ bsize = vp->v_mount->mnt_stat.f_bsize;
+ foff = m->offset + paging_offset;
+
+ VOP_BMAP(vp, foff, &dp, 0);
+
+ kva = vm_pager_map_page(m);
+
+ for (i = 0; i < NBPG / bsize; i++) {
+
+ /*
+ * calculate logical block and offset
+ */
+ block = foff / bsize + i;
+ s = splbio();
+ while (bp = incore(vp, block)) {
+ int amount;
+
+ /*
+ * wait until the buffer is avail or gone
+ */
+ if (bp->b_flags & B_BUSY) {
+ bp->b_flags |= B_WANTED;
+ tsleep((caddr_t) bp, PVM, "vnwblk", 0);
+ continue;
+ }
+ amount = bsize;
+ if ((foff + bsize) > vnp->vnp_size)
+ amount = vnp->vnp_size - foff;
+
+ /*
+ * make sure that this page is in the buffer
+ */
+ if ((amount > 0) && amount <= bp->b_bcount) {
+ bp->b_flags |= B_BUSY;
+ splx(s);
+
+ /*
+ * copy the data from the buffer
+ */
+ bcopy(bp->b_un.b_addr, (caddr_t) kva + i * bsize, amount);
+ if (amount < bsize) {
+ bzero((caddr_t) kva + amount, bsize - amount);
+ }
+ bp->b_flags &= ~B_BUSY;
+ wakeup((caddr_t) bp);
+ goto nextblock;
+ }
+ break;
+ }
+ splx(s);
+ fileaddr = vnode_pager_addr(vp, foff + i * bsize);
+ if (fileaddr != -1) {
+ VHOLD(vp);
+ bp = getpbuf();
+
+ /* build a minimal buffer header */
+ bp->b_flags = B_BUSY | B_READ | B_CALL;
+ bp->b_iodone = vnode_pager_iodone;
+ bp->b_proc = curproc;
+ bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred;
+ bp->b_un.b_addr = (caddr_t) kva + i * bsize;
+ bp->b_blkno = fileaddr / DEV_BSIZE;
+ bp->b_vp = dp;
+
+ /*
+ * Should be a BLOCK or character DEVICE if we get
+ * here
+ */
+ bp->b_dev = dp->v_rdev;
+ bp->b_bcount = bsize;
+ bp->b_bufsize = bsize;
+
+ /* do the input */
+ VOP_STRATEGY(bp);
+
+ /* we definitely need to be at splbio here */
+
+ s = splbio();
+ while ((bp->b_flags & B_DONE) == 0) {
+ tsleep((caddr_t) bp, PVM, "vnsrd", 0);
+ }
+ splx(s);
+ if ((bp->b_flags & B_ERROR) != 0)
+ error = EIO;
+
+ HOLDRELE(vp);
+
+ /*
+ * free the buffer header back to the swap buffer pool
+ */
+ relpbuf(bp);
+ if (error)
+ break;
+ } else {
+ bzero((caddr_t) kva + i * bsize, bsize);
+ }
+nextblock:
+ }
+ vm_pager_unmap_page(kva);
+ if (error) {
+ return VM_PAGER_FAIL;
+ }
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m));
+ m->flags |= PG_CLEAN;
+ m->flags &= ~PG_LAUNDRY;
+ return VM_PAGER_OK;
+
+}
+
+
+/*
+ * old style vnode pager output routine
*/
+int
+vnode_pager_input_old(vnp, m)
+ vn_pager_t vnp;
+ vm_page_t m;
+{
+ int i;
+ struct uio auio;
+ struct iovec aiov;
+ int error;
+ int size;
+ vm_offset_t foff;
+ vm_offset_t kva;
+
+ error = 0;
+ foff = m->offset + m->object->paging_offset;
+ /*
+ * Return failure if beyond current EOF
+ */
+ if (foff >= vnp->vnp_size) {
+ return VM_PAGER_BAD;
+ } else {
+ size = NBPG;
+ if (foff + size > vnp->vnp_size)
+ size = vnp->vnp_size - foff;
+/*
+ * Allocate a kernel virtual address and initialize so that
+ * we can use VOP_READ/WRITE routines.
+ */
+ kva = vm_pager_map_page(m);
+ aiov.iov_base = (caddr_t) kva;
+ aiov.iov_len = size;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_offset = foff;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_resid = size;
+ auio.uio_procp = (struct proc *) 0;
+
+ error = VOP_READ(vnp->vnp_vp, &auio, IO_PAGER, curproc->p_ucred);
+ if (!error) {
+ register int count = size - auio.uio_resid;
+
+ if (count == 0)
+ error = EINVAL;
+ else if (count != NBPG)
+ bzero((caddr_t) kva + count, NBPG - count);
+ }
+ vm_pager_unmap_page(kva);
+ }
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m));
+ m->flags |= PG_CLEAN;
+ m->flags &= ~PG_LAUNDRY;
+ return error ? VM_PAGER_FAIL : VM_PAGER_OK;
+}
+
+/*
+ * generic vnode pager input routine
+ */
int
-vnode_pager_io(vnp, m, count, reqpage, rw)
+vnode_pager_input(vnp, m, count, reqpage)
register vn_pager_t vnp;
vm_page_t *m;
- int count, reqpage;
- enum uio_rw rw;
+ int count, reqpage;
{
- int i,j;
- struct uio auio;
- struct iovec aiov;
+ int i, j;
vm_offset_t kva, foff;
- int size;
- struct proc *p = curproc; /* XXX */
+ int size;
+ struct proc *p = curproc; /* XXX */
vm_object_t object;
vm_offset_t paging_offset;
struct vnode *dp, *vp;
vm_offset_t mapsize;
- int bsize;
- int errtype=0; /* 0 is file type otherwise vm type */
- int error = 0;
- int trimmed;
+ int bsize;
+
+ int first, last;
+ int reqaddr, firstaddr;
+ int block, offset;
- object = m[reqpage]->object; /* all vm_page_t items are in same object */
+ int nbp;
+ struct buf *bp;
+ int s;
+ int failflag;
+
+ int errtype = 0; /* 0 is file type otherwise vm type */
+ int error = 0;
+
+ object = m[reqpage]->object; /* all vm_page_t items are in same
+ * object */
paging_offset = object->paging_offset;
vp = vnp->vnp_vp;
bsize = vp->v_mount->mnt_stat.f_bsize;
/* get the UNDERLYING device for the file with VOP_BMAP() */
+
/*
- * originally, we did not check for an error return
- * value -- assuming an fs always has a bmap entry point
- * -- that assumption is wrong!!!
- */
- /*
- * we only do direct I/O if the file is on a local
- * BLOCK device and currently if it is a read operation only.
+ * originally, we did not check for an error return value -- assuming
+ * an fs always has a bmap entry point -- that assumption is wrong!!!
*/
kva = 0;
mapsize = 0;
- if (!VOP_BMAP(vp, m[reqpage]->offset+paging_offset, &dp, 0) &&
- rw == UIO_READ && ((dp->v_type == VBLK &&
- (vp->v_mount->mnt_stat.f_type == MOUNT_UFS)) ||
- (vp->v_mount->mnt_stat.f_type == MOUNT_NFS))) {
+ foff = m[reqpage]->offset + paging_offset;
+ if (!VOP_BMAP(vp, foff, &dp, 0)) {
+
/*
* we do not block for a kva, notice we default to a kva
* conservative behavior
*/
- kva = kmem_alloc_pageable(pager_map,
- (mapsize = count*NBPG));
- if( !kva) {
+ kva = kmem_alloc_pageable(pager_map, (mapsize = count * NBPG));
+ if (!kva) {
for (i = 0; i < count; i++) {
if (i != reqpage) {
vnode_pager_freepage(m[i]);
- m[i] = 0;
}
}
m[0] = m[reqpage];
- kva = vm_pager_map_page(m[0]);
+ kva = kmem_alloc_wait(pager_map, mapsize = NBPG);
reqpage = 0;
count = 1;
- mapsize = count*NBPG;
}
}
+ /*
+ * if we can't get a kva or we can't bmap, use old VOP code
+ */
if (!kva) {
+ for (i = 0; i < count; i++) {
+ if (i != reqpage) {
+ vnode_pager_freepage(m[i]);
+ }
+ }
+ return vnode_pager_input_old(vnp, m[reqpage]);
+
/*
- * here on I/O through VFS
+ * if the blocksize is smaller than a page size, then use
+ * special small filesystem code. NFS sometimes has a small
+ * blocksize, but it can handle large reads itself.
*/
+ } else if ((NBPG / bsize) > 1 &&
+ (vp->v_mount->mnt_stat.f_type != MOUNT_NFS)) {
+
+ kmem_free_wakeup(pager_map, kva, mapsize);
+
for (i = 0; i < count; i++) {
if (i != reqpage) {
vnode_pager_freepage(m[i]);
- m[i] = 0;
}
}
- m[0] = m[reqpage];
- foff = m[0]->offset + paging_offset;
- reqpage = 0;
- count = 1;
- /*
- * Return failure if beyond current EOF
- */
- if (foff >= vnp->vnp_size) {
- errtype = 1;
- error = VM_PAGER_BAD;
- } else {
- if (foff + NBPG > vnp->vnp_size)
- size = vnp->vnp_size - foff;
- else
- size = NBPG;
+ return vnode_pager_input_smlfs(vnp, m[reqpage]);
+ }
/*
- * Allocate a kernel virtual address and initialize so that
- * we can use VOP_READ/WRITE routines.
+ * here on direct device I/O
*/
- kva = vm_pager_map_page(m[0]);
- aiov.iov_base = (caddr_t)kva;
- aiov.iov_len = size;
- auio.uio_iov = &aiov;
- auio.uio_iovcnt = 1;
- auio.uio_offset = foff;
- auio.uio_segflg = UIO_SYSSPACE;
- auio.uio_rw = rw;
- auio.uio_resid = size;
- auio.uio_procp = (struct proc *)0;
- if (rw == UIO_READ) {
- error = VOP_READ(vp, &auio, IO_PAGER, p->p_ucred);
- } else {
- error = VOP_WRITE(vp, &auio, IO_PAGER, p->p_ucred);
- }
- if (!error) {
- register int count = size - auio.uio_resid;
- if (count == 0)
- error = EINVAL;
- else if (count != NBPG && rw == UIO_READ)
- bzero((caddr_t)kva + count, NBPG - count);
- }
- vm_pager_unmap_page(kva);
- }
- } else {
- /*
- * here on direct device I/O
- */
- int first=0, last=count;
- int reqaddr, firstaddr;
- int block, offset;
-
- struct buf *bp;
- int s;
- int failflag;
+ /*
+ * This pathetic hack gets data from the buffer cache, if it's there.
+ * I believe that this is not really necessary, and the ends can be
+ * gotten by defaulting to the normal vfs read behavior, but this
+ * might be more efficient, because the will NOT invoke read-aheads
+ * and one of the purposes of this code is to bypass the buffer cache
+ * and keep from flushing it by reading in a program.
+ */
- foff = m[reqpage]->offset + paging_offset;
+ /*
+ * calculate logical block and offset
+ */
+ block = foff / bsize;
+ offset = foff % bsize;
+ s = splbio();
+
+ /*
+ * if we have a buffer in core, then try to use it
+ */
+ while (bp = incore(vp, block)) {
+ int amount;
/*
- * This pathetic hack gets data from the buffer cache, if it's there.
- * I believe that this is not really necessary, and the ends can
- * be gotten by defaulting to the normal vfs read behavior, but this
- * might be more efficient, because the will NOT invoke read-aheads
- * and one of the purposes of this code is to bypass the buffer
- * cache and keep from flushing it by reading in a program.
- */
- /*
- * calculate logical block and offset
+ * wait until the buffer is avail or gone
*/
- block = foff / bsize;
- offset = foff % bsize;
- s = splbio();
+ if (bp->b_flags & B_BUSY) {
+ bp->b_flags |= B_WANTED;
+ tsleep((caddr_t) bp, PVM, "vnwblk", 0);
+ continue;
+ }
+ amount = NBPG;
+ if ((foff + amount) > vnp->vnp_size)
+ amount = vnp->vnp_size - foff;
/*
- * if we have a buffer in core, then try to use it
+ * make sure that this page is in the buffer
*/
- while (bp = incore(vp, block)) {
- int amount;
-
+ if ((amount > 0) && (offset + amount) <= bp->b_bcount) {
+ bp->b_flags |= B_BUSY;
+ splx(s);
+
/*
- * wait until the buffer is avail or gone
+ * map the requested page
*/
- if (bp->b_flags & B_BUSY) {
- bp->b_flags |= B_WANTED;
- tsleep ((caddr_t)bp, PVM, "vnwblk", 0);
- continue;
+ pmap_kenter(kva, VM_PAGE_TO_PHYS(m[reqpage]));
+ pmap_update();
+
+ /*
+ * copy the data from the buffer
+ */
+ bcopy(bp->b_un.b_addr + offset, (caddr_t) kva, amount);
+ if (amount < NBPG) {
+ bzero((caddr_t) kva + amount, NBPG - amount);
}
- amount = NBPG;
- if ((foff + amount) > vnp->vnp_size)
- amount = vnp->vnp_size - foff;
+ /*
+ * unmap the page and free the kva
+ */
+ pmap_remove(vm_map_pmap(pager_map), kva, kva + NBPG);
+ kmem_free_wakeup(pager_map, kva, mapsize);
/*
- * make sure that this page is in the buffer
+ * release the buffer back to the block subsystem
*/
- if ((amount > 0) && (offset + amount) <= bp->b_bcount) {
- bp->b_flags |= B_BUSY;
- splx(s);
+ bp->b_flags &= ~B_BUSY;
+ wakeup((caddr_t) bp);
- /*
- * map the requested page
- */
- pmap_enter(vm_map_pmap(pager_map),
- kva, VM_PAGE_TO_PHYS(m[reqpage]),
- VM_PROT_DEFAULT, TRUE);
- /*
- * copy the data from the buffer
- */
- bcopy(bp->b_un.b_addr + offset, (caddr_t)kva, amount);
- if (amount < NBPG) {
- bzero((caddr_t)kva + amount, NBPG - amount);
- }
- /*
- * unmap the page and free the kva
- */
- pmap_remove(vm_map_pmap(pager_map), kva, kva + NBPG);
- kmem_free_wakeup(pager_map, kva, mapsize);
- /*
- * release the buffer back to the block subsystem
- */
- bp->b_flags &= ~B_BUSY;
- wakeup((caddr_t)bp);
- /*
- * we did not have to do any work to get the requested
- * page, the read behind/ahead does not justify a read
- */
- for (i = 0; i < count; i++) {
- if (i != reqpage) {
- vnode_pager_freepage(m[i]);
- m[i] = 0;
- }
+ /*
+ * we did not have to do any work to get the requested
+ * page, the read behind/ahead does not justify a read
+ */
+ for (i = 0; i < count; i++) {
+ if (i != reqpage) {
+ vnode_pager_freepage(m[i]);
}
- /*
- * sorry for the goto
- */
- goto finishup;
}
+ count = 1;
+ reqpage = 0;
+ m[0] = m[reqpage];
+
/*
- * buffer is nowhere to be found, read from the disk
+ * sorry for the goto
*/
- break;
+ goto finishup;
}
- foff = m[reqpage]->offset + paging_offset;
- reqaddr = vnode_pager_addr(vp, foff);
/*
- * Make sure that our I/O request is contiguous.
- * Scan backward and stop for the first discontiguous
- * entry or stop for a page being in buffer cache.
+ * buffer is nowhere to be found, read from the disk
*/
- failflag = 0;
- for (i = reqpage - 1; i >= 0; --i) {
- int myaddr;
- if (failflag ||
- incore(vp, (foff + (i - reqpage) * NBPG) / bsize) ||
- (myaddr = vnode_pager_addr(vp, m[i]->offset + paging_offset))
- != reqaddr + (i - reqpage) * NBPG) {
- vnode_pager_freepage(m[i]);
- m[i] = 0;
- if (first == 0)
- first = i + 1;
- failflag = 1;
- }
- }
+ break;
+ }
+ splx(s);
- /*
- * Scan forward and stop for the first non-contiguous
- * entry or stop for a page being in buffer cache.
- */
- failflag = 0;
- for (i = reqpage + 1; i < count; i++) {
- int myaddr;
- if (failflag ||
- incore(vp, (foff + (i - reqpage) * NBPG) / bsize) ||
- (myaddr = vnode_pager_addr(vp, m[i]->offset + paging_offset))
- != reqaddr + (i - reqpage) * NBPG) {
- vnode_pager_freepage(m[i]);
- m[i] = 0;
- if (last == count)
- last = i;
- failflag = 1;
- }
+ reqaddr = vnode_pager_addr(vp, foff);
+
+ /*
+ * Make sure that our I/O request is contiguous. Scan backward and
+ * stop for the first discontiguous entry or stop for a page being in
+ * buffer cache.
+ */
+ failflag = 0;
+ first = reqpage;
+ for (i = reqpage - 1; i >= 0; --i) {
+ if (failflag ||
+ incore(vp, (foff + (i - reqpage) * NBPG) / bsize) ||
+ (vnode_pager_addr(vp, m[i]->offset + paging_offset))
+ != reqaddr + (i - reqpage) * NBPG) {
+ vnode_pager_freepage(m[i]);
+ failflag = 1;
+ } else {
+ first = i;
}
+ }
- /*
- * the first and last page have been calculated now, move input
- * pages to be zero based...
- */
- count = last;
- if (first != 0) {
- for (i = first; i < count; i++) {
- m[i - first] = m[i];
- }
- count -= first;
- reqpage -= first;
+ /*
+ * Scan forward and stop for the first non-contiguous entry or stop
+ * for a page being in buffer cache.
+ */
+ failflag = 0;
+ last = reqpage + 1;
+ for (i = reqpage + 1; i < count; i++) {
+ if (failflag ||
+ incore(vp, (foff + (i - reqpage) * NBPG) / bsize) ||
+ (vnode_pager_addr(vp, m[i]->offset + paging_offset))
+ != reqaddr + (i - reqpage) * NBPG) {
+ vnode_pager_freepage(m[i]);
+ failflag = 1;
+ } else {
+ last = i + 1;
}
+ }
+ /*
+ * the first and last page have been calculated now, move input pages
+ * to be zero based...
+ */
+ count = last;
+ if (first != 0) {
+ for (i = first; i < count; i++) {
+ m[i - first] = m[i];
+ }
+ count -= first;
+ reqpage -= first;
+ }
- /*
- * calculate the file virtual address for the transfer
- */
- foff = m[0]->offset + paging_offset;
- /*
- * and get the disk physical address (in bytes)
- */
- firstaddr = vnode_pager_addr(vp, foff);
+ /*
+ * calculate the file virtual address for the transfer
+ */
+ foff = m[0]->offset + paging_offset;
- /*
- * calculate the size of the transfer
- */
- if ((m[count - 1]->offset + paging_offset) + NBPG > vnp->vnp_size)
- size = vnp->vnp_size - foff;
- else
- size = count * NBPG;
+ /*
+ * and get the disk physical address (in bytes)
+ */
+ firstaddr = vnode_pager_addr(vp, foff);
+ /*
+ * calculate the size of the transfer
+ */
+ size = count * NBPG;
+ if ((foff + size) > vnp->vnp_size)
+ size = vnp->vnp_size - foff;
- /*
- * and map the pages to be read into the kva
- */
- for (i = 0; i < count; i++)
- pmap_enter(vm_map_pmap(pager_map),
- kva + NBPG * i, VM_PAGE_TO_PHYS(m[i]),
- VM_PROT_DEFAULT, TRUE);
- VHOLD(vp);
- bp = getpbuf();
-
- /* build a minimal buffer header */
- bzero((caddr_t)bp, sizeof(struct buf));
- bp->b_flags = B_BUSY | B_READ | B_CALL;
- bp->b_iodone = vnode_pager_iodone;
- /* B_PHYS is not set, but it is nice to fill this in */
- /* bp->b_proc = &proc0; */
- bp->b_proc = curproc;
- bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred;
- bp->b_un.b_addr = (caddr_t) kva;
- bp->b_blkno = firstaddr / DEV_BSIZE;
- bp->b_vp = dp;
-
- /* Should be a BLOCK or character DEVICE if we get here */
- bp->b_dev = dp->v_rdev;
- bp->b_bcount = NBPG * count;
-
- /* do the input */
- VOP_STRATEGY(bp);
-
- /* we definitely need to be at splbio here */
-
- while ((bp->b_flags & B_DONE) == 0) {
- tsleep((caddr_t)bp, PVM, "vnread", 0);
- }
- splx(s);
- if ((bp->b_flags & B_ERROR) != 0)
- error = EIO;
+ /*
+ * round up physical size for real devices
+ */
+ if (dp->v_type == VBLK || dp->v_type == VCHR)
+ size = (size + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1);
- if (!error) {
- if (size != count * NBPG)
- bzero((caddr_t)kva + size, NBPG * count - size);
- }
- HOLDRELE(vp);
+ /*
+ * and map the pages to be read into the kva
+ */
+ for (i = 0; i < count; i++)
+ pmap_kenter(kva + NBPG * i, VM_PAGE_TO_PHYS(m[i]));
+
+ pmap_update();
+ VHOLD(vp);
+ bp = getpbuf();
+
+ /* build a minimal buffer header */
+ bp->b_flags = B_BUSY | B_READ | B_CALL;
+ bp->b_iodone = vnode_pager_iodone;
+ /* B_PHYS is not set, but it is nice to fill this in */
+ bp->b_proc = curproc;
+ bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred;
+ bp->b_un.b_addr = (caddr_t) kva;
+ bp->b_blkno = firstaddr / DEV_BSIZE;
+ bp->b_vp = dp;
+
+ /* Should be a BLOCK or character DEVICE if we get here */
+ bp->b_dev = dp->v_rdev;
+ bp->b_bcount = size;
+ bp->b_bufsize = size;
+
+ /* do the input */
+ VOP_STRATEGY(bp);
+
+ s = splbio();
+ /* we definitely need to be at splbio here */
+
+ while ((bp->b_flags & B_DONE) == 0) {
+ tsleep((caddr_t) bp, PVM, "vnread", 0);
+ }
+ splx(s);
+ if ((bp->b_flags & B_ERROR) != 0)
+ error = EIO;
- pmap_remove(vm_map_pmap(pager_map), kva, kva + NBPG * count);
- kmem_free_wakeup(pager_map, kva, mapsize);
+ if (!error) {
+ if (size != count * NBPG)
+ bzero((caddr_t) kva + size, NBPG * count - size);
+ }
+ HOLDRELE(vp);
- /*
- * free the buffer header back to the swap buffer pool
- */
- relpbuf(bp);
+ pmap_remove(vm_map_pmap(pager_map), kva, kva + NBPG * count);
+ kmem_free_wakeup(pager_map, kva, mapsize);
- }
+ /*
+ * free the buffer header back to the swap buffer pool
+ */
+ relpbuf(bp);
finishup:
- if (rw == UIO_READ)
for (i = 0; i < count; i++) {
- /*
- * we dont mess with pages that have been already
- * deallocated....
- */
- if (!m[i])
- continue;
pmap_clear_modify(VM_PAGE_TO_PHYS(m[i]));
m[i]->flags |= PG_CLEAN;
m[i]->flags &= ~PG_LAUNDRY;
if (i != reqpage) {
+
/*
- * whether or not to leave the page activated
- * is up in the air, but we should put the page
- * on a page queue somewhere. (it already is in
- * the object).
- * Result: It appears that emperical results show
- * that deactivating pages is best.
+ * whether or not to leave the page activated is up in
+ * the air, but we should put the page on a page queue
+ * somewhere. (it already is in the object). Result:
+ * It appears that emperical results show that
+ * deactivating pages is best.
*/
+
/*
- * just in case someone was asking for this
- * page we now tell them that it is ok to use
+ * just in case someone was asking for this page we
+ * now tell them that it is ok to use
*/
if (!error) {
vm_page_deactivate(m[i]);
@@ -927,15 +1060,380 @@ finishup:
}
}
}
- if (!error && rw == UIO_WRITE) {
- pmap_clear_modify(VM_PAGE_TO_PHYS(m[reqpage]));
- m[reqpage]->flags |= PG_CLEAN;
- m[reqpage]->flags &= ~PG_LAUNDRY;
- }
if (error) {
- printf("vnode pager error: %d\n", error);
+ printf("vnode pager read error: %d\n", error);
}
if (errtype)
return error;
return (error ? VM_PAGER_FAIL : VM_PAGER_OK);
}
+
+/*
+ * old-style vnode pager output routine
+ */
+int
+vnode_pager_output_old(vnp, m)
+ register vn_pager_t vnp;
+ vm_page_t m;
+{
+ vm_offset_t foff;
+ vm_offset_t kva;
+ vm_offset_t size;
+ struct iovec aiov;
+ struct uio auio;
+ struct vnode *vp;
+ int error;
+
+ vp = vnp->vnp_vp;
+ foff = m->offset + m->object->paging_offset;
+
+ /*
+ * Return failure if beyond current EOF
+ */
+ if (foff >= vnp->vnp_size) {
+ return VM_PAGER_BAD;
+ } else {
+ size = NBPG;
+ if (foff + size > vnp->vnp_size)
+ size = vnp->vnp_size - foff;
+/*
+ * Allocate a kernel virtual address and initialize so that
+ * we can use VOP_WRITE routines.
+ */
+ kva = vm_pager_map_page(m);
+ aiov.iov_base = (caddr_t) kva;
+ aiov.iov_len = size;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_offset = foff;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_WRITE;
+ auio.uio_resid = size;
+ auio.uio_procp = (struct proc *) 0;
+
+ error = VOP_WRITE(vp, &auio, IO_PAGER, curproc->p_ucred);
+
+ if (!error) {
+ if ((size - auio.uio_resid) == 0) {
+ error = EINVAL;
+ }
+ }
+ vm_pager_unmap_page(kva);
+ return error ? VM_PAGER_FAIL : VM_PAGER_OK;
+ }
+}
+
+/*
+ * vnode pager output on a small-block file system
+ */
+int
+vnode_pager_output_smlfs(vnp, m)
+ vn_pager_t vnp;
+ vm_page_t m;
+{
+ int i;
+ int s;
+ vm_offset_t paging_offset;
+ struct vnode *dp, *vp;
+ struct buf *bp;
+ vm_offset_t mapsize;
+ vm_offset_t foff;
+ vm_offset_t kva;
+ int fileaddr;
+ int block;
+ vm_offset_t bsize;
+ int error = 0;
+
+ paging_offset = m->object->paging_offset;
+ vp = vnp->vnp_vp;
+ bsize = vp->v_mount->mnt_stat.f_bsize;
+ foff = m->offset + paging_offset;
+
+ VOP_BMAP(vp, foff, &dp, 0);
+ kva = vm_pager_map_page(m);
+ for (i = 0; !error && i < (NBPG / bsize); i++) {
+
+ /*
+ * calculate logical block and offset
+ */
+ fileaddr = vnode_pager_addr(vp, foff + i * bsize);
+ if (fileaddr != -1) {
+ s = splbio();
+ if (bp = incore(vp, (foff / bsize) + i)) {
+ bp = getblk(vp, (foff / bsize) + i, bp->b_bufsize);
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ }
+ splx(s);
+
+ VHOLD(vp);
+ bp = getpbuf();
+
+ /* build a minimal buffer header */
+ bp->b_flags = B_BUSY | B_CALL | B_WRITE;
+ bp->b_iodone = vnode_pager_iodone;
+ bp->b_proc = curproc;
+ bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred;
+ bp->b_un.b_addr = (caddr_t) kva + i * bsize;
+ bp->b_blkno = fileaddr / DEV_BSIZE;
+ bp->b_vp = dp;
+ ++dp->v_numoutput;
+ /* for NFS */
+ bp->b_dirtyoff = 0;
+ bp->b_dirtyend = bsize;
+
+ /*
+ * Should be a BLOCK or character DEVICE if we get
+ * here
+ */
+ bp->b_dev = dp->v_rdev;
+ bp->b_bcount = bsize;
+ bp->b_bufsize = bsize;
+
+ /* do the input */
+ VOP_STRATEGY(bp);
+
+ /* we definitely need to be at splbio here */
+
+ s = splbio();
+ while ((bp->b_flags & B_DONE) == 0) {
+ tsleep((caddr_t) bp, PVM, "vnswrt", 0);
+ }
+ splx(s);
+ if ((bp->b_flags & B_ERROR) != 0)
+ error = EIO;
+
+ HOLDRELE(vp);
+
+ /*
+ * free the buffer header back to the swap buffer pool
+ */
+ relpbuf(bp);
+ }
+ }
+ vm_pager_unmap_page(kva);
+ if (error)
+ return VM_PAGER_FAIL;
+ else
+ return VM_PAGER_OK;
+}
+
+/*
+ * generic vnode pager output routine
+ */
+int
+vnode_pager_output(vnp, m, count, rtvals)
+ vn_pager_t vnp;
+ vm_page_t *m;
+ int count;
+ int *rtvals;
+{
+ int i, j;
+ vm_offset_t kva, foff;
+ int size;
+ struct proc *p = curproc; /* XXX */
+ vm_object_t object;
+ vm_offset_t paging_offset;
+ struct vnode *dp, *vp;
+ struct buf *bp;
+ vm_offset_t mapsize;
+ vm_offset_t reqaddr;
+ int bsize;
+ int s;
+
+ int error = 0;
+
+retryoutput:
+ object = m[0]->object; /* all vm_page_t items are in same object */
+ paging_offset = object->paging_offset;
+
+ vp = vnp->vnp_vp;
+ bsize = vp->v_mount->mnt_stat.f_bsize;
+
+ for (i = 0; i < count; i++)
+ rtvals[i] = VM_PAGER_TRYAGAIN;
+
+ /*
+ * if the filesystem does not have a bmap, then use the old code
+ */
+ if (VOP_BMAP(vp, m[0]->offset + paging_offset, &dp, 0)) {
+
+ rtvals[0] = vnode_pager_output_old(vnp, m[0]);
+
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m[0]));
+ m[0]->flags |= PG_CLEAN;
+ m[0]->flags &= ~PG_LAUNDRY;
+ return rtvals[0];
+ }
+
+ /*
+ * if the filesystem has a small blocksize, then use the small block
+ * filesystem output code
+ */
+ if ((bsize < NBPG) &&
+ (vp->v_mount->mnt_stat.f_type != MOUNT_NFS)) {
+
+ for (i = 0; i < count; i++) {
+ rtvals[i] = vnode_pager_output_smlfs(vnp, m[i]);
+ if (rtvals[i] == VM_PAGER_OK) {
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m[i]));
+ m[i]->flags |= PG_CLEAN;
+ m[i]->flags &= ~PG_LAUNDRY;
+ }
+ }
+ return rtvals[0];
+ }
+
+ /*
+ * get some kva for the output
+ */
+ kva = kmem_alloc_pageable(pager_map, (mapsize = count * NBPG));
+ if (!kva) {
+ kva = kmem_alloc_pageable(pager_map, (mapsize = NBPG));
+ count = 1;
+ if (!kva)
+ return rtvals[0];
+ }
+ for (i = 0; i < count; i++) {
+ foff = m[i]->offset + paging_offset;
+ if (foff >= vnp->vnp_size) {
+ for (j = i; j < count; j++)
+ rtvals[j] = VM_PAGER_BAD;
+ count = i;
+ break;
+ }
+ }
+ if (count == 0) {
+ return rtvals[0];
+ }
+ foff = m[0]->offset + paging_offset;
+ reqaddr = vnode_pager_addr(vp, foff);
+
+ /*
+ * Scan forward and stop for the first non-contiguous entry or stop
+ * for a page being in buffer cache.
+ */
+ for (i = 1; i < count; i++) {
+ if (vnode_pager_addr(vp, m[i]->offset + paging_offset)
+ != reqaddr + i * NBPG) {
+ count = i;
+ break;
+ }
+ }
+
+ /*
+ * calculate the size of the transfer
+ */
+ size = count * NBPG;
+ if ((foff + size) > vnp->vnp_size)
+ size = vnp->vnp_size - foff;
+
+ /*
+ * round up physical size for real devices
+ */
+ if (dp->v_type == VBLK || dp->v_type == VCHR)
+ size = (size + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1);
+
+ /*
+ * and map the pages to be read into the kva
+ */
+ for (i = 0; i < count; i++)
+ pmap_kenter(kva + NBPG * i, VM_PAGE_TO_PHYS(m[i]));
+ pmap_update();
+/*
+ printf("vnode: writing foff: %d, devoff: %d, size: %d\n",
+ foff, reqaddr, size);
+*/
+
+ /*
+ * next invalidate the incore vfs_bio data
+ */
+ for (i = 0; i < count; i++) {
+ int filblock = (foff + i * NBPG) / bsize;
+ struct buf *fbp;
+
+ s = splbio();
+ if (fbp = incore(vp, filblock)) {
+ fbp = getblk(vp, filblock, fbp->b_bufsize);
+ if (fbp->b_flags & B_DELWRI) {
+ if (fbp->b_bufsize <= NBPG)
+ fbp->b_flags &= ~B_DELWRI;
+ else {
+ bwrite(fbp);
+ fbp = getblk(vp, filblock,
+ fbp->b_bufsize);
+ }
+ }
+ fbp->b_flags |= B_INVAL;
+ brelse(fbp);
+ }
+ splx(s);
+ }
+
+
+ VHOLD(vp);
+ bp = getpbuf();
+
+ /* build a minimal buffer header */
+ bp->b_flags = B_BUSY | B_WRITE | B_CALL;
+ bp->b_iodone = vnode_pager_iodone;
+ /* B_PHYS is not set, but it is nice to fill this in */
+ /* bp->b_proc = &proc0; */
+ bp->b_proc = curproc;
+ bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred;
+ bp->b_un.b_addr = (caddr_t) kva;
+ bp->b_blkno = reqaddr / DEV_BSIZE;
+ bp->b_vp = dp;
+ ++dp->v_numoutput;
+
+ /* Should be a BLOCK or character DEVICE if we get here */
+ bp->b_dev = dp->v_rdev;
+ /* for NFS */
+ bp->b_dirtyoff = 0;
+ bp->b_dirtyend = size;
+
+ bp->b_bcount = size;
+ bp->b_bufsize = size;
+
+ /* do the output */
+ VOP_STRATEGY(bp);
+
+ s = splbio();
+
+ /* we definitely need to be at splbio here */
+
+ while ((bp->b_flags & B_DONE) == 0) {
+ tsleep((caddr_t) bp, PVM, "vnwrite", 0);
+ }
+ splx(s);
+
+ if ((bp->b_flags & B_ERROR) != 0)
+ error = EIO;
+
+ HOLDRELE(vp);
+
+ pmap_remove(vm_map_pmap(pager_map), kva, kva + NBPG * count);
+ kmem_free_wakeup(pager_map, kva, mapsize);
+
+ /*
+ * free the buffer header back to the swap buffer pool
+ */
+ relpbuf(bp);
+
+ if (!error) {
+ for (i = 0; i < count; i++) {
+ pmap_clear_modify(VM_PAGE_TO_PHYS(m[i]));
+ m[i]->flags |= PG_CLEAN;
+ m[i]->flags &= ~PG_LAUNDRY;
+ rtvals[i] = VM_PAGER_OK;
+ }
+ } else if (count != 1) {
+ error = 0;
+ count = 1;
+ goto retryoutput;
+ }
+ if (error) {
+ printf("vnode pager write error: %d\n", error);
+ }
+ return (error ? VM_PAGER_FAIL : VM_PAGER_OK);
+}