Index: head/sys/amd64/include/cpufunc.h =================================================================== --- head/sys/amd64/include/cpufunc.h (revision 18264) +++ head/sys/amd64/include/cpufunc.h (revision 18265) @@ -1,427 +1,379 @@ /*- * Copyright (c) 1993 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: cpufunc.h,v 1.53 1996/07/23 07:45:19 asami Exp $ + * $Id: cpufunc.h,v 1.54 1996/08/01 20:29:28 wollman Exp $ */ /* * Functions to provide access to special i386 instructions. */ #ifndef _MACHINE_CPUFUNC_H_ #define _MACHINE_CPUFUNC_H_ #include #include #ifdef __GNUC__ static __inline void breakpoint(void) { __asm __volatile("int $3"); } static __inline void disable_intr(void) { __asm __volatile("cli" : : : "memory"); } static __inline void enable_intr(void) { __asm __volatile("sti"); } #define HAVE_INLINE_FFS static __inline int ffs(int mask) { int result; /* * bsfl turns out to be not all that slow on 486's. It can beaten * using a binary search to reduce to 4 bits and then a table lookup, * but only if the code is inlined and in the cache, and the code * is quite large so inlining it probably busts the cache. * * Note that gcc-2's builtin ffs would be used if we didn't declare * this inline or turn off the builtin. The builtin is faster but * broken in gcc-2.4.5 and slower but working in gcc-2.5 and 2.6. */ __asm __volatile("testl %0,%0; je 1f; bsfl %0,%0; incl %0; 1:" : "=r" (result) : "0" (mask)); return (result); } #define HAVE_INLINE_FLS static __inline int fls(int mask) { int result; __asm __volatile("testl %0,%0; je 1f; bsrl %0,%0; incl %0; 1:" : "=r" (result) : "0" (mask)); return (result); } #if __GNUC__ < 2 #define inb(port) inbv(port) #define outb(port, data) outbv(port, data) #else /* __GNUC >= 2 */ /* * The following complications are to get around gcc not having a * constraint letter for the range 0..255. We still put "d" in the * constraint because "i" isn't a valid constraint when the port * isn't constant. This only matters for -O0 because otherwise * the non-working version gets optimized away. * * Use an expression-statement instead of a conditional expression * because gcc-2.6.0 would promote the operands of the conditional * and produce poor code for "if ((inb(var) & const1) == const2)". */ #define inb(port) ({ \ u_char _data; \ if (__builtin_constant_p((int) (port)) && (port) < 256ul) \ _data = inbc(port); \ else \ _data = inbv(port); \ _data; }) #define outb(port, data) \ (__builtin_constant_p((int) (port)) && (port) < 256ul \ ? outbc(port, data) : outbv(port, data)) static __inline u_char inbc(u_int port) { u_char data; __asm __volatile("inb %1,%0" : "=a" (data) : "id" ((u_short)(port))); return (data); } static __inline void outbc(u_int port, u_char data) { __asm __volatile("outb %0,%1" : : "a" (data), "id" ((u_short)(port))); } #endif /* __GNUC <= 2 */ static __inline u_char inbv(u_int port) { u_char data; /* * We use %%dx and not %1 here because i/o is done at %dx and not at * %edx, while gcc generates inferior code (movw instead of movl) * if we tell it to load (u_short) port. */ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port)); return (data); } static __inline u_long inl(u_int port) { u_long data; __asm __volatile("inl %%dx,%0" : "=a" (data) : "d" (port)); return (data); } static __inline void insb(u_int port, void *addr, size_t cnt) { __asm __volatile("cld; rep; insb" : : "d" (port), "D" (addr), "c" (cnt) : "di", "cx", "memory"); } static __inline void insw(u_int port, void *addr, size_t cnt) { __asm __volatile("cld; rep; insw" : : "d" (port), "D" (addr), "c" (cnt) : "di", "cx", "memory"); } static __inline void insl(u_int port, void *addr, size_t cnt) { __asm __volatile("cld; rep; insl" : : "d" (port), "D" (addr), "c" (cnt) : "di", "cx", "memory"); } static __inline u_short inw(u_int port) { u_short data; __asm __volatile("inw %%dx,%0" : "=a" (data) : "d" (port)); return (data); } static __inline u_int loadandclear(u_int *addr) { u_int result; __asm __volatile("xorl %0,%0; xchgl %1,%0" : "=&r" (result) : "m" (*addr)); return (result); } static __inline void outbv(u_int port, u_char data) { u_char al; /* * Use an unnecessary assignment to help gcc's register allocator. * This make a large difference for gcc-1.40 and a tiny difference * for gcc-2.6.0. For gcc-1.40, al had to be ``asm("ax")'' for * best results. gcc-2.6.0 can't handle this. */ al = data; __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port)); } static __inline void outl(u_int port, u_long data) { /* * outl() and outw() aren't used much so we haven't looked at * possible micro-optimizations such as the unnecessary * assignment for them. */ __asm __volatile("outl %0,%%dx" : : "a" (data), "d" (port)); } static __inline void outsb(u_int port, void *addr, size_t cnt) { __asm __volatile("cld; rep; outsb" : : "d" (port), "S" (addr), "c" (cnt) : "si", "cx"); } static __inline void outsw(u_int port, void *addr, size_t cnt) { __asm __volatile("cld; rep; outsw" : : "d" (port), "S" (addr), "c" (cnt) : "si", "cx"); } static __inline void outsl(u_int port, void *addr, size_t cnt) { __asm __volatile("cld; rep; outsl" : : "d" (port), "S" (addr), "c" (cnt) : "si", "cx"); } static __inline void outw(u_int port, u_short data) { __asm __volatile("outw %0,%%dx" : : "a" (data), "d" (port)); } -#ifdef PC98 -#include - -static inline u_char -epson_inb(u_int port) -{ - u_char data; - - outb(0x43f, 0x42); - data = inb(port); - outb(0x43f, 0x40); - return (data); -} - -static inline void -epson_outb(u_int port, u_char data) -{ - outb(0x43f, 0x42); - outb(port,data); - outb(0x43f, 0x40); -} - -static inline void -epson_insw(u_int port, void *addr, size_t cnt) -{ - int s; - - s = splbio(); - outb(0x43f, 0x42); - disable_intr(); - insw((u_int)port, (void *)addr, (size_t)cnt); - outb(0x43f, 0x40); - splx(s); -} - -static inline void -epson_outsw(u_int port, void *addr, size_t cnt) -{ - int s; - - s = splbio(); - outb(0x43f, 0x42); - disable_intr(); - outsw((u_int)port, (void *)addr, (size_t)cnt); - outb(0x43f, 0x40); - splx(s); -} -#endif /* PC98 */ static __inline void pmap_update(void) { u_long temp; /* * This should be implemented as load_cr3(rcr3()) when load_cr3() * is inlined. */ __asm __volatile("movl %%cr3, %0; movl %0, %%cr3" : "=r" (temp) : : "memory"); } static __inline u_long rcr2(void) { u_long data; __asm __volatile("movl %%cr2,%0" : "=r" (data)); return (data); } static __inline u_long read_eflags(void) { u_long ef; __asm __volatile("pushfl; popl %0" : "=r" (ef)); return (ef); } static __inline quad_t rdmsr(u_int msr) { quad_t rv; __asm __volatile(".byte 0x0f, 0x32" : "=A" (rv) : "c" (msr)); return (rv); } static __inline quad_t rdpmc(u_int pmc) { quad_t rv; __asm __volatile(".byte 0x0f, 0x33" : "=A" (rv) : "c" (pmc)); return (rv); } static __inline quad_t rdtsc(void) { quad_t rv; __asm __volatile(".byte 0x0f, 0x31" : "=A" (rv)); return (rv); } static __inline void setbits(volatile unsigned *addr, u_int bits) { __asm __volatile("orl %1,%0" : "=m" (*addr) : "ir" (bits)); } static __inline void write_eflags(u_long ef) { __asm __volatile("pushl %0; popfl" : : "r" (ef)); } static __inline void wrmsr(u_int msr, quad_t newval) { __asm __volatile(".byte 0x0f, 0x30" : : "A" (newval), "c" (msr)); } #else /* !__GNUC__ */ int breakpoint __P((void)); void disable_intr __P((void)); void enable_intr __P((void)); u_char inb __P((u_int port)); u_long inl __P((u_int port)); void insb __P((u_int port, void *addr, size_t cnt)); void insl __P((u_int port, void *addr, size_t cnt)); void insw __P((u_int port, void *addr, size_t cnt)); u_short inw __P((u_int port)); u_int loadandclear __P((u_int *addr)); void outb __P((u_int port, u_char data)); void outl __P((u_int port, u_long data)); void outsb __P((u_int port, void *addr, size_t cnt)); void outsl __P((u_int port, void *addr, size_t cnt)); void outsw __P((u_int port, void *addr, size_t cnt)); void outw __P((u_int port, u_short data)); void pmap_update __P((void)); u_long rcr2 __P((void)); quad_t rdmsr __P((u_int msr)); quad_t rdpmc __P((u_int pmc)); quad_t rdtsc __P((void)); u_long read_eflags __P((void)); void setbits __P((volatile unsigned *addr, u_int bits)); void write_eflags __P((u_long ef)); void wrmsr __P((u_int msr, quad_t newval)); #endif /* __GNUC__ */ void load_cr0 __P((u_long cr0)); void load_cr3 __P((u_long cr3)); void ltr __P((u_short sel)); u_int rcr0 __P((void)); u_long rcr3 __P((void)); #include /* XXX belongs elsewhere */ #endif /* !_MACHINE_CPUFUNC_H_ */ Index: head/sys/conf/files.pc98 =================================================================== --- head/sys/conf/files.pc98 (revision 18264) +++ head/sys/conf/files.pc98 (revision 18265) @@ -1,280 +1,281 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # -# modified for PC-9801 after: -# $Id: files.pc98,v 1.5 1996/09/04 09:52:08 asami Exp $ +# modified for PC-9801 # +# $Id: files.i386,v 1.140 1996/09/11 19:53:30 phk Exp $ +# aic7xxx_asm optional ahc device-driver \ dependency "$S/dev/aic7xxx/aic7xxx_asm.c" \ compile-with "${CC} -Wall -o $@ $>" \ no-obj no-implicit-rule \ clean "aic7xxx_asm" # aic7xxx_seq.h optional ahc device-driver \ compile-with "./aic7xxx_asm -o $@ $S/dev/aic7xxx/aic7xxx.seq" \ no-obj no-implicit-rule before-depend \ clean "aic7xxx_seq.h" \ dependency "$S/dev/aic7xxx/aic7xxx_reg.h $S/dev/aic7xxx/aic7xxx.seq aic7xxx_asm" # linux_genassym optional compat_linux \ dependency "$S/i386/linux/linux_genassym.c $S/i386/linux/linux.h" \ compile-with "${CC} ${CFLAGS} -o $@ $<" \ no-obj no-implicit-rule \ clean "linux_genassym" # linux_assym.h optional compat_linux \ dependency "linux_genassym" \ compile-with "./linux_genassym > $@" \ no-obj no-implicit-rule before-depend \ clean "linux_assym.h" # i386/scsi/93cx6.c optional ahc device-driver i386/apm/apm.c optional apm device-driver i386/apm/apm_setup.s optional apm #i386/eisa/3c5x9.c optional ep device-driver #i386/eisa/aic7770.c optional ahc device-driver #i386/eisa/aha1742.c optional ahb device-driver #i386/eisa/bt74x.c optional bt device-driver i386/eisa/eisaconf.c optional eisa i386/i386/autoconf.c standard device-driver i386/i386/cons.c standard i386/i386/db_disasm.c optional ddb i386/i386/db_interface.c optional ddb i386/i386/db_trace.c optional ddb i386/i386/i386-gdbstub.c optional ddb pc98/i386/exception.s standard i386/i386/identcpu.c standard i386/i386/in_cksum.c optional inet # locore.s needs to be handled in Makefile to put it first. Otherwise it's # now normal. # i386/i386/locore.s standard pc98/i386/machdep.c standard pc98/pc98/pc98_machdep.c standard i386/i386/math_emulate.c optional math_emulate i386/i386/mem.c standard pc98/i386/microtime.s standard i386/i386/perfmon.c optional perfmon pc98/i386/pmap.c standard i386/i386/procfs_machdep.c standard i386/i386/support.s standard i386/i386/swtch.s standard i386/i386/sys_machdep.c standard pc98/i386/trap.c standard -pc98/i386/userconfig.c standard +pc98/i386/userconfig.c optional userconfig pc98/i386/vm_machdep.c standard i386/ibcs2/ibcs2_fcntl.c optional ibcs2 i386/ibcs2/ibcs2_stat.c optional ibcs2 i386/ibcs2/ibcs2_ipc.c optional ibcs2 i386/ibcs2/ibcs2_msg.c optional ibcs2 i386/ibcs2/ibcs2_misc.c optional ibcs2 i386/ibcs2/ibcs2_other.c optional ibcs2 i386/ibcs2/ibcs2_signal.c optional ibcs2 i386/ibcs2/ibcs2_ioctl.c optional ibcs2 i386/ibcs2/ibcs2_socksys.c optional ibcs2 i386/ibcs2/ibcs2_sysi86.c optional ibcs2 i386/ibcs2/ibcs2_util.c optional ibcs2 i386/ibcs2/ibcs2_isc.c optional ibcs2 i386/ibcs2/ibcs2_isc_sysent.c optional ibcs2 i386/ibcs2/ibcs2_xenix.c optional ibcs2 i386/ibcs2/ibcs2_xenix_sysent.c optional ibcs2 i386/ibcs2/ibcs2_errno.c optional ibcs2 i386/ibcs2/ibcs2_sysent.c optional ibcs2 i386/ibcs2/ibcs2_sysvec.c optional ibcs2 i386/ibcs2/imgact_coff.c optional ibcs2 pc98/pc98/bs/bs.c optional bs device-driver pc98/pc98/bs/bsfunc.c optional bs device-driver pc98/pc98/bs/bshw.c optional bs device-driver pc98/pc98/bs/bsif.c optional bs device-driver pc98/pc98/sbic55.c optional sbic device-driver #i386/pc98/aha1542.c optional aha device-driver pc98/pc98/aic6360.c optional aic device-driver pc98/pc98/b004.c optional bqu device-driver i386/pc98/bt742a.c optional bt device-driver i386/pc98/bt5xx-445.c optional bt device-driver pc98/pc98/clock.c standard pc98/isa/cronyx.c optional cx device-driver pc98/isa/ctx.c optional ctx device-driver pc98/isa/cx.c optional cx device-driver pc98/isa/cy.c optional cy device-driver pc98/pc98/diskslice_machdep.c standard pc98/pc98/atcompat_diskslice.c optional compat_atdisk i386/isa/elink.c optional ep device-driver #i386/isa/elink.c optional ie device-driver pc98/pc98/fd.c optional fd device-driver pc98/pc98/ft.c optional ft device-driver pc98/pc98/gpib.c optional gp device-driver pc98/isa/asc.c optional asc device-driver pc98/isa/gsc.c optional gsc device-driver pc98/isa/if_ar.c optional ar device-driver pc98/isa/if_cx.c optional cx device-driver pc98/pc98/if_ed.c optional ed device-driver pc98/pc98/if_el.c optional el device-driver i386/isa/if_ep.c optional ep device-driver pc98/pc98/if_fe.c optional fe device-driver #pc98/isa/if_ie.c optional ie device-driver #pc98/isa/if_ix.c optional ix device-driver #pc98/isa/if_le.c optional le device-driver #pc98/isa/if_lnc.c optional lnc device-driver #i386/isa/if_sr.c optional sr device-driver #pc98/isa/if_ze.c optional ze device-driver i386/isa/if_zp.c optional zp device-driver pc98/pc98/pc98.c optional isa device-driver pc98/isa/istallion.c optional stli device-driver pc98/isa/joy.c optional joy device-driver pc98/pc98/labpc.c optional labpc device-driver pc98/pc98/lpt.c optional lpt device-driver pc98/pc98/mcd.c optional mcd device-driver pc98/pc98/mse.c optional mse device-driver pc98/isa/ncr5380.c optional nca device-driver pc98/pc98/npx.c optional npx device-driver pc98/pc98/pcaudio.c optional pca device-driver pc98/pc98/matcd/matcd.c optional matcd device-driver pc98/pc98/pcibus.c optional pci device-driver i386/isa/pcicx.c optional ze device-driver i386/isa/pcicx.c optional zp device-driver pc98/isa/pcvt/pcvt_drv.c optional vt device-driver pc98/isa/pcvt/pcvt_ext.c optional vt device-driver pc98/isa/pcvt/pcvt_kbd.c optional vt device-driver pc98/isa/pcvt/pcvt_out.c optional vt device-driver pc98/isa/pcvt/pcvt_sup.c optional vt device-driver pc98/isa/pcvt/pcvt_vtf.c optional vt device-driver pc98/pc98/prof_machdep.c optional profiling-routine pc98/pc98/psm.c optional psm device-driver pc98/isa/qcam.c optional qcam device-driver pc98/isa/qcamio.c optional qcam device-driver pc98/pc98/random_machdep.c standard pc98/isa/rc.c optional rc device-driver i386/isa/scd.c optional scd device-driver pc98/isa/seagate.c optional sea device-driver pc98/isa/si.c optional si device-driver pc98/isa/si_code.c optional si device-driver pc98/pc98/sio.c optional sio device-driver pc98/pc98/sound/pcm86.c optional pcm device-driver pc98/pc98/sound/dev_table.c optional snd device-driver pc98/pc98/sound/soundcard.c optional snd device-driver pc98/pc98/sound/sound_switch.c optional snd device-driver pc98/pc98/sound/audio.c optional snd device-driver pc98/pc98/sound/dmabuf.c optional snd device-driver pc98/pc98/sound/sys_timer.c optional snd device-driver pc98/pc98/sound/sequencer.c optional snd device-driver pc98/pc98/sound/patmgr.c optional snd device-driver pc98/pc98/sound/adlib_card.c optional opl device-driver pc98/pc98/sound/opl3.c optional opl device-driver pc98/pc98/sound/gus_card.c optional gus device-driver pc98/pc98/sound/gus_midi.c optional gus device-driver pc98/pc98/sound/gus_vol.c optional gus device-driver pc98/pc98/sound/gus_wave.c optional gus device-driver pc98/pc98/sound/ics2101.c optional gus device-driver pc98/pc98/sound/sound_timer.c optional gus device-driver pc98/pc98/sound/midi_synth.c optional gus device-driver pc98/pc98/sound/midibuf.c optional gus device-driver pc98/pc98/sound/ad1848.c optional gusxvi device-driver pc98/pc98/sound/ad1848.c optional gus device-driver pc98/pc98/sound/ad1848.c optional mss device-driver pc98/pc98/sound/midi_synth.c optional mss device-driver pc98/pc98/sound/midibuf.c optional mss device-driver pc98/pc98/sound/mpu401.c optional mpu device-driver pc98/pc98/sound/midi_synth.c optional mpu device-driver pc98/pc98/sound/midibuf.c optional mpu device-driver pc98/pc98/sound/pas2_card.c optional pas device-driver pc98/pc98/sound/pas2_midi.c optional pas device-driver pc98/pc98/sound/pas2_mixer.c optional pas device-driver pc98/pc98/sound/pas2_pcm.c optional pas device-driver pc98/pc98/sound/midi_synth.c optional pas device-driver pc98/pc98/sound/midibuf.c optional pas device-driver pc98/pc98/sound/sb_card.c optional sb device-driver pc98/pc98/sound/sb_dsp.c optional sb device-driver pc98/pc98/sound/sb_midi.c optional sb device-driver pc98/pc98/sound/sb_mixer.c optional sb device-driver pc98/pc98/sound/midi_synth.c optional sb device-driver pc98/pc98/sound/midibuf.c optional sb device-driver pc98/pc98/sound/sb16_dsp.c optional sbxvi device-driver pc98/pc98/sound/sb16_midi.c optional sbmidi device-driver pc98/pc98/sound/uart6850.c optional uart device-driver pc98/pc98/sound/midi_synth.c optional uart device-driver pc98/pc98/sound/midibuf.c optional uart device-driver pc98/pc98/sound/trix.c optional trix device-driver pc98/pc98/sound/sscape.c optional sscape device-driver pc98/isa/spigot.c optional spigot device-driver pc98/pc98/spkr.c optional speaker device-driver pc98/isa/stallion.c optional stl device-driver pc98/pc98/syscons.c optional sc device-driver pc98/isa/tw.c optional tw device-driver pc98/isa/ultra14f.c optional uha device-driver pc98/pc98/wd.c optional wdc device-driver pc98/pc98/wd.c optional wd device-driver pc98/pc98/atapi.c optional atapi device-driver pc98/pc98/wcd.c optional wcd device-driver pc98/isa/wd7000.c optional wds device-driver pc98/pc98/wt.c optional wt device-driver i386/linux/imgact_linux.c optional compat_linux i386/linux/linux_dummy.c optional compat_linux i386/linux/linux_file.c optional compat_linux i386/linux/linux_ioctl.c optional compat_linux i386/linux/linux_ipc.c optional compat_linux i386/linux/linux_locore.s optional compat_linux \ dependency "linux_assym.h" i386/linux/linux_misc.c optional compat_linux i386/linux/linux_signal.c optional compat_linux i386/linux/linux_socket.c optional compat_linux i386/linux/linux_stats.c optional compat_linux i386/linux/linux_sysent.c optional compat_linux i386/linux/linux_sysvec.c optional compat_linux i386/linux/linux_util.c optional compat_linux i386/scsi/aic7xxx.c optional ahc device-driver \ dependency "$S/dev/aic7xxx/aic7xxx_reg.h aic7xxx_seq.h" pc98/scsi/bt.c optional bt device-driver libkern/bcd.c standard libkern/divdi3.c standard libkern/inet_ntoa.c standard libkern/index.c standard libkern/mcount.c optional profiling-routine libkern/moddi3.c standard libkern/qdivrem.c standard libkern/qsort.c standard libkern/random.c standard libkern/scanc.c standard libkern/skpc.c standard libkern/strcat.c standard libkern/strcmp.c standard libkern/strcpy.c standard libkern/strlen.c standard libkern/strncmp.c standard libkern/strncpy.c standard libkern/udivdi3.c standard libkern/umoddi3.c standard gnu/i386/fpemul/div_small.s optional gpl_math_emulate gnu/i386/fpemul/errors.c optional gpl_math_emulate gnu/i386/fpemul/fpu_arith.c optional gpl_math_emulate gnu/i386/fpemul/fpu_aux.c optional gpl_math_emulate gnu/i386/fpemul/fpu_entry.c optional gpl_math_emulate gnu/i386/fpemul/fpu_etc.c optional gpl_math_emulate gnu/i386/fpemul/fpu_trig.c optional gpl_math_emulate gnu/i386/fpemul/get_address.c optional gpl_math_emulate gnu/i386/fpemul/load_store.c optional gpl_math_emulate gnu/i386/fpemul/poly_2xm1.c optional gpl_math_emulate gnu/i386/fpemul/poly_atan.c optional gpl_math_emulate gnu/i386/fpemul/poly_div.s optional gpl_math_emulate gnu/i386/fpemul/poly_l2.c optional gpl_math_emulate gnu/i386/fpemul/poly_mul64.s optional gpl_math_emulate gnu/i386/fpemul/poly_sin.c optional gpl_math_emulate gnu/i386/fpemul/poly_tan.c optional gpl_math_emulate gnu/i386/fpemul/polynomial.s optional gpl_math_emulate gnu/i386/fpemul/reg_add_sub.c optional gpl_math_emulate gnu/i386/fpemul/reg_compare.c optional gpl_math_emulate gnu/i386/fpemul/reg_constant.c optional gpl_math_emulate gnu/i386/fpemul/reg_div.s optional gpl_math_emulate gnu/i386/fpemul/reg_ld_str.c optional gpl_math_emulate gnu/i386/fpemul/reg_mul.c optional gpl_math_emulate gnu/i386/fpemul/reg_norm.s optional gpl_math_emulate gnu/i386/fpemul/reg_round.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_add.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_div.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_mul.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_sub.s optional gpl_math_emulate gnu/i386/fpemul/wm_shrx.s optional gpl_math_emulate gnu/i386/fpemul/wm_sqrt.s optional gpl_math_emulate gnu/i386/isa/dgb.c optional dgb device-driver gnu/i386/isa/nic3008.c optional nic device-driver gnu/i386/isa/nic3009.c optional nnic device-driver pci/wd82371.c optional wd device-driver Index: head/sys/conf/options.pc98 =================================================================== --- head/sys/conf/options.pc98 (revision 18264) +++ head/sys/conf/options.pc98 (revision 18265) @@ -1,34 +1,39 @@ -# $Id: options.pc98,v 1.2 1996/07/23 07:45:51 asami Exp $ +# $Id: options.pc98,v 1.3 1996/09/10 09:37:14 asami Exp $ BOUNCEPAGES opt_bounce.h USER_LDT MATH_EMULATE opt_math_emulate.h GPL_MATH_EMULATE opt_math_emulate.h IBCS2 opt_dontuse.h COMPAT_LINUX opt_dontuse.h SHOW_BUSYBUFS opt_machdep.h PANIC_REBOOT_WAIT_TIME opt_machdep.h -LARGEMEM opt_machdep.h MAXMEM opt_machdep.h PERFMON opt_perfmon.h AUTO_EOI_1 opt_auto_eoi.h AUTO_EOI_2 opt_auto_eoi.h BREAK_TO_DEBUGGER opt_comconsole.h COMCONSOLE opt_comconsole.h COM_ESP opt_sio.h COM_MULTIPORT opt_sio.h DSI_SOFT_MODEM opt_sio.h FAT_CURSOR opt_pcvt.h PCVT_FREEBSD opt_pcvt.h PCVT_SCANSET opt_pcvt.h XSERVER opt_pcvt.h CLK_CALIBRATION_LOOP opt_clock.h CLK_USE_I8254_CALIBRATION opt_clock.h CLK_USE_I586_CALIBRATION opt_clock.h -SC_KEYBOARD_PROBE_WORKS opt_syscons.h +SC_KBD_PROBE_WORKS opt_syscons.h +MAXCONS opt_syscons.h +SLOW_VGA opt_syscons.h +XT_KEYBOARD opt_syscons.h ATAPI opt_atapi.h ATAPI_STATIC opt_atapi.h + +USERCONFIG opt_userconfig.h +VISUAL_USERCONFIG opt_userconfig.h Index: head/sys/dev/ic/ns16550.h =================================================================== --- head/sys/dev/ic/ns16550.h (revision 18264) +++ head/sys/dev/ic/ns16550.h (revision 18265) @@ -1,51 +1,64 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)ns16550.h 7.1 (Berkeley) 5/9/91 - * $Id$ + * $Id: ns16550.h,v 1.2 1993/10/16 13:48:52 rgrimes Exp $ */ /* * NS16550 UART registers */ - +#ifdef PC98 +#define com_data 0x000 /* data register (R/W) */ +#define com_dlbl 0x000 /* divisor latch low (W) */ +#define com_dlbh 0x100 /* divisor latch high (W) */ +#define com_ier 0x100 /* interrupt enable (W) */ +#define com_iir 0x200 /* interrupt identification (R) */ +#define com_fifo 0x200 /* FIFO control (W) */ +#define com_lctl 0x300 /* line control register (R/W) */ +#define com_cfcr 0x300 /* line control register (R/W) */ +#define com_mcr 0x400 /* modem control register (R/W) */ +#define com_lsr 0x500 /* line status register (R/W) */ +#define com_msr 0x600 /* modem status register (R/W) */ +#else /* IBM-PC */ #define com_data 0 /* data register (R/W) */ #define com_dlbl 0 /* divisor latch low (W) */ #define com_dlbh 1 /* divisor latch high (W) */ #define com_ier 1 /* interrupt enable (W) */ #define com_iir 2 /* interrupt identification (R) */ #define com_fifo 2 /* FIFO control (W) */ #define com_lctl 3 /* line control register (R/W) */ #define com_cfcr 3 /* line control register (R/W) */ #define com_mcr 4 /* modem control register (R/W) */ #define com_lsr 5 /* line status register (R/W) */ #define com_msr 6 /* modem status register (R/W) */ +#endif /* PC98 */ Index: head/sys/i386/include/apm_bios.h =================================================================== --- head/sys/i386/include/apm_bios.h (revision 18264) +++ head/sys/i386/include/apm_bios.h (revision 18265) @@ -1,190 +1,195 @@ /* * APM (Advanced Power Management) BIOS Device Driver * * Copyright (c) 1994-1995 by HOSOKAWA, Tatsumi * * This software may be used, modified, copied, and distributed, in * both source and binary form provided that the above copyright and * these terms are retained. Under no circumstances is the author * responsible for the proper functioning of this software, nor does * the author assume any responsibility for damages incurred with its * use. * * Aug, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) * - * $Id: apm_bios.h,v 1.9 1996/03/13 00:41:45 nate Exp $ + * $Id: apm_bios.h,v 1.10 1996/04/23 16:02:53 nate Exp $ */ #ifndef _MACHINE_APM_BIOS_H_ #define _MACHINE_APM_BIOS_H_ 1 #ifdef KERNEL /* BIOS id */ +#ifdef PC98 +#define APM_BIOS 0x9a +#define SYSTEM_BIOS 0x1f +#else #define APM_BIOS 0x53 #define SYSTEM_BIOS 0x15 +#endif /* APM flags */ #define APM_16BIT_SUPPORT 0x01 #define APM_32BIT_SUPPORT 0x02 #define APM_CPUIDLE_SLOW 0x04 #define APM_DISABLED 0x08 #define APM_DISENGAGED 0x10 /* APM initializer physical address */ #define APM_OURADDR 0x00080000 /* Error code of APM initializer */ #define APMINI_CANTFIND 0xffffffff #define APMINI_NOT32BIT 0xfffffffe #define APMINI_CONNECTERR 0xfffffffd /* APM functions */ #define APM_INSTCHECK 0x00 #define APM_REALCONNECT 0x01 #define APM_PROT16CONNECT 0x02 #define APM_PROT32CONNECT 0x03 #define APM_DISCONNECT 0x04 #define APM_CPUIDLE 0x05 #define APM_CPUBUSY 0x06 #define APM_SETPWSTATE 0x07 #define APM_ENABLEDISABLEPM 0x08 #define APM_RESTOREDEFAULT 0x09 #define APM_GETPWSTATUS 0x0a #define APM_GETPMEVENT 0x0b #define APM_GETPWSTATE 0x0c #define APM_ENABLEDISABLEDPM 0x0d #define APM_DRVVERSION 0x0e #define APM_ENGAGEDISENGAGEPM 0x0f #define APM_OEMFUNC 0x80 /* error code */ #define APME_OK 0x00 #define APME_PMDISABLED 0x01 #define APME_REALESTABLISHED 0x02 #define APME_NOTCONNECTED 0x03 #define APME_PROT16ESTABLISHED 0x05 #define APME_PROT16NOTSUPPORTED 0x06 #define APME_PROT32ESTABLISHED 0x07 #define APME_PROT32NOTDUPPORTED 0x08 #define APME_UNKNOWNDEVICEID 0x09 #define APME_OUTOFRANGE 0x0a #define APME_NOTENGAGED 0x0b #define APME_CANTENTERSTATE 0x60 #define APME_NOPMEVENT 0x80 #define APME_NOAPMPRESENT 0x86 /* device code */ #define PMDV_APMBIOS 0x0000 #define PMDV_ALLDEV 0x0001 #define PMDV_DISP0 0x0100 #define PMDV_DISP1 0x0101 #define PMDV_2NDSTORAGE0 0x0200 #define PMDV_2NDSTORAGE1 0x0201 #define PMDV_2NDSTORAGE2 0x0202 #define PMDV_2NDSTORAGE3 0x0203 #define PMDV_PARALLEL0 0x0300 #define PMDV_PARALLEL1 0x0301 #define PMDV_SERIAL0 0x0400 #define PMDV_SERIAL1 0x0401 #define PMDV_SERIAL2 0x0402 #define PMDV_SERIAL3 0x0403 #define PMDV_SERIAL4 0x0404 #define PMDV_SERIAL5 0x0405 #define PMDV_SERIAL6 0x0406 #define PMDV_SERIAL7 0x0407 #define PMDV_NET0 0x0500 #define PMDV_NET1 0x0501 #define PMDV_NET2 0x0502 #define PMDV_NET3 0x0503 #define PMDV_PCMCIA0 0x0600 #define PMDV_PCMCIA1 0x0601 #define PMDV_PCMCIA2 0x0602 #define PMDV_PCMCIA3 0x0603 /* 0x0700 - 0xdfff Reserved */ /* 0xe000 - 0xefff OEM-defined power device IDs */ /* 0xf000 - 0xffff Reserved */ /* Power state */ #define PMST_APMENABLED 0x0000 #define PMST_STANDBY 0x0001 #define PMST_SUSPEND 0x0002 #define PMST_OFF 0x0003 #define PMST_LASTREQNOTIFY 0x0004 #define PMST_LASTREQREJECT 0x0005 /* 0x0006 - 0x001f Reserved system states */ /* 0x0020 - 0x003f OEM-defined system states */ /* 0x0040 - 0x007f OEM-defined device states */ /* 0x0080 - 0xffff Reserved device states */ #if !defined(ASSEMBLER) && !defined(INITIALIZER) /* C definitions */ struct apmhook { struct apmhook *ah_next; int (*ah_fun) __P((void *ah_arg)); void *ah_arg; const char *ah_name; int ah_order; }; #define APM_HOOK_NONE (-1) #define APM_HOOK_SUSPEND 0 #define APM_HOOK_RESUME 1 #define NAPM_HOOK 2 void apm_suspend(void); struct apmhook *apm_hook_establish (int apmh, struct apmhook *); void apm_hook_disestablish (int apmh, struct apmhook *); void apm_cpu_idle(void); void apm_cpu_busy(void); #endif /* !ASSEMBLER && !INITIALIZER */ #define APM_MIN_ORDER 0x00 #define APM_MID_ORDER 0x80 #define APM_MAX_ORDER 0xff #endif /* KERNEL */ /* power management event code */ #define PMEV_NOEVENT 0x0000 #define PMEV_STANDBYREQ 0x0001 #define PMEV_SUSPENDREQ 0x0002 #define PMEV_NORMRESUME 0x0003 #define PMEV_CRITRESUME 0x0004 #define PMEV_BATTERYLOW 0x0005 #define PMEV_POWERSTATECHANGE 0x0006 #define PMEV_UPDATETIME 0x0007 #define PMEV_CRITSUSPEND 0x0008 #define PMEV_USERSTANDBYREQ 0x0009 #define PMEV_USERSUSPENDREQ 0x000a #define PMEV_STANDBYRESUME 0x000b /* 0x000c - 0x00ff Reserved system events */ /* 0x0100 - 0x01ff Reserved device events */ /* 0x0200 - 0x02ff OEM-defined APM events */ /* 0x0300 - 0xffff Reserved */ #define PMEV_DEFAULT 0xffffffff /* used for customization */ #if !defined(ASSEMBLER) && !defined(INITIALIZER) typedef struct apm_info { u_int ai_major; /* APM major version */ u_int ai_minor; /* APM minor version */ u_int ai_acline; /* AC line status */ u_int ai_batt_stat; /* Battery status */ u_int ai_batt_life; /* Remaining battery life */ u_int ai_status; /* Status of APM support (enabled/disabled) */ } *apm_info_t; #define APMIO_SUSPEND _IO('P', 1) #define APMIO_GETINFO _IOR('P', 2, struct apm_info) #define APMIO_ENABLE _IO('P', 5) #define APMIO_DISABLE _IO('P', 6) #define APMIO_HALTCPU _IO('P', 7) #define APMIO_NOTHALTCPU _IO('P', 8) #define APMIO_DISPLAYOFF _IO('P', 9) #endif /* !ASSEMBLER && !INITIALIZER */ #endif /* _MACHINE_APM_BIOS_H_ */ Index: head/sys/i386/include/cpufunc.h =================================================================== --- head/sys/i386/include/cpufunc.h (revision 18264) +++ head/sys/i386/include/cpufunc.h (revision 18265) @@ -1,427 +1,379 @@ /*- * Copyright (c) 1993 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: cpufunc.h,v 1.53 1996/07/23 07:45:19 asami Exp $ + * $Id: cpufunc.h,v 1.54 1996/08/01 20:29:28 wollman Exp $ */ /* * Functions to provide access to special i386 instructions. */ #ifndef _MACHINE_CPUFUNC_H_ #define _MACHINE_CPUFUNC_H_ #include #include #ifdef __GNUC__ static __inline void breakpoint(void) { __asm __volatile("int $3"); } static __inline void disable_intr(void) { __asm __volatile("cli" : : : "memory"); } static __inline void enable_intr(void) { __asm __volatile("sti"); } #define HAVE_INLINE_FFS static __inline int ffs(int mask) { int result; /* * bsfl turns out to be not all that slow on 486's. It can beaten * using a binary search to reduce to 4 bits and then a table lookup, * but only if the code is inlined and in the cache, and the code * is quite large so inlining it probably busts the cache. * * Note that gcc-2's builtin ffs would be used if we didn't declare * this inline or turn off the builtin. The builtin is faster but * broken in gcc-2.4.5 and slower but working in gcc-2.5 and 2.6. */ __asm __volatile("testl %0,%0; je 1f; bsfl %0,%0; incl %0; 1:" : "=r" (result) : "0" (mask)); return (result); } #define HAVE_INLINE_FLS static __inline int fls(int mask) { int result; __asm __volatile("testl %0,%0; je 1f; bsrl %0,%0; incl %0; 1:" : "=r" (result) : "0" (mask)); return (result); } #if __GNUC__ < 2 #define inb(port) inbv(port) #define outb(port, data) outbv(port, data) #else /* __GNUC >= 2 */ /* * The following complications are to get around gcc not having a * constraint letter for the range 0..255. We still put "d" in the * constraint because "i" isn't a valid constraint when the port * isn't constant. This only matters for -O0 because otherwise * the non-working version gets optimized away. * * Use an expression-statement instead of a conditional expression * because gcc-2.6.0 would promote the operands of the conditional * and produce poor code for "if ((inb(var) & const1) == const2)". */ #define inb(port) ({ \ u_char _data; \ if (__builtin_constant_p((int) (port)) && (port) < 256ul) \ _data = inbc(port); \ else \ _data = inbv(port); \ _data; }) #define outb(port, data) \ (__builtin_constant_p((int) (port)) && (port) < 256ul \ ? outbc(port, data) : outbv(port, data)) static __inline u_char inbc(u_int port) { u_char data; __asm __volatile("inb %1,%0" : "=a" (data) : "id" ((u_short)(port))); return (data); } static __inline void outbc(u_int port, u_char data) { __asm __volatile("outb %0,%1" : : "a" (data), "id" ((u_short)(port))); } #endif /* __GNUC <= 2 */ static __inline u_char inbv(u_int port) { u_char data; /* * We use %%dx and not %1 here because i/o is done at %dx and not at * %edx, while gcc generates inferior code (movw instead of movl) * if we tell it to load (u_short) port. */ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port)); return (data); } static __inline u_long inl(u_int port) { u_long data; __asm __volatile("inl %%dx,%0" : "=a" (data) : "d" (port)); return (data); } static __inline void insb(u_int port, void *addr, size_t cnt) { __asm __volatile("cld; rep; insb" : : "d" (port), "D" (addr), "c" (cnt) : "di", "cx", "memory"); } static __inline void insw(u_int port, void *addr, size_t cnt) { __asm __volatile("cld; rep; insw" : : "d" (port), "D" (addr), "c" (cnt) : "di", "cx", "memory"); } static __inline void insl(u_int port, void *addr, size_t cnt) { __asm __volatile("cld; rep; insl" : : "d" (port), "D" (addr), "c" (cnt) : "di", "cx", "memory"); } static __inline u_short inw(u_int port) { u_short data; __asm __volatile("inw %%dx,%0" : "=a" (data) : "d" (port)); return (data); } static __inline u_int loadandclear(u_int *addr) { u_int result; __asm __volatile("xorl %0,%0; xchgl %1,%0" : "=&r" (result) : "m" (*addr)); return (result); } static __inline void outbv(u_int port, u_char data) { u_char al; /* * Use an unnecessary assignment to help gcc's register allocator. * This make a large difference for gcc-1.40 and a tiny difference * for gcc-2.6.0. For gcc-1.40, al had to be ``asm("ax")'' for * best results. gcc-2.6.0 can't handle this. */ al = data; __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port)); } static __inline void outl(u_int port, u_long data) { /* * outl() and outw() aren't used much so we haven't looked at * possible micro-optimizations such as the unnecessary * assignment for them. */ __asm __volatile("outl %0,%%dx" : : "a" (data), "d" (port)); } static __inline void outsb(u_int port, void *addr, size_t cnt) { __asm __volatile("cld; rep; outsb" : : "d" (port), "S" (addr), "c" (cnt) : "si", "cx"); } static __inline void outsw(u_int port, void *addr, size_t cnt) { __asm __volatile("cld; rep; outsw" : : "d" (port), "S" (addr), "c" (cnt) : "si", "cx"); } static __inline void outsl(u_int port, void *addr, size_t cnt) { __asm __volatile("cld; rep; outsl" : : "d" (port), "S" (addr), "c" (cnt) : "si", "cx"); } static __inline void outw(u_int port, u_short data) { __asm __volatile("outw %0,%%dx" : : "a" (data), "d" (port)); } -#ifdef PC98 -#include - -static inline u_char -epson_inb(u_int port) -{ - u_char data; - - outb(0x43f, 0x42); - data = inb(port); - outb(0x43f, 0x40); - return (data); -} - -static inline void -epson_outb(u_int port, u_char data) -{ - outb(0x43f, 0x42); - outb(port,data); - outb(0x43f, 0x40); -} - -static inline void -epson_insw(u_int port, void *addr, size_t cnt) -{ - int s; - - s = splbio(); - outb(0x43f, 0x42); - disable_intr(); - insw((u_int)port, (void *)addr, (size_t)cnt); - outb(0x43f, 0x40); - splx(s); -} - -static inline void -epson_outsw(u_int port, void *addr, size_t cnt) -{ - int s; - - s = splbio(); - outb(0x43f, 0x42); - disable_intr(); - outsw((u_int)port, (void *)addr, (size_t)cnt); - outb(0x43f, 0x40); - splx(s); -} -#endif /* PC98 */ static __inline void pmap_update(void) { u_long temp; /* * This should be implemented as load_cr3(rcr3()) when load_cr3() * is inlined. */ __asm __volatile("movl %%cr3, %0; movl %0, %%cr3" : "=r" (temp) : : "memory"); } static __inline u_long rcr2(void) { u_long data; __asm __volatile("movl %%cr2,%0" : "=r" (data)); return (data); } static __inline u_long read_eflags(void) { u_long ef; __asm __volatile("pushfl; popl %0" : "=r" (ef)); return (ef); } static __inline quad_t rdmsr(u_int msr) { quad_t rv; __asm __volatile(".byte 0x0f, 0x32" : "=A" (rv) : "c" (msr)); return (rv); } static __inline quad_t rdpmc(u_int pmc) { quad_t rv; __asm __volatile(".byte 0x0f, 0x33" : "=A" (rv) : "c" (pmc)); return (rv); } static __inline quad_t rdtsc(void) { quad_t rv; __asm __volatile(".byte 0x0f, 0x31" : "=A" (rv)); return (rv); } static __inline void setbits(volatile unsigned *addr, u_int bits) { __asm __volatile("orl %1,%0" : "=m" (*addr) : "ir" (bits)); } static __inline void write_eflags(u_long ef) { __asm __volatile("pushl %0; popfl" : : "r" (ef)); } static __inline void wrmsr(u_int msr, quad_t newval) { __asm __volatile(".byte 0x0f, 0x30" : : "A" (newval), "c" (msr)); } #else /* !__GNUC__ */ int breakpoint __P((void)); void disable_intr __P((void)); void enable_intr __P((void)); u_char inb __P((u_int port)); u_long inl __P((u_int port)); void insb __P((u_int port, void *addr, size_t cnt)); void insl __P((u_int port, void *addr, size_t cnt)); void insw __P((u_int port, void *addr, size_t cnt)); u_short inw __P((u_int port)); u_int loadandclear __P((u_int *addr)); void outb __P((u_int port, u_char data)); void outl __P((u_int port, u_long data)); void outsb __P((u_int port, void *addr, size_t cnt)); void outsl __P((u_int port, void *addr, size_t cnt)); void outsw __P((u_int port, void *addr, size_t cnt)); void outw __P((u_int port, u_short data)); void pmap_update __P((void)); u_long rcr2 __P((void)); quad_t rdmsr __P((u_int msr)); quad_t rdpmc __P((u_int pmc)); quad_t rdtsc __P((void)); u_long read_eflags __P((void)); void setbits __P((volatile unsigned *addr, u_int bits)); void write_eflags __P((u_long ef)); void wrmsr __P((u_int msr, quad_t newval)); #endif /* __GNUC__ */ void load_cr0 __P((u_long cr0)); void load_cr3 __P((u_long cr3)); void ltr __P((u_short sel)); u_int rcr0 __P((void)); u_long rcr3 __P((void)); #include /* XXX belongs elsewhere */ #endif /* !_MACHINE_CPUFUNC_H_ */ Index: head/sys/i386/isa/ic/i8251.h =================================================================== --- head/sys/i386/isa/ic/i8251.h (nonexistent) +++ head/sys/i386/isa/ic/i8251.h (revision 18265) @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ns16550.h 7.1 (Berkeley) 5/9/91 + */ + +/* + * modified for PC9801 by M.Ishii + * Kyoto University Microcomputer Club (KMC) + */ + +/* define command and status code */ +#define CMD8251_TxEN 0x01 /* transmit enable */ +#define CMD8251_DTR 0x02 /* assert DTR */ +#define CMD8251_RxEN 0x04 /* receive enable */ +#define CMD8251_SBRK 0x08 /* send break */ +#define CMD8251_ER 0x10 /* error reset */ +#define CMD8251_RTS 0x20 /* assert RTS */ +#define CMD8251_RESET 0x40 /* internal reset */ +#define CMD8251_EH 0x80 /* enter hunt mode (only synchronous mode)*/ + +#define STS8251_TxRDY 0x01 /* transmit READY */ +#define STS8251_RxRDY 0x02 /* data exists in receive buffer */ +#define STS8251_TxEMP 0x04 /* transmit buffer EMPTY */ +#define STS8251_PE 0x08 /* perity error */ +#define STS8251_OE 0x10 /* overrun error */ +#define STS8251_FE 0x20 /* framing error */ +#define STS8251_BD_SD 0x40 /* break detect (async) / sync detect (sync) */ +#define STS8251_DSR 0x80 /* DSR is asserted */ + +#define MOD8251_5BITS 0x00 +#define MOD8251_6BITS 0x04 +#define MOD8251_7BITS 0x08 +#define MOD8251_8BITS 0x0c +#define MOD8251_PDISAB 0x00 /* parity disable */ +#define MOD8251_PODD 0x10 /* parity odd */ +#define MOD8251_PEVEN 0x30 /* parity even */ +#define MOD8251_STOP1 0x40 /* stop bit len = 1bit */ +#define MOD8251_STOP2 0xc0 /* stop bit len = 2bit */ +#define MOD8251_CLKX16 0x02 /* x16 */ +#define MOD8251_CLKX1 0x01 /* x1 */ + +#define CICSCD_CI 0x80 /* CI */ +#define CICSCD_CS 0x40 /* CS */ +#define CICSCD_CD 0x20 /* CD */ + +/* interrupt mask control */ +#define IEN_Rx 0x01 +#define IEN_TxEMP 0x02 +#define IEN_Tx 0x04 Property changes on: head/sys/i386/isa/ic/i8251.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/i386/isa/ic/ns16550.h =================================================================== --- head/sys/i386/isa/ic/ns16550.h (revision 18264) +++ head/sys/i386/isa/ic/ns16550.h (revision 18265) @@ -1,51 +1,64 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)ns16550.h 7.1 (Berkeley) 5/9/91 - * $Id$ + * $Id: ns16550.h,v 1.2 1993/10/16 13:48:52 rgrimes Exp $ */ /* * NS16550 UART registers */ - +#ifdef PC98 +#define com_data 0x000 /* data register (R/W) */ +#define com_dlbl 0x000 /* divisor latch low (W) */ +#define com_dlbh 0x100 /* divisor latch high (W) */ +#define com_ier 0x100 /* interrupt enable (W) */ +#define com_iir 0x200 /* interrupt identification (R) */ +#define com_fifo 0x200 /* FIFO control (W) */ +#define com_lctl 0x300 /* line control register (R/W) */ +#define com_cfcr 0x300 /* line control register (R/W) */ +#define com_mcr 0x400 /* modem control register (R/W) */ +#define com_lsr 0x500 /* line status register (R/W) */ +#define com_msr 0x600 /* modem status register (R/W) */ +#else /* IBM-PC */ #define com_data 0 /* data register (R/W) */ #define com_dlbl 0 /* divisor latch low (W) */ #define com_dlbh 1 /* divisor latch high (W) */ #define com_ier 1 /* interrupt enable (W) */ #define com_iir 2 /* interrupt identification (R) */ #define com_fifo 2 /* FIFO control (W) */ #define com_lctl 3 /* line control register (R/W) */ #define com_cfcr 3 /* line control register (R/W) */ #define com_mcr 4 /* modem control register (R/W) */ #define com_lsr 5 /* line status register (R/W) */ #define com_msr 6 /* modem status register (R/W) */ +#endif /* PC98 */ Index: head/sys/i386/isa/ic/wd33c93.h =================================================================== --- head/sys/i386/isa/ic/wd33c93.h (nonexistent) +++ head/sys/i386/isa/ic/wd33c93.h (revision 18265) @@ -0,0 +1,127 @@ +/* + * PC9801 SCSI I/F (PC-9801-55) + * modified for PC9801 by A.Kojima + * Kyoto University Microcomputer Club (KMC) + */ + +/* I/O address */ + +/* WD33C93 */ +#define SCSI_ADR_REG 0xcc0 /* write Address Register */ +#define SCSI_AUX_REG 0xcc0 /* read Aux. Status Register */ +#define SCSI_CTL_REG 0xcc2 /* read/write Control Registers */ + +/* Port */ +#define SCSI_STAT_RD 0xcc4 /* read Status Read */ +#define SCSI_CMD_WRT 0xcc4 /* write Command Write */ + +#if 0 /* H98 extended mode */ +/* WD33C93 */ +#define SCSI_ADR_REG 0xee0 /* write Address Register */ +#define SCSI_AUX_REG 0xee0 /* read Control Register */ +#define SCSI_CTL_REG 0xee2 /* read/write Registers */ + +/* Port */ +#define SCSI_STAT_RD 0xee4 /* read Status Read */ +#define SCSI_CMD_WRT 0xee4 /* write Command Write */ +#endif + +/****************************************************************/ + +/* WD33C93 Registers */ +#define REG_OWN_ID 0x00 /* Own ID */ +#define REG_CONTROL 0x01 /* Control */ +#define REG_TIMEOUT_PERIOD 0x02 /* Timeout Period */ +#define REG_TOTAL_SECTORS 0x03 /* Total Sectors */ +#define REG_TOTAL_HEADS 0x04 /* Total Heads */ +#define REG_TOTAL_CYL_H 0x05 /* Total Cylinders (MSB) */ +#define REG_TOTAL_CYL_L 0x06 /* Total Cylinders (LSB) */ +#define REG_LOG_SECTOR_HH 0x07 /* Logical Address (MSB) */ +#define REG_LOG_SECTOR_HL 0x08 /* Logical Address */ +#define REG_LOG_SECTOR_LH 0x09 /* Logical Address */ +#define REG_LOG_SECTOR_LL 0x0a /* Logical Address (LSB) */ +#define REG_SECTOR_NUMBER 0x0b /* Sector Number */ +#define REG_HEAD_NUMBER 0x0c /* Head Number */ +#define REG_CYL_NUMBER_H 0x0d /* Cylinder Number (MSB) */ +#define REG_CYL_NUMBER_L 0x0e /* Cylinder Number (LSB) */ +#define REG_TARGET_LUN 0x0f /* Target LUN */ +#define REG_CMD_PHASE 0x10 /* Command Phase */ +#define REG_SYNC_TFR 0x11 /* Synchronous Transfer */ +#define REG_TFR_COUNT_H 0x12 /* Transfer Count (MSB) */ +#define REG_TFR_COUNT_M 0x13 /* Transfer Count */ +#define REG_TFR_COUNT_L 0x14 /* Transfer Count (LSB) */ +#define REG_DST_ID 0x15 /* Destination ID */ +#define REG_SRC_ID 0x16 /* Source ID */ +#define REG_SCSI_STATUS 0x17 /* SCSI Status (Read Only) */ +#define REG_COMMAND 0x18 /* Command */ +#define REG_DATA 0x19 /* Data */ + +/* PC98 only */ +#define REG_MEM_BANK 0x30 /* Memory Bank */ +#define REG_MEM_WIN 0x31 /* Memery Window */ +#define REG_RESERVED1 0x32 /* NEC Reserved 1 */ +#define REG_RESET_INT 0x33 /* Reset/Int */ +#define REG_RESERVED2 0x34 /* NEC Reserved 2 */ + +/****************************************************************/ + +/* WD33C93 Commands */ +#define CMD_RESET 0x00 /* Reset */ +#define CMD_ABORT 0x01 /* Abort */ +#define CMD_ASSERT_ATN 0x02 /* Assert ATN */ +#define CMD_NEGATE_ATN 0x03 /* Negate ATN */ +#define CMD_DISCONNECT 0x04 /* Disconnect */ +#define CMD_RESELECT 0x05 /* Reselect */ +#define CMD_SELECT_ATN 0x06 /* Select with ATN */ +#define CMD_SELECT_NO_ATN 0x07 /* Select without ATN */ +#define CMD_SELECT_ATN_TFR 0x08 /* Select with ATN and Transfer */ +#define CMD_SELECT_NO_ATN_TFR 0x09 /* Select without ATN and Transfer */ +#define CMD_RESELECT_RCV_DATA 0x0a /* Reselect and Recieve Data */ +#define CMD_RESELECT_SEND_DATA 0x0b /* Reselect and Send Data */ +#define CMD_WAIT_SELECT_RCV 0x0c /* Wait for Select and Recieve */ +#define CMD_RCV_CMD 0x10 /* Recieve Command */ +#define CMD_RCV_DATA 0x11 /* Recieve Data */ +#define CMD_RCV_MSG_OUT 0x12 /* Recieve Message Info Out*/ +#define CMD_RCV_UNSP_INFO_OUT 0x13 /* Recieve Unspecified Info Out */ +#define CMD_SEND_STATUS 0x14 /* Send Status */ +#define CMD_SEND_DATA 0x15 /* Send Data */ +#define CMD_SEND_MSG_IN 0x16 /* Send Message In */ +#define CMD_SEND_UNSP_INFO_IN 0x17 /* Send Unspecified Info In */ +#define CMD_TRANSLATE_ADDRESS 0x18 /* Translate Address */ +#define CMD_TFR_INFO 0x20 /* Transfer Info */ +#define CMD_TFR_PAD 0x21 /* Transfer Pad */ +#define CMD_SBT_SFX 0x80 /* single byte suffix */ + +/* WD33C93 bus status register (lower nibble) */ +#define STAT_DATAOUT 0x08 /* Data out phase */ +#define STAT_DATAIN 0x09 /* Data in phase */ +#define STAT_CMDOUT 0x0a /* Command out phase */ +#define STAT_STATIN 0x0b /* Status in phase */ +#define STAT_MSGOUT 0x0e /* Message out phase */ +#define STAT_MSGIN 0x0f /* Message in phase */ + +/* SCSI Status byte */ +#define SS_GOOD 0x00 /* Good status */ +#define SS_CHKCOND 0x02 +#define SS_MET 0x04 +#define SS_BUSY 0x08 +#define SS_INTERGOOD 0x10 +#define SS_INTERMET 0x14 +#define SS_CONFLICT 0x18 + +/* SCSI message system */ +#define MSG_COMPLETE 0x00 /* Command complete message */ +#define MSG_EXTEND 0x01 /* Extend message */ +#define MSG_SAVEPTR 0x02 /* Save data pointer message */ +#define MSG_RESTORE 0x03 /* Restore data pointer message */ +#define MSG_DISCON 0x04 /* Disconnect message */ +#define MSG_INIERROR 0x05 +#define MSG_ABORT 0x06 +#define MSG_REJECT 0x07 +#define MSG_NOP 0x08 +#define MSG_PARERROR 0x09 +#define MSG_LCOMPLETE 0x0a +#define MSG_LCOMPLETEF 0x0b +#define MSG_DEVRESET 0x0c +#define MSG_IDENTIFY 0x80 /* Identify message */ + Property changes on: head/sys/i386/isa/ic/wd33c93.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/isa/ic/ns16550.h =================================================================== --- head/sys/isa/ic/ns16550.h (revision 18264) +++ head/sys/isa/ic/ns16550.h (revision 18265) @@ -1,51 +1,64 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)ns16550.h 7.1 (Berkeley) 5/9/91 - * $Id$ + * $Id: ns16550.h,v 1.2 1993/10/16 13:48:52 rgrimes Exp $ */ /* * NS16550 UART registers */ - +#ifdef PC98 +#define com_data 0x000 /* data register (R/W) */ +#define com_dlbl 0x000 /* divisor latch low (W) */ +#define com_dlbh 0x100 /* divisor latch high (W) */ +#define com_ier 0x100 /* interrupt enable (W) */ +#define com_iir 0x200 /* interrupt identification (R) */ +#define com_fifo 0x200 /* FIFO control (W) */ +#define com_lctl 0x300 /* line control register (R/W) */ +#define com_cfcr 0x300 /* line control register (R/W) */ +#define com_mcr 0x400 /* modem control register (R/W) */ +#define com_lsr 0x500 /* line status register (R/W) */ +#define com_msr 0x600 /* modem status register (R/W) */ +#else /* IBM-PC */ #define com_data 0 /* data register (R/W) */ #define com_dlbl 0 /* divisor latch low (W) */ #define com_dlbh 1 /* divisor latch high (W) */ #define com_ier 1 /* interrupt enable (W) */ #define com_iir 2 /* interrupt identification (R) */ #define com_fifo 2 /* FIFO control (W) */ #define com_lctl 3 /* line control register (R/W) */ #define com_cfcr 3 /* line control register (R/W) */ #define com_mcr 4 /* modem control register (R/W) */ #define com_lsr 5 /* line status register (R/W) */ #define com_msr 6 /* modem status register (R/W) */ +#endif /* PC98 */ Index: head/sys/pc98/boot/Makefile =================================================================== --- head/sys/pc98/boot/Makefile (revision 18264) +++ head/sys/pc98/boot/Makefile (revision 18265) @@ -1,5 +1,5 @@ -# $Id: Makefile,v 1.25 1995/04/15 08:24:33 phk Exp $ - -SUBDIR= biosboot netboot +# $Id: Makefile,v 1.26 1996/09/11 19:22:21 phk Exp $ + +SUBDIR= biosboot kzipboot netboot rawboot .include Index: head/sys/pc98/boot/biosboot/Makefile =================================================================== --- head/sys/pc98/boot/biosboot/Makefile (revision 18264) +++ head/sys/pc98/boot/biosboot/Makefile (revision 18265) @@ -1,97 +1,99 @@ -# $Id: Makefile,v 1.1.1.1 1996/06/14 10:04:37 asami Exp $ +# $Id: Makefile,v 1.2 1996/07/23 07:45:33 asami Exp $ # PROG= boot # Order is very important on the SRCS line for this prog SRCS= start.S table.c boot2.S boot.c asm.S bios.S serial.S SRCS+= probe_keyboard.c io.c disk.c sys.c BINDIR= /usr/mdec BINMODE= 444 CFLAGS= -O2 \ -DPC98 -DBOOTWAIT=${BOOTWAIT} -DTIMEOUT=${TIMEOUT} CFLAGS+= -DBOOTSEG=${BOOTSEG} -DBOOTSTACK=${BOOTSTACK} CFLAGS+= -DCOMCONSOLE=0x30 -DCOMCONSOLE_CLK=16 -DCOMCONSOLE_MODE=0x0c # Probe the keyboard and use the serial console if the keyboard isn't found. #CFLAGS+= -DPROBE_KEYBOARD # Force use of the serial console (after probing the keyboard if # PROBE_KEYBOARD is defined). #CFLAGS+= -DFORCE_COMCONSOLE # Enable code to take the default boot string from a fixed location on the # disk. See nextboot(8) and README.386BSD for more info. #CFLAGS+= -DNAMEBLOCK #CFLAGS+= -DNAMEBLOCK_WRITEBACK # Bias the conversion from the BIOS drive number to the FreeBSD unit number # for hard disks. This may be useful for people booting in a mixed IDE/SCSI # environment (set BOOT_HD_BIAS to the number of IDE drives). #CFLAGS+= -DBOOT_HD_BIAS=1 # # Details: this only applies if BOOT_HD_BIAS > 0. If the BIOS drive number # for the boot drive is >= BOOT_HD_BIAS, then the boot drive is assumed to # be SCSI and have unit number (BIOS_drive_number - BOOT_HD_BIAS). E.g., # BOOT_HD_BIAS=1 makes BIOS drive 1 correspond to 1:sd(0,a) instead of # 1:wd(1,a). If `sd' is given explicitly, then the drive is assumed to be # SCSI and have BIOS drive number (sd_unit_number + BOOT_HD_BIAS). E.g., # BOOT_HD_BIAS=1 makes sd(0,a) correspond to 1:sd(0,a) instead of 0:sd(0,a). CLEANFILES+= boot.nohdr boot.strip boot1 boot2 sizetest DPADD= ${LIBC} LDFLAGS+= -N -T 0 -nostdlib LDADD= -lc #LINKS= ${BINDIR}/sdboot ${BINDIR}/wdboot\ # ${BINDIR}/sdboot ${BINDIR}/fdboot\ # ${BINDIR}/bootsd ${BINDIR}/bootwd\ # ${BINDIR}/bootsd ${BINDIR}/bootfd NOSHARED= YES NOMAN= STRIP= # tunable timeout parameter, waiting for keypress, calibrated in ms BOOTWAIT?= 5000 # tunable timeout during string input, calibrated in ms #TIMEOUT?= 30000 # Location that boot2 is loaded at BOOTSEG= 0x9000 # Offset in BOOTSEG for the top of the stack, keep this 16 byte aligned BOOTSTACK= 0xFFF0 boot.strip: boot cp -p boot boot.strip strip boot.strip size boot.strip boot.nohdr: boot.strip dd if=boot.strip of=boot.nohdr ibs=32 skip=1 obs=1024b ls -l boot.nohdr boot1: boot.nohdr dd if=boot.nohdr of=boot1 bs=512 count=1 boot2: boot.nohdr dd if=boot.nohdr of=boot2 bs=512 skip=1 @dd if=boot2 skip=14 of=sizetest 2> /dev/null @if [ -s sizetest ] ; then \ - echo "*** Boot2 is too BIG ***" ; exit 2 ; \ + echo "boot2 is too big" >&2 ; \ + rm boot2 ; \ + exit 2 ; \ fi all: boot1 boot2 install: ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE}\ boot1 ${DESTDIR}${BINDIR}/boot1 ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE}\ boot2 ${DESTDIR}${BINDIR}/boot2 for i in sd fd wd od vn ; do \ ( cd ${DESTDIR}${BINDIR} ; \ rm -f boot$${i} $${i}boot ; \ ln -s boot1 $${i}boot ; \ ln -s boot2 boot$${i} ; ) \ done .include Index: head/sys/pc98/boot/biosboot/boot.c =================================================================== --- head/sys/pc98/boot/biosboot/boot.c (revision 18264) +++ head/sys/pc98/boot/biosboot/boot.c (revision 18265) @@ -1,380 +1,381 @@ /* * Mach Operating System * Copyright (c) 1992, 1991 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. * * from: Mach, [92/04/03 16:51:14 rvb] - * $Id: boot.c,v 1.2 1996/07/23 07:45:35 asami Exp $ + * $Id: boot.c,v 1.3 1996/08/31 15:06:21 asami Exp $ */ /* Copyright 1988, 1989, 1990, 1991, 1992 by Intel Corporation, Santa Clara, California. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies and that both the copyright notice and this permission notice appear in supporting documentation, and that the name of Intel not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "boot.h" #include #include #include #define ouraddr (BOOTSEG << 4) /* XXX */ #define NAMEBUF_LEN (8*1024) #ifdef NAMEBLOCK char *dflt_name; #endif char namebuf[NAMEBUF_LEN]; -struct exec head; struct bootinfo bootinfo; int loadflags; static void getbootdev(char *ptr, int *howto); static void loadprog(void); /* NORETURN */ void boot(int drive) { int ret; #ifdef PROBE_KEYBOARD if (probe_keyboard()) { init_serial(); loadflags |= RB_SERIAL; printf("\nNo keyboard found."); } #endif #ifdef FORCE_COMCONSOLE init_serial(); loadflags |= RB_SERIAL; printf("\nSerial console forced."); #endif /* Pick up the story from the Bios on geometry of disks */ #ifdef PC98 for(ret = 0; ret < 2; ret ++) { if (*(unsigned char*)0x1155d & (1 << ret)) { bootinfo.bi_bios_geom[ret] = get_diskinfo(ret + 0x80); } #else /* IBM-PC */ for(ret = 0; ret < N_BIOS_GEOM; ret ++) bootinfo.bi_bios_geom[ret] = get_diskinfo(ret + 0x80); #endif /* PC98 */ } bootinfo.bi_basemem = memsize(0); bootinfo.bi_extmem = memsize(1); bootinfo.bi_memsizes_valid = 1; gateA20(); #ifdef PC98 /* set machine type to PC98_SYSTEM_PARAMETER */ machine_check(); #endif /* PC98 */ /* * The default boot device is the first partition in the * compatibility slice on the boot drive. */ dosdev = drive; #ifdef PC98 maj = (drive&0x70) >> 3; /* a good first bet */ unit = drive & 0x0f; #else /* IBM-PC */ maj = 2; unit = drive & 0x7f; #ifdef dontneed slice = 0; part = 0; #endif if (drive & 0x80) { /* Hard drive. Adjust. */ maj = 0; #if BOOT_HD_BIAS > 0 if (unit >= BOOT_HD_BIAS) { /* * The drive is probably a SCSI drive with a unit * number BOOT_HD_BIAS less than the BIOS drive * number. */ maj = 4; unit -= BOOT_HD_BIAS; } #endif } #endif /* PC98 */ #ifdef NAMEBLOCK /* * XXX * DAMN! I don't understand why this is not being set * by the code in boot2.S */ dflt_name= (char *)0x0000ffb0; if( (*dflt_name++ == 'D') && (*dflt_name++ == 'N')) { name = dflt_name; } else #endif /*NAMEBLOCK*/ loadstart: name = dflname; /* re-initialize in case of loop */ /* print this all each time.. (saves space to do so) */ /* If we have looped, use the previous entries as defaults */ printf("\n>> FreeBSD BOOT @ 0x%x: %d/%d k of memory\n" "Usage: [[[%d:][%s](%d,a)]%s][-abcCdghrsv]\n" "Use 1:sd(0,a)kernel to boot sd0 if it is BIOS drive 1\n" "Use ? for file list or press Enter for defaults\n\nBoot: ", ouraddr, bootinfo.bi_basemem, bootinfo.bi_extmem, #ifdef PC98 dosdev & 0x0f, devs[maj], unit, name); #else dosdev & 0x7f, devs[maj], unit, name); #endif loadflags &= RB_SERIAL; /* clear all, but leave serial console */ getbootdev(namebuf, &loadflags); ret = openrd(); if (ret != 0) { if (ret > 0) printf("Can't find %s\n", name); goto loadstart; } /* if (inode.i_mode&IEXEC) loadflags |= RB_KDB; */ loadprog(); goto loadstart; } static void loadprog(void) { + struct exec head; long int startaddr; long int addr; /* physical address.. not directly useable */ long int bootdev; int i; unsigned pad; read((void *)&head, sizeof(head)); if ( N_BADMAG(head)) { printf("Invalid format!\n"); return; } poff = N_TXTOFF(head); /*if(poff==0) poff = 32;*/ /* * We assume that the entry address is the same as the lowest text * address and that the kernel startup code handles relocation by * this address rounded down to a multiple of 16M. */ startaddr = head.a_entry & 0x00FFFFFF; addr = startaddr; printf("Booting %d:%s(%d,%c)%s @ 0x%x\n" #ifdef PC98 , dosdev & 0x0f #else , dosdev & 0x7f #endif , devs[maj] , unit , 'a'+part , name , addr); if(addr < 0x00100000) { /* * Bail out, instead of risking to damage the BIOS * variables, the loader, or the adapter memory area. * We don't support loading below 1 MB any more. */ printf("Start address too low\n"); return; } printf("text=0x%x ", head.a_text); /********************************************************/ /* LOAD THE TEXT SEGMENT */ /********************************************************/ xread((void *)addr, head.a_text); addr += head.a_text; /********************************************************/ /* Load the Initialised data after the text */ /********************************************************/ while (addr & PAGE_MASK) *(char *)addr++ = 0; printf("data=0x%x ", head.a_data); xread((void *)addr, head.a_data); addr += head.a_data; /********************************************************/ /* Skip over the uninitialised data */ /* (but clear it) */ /********************************************************/ printf("bss=0x%x ", head.a_bss); /* * XXX however, we should be checking that we don't load ... into * nonexistent memory. A full symbol table is unlikely to fit on 4MB * machines. */ pbzero((void *)addr,head.a_bss); addr += head.a_bss; /* Pad to a page boundary. */ pad = (unsigned)addr & PAGE_MASK; if (pad != 0) { pad = PAGE_SIZE - pad; addr += pad; } bootinfo.bi_symtab = addr; /********************************************************/ /* Copy the symbol table size */ /********************************************************/ pcpy(&head.a_syms, (void *)addr, sizeof(head.a_syms)); addr += sizeof(head.a_syms); /********************************************************/ /* Load the symbol table */ /********************************************************/ printf("symbols=[+0x%x+0x%x+0x%x", pad, sizeof(head.a_syms), head.a_syms); xread((void *)addr, head.a_syms); addr += head.a_syms; /********************************************************/ /* Load the string table size */ /********************************************************/ read((void *)&i, sizeof(int)); pcpy(&i, (void *)addr, sizeof(int)); i -= sizeof(int); addr += sizeof(int); /********************************************************/ /* Load the string table */ /********************************************************/ printf("+0x%x+0x%x]\n", sizeof(int), i); xread((void *)addr, i); addr += i; bootinfo.bi_esymtab = addr; /* * For backwards compatibility, use the previously-unused adaptor * and controller bitfields to hold the slice number. */ bootdev = MAKEBOOTDEV(maj, (slice >> 4), slice & 0xf, unit, part); bootinfo.bi_version = BOOTINFO_VERSION; bootinfo.bi_kernelname = name + ouraddr; bootinfo.bi_nfs_diskless = NULL; bootinfo.bi_size = sizeof(bootinfo); printf("total=0x%x entry point=0x%x\n", (int)addr, (int)startaddr); startprog((int)startaddr, loadflags | RB_BOOTINFO, bootdev, (int)&bootinfo + ouraddr); } void getbootdev(char *ptr, int *howto) { char c; /* * Be paranoid and make doubly sure that the input buffer is empty. */ if (*howto & RB_SERIAL) init_serial(); if (!gets(ptr)) { putchar('\n'); return; } while ((c = *ptr) != '\0') { nextarg: while (c == ' ') c = *++ptr; if (c == '-') while ((c = *++ptr) != '\0') { if (c == ' ') goto nextarg; if (c == 'C') *howto |= RB_CDROM; if (c == 'a') *howto |= RB_ASKNAME; if (c == 'b') *howto |= RB_HALT; if (c == 'c') *howto |= RB_CONFIG; if (c == 'd') *howto |= RB_KDB; if (c == 'h') { *howto ^= RB_SERIAL; if (*howto & RB_SERIAL) init_serial(); + continue; } if (c == 'g') *howto |= RB_GDB; if (c == 'r') *howto |= RB_DFLTROOT; if (c == 's') *howto |= RB_SINGLE; if (c == 'v') *howto |= RB_VERBOSE; } if (c == '\0') return; name = ptr; while (*++ptr != '\0') { if (*ptr == ' ') { *ptr++ = '\0'; break; } } } } Index: head/sys/pc98/boot/biosboot/boot.h =================================================================== --- head/sys/pc98/boot/biosboot/boot.h (revision 18264) +++ head/sys/pc98/boot/biosboot/boot.h (revision 18265) @@ -1,101 +1,101 @@ /* * Mach Operating System * Copyright (c) 1992, 1991 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. * * from: Mach, Revision 2.2 92/04/04 11:35:03 rpd - * $Id: boot.h,v 1.11 1995/06/25 14:02:52 joerg Exp $ + * $Id: boot.h,v 1.1.1.1 1996/06/14 10:04:37 asami Exp $ */ #include #include #include #include #include #include extern char *devs[], *iodest; extern char *name, dflname[]; extern struct fs *fs; extern struct inode inode; extern int dosdev, unit, slice, part, maj, boff, poff, bnum, cnt; extern unsigned long tw_chars; extern int loadflags; extern struct disklabel disklabel; /* asm.S */ #if ASM_ONLY void real_to_prot(void); void prot_to_real(void); #endif void startprog(unsigned int physaddr, int howto, int bootdev, /* XXX struct bootinfo * */ unsigned int bootinfo); void pbzero(void *dst, size_t count); void pcpy(const void *src, void *dst, size_t count); /* bios.S */ int biosread(int dev, int cyl, int head, int sec, int nsec, void *offset); void putc(int c); int getc(void); int ischar(void); int get_diskinfo(int drive); int memsize(int extended); /* boot.c */ void boot(int drive); /* boot2.S */ void boot2(void); /* disk.c */ int devopen(void); -void devread(void); -void Bread(int dosdev, int sector); +void devread(char *iodest, int sector, int cnt); +char * Bread(int dosdev, int sector); int badsect(int dosdev, int sector); /* io.c */ void gateA20(void); void printf(const char *format, ...); void putchar(int c); int getchar(int in_buf); void delay1ms(void); int gets(char *buf); int strcmp(const char *s1, const char *s2); void bcopy(const char *from, char *to, int len); void twiddle(void); /* probe_keyboard.c */ int probe_keyboard(void); /* serial.S */ void serial_putc(int ch); int serial_getc(void); int serial_ischar(void); void init_serial(void); /* sys.c */ void xread(char *addr, int size); void read(char *buffer, int count); int find(char *path); int block_map(int file_block); int openrd(void); Index: head/sys/pc98/boot/biosboot/disk.c =================================================================== --- head/sys/pc98/boot/biosboot/disk.c (revision 18264) +++ head/sys/pc98/boot/biosboot/disk.c (revision 18265) @@ -1,322 +1,320 @@ /* * Mach Operating System * Copyright (c) 1992, 1991 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. * * from: Mach, Revision 2.2 92/04/04 11:35:49 rpd - * $Id: disk.c,v 1.1.1.1 1996/06/14 10:04:37 asami Exp $ + * $Id: disk.c,v 1.2 1996/07/23 07:45:36 asami Exp $ */ /* * Ported to PC-9801 by Yoshio Kimura */ /* * 93/10/08 bde * If there is no 386BSD partition, initialize the label sector with * LABELSECTOR instead of with garbage. * * 93/08/22 bde * Fixed reading of bad sector table. It is at the end of the 'c' * partition, which is not always at the end of the disk. */ #include "boot.h" #ifdef DO_BAD144 #include #endif DO_BAD144 #include #include #define BIOS_DEV_FLOPPY 0x0 #define BIOS_DEV_WIN 0x80 #define BPS 512 #define SPT(di) ((di)&0xff) #define HEADS(di) (((di)>>8)&0xff) #ifdef DO_BAD144 struct dkbad dkb; int do_bad144; #endif DO_BAD144 int bsize; int spt, spc; -char *iodest; struct fs *fs; struct inode inode; -int dosdev, unit, slice, part, maj, boff, poff, bnum, cnt; +int dosdev, unit, slice, part, maj, boff, poff; /*#define EMBEDDED_DISKLABEL 1*/ -#define I_ADDR ((void *) 0) /* XXX where all reads go */ - /* Read ahead buffer large enough for one track on a 1440K floppy. For * reading from floppies, the bootstrap has to be loaded on a 64K boundary * to ensure that this buffer doesn't cross a 64K DMA boundary. */ #define RA_SECTORS 18 static char ra_buf[RA_SECTORS * BPS]; static int ra_dev; static int ra_end; static int ra_first; int devopen(void) { struct dos_partition *dptr; struct disklabel *dl; - int dosdev = inode.i_dev; + char *p; int i, sector = 0, di; -#if 0 /* Save space, already have hard error for cyl > 1023 in Bread */ - u_long bend; -#endif di = get_diskinfo(dosdev); spc = (spt = SPT(di)) * HEADS(di); + +#ifndef RAWBOOT if ((dosdev & 0xf0) == 0x90) { boff = 0; part = (spt == 15 ? 0 : 1); } else { #ifdef EMBEDDED_DISKLABEL dl = &disklabel; #else EMBEDDED_DISKLABEL #ifdef PC98 - Bread(dosdev, 1); + p = Bread(dosdev, 1); dptr = (struct dos_partition *)0; slice = WHOLE_DISK_SLICE; for (i = 0; i < NDOSPART; i++, dptr++) if (dptr->dp_mid == DOSPTYP_386BSD) { slice = BASE_SLICE + i; sector = dptr->dp_scyl * spc; break; } Bread(dosdev, sector + LABELSECTOR); dl=((struct disklabel *)0); disklabel = *dl; /* structure copy (maybe useful later)*/ #else - Bread(dosdev, 0); - dptr = (struct dos_partition *)(((char *)0)+DOSPARTOFF); + p = Bread(dosdev, 0); + dptr = (struct dos_partition *)(p+DOSPARTOFF); slice = WHOLE_DISK_SLICE; for (i = 0; i < NDOSPART; i++, dptr++) if (dptr->dp_typ == DOSPTYP_386BSD) { slice = BASE_SLICE + i; sector = dptr->dp_start; break; } - Bread(dosdev, sector + LABELSECTOR); - dl=((struct disklabel *)0); + p = Bread(dosdev, sector + LABELSECTOR); + dl=((struct disklabel *)p); disklabel = *dl; /* structure copy (maybe useful later)*/ #endif /* PC98 */ #endif EMBEDDED_DISKLABEL if (dl->d_magic != DISKMAGIC) { printf("bad disklabel"); return 1; } if( (maj == 4) || (maj == 0) || (maj == 1)) { if (dl->d_type == DTYPE_SCSI) { maj = 4; /* use scsi as boot dev */ } else { maj = 0; /* must be ESDI/IDE */ } } /* This little trick is for OnTrack DiskManager disks */ boff = dl->d_partitions[part].p_offset - dl->d_partitions[2].p_offset + sector; #ifndef PC98 /* This is a good idea for all disks */ bsize = dl->d_partitions[part].p_size; -#if 0 /* Save space, already have hard error for cyl > 1023 in Bread */ - bend = boff + bsize - 1 ; - if (bend / spc >= 1024) { - printf("boot partition end >= cyl 1024, BIOS can't load kernel stored beyond this limit\n"); #endif -#endif #ifdef DO_BAD144 do_bad144 = 0; if (dl->d_flags & D_BADSECT) { /* this disk uses bad144 */ int i; int dkbbnum; struct dkbad *dkbptr; /* find the first readable bad sector table */ /* some of this code is copied from ufs/ufs_disksubr.c */ /* including the bugs :-( */ /* read a bad sector table */ #define BAD144_PART 2 /* XXX scattered magic numbers */ #define BSD_PART 0 /* XXX should be 2 but bad144.c uses 0 */ if (dl->d_partitions[BSD_PART].p_offset != 0) dkbbnum = dl->d_partitions[BAD144_PART].p_offset + dl->d_partitions[BAD144_PART].p_size; else dkbbnum = dl->d_secperunit; dkbbnum -= dl->d_nsectors; if (dl->d_secsize > DEV_BSIZE) dkbbnum *= dl->d_secsize / DEV_BSIZE; else dkbbnum /= DEV_BSIZE / dl->d_secsize; i = 0; do_bad144 = 0; do { /* XXX: what if the "DOS sector" < 512 bytes ??? */ - Bread(dosdev, dkbbnum + i); - dkbptr = (struct dkbad *) 0; + p = Bread(dosdev, dkbbnum + i); + dkbptr = (struct dkbad *) p; /* XXX why is this not in ??? */ #define DKBAD_MAGIC 0x4321 if (dkbptr->bt_mbz == 0 && dkbptr->bt_flag == DKBAD_MAGIC) { dkb = *dkbptr; /* structure copy */ do_bad144 = 1; break; } i += 2; } while (i < 10 && i < dl->d_nsectors); if (!do_bad144) printf("Bad bad sector table\n"); else printf("Using bad sector table at %d\n", dkbbnum+i); } -#endif DO_BAD144 +#endif /* DO_BAD144 */ } +#endif /* RAWBOOT */ return 0; } + +/* + * Be aware that cnt is rounded up to N*BPS + */ void -devread(void) +devread(char *iodest, int sector, int cnt) { - int offset, sector = bnum; - int dosdev = inode.i_dev; + int offset; + char *p; + for (offset = 0; offset < cnt; offset += BPS) { - Bread(dosdev, badsect(dosdev, sector++)); - bcopy(0, iodest+offset, BPS); + p = Bread(dosdev, badsect(dosdev, sector++)); + bcopy(p, iodest+offset, BPS); } } -void + +char * Bread(int dosdev, int sector) { if (dosdev != ra_dev || sector < ra_first || sector >= ra_end) { int cyl, head, sec, nsec; cyl = sector/spc; #ifndef PC98 if (cyl > 1023) { printf("Error: C:%d > 1023 (BIOS limit)\n", cyl); for(;;); /* loop forever */ } #endif head = (sector % spc) / spt; sec = sector % spt; nsec = spt - sec; if (nsec > RA_SECTORS) nsec = RA_SECTORS; twiddle(); if (biosread(dosdev, cyl, head, sec, nsec, ra_buf) != 0) { nsec = 1; twiddle(); while (biosread(dosdev, cyl, head, sec, nsec, ra_buf) != 0) { printf("Error: C:%d H:%d S:%d\n", cyl, head, sec); twiddle(); } } ra_dev = dosdev; ra_first = sector; ra_end = sector + nsec; } - bcopy(ra_buf + (sector - ra_first) * BPS, I_ADDR, BPS); + return (ra_buf + (sector - ra_first) * BPS); } int badsect(int dosdev, int sector) { +#if defined(DO_BAD144) && !defined(RAWBOOT) int i; -#ifdef DO_BAD144 if (do_bad144) { u_short cyl; u_short head; u_short sec; int newsec; struct disklabel *dl = &disklabel; /* XXX */ /* from wd.c */ /* bt_cyl = cylinder number in sorted order */ /* bt_trksec is actually (head << 8) + sec */ /* only remap sectors in the partition */ if (sector < boff || sector >= boff + bsize) { goto no_remap; } cyl = (sector-boff) / dl->d_secpercyl; head = ((sector-boff) % dl->d_secpercyl) / dl->d_nsectors; sec = (sector-boff) % dl->d_nsectors; sec = (head<<8) + sec; /* now, look in the table for a possible bad sector */ for (i=0; i<126; i++) { if (dkb.bt_bad[i].bt_cyl == cyl) { /* found same cylinder */ if (dkb.bt_bad[i].bt_trksec == sec) { /* FOUND! */ break; } } else if (dkb.bt_bad[i].bt_cyl > cyl) { i = 126; break; } } if (i == 126) { /* didn't find bad sector */ goto no_remap; } /* otherwise find replacement sector */ if (dl->d_partitions[BSD_PART].p_offset != 0) newsec = dl->d_partitions[BAD144_PART].p_offset + dl->d_partitions[BAD144_PART].p_size; else newsec = dl->d_secperunit; newsec -= dl->d_nsectors + i + 1; return newsec; } -#endif DO_BAD144 no_remap: +#endif return sector; } Index: head/sys/pc98/boot/biosboot/io.c =================================================================== --- head/sys/pc98/boot/biosboot/io.c (revision 18264) +++ head/sys/pc98/boot/biosboot/io.c (revision 18265) @@ -1,398 +1,398 @@ /* * Mach Operating System * Copyright (c) 1992, 1991 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. * * from: Mach, Revision 2.2 92/04/04 11:35:57 rpd * $Id: io.c,v 1.19 1996/04/30 23:43:25 bde Exp $ */ #include "boot.h" #include #include #ifdef PC98 -#include "../../pc98/pc98_bios.h" +#include "../../pc98/pc98.h" #endif /* * Gate A20 for high memory */ void gateA20(void) { outb(0xf2, 0x00); outb(0xf6, 0x02); } /* printf - only handles %d as decimal, %c as char, %s as string */ void printf(const char *format, ...) { int *dataptr = (int *)&format; char c; dataptr++; while ((c = *format++)) if (c != '%') putchar(c); else switch (c = *format++) { case 'd': { int num = *dataptr++; char buf[10], *ptr = buf; if (num<0) { num = -num; putchar('-'); } do *ptr++ = '0'+num%10; while (num /= 10); do putchar(*--ptr); while (ptr != buf); break; } case 'x': { unsigned int num = *dataptr++, dig; char buf[8], *ptr = buf; do *ptr++ = (dig=(num&0xf)) > 9? 'a' + dig - 10 : '0' + dig; while (num >>= 4); do putchar(*--ptr); while (ptr != buf); break; } case 'c': putchar((*dataptr++)&0xff); break; case 's': { char *ptr = (char *)*dataptr++; while ((c = *ptr++)) putchar(c); break; } } } void putchar(int c) { if (c == '\n') { if (loadflags & RB_SERIAL) serial_putc('\r'); else putc('\r'); } if (loadflags & RB_SERIAL) serial_putc(c); else putc(c); } int getchar(int in_buf) { int c; loop: if ((c = ((loadflags & RB_SERIAL) ? serial_getc() : getc())) == '\r') c = '\n'; if (c == '\b') { if (in_buf != 0) { putchar('\b'); putchar(' '); } else { goto loop; } } putchar(c); return(c); } #ifdef PROBE_KEYBOARD /* * This routine uses an inb to an unused port, the time to execute that * inb is approximately 1.25uS. This value is pretty constant across * all CPU's and all buses, with the exception of some PCI implentations * that do not forward this I/O adress to the ISA bus as they know it * is not a valid ISA bus address, those machines execute this inb in * 60 nS :-(. * * XXX this should be converted to use bios_tick. */ void delay1ms(void) { #ifdef PC98 int i = 800; while (--i >= 0) (void)outb(0x5f,0); /* about 600ns */ #else int i = 800; while (--i >= 0) (void)inb(0x84); #endif } #endif /* PROBE_KEYBOARD */ static __inline int isch(void) { int isc; /* * Checking the keyboard has the side effect of enabling clock * interrupts so that bios_tick works. Check the keyboard to * get this side effect even if we only want the serial status. */ isc = ischar(); if (!(loadflags & RB_SERIAL)) return (isc); return (serial_ischar()); } #ifdef PC98 static __inline unsigned #else static __inline unsigned #endif pword(unsigned physaddr) { #ifdef PC98 static int counter = 0; int i; for (i = 0; i < 512; i++) (void)outb(0x5f, 0); return (counter++); #else unsigned result; /* * Give the fs prefix separately because gas omits it for * "movl %fs:0x46c, %eax". */ __asm __volatile("fs; movl %1, %0" : "=r" (result) : "m" (*(unsigned *)physaddr)); return (result); #endif } int gets(char *buf) { #define bios_tick pword(0x46c) #ifdef PC98 #define BIOS_TICK_MS 1 #else #define BIOS_TICK_MS 55 #endif unsigned initial_bios_tick; char *ptr=buf; #if BOOTWAIT for (initial_bios_tick = bios_tick; bios_tick - initial_bios_tick < BOOTWAIT / BIOS_TICK_MS;) #endif if (isch()) for (;;) { switch(*ptr = getchar(ptr - buf) & 0xff) { case '\n': case '\r': *ptr = '\0'; return 1; case '\b': if (ptr > buf) ptr--; continue; default: ptr++; } #if TIMEOUT + 0 #if !BOOTWAIT #error "TIMEOUT without BOOTWAIT" #endif for (initial_bios_tick = bios_tick;;) { if (isch()) break; if (bios_tick - initial_bios_tick >= TIMEOUT / BIOS_TICK_MS) return 0; } #endif } return 0; } int strcmp(const char *s1, const char *s2) { while (*s1 == *s2) { if (!*s1++) return 0; s2++; } return 1; } void bcopy(const char *from, char *to, int len) { while (len-- > 0) *to++ = *from++; } /* To quote Ken: "You are not expected to understand this." :) */ void twiddle(void) { putchar((char)tw_chars); tw_chars = (tw_chars >> 8) | ((tw_chars & (unsigned long)0xFF) << 24); putchar('\b'); } static unsigned short *Crtat = (unsigned short *)0; static int row; static int col; void putc(int c) { static unsigned short *crtat; unsigned char sys_type; unsigned short *cp; int i, pos; if (Crtat == 0) { sys_type = *(unsigned char *)0x11501; if (sys_type & 0x08) { Crtat = (unsigned short *)0x50000; crtat = Crtat; row = 31; col = 80; } else { Crtat = (unsigned short *)0x10000; crtat = Crtat; row = 25; col = 80; } } switch(c) { case '\t': do { putc(' '); } while ((int)crtat % 16); break; case '\b': crtat--; break; case '\r': crtat -= (crtat - Crtat) % col; break; case '\n': crtat += col; break; default: *crtat = (c == 0x5c ? 0xfc : c); *(crtat++ + 0x1000) = 0xe1; break; } if (crtat >= Crtat + col * row) { for (i = 1; i < row; i++) bcopy((void*)(Crtat+col*i), (void*)(Crtat+col*(i-1)), col*2); for (i = 0, cp = Crtat + col * (row - 1); i < col*2; i++) { *cp++ = ' '; } crtat -= col; } pos = crtat - Crtat; while((inb(0x60) & 0x04) == 0) {} outb(0x62, 0x49); outb(0x60, pos & 0xff); outb(0x60, pos >> 8); } void machine_check(void) { int ret; int i; int data = 0; u_char epson_machine_id = *(unsigned char *)(0x11624); /* PC98_SYSTEM_PARAMETER(0x501) */ ret = ((*(unsigned char*)0x11501) & 0x08) ? M_HIGHRESO : M_NORMAL; /* wait V-SYNC */ while (inb(0x60) & 0x20) {} while (!(inb(0x60) & 0x20)) {} /* ANK 'A' font */ outb(0xa1, 0x00); outb(0xa3, 0x41); /* M_NORMAL, use CG window (all NEC OK) */ /* sum */ for (i = 0; i < 4; i++) { data += *((unsigned long*)0x14000 + i);/* 0xa4000 */ } if (data == 0x6efc58fc) { /* DA data */ ret |= M_NEC_PC98; } else { ret |= M_EPSON_PC98; } ret |= (inb(0x42) & 0x20) ? M_8M : 0; /* PC98_SYSTEM_PARAMETER(0x400) */ if ((*(unsigned char*)0x11400) & 0x80) { ret |= M_NOTE; } if (ret & M_NEC_PC98) { /* PC98_SYSTEM_PARAMETER(0x458) */ if ((*(unsigned char*)0x11458) & 0x80) { ret |= M_H98; } else { ret |= M_NOT_H98; } } else { ret |= M_NOT_H98; switch (epson_machine_id) { case 0x20: /* note A */ case 0x22: /* note W */ case 0x27: /* note AE */ case 0x2a: /* note WR */ /*case 0x2: /* note AR */ ret |= M_NOTE; break; default: break; } } (*(unsigned long *)(0x11620)) = ret; } Index: head/sys/pc98/boot/biosboot/sys.c =================================================================== --- head/sys/pc98/boot/biosboot/sys.c (revision 18264) +++ head/sys/pc98/boot/biosboot/sys.c (revision 18265) @@ -1,297 +1,322 @@ /* * Mach Operating System * Copyright (c) 1992, 1991 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright - * notice and this permission notice appear in all copies of the +e* notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. * * from: Mach, Revision 2.2 92/04/04 11:36:34 rpd - * $Id: sys.c,v 1.1.1.1 1996/06/14 10:04:37 asami Exp $ + * $Id: sys.c,v 1.2 1996/08/31 15:06:24 asami Exp $ */ /* * Ported to PC-9801 by Yoshio Kimura */ #include "boot.h" #include #include #ifdef 0 /* #define BUFSIZE 4096 */ #define BUFSIZE MAXBSIZE char buf[BUFSIZE], fsbuf[SBSIZE], iobuf[MAXBSIZE]; #endif static char biosdrivedigit; #define BUFSIZE 8192 #define MAPBUFSIZE BUFSIZE char buf[BUFSIZE], fsbuf[BUFSIZE], iobuf[BUFSIZE]; char mapbuf[MAPBUFSIZE]; int mapblock; +#ifdef RAWBOOT +#define STARTBYTE 8192 /* Where on the media the kernel starts */ +#endif + void xread(char *addr, int size) { int count = BUFSIZE; while (size > 0) { if (BUFSIZE > size) count = size; read(buf, count); pcpy(buf, addr, count); size -= count; addr += count; } } +#ifndef RAWBOOT void read(char *buffer, int count) { int logno, off, size; int cnt2, bnum2; while (count) { off = blkoff(fs, poff); logno = lblkno(fs, poff); cnt2 = size = blksize(fs, &inode, logno); bnum2 = fsbtodb(fs, block_map(logno)) + boff; - cnt = cnt2; - bnum = bnum2; - if ( (!off) && (size <= count)) - { - iodest = buffer; - devread(); - } - else - { - iodest = iobuf; + if ( (!off) && (size <= count)) { + devread(buffer, bnum2, cnt2); + } else { size -= off; if (size > count) size = count; - devread(); - bcopy(iodest+off,buffer,size); + devread(iobuf, bnum2, cnt2); + bcopy(iobuf+off, buffer, size); } buffer += size; count -= size; poff += size; } } +#else +void +read(char *buffer, int count) +{ + int cnt, bnum, off, size; + off = STARTBYTE + poff; + poff += count; + + /* Read any unaligned bit at the front */ + cnt = off & 511; + if (cnt) { + size = 512-cnt; + if (count < size) + size = count; + devread(iobuf, off >> 9, 512); + bcopy(iobuf+cnt, buffer, size); + count -= size; + off += size; + buffer += size; + } + size = count & (~511); + if (size && (off & (~511))) { + devread(buffer, off >> 9, size); + off += size; + count -= size; + buffer += size; + } + if (count) { + devread(iobuf, off >> 9, 512); + bcopy(iobuf, buffer, count); + } +} + +#endif int find(char *path) { char *rest, ch; int block, off, loc, ino = ROOTINO; struct direct *dp; - int list_only = 0; + char list_only; - if (strcmp("?", path) == 0) - list_only = 1; -loop: iodest = iobuf; - cnt = fs->fs_bsize; - bnum = fsbtodb(fs,ino_to_fsba(fs,ino)) + boff; - devread(); - bcopy((void *)&((struct dinode *)iodest)[ino % fs->fs_inopb], + list_only = (path[0] == '?' && path[1] == '\0'); +loop: + devread(iobuf, fsbtodb(fs, ino_to_fsba(fs, ino)) + boff, fs->fs_bsize); + bcopy((void *)&((struct dinode *)iobuf)[ino % fs->fs_inopb], (void *)&inode.i_din, sizeof (struct dinode)); if (!*path) return 1; while (*path == '/') path++; if (!inode.i_size || ((inode.i_mode&IFMT) != IFDIR)) return 0; for (rest = path; (ch = *rest) && ch != '/'; rest++) ; *rest = 0; loc = 0; do { if (loc >= inode.i_size) { if (list_only) { - printf("\n"); + putchar('\n'); return -1; } else { return 0; } } if (!(off = blkoff(fs, loc))) { block = lblkno(fs, loc); - cnt = blksize(fs, &inode, block); - bnum = fsbtodb(fs, block_map(block)) + boff; - iodest = iobuf; - devread(); + devread(iobuf, fsbtodb(fs, block_map(block)) + boff, + blksize(fs, &inode, block)); } - dp = (struct direct *)(iodest + off); + dp = (struct direct *)(iobuf + off); loc += dp->d_reclen; if (dp->d_ino && list_only) printf("%s ", dp->d_name); } while (!dp->d_ino || strcmp(path, dp->d_name)); ino = dp->d_ino; *(path = rest) = ch; goto loop; } int block_map(int file_block) { + int bnum; if (file_block < NDADDR) return(inode.i_db[file_block]); if ((bnum=fsbtodb(fs, inode.i_ib[0])+boff) != mapblock) { - iodest = mapbuf; - cnt = fs->fs_bsize; - devread(); + devread(mapbuf, bnum, fs->fs_bsize); mapblock = bnum; } return (((int *)mapbuf)[(file_block - NDADDR) % NINDIR(fs)]); } int openrd(void) { char **devp, *cp = name; int biosdrive, ret; #ifdef PC98 int i; unsigned char disk_equips; int sdunit = 0; #endif /*******************************************************\ * If bracket given look for preceding device name * \*******************************************************/ while (*cp && *cp!='(') cp++; if (!*cp) { cp = name; } else { /* * Look for a BIOS drive number (a leading digit followed * by a colon). */ + biosdrivedigit = '\0'; if (*(name + 1) == ':' && *name >= '0' && *name <= '9') { biosdrivedigit = *name; name += 2; - } else - biosdrivedigit = '\0'; + } if (cp++ != name) { for (devp = devs; *devp; devp++) if (name[0] == (*devp)[0] && name[1] == (*devp)[1]) break; if (!*devp) { printf("Unknown device\n"); return 1; } maj = devp-devs; } /*******************************************************\ * Look inside brackets for unit number, and partition * \*******************************************************/ /* * Allow any valid digit as the unit number, as the BIOS * will complain if the unit number is out of range. * Restricting the range here prevents the possibilty of using * BIOSes that support more than 2 units. * XXX Bad values may cause strange errors, need to check if * what happens when a value out of range is supplied. */ if (*cp >= '0' && *cp <= '9') unit = *cp++ - '0'; if (!*cp || (*cp == ',' && !*++cp)) return 1; if (*cp >= 'a' && *cp <= 'p') part = *cp++ - 'a'; while (*cp && *cp++!=')') ; if (!*cp) return 1; } - if (biosdrivedigit != '\0') - biosdrive = biosdrivedigit - '0'; - else { + biosdrive = biosdrivedigit - '0'; + if (biosdrivedigit == '\0') { biosdrive = unit; #if BOOT_HD_BIAS > 0 /* XXX */ if (maj == 4) biosdrive += BOOT_HD_BIAS; #endif } switch(maj) { case 4: /* sd */ #ifdef PC98 dosdev = unit | 0xa0; disk_equips = *(unsigned char *)0x11482; unit = 0; for (i = 0; i < unit; i++) unit += ((disk_equips >> i) & 0x01); #else /* IBM-PC */ dosdev = biosdrive | 0x80; #endif break; case 0: case 2: #ifdef PC98 dosdev = (maj << 3) | unit | 0x80; #else dosdev = biosdrive; #endif break; case 3: printf("Unknown device\n"); return 1; } printf("dosdev = %x, biosdrive = %d, unit = %d, maj = %d\n", dosdev, biosdrive, unit, maj); - inode.i_dev = dosdev; /***********************************************\ * Now we know the disk unit and part, * * Load disk info, (open the device) * \***********************************************/ if (devopen()) return 1; +#ifndef RAWBOOT /***********************************************\ * Load Filesystem info (mount the device) * \***********************************************/ - iodest = (char *)(fs = (struct fs *)fsbuf); - cnt = SBSIZE; - bnum = SBLOCK + boff; - devread(); + devread((char *)(fs = (struct fs *)fsbuf), SBLOCK + boff, SBSIZE); /***********************************************\ * Find the actual FILE on the mounted device * \***********************************************/ ret = find(cp); - if (ret <= 0) - return (ret == 0) ? 1 : -1; + if (ret == 0) + return 1; + if (ret < 0) + return -1; poff = 0; name = cp; +#endif /* RAWBOOT */ return 0; } Index: head/sys/pc98/boot/rawboot/Makefile =================================================================== --- head/sys/pc98/boot/rawboot/Makefile (nonexistent) +++ head/sys/pc98/boot/rawboot/Makefile (revision 18265) @@ -0,0 +1,84 @@ +# $Id: Makefile,v 1.1 1996/09/11 19:25:11 phk Exp $ +# + +PROG= boot + +# Order is very important on the SRCS line for this prog +SRCS= start.S table.c boot2.S boot.c asm.S bios.S serial.S +SRCS+= probe_keyboard.c io.c disk.c sys.c + +.PATH: ${.CURDIR}/../biosboot + +BINDIR= /usr/mdec +BINMODE= 444 +CFLAGS= -O2 \ + -DPC98 \ + -DRAWBOOT \ + -I${.CURDIR}/../biosboot \ + -DBOOTWAIT=${BOOTWAIT} -DTIMEOUT=${TIMEOUT} +CFLAGS+= -DCOMCONSOLE=0x30 -DCOMCONSOLE_CLK=16 -DCOMCONSOLE_MODE=0x0c +CFLAGS+= -DBOOTSEG=${BOOTSEG} -DBOOTSTACK=${BOOTSTACK} + +# Probe the keyboard and use the serial console if the keyboard isn't found. +#CFLAGS+= -DPROBE_KEYBOARD + +# Force use of the serial console (after probing the keyboard if +# PROBE_KEYBOARD is defined). +#CFLAGS+= -DFORCE_COMCONSOLE + +# Enable code to take the default boot string from a fixed location on the +# disk. See nextboot(8) and README.386BSD for more info. +#CFLAGS+= -DNAMEBLOCK +#CFLAGS+= -DNAMEBLOCK_WRITEBACK + +# Bias the conversion from the BIOS drive number to the FreeBSD unit number +# for hard disks. This may be useful for people booting in a mixed IDE/SCSI +# environment (set BOOT_HD_BIAS to the number of IDE drives). +#CFLAGS+= -DBOOT_HD_BIAS=1 +# +# Details: this only applies if BOOT_HD_BIAS > 0. If the BIOS drive number +# for the boot drive is >= BOOT_HD_BIAS, then the boot drive is assumed to +# be SCSI and have unit number (BIOS_drive_number - BOOT_HD_BIAS). E.g., +# BOOT_HD_BIAS=1 makes BIOS drive 1 correspond to 1:sd(0,a) instead of +# 1:wd(1,a). If `sd' is given explicitly, then the drive is assumed to be +# SCSI and have BIOS drive number (sd_unit_number + BOOT_HD_BIAS). E.g., +# BOOT_HD_BIAS=1 makes sd(0,a) correspond to 1:sd(0,a) instead of 0:sd(0,a). + +CLEANFILES+= boot.nohdr boot.strip rawboot sizetest +DPADD= ${LIBC} +LDFLAGS+= -N -T 0 -nostdlib +LDADD= -lc +NOSHARED= YES +NOMAN= +STRIP= + +# tunable timeout parameter, waiting for keypress, calibrated in ms +BOOTWAIT?= 5000 +# tunable timeout during string input, calibrated in ms +#TIMEOUT?= 30000 + +# Location that boot2 is loaded at +BOOTSEG= 0x9000 + +# Offset in BOOTSEG for the top of the stack, keep this 16 byte aligned +BOOTSTACK= 0xFFF0 + +boot.strip: boot + cp -p boot boot.strip + strip boot.strip + size boot.strip + +boot.nohdr: boot.strip + dd if=boot.strip of=boot.nohdr ibs=32 skip=1 obs=1024b + ls -l boot.nohdr + +rawboot: boot.nohdr + dd if=boot.nohdr of=rawboot bs=8k count=1 conv=sync + +all: rawboot + +install: + ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE}\ + rawboot ${DESTDIR}${BINDIR}/rawboot + +.include Property changes on: head/sys/pc98/boot/rawboot/Makefile ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/pc98/boot/rawboot/README =================================================================== --- head/sys/pc98/boot/rawboot/README (nonexistent) +++ head/sys/pc98/boot/rawboot/README (revision 18265) @@ -0,0 +1,17 @@ +RAWboot readme. + +This is a dumber version of the code in biosboot. + +The intended usage is: + + cat /usr/mdec/rawboot /sys/compile/FOO/kernel | fdwrite + +This makes it a lot easier to make a bootable floppy, and saves space +on the floppy to boot. + +Of course the name you enter for the kernel isn't used... Then again +if you know how to make two kernels fit a floppy and have a use for +it, you don't need this bootblock. + +Poul-Henning Kamp +phk@FreeBSD.org Property changes on: head/sys/pc98/boot/rawboot/README ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/pc98/cbus/cbus.h =================================================================== --- head/sys/pc98/cbus/cbus.h (revision 18264) +++ head/sys/pc98/cbus/cbus.h (revision 18265) @@ -1,223 +1,270 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)isa.h 5.7 (Berkeley) 5/9/91 - * $Id: pc98.h,v 1.2 1996/09/03 10:23:48 asami Exp $ + * $Id: pc98.h,v 1.3 1996/09/10 09:38:21 asami Exp $ */ #ifndef _PC98_PC98_PC98_H_ #define _PC98_PC98_PC98_H_ /* BEWARE: Included in both assembler and C code */ /* * PC98 Bus conventions */ /* * PC98 Bus conventions * modified for PC9801 by A.Kojima F.Ukai M.Ishii * Kyoto University Microcomputer Club (KMC) */ /* * Input / Output Port Assignments */ #ifndef IO_ISABEGIN #define IO_ISABEGIN 0x000 /* 0x000 - Beginning of I/O Registers */ /* PC98 IO address ... very dirty (^_^; */ #define IO_ICU1 0x000 /* 8259A Interrupt Controller #1 */ #define IO_DMA 0x001 /* 8237A DMA Controller */ #define IO_ICU2 0x008 /* 8259A Interrupt Controller #2 */ #define IO_RTC 0x020 /* 4990A RTC */ #define IO_DMAPG 0x021 /* DMA Page Registers */ #define IO_COM1 0x030 /* 8251A RS232C serial I/O (int) */ #define IO_SYSPORT 0x031 /* 8255A System Port */ #define IO_PPI 0x035 /* Programmable Peripheral Interface */ #define IO_LPT 0x040 /* 8255A Printer Port */ #define IO_KBD 0x041 /* 8251A Keyboard */ #define IO_NMI 0x050 /* NMI Control */ #define IO_WAIT 0x05F /* WAIT 0.6 us */ #define IO_GDC1 0x060 /* 7220 GDC Text Control */ #define IO_TIMER 0x071 /* 8253C Timer */ #define IO_SASI 0x080 /* SASI Hard Disk Controller */ #define IO_FD1 0x090 /* 765A 1MB FDC */ #define IO_GDC2 0x0a0 /* 7220 GDC Graphic Control */ #define IO_CGROM 0x0a1 /* Character ROM */ #define IO_COM2 0x0b1 /* 8251A RS232C serial I/O (ext) */ #define IO_COM3 0x0b9 /* 8251A RS232C serial I/O (ext) */ #define IO_FDPORT 0x0be /* FD I/F port (1M<->640K,EMTON) */ #define IO_FD2 0x0c8 /* 765A 640KB FDC */ #define IO_SIO1 0x0d0 /* MC16550II ext RS232C */ #define IO_REEST 0x0F0 /* CPU FPU reset */ #define IO_A2OEN 0x0F2 /* A20 enable */ #define IO_A20CT 0x0F6 /* A20 control enable/disable */ #define IO_NPX 0x0F8 /* Numeric Coprocessor */ #define IO_SOUND 0x188 /* YM2203 FM sound board */ #define IO_EGC 0x4a0 /* 7220 GDC Graphic Control */ #define IO_SCSI 0xcc0 /* SCSI Controller */ #define IO_SIO2 0x8d0 /* MC16550II ext RS232C */ #define IO_BEEPF 0x3fdb /* beep frequency */ #define IO_MOUSE 0x7fd9 /* mouse */ #define IO_BMS 0x7fd9 /* Bus Mouse */ #define IO_MSE 0x7fd9 /* Bus Mouse */ #define IO_MOUSETM 0xdfbd /* mouse timer */ #define IO_WD1_NEC 0x640 /* 98note IDE Hard disk controller */ #define IO_WD1_EPSON 0x80 /* 386note Hard disk controller */ #define IO_WD1 IO_WD1_NEC /* IDE Hard disk controller */ #define IO_ISAEND 0xFFFF /* - 0x3FF End of I/O Registers */ #endif /* !IO_ISABEGIN */ /* * Input / Output Port Sizes - these are from several sources, and tend * to be the larger of what was found, ie COM ports can be 4, but some * boards do not fully decode the address, thus 8 ports are used. */ #ifndef IO_ISASIZES #define IO_ISASIZES #define IO_COMSIZE 8 /* 8250, 16X50 com controllers (4?) */ #define IO_CGASIZE 16 /* CGA controllers */ #define IO_DMASIZE 16 /* 8237 DMA controllers */ #define IO_DPGSIZE 32 /* 74LS612 DMA page registers */ #define IO_FDCSIZE 8 /* Nec765 floppy controllers */ #define IO_WDCSIZE 8 /* WD compatible disk controllers */ #define IO_GAMSIZE 16 /* AT compatible game controllers */ #define IO_ICUSIZE 16 /* 8259A interrupt controllers */ #define IO_KBDSIZE 16 /* 8042 Keyboard controllers */ #define IO_LPTSIZE 8 /* LPT controllers, some use only 4 */ #define IO_MDASIZE 16 /* Monochrome display controllers */ #define IO_RTCSIZE 16 /* CMOS real time clock, NMI control */ #define IO_TMRSIZE 16 /* 8253 programmable timers */ #define IO_NPXSIZE 16 /* 80387/80487 NPX registers */ #define IO_VGASIZE 16 /* VGA controllers */ #define IO_EISASIZE 4096 /* EISA controllers */ #define IO_PMPSIZE 2 /* 82347 power management peripheral */ #endif /* !IO_ISASIZES */ /* * Input / Output Memory Physical Addresses */ #ifndef IOM_BEGIN #define IOM_BEGIN 0x0a0000 /* Start of I/O Memory "hole" */ #define IOM_END 0x100000 /* End of I/O Memory "hole" */ #define IOM_SIZE (IOM_END - IOM_BEGIN) #endif /* !RAM_BEGIN */ /* * RAM Physical Address Space (ignoring the above mentioned "hole") */ #ifndef RAM_BEGIN #define RAM_BEGIN 0x0000000 /* Start of RAM Memory */ #ifdef EPSON_BOUNCEDMA #define RAM_END 0x0f00000 /* End of EPSON GR?? RAM Memory */ #else #define RAM_END 0x1000000 /* End of RAM Memory */ #endif #define RAM_SIZE (RAM_END - RAM_BEGIN) #endif /* !RAM_BEGIN */ #ifndef PC98 /* IBM-PC */ /* * Oddball Physical Memory Addresses */ #ifndef COMPAQ_RAMRELOC #define COMPAQ_RAMRELOC 0x80c00000 /* Compaq RAM relocation/diag */ #define COMPAQ_RAMSETUP 0x80c00002 /* Compaq RAM setup */ #define WEITEK_FPU 0xC0000000 /* WTL 2167 */ #define CYRIX_EMC 0xC0000000 /* Cyrix EMC */ #endif COMPAQ_RAMRELOC #endif #define PC98_VECTOR_SIZE (0x400) #define PC98_SYSTEM_PARAMETER_SIZE (0x230) #define PC98_SAVE_AREA(highreso_flag) (0xa1000) #define PC98_SAVE_AREA_ADDRESS (0x10) #define OFS_BOOT_boothowto 0x210 #define OFS_BOOT_bootdev 0x214 #define OFS_BOOT_cyloffset 0x218 #define OFS_WD_BIOS_SECSIZE(i) (0x200+(i)*6) #define OFS_WD_BIOS_NCYL(i) (0x202+(i)*6) #define OFS_WD_BIOS_HEAD(i) (0x205+(i)*6) #define OFS_WD_BIOS_SEC(i) (0x204+(i)*6) #define OFS_pc98_machine_type 0x220 #define OFS_epson_machine_id 0x224 #define OFS_epson_bios_id 0x225 #define OFS_epson_system_type 0x226 #define M_NEC_PC98 0x0001 #define M_EPSON_PC98 0x0002 #define M_NOT_H98 0x0010 #define M_H98 0x0020 #define M_NOTE 0x0040 #define M_NORMAL 0x1000 #define M_HIGHRESO 0x2000 #define M_8M 0x8000 #if defined(KERNEL) && !defined(LOCORE) /* BIOS parameter block */ extern unsigned char pc98_system_parameter[]; /* in locore.c */ #define PC98_SYSTEM_PARAMETER(x) pc98_system_parameter[(x)-0x400] #define BOOT_boothowto (*(unsigned long*)(&pc98_system_parameter[OFS_BOOT_boothowto])) #define BOOT_bootdev (*(unsigned long*)(&pc98_system_parameter[OFS_BOOT_bootdev])) #define BOOT_cyloffset (*(unsigned long*)(&pc98_system_parameter[OFS_BOOT_cyloffset])) #define WD_BIOS_SECSIZE(i) (*(unsigned short*)(&pc98_system_parameter[OFS_WD_BIOS_SECSIZE(i)])) #define WD_BIOS_NCYL(i) (*(unsigned short*)(&pc98_system_parameter[OFS_WD_BIOS_NCYL(i)])) #define WD_BIOS_HEAD(i) (pc98_system_parameter[OFS_WD_BIOS_HEAD(i)]) #define WD_BIOS_SEC(i) (pc98_system_parameter[OFS_WD_BIOS_SEC(i)]) #define pc98_machine_type (*(unsigned long*)&pc98_system_parameter[OFS_pc98_machine_type]) #define epson_machine_id (pc98_system_parameter[OFS_epson_machine_id]) #define epson_bios_id (pc98_system_parameter[OFS_epson_bios_id]) #define epson_system_type (pc98_system_parameter[OFS_epson_system_type]) # define PC98_TYPE_CHECK(x) ((pc98_machine_type & (x)) == (x)) + +#include + +static inline u_char +epson_inb(u_int port) +{ + u_char data; + + outb(0x43f, 0x42); + data = inb(port); + outb(0x43f, 0x40); + return (data); +} + +static inline void +epson_outb(u_int port, u_char data) +{ + outb(0x43f, 0x42); + outb(port,data); + outb(0x43f, 0x40); +} + +static inline void +epson_insw(u_int port, void *addr, size_t cnt) +{ + int s; + + s = splbio(); + outb(0x43f, 0x42); + disable_intr(); + insw((u_int)port, (void *)addr, (size_t)cnt); + outb(0x43f, 0x40); + splx(s); +} + +static inline void +epson_outsw(u_int port, void *addr, size_t cnt) +{ + int s; + + s = splbio(); + outb(0x43f, 0x42); + disable_intr(); + outsw((u_int)port, (void *)addr, (size_t)cnt); + outb(0x43f, 0x40); + splx(s); +} #endif /* KERNEL */ /* * Obtained from NetBSD/pc98 */ #define MADDRUNK -1 #endif /* !_PC98_PC98_PC98_H_ */ Index: head/sys/pc98/cbus/sio.c =================================================================== --- head/sys/pc98/cbus/sio.c (revision 18264) +++ head/sys/pc98/cbus/sio.c (revision 18265) @@ -1,3838 +1,3837 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.5 1996/09/07 02:14:23 asami Exp $ + * $Id: sio.c,v 1.6 1996/09/10 09:38:34 asami Exp $ */ #include "opt_comconsole.h" #include "opt_ddb.h" #include "opt_sio.h" #include "sio.h" /* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. * * Changes for PC-Card integration: * - Added PC-Card driver table and handlers */ /*=============================================================== * 386BSD(98),FreeBSD-1.1x(98) com driver. * ----- * modified for PC9801 by M.Ishii * Kyoto University Microcomputer Club (KMC) * Chou "TEFUTEFU" Hirotomi * Kyoto Univ. the faculty of medicine *=============================================================== * FreeBSD-2.0.1(98) sio driver. * ----- * modified for pc98 Internal i8251 and MICRO CORE MC16550II * T.Koike(hfc01340@niftyserve.or.jp) * implement kernel device configuration * aizu@orient.center.nitech.ac.jp * * Notes. * ----- * PC98 localization based on 386BSD(98) com driver. Using its PC98 local * functions. * This driver is under debugging,has bugs. * * 1) config * options COM_MULTIPORT #if using MC16550II * device sio0 at nec? port 0x30 tty irq 4 vector siointr #internal * device sio1 at nec? port 0xd2 tty irq 5 flags 0x101 vector siointr #mc1 * device sio2 at nec? port 0x8d2 tty flags 0x101 vector siointr #mc2 * # ~~~~~iobase ~~multi port flag * # ~ master device is sio1 * 2) device * cd /dev; MAKEDEV ttyd0 ttyd1 .. * 3) /etc/rc.serial * 57600bps is too fast for sio0(internal8251) * my ex. * #set default speed 9600 * modem() * : * stty last update: 15 Sep.1995 * * How to configure... * # options COM_MULTIPORT # support for MICROCORE MC16550II * ... comment-out this line, which will conflict with B98_01. * options "B98_01" # support for AIWA B98-01 * device sio1 at nec? port 0x00d1 tty irq ? vector siointr * device sio2 at nec? port 0x00d5 tty irq ? vector siointr * ... you can leave these lines `irq ?', irq will be autodetected. */ #ifdef PC98 #define MC16550 0 #define COM_IF_INTERNAL 1 #if 0 #define COM_IF_PC9861K 2 #define COM_IF_PIO9032B 3 #endif #ifdef B98_01 #undef COM_MULTIPORT /* COM_MULTIPORT will conflict with B98_01 */ #define COM_IF_B98_01 4 #endif /* B98_01 */ #endif /* PC98 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #ifdef PC98 #include #include #include #include -#include -#include +#include #else #include #include #include +#endif #ifdef COM_ESP #include #endif #include -#endif #include "crd.h" #if NCRD > 0 #include #include #include #endif #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RB_I_HIGH_WATER (TTYHOG - 2 * RS_IBUFSIZE) #define RS_IBUFSIZE 256 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) #ifdef COM_MULTIPORT /* checks in flags for multiport and which is multiport "master chip" * for a given card */ #define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01) #define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff) #define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) #endif /* COM_MULTIPORT */ #define COM_LOSESOUTINTS(dev) ((dev)->id_flags & 0x08) #define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) #define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) #ifndef PC98 #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ #endif /* !PC98 */ /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches * high water, or when the high level requests it. * The high level is notified immediately (rather than at a later clock tick) * when this watermark is reached. * The buffer size is chosen so the watermark should almost never be reached. * The low watermark is invisibly 0 since the buffer is always emptied all at * once. */ #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * siostop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_int Port_t; /* hardware port */ typedef u_char bool_t; /* boolean */ /* queue of linear buffers */ struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */ }; /* com device structure */ struct com_s { u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ #ifdef COM_ESP bool_t esp; /* is this unit a hayes esp board? */ #endif u_char fifo_image; /* copy of value written to FIFO */ bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t loses_outints; /* nonzero if device loses output interrupts */ u_char mcr_image; /* copy of value written to MCR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t gone; /* hardware disappeared */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ #ifdef PC98 Port_t cmd_port; Port_t sts_port; Port_t in_modem_port; Port_t intr_ctrl_port; int intr_enable; int pc98_prev_modem_status; int pc98_modem_delta; int modem_car_chg_timer; int pc98_prev_siocmd; int pc98_prev_siomod; int modem_checking; int pc98_if_type; #endif /* PC98 */ Port_t data_port; /* i/o ports */ #ifdef COM_ESP Port_t esp_port; #endif Port_t int_id_port; Port_t iobase; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; /* * Ping-pong input buffers. The extra factor of 2 in the sizes is * to allow for an error byte for each input byte. */ #define CE_INPUT_OFFSET RS_IBUFSIZE u_char ibuf1[2 * RS_IBUFSIZE]; u_char ibuf2[2 * RS_IBUFSIZE]; /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ u_char obuf1[256]; u_char obuf2[256]; #ifdef DEVFS void *devfs_token_ttyd; void *devfs_token_ttyl; void *devfs_token_ttyi; void *devfs_token_cuaa; void *devfs_token_cual; void *devfs_token_cuai; #endif }; /* * XXX public functions in drivers should be declared in headers produced * by `config', not here. */ /* Interrupt handling entry point. */ void siopoll __P((void)); /* Device switch entry points. */ #define sioreset noreset #define siommap nommap #define siostrategy nostrategy #ifdef COM_ESP static int espattach __P((struct isa_device *isdp, struct com_s *com, Port_t esp_port)); #endif static int sioattach __P((struct isa_device *dev)); static timeout_t siodtrwakeup; static void comhardclose __P((struct com_s *com)); static void siointr1 __P((struct com_s *com)); static int commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); static int sioprobe __P((struct isa_device *dev)); static void siosettimeout __P((void)); static void comstart __P((struct tty *tp)); static timeout_t comwakeup; static int tiocm_xxx2mcr __P((int tiocm_xxx)); static void disc_optim __P((struct tty *tp, struct termios *t, struct com_s *com)); #ifdef DSI_SOFT_MODEM static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr)); #endif /* DSI_SOFT_MODEM */ static char driver_name[] = "sio"; /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIO]; #define com_addr(unit) (p_com_addr[unit]) struct isa_driver siodriver = { sioprobe, sioattach, driver_name }; static d_open_t sioopen; static d_close_t sioclose; static d_read_t sioread; static d_write_t siowrite; static d_ioctl_t sioioctl; static d_stop_t siostop; static d_devtotty_t siodevtotty; #define CDEV_MAJOR 28 static struct cdevsw sio_cdevsw = { sioopen, sioclose, sioread, siowrite, sioioctl, siostop, noreset, siodevtotty, ttselect, nommap, NULL, driver_name, NULL, -1, }; static int comconsole = -1; static speed_t comdefaultrate = TTYDEF_SPEED; static u_int com_events; /* input chars + weighted output completions */ static int sio_timeout; static int sio_timeouts_until_log; #if 0 /* XXX */ static struct tty *sio_tty[NSIO]; #else static struct tty sio_tty[NSIO]; #endif static const int nsio_tty = NSIO; #ifdef PC98 struct siodev { short if_type; short irq; Port_t cmd, sts, ctrl, mod; }; static int sysclock; static short port_table[5][3] = { {0x30, 0xb1, 0xb9}, {0x32, 0xb3, 0xbb}, {0x32, 0xb3, 0xbb}, {0x33, 0xb0, 0xb2}, {0x35, 0xb0, 0xb2} }; #define PC98SIO_data_port(ch) port_table[0][ch] #define PC98SIO_cmd_port(ch) port_table[1][ch] #define PC98SIO_sts_port(ch) port_table[2][ch] #define PC98SIO_in_modem_port(ch) port_table[3][ch] #define PC98SIO_intr_ctrl_port(ch) port_table[4][ch] #ifdef COM_IF_PIO9032B #define IO_COM_PIO9032B_2 0x0b8 #define IO_COM_PIO9032B_3 0x0ba #endif /* COM_IF_PIO9032B */ #ifdef COM_IF_B98_01 #define IO_COM_B98_01_2 0x0d1 #define IO_COM_B98_01_3 0x0d5 #endif /* COM_IF_B98_01 */ #define COM_INT_DISABLE {int previpri; previpri=spltty(); #define COM_INT_ENABLE splx(previpri);} #define IEN_TxFLAG IEN_Tx #define COM_CARRIER_DETECT_EMULATE 0 #define PC98_CHECK_MODEM_INTERVAL (hz/10) #define DCD_OFF_TOLERANCE 2 #define DCD_ON_RECOGNITION 2 #define IS_8251(type) (type != MC16550) #define IS_PC98IN(adr) (adr == 0x30) static void commint __P((dev_t dev)); static void com_tiocm_set __P((struct com_s *com, int msr)); static void com_tiocm_bis __P((struct com_s *com, int msr)); static void com_tiocm_bic __P((struct com_s *com, int msr)); static int com_tiocm_get __P((struct com_s *com)); static int com_tiocm_get_delta __P((struct com_s *com)); static void pc98_msrint_start __P((dev_t dev)); static void com_cflag_and_speed_set __P((struct com_s *com, int cflag, int speed)); static int pc98_ttspeedtab __P((struct com_s *com, int speed)); static int pc98_get_modem_status __P((struct com_s *com)); static timeout_t pc98_check_msr; static void pc98_set_baud_rate __P((struct com_s *com, int count)); static void pc98_i8251_reset __P((struct com_s *com, int mode, int command)); static void pc98_disable_i8251_interrupt __P((struct com_s *com, int mod)); static void pc98_enable_i8251_interrupt __P((struct com_s *com, int mod)); static int pc98_check_i8251_interrupt __P((struct com_s *com)); static int pc98_i8251_get_cmd __P((struct com_s *com)); static int pc98_i8251_get_mod __P((struct com_s *com)); static void pc98_i8251_set_cmd __P((struct com_s *com, int x)); static void pc98_i8251_or_cmd __P((struct com_s *com, int x)); static void pc98_i8251_clear_cmd __P((struct com_s *com, int x)); static void pc98_i8251_clear_or_cmd __P((struct com_s *com, int clr, int x)); static int pc98_check_if_type __P((int iobase, struct siodev *iod)); static void pc98_check_sysclock __P((void)); static int pc98_set_ioport __P((struct com_s *com, int io_base)); #define com_int_Tx_disable(com) \ pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP) #define com_int_Tx_enable(com) \ pc98_enable_i8251_interrupt(com,IEN_TxFLAG) #define com_int_Rx_disable(com) \ pc98_disable_i8251_interrupt(com,IEN_Rx) #define com_int_Rx_enable(com) \ pc98_enable_i8251_interrupt(com,IEN_Rx) #define com_int_TxRx_disable(com) \ pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP|IEN_Rx) #define com_int_TxRx_enable(com) \ pc98_enable_i8251_interrupt(com,IEN_TxFLAG|IEN_Rx) #define com_send_break_on(com) \ pc98_i8251_or_cmd(com,CMD8251_SBRK) #define com_send_break_off(com) \ pc98_i8251_clear_cmd(com,CMD8251_SBRK) struct speedtab pc98speedtab[] = { /* internal RS232C interface */ 0, 0, 50, 50, 75, 75, 150, 150, 200, 200, 300, 300, 600, 600, 1200, 1200, 2400, 2400, 4800, 4800, 9600, 9600, 19200, 19200, 38400, 38400, 76800, 76800, 20800, 20800, 41600, 41600, 15600, 15600, 31200, 31200, 62400, 62400, -1, -1 }; #ifdef COM_IF_PIO9032B struct speedtab comspeedtab_pio9032b[] = { 300, 6, 600, 5, 1200, 4, 2400, 3, 4800, 2, 9600, 1, 19200, 0, 38400, 7, -1, -1 }; #endif #ifdef COM_IF_B98_01 struct speedtab comspeedtab_b98_01[] = { 0, 0, 75, 15, 150, 14, 300, 13, 600, 12, 1200, 11, 2400, 10, 4800, 9, 9600, 8, 19200, 7, 38400, 6, 76800, 5, 153600, 4, -1, -1 }; #endif #endif /* PC98 */ static struct speedtab comspeedtab[] = { { 0, 0 }, { 50, COMBRD(50) }, { 75, COMBRD(75) }, { 110, COMBRD(110) }, { 134, COMBRD(134) }, { 150, COMBRD(150) }, { 200, COMBRD(200) }, { 300, COMBRD(300) }, { 600, COMBRD(600) }, { 1200, COMBRD(1200) }, { 1800, COMBRD(1800) }, { 2400, COMBRD(2400) }, { 4800, COMBRD(4800) }, { 9600, COMBRD(9600) }, { 19200, COMBRD(19200) }, { 38400, COMBRD(38400) }, { 57600, COMBRD(57600) }, { 115200, COMBRD(115200) }, { -1, -1 } }; #ifdef COM_ESP /* XXX configure this properly. */ static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; #endif #if NCRD > 0 /* * PC-Card (PCMCIA) specific code. */ static int card_intr(struct pccard_dev *); /* Interrupt handler */ static void siounload(struct pccard_dev *); /* Disable driver */ static void siosuspend(struct pccard_dev *); /* Suspend driver */ static int sioinit(struct pccard_dev *, int); /* init device */ static struct pccard_drv sio_info = { driver_name, card_intr, siounload, siosuspend, sioinit, 0, /* Attributes - presently unused */ &tty_imask /* Interrupt mask for device */ /* XXX - Should this also include net_imask? */ }; /* * Called when a power down is requested. Shuts down the * device and configures the device as unavailable (but * still loaded...). A resume is done by calling * sioinit with first=0. This is called when the user suspends * the system, or the APM code suspends the system. */ static void siosuspend(struct pccard_dev *dp) { printf("sio%d: suspending\n", dp->isahd.id_unit); } /* * Initialize the device - called from Slot manager. * If first is set, then check for the device's existence * before initializing it. Once initialized, the device table may * be set up. */ int sioinit(struct pccard_dev *dp, int first) { /* validate unit number. */ if (first) { if (dp->isahd.id_unit >= NSIO) return(ENODEV); /* Make sure it isn't already probed. */ if (com_addr(dp->isahd.id_unit)) return(EBUSY); /* * Probe the device. If a value is returned, the * device was found at the location. */ if (sioprobe(&dp->isahd)==0) return(ENXIO); if (sioattach(&dp->isahd)==0) return(ENXIO); } /* * XXX TODO: * If it was initialized before, the device structure * should also be initialized. We should * reset (and possibly restart) the hardware, but * I am not sure of the best way to do this... */ return(0); } /* * siounload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void siounload(struct pccard_dev *dp) { struct com_s *com; com = com_addr(dp->isahd.id_unit); if (!com->iobase) { printf("sio%d already unloaded!\n",dp->isahd.id_unit); return; } if (com->tp && (com->tp->t_state & TS_ISOPEN)) { com->gone = 1; printf("sio%d: unload\n", dp->isahd.id_unit); com->tp->t_gen++; ttyclose(com->tp); ttwakeup(com->tp); ttwwakeup(com->tp); } else { com_addr(com->unit) = NULL; bzero(com, sizeof *com); free(com,M_TTYS); printf("sio%d: unload,gone\n", dp->isahd.id_unit); } } /* * card_intr - Shared interrupt called from * front end of PC-Card handler. */ static int card_intr(struct pccard_dev *dp) { struct com_s *com; com = com_addr(dp->isahd.id_unit); if (com && !com_addr(dp->isahd.id_unit)->gone) siointr1(com_addr(dp->isahd.id_unit)); return(1); } #endif /* NCRD > 0 */ static int sioprobe(dev) struct isa_device *dev; { static bool_t already_init; bool_t failures[10]; int fn; struct isa_device *idev; Port_t iobase; u_char mcr_image; int result; #ifdef PC98 struct isa_device *xdev; int irqout=0; int ret = 0; int tmp; struct siodev iod; #else struct isa_device *xdev; #endif if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ for (xdev = isa_devtab_tty; xdev->id_driver != NULL; xdev++) if (xdev->id_driver == &siodriver && xdev->id_enabled) #ifdef PC98 if (IS_PC98IN(xdev->id_iobase)) outb(xdev->id_iobase + 2, 0xf2); else #else outb(xdev->id_iobase + com_mcr, 0); #endif #if NCRD > 0 /* * If PC-Card probe required, then register driver with * slot manager. */ pccard_add_driver(&sio_info); #endif already_init = TRUE; } #ifdef PC98 /* * If the port is i8251 UART (internal, B98_01) */ if(pc98_check_if_type(dev->id_iobase, &iod) == -1) return 0; if(IS_8251(iod.if_type)){ if ( iod.irq > 0 ) dev->id_irq = (1 << iod.irq); outb(iod.cmd, 0); DELAY(10); outb(iod.cmd, 0); DELAY(10); outb(iod.cmd, 0); DELAY(10); outb(iod.cmd, CMD8251_RESET); DELAY(1000); /* for a while...*/ outb(iod.cmd, 0xf2); /* MODE (dummy) */ DELAY(10); outb(iod.cmd, 0x01); /* CMD (dummy) */ DELAY(1000); /* for a while...*/ if (( inb(iod.sts) & STS8251_TxEMP ) == 0 ) { ret = 0; } switch (iod.if_type) { case COM_IF_INTERNAL: COM_INT_DISABLE tmp = ( inb( iod.ctrl ) & ~(IEN_Rx|IEN_TxEMP|IEN_Tx)); outb( iod.ctrl, tmp|IEN_TxEMP ); ret = isa_irq_pending(dev) ? 4 : 0; outb( iod.ctrl, tmp ); COM_INT_ENABLE break; #ifdef COM_IF_B98_01 case COM_IF_B98_01: /* B98_01 doesn't activate TxEMP interrupt line when being reset, so we can't check irq pending.*/ ret = 4; break; #endif } if (epson_machine_id==0x20) { /* XXX */ ret = 4; } return ret; } #endif /* PC98 */ /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. */ idev = dev; mcr_image = MCR_IENABLE; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(dev)) { idev = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(dev)); if (idev == NULL) { printf("sio%d: master device %d not configured\n", dev->id_unit, COM_MPMASTER(dev)); return (0); } #ifndef PC98 if (!COM_NOTAST4(dev)) { outb(idev->id_iobase + com_scr, idev->id_irq ? 0x80 : 0); mcr_image = 0; } #endif /* !PC98 */ } #endif /* COM_MULTIPORT */ if (idev->id_irq == 0) mcr_image = 0; #ifdef PC98 switch(idev->id_irq){ case IRQ3: irqout = 4; break; case IRQ5: irqout = 5; break; case IRQ6: irqout = 6; break; case IRQ12: irqout = 7; break; default: printf("sio%d: irq configuration error\n",dev->id_unit); return (0); } outb(dev->id_iobase+0x1000, irqout); #endif bzero(failures, sizeof failures); iobase = dev->id_iobase; /* * We don't want to get actual interrupts, just masked ones. * Interrupts from this line should already be masked in the ICU, * but mask them in the processor as well in case there are some * (misconfigured) shared interrupts. */ disable_intr(); /* EXTRA DELAY? */ /* * Initialize the speed and the word size and wait long enough to * drain the maximum of 16 bytes of junk in device output queues. * The speed is undefined after a master reset and must be set * before relying on anything related to output. There may be * junk after a (very fast) soft reboot and (apparently) after * master reset. * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ outb(iobase + com_cfcr, CFCR_DLAB); outb(iobase + com_dlbl, COMBRD(9600) & 0xff); outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8); outb(iobase + com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (9600 / 10)); /* * Enable the interrupt gate and disable device interupts. This * should leave the device driving the interrupt line low and * guarantee an edge trigger if an interrupt can be generated. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); outb(iobase + com_ier, 0); /* * Attempt to set loopback mode so that we can send a null byte * without annoying any external device. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); /* * Attempt to generate an output interrupt. On 8250's, setting * IER_ETXRDY generates an interrupt independent of the current * setting and independent of whether the THR is empty. On 16450's, * setting IER_ETXRDY generates an interrupt independent of the * current setting. On 16550A's, setting IER_ETXRDY only * generates an interrupt when IER_ETXRDY is not already set. */ outb(iobase + com_ier, IER_ETXRDY); /* * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate * an interrupt. They'd better generate one for actually doing * output. Loopback may be broken on the same incompatibles but * it's unlikely to do more than allow the null byte out. */ outb(iobase + com_data, 0); DELAY((1 + 2) * 1000000 / (9600 / 10)); /* * Turn off loopback mode so that the interrupt gate works again * (MCR_IENABLE was hidden). This should leave the device driving * an interrupt line high. It doesn't matter if the interrupt * line oscillates while we are not looking at it, since interrupts * are disabled. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); /* * Check that * o the CFCR, IER and MCR in UART hold the values written to them * (the values happen to be all distinct - this is good for * avoiding false positive tests from bus echoes). * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ /* EXTRA DELAY? */ failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; failures[1] = inb(iobase + com_ier) - IER_ETXRDY; failures[2] = inb(iobase + com_mcr) - mcr_image; DELAY(10000); /* Some internal modems need this time */ if (idev->id_irq != 0) failures[3] = isa_irq_pending(idev) ? 0 : 1; failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; DELAY(1000); /* XXX */ if (idev->id_irq != 0) 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 alone. For ports without a master port, it gates * the OUT2 output of the UART to * the ICU input. Closing the gate would give a floating ICU input * (unless there is another device driving at) and spurious interrupts. * (On the system that this was first tested on, the input floats high * and gives a (masked) interrupt as soon as the gate is closed.) */ outb(iobase + com_ier, 0); outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = inb(iobase + com_ier); DELAY(1000); /* XXX */ if (idev->id_irq != 0) failures[8] = isa_irq_pending(idev) ? 1 : 0; failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; enable_intr(); result = IO_COMSIZE; for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) { outb(iobase + com_mcr, 0); result = 0; if (COM_VERBOSE(dev)) printf("sio%d: probe test %d failed\n", dev->id_unit, fn); } return (result); } #ifdef COM_ESP static int espattach(isdp, com, esp_port) struct isa_device *isdp; struct com_s *com; Port_t esp_port; { u_char dips; u_char val; /* * Check the ESP-specific I/O port to see if we're an ESP * card. If not, return failure immediately. */ if ((inb(esp_port) & 0xf3) == 0) { printf(" port 0x%x is not an ESP board?\n", esp_port); return (0); } /* * We've got something that claims to be a Hayes ESP card. * Let's hope so. */ /* Get the dip-switch configuration */ outb(esp_port + ESP_CMD1, ESP_GETDIPS); dips = inb(esp_port + ESP_STATUS1); /* * Bits 0,1 of dips say which COM port we are. */ if (com->iobase == likely_com_ports[dips & 0x03]) printf(" : ESP"); else { printf(" esp_port has com %d\n", dips & 0x03); return (0); } /* * Check for ESP version 2.0 or later: bits 4,5,6 = 010. */ outb(esp_port + ESP_CMD1, ESP_GETTEST); val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ val = inb(esp_port + ESP_STATUS2); if ((val & 0x70) < 0x20) { printf("-old (%o)", val & 0x70); return (0); } /* * Check for ability to emulate 16550: bit 7 == 1 */ if ((dips & 0x80) == 0) { printf(" slave"); return (0); } /* * Okay, we seem to be a Hayes ESP card. Whee. */ com->esp = TRUE; com->esp_port = esp_port; return (1); } #endif /* COM_ESP */ static int sioattach(isdp) struct isa_device *isdp; { struct com_s *com; dev_t dev; #ifdef COM_ESP Port_t *espp; #endif Port_t iobase; int s; int unit; isdp->id_ri_flags |= RI_FAST; iobase = isdp->id_iobase; unit = isdp->id_unit; com = malloc(sizeof *com, M_TTYS, M_NOWAIT); if (com == NULL) return (0); /* * sioprobe() has initialized the device registers as follows: * o cfcr = CFCR_8BITS. * It is most important that CFCR_DLAB is off, so that the * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->unit = unit; com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; com->loses_outints = COM_LOSESOUTINTS(isdp) != 0; com->no_irq = isdp->id_irq == 0; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; com->iobase = iobase; #ifdef PC98 if(pc98_set_ioport(com, iobase) == -1) if((iobase & 0x0f0) == 0xd0) { com->pc98_if_type = MC16550; com->data_port = iobase + com_data; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; } #else /* not PC98 */ com->data_port = iobase + com_data; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; #endif /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole) { #ifdef PC98 if(IS_8251(com->pc98_if_type)) DELAY(100000); #endif com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; } termioschars(&com->it_in); com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); #ifdef DSI_SOFT_MODEM if((inb(iobase+7) ^ inb(iobase+7)) & 0x80) { printf(" Digicom Systems, Inc. SoftModem"); goto determined_type; } #endif /* DSI_SOFT_MODEM */ #ifndef PC98 #ifdef COM_MULTIPORT if (!COM_ISMULTIPORT(isdp)) #endif { u_char scr; u_char scr1; u_char scr2; scr = inb(iobase + com_scr); outb(iobase + com_scr, 0xa5); scr1 = inb(iobase + com_scr); outb(iobase + com_scr, 0x5a); scr2 = inb(iobase + com_scr); outb(iobase + com_scr, scr); if (scr1 != 0xa5 || scr2 != 0x5a) { printf(" 8250"); goto determined_type; } } #endif /* !PC98 */ #ifdef PC98 if(IS_8251(com->pc98_if_type)){ com_int_TxRx_disable( com ); com_cflag_and_speed_set( com, com->it_in.c_cflag, comdefaultrate ); com_tiocm_bic( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE ); com_send_break_off( com ); switch(com->pc98_if_type){ case COM_IF_INTERNAL: printf(" 8251 (internal)"); break; #ifdef COM_IF_PC9861K case COM_IF_PC9861K: printf(" 8251 (PC9861K)"); break; #endif #ifdef COM_IF_PIO9032B case COM_IF_PIO9032B: printf(" 8251 (PIO9032B)"); break; #endif #ifdef COM_IF_B98_01 case COM_IF_B98_01: printf(" 8251 (B98_01)"); break; #endif } } else { #endif /* PC98 */ outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); DELAY(100); switch (inb(com->int_id_port) & IIR_FIFO_MASK) { case FIFO_RX_LOW: printf(" 16450"); break; case FIFO_RX_MEDL: printf(" 16450?"); break; case FIFO_RX_MEDH: printf(" 16550?"); break; case FIFO_RX_HIGH: printf(" 16550A"); if (COM_NOFIFO(isdp)) { printf(" fifo disabled"); } else { com->hasfifo = TRUE; com->tx_fifo_size = 16; #ifdef COM_ESP for (espp = likely_esp_ports; *espp != 0; espp++) if (espattach(isdp, com, *espp)) { com->tx_fifo_size = 1024; break; } #endif } #if 0 /* * Check for the Startech ST16C650 chip. * it has a shadow register under the com_iir, * which can only be accessed when cfcr == 0xff */ { u_char i, j; i = inb(iobase + com_iir); outb(iobase + com_cfcr, 0xff); outb(iobase + com_iir, 0x0); outb(iobase + com_cfcr, CFCR_8BITS); j = inb(iobase + com_iir); outb(iobase + com_iir, i); if (i != j) { printf(" 16550A"); } else { com->tx_fifo_size = 32; printf(" 16650"); } if (!com->tx_fifo_size) printf(" fifo disabled"); } #endif break; } #ifdef COM_ESP if (com->esp) { outb(iobase + com_fifo, FIFO_DMA_MODE | FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_RX_MEDH); /* Set 16550 compatibility mode. */ outb(com->esp_port + ESP_CMD1, ESP_SETMODE); outb(com->esp_port + ESP_CMD2, ESP_MODE_SCALE | ESP_MODE_RTS | ESP_MODE_FIFO); /* Set RTS/CTS flow control. */ outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); /* Set flow-control levels. */ outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); outb(com->esp_port + ESP_CMD2, HIBYTE(768)); outb(com->esp_port + ESP_CMD2, LOBYTE(768)); outb(com->esp_port + ESP_CMD2, HIBYTE(512)); outb(com->esp_port + ESP_CMD2, LOBYTE(512)); } #endif /* COM_ESP */ outb(iobase + com_fifo, 0); determined_type: ; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(isdp)) { com->multiport = TRUE; printf(" (multiport"); if (unit == COM_MPMASTER(isdp)) printf(" master"); printf(")"); com->no_irq = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(isdp))->id_irq == 0; } #endif /* COM_MULTIPORT */ #ifdef PC98 } #endif printf("\n"); s = spltty(); com_addr(unit) = com; splx(s); dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev, &sio_cdevsw, NULL); #ifdef DEVFS /* devsw, minor, type, uid, gid, perm, fmt, ... */ com->devfs_token_ttyd = devfs_add_devswf(&sio_cdevsw, unit, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyd%n", unit); com->devfs_token_ttyi = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_INIT_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyid%n", unit); com->devfs_token_ttyl = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_LOCK_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyld%n", unit); com->devfs_token_cuaa = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaa%n", unit); com->devfs_token_cuai = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaia%n", unit); com->devfs_token_cual = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuala%n", unit); #endif return (1); } static int sioopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); if (com->gone) return (ENXIO); if (mynor & CONTROL_MASK) return (0); #if 0 /* XXX */ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else tp = com->tp = &sio_tty[unit]; #endif s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "siobi", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; #ifdef PC98 if(!IS_8251(com->pc98_if_type)) #endif (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); com->poll = com->no_irq; com->poll_output = com->loses_outints; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; #ifdef PC98 if(IS_8251(com->pc98_if_type)){ com_tiocm_bis(com, TIOCM_DTR|TIOCM_RTS); pc98_msrint_start(dev); } #endif /* * XXX we should goto open_top if comparam() slept. */ ttsetwater(tp); iobase = com->iobase; if (com->hasfifo) { /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); DELAY(100); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(100); (void) inb(com->data_port); } } disable_intr(); #ifdef PC98 if(IS_8251(com->pc98_if_type)){ com_tiocm_bis(com, TIOCM_LE); com->pc98_prev_modem_status = pc98_get_modem_status(com); com_int_Rx_enable(com); } else { #endif (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); #ifdef PC98 } #endif enable_intr(); /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "siobi" * instead of "siodcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ #ifdef PC98 if ((IS_8251(com->pc98_if_type) && (pc98_get_modem_status(com) & TIOCM_CAR)) || (!IS_8251(com->pc98_if_type) && (com->prev_modem_status & MSR_DCD)) || mynor & CALLOUT_MASK) #else if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) #endif (*linesw[tp->t_line].l_modem)(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); if (com_addr(unit) == NULL) return (ENXIO); --com->wopeners; if (error != 0 || com->gone) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); disc_optim(tp, &tp->t_termios, com); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout(); out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } static int sioclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); #ifdef PC98 com->modem_checking = 0; #endif disc_optim(tp, &tp->t_termios, com); siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s); if (com->gone) { printf("sio%d: gone\n", com->unit); s = spltty(); com_addr(com->unit) = 0; bzero(tp,sizeof *tp); bzero(com,sizeof *com); free(com,M_TTYS); splx(s); } return (0); } static void comhardclose(com) struct com_s *com; { Port_t iobase; int s; struct tty *tp; int unit; unit = com->unit; iobase = com->iobase; s = spltty(); com->poll = FALSE; com->poll_output = FALSE; com->do_timestamp = 0; #ifdef PC98 if(IS_8251(com->pc98_if_type)) com_send_break_off(com); else #endif outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); { #ifdef PC98 int tmp; if(IS_8251(com->pc98_if_type)) com_int_TxRx_disable(com); else #endif outb(iobase + com_ier, 0); tp = com->tp; #ifdef PC98 if(IS_8251(com->pc98_if_type)) tmp = pc98_get_modem_status(com) & TIOCM_CAR; else tmp = com->prev_modem_status & MSR_DCD; #endif if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || !com->active_out #ifdef PC98 && !(tmp) #else && !(com->prev_modem_status & MSR_DCD) #endif && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { #ifdef PC98 if(IS_8251(com->pc98_if_type)) com_tiocm_bic(com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE); else #endif (void)commctl(com, TIOCM_DTR, DMBIC); if (com->dtr_wait != 0) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } #ifdef PC98 else { if(IS_8251(com->pc98_if_type)) com_tiocm_bic(com, TIOCM_LE ); } #endif } if (com->hasfifo) { /* * Disable fifos so that they are off after controlled * reboots. Some BIOSes fail to detect 16550s when the * fifos are enabled. */ outb(iobase + com_fifo, 0); } com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } static int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; int unit; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); if (com_addr(unit)->gone) return (ENODEV); tp = com_addr(unit)->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } static int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); if (com_addr(unit)->gone) return (ENODEV); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty != NULL && unit == comconsole) constty = NULL; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); } void siointr(unit) int unit; { #ifndef COM_MULTIPORT siointr1(com_addr(unit)); #else /* COM_MULTIPORT */ struct com_s *com; bool_t possibly_more_intrs; /* * Loop until there is no activity on any port. This is necessary * to get an interrupt edge more than to avoid another interrupt. * If the IRQ signal is just an OR of the IRQ signals from several * devices, then the edge from one may be lost because another is * on. */ do { possibly_more_intrs = FALSE; for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); #ifdef PC98 if (com != NULL && !com->gone && IS_8251(com->pc98_if_type)){ siointr1(com); } else #endif /* PC98 */ if (com != NULL && !com->gone && (inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { siointr1(com); possibly_more_intrs = TRUE; } } } while (possibly_more_intrs); #endif /* COM_MULTIPORT */ } static void siointr1(com) struct com_s *com; { u_char line_status; u_char modem_status; u_char *ioptr; u_char recv_data; #ifdef PC98 u_char tmp=0; recv_data=0; #endif /* PC98 */ while (TRUE) { #ifdef PC98 status_read:; if (IS_8251(com->pc98_if_type)) { tmp = inb(com->sts_port); more_intr: line_status = 0; if (tmp & STS8251_TxRDY) line_status |= LSR_TXRDY; if (tmp & STS8251_RxRDY) line_status |= LSR_RXRDY; if (tmp & STS8251_TxEMP) line_status |= LSR_TSRE; if (tmp & STS8251_PE) line_status |= LSR_PE; if (tmp & STS8251_OE) line_status |= LSR_OE; if (tmp & STS8251_FE) line_status |= LSR_FE; if (tmp & STS8251_BD_SD) line_status |= LSR_BI; } else #endif /* PC98 */ line_status = inb(com->line_status_port); /* input event? (check first to help avoid overruns) */ while (line_status & LSR_RCV_MASK) { /* break/unnattached error bits or real input? */ #ifdef PC98 if(IS_8251(com->pc98_if_type)){ recv_data = inb(com->data_port); if(tmp & 0x78){ pc98_i8251_or_cmd(com,CMD8251_ER); recv_data = 0; } } else { #endif /* PC98 */ if (!(line_status & LSR_RXRDY)) recv_data = 0; else recv_data = inb(com->data_port); #ifdef PC98 } #endif if (line_status & (LSR_PE|LSR_FE|LSR_BI)) { #ifdef DDB #ifdef BREAK_TO_DEBUGGER if (line_status & LSR_BI && com->unit == comconsole) { Debugger("serial console break"); goto cont; } #endif #endif /* Don't store PE if IGNPAR and BI if IGNBRK, this hack allows "raw" tty optimization works even if IGN* is set. */ if ( com->tp == NULL || !(com->tp->t_state & TS_ISOPEN) || (line_status & (LSR_PE|LSR_FE)) && (com->tp->t_iflag & IGNPAR) || (line_status & LSR_BI) && (com->tp->t_iflag & IGNBRK)) goto cont; if ( (line_status & (LSR_PE|LSR_FE)) && (com->tp->t_state & TS_CAN_BYPASS_L_RINT) && ((line_status & LSR_FE) || (line_status & LSR_PE) && (com->tp->t_iflag & INPCK))) recv_data = 0; } ++com->bytes_in; if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { if (com->do_timestamp) microtime(&com->timestamp); ++com_events; schedsofttty(); #if 0 /* for testing input latency vs efficiency */ if (com->iptr - com->ibuf == 8) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) #ifdef PC98 if(IS_8251(com->pc98_if_type)) com_tiocm_bic(com, TIOCM_RTS); else #endif outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } cont: /* * "& 0x7F" is to avoid the gcc-1.40 generating a slow * jump from the top of the loop to here */ #ifdef PC98 if(IS_8251(com->pc98_if_type)) goto status_read; else #endif line_status = inb(com->line_status_port) & 0x7F; } /* modem status change? (always check before doing output) */ #ifdef PC98 if(!IS_8251(com->pc98_if_type)){ #endif modem_status = inb(com->modem_status_port); if (modem_status != com->last_modem_status) { if (com->do_dcd_timestamp && !(com->last_modem_status & MSR_DCD) && modem_status & MSR_DCD) microtime(&com->dcd_timestamp); /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; setsofttty(); } /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } } #ifdef PC98 } #endif /* output queued and everything ready? */ if (line_status & LSR_TXRDY && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { ioptr = com->obufq.l_head; if (com->tx_fifo_size > 1) { u_int ocount; ocount = com->obufq.l_tail - ioptr; if (ocount > com->tx_fifo_size) ocount = com->tx_fifo_size; com->bytes_out += ocount; do outb(com->data_port, *ioptr++); while (--ocount != 0); } else { outb(com->data_port, *ioptr++); ++com->bytes_out; } #ifdef PC98 if(IS_8251(com->pc98_if_type)) if ( !(pc98_check_i8251_interrupt(com) & IEN_TxFLAG) ) com_int_Tx_enable(com); #endif com->obufq.l_head = ioptr; if (ioptr >= com->obufq.l_tail) { struct lbq *qp; qp = com->obufq.l_next; qp->l_queued = FALSE; qp = qp->l_next; if (qp != NULL) { com->obufq.l_head = qp->l_head; com->obufq.l_tail = qp->l_tail; com->obufq.l_next = qp; } else { /* output just completed */ com->state &= ~CS_BUSY; #if defined(PC98) if(IS_8251(com->pc98_if_type)) if ( pc98_check_i8251_interrupt(com) & IEN_TxFLAG ) com_int_Tx_disable(com); #endif } if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; setsofttty(); /* handle at high level ASAP */ } } } #ifdef PC98 else if (line_status & LSR_TXRDY) { if(IS_8251(com->pc98_if_type)) if ( pc98_check_i8251_interrupt(com) & IEN_TxFLAG ) com_int_Tx_disable(com); } if(IS_8251(com->pc98_if_type)) if ((tmp = inb(com->sts_port)) & STS8251_RxRDY) goto more_intr; #endif /* finished? */ #ifndef COM_MULTIPORT #ifdef PC98 if(IS_8251(com->pc98_if_type)) return; #endif if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) #endif /* COM_MULTIPORT */ return; } } static int sioioctl(dev, cmd, data, flag, p) dev_t dev; int cmd; caddr_t data; int flag; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) int oldcmd; struct termios term; #endif mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); if (com->gone) return (ENODEV); iobase = com->iobase; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); #ifdef DSI_SOFT_MODEM /* * Download micro-code to Digicom modem. */ case TIOCDSIMICROCODE: { u_long l; u_char *p,*pi; pi = (u_char*)(*(caddr_t*)data); error = copyin(pi,&l,sizeof l); if(error) {return error;}; pi += sizeof l; p = malloc(l,M_TEMP,M_NOWAIT); if(!p) {return ENOBUFS;} error = copyin(pi,p,l); if(error) {free(p,M_TEMP); return error;}; if(error = LoadSoftModem( MINOR_TO_UNIT(mynor),iobase,l,p)) {free(p,M_TEMP); return error;} free(p,M_TEMP); return(0); } #endif /* DSI_SOFT_MODEM */ default: return (ENOTTY); } } tp = com->tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); s = spltty(); error = ttioctl(tp, cmd, data, flag); disc_optim(tp, &tp->t_termios, com); if (error >= 0) { splx(s); return (error); } #ifdef PC98 if(IS_8251(com->pc98_if_type)){ switch (cmd) { case TIOCSBRK: com_send_break_on( com ); break; case TIOCCBRK: com_send_break_off( com ); break; case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); return (ENOTTY); } } else { #endif switch (cmd) { case TIOCSBRK: outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; default: splx(s); return (ENOTTY); } #ifdef PC98 } #endif splx(s); return (0); } void siopoll() { int unit; if (com_events == 0) return; repeat: for (unit = 0; unit < NSIO; ++unit) { u_char *buf; struct com_s *com; u_char *ibuf; int incc; struct tty *tp; #ifdef PC98 int tmp; #endif com = com_addr(unit); if (com == NULL) continue; if (com->gone) continue; tp = com->tp; if (tp == NULL) { /* * XXX forget any events related to closed devices * (actually never opened devices) so that we don't * loop. */ disable_intr(); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; enable_intr(); if (incc != 0) log(LOG_DEBUG, "sio%d: %d events for device with no tp\n", unit, incc); continue; } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { buf = NULL; /* not used, but compiler can't tell */ incc = 0; } else { buf = ibuf; disable_intr(); incc = com->iptr - buf; com_events -= incc; if (ibuf == com->ibuf1) ibuf = com->ibuf2; else ibuf = com->ibuf1; com->ibufend = ibuf + RS_IBUFSIZE; com->ihighwater = ibuf + RS_IHIGHWATER; com->iptr = ibuf; /* * There is now room for another low-level buffer full * of input, so enable RTS if it is now disabled and * there is room in the high-level buffer. */ /* * XXX this used not to look at CS_RTS_IFLOW. The * change is to allow full control of MCR_RTS via * ioctls after turning CS_RTS_IFLOW off. Check * for races. We shouldn't allow the ioctls while * CS_RTS_IFLOW is on. */ #ifdef PC98 if(IS_8251(com->pc98_if_type)) tmp = com_tiocm_get(com) & TIOCM_RTS; else tmp = com->mcr_image & MCR_RTS; #endif if ((com->state & CS_RTS_IFLOW) #ifdef PC98 && !(tmp) #else && !(com->mcr_image & MCR_RTS) #endif && !(tp->t_state & TS_TBLOCK)) #ifdef PC98 if(IS_8251(com->pc98_if_type)) com_tiocm_bis(com, TIOCM_RTS); else #endif outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); enable_intr(); com->ibuf = ibuf; } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; #ifdef PC98 if(!IS_8251(com->pc98_if_type)){ #endif disable_intr(); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); if (delta_modem_status & MSR_DCD) (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); #ifdef PC98 } #endif } if (com->state & CS_ODONE) { disable_intr(); com_events -= LOTS_OF_EVENTS; com->state &= ~CS_ODONE; if (!(com->state & CS_BUSY)) com->tp->t_state &= ~TS_BUSY; enable_intr(); (*linesw[tp->t_line].l_start)(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) continue; /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (tp->t_rawq.c_cc + incc >= RB_I_HIGH_WATER && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; comstart(tp); } } else { do { u_char line_status; int recv_data; line_status = (u_char) buf[CE_INPUT_OFFSET]; recv_data = (u_char) *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } (*linesw[tp->t_line].l_rint)(recv_data, tp); } while (--incc > 0); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { u_int cfcr; int cflag; struct com_s *com; int divisor; int error; Port_t iobase; int s; int unit; int txtimeout; #ifdef PC98 Port_t tmp_port; int tmp_flg; #endif #ifdef PC98 cfcr = 0; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; if(IS_8251(com->pc98_if_type)) { divisor = pc98_ttspeedtab(com, t->c_ospeed); } else #endif /* do historical conversions */ if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; /* check requested parameters */ divisor = ttspeedtab(t->c_ospeed, comspeedtab); if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ #ifndef PC98 unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; #endif s = spltty(); #ifdef PC98 if(IS_8251(com->pc98_if_type)){ if(divisor == 0){ com_int_TxRx_disable( com ); com_tiocm_bic( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE ); } } else { #endif if (divisor == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ else (void)commctl(com, TIOCM_DTR, DMBIS); #ifdef PC98 } #endif cflag = t->c_cflag; #ifdef PC98 if(!IS_8251(com->pc98_if_type)){ #endif switch (cflag & CSIZE) { case CS5: cfcr = CFCR_5BITS; break; case CS6: cfcr = CFCR_6BITS; break; case CS7: cfcr = CFCR_7BITS; break; default: cfcr = CFCR_8BITS; break; } if (cflag & PARENB) { cfcr |= CFCR_PENAB; if (!(cflag & PARODD)) cfcr |= CFCR_PEVEN; } if (cflag & CSTOPB) cfcr |= CFCR_STOPB; if (com->hasfifo && divisor != 0) { /* * Use a fifo trigger level low enough so that the input * latency from the fifo is less than about 16 msec and * the total latency is less than about 30 msec. These * latencies are reasonable for humans. Serial comms * protocols shouldn't expect anything better since modem * latencies are larger. */ com->fifo_image = t->c_ospeed <= 4800 ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_HIGH; outb(iobase + com_fifo, com->fifo_image); } /* * Some UARTs lock up if the divisor latch registers are selected * while the UART is doing output (they refuse to transmit anything * more until given a hard reset). Fix this by stopping filling * the device buffers and waiting for them to drain. Reading the * line status port outside of siointr1() might lose some receiver * error bits, but that is acceptable here. */ #ifdef PC98 } #endif disable_intr(); retry: com->state &= ~CS_TTGO; txtimeout = tp->t_timeout; enable_intr(); #ifdef PC98 if(IS_8251(com->pc98_if_type)){ tmp_port = com->sts_port; tmp_flg = (STS8251_TxRDY|STS8251_TxEMP); } else { tmp_port = com->line_status_port; tmp_flg = (LSR_TSRE|LSR_TXRDY); } while ((inb(tmp_port) & tmp_flg) != tmp_flg) { #else while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) { #endif tp->t_state |= TS_SO_OCOMPLETE; error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH, "siotx", hz / 100); if ( txtimeout != 0 && (!error || error == EAGAIN) && (txtimeout -= hz / 100) <= 0 ) error = EIO; if (com->gone) error = ENODEV; if (error != 0 && error != EAGAIN) { if (!(tp->t_state & TS_TTSTOP)) { disable_intr(); com->state |= CS_TTGO; enable_intr(); } splx(s); return (error); } } disable_intr(); /* very important while com_data is hidden */ /* * XXX - clearing CS_TTGO is not sufficient to stop further output, * because siopoll() calls comstart() which usually sets it again * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be * sufficient, for similar reasons. */ #ifdef PC98 if ((inb(tmp_port) & tmp_flg) != tmp_flg) #else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) #endif goto retry; #ifdef PC98 if(!IS_8251(com->pc98_if_type)){ #endif if (divisor != 0) { outb(iobase + com_cfcr, cfcr | CFCR_DLAB); outb(iobase + com_dlbl, divisor & 0xFF); outb(iobase + com_dlbh, (u_int) divisor >> 8); } outb(iobase + com_cfcr, com->cfcr_image = cfcr); #ifdef PC98 } else com_cflag_and_speed_set(com, cflag, t->c_ospeed); #endif if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; if (cflag & CRTS_IFLOW) com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */ else com->state &= ~CS_RTS_IFLOW; /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state |= CS_ODEVREADY; com->state &= ~CS_CTS_OFLOW; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; #ifdef PC98 if(IS_8251(com->pc98_if_type)){ if (!(pc98_get_modem_status(com) & TIOCM_CTS)) com->state &= ~CS_ODEVREADY; } else { #endif if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; #ifdef PC98 } #endif } /* XXX shouldn't call functions while intrs are disabled. */ disc_optim(tp, t, com); /* * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). */ if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); enable_intr(); splx(s); return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; int s; int unit; #ifdef PC98 int tmp; #endif unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); s = spltty(); disable_intr(); if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; if (tp->t_state & TS_TBLOCK) { #ifdef PC98 if(IS_8251(com->pc98_if_type)) tmp = com_tiocm_get(com) & TIOCM_RTS; else tmp = com->mcr_image & MCR_RTS; if (tmp && (com->state & CS_RTS_IFLOW)) #else if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) #endif #ifdef PC98 if(IS_8251(com->pc98_if_type)) com_tiocm_bic(com, TIOCM_RTS); else #endif outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } else { /* * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it * appropriately in comparam() if RTS-flow is being changed. * Check for races. */ #ifdef PC98 if(IS_8251(com->pc98_if_type)) tmp = com_tiocm_get(com) & TIOCM_RTS; else tmp = com->mcr_image & MCR_RTS; if (!(tmp) && com->iptr < com->ihighwater) #else if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater) #endif #ifdef PC98 if(IS_8251(com->pc98_if_type)) com_tiocm_bis(com, TIOCM_RTS); else #endif outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { #ifdef PC98 /* if(IS_8251(com->pc98_if_type)) com_int_Tx_enable(com); */ #endif splx(s); return; } if (tp->t_outq.c_cc != 0) { struct lbq *qp; struct lbq *next; if (!com->obufs[0].l_queued) { com->obufs[0].l_tail = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, sizeof com->obuf1); com->obufs[0].l_next = NULL; com->obufs[0].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[0]; } else { com->obufq.l_head = com->obufs[0].l_head; com->obufq.l_tail = com->obufs[0].l_tail; com->obufq.l_next = &com->obufs[0]; com->state |= CS_BUSY; } enable_intr(); } if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { com->obufs[1].l_tail = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, sizeof com->obuf2); com->obufs[1].l_next = NULL; com->obufs[1].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[1]; } else { com->obufq.l_head = com->obufs[1].l_head; com->obufq.l_tail = com->obufs[1].l_tail; com->obufq.l_next = &com->obufs[1]; com->state |= CS_BUSY; } enable_intr(); } tp->t_state |= TS_BUSY; } disable_intr(); if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); /* fake interrupt to start output */ enable_intr(); #ifdef PC98 /* if(IS_8251(com->pc98_if_type)) com_int_Tx_enable(com); */ #endif ttwwakeup(tp); splx(s); } static void siostop(tp, rw) struct tty *tp; int rw; { struct com_s *com; com = com_addr(DEV_TO_UNIT(tp->t_dev)); if (com->gone) return; disable_intr(); if (rw & FWRITE) { com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); com->tp->t_state &= ~TS_BUSY; } if (rw & FREAD) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } enable_intr(); comstart(tp); /* XXX should clear h/w fifos too. */ } static struct tty * siodevtotty(dev) dev_t dev; { int mynor; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO) return (NULL); return (&sio_tty[unit]); } static int commctl(com, bits, how) struct com_s *com; int bits; int how; { int mcr; int msr; if (how == DMGET) { bits = TIOCM_LE; /* XXX - always enabled while open */ mcr = com->mcr_image; if (mcr & MCR_DTR) bits |= TIOCM_DTR; if (mcr & MCR_RTS) bits |= TIOCM_RTS; msr = com->prev_modem_status; if (msr & MSR_CTS) bits |= TIOCM_CTS; if (msr & MSR_DCD) bits |= TIOCM_CD; if (msr & MSR_DSR) bits |= TIOCM_DSR; /* * XXX - MSR_RI is naturally volatile, and we make MSR_TERI * more volatile by reading the modem status a lot. Perhaps * we should latch both bits until the status is read here. */ if (msr & (MSR_RI | MSR_TERI)) bits |= TIOCM_RI; return (bits); } mcr = 0; if (bits & TIOCM_DTR) mcr |= MCR_DTR; if (bits & TIOCM_RTS) mcr |= MCR_RTS; if (com->gone) return(0); disable_intr(); switch (how) { case DMSET: outb(com->modem_ctl_port, com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); break; case DMBIS: outb(com->modem_ctl_port, com->mcr_image |= mcr); break; case DMBIC: outb(com->modem_ctl_port, com->mcr_image &= ~mcr); break; } enable_intr(); return (0); } static void siosettimeout() { struct com_s *com; bool_t someopen; int unit; /* * Set our timeout period to 1 second if no polled devices are open. * Otherwise set it to max(1/200, 1/hz). * Enable timeouts iff some device is open. */ untimeout(comwakeup, (void *)NULL); sio_timeout = hz; someopen = FALSE; for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && com->tp != NULL && com->tp->t_state & TS_ISOPEN && !com->gone) { someopen = TRUE; if (com->poll || com->poll_output) { sio_timeout = hz > 200 ? hz / 200 : 1; break; } } } if (someopen) { sio_timeouts_until_log = hz / sio_timeout; timeout(comwakeup, (void *)NULL, sio_timeout); } else { /* Flush error messages, if any. */ sio_timeouts_until_log = 1; comwakeup((void *)NULL); untimeout(comwakeup, (void *)NULL); } } static void comwakeup(chan) void *chan; { struct com_s *com; int unit; timeout(comwakeup, (void *)NULL, sio_timeout); /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && !com->gone && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); } } /* * Check for and log errors, but not too often. */ if (--sio_timeouts_until_log > 0) return; sio_timeouts_until_log = hz / sio_timeout; for (unit = 0; unit < NSIO; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; if (com->gone) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; disable_intr(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; enable_intr(); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } } } #ifdef PC98 /* commint is called when modem control line changes */ static void commint(dev_t dev) { register struct tty *tp; int stat,delta; struct com_s *com; int mynor,unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); tp = com->tp; stat = com_tiocm_get(com); delta = com_tiocm_get_delta(com); if (com->state & CS_CTS_OFLOW) { if (stat & TIOCM_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } if ((delta & TIOCM_CAR) && (mynor & CALLOUT_MASK) == 0) { if (stat & TIOCM_CAR ) (void)(*linesw[tp->t_line].l_modem)(tp, 1); else if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0) { /* negate DTR, RTS */ com_tiocm_bic(com, (tp->t_cflag & HUPCL) ? TIOCM_DTR|TIOCM_RTS|TIOCM_LE : TIOCM_LE ); /* disable IENABLE */ com_int_TxRx_disable( com ); } } } #endif static void disc_optim(tp, t, com) struct tty *tp; struct termios *t; struct com_s *com; { if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; /* * Prepare to reduce input latency for packet * discplines with a end of packet character. */ if (tp->t_line == SLIPDISC) com->hotchar = 0xc0; else if (tp->t_line == PPPDISC) com->hotchar = 0x7e; else com->hotchar = 0; } /* * Following are all routines needed for SIO to act as console */ #include 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); /* * We don't want interrupts, but must be careful not to "disable" * them by clearing the MCR_IENABLE bit, since that might cause * an interrupt by floating the IRQ line. */ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void siocnclose(sp) struct siocnstate *sp; { Port_t iobase; /* * Restore the device control registers. */ siocntxwait(); iobase = siocniobase; outb(iobase + com_cfcr, CFCR_DLAB); outb(iobase + com_dlbl, sp->dlbl); outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); } void siocnprobe(cp) struct consdev *cp; { int unit; /* XXX: ick */ unit = DEV_TO_UNIT(CONUNIT); siocniobase = CONADDR; /* make sure hardware exists? XXX */ /* initialize required fields */ cp->cn_dev = makedev(CDEV_MAJOR, unit); #ifdef COMCONSOLE cp->cn_pri = CN_REMOTE; /* Force a serial port console */ #else cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL; #endif } void siocninit(cp) struct consdev *cp; { comconsole = DEV_TO_UNIT(cp->cn_dev); } int siocncheckc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); if (inb(iobase + com_lsr) & LSR_RXRDY) c = inb(iobase + com_data); else c = 0; siocnclose(&sp); splx(s); return (c); } int siocngetc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp); splx(s); return (c); } void siocnputc(dev, c) dev_t dev; int c; { int s; struct siocnstate sp; s = spltty(); siocnopen(&sp); siocntxwait(); outb(siocniobase + com_data, c); siocnclose(&sp); splx(s); } #ifdef DSI_SOFT_MODEM /* * The magic code to download microcode to a "Connection 14.4+Fax" * modem from Digicom Systems Inc. Very magic. */ #define DSI_ERROR(str) { ptr = str; goto error; } static int LoadSoftModem(int unit, int base_io, u_long size, u_char *ptr) { int int_c,int_k; int data_0188, data_0187; /* * First see if it is a DSI SoftModem */ if(!((inb(base_io+7) ^ inb(base_io+7) & 0x80))) return ENODEV; data_0188 = inb(base_io+4); data_0187 = inb(base_io+3); outb(base_io+3,0x80); outb(base_io+4,0x0C); outb(base_io+0,0x31); outb(base_io+1,0x8C); outb(base_io+7,0x10); outb(base_io+7,0x19); if(0x18 != (inb(base_io+7) & 0x1A)) DSI_ERROR("dsp bus not granted"); if(0x01 != (inb(base_io+7) & 0x01)) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x01 != (inb(base_io+7) & 0x01)) DSI_ERROR("program mem not granted"); } int_c = 0; while(1) { if(int_c >= 7 || size <= 0x1800) break; for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; int_c++; } if(size > 0x1800) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; while(size > 0x1800) { for(int_k = 0 ; int_k < 0xC00; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; } if(size < 0x1800) { for(int_k=0;int_k 0) { if(int_c == 7) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } else { for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } } outb(base_io+7,0x11); outb(base_io+7,3); outb(base_io+4,data_0188 & 0xfb); outb(base_io+3,data_0187); return 0; error: printf("sio%d: DSI SoftModem microcode load failed: <%s>\n",unit,ptr); outb(base_io+7,0x00); \ outb(base_io+3,data_0187); \ outb(base_io+4,data_0188); \ return EIO; } #endif /* DSI_SOFT_MODEM */ #ifdef PC98 /* * pc98 local function */ static void com_tiocm_set(struct com_s *com, int msr) { int s; int tmp = 0; int mask = CMD8251_TxEN|CMD8251_RxEN|CMD8251_DTR|CMD8251_RTS; s=spltty(); com->pc98_prev_modem_status = ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ) | ( com->pc98_prev_modem_status & ~(TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); tmp |= (CMD8251_TxEN|CMD8251_RxEN); if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; pc98_i8251_clear_or_cmd( com, mask, tmp ); splx(s); } static void com_tiocm_bis(struct com_s *com, int msr) { int s; int tmp = 0; s=spltty(); com->pc98_prev_modem_status |= ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); tmp |= CMD8251_TxEN|CMD8251_RxEN; if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; pc98_i8251_or_cmd( com, tmp ); splx(s); } static void com_tiocm_bic(struct com_s *com, int msr) { int s; int tmp = msr; s=spltty(); com->pc98_prev_modem_status &= ~( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; pc98_i8251_clear_cmd( com, tmp ); splx(s); } static int com_tiocm_get(struct com_s *com) { return( com->pc98_prev_modem_status ); } static int com_tiocm_get_delta(struct com_s *com) { int tmp; tmp = com->pc98_modem_delta; com->pc98_modem_delta = 0; return( tmp ); } /* convert to TIOCM_?? ( ioctl.h ) */ static int pc98_get_modem_status(struct com_s *com) { int stat, stat2; register int msr; stat = inb(com->sts_port); stat2 = inb(com->in_modem_port); msr = com->pc98_prev_modem_status & ~(TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS); if ( !(stat2 & CICSCD_CD) ) msr |= TIOCM_CAR; if ( !(stat2 & CICSCD_CI) ) msr |= TIOCM_RI; if ( stat & STS8251_DSR ) msr |= TIOCM_DSR; if ( !(stat2 & CICSCD_CS) ) msr |= TIOCM_CTS; #if COM_CARRIER_DETECT_EMULATE if ( msr & (TIOCM_DSR|TIOCM_CTS) ) { msr |= TIOCM_CAR; } #endif return(msr); } static void pc98_check_msr(void* chan) { int msr, delta; int s; register struct tty *tp; struct com_s *com; int mynor; int unit; dev_t dev; dev=(dev_t)chan; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); tp = com->tp; s = spltty(); msr = pc98_get_modem_status(com); /* make change flag */ delta = msr ^ com->pc98_prev_modem_status; if ( delta & TIOCM_CAR ) { if ( com->modem_car_chg_timer ) { if ( -- com->modem_car_chg_timer ) msr ^= TIOCM_CAR; } else { if ( com->modem_car_chg_timer = ( msr & TIOCM_CAR ) ? DCD_ON_RECOGNITION : DCD_OFF_TOLERANCE ) msr ^= TIOCM_CAR; } } else com->modem_car_chg_timer = 0; delta = ( msr ^ com->pc98_prev_modem_status ) & (TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS); com->pc98_prev_modem_status = msr; delta = ( com->pc98_modem_delta |= delta ); splx(s); if ( com->modem_checking || (tp->t_state & (TS_ISOPEN)) ) { if ( delta ) { commint(dev); } timeout(pc98_check_msr, (caddr_t)dev, PC98_CHECK_MODEM_INTERVAL); } else { com->modem_checking = 0; } } static void pc98_msrint_start(dev_t dev) { struct com_s *com; int mynor; int unit; int s = spltty(); mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); /* modem control line check routine envoke interval is 1/10 sec */ if ( com->modem_checking == 0 ) { com->pc98_prev_modem_status = pc98_get_modem_status(com); com->pc98_modem_delta = 0; timeout(pc98_check_msr, (caddr_t)dev, PC98_CHECK_MODEM_INTERVAL); com->modem_checking = 1; } splx(s); } static void pc98_disable_i8251_interrupt(struct com_s *com, int mod) { /* disable interrupt */ register int tmp; mod |= ~(IEN_Tx|IEN_TxEMP|IEN_Rx); COM_INT_DISABLE tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx); outb( com->intr_ctrl_port, (com->intr_enable&=~mod) | tmp ); COM_INT_ENABLE } static void pc98_enable_i8251_interrupt(struct com_s *com, int mod) { register int tmp; COM_INT_DISABLE tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx); outb( com->intr_ctrl_port, (com->intr_enable|=mod) | tmp ); COM_INT_ENABLE } static int pc98_check_i8251_interrupt(struct com_s *com) { return ( com->intr_enable & 0x07 ); } static void pc98_i8251_clear_cmd(struct com_s *com, int x) { int tmp; COM_INT_DISABLE tmp = com->pc98_prev_siocmd & ~(x); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); COM_INT_ENABLE } static void pc98_i8251_or_cmd(struct com_s *com, int x) { int tmp; COM_INT_DISABLE tmp = com->pc98_prev_siocmd | (x); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); COM_INT_ENABLE } static void pc98_i8251_set_cmd(struct com_s *com, int x) { int tmp; COM_INT_DISABLE tmp = (x); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); COM_INT_ENABLE } static void pc98_i8251_clear_or_cmd(struct com_s *com, int clr, int x) { int tmp; COM_INT_DISABLE tmp = com->pc98_prev_siocmd & ~(clr); tmp |= (x); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); COM_INT_ENABLE } static int pc98_i8251_get_cmd(struct com_s *com) { return com->pc98_prev_siocmd; } static int pc98_i8251_get_mod(struct com_s *com) { return com->pc98_prev_siomod; } static void pc98_i8251_reset(struct com_s *com, int mode, int command) { outb(com->cmd_port, 0); /* dummy */ DELAY(2); outb(com->cmd_port, 0); /* dummy */ DELAY(2); outb(com->cmd_port, 0); /* dummy */ DELAY(2); outb(com->cmd_port, CMD8251_RESET); /* internal reset */ DELAY(2); outb(com->cmd_port, mode ); /* mode register */ com->pc98_prev_siomod = mode; DELAY(2); pc98_i8251_set_cmd( com, (command|CMD8251_ER) ); } static void pc98_check_sysclock(void) { /* get system clock from port */ if ( pc98_machine_type & M_8M ) { /* 8 MHz system & H98 */ sysclock = 8; } else { /* 5 MHz system */ sysclock = 5; } } static void com_cflag_and_speed_set( struct com_s *com, int cflag, int speed) { int cfcr=0, count; int previnterrupt; count = pc98_ttspeedtab( com, speed ); if ( count < 0 ) return; previnterrupt = pc98_check_i8251_interrupt(com); pc98_disable_i8251_interrupt( com, IEN_Tx|IEN_TxEMP|IEN_Rx ); switch ( cflag&CSIZE ) { case CS5: cfcr = MOD8251_5BITS; break; case CS6: cfcr = MOD8251_6BITS; break; case CS7: cfcr = MOD8251_7BITS; break; case CS8: cfcr = MOD8251_8BITS; break; } if ( cflag&PARENB ) { if ( cflag&PARODD ) cfcr |= MOD8251_PODD; else cfcr |= MOD8251_PEVEN; } else cfcr |= MOD8251_PDISAB; if ( cflag&CSTOPB ) cfcr |= MOD8251_STOP2; else cfcr |= MOD8251_STOP1; if ( count & 0x10000 ) cfcr |= MOD8251_CLKX1; else cfcr |= MOD8251_CLKX16; if (epson_machine_id != 0x20) { /* XXX */ { int tmp; while (!((tmp = inb(com->sts_port)) & STS8251_TxEMP)) ; } } /* set baud rate from ospeed */ pc98_set_baud_rate( com, count ); if ( cfcr != pc98_i8251_get_mod(com) ) pc98_i8251_reset(com, cfcr, pc98_i8251_get_cmd(com) ); pc98_enable_i8251_interrupt( com, previnterrupt ); } static int pc98_ttspeedtab(struct com_s *com, int speed) { int effect_sp, count=-1, mod; switch ( com->pc98_if_type ) { case COM_IF_INTERNAL: /* for *1CLK asynchronous! mode , TEFUTEFU */ effect_sp = ttspeedtab( speed, pc98speedtab ); if ( effect_sp < 0 ) effect_sp = ttspeedtab( (speed-1), pc98speedtab ); if ( effect_sp <= 0 ) return effect_sp; mod = (sysclock == 5 ? 2457600 : 1996800); if ( effect_sp == speed ) mod /= 16; count = mod / effect_sp; if ( count > 65535 ) return(-1); if ( effect_sp >= 2400 ) if ( !(sysclock != 5 && (effect_sp == 19200 || effect_sp == 38400)) ) if ( ( mod % effect_sp ) != 0 ) return(-1); if ( effect_sp != speed ) count |= 0x10000; break; #ifdef COM_IF_PC9861K case COM_IF_PC9861K: effect_sp = speed; count = 1; break; #endif #ifdef COM_IF_PIO9032B case COM_IF_PIO9032B: if ( speed == 0 ) return 0; count = ttspeedtab( speed, comspeedtab_pio9032b ); if ( count < 0 ) return count; effect_sp = speed; break; #endif #ifdef COM_IF_B98_01 case COM_IF_B98_01: effect_sp=speed; count = ttspeedtab( speed, comspeedtab_b98_01 ); if ( count <= 3 ) return -1; /* invalid speed/count */ if ( count <= 5 ) count |= 0x10000; /* x1 mode for 76800 and 153600 */ else count -= 4; /* x16 mode for slower */ break; #endif } return count; } static void pc98_set_baud_rate( struct com_s *com, int count) { int s; switch ( com->pc98_if_type ) { case COM_IF_INTERNAL: if ( count < 0 ) { printf( "[ Illegal count : %d ]", count ); return; } else if ( count == 0) return; /* set i8253 */ s = splclock(); outb( 0x77, 0xb6 ); outb( 0x5f, 0); outb( 0x75, count & 0xff ); outb( 0x5f, 0); outb( 0x75, (count >> 8) & 0xff ); splx(s); break; #if 0 #ifdef COM_IF_PC9861K case COM_IF_PC9861K: break; /* ext. RS232C board: speed is determined by DIP switch */ #endif #endif /* 0 */ #ifdef COM_IF_PIO9032B case COM_IF_PIO9032B: outb( com_addr[unit], count & 0x07 ); break; #endif #ifdef COM_IF_B98_01 case COM_IF_B98_01: outb( com->iobase, count & 0x0f ); #ifdef B98_01_OLD /* some old board should be controlled in different way, but this hasn't been tested yet.*/ outb( com->iobase+2, ( count & 0x10000 ) ? 0xf0 : 0xf2 ); #endif break; #endif } } static int pc98_check_if_type( int iobase, struct siodev *iod) { int irr = 0, tmp = 0; int ret = 0; static short irq_tab[2][8] = { { 3, 5, 6, 9, 10, 12, 13, -1}, { 3, 10, 12, 13, 5, 6, 9, -1} }; iod->irq = 0; switch ( iobase & 0xff ) { case IO_COM1: iod->if_type = COM_IF_INTERNAL; ret = 0; iod->irq = 4; break; #ifdef COM_IF_PC9861K case IO_COM2: iod->if_type = COM_IF_PC9861K; ret = 1; irr = 0; tmp = 3; break; case IO_COM3: iod->if_type = COM_IF_PC9861K; ret = 2; irr = 1; tmp = 3; break; #endif #ifdef COM_IF_PIO9032B case IO_COM_PIO9032B_2: iod->if_type = COM_IF_PIO9032B; ret = 1; irr = 0; tmp = 7; break; case IO_COM_PIO9032B_3: iod->if_type = COM_IF_PIO9032B; ret = 2; irr = 1; tmp = 7; break; #endif #ifdef COM_IF_B98_01 case IO_COM_B98_01_2: iod->if_type = COM_IF_B98_01; ret = 1; irr = 0; tmp = 7; outb(iobase + 2, 0xf2); outb(iobase, 4); break; case IO_COM_B98_01_3: iod->if_type = COM_IF_B98_01; ret = 2; irr = 1; tmp = 7; outb(iobase + 2, 0xf2); outb(iobase , 4); break; #endif default: if((iobase & 0x0f0) == 0xd0){ iod->if_type = MC16550; return 0; } return -1; } iod->cmd = ( iobase & 0xff00 )|PC98SIO_cmd_port(ret); iod->sts = ( iobase & 0xff00 )|PC98SIO_sts_port(ret); iod->mod = ( iobase & 0xff00 )|PC98SIO_in_modem_port(ret); iod->ctrl = ( iobase & 0xff00 )|PC98SIO_intr_ctrl_port(ret); if ( iod->irq == 0 ) { tmp &= inb( iod->mod ); iod->irq = irq_tab[irr][tmp]; if ( iod->irq == -1 ) return -1; } return 0; } static int pc98_set_ioport( struct com_s *com, int io_base ) { int a, io, type; switch ( io_base & 0xff ) { case IO_COM1: a = 0; io = 0; type = COM_IF_INTERNAL; pc98_check_sysclock(); break; #ifdef COM_IF_PC9861K case IO_COM2: a = 1; io = 0; type = COM_IF_PC9861K; break; case IO_COM3: a = 2; io = 0; type = COM_IF_PC9861K; break; #endif /* COM_IF_PC9861K */ #ifdef COM_IF_PIO9032B /* PIO9032B : I/O address is changeable */ case IO_COM_PIO9032B_2: a = 1; io = io_base & 0xff00; type = COM_IF_PIO9032B; break; case IO_COM_PIO9032B_3: a = 2; io = io_base & 0xff00; type = COM_IF_PIO9032B; break; #endif /* COM_IF_PIO9032B */ #ifdef COM_IF_B98_01 case IO_COM_B98_01_2: a = 1; io = 0; type = COM_IF_B98_01; break; case IO_COM_B98_01_3: a = 2; io = 0; type = COM_IF_B98_01; break; #endif /* COM_IF_B98_01*/ default: /* i/o address not match */ return -1; } com->pc98_if_type = type; com->data_port = io | PC98SIO_data_port(a); com->cmd_port = io | PC98SIO_cmd_port(a); com->sts_port = io | PC98SIO_sts_port(a); com->in_modem_port = io | PC98SIO_in_modem_port(a); com->intr_ctrl_port = io | PC98SIO_intr_ctrl_port(a); return 0; } #endif /* PC98 defined */ Index: head/sys/pc98/conf/GENERIC =================================================================== --- head/sys/pc98/conf/GENERIC (revision 18264) +++ head/sys/pc98/conf/GENERIC (revision 18265) @@ -1,200 +1,202 @@ # # GENERIC -- Generic machine with WD/AHx/NCR/BTx family disks # # For more information read the handbook part System Administration -> # Configuring the FreeBSD Kernel -> The Configuration File. # The handbook is available in /usr/share/doc/handbook or online as # latest version from the FreeBSD World Wide Web server # # # An exhaustive list of options and more detailed explanations of the # device lines is present in the ./LINT configuration file. If you are # in doubt as to the purpose or necessity of a line, check first in LINT. # -# $Id: GENERIC98,v 1.3 1996/08/31 15:06:28 asami Exp $ +# $Id: GENERIC98,v 1.4 1996/09/07 02:13:23 asami Exp $ # GENERIC98 -- Generic PC98 machine with WD/SBIC55 disks machine "pc98" cpu "I386_CPU" cpu "I486_CPU" cpu "I586_CPU" cpu "I686_CPU" ident "GENERIC98" maxusers 10 options "PC98" #PC98 options MATH_EMULATE #Support for x87 emulation #options GPL_MATH_EMULATE #GPL-licensed emulator options INET #InterNETworking options FFS #Berkeley Fast Filesystem options NFS #Network Filesystem options MSDOSFS #MSDOS Filesystem options "CD9660" #ISO 9660 Filesystem options PROCFS #Process filesystem options "COMPAT_43" #Compatible with BSD 4.3 [KEEP THIS!] options SYSVSHM options SYSVSEM options SYSVMSG options UCONSOLE #Allow users to grab the console options FAILSAFE #Be conservative +options USERCONFIG #boot -c editor +options VISUAL_USERCONFIG #visual boot -c editor options "MAXCONS=4" #4 virtual consoles options BOUNCE_BUFFERS #include support for DMA bounce buffers options EPSON_BOUNCEDMA #use bounce buufer for 15-16M #options EPSON_MEMWIN #EPSON memory window support options "LINE30" options AUTO_CLOCK options COM_MULTIPORT # # non-Intel CPU support # #options "IBM_486SLC" # IBM486SLC/SLC2 support #options "CYRIX_486DLC" # Cyrix 486DLC/SLC/DLC2/SLC2 support #option "CYRIX_5X86" # Cyrix 5x86 support #options SUSP_HLT # CPU enters suspend mode when HALT #options "DISABLE_5X86_LSSER" # Load-Store reordering enable # # sbic55.c.new # #options SCSI_SYNC # synchronous transfer mode #options FORCE_BUSMASTER #options "HA55BS_ID=0" # # IBM-PC HDD support #options COMPAT_ATDISK # # FreeBSD(98)-current is a *TEST VERSION*. # It is highly recomended to compile with following options, and to # record the panic messages and the result of trace command brefore # reporting a problem. options DDB options DIAGNOSTIC config kernel root on wd0 controller isa0 controller pci0 controller fdc0 at isa? port "IO_FD1" bio irq 11 drq 2 vector fdintr disk fd0 at fdc0 drive 0 disk fd1 at fdc0 drive 1 disk fd2 at fdc0 drive 2 disk fd3 at fdc0 drive 3 tape ft0 at fdc0 drive 4 controller wdc0 at isa? port "IO_WD1" bio irq 9 vector wdintr disk wd0 at wdc0 drive 0 #disk wd1 at wdc0 drive 1 #disk wd2 at wdc0 drive 2 #disk wd3 at wdc0 drive 3 options ATAPI # Enable ATAPI support for IDE bus options ATAPI_STATIC #Don't do it as an LKM device wcd #IDE CD-ROM # A single entry for any of these controllers (ncr, ahb, ahc) is sufficient # for any number of installed devices. controller ncr0 controller ahc0 controller sbic0 at isa? port "IO_SCSI" bio irq 5 drq 3 vector sbicintr #controller sbic0 at isa? port "IO_SCSI" bio irq 5 drq 3 flags 0xff vector sbicintr controller aic0 at isa? port 0x1840 bio irq 5 vector aicintr controller scbus0 device sd0 device st0 device cd0 #Only need one of these, the code dynamically grows device od0 controller matcd0 at isa? port? bio # syscons is the default console driver, resembling an SCO console device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr #options XSERVER # include code for XFree86 # Mandatory, don't remove device npx0 at isa? port "IO_NPX" irq 8 vector npxintr # # Laptop support (see LINT for more options) # device apm0 at isa? disable # Advanced Power Management options APM_BROKEN_STATCLOCK # Workaround some buggy APM BIOS # PCCARD (PCMCIA) support #controller crd0 #device pcic0 at crd? #device pcic1 at crd? device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr device sio1 at isa? port 0xd2 tty irq 5 flags 0x101 vector siointr device sio2 at isa? port 0x8d2 tty flags 0x101 vector siointr device lpt0 at isa? port "IO_LPT" tty device mse0 at isa? port "IO_MSE" tty irq 13 vector mseintr # Order is important here due to intrusive probes, do *not* alphabetize # this list of network interfaces until the probes have been fixed. # Right now it appears that the ie0 must be probed before ep0. See # revision 1.20 of this file. device de0 device fxp0 device vx0 # # DP8390 NIC # # ed0: generic driver # ed1: LANEED LD-BDN # ed2: EGY-98 # ed3: LGY-98 # ed4: ICM-IF-2766/EN-2298-T # ed5: SIC-98 # ed6: PC-9801-108 # ed7: LA-98 # device ed0 at isa? port 0x00d0 net irq 6 vector edintr device ed1 at isa? port 0x00d8 net irq 6 flags 0x20000 vector edintr device ed2 at isa? port 0x00d8 net irq 6 flags 0x30000 vector edintr device ed3 at isa? port 0x00d8 net irq 6 flags 0x40000 vector edintr device ed4 at isa? port 0x56d0 net irq 5 flags 0x50000 vector edintr device ed5 at isa? port 0x00d0 net irq 6 iomem 0xd0000 iosiz 16384 flags 0x60000 vector edintr device ed6 at isa? port 0x00d0 net irq 6 flags 0x80000 vector edintr device ed7 at isa? port 0x00d0 net irq 6 flags 0x90000 vector edintr device fe0 at isa? port 0x00d0 net irq 3 vector feintr device zp0 at isa? port 0x0300 net irq 10 iomem 0xe0000 vector zpintr device ep0 at isa? port 0x00d0 net irq 6 vector epintr #controller snd0 #device sb0 at isa? port 0x20d2 irq 10 conflicts drq 3 vector sbintr #device sbxvi0 at isa? drq 3 #device sbmidi0 at isa? port 0x80d2 #device opl0 at isa? port 0x28d2 #device pcm0 at isa? port 0xa460 irq 12 vector pcmintr #device mss0 at isa? port 0xf40 irq12 drq 1 vectro adintr pseudo-device loop pseudo-device ether pseudo-device log pseudo-device sl 2 # ijppp uses tun instead of ppp device #pseudo-device ppp 1 pseudo-device tun 1 pseudo-device pty 16 pseudo-device gzip # Exec gzipped a.out's # KTRACE enables the system-call tracing facility ktrace(2). # This adds 4 KB bloat to your kernel, and slightly increases # the costs of each syscall. options KTRACE #kernel tracing Index: head/sys/pc98/conf/GENERIC98 =================================================================== --- head/sys/pc98/conf/GENERIC98 (revision 18264) +++ head/sys/pc98/conf/GENERIC98 (revision 18265) @@ -1,200 +1,202 @@ # # GENERIC -- Generic machine with WD/AHx/NCR/BTx family disks # # For more information read the handbook part System Administration -> # Configuring the FreeBSD Kernel -> The Configuration File. # The handbook is available in /usr/share/doc/handbook or online as # latest version from the FreeBSD World Wide Web server # # # An exhaustive list of options and more detailed explanations of the # device lines is present in the ./LINT configuration file. If you are # in doubt as to the purpose or necessity of a line, check first in LINT. # -# $Id: GENERIC98,v 1.3 1996/08/31 15:06:28 asami Exp $ +# $Id: GENERIC98,v 1.4 1996/09/07 02:13:23 asami Exp $ # GENERIC98 -- Generic PC98 machine with WD/SBIC55 disks machine "pc98" cpu "I386_CPU" cpu "I486_CPU" cpu "I586_CPU" cpu "I686_CPU" ident "GENERIC98" maxusers 10 options "PC98" #PC98 options MATH_EMULATE #Support for x87 emulation #options GPL_MATH_EMULATE #GPL-licensed emulator options INET #InterNETworking options FFS #Berkeley Fast Filesystem options NFS #Network Filesystem options MSDOSFS #MSDOS Filesystem options "CD9660" #ISO 9660 Filesystem options PROCFS #Process filesystem options "COMPAT_43" #Compatible with BSD 4.3 [KEEP THIS!] options SYSVSHM options SYSVSEM options SYSVMSG options UCONSOLE #Allow users to grab the console options FAILSAFE #Be conservative +options USERCONFIG #boot -c editor +options VISUAL_USERCONFIG #visual boot -c editor options "MAXCONS=4" #4 virtual consoles options BOUNCE_BUFFERS #include support for DMA bounce buffers options EPSON_BOUNCEDMA #use bounce buufer for 15-16M #options EPSON_MEMWIN #EPSON memory window support options "LINE30" options AUTO_CLOCK options COM_MULTIPORT # # non-Intel CPU support # #options "IBM_486SLC" # IBM486SLC/SLC2 support #options "CYRIX_486DLC" # Cyrix 486DLC/SLC/DLC2/SLC2 support #option "CYRIX_5X86" # Cyrix 5x86 support #options SUSP_HLT # CPU enters suspend mode when HALT #options "DISABLE_5X86_LSSER" # Load-Store reordering enable # # sbic55.c.new # #options SCSI_SYNC # synchronous transfer mode #options FORCE_BUSMASTER #options "HA55BS_ID=0" # # IBM-PC HDD support #options COMPAT_ATDISK # # FreeBSD(98)-current is a *TEST VERSION*. # It is highly recomended to compile with following options, and to # record the panic messages and the result of trace command brefore # reporting a problem. options DDB options DIAGNOSTIC config kernel root on wd0 controller isa0 controller pci0 controller fdc0 at isa? port "IO_FD1" bio irq 11 drq 2 vector fdintr disk fd0 at fdc0 drive 0 disk fd1 at fdc0 drive 1 disk fd2 at fdc0 drive 2 disk fd3 at fdc0 drive 3 tape ft0 at fdc0 drive 4 controller wdc0 at isa? port "IO_WD1" bio irq 9 vector wdintr disk wd0 at wdc0 drive 0 #disk wd1 at wdc0 drive 1 #disk wd2 at wdc0 drive 2 #disk wd3 at wdc0 drive 3 options ATAPI # Enable ATAPI support for IDE bus options ATAPI_STATIC #Don't do it as an LKM device wcd #IDE CD-ROM # A single entry for any of these controllers (ncr, ahb, ahc) is sufficient # for any number of installed devices. controller ncr0 controller ahc0 controller sbic0 at isa? port "IO_SCSI" bio irq 5 drq 3 vector sbicintr #controller sbic0 at isa? port "IO_SCSI" bio irq 5 drq 3 flags 0xff vector sbicintr controller aic0 at isa? port 0x1840 bio irq 5 vector aicintr controller scbus0 device sd0 device st0 device cd0 #Only need one of these, the code dynamically grows device od0 controller matcd0 at isa? port? bio # syscons is the default console driver, resembling an SCO console device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr #options XSERVER # include code for XFree86 # Mandatory, don't remove device npx0 at isa? port "IO_NPX" irq 8 vector npxintr # # Laptop support (see LINT for more options) # device apm0 at isa? disable # Advanced Power Management options APM_BROKEN_STATCLOCK # Workaround some buggy APM BIOS # PCCARD (PCMCIA) support #controller crd0 #device pcic0 at crd? #device pcic1 at crd? device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr device sio1 at isa? port 0xd2 tty irq 5 flags 0x101 vector siointr device sio2 at isa? port 0x8d2 tty flags 0x101 vector siointr device lpt0 at isa? port "IO_LPT" tty device mse0 at isa? port "IO_MSE" tty irq 13 vector mseintr # Order is important here due to intrusive probes, do *not* alphabetize # this list of network interfaces until the probes have been fixed. # Right now it appears that the ie0 must be probed before ep0. See # revision 1.20 of this file. device de0 device fxp0 device vx0 # # DP8390 NIC # # ed0: generic driver # ed1: LANEED LD-BDN # ed2: EGY-98 # ed3: LGY-98 # ed4: ICM-IF-2766/EN-2298-T # ed5: SIC-98 # ed6: PC-9801-108 # ed7: LA-98 # device ed0 at isa? port 0x00d0 net irq 6 vector edintr device ed1 at isa? port 0x00d8 net irq 6 flags 0x20000 vector edintr device ed2 at isa? port 0x00d8 net irq 6 flags 0x30000 vector edintr device ed3 at isa? port 0x00d8 net irq 6 flags 0x40000 vector edintr device ed4 at isa? port 0x56d0 net irq 5 flags 0x50000 vector edintr device ed5 at isa? port 0x00d0 net irq 6 iomem 0xd0000 iosiz 16384 flags 0x60000 vector edintr device ed6 at isa? port 0x00d0 net irq 6 flags 0x80000 vector edintr device ed7 at isa? port 0x00d0 net irq 6 flags 0x90000 vector edintr device fe0 at isa? port 0x00d0 net irq 3 vector feintr device zp0 at isa? port 0x0300 net irq 10 iomem 0xe0000 vector zpintr device ep0 at isa? port 0x00d0 net irq 6 vector epintr #controller snd0 #device sb0 at isa? port 0x20d2 irq 10 conflicts drq 3 vector sbintr #device sbxvi0 at isa? drq 3 #device sbmidi0 at isa? port 0x80d2 #device opl0 at isa? port 0x28d2 #device pcm0 at isa? port 0xa460 irq 12 vector pcmintr #device mss0 at isa? port 0xf40 irq12 drq 1 vectro adintr pseudo-device loop pseudo-device ether pseudo-device log pseudo-device sl 2 # ijppp uses tun instead of ppp device #pseudo-device ppp 1 pseudo-device tun 1 pseudo-device pty 16 pseudo-device gzip # Exec gzipped a.out's # KTRACE enables the system-call tracing facility ktrace(2). # This adds 4 KB bloat to your kernel, and slightly increases # the costs of each syscall. options KTRACE #kernel tracing Index: head/sys/pc98/conf/files.pc98 =================================================================== --- head/sys/pc98/conf/files.pc98 (revision 18264) +++ head/sys/pc98/conf/files.pc98 (revision 18265) @@ -1,280 +1,281 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # -# modified for PC-9801 after: -# $Id: files.pc98,v 1.5 1996/09/04 09:52:08 asami Exp $ +# modified for PC-9801 # +# $Id: files.i386,v 1.140 1996/09/11 19:53:30 phk Exp $ +# aic7xxx_asm optional ahc device-driver \ dependency "$S/dev/aic7xxx/aic7xxx_asm.c" \ compile-with "${CC} -Wall -o $@ $>" \ no-obj no-implicit-rule \ clean "aic7xxx_asm" # aic7xxx_seq.h optional ahc device-driver \ compile-with "./aic7xxx_asm -o $@ $S/dev/aic7xxx/aic7xxx.seq" \ no-obj no-implicit-rule before-depend \ clean "aic7xxx_seq.h" \ dependency "$S/dev/aic7xxx/aic7xxx_reg.h $S/dev/aic7xxx/aic7xxx.seq aic7xxx_asm" # linux_genassym optional compat_linux \ dependency "$S/i386/linux/linux_genassym.c $S/i386/linux/linux.h" \ compile-with "${CC} ${CFLAGS} -o $@ $<" \ no-obj no-implicit-rule \ clean "linux_genassym" # linux_assym.h optional compat_linux \ dependency "linux_genassym" \ compile-with "./linux_genassym > $@" \ no-obj no-implicit-rule before-depend \ clean "linux_assym.h" # i386/scsi/93cx6.c optional ahc device-driver i386/apm/apm.c optional apm device-driver i386/apm/apm_setup.s optional apm #i386/eisa/3c5x9.c optional ep device-driver #i386/eisa/aic7770.c optional ahc device-driver #i386/eisa/aha1742.c optional ahb device-driver #i386/eisa/bt74x.c optional bt device-driver i386/eisa/eisaconf.c optional eisa i386/i386/autoconf.c standard device-driver i386/i386/cons.c standard i386/i386/db_disasm.c optional ddb i386/i386/db_interface.c optional ddb i386/i386/db_trace.c optional ddb i386/i386/i386-gdbstub.c optional ddb pc98/i386/exception.s standard i386/i386/identcpu.c standard i386/i386/in_cksum.c optional inet # locore.s needs to be handled in Makefile to put it first. Otherwise it's # now normal. # i386/i386/locore.s standard pc98/i386/machdep.c standard pc98/pc98/pc98_machdep.c standard i386/i386/math_emulate.c optional math_emulate i386/i386/mem.c standard pc98/i386/microtime.s standard i386/i386/perfmon.c optional perfmon pc98/i386/pmap.c standard i386/i386/procfs_machdep.c standard i386/i386/support.s standard i386/i386/swtch.s standard i386/i386/sys_machdep.c standard pc98/i386/trap.c standard -pc98/i386/userconfig.c standard +pc98/i386/userconfig.c optional userconfig pc98/i386/vm_machdep.c standard i386/ibcs2/ibcs2_fcntl.c optional ibcs2 i386/ibcs2/ibcs2_stat.c optional ibcs2 i386/ibcs2/ibcs2_ipc.c optional ibcs2 i386/ibcs2/ibcs2_msg.c optional ibcs2 i386/ibcs2/ibcs2_misc.c optional ibcs2 i386/ibcs2/ibcs2_other.c optional ibcs2 i386/ibcs2/ibcs2_signal.c optional ibcs2 i386/ibcs2/ibcs2_ioctl.c optional ibcs2 i386/ibcs2/ibcs2_socksys.c optional ibcs2 i386/ibcs2/ibcs2_sysi86.c optional ibcs2 i386/ibcs2/ibcs2_util.c optional ibcs2 i386/ibcs2/ibcs2_isc.c optional ibcs2 i386/ibcs2/ibcs2_isc_sysent.c optional ibcs2 i386/ibcs2/ibcs2_xenix.c optional ibcs2 i386/ibcs2/ibcs2_xenix_sysent.c optional ibcs2 i386/ibcs2/ibcs2_errno.c optional ibcs2 i386/ibcs2/ibcs2_sysent.c optional ibcs2 i386/ibcs2/ibcs2_sysvec.c optional ibcs2 i386/ibcs2/imgact_coff.c optional ibcs2 pc98/pc98/bs/bs.c optional bs device-driver pc98/pc98/bs/bsfunc.c optional bs device-driver pc98/pc98/bs/bshw.c optional bs device-driver pc98/pc98/bs/bsif.c optional bs device-driver pc98/pc98/sbic55.c optional sbic device-driver #i386/pc98/aha1542.c optional aha device-driver pc98/pc98/aic6360.c optional aic device-driver pc98/pc98/b004.c optional bqu device-driver i386/pc98/bt742a.c optional bt device-driver i386/pc98/bt5xx-445.c optional bt device-driver pc98/pc98/clock.c standard pc98/isa/cronyx.c optional cx device-driver pc98/isa/ctx.c optional ctx device-driver pc98/isa/cx.c optional cx device-driver pc98/isa/cy.c optional cy device-driver pc98/pc98/diskslice_machdep.c standard pc98/pc98/atcompat_diskslice.c optional compat_atdisk i386/isa/elink.c optional ep device-driver #i386/isa/elink.c optional ie device-driver pc98/pc98/fd.c optional fd device-driver pc98/pc98/ft.c optional ft device-driver pc98/pc98/gpib.c optional gp device-driver pc98/isa/asc.c optional asc device-driver pc98/isa/gsc.c optional gsc device-driver pc98/isa/if_ar.c optional ar device-driver pc98/isa/if_cx.c optional cx device-driver pc98/pc98/if_ed.c optional ed device-driver pc98/pc98/if_el.c optional el device-driver i386/isa/if_ep.c optional ep device-driver pc98/pc98/if_fe.c optional fe device-driver #pc98/isa/if_ie.c optional ie device-driver #pc98/isa/if_ix.c optional ix device-driver #pc98/isa/if_le.c optional le device-driver #pc98/isa/if_lnc.c optional lnc device-driver #i386/isa/if_sr.c optional sr device-driver #pc98/isa/if_ze.c optional ze device-driver i386/isa/if_zp.c optional zp device-driver pc98/pc98/pc98.c optional isa device-driver pc98/isa/istallion.c optional stli device-driver pc98/isa/joy.c optional joy device-driver pc98/pc98/labpc.c optional labpc device-driver pc98/pc98/lpt.c optional lpt device-driver pc98/pc98/mcd.c optional mcd device-driver pc98/pc98/mse.c optional mse device-driver pc98/isa/ncr5380.c optional nca device-driver pc98/pc98/npx.c optional npx device-driver pc98/pc98/pcaudio.c optional pca device-driver pc98/pc98/matcd/matcd.c optional matcd device-driver pc98/pc98/pcibus.c optional pci device-driver i386/isa/pcicx.c optional ze device-driver i386/isa/pcicx.c optional zp device-driver pc98/isa/pcvt/pcvt_drv.c optional vt device-driver pc98/isa/pcvt/pcvt_ext.c optional vt device-driver pc98/isa/pcvt/pcvt_kbd.c optional vt device-driver pc98/isa/pcvt/pcvt_out.c optional vt device-driver pc98/isa/pcvt/pcvt_sup.c optional vt device-driver pc98/isa/pcvt/pcvt_vtf.c optional vt device-driver pc98/pc98/prof_machdep.c optional profiling-routine pc98/pc98/psm.c optional psm device-driver pc98/isa/qcam.c optional qcam device-driver pc98/isa/qcamio.c optional qcam device-driver pc98/pc98/random_machdep.c standard pc98/isa/rc.c optional rc device-driver i386/isa/scd.c optional scd device-driver pc98/isa/seagate.c optional sea device-driver pc98/isa/si.c optional si device-driver pc98/isa/si_code.c optional si device-driver pc98/pc98/sio.c optional sio device-driver pc98/pc98/sound/pcm86.c optional pcm device-driver pc98/pc98/sound/dev_table.c optional snd device-driver pc98/pc98/sound/soundcard.c optional snd device-driver pc98/pc98/sound/sound_switch.c optional snd device-driver pc98/pc98/sound/audio.c optional snd device-driver pc98/pc98/sound/dmabuf.c optional snd device-driver pc98/pc98/sound/sys_timer.c optional snd device-driver pc98/pc98/sound/sequencer.c optional snd device-driver pc98/pc98/sound/patmgr.c optional snd device-driver pc98/pc98/sound/adlib_card.c optional opl device-driver pc98/pc98/sound/opl3.c optional opl device-driver pc98/pc98/sound/gus_card.c optional gus device-driver pc98/pc98/sound/gus_midi.c optional gus device-driver pc98/pc98/sound/gus_vol.c optional gus device-driver pc98/pc98/sound/gus_wave.c optional gus device-driver pc98/pc98/sound/ics2101.c optional gus device-driver pc98/pc98/sound/sound_timer.c optional gus device-driver pc98/pc98/sound/midi_synth.c optional gus device-driver pc98/pc98/sound/midibuf.c optional gus device-driver pc98/pc98/sound/ad1848.c optional gusxvi device-driver pc98/pc98/sound/ad1848.c optional gus device-driver pc98/pc98/sound/ad1848.c optional mss device-driver pc98/pc98/sound/midi_synth.c optional mss device-driver pc98/pc98/sound/midibuf.c optional mss device-driver pc98/pc98/sound/mpu401.c optional mpu device-driver pc98/pc98/sound/midi_synth.c optional mpu device-driver pc98/pc98/sound/midibuf.c optional mpu device-driver pc98/pc98/sound/pas2_card.c optional pas device-driver pc98/pc98/sound/pas2_midi.c optional pas device-driver pc98/pc98/sound/pas2_mixer.c optional pas device-driver pc98/pc98/sound/pas2_pcm.c optional pas device-driver pc98/pc98/sound/midi_synth.c optional pas device-driver pc98/pc98/sound/midibuf.c optional pas device-driver pc98/pc98/sound/sb_card.c optional sb device-driver pc98/pc98/sound/sb_dsp.c optional sb device-driver pc98/pc98/sound/sb_midi.c optional sb device-driver pc98/pc98/sound/sb_mixer.c optional sb device-driver pc98/pc98/sound/midi_synth.c optional sb device-driver pc98/pc98/sound/midibuf.c optional sb device-driver pc98/pc98/sound/sb16_dsp.c optional sbxvi device-driver pc98/pc98/sound/sb16_midi.c optional sbmidi device-driver pc98/pc98/sound/uart6850.c optional uart device-driver pc98/pc98/sound/midi_synth.c optional uart device-driver pc98/pc98/sound/midibuf.c optional uart device-driver pc98/pc98/sound/trix.c optional trix device-driver pc98/pc98/sound/sscape.c optional sscape device-driver pc98/isa/spigot.c optional spigot device-driver pc98/pc98/spkr.c optional speaker device-driver pc98/isa/stallion.c optional stl device-driver pc98/pc98/syscons.c optional sc device-driver pc98/isa/tw.c optional tw device-driver pc98/isa/ultra14f.c optional uha device-driver pc98/pc98/wd.c optional wdc device-driver pc98/pc98/wd.c optional wd device-driver pc98/pc98/atapi.c optional atapi device-driver pc98/pc98/wcd.c optional wcd device-driver pc98/isa/wd7000.c optional wds device-driver pc98/pc98/wt.c optional wt device-driver i386/linux/imgact_linux.c optional compat_linux i386/linux/linux_dummy.c optional compat_linux i386/linux/linux_file.c optional compat_linux i386/linux/linux_ioctl.c optional compat_linux i386/linux/linux_ipc.c optional compat_linux i386/linux/linux_locore.s optional compat_linux \ dependency "linux_assym.h" i386/linux/linux_misc.c optional compat_linux i386/linux/linux_signal.c optional compat_linux i386/linux/linux_socket.c optional compat_linux i386/linux/linux_stats.c optional compat_linux i386/linux/linux_sysent.c optional compat_linux i386/linux/linux_sysvec.c optional compat_linux i386/linux/linux_util.c optional compat_linux i386/scsi/aic7xxx.c optional ahc device-driver \ dependency "$S/dev/aic7xxx/aic7xxx_reg.h aic7xxx_seq.h" pc98/scsi/bt.c optional bt device-driver libkern/bcd.c standard libkern/divdi3.c standard libkern/inet_ntoa.c standard libkern/index.c standard libkern/mcount.c optional profiling-routine libkern/moddi3.c standard libkern/qdivrem.c standard libkern/qsort.c standard libkern/random.c standard libkern/scanc.c standard libkern/skpc.c standard libkern/strcat.c standard libkern/strcmp.c standard libkern/strcpy.c standard libkern/strlen.c standard libkern/strncmp.c standard libkern/strncpy.c standard libkern/udivdi3.c standard libkern/umoddi3.c standard gnu/i386/fpemul/div_small.s optional gpl_math_emulate gnu/i386/fpemul/errors.c optional gpl_math_emulate gnu/i386/fpemul/fpu_arith.c optional gpl_math_emulate gnu/i386/fpemul/fpu_aux.c optional gpl_math_emulate gnu/i386/fpemul/fpu_entry.c optional gpl_math_emulate gnu/i386/fpemul/fpu_etc.c optional gpl_math_emulate gnu/i386/fpemul/fpu_trig.c optional gpl_math_emulate gnu/i386/fpemul/get_address.c optional gpl_math_emulate gnu/i386/fpemul/load_store.c optional gpl_math_emulate gnu/i386/fpemul/poly_2xm1.c optional gpl_math_emulate gnu/i386/fpemul/poly_atan.c optional gpl_math_emulate gnu/i386/fpemul/poly_div.s optional gpl_math_emulate gnu/i386/fpemul/poly_l2.c optional gpl_math_emulate gnu/i386/fpemul/poly_mul64.s optional gpl_math_emulate gnu/i386/fpemul/poly_sin.c optional gpl_math_emulate gnu/i386/fpemul/poly_tan.c optional gpl_math_emulate gnu/i386/fpemul/polynomial.s optional gpl_math_emulate gnu/i386/fpemul/reg_add_sub.c optional gpl_math_emulate gnu/i386/fpemul/reg_compare.c optional gpl_math_emulate gnu/i386/fpemul/reg_constant.c optional gpl_math_emulate gnu/i386/fpemul/reg_div.s optional gpl_math_emulate gnu/i386/fpemul/reg_ld_str.c optional gpl_math_emulate gnu/i386/fpemul/reg_mul.c optional gpl_math_emulate gnu/i386/fpemul/reg_norm.s optional gpl_math_emulate gnu/i386/fpemul/reg_round.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_add.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_div.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_mul.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_sub.s optional gpl_math_emulate gnu/i386/fpemul/wm_shrx.s optional gpl_math_emulate gnu/i386/fpemul/wm_sqrt.s optional gpl_math_emulate gnu/i386/isa/dgb.c optional dgb device-driver gnu/i386/isa/nic3008.c optional nic device-driver gnu/i386/isa/nic3009.c optional nnic device-driver pci/wd82371.c optional wd device-driver Index: head/sys/pc98/conf/options.pc98 =================================================================== --- head/sys/pc98/conf/options.pc98 (revision 18264) +++ head/sys/pc98/conf/options.pc98 (revision 18265) @@ -1,34 +1,39 @@ -# $Id: options.pc98,v 1.2 1996/07/23 07:45:51 asami Exp $ +# $Id: options.pc98,v 1.3 1996/09/10 09:37:14 asami Exp $ BOUNCEPAGES opt_bounce.h USER_LDT MATH_EMULATE opt_math_emulate.h GPL_MATH_EMULATE opt_math_emulate.h IBCS2 opt_dontuse.h COMPAT_LINUX opt_dontuse.h SHOW_BUSYBUFS opt_machdep.h PANIC_REBOOT_WAIT_TIME opt_machdep.h -LARGEMEM opt_machdep.h MAXMEM opt_machdep.h PERFMON opt_perfmon.h AUTO_EOI_1 opt_auto_eoi.h AUTO_EOI_2 opt_auto_eoi.h BREAK_TO_DEBUGGER opt_comconsole.h COMCONSOLE opt_comconsole.h COM_ESP opt_sio.h COM_MULTIPORT opt_sio.h DSI_SOFT_MODEM opt_sio.h FAT_CURSOR opt_pcvt.h PCVT_FREEBSD opt_pcvt.h PCVT_SCANSET opt_pcvt.h XSERVER opt_pcvt.h CLK_CALIBRATION_LOOP opt_clock.h CLK_USE_I8254_CALIBRATION opt_clock.h CLK_USE_I586_CALIBRATION opt_clock.h -SC_KEYBOARD_PROBE_WORKS opt_syscons.h +SC_KBD_PROBE_WORKS opt_syscons.h +MAXCONS opt_syscons.h +SLOW_VGA opt_syscons.h +XT_KEYBOARD opt_syscons.h ATAPI opt_atapi.h ATAPI_STATIC opt_atapi.h + +USERCONFIG opt_userconfig.h +VISUAL_USERCONFIG opt_userconfig.h Index: head/sys/pc98/i386/machdep.c =================================================================== --- head/sys/pc98/i386/machdep.c (revision 18264) +++ head/sys/pc98/i386/machdep.c (revision 18265) @@ -1,1576 +1,1568 @@ /*- * Copyright (c) 1992 Terrence R. Lambert. * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 - * $Id: machdep.c,v 1.6 1996/09/07 02:13:32 asami Exp $ + * $Id: machdep.c,v 1.7 1996/09/10 09:37:35 asami Exp $ */ #include "npx.h" #include "opt_sysvipc.h" #include "opt_ddb.h" #include "opt_bounce.h" #include "opt_machdep.h" #include "opt_perfmon.h" +#include "opt_userconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SYSVSHM #include #endif #ifdef SYSVMSG #include #endif #ifdef SYSVSEM #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PERFMON #include #endif #include #ifdef PC98 #include #else #include #endif #include extern void init386 __P((int first)); extern int ptrace_set_pc __P((struct proc *p, unsigned int addr)); extern int ptrace_single_step __P((struct proc *p)); extern int ptrace_write_u __P((struct proc *p, vm_offset_t off, int data)); extern void dblfault_handler __P((void)); extern void identifycpu(void); /* XXX header file */ extern void earlysetcpuclass(void); /* same header file */ static void cpu_startup __P((void *)); SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL) #ifdef BOUNCE_BUFFERS extern char *bouncememory; extern int maxbkva; #ifdef BOUNCEPAGES int bouncepages = BOUNCEPAGES; #else int bouncepages = 0; #endif #endif /* BOUNCE_BUFFERS */ extern int freebufspace; int msgbufmapped = 0; /* set when safe to use msgbuf */ int _udatasel, _ucodesel; u_int atdevbase; int physmem = 0; int cold = 1; static int sysctl_hw_physmem SYSCTL_HANDLER_ARGS { int error = sysctl_handle_int(oidp, 0, ctob(physmem), req); return (error); } SYSCTL_PROC(_hw, HW_PHYSMEM, physmem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_physmem, "I", ""); static int sysctl_hw_usermem SYSCTL_HANDLER_ARGS { int error = sysctl_handle_int(oidp, 0, ctob(physmem - cnt.v_wire_count), req); return (error); } SYSCTL_PROC(_hw, HW_USERMEM, usermem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_usermem, "I", ""); int boothowto = 0, bootverbose = 0, Maxmem = 0; static int badpages = 0; #ifdef PC98 int Maxmem_under16M = 0; #endif long dumplo; extern int bootdev; vm_offset_t phys_avail[10]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) static void setup_netisrs __P((struct linker_set *)); /* XXX declare elsewhere */ static vm_offset_t buffer_sva, buffer_eva; vm_offset_t clean_sva, clean_eva; static vm_offset_t pager_sva, pager_eva; extern struct linker_set netisr_set; #define offsetof(type, member) ((size_t)(&((type *)0)->member)) static void cpu_startup(dummy) void *dummy; { register unsigned i; register caddr_t v; vm_offset_t maxaddr; vm_size_t size = 0; int firstaddr; vm_offset_t minaddr; if (boothowto & RB_VERBOSE) bootverbose++; /* * Initialize error message buffer (at end of core). */ /* avail_end was pre-decremented in init386() to compensate */ for (i = 0; i < btoc(sizeof (struct msgbuf)); i++) pmap_enter(pmap_kernel(), (vm_offset_t)msgbufp, avail_end + i * PAGE_SIZE, VM_PROT_ALL, TRUE); msgbufmapped = 1; /* * Good {morning,afternoon,evening,night}. */ printf(version); earlysetcpuclass(); startrtclock(); identifycpu(); #ifdef PERFMON perfmon_init(); #endif printf("real memory = %d (%dK bytes)\n", ptoa(Maxmem), ptoa(Maxmem) / 1024); /* * Display any holes after the first chunk of extended memory. */ if (badpages != 0) { int indx = 1; /* * XXX skip reporting ISA hole & unmanaged kernel memory */ if (phys_avail[0] == PAGE_SIZE) indx += 2; printf("Physical memory hole(s):\n"); for (; phys_avail[indx + 1] != 0; indx += 2) { int size = phys_avail[indx + 1] - phys_avail[indx]; printf("0x%08lx - 0x%08lx, %d bytes (%d pages)\n", phys_avail[indx], phys_avail[indx + 1] - 1, size, size / PAGE_SIZE); } } /* * Quickly wire in netisrs. */ setup_netisrs(&netisr_set); /* #ifdef ISDN DONET(isdnintr, NETISR_ISDN); #endif */ /* * Allocate space for system data structures. * The first available kernel virtual address is in "v". * As pages of kernel virtual memory are allocated, "v" is incremented. * As pages of memory are allocated and cleared, * "firstaddr" is incremented. * An index into the kernel page table corresponding to the * virtual memory address maintained in "v" is kept in "mapaddr". */ /* * Make two passes. The first pass calculates how much memory is * needed and allocates it. The second pass assigns virtual * addresses to the various data structures. */ firstaddr = 0; again: v = (caddr_t)firstaddr; #define valloc(name, type, num) \ (name) = (type *)v; v = (caddr_t)((name)+(num)) #define valloclim(name, type, num, lim) \ (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num))) valloc(callout, struct callout, ncallout); #ifdef SYSVSHM valloc(shmsegs, struct shmid_ds, shminfo.shmmni); #endif #ifdef SYSVSEM valloc(sema, struct semid_ds, seminfo.semmni); valloc(sem, struct sem, seminfo.semmns); /* This is pretty disgusting! */ valloc(semu, int, (seminfo.semmnu * seminfo.semusz) / sizeof(int)); #endif #ifdef SYSVMSG valloc(msgpool, char, msginfo.msgmax); valloc(msgmaps, struct msgmap, msginfo.msgseg); valloc(msghdrs, struct msg, msginfo.msgtql); valloc(msqids, struct msqid_ds, msginfo.msgmni); #endif if (nbuf == 0) { nbuf = 30; if( physmem > 1024) nbuf += min((physmem - 1024) / 12, 1024); } nswbuf = min(nbuf, 128); valloc(swbuf, struct buf, nswbuf); valloc(buf, struct buf, nbuf); #ifdef BOUNCE_BUFFERS /* * If there is more than 16MB of memory, allocate some bounce buffers */ if (Maxmem > 4096) { if (bouncepages == 0) { bouncepages = 64; bouncepages += ((Maxmem - 4096) / 2048) * 32; } v = (caddr_t)((vm_offset_t)round_page(v)); valloc(bouncememory, char, bouncepages * PAGE_SIZE); } #endif /* * End of first pass, size has been calculated so allocate memory */ if (firstaddr == 0) { size = (vm_size_t)(v - firstaddr); firstaddr = (int)kmem_alloc(kernel_map, round_page(size)); if (firstaddr == 0) panic("startup: no room for tables"); goto again; } /* * End of second pass, addresses have been assigned */ if ((vm_size_t)(v - firstaddr) != size) panic("startup: table size inconsistency"); #ifdef BOUNCE_BUFFERS clean_map = kmem_suballoc(kernel_map, &clean_sva, &clean_eva, (nbuf*MAXBSIZE) + (nswbuf*MAXPHYS) + maxbkva + pager_map_size, TRUE); io_map = kmem_suballoc(clean_map, &minaddr, &maxaddr, maxbkva, FALSE); #else clean_map = kmem_suballoc(kernel_map, &clean_sva, &clean_eva, (nbuf*MAXBSIZE) + (nswbuf*MAXPHYS) + pager_map_size, TRUE); #endif buffer_map = kmem_suballoc(clean_map, &buffer_sva, &buffer_eva, (nbuf*MAXBSIZE), TRUE); pager_map = kmem_suballoc(clean_map, &pager_sva, &pager_eva, (nswbuf*MAXPHYS) + pager_map_size, TRUE); exec_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, (16*ARG_MAX), TRUE); exech_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, (32*ARG_MAX), TRUE); u_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, (maxproc*UPAGES*PAGE_SIZE), FALSE); /* * Finally, allocate mbuf pool. Since mclrefcnt is an off-size * we use the more space efficient malloc in place of kmem_alloc. */ mclrefcnt = (char *)malloc(nmbclusters+PAGE_SIZE/MCLBYTES, M_MBUF, M_NOWAIT); bzero(mclrefcnt, nmbclusters+PAGE_SIZE/MCLBYTES); mcl_map = kmem_suballoc(kmem_map, (vm_offset_t *)&mbutl, &maxaddr, nmbclusters * MCLBYTES, FALSE); { vm_size_t mb_map_size; mb_map_size = nmbufs * MSIZE; mb_map = kmem_suballoc(kmem_map, &minaddr, &maxaddr, round_page(mb_map_size), FALSE); } /* * Initialize callouts */ callfree = callout; for (i = 1; i < ncallout; i++) callout[i-1].c_next = &callout[i]; if (boothowto & RB_CONFIG) { +#ifdef USERCONFIG userconfig(); cninit(); /* the preferred console may have changed */ +#else + printf("Sorry! no userconfig in this kernel\n"); +#endif } #ifdef BOUNCE_BUFFERS /* * init bounce buffers */ vm_bounce_init(); #endif printf("avail memory = %d (%dK bytes)\n", ptoa(cnt.v_free_count), ptoa(cnt.v_free_count) / 1024); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); /* * In verbose mode, print out the BIOS's idea of the disk geometries. */ if (bootverbose) { printf("BIOS Geometries:\n"); for (i = 0; i < N_BIOS_GEOM; i++) { unsigned long bios_geom; int max_cylinder, max_head, max_sector; bios_geom = bootinfo.bi_bios_geom[i]; /* * XXX the bootstrap punts a 1200K floppy geometry * when the get-disk-geometry interrupt fails. Skip * drives that have this geometry. */ if (bios_geom == 0x4f010f) continue; printf(" %x:%08lx ", i, bios_geom); max_cylinder = bios_geom >> 16; max_head = (bios_geom >> 8) & 0xff; max_sector = bios_geom & 0xff; printf( "0..%d=%d cylinders, 0..%d=%d heads, 1..%d=%d sectors\n", max_cylinder, max_cylinder + 1, max_head, max_head + 1, max_sector, max_sector); } printf(" %d accounted for\n", bootinfo.bi_n_bios_used); } } int register_netisr(num, handler) int num; netisr_t *handler; { if (num < 0 || num >= (sizeof(netisrs)/sizeof(*netisrs)) ) { printf("register_netisr: bad isr number: %d\n", num); return (EINVAL); } netisrs[num] = handler; return (0); } static void setup_netisrs(ls) struct linker_set *ls; { int i; const struct netisrtab *nit; for(i = 0; ls->ls_items[i]; i++) { nit = (const struct netisrtab *)ls->ls_items[i]; register_netisr(nit->nit_num, nit->nit_isr); } } /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * at top to call routine, followed by kcall * to sigreturn routine below. After sigreturn * resets the signal mask, the stack, and the * frame pointer, it returns to the user * specified pc, psl. */ void sendsig(catcher, sig, mask, code) sig_t catcher; int sig, mask; u_long code; { register struct proc *p = curproc; register int *regs; register struct sigframe *fp; struct sigframe sf; struct sigacts *psp = p->p_sigacts; int oonstack; regs = p->p_md.md_regs; oonstack = psp->ps_sigstk.ss_flags & SS_ONSTACK; /* * Allocate and validate space for the signal handler context. */ if ((psp->ps_flags & SAS_ALTSTACK) && !oonstack && (psp->ps_sigonstack & sigmask(sig))) { fp = (struct sigframe *)(psp->ps_sigstk.ss_sp + psp->ps_sigstk.ss_size - sizeof(struct sigframe)); psp->ps_sigstk.ss_flags |= SS_ONSTACK; } else { fp = (struct sigframe *)regs[tESP] - 1; } /* * grow() will return FALSE if the fp will not fit inside the stack * and the stack can not be grown. useracc will return FALSE * if access is denied. */ if ((grow(p, (int)fp) == FALSE) || (useracc((caddr_t)fp, sizeof (struct sigframe), B_WRITE) == FALSE)) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ SIGACTION(p, SIGILL) = SIG_DFL; sig = sigmask(SIGILL); p->p_sigignore &= ~sig; p->p_sigcatch &= ~sig; p->p_sigmask &= ~sig; psignal(p, SIGILL); return; } /* * Build the argument list for the signal handler. */ if (p->p_sysent->sv_sigtbl) { if (sig < p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[sig]; else sig = p->p_sysent->sv_sigsize + 1; } sf.sf_signum = sig; sf.sf_code = code; sf.sf_scp = &fp->sf_sc; sf.sf_addr = (char *) regs[tERR]; sf.sf_handler = catcher; /* save scratch registers */ sf.sf_sc.sc_eax = regs[tEAX]; sf.sf_sc.sc_ebx = regs[tEBX]; sf.sf_sc.sc_ecx = regs[tECX]; sf.sf_sc.sc_edx = regs[tEDX]; sf.sf_sc.sc_esi = regs[tESI]; sf.sf_sc.sc_edi = regs[tEDI]; sf.sf_sc.sc_cs = regs[tCS]; sf.sf_sc.sc_ds = regs[tDS]; sf.sf_sc.sc_ss = regs[tSS]; sf.sf_sc.sc_es = regs[tES]; sf.sf_sc.sc_isp = regs[tISP]; /* * Build the signal context to be used by sigreturn. */ sf.sf_sc.sc_onstack = oonstack; sf.sf_sc.sc_mask = mask; sf.sf_sc.sc_sp = regs[tESP]; sf.sf_sc.sc_fp = regs[tEBP]; sf.sf_sc.sc_pc = regs[tEIP]; sf.sf_sc.sc_ps = regs[tEFLAGS]; /* * Copy the sigframe out to the user's stack. */ if (copyout(&sf, fp, sizeof(struct sigframe)) != 0) { /* * Something is wrong with the stack pointer. * ...Kill the process. */ sigexit(p, SIGILL); }; regs[tESP] = (int)fp; regs[tEIP] = (int)(((char *)PS_STRINGS) - *(p->p_sysent->sv_szsigcode)); regs[tEFLAGS] &= ~PSL_VM; regs[tCS] = _ucodesel; regs[tDS] = _udatasel; regs[tES] = _udatasel; regs[tSS] = _udatasel; } /* * System call to cleanup state after a signal * has been taken. Reset signal mask and * stack state from context left by sendsig (above). * Return to previous pc and psl as specified by * context left by sendsig. Check carefully to * make sure that the user has not modified the * state to gain improper privileges. */ int sigreturn(p, uap, retval) struct proc *p; struct sigreturn_args /* { struct sigcontext *sigcntxp; } */ *uap; int *retval; { register struct sigcontext *scp; register struct sigframe *fp; register int *regs = p->p_md.md_regs; int eflags; /* * (XXX old comment) regs[tESP] points to the return address. * The user scp pointer is above that. * The return address is faked in the signal trampoline code * for consistency. */ scp = uap->sigcntxp; fp = (struct sigframe *) ((caddr_t)scp - offsetof(struct sigframe, sf_sc)); if (useracc((caddr_t)fp, sizeof (*fp), 0) == 0) return(EINVAL); /* * Don't allow users to change privileged or reserved flags. */ #define EFLAGS_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) eflags = scp->sc_ps; /* * XXX do allow users to change the privileged flag PSL_RF. The * cpu sets PSL_RF in tf_eflags for faults. Debuggers should * sometimes set it there too. tf_eflags is kept in the signal * context during signal handling and there is no other place * to remember it, so the PSL_RF bit may be corrupted by the * signal handler without us knowing. Corruption of the PSL_RF * bit at worst causes one more or one less debugger trap, so * allowing it is fairly harmless. */ if (!EFLAGS_SECURE(eflags & ~PSL_RF, regs[tEFLAGS] & ~PSL_RF)) { #ifdef DEBUG printf("sigreturn: eflags = 0x%x\n", eflags); #endif return(EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) if (!CS_SECURE(scp->sc_cs)) { #ifdef DEBUG printf("sigreturn: cs = 0x%x\n", scp->sc_cs); #endif trapsignal(p, SIGBUS, T_PROTFLT); return(EINVAL); } /* restore scratch registers */ regs[tEAX] = scp->sc_eax; regs[tEBX] = scp->sc_ebx; regs[tECX] = scp->sc_ecx; regs[tEDX] = scp->sc_edx; regs[tESI] = scp->sc_esi; regs[tEDI] = scp->sc_edi; regs[tCS] = scp->sc_cs; regs[tDS] = scp->sc_ds; regs[tES] = scp->sc_es; regs[tSS] = scp->sc_ss; regs[tISP] = scp->sc_isp; if (useracc((caddr_t)scp, sizeof (*scp), 0) == 0) return(EINVAL); if (scp->sc_onstack & 01) p->p_sigacts->ps_sigstk.ss_flags |= SS_ONSTACK; else p->p_sigacts->ps_sigstk.ss_flags &= ~SS_ONSTACK; p->p_sigmask = scp->sc_mask &~ (sigmask(SIGKILL)|sigmask(SIGCONT)|sigmask(SIGSTOP)); regs[tEBP] = scp->sc_fp; regs[tESP] = scp->sc_sp; regs[tEIP] = scp->sc_pc; regs[tEFLAGS] = eflags; return(EJUSTRETURN); } /* * Machine depdnetnt boot() routine * * I haven't seen anything too put here yet * Possibly some stuff might be grafted back here from boot() */ void cpu_boot(int howto) { } /* * Clear registers on exec */ void setregs(p, entry, stack) struct proc *p; u_long entry; u_long stack; { int *regs = p->p_md.md_regs; #ifdef USER_LDT struct pcb *pcb = &p->p_addr->u_pcb; /* was i386_user_cleanup() in NetBSD */ if (pcb->pcb_ldt) { if (pcb == curpcb) lldt(GSEL(GUSERLDT_SEL, SEL_KPL)); kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ldt, pcb->pcb_ldt_len * sizeof(union descriptor)); pcb->pcb_ldt_len = (int)pcb->pcb_ldt = 0; } #endif bzero(regs, sizeof(struct trapframe)); regs[tEIP] = entry; regs[tESP] = stack; regs[tEFLAGS] = PSL_USER | (regs[tEFLAGS] & PSL_T); regs[tSS] = _udatasel; regs[tDS] = _udatasel; regs[tES] = _udatasel; regs[tCS] = _ucodesel; p->p_addr->u_pcb.pcb_flags = 0; /* no fp at all */ load_cr0(rcr0() | CR0_TS); /* start emulating */ #if NNPX > 0 npxinit(__INITIAL_NPXCW__); #endif /* NNPX > 0 */ } static int sysctl_machdep_adjkerntz SYSCTL_HANDLER_ARGS { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) resettodr(); return (error); } SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW, &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", ""); SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_STRUCT(_machdep, CPU_BOOTINFO, bootinfo, CTLFLAG_RD, &bootinfo, bootinfo, ""); SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock, CTLFLAG_RW, &wall_cmos_clock, 0, ""); /* * Initialize 386 and configure to run kernel */ /* * Initialize segments & interrupt table */ int currentldt; int _default_ldt; union descriptor gdt[NGDT]; /* global descriptor table */ struct gate_descriptor idt[NIDT]; /* interrupt descriptor table */ union descriptor ldt[NLDT]; /* local descriptor table */ static struct i386tss dblfault_tss; static char dblfault_stack[PAGE_SIZE]; extern struct user *proc0paddr; /* software prototypes -- in more palatable form */ struct soft_segment_descriptor gdt_segs[] = { /* GNULL_SEL 0 Null Descriptor */ { 0x0, /* segment base address */ 0x0, /* length */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GCODE_SEL 1 Code Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GDATA_SEL 2 Data Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GLDT_SEL 3 LDT Descriptor */ { (int) ldt, /* segment base address */ sizeof(ldt)-1, /* length - all address space */ SDT_SYSLDT, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GTGATE_SEL 4 Null Descriptor - Placeholder */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GPANIC_SEL 5 Panic Tss Descriptor */ { (int) &dblfault_tss, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GPROC0_SEL 6 Proc 0 Tss Descriptor */ { (int) kstack, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GUSERLDT_SEL 7 User LDT Descriptor per process */ { (int) ldt, /* segment base address */ (512 * sizeof(union descriptor)-1), /* length */ SDT_SYSLDT, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GAPMCODE32_SEL 8 APM BIOS 32-bit interface (32bit Code) */ { 0, /* segment base address (overwritten by APM) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GAPMCODE16_SEL 9 APM BIOS 32-bit interface (16bit Code) */ { 0, /* segment base address (overwritten by APM) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GAPMDATA_SEL 10 APM BIOS 32-bit interface (Data) */ { 0, /* segment base address (overwritten by APM) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; static struct soft_segment_descriptor ldt_segs[] = { /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Code Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* Data Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; void setidt(idx, func, typ, dpl, selec) int idx; inthand_t *func; int typ; int dpl; int selec; { struct gate_descriptor *ip = idt + idx; ip->gd_looffset = (int)func; ip->gd_selector = selec; ip->gd_stkcpy = 0; ip->gd_xx = 0; ip->gd_type = typ; ip->gd_dpl = dpl; ip->gd_p = 1; ip->gd_hioffset = ((int)func)>>16 ; } #define IDTVEC(name) __CONCAT(X,name) extern inthand_t IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(fpusegm), IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot), IDTVEC(page), IDTVEC(mchk), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align), IDTVEC(syscall), IDTVEC(int0x80_syscall); void sdtossd(sd, ssd) struct segment_descriptor *sd; struct soft_segment_descriptor *ssd; { ssd->ssd_base = (sd->sd_hibase << 24) | sd->sd_lobase; ssd->ssd_limit = (sd->sd_hilimit << 16) | sd->sd_lolimit; ssd->ssd_type = sd->sd_type; ssd->ssd_dpl = sd->sd_dpl; ssd->ssd_p = sd->sd_p; ssd->ssd_def32 = sd->sd_def32; ssd->ssd_gran = sd->sd_gran; } void init386(first) int first; { int x; unsigned biosbasemem, biosextmem; struct gate_descriptor *gdp; int gsel_tss; /* table descriptors - used to load tables by microp */ struct region_descriptor r_gdt, r_idt; int pagesinbase, pagesinext; int target_page, pa_indx; proc0.p_addr = proc0paddr; atdevbase = ISA_HOLE_START + KERNBASE; /* * Initialize the console before we print anything out. */ cninit(); #ifdef PC98 /* * Initialize DMAC */ init_pc98_dmac(); #endif /* * make gdt memory segments, the code segment goes up to end of the * page with etext in it, the data segment goes to the end of * the address space */ /* * XXX text protection is temporarily (?) disabled. The limit was * i386_btop(round_page(etext)) - 1. */ gdt_segs[GCODE_SEL].ssd_limit = i386_btop(0) - 1; gdt_segs[GDATA_SEL].ssd_limit = i386_btop(0) - 1; for (x = 0; x < NGDT; x++) ssdtosd(&gdt_segs[x], &gdt[x].sd); /* make ldt memory segments */ /* * The data segment limit must not cover the user area because we * don't want the user area to be writable in copyout() etc. (page * level protection is lost in kernel mode on 386's). Also, we * don't want the user area to be writable directly (page level * protection of the user area is not available on 486's with * CR0_WP set, because there is no user-read/kernel-write mode). * * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it * should be spelled ...MAX_USER... */ #define VM_END_USER_RW_ADDRESS VM_MAXUSER_ADDRESS /* * The code segment limit has to cover the user area until we move * the signal trampoline out of the user area. This is safe because * the code segment cannot be written to directly. */ #define VM_END_USER_R_ADDRESS (VM_END_USER_RW_ADDRESS + UPAGES * PAGE_SIZE) ldt_segs[LUCODE_SEL].ssd_limit = i386_btop(VM_END_USER_R_ADDRESS) - 1; ldt_segs[LUDATA_SEL].ssd_limit = i386_btop(VM_END_USER_RW_ADDRESS) - 1; /* Note. eventually want private ldts per process */ for (x = 0; x < NLDT; x++) ssdtosd(&ldt_segs[x], &ldt[x].sd); /* exceptions */ for (x = 0; x < NIDT; x++) setidt(x, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0, &IDTVEC(div), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(1, &IDTVEC(dbg), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(2, &IDTVEC(nmi), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(3, &IDTVEC(bpt), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(4, &IDTVEC(ofl), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(5, &IDTVEC(bnd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(7, &IDTVEC(dna), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(8, 0, SDT_SYSTASKGT, SEL_KPL, GSEL(GPANIC_SEL, SEL_KPL)); setidt(9, &IDTVEC(fpusegm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(10, &IDTVEC(tss), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(11, &IDTVEC(missing), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(12, &IDTVEC(stk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #ifdef CYRIX_486DLC setidt(14, &IDTVEC(page), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #else setidt(14, &IDTVEC(page), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif setidt(15, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(16, &IDTVEC(fpu), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(17, &IDTVEC(align), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(18, &IDTVEC(mchk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0x80, &IDTVEC(int0x80_syscall), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); #include "isa.h" #if NISA >0 isa_defaultirq(); #endif rand_initialize(); r_gdt.rd_limit = sizeof(gdt) - 1; r_gdt.rd_base = (int) gdt; lgdt(&r_gdt); r_idt.rd_limit = sizeof(idt) - 1; r_idt.rd_base = (int) idt; lidt(&r_idt); _default_ldt = GSEL(GLDT_SEL, SEL_KPL); lldt(_default_ldt); currentldt = _default_ldt; #ifdef DDB kdb_init(); if (boothowto & RB_KDB) Debugger("Boot flags requested debugger"); #endif #ifdef PC98 #ifdef EPSON_MEMWIN init_epson_memwin(); #endif biosbasemem = 640; /* 640KB */ biosextmem = (Maxmem * PAGE_SIZE - 0x100000)/1024; /* extent memory */ #else /* IBM-PC */ /* Use BIOS values stored in RTC CMOS RAM, since probing * breaks certain 386 AT relics. */ biosbasemem = rtcin(RTC_BASELO)+ (rtcin(RTC_BASEHI)<<8); biosextmem = rtcin(RTC_EXTLO)+ (rtcin(RTC_EXTHI)<<8); /* * If BIOS tells us that it has more than 640k in the basemem, * don't believe it - set it to 640k. */ if (biosbasemem > 640) { printf("Preposterous RTC basemem of %dK, truncating to 640K\n", biosbasemem); biosbasemem = 640; } if (bootinfo.bi_memsizes_valid && bootinfo.bi_basemem > 640) { printf("Preposterous BIOS basemem of %dK, truncating to 640K\n", bootinfo.bi_basemem); bootinfo.bi_basemem = 640; } /* * Warn if the official BIOS interface disagrees with the RTC * interface used above about the amount of base memory or the * amount of extended memory. Prefer the BIOS value for the base * memory. This is necessary for machines that `steal' base * memory for use as BIOS memory, at least if we are going to use * the BIOS for apm. Prefer the RTC value for extended memory. * Eventually the hackish interface shouldn't even be looked at. */ if (bootinfo.bi_memsizes_valid) { if (bootinfo.bi_basemem != biosbasemem) { vm_offset_t pa; printf( "BIOS basemem (%ldK) != RTC basemem (%dK), setting to BIOS value\n", biosbasemem = bootinfo.bi_basemem; /* * XXX if biosbasemem is now < 640, there is `hole' * between the end of base memory and the start of * ISA memory. The hole may be empty or it may * contain BIOS code or data. Map it read/write so * that the BIOS can write to it. (Memory from 0 to * the physical end of the kernel is mapped read-only * to begin with and then parts of it are remapped. * The parts that aren't remapped form holes that * remain read-only and are unused by the kernel. * The base memory area is below the physical end of * the kernel and right now forms a read-only hole. * The part of it from 0 to * (trunc_page(biosbasemem * 1024) - 1) will be * remapped and used by the kernel later.) * * This code is similar to the code used in * pmap_mapdev, but since no memory needs to be * allocated we simply change the mapping. */ for (pa = trunc_page(biosbasemem * 1024); pa < ISA_HOLE_START; pa += PAGE_SIZE) { unsigned *pte; pte = (unsigned *)vtopte(pa + KERNBASE); *pte = pa | PG_RW | PG_V; } } if (bootinfo.bi_extmem != biosextmem) printf("BIOS extmem (%ldK) != RTC extmem (%dK)\n", bootinfo.bi_extmem, biosextmem); } -#endif - - /* - * Some 386 machines might give us a bogus number for extended - * mem. If this happens, stop now. - */ -#ifndef PC98 -#ifndef LARGEMEM - if (biosextmem > 65536) { - panic("extended memory beyond limit of 64MB"); - /* NOTREACHED */ - } -#endif #endif pagesinbase = biosbasemem * 1024 / PAGE_SIZE; pagesinext = biosextmem * 1024 / PAGE_SIZE; /* * Special hack for chipsets that still remap the 384k hole when * there's 16MB of memory - this really confuses people that * are trying to use bus mastering ISA controllers with the * "16MB limit"; they only have 16MB, but the remapping puts * them beyond the limit. */ #ifndef PC98 /* * If extended memory is between 15-16MB (16-17MB phys address range), * chop it to 15MB. */ if ((pagesinext > 3840) && (pagesinext < 4096)) pagesinext = 3840; #endif /* * Maxmem isn't the "maximum memory", it's one larger than the * highest page of the physical address space. It should be * called something like "Maxphyspage". */ Maxmem = pagesinext + 0x100000/PAGE_SIZE; #ifdef MAXMEM Maxmem = MAXMEM/4; #endif /* call pmap initialization to make new kernel address space */ pmap_bootstrap (first, 0); /* * Size up each available chunk of physical memory. */ /* * We currently don't bother testing base memory. * XXX ...but we probably should. */ pa_indx = 0; badpages = 0; if (pagesinbase > 1) { phys_avail[pa_indx++] = PAGE_SIZE; /* skip first page of memory */ phys_avail[pa_indx] = ptoa(pagesinbase);/* memory up to the ISA hole */ physmem = pagesinbase - 1; } else { /* point at first chunk end */ pa_indx++; } #ifdef PC98 #ifdef notyet init_cpu_accel_mem(); #endif #endif for (target_page = avail_start; target_page < ptoa(Maxmem); target_page += PAGE_SIZE) { int tmp, page_bad = FALSE; #ifdef PC98 /* skip system area */ if (target_page>=ptoa(Maxmem_under16M) && target_page < ptoa(4096)) page_bad = TRUE; #endif /* * map page into kernel: valid, read/write, non-cacheable */ *(int *)CMAP1 = PG_V | PG_RW | PG_N | target_page; pmap_update(); tmp = *(int *)CADDR1; /* * Test for alternating 1's and 0's */ *(volatile int *)CADDR1 = 0xaaaaaaaa; if (*(volatile int *)CADDR1 != 0xaaaaaaaa) { page_bad = TRUE; } /* * Test for alternating 0's and 1's */ *(volatile int *)CADDR1 = 0x55555555; if (*(volatile int *)CADDR1 != 0x55555555) { page_bad = TRUE; } /* * Test for all 1's */ *(volatile int *)CADDR1 = 0xffffffff; if (*(volatile int *)CADDR1 != 0xffffffff) { page_bad = TRUE; } /* * Test for all 0's */ *(volatile int *)CADDR1 = 0x0; if (*(volatile int *)CADDR1 != 0x0) { /* * test of page failed */ page_bad = TRUE; } /* * Restore original value. */ *(int *)CADDR1 = tmp; /* * Adjust array of valid/good pages. */ if (page_bad == FALSE) { /* * If this good page is a continuation of the * previous set of good pages, then just increase * the end pointer. Otherwise start a new chunk. * Note that "end" points one higher than end, * making the range >= start and < end. */ if (phys_avail[pa_indx] == target_page) { phys_avail[pa_indx] += PAGE_SIZE; } else { pa_indx++; if (pa_indx == PHYS_AVAIL_ARRAY_END) { printf("Too many holes in the physical address space, giving up\n"); pa_indx--; break; } phys_avail[pa_indx++] = target_page; /* start */ phys_avail[pa_indx] = target_page + PAGE_SIZE; /* end */ } physmem++; } else { badpages++; page_bad = FALSE; } } *(int *)CMAP1 = 0; pmap_update(); /* * XXX * The last chunk must contain at least one page plus the message * buffer to avoid complicating other code (message buffer address * calculation, etc.). */ while (phys_avail[pa_indx - 1] + PAGE_SIZE + round_page(sizeof(struct msgbuf)) >= phys_avail[pa_indx]) { physmem -= atop(phys_avail[pa_indx] - phys_avail[pa_indx - 1]); phys_avail[pa_indx--] = 0; phys_avail[pa_indx--] = 0; } Maxmem = atop(phys_avail[pa_indx]); /* Trim off space for the message buffer. */ phys_avail[pa_indx] -= round_page(sizeof(struct msgbuf)); avail_end = phys_avail[pa_indx]; /* now running on new page tables, configured,and u/iom is accessible */ /* make a initial tss so microp can get interrupt stack on syscall! */ proc0.p_addr->u_pcb.pcb_tss.tss_esp0 = (int) kstack + UPAGES*PAGE_SIZE; proc0.p_addr->u_pcb.pcb_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL) ; gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); dblfault_tss.tss_esp = dblfault_tss.tss_esp0 = dblfault_tss.tss_esp1 = dblfault_tss.tss_esp2 = (int) &dblfault_stack[sizeof(dblfault_stack)]; dblfault_tss.tss_ss = dblfault_tss.tss_ss0 = dblfault_tss.tss_ss1 = dblfault_tss.tss_ss2 = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_cr3 = IdlePTD; dblfault_tss.tss_eip = (int) dblfault_handler; dblfault_tss.tss_eflags = PSL_KERNEL; dblfault_tss.tss_ds = dblfault_tss.tss_es = dblfault_tss.tss_fs = dblfault_tss.tss_gs = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_cs = GSEL(GCODE_SEL, SEL_KPL); dblfault_tss.tss_ldt = GSEL(GLDT_SEL, SEL_KPL); ((struct i386tss *)gdt_segs[GPROC0_SEL].ssd_base)->tss_ioopt = (sizeof(struct i386tss))<<16; ltr(gsel_tss); /* make a call gate to reenter kernel with */ gdp = &ldt[LSYS5CALLS_SEL].gd; x = (int) &IDTVEC(syscall); gdp->gd_looffset = x++; gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL); gdp->gd_stkcpy = 1; gdp->gd_type = SDT_SYS386CGT; gdp->gd_dpl = SEL_UPL; gdp->gd_p = 1; gdp->gd_hioffset = ((int) &IDTVEC(syscall)) >>16; /* transfer to user mode */ _ucodesel = LSEL(LUCODE_SEL, SEL_UPL); _udatasel = LSEL(LUDATA_SEL, SEL_UPL); /* setup proc 0's pcb */ proc0.p_addr->u_pcb.pcb_flags = 0; proc0.p_addr->u_pcb.pcb_cr3 = IdlePTD; } /* * The registers are in the frame; the frame is in the user area of * the process in question; when the process is active, the registers * are in "the kernel stack"; when it's not, they're still there, but * things get flipped around. So, since p->p_md.md_regs is the whole address * of the register set, take its offset from the kernel stack, and * index into the user block. Don't you just *love* virtual memory? * (I'm starting to think seymour is right...) */ #define TF_REGP(p) ((struct trapframe *) \ ((char *)(p)->p_addr \ + ((char *)(p)->p_md.md_regs - kstack))) int ptrace_set_pc(p, addr) struct proc *p; unsigned int addr; { TF_REGP(p)->tf_eip = addr; return (0); } int ptrace_single_step(p) struct proc *p; { TF_REGP(p)->tf_eflags |= PSL_T; return (0); } int ptrace_write_u(p, off, data) struct proc *p; vm_offset_t off; int data; { struct trapframe frame_copy; vm_offset_t min; struct trapframe *tp; /* * Privileged kernel state is scattered all over the user area. * Only allow write access to parts of regs and to fpregs. */ min = (char *)p->p_md.md_regs - kstack; if (off >= min && off <= min + sizeof(struct trapframe) - sizeof(int)) { tp = TF_REGP(p); frame_copy = *tp; *(int *)((char *)&frame_copy + (off - min)) = data; if (!EFLAGS_SECURE(frame_copy.tf_eflags, tp->tf_eflags) || !CS_SECURE(frame_copy.tf_cs)) return (EINVAL); *(int*)((char *)p->p_addr + off) = data; return (0); } min = offsetof(struct user, u_pcb) + offsetof(struct pcb, pcb_savefpu); if (off >= min && off <= min + sizeof(struct save87) - sizeof(int)) { *(int*)((char *)p->p_addr + off) = data; return (0); } return (EFAULT); } int fill_regs(p, regs) struct proc *p; struct reg *regs; { struct trapframe *tp; tp = TF_REGP(p); regs->r_es = tp->tf_es; regs->r_ds = tp->tf_ds; regs->r_edi = tp->tf_edi; regs->r_esi = tp->tf_esi; regs->r_ebp = tp->tf_ebp; regs->r_ebx = tp->tf_ebx; regs->r_edx = tp->tf_edx; regs->r_ecx = tp->tf_ecx; regs->r_eax = tp->tf_eax; regs->r_eip = tp->tf_eip; regs->r_cs = tp->tf_cs; regs->r_eflags = tp->tf_eflags; regs->r_esp = tp->tf_esp; regs->r_ss = tp->tf_ss; return (0); } int set_regs(p, regs) struct proc *p; struct reg *regs; { struct trapframe *tp; tp = TF_REGP(p); if (!EFLAGS_SECURE(regs->r_eflags, tp->tf_eflags) || !CS_SECURE(regs->r_cs)) return (EINVAL); tp->tf_es = regs->r_es; tp->tf_ds = regs->r_ds; tp->tf_edi = regs->r_edi; tp->tf_esi = regs->r_esi; tp->tf_ebp = regs->r_ebp; tp->tf_ebx = regs->r_ebx; tp->tf_edx = regs->r_edx; tp->tf_ecx = regs->r_ecx; tp->tf_eax = regs->r_eax; tp->tf_eip = regs->r_eip; tp->tf_cs = regs->r_cs; tp->tf_eflags = regs->r_eflags; tp->tf_esp = regs->r_esp; tp->tf_ss = regs->r_ss; return (0); } #ifndef DDB void Debugger(const char *msg) { printf("Debugger(\"%s\") called.\n", msg); } #endif /* no DDB */ #include #define b_cylin b_resid /* * Determine the size of the transfer, and make sure it is * within the boundaries of the partition. Adjust transfer * if needed, and signal errors or early completion. */ int bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel) { struct partition *p = lp->d_partitions + dkpart(bp->b_dev); int labelsect = lp->d_partitions[0].p_offset; int maxsz = p->p_size, sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; /* overwriting disk label ? */ /* XXX should also protect bootstrap in first 8K */ if (bp->b_blkno + p->p_offset <= LABELSECTOR + labelsect && #if LABELSECTOR != 0 bp->b_blkno + p->p_offset + sz > LABELSECTOR + labelsect && #endif (bp->b_flags & B_READ) == 0 && wlabel == 0) { bp->b_error = EROFS; goto bad; } #if defined(DOSBBSECTOR) && defined(notyet) /* overwriting master boot record? */ if (bp->b_blkno + p->p_offset <= DOSBBSECTOR && (bp->b_flags & B_READ) == 0 && wlabel == 0) { bp->b_error = EROFS; goto bad; } #endif /* beyond partition? */ if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) { /* if exactly at end of disk, return an EOF */ if (bp->b_blkno == maxsz) { bp->b_resid = bp->b_bcount; return(0); } /* or truncate if part of it fits */ sz = maxsz - bp->b_blkno; if (sz <= 0) { bp->b_error = EINVAL; goto bad; } bp->b_bcount = sz << DEV_BSHIFT; } /* calculate cylinder for disksort to order transfers with */ bp->b_pblkno = bp->b_blkno + p->p_offset; bp->b_cylin = bp->b_pblkno / lp->d_secpercyl; return(1); bad: bp->b_flags |= B_ERROR; return(-1); } Index: head/sys/pc98/i386/pmap.c =================================================================== --- head/sys/pc98/i386/pmap.c (revision 18264) +++ head/sys/pc98/i386/pmap.c (revision 18265) @@ -1,2675 +1,3002 @@ /* * Copyright (c) 1991 Regents of the University of California. * All rights reserved. * Copyright (c) 1994 John S. Dyson * All rights reserved. * Copyright (c) 1994 David Greenman * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department and William Jolitz of UUNET Technologies Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)pmap.c 7.7 (Berkeley) 5/12/91 - * $Id: pmap.c,v 1.1.1.1 1996/06/14 10:04:41 asami Exp $ + * $Id: pmap.c,v 1.2 1996/07/23 07:45:56 asami Exp $ */ /* * Manages physical address maps. * * In addition to hardware address maps, this * module is called upon to provide software-use-only * maps which may or may not be stored in the same * form as hardware maps. These pseudo-maps are * used to store intermediate results from copy * operations to and from address spaces. * * Since the information managed by this module is * also stored by the logical address mapping module, * this module may throw away valid virtual-to-physical * mappings at almost any time. However, invalidations * of virtual-to-physical mappings must be done as * requested. * * In order to cope with hardware architectures which * make virtual-to-physical map invalidates expensive, * this module may delay invalidate or reduced protection * operations until such time as they are actually * necessary. This module is given full information as * to which processors are currently using which maps, * and to when physical maps must be made correct. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PMAP_KEEP_PDIRS #if defined(DIAGNOSTIC) #define PMAP_DIAGNOSTIC #endif +#if !defined(PMAP_DIAGNOSTIC) +#define PMAP_INLINE __inline +#else +#define PMAP_INLINE +#endif + +#define PTPHINT + static void init_pv_entries __P((int)); /* * Get PDEs and PTEs for user/kernel address space */ #define pmap_pde(m, v) (&((m)->pm_pdir[(vm_offset_t)(v) >> PDRSHIFT])) #define pdir_pde(m, v) (m[(vm_offset_t)(v) >> PDRSHIFT]) #define pmap_pde_v(pte) ((*(int *)pte & PG_V) != 0) #define pmap_pte_w(pte) ((*(int *)pte & PG_W) != 0) #define pmap_pte_m(pte) ((*(int *)pte & PG_M) != 0) #define pmap_pte_u(pte) ((*(int *)pte & PG_A) != 0) #define pmap_pte_v(pte) ((*(int *)pte & PG_V) != 0) #define pmap_pte_set_w(pte, v) ((v)?(*(int *)pte |= PG_W):(*(int *)pte &= ~PG_W)) #define pmap_pte_set_prot(pte, v) ((*(int *)pte &= ~PG_PROT), (*(int *)pte |= (v))) /* * Given a map and a machine independent protection code, * convert to a vax protection code. */ #define pte_prot(m, p) (protection_codes[p]) static int protection_codes[8]; static struct pmap kernel_pmap_store; pmap_t kernel_pmap; vm_offset_t avail_start; /* PA of first available physical page */ vm_offset_t avail_end; /* PA of last available physical page */ vm_offset_t virtual_avail; /* VA of first avail page (after kernel bss) */ vm_offset_t virtual_end; /* VA of last avail page (end of kernel AS) */ static boolean_t pmap_initialized = FALSE; /* Has pmap_init completed? */ static vm_offset_t vm_first_phys; static int nkpt; static vm_page_t nkpg; vm_offset_t kernel_vm_end; extern vm_offset_t clean_sva, clean_eva; extern int cpu_class; #if defined(I386_CPU) || defined(CYRIX_486DLC) extern int cpu; #endif #define PV_FREELIST_MIN ((PAGE_SIZE / sizeof (struct pv_entry)) / 2) /* * Data for the pv entry allocation mechanism */ static int pv_freelistcnt; -static pv_entry_t pv_freelist; +TAILQ_HEAD (,pv_entry) pv_freelist; static vm_offset_t pvva; static int npvvapg; /* * All those kernel PT submaps that BSD is so fond of */ pt_entry_t *CMAP1; static pt_entry_t *CMAP2, *ptmmap; -static pv_entry_t *pv_table; caddr_t CADDR1, ptvmmap; static caddr_t CADDR2; static pt_entry_t *msgbufmap; struct msgbuf *msgbufp; pt_entry_t *PMAP1; unsigned *PADDR1; static void free_pv_entry __P((pv_entry_t pv)); -static __inline unsigned * get_ptbase __P((pmap_t pmap)); +static unsigned * get_ptbase __P((pmap_t pmap)); static pv_entry_t get_pv_entry __P((void)); static void i386_protection_init __P((void)); static void pmap_alloc_pv_entry __P((void)); static void pmap_changebit __P((vm_offset_t pa, int bit, boolean_t setem)); static int pmap_is_managed __P((vm_offset_t pa)); static void pmap_remove_all __P((vm_offset_t pa)); -static void pmap_enter_quick __P((pmap_t pmap, vm_offset_t va, - vm_offset_t pa)); +static vm_page_t pmap_enter_quick __P((pmap_t pmap, vm_offset_t va, + vm_offset_t pa, vm_page_t mpte)); static int pmap_remove_pte __P((struct pmap *pmap, unsigned *ptq, vm_offset_t sva)); static void pmap_remove_page __P((struct pmap *pmap, vm_offset_t va)); -static __inline int pmap_remove_entry __P((struct pmap *pmap, pv_entry_t *pv, +static int pmap_remove_entry __P((struct pmap *pmap, pv_table_t *pv, vm_offset_t va)); static boolean_t pmap_testbit __P((vm_offset_t pa, int bit)); -static __inline void pmap_insert_entry __P((pmap_t pmap, vm_offset_t va, +static void pmap_insert_entry __P((pmap_t pmap, vm_offset_t va, vm_page_t mpte, vm_offset_t pa)); -static __inline vm_page_t pmap_allocpte __P((pmap_t pmap, vm_offset_t va)); +static vm_page_t pmap_allocpte __P((pmap_t pmap, vm_offset_t va)); -static __inline int pmap_release_free_page __P((pmap_t pmap, vm_page_t p)); -static vm_page_t _pmap_allocpte __P((pmap_t pmap, int ptepindex)); +static int pmap_release_free_page __P((pmap_t pmap, vm_page_t p)); +static vm_page_t _pmap_allocpte __P((pmap_t pmap, unsigned ptepindex)); +static unsigned * pmap_pte_quick __P((pmap_t pmap, vm_offset_t va)); +static vm_page_t pmap_page_alloc __P((vm_object_t object, vm_pindex_t pindex)); +static PMAP_INLINE void pmap_lock __P((pmap_t pmap)); +static PMAP_INLINE void pmap_unlock __P((pmap_t pmap)); +static void pmap_lock2 __P((pmap_t pmap1, pmap_t pmap2)); -#define VATRACK 4 -#define PDSTACKMAX 16 +#define PDSTACKMAX 6 static vm_offset_t pdstack[PDSTACKMAX]; static int pdstackptr; /* * Bootstrap the system enough to run with virtual memory. * * On the i386 this is called after mapping has already been enabled * and just syncs the pmap module with what has already been done. * [We can't call it easily with mapping off since the kernel is not * mapped with PA == VA, hence we would have to relocate every address * from the linked base (virtual) address "KERNBASE" to the actual * (physical) address starting relative to 0] */ void pmap_bootstrap(firstaddr, loadaddr) vm_offset_t firstaddr; vm_offset_t loadaddr; { vm_offset_t va; pt_entry_t *pte; avail_start = firstaddr; /* * XXX The calculation of virtual_avail is wrong. It's NKPT*PAGE_SIZE too * large. It should instead be correctly calculated in locore.s and * not based on 'first' (which is a physical address, not a virtual * address, for the start of unused physical memory). The kernel * page tables are NOT double mapped and thus should not be included * in this calculation. */ virtual_avail = (vm_offset_t) KERNBASE + firstaddr; virtual_end = VM_MAX_KERNEL_ADDRESS; /* * Initialize protection array. */ i386_protection_init(); /* * The kernel's pmap is statically allocated so we don't have to use * pmap_create, which is unlikely to work correctly at this part of * the boot sequence (XXX and which no longer exists). */ kernel_pmap = &kernel_pmap_store; kernel_pmap->pm_pdir = (pd_entry_t *) (KERNBASE + IdlePTD); kernel_pmap->pm_count = 1; + TAILQ_INIT(&kernel_pmap->pm_pvlist); nkpt = NKPT; /* * Reserve some special page table entries/VA space for temporary * mapping of pages. */ #define SYSMAP(c, p, v, n) \ v = (c)va; va += ((n)*PAGE_SIZE); p = pte; pte += (n); va = virtual_avail; pte = (pt_entry_t *) pmap_pte(kernel_pmap, va); /* * CMAP1/CMAP2 are used for zeroing and copying pages. */ SYSMAP(caddr_t, CMAP1, CADDR1, 1) SYSMAP(caddr_t, CMAP2, CADDR2, 1) /* * ptmmap is used for reading arbitrary physical pages via /dev/mem. */ SYSMAP(caddr_t, ptmmap, ptvmmap, 1) /* * msgbufmap is used to map the system message buffer. */ SYSMAP(struct msgbuf *, msgbufmap, msgbufp, 1) /* * ptemap is used for pmap_pte_quick */ SYSMAP(unsigned *, PMAP1, PADDR1, 1); virtual_avail = va; *(int *) CMAP1 = *(int *) CMAP2 = *(int *) PTD = 0; pmap_update(); } /* * Initialize the pmap module. * Called by vm_init, to initialize any structures that the pmap * system needs to map virtual memory. * pmap_init has been enhanced to support in a fairly consistant * way, discontiguous physical memory. */ void pmap_init(phys_start, phys_end) vm_offset_t phys_start, phys_end; { vm_offset_t addr; vm_size_t npg, s; int i; /* * calculate the number of pv_entries needed */ vm_first_phys = phys_avail[0]; for (i = 0; phys_avail[i + 1]; i += 2); npg = (phys_avail[(i - 2) + 1] - vm_first_phys) / PAGE_SIZE; /* * Allocate memory for random pmap data structures. Includes the * pv_head_table. */ - s = (vm_size_t) (sizeof(struct pv_entry *) * npg); + s = (vm_size_t) (sizeof(pv_table_t) * npg); s = round_page(s); + addr = (vm_offset_t) kmem_alloc(kernel_map, s); - pv_table = (pv_entry_t *) addr; + pv_table = (pv_table_t *) addr; + for(i=0;i= clean_eva)) return 1; else return 0; } /* * The below are finer grained pmap_update routines. These eliminate * the gratuitious tlb flushes on non-i386 architectures. */ -static __inline void +static PMAP_INLINE void pmap_update_1pg( vm_offset_t va) { #if defined(I386_CPU) || defined(CYRIX_486DLC) /* CYRIX Bug? */ if (cpu_class == CPUCLASS_386 || cpu == CPU_486DLC) pmap_update(); else #endif __asm __volatile(".byte 0xf,0x1,0x38": :"a" (va)); } -static __inline void +static PMAP_INLINE void pmap_update_2pg( vm_offset_t va1, vm_offset_t va2) { #if defined(I386_CPU) || defined(CYRIX_486DLC) /* CYRIX Bug? */ if (cpu_class == CPUCLASS_386 || cpu == CPU_486DLC) { pmap_update(); } else #endif { __asm __volatile(".byte 0xf,0x1,0x38": :"a" (va1)); __asm __volatile(".byte 0xf,0x1,0x38": :"a" (va2)); } } -static __pure unsigned * + +static PMAP_INLINE void +pmap_lock(pmap) +pmap_t pmap; +{ + int s; + if (pmap == kernel_pmap) + return; + s = splhigh(); + while (pmap->pm_flags & PM_FLAG_LOCKED) { + pmap->pm_flags |= PM_FLAG_WANTED; + tsleep(pmap, PVM - 1, "pmaplk", 0); + } + splx(s); +} + +static PMAP_INLINE void +pmap_unlock(pmap) +pmap_t pmap; +{ + int s; + if (pmap == kernel_pmap) + return; + s = splhigh(); + pmap->pm_flags &= ~PM_FLAG_LOCKED; + if (pmap->pm_flags & PM_FLAG_WANTED) { + pmap->pm_flags &= ~PM_FLAG_WANTED; + wakeup(pmap); + } +} + +static void +pmap_lock2(pmap1, pmap2) +pmap_t pmap1, pmap2; +{ + int s; + if (pmap1 == kernel_pmap || pmap2 == kernel_pmap) + return; + s = splhigh(); + while ((pmap1->pm_flags | pmap2->pm_flags) & PM_FLAG_LOCKED) { + while (pmap1->pm_flags & PM_FLAG_LOCKED) { + pmap1->pm_flags |= PM_FLAG_WANTED; + tsleep(pmap1, PVM - 1, "pmapl1", 0); + } + while (pmap2->pm_flags & PM_FLAG_LOCKED) { + pmap2->pm_flags |= PM_FLAG_WANTED; + tsleep(pmap2, PVM - 1, "pmapl2", 0); + } + } + splx(s); +} + +static unsigned * get_ptbase(pmap) pmap_t pmap; { unsigned frame = (unsigned) pmap->pm_pdir[PTDPTDI] & PG_FRAME; /* are we current address space or kernel? */ if (pmap == kernel_pmap || frame == (((unsigned) PTDpde) & PG_FRAME)) { return (unsigned *) PTmap; } /* otherwise, we are alternate address space */ if (frame != (((unsigned) APTDpde) & PG_FRAME)) { APTDpde = (pd_entry_t) (frame | PG_RW | PG_V); pmap_update(); } return (unsigned *) APTmap; } /* * Routine: pmap_pte * Function: * Extract the page table entry associated * with the given map/virtual_address pair. */ -__inline unsigned * __pure +PMAP_INLINE unsigned * pmap_pte(pmap, va) register pmap_t pmap; vm_offset_t va; { if (pmap && *pmap_pde(pmap, va)) { return get_ptbase(pmap) + i386_btop(va); } return (0); } /* * Super fast pmap_pte routine best used when scanning * the pv lists. This eliminates many coarse-grained - * pmap_update calls. + * pmap_update calls. Note that many of the pv list + * scans are across different pmaps. It is very wasteful + * to do an entire pmap_update for checking a single mapping. */ -__inline unsigned * __pure + +unsigned * pmap_pte_quick(pmap, va) register pmap_t pmap; vm_offset_t va; { - unsigned pde; + unsigned pde, newpf; if (pde = (unsigned) pmap->pm_pdir[va >> PDRSHIFT]) { unsigned frame = (unsigned) pmap->pm_pdir[PTDPTDI] & PG_FRAME; + unsigned index = i386_btop(va); /* are we current address space or kernel? */ - if (pmap == kernel_pmap || frame == (((unsigned) PTDpde) & PG_FRAME)) { - return (unsigned *) PTmap + i386_btop(va); + if ((pmap == kernel_pmap) || + (frame == (((unsigned) PTDpde) & PG_FRAME))) { + return (unsigned *) PTmap + index; } - * (int *) PMAP1 = (pde & PG_FRAME) | PG_V | PG_RW; - pmap_update_1pg((vm_offset_t) PADDR1); - return PADDR1 + ((unsigned) i386_btop(va) & (NPTEPG - 1)); + newpf = pde & PG_FRAME; + if ( ((* (unsigned *) PMAP1) & PG_FRAME) != newpf) { + * (unsigned *) PMAP1 = newpf | PG_RW | PG_V; + pmap_update_1pg((vm_offset_t) PADDR1); + } + return PADDR1 + ((unsigned) index & (NPTEPG - 1)); } return (0); } - /* * Routine: pmap_extract * Function: * Extract the physical page address associated * with the given map/virtual_address pair. */ -vm_offset_t __pure +vm_offset_t pmap_extract(pmap, va) register pmap_t pmap; vm_offset_t va; { + vm_offset_t rtval; + pmap_lock(pmap); if (pmap && *pmap_pde(pmap, va)) { unsigned *pte; pte = get_ptbase(pmap) + i386_btop(va); - return ((*pte & PG_FRAME) | (va & PAGE_MASK)); + rtval = ((*pte & PG_FRAME) | (va & PAGE_MASK)); + pmap_unlock(pmap); + return rtval; } + pmap_unlock(pmap); return 0; } /* * determine if a page is managed (memory vs. device) */ -static __inline __pure int +static PMAP_INLINE int pmap_is_managed(pa) vm_offset_t pa; { int i; if (!pmap_initialized) return 0; for (i = 0; phys_avail[i + 1]; i += 2) { if (pa < phys_avail[i + 1] && pa >= phys_avail[i]) return 1; } return 0; } + /*************************************************** * Low level mapping routines..... ***************************************************/ /* * Add a list of wired pages to the kva * this routine is only used for temporary * kernel mappings that do not need to have * page modification or references recorded. * Note that old mappings are simply written * over. The page *must* be wired. */ void pmap_qenter(va, m, count) vm_offset_t va; vm_page_t *m; int count; { int i; register unsigned *pte; for (i = 0; i < count; i++) { vm_offset_t tva = va + i * PAGE_SIZE; unsigned npte = VM_PAGE_TO_PHYS(m[i]) | PG_RW | PG_V; unsigned opte; pte = (unsigned *)vtopte(tva); opte = *pte; *pte = npte; if (opte) pmap_update_1pg(tva); } } + /* * this routine jerks page mappings from the * kernel -- it is meant only for temporary mappings. */ void pmap_qremove(va, count) vm_offset_t va; int count; { int i; register unsigned *pte; for (i = 0; i < count; i++) { pte = (unsigned *)vtopte(va); *pte = 0; pmap_update_1pg(va); va += PAGE_SIZE; } } /* * add a wired page to the kva * note that in order for the mapping to take effect -- you * should do a pmap_update after doing the pmap_kenter... */ -__inline void +PMAP_INLINE void pmap_kenter(va, pa) vm_offset_t va; register vm_offset_t pa; { register unsigned *pte; unsigned npte, opte; npte = pa | PG_RW | PG_V; pte = (unsigned *)vtopte(va); opte = *pte; *pte = npte; if (opte) pmap_update_1pg(va); } /* * remove a page from the kernel pagetables */ -__inline void +PMAP_INLINE void pmap_kremove(va) vm_offset_t va; { register unsigned *pte; pte = (unsigned *)vtopte(va); *pte = 0; pmap_update_1pg(va); } +static vm_page_t +pmap_page_alloc(object, pindex) + vm_object_t object; + vm_pindex_t pindex; +{ + vm_page_t m; + m = vm_page_alloc(object, pindex, VM_ALLOC_ZERO); + if (m == NULL) { + VM_WAIT; + } + return m; +} +vm_page_t +pmap_page_lookup(object, pindex) + vm_object_t object; + vm_pindex_t pindex; +{ + vm_page_t m; +retry: + m = vm_page_lookup(object, pindex); + if (m) { + if (m->flags & PG_BUSY) { + m->flags |= PG_WANTED; + tsleep(m, PVM, "pplookp", 0); + goto retry; + } + } + + return m; +} + + + + /*************************************************** * Page table page management routines..... ***************************************************/ /* * This routine unholds page table pages, and if the hold count * drops to zero, then it decrements the wire count. */ -static __inline int +static int pmap_unwire_pte_hold(pmap_t pmap, vm_page_t m) { + int s; + vm_page_unhold(m); + + s = splvm(); + while (m->flags & PG_BUSY) { + m->flags |= PG_WANTED; + tsleep(m, PVM, "pmuwpt", 0); + } + splx(s); + if (m->hold_count == 0) { vm_offset_t pteva; /* * unmap the page table page */ pmap->pm_pdir[m->pindex] = 0; --pmap->pm_stats.resident_count; + if ((((unsigned)pmap->pm_pdir[PTDPTDI]) & PG_FRAME) == + (((unsigned) PTDpde) & PG_FRAME)) { + /* + * Do a pmap_update to make the invalidated mapping + * take effect immediately. + */ + pteva = UPT_MIN_ADDRESS + i386_ptob(m->pindex); + pmap_update_1pg(pteva); + } + +#if defined(PTPHINT) + if (pmap->pm_ptphint == m) + pmap->pm_ptphint = NULL; +#endif + /* - * Do a pmap_update to make the invalidated mapping - * take effect immediately. - */ - pteva = UPT_MIN_ADDRESS + i386_ptob(m->pindex); - pmap_update_1pg(pteva); - /* * If the page is finally unwired, simply free it. */ --m->wire_count; if (m->wire_count == 0) { + + if (m->flags & PG_WANTED) { + m->flags &= ~PG_WANTED; + wakeup(m); + } + vm_page_free_zero(m); --cnt.v_wire_count; } return 1; } return 0; } /* * After removing a page table entry, this routine is used to * conditionally free the page, and manage the hold/wire counts. */ int pmap_unuse_pt(pmap, va, mpte) pmap_t pmap; vm_offset_t va; vm_page_t mpte; { + unsigned ptepindex; if (va >= UPT_MIN_ADDRESS) return 0; if (mpte == NULL) { - vm_offset_t ptepa; - ptepa = ((vm_offset_t) *pmap_pde(pmap, va)); -#if defined(PMAP_DIAGNOSTIC) - if (!ptepa) - panic("pmap_unuse_pt: pagetable page missing, va: 0x%x", va); + ptepindex = (va >> PDRSHIFT); +#if defined(PTPHINT) + if (pmap->pm_ptphint && + (pmap->pm_ptphint->pindex == ptepindex)) { + mpte = pmap->pm_ptphint; + } else { + mpte = pmap_page_lookup( pmap->pm_pteobj, ptepindex); + pmap->pm_ptphint = mpte; + } +#else + mpte = pmap_page_lookup( pmap->pm_pteobj, ptepindex); #endif - if (!ptepa) - return 0; - mpte = PHYS_TO_VM_PAGE(ptepa); } -#if defined(PMAP_DIAGNOSTIC) - if (mpte->pindex != (va >> PDRSHIFT)) - panic("pmap_unuse_pt: pindex(0x%x) != va(0x%x)", - mpte->pindex, (va >> PDRSHIFT)); - - if (mpte->hold_count == 0) { - panic("pmap_unuse_pt: hold count < 0, va: 0x%x", va); - } -#endif - return pmap_unwire_pte_hold(pmap, mpte); } /* * Initialize a preallocated and zeroed pmap structure, * such as one in a vmspace structure. */ void pmap_pinit(pmap) register struct pmap *pmap; { vm_page_t ptdpg; /* * No need to allocate page table space yet but we do need a valid * page directory table. */ if (pdstackptr > 0) { --pdstackptr; pmap->pm_pdir = (pd_entry_t *)pdstack[pdstackptr]; } else { pmap->pm_pdir = (pd_entry_t *)kmem_alloc_pageable(kernel_map, PAGE_SIZE); } /* * allocate object for the ptes */ pmap->pm_pteobj = vm_object_allocate( OBJT_DEFAULT, PTDPTDI + 1); /* * allocate the page directory page */ retry: - ptdpg = vm_page_alloc( pmap->pm_pteobj, PTDPTDI, VM_ALLOC_ZERO); - if (ptdpg == NULL) { - VM_WAIT; + ptdpg = pmap_page_alloc( pmap->pm_pteobj, PTDPTDI); + if (ptdpg == NULL) goto retry; - } - vm_page_wire(ptdpg); + + ptdpg->wire_count = 1; + ++cnt.v_wire_count; + ptdpg->flags &= ~(PG_MAPPED|PG_BUSY); /* not mapped normally */ ptdpg->valid = VM_PAGE_BITS_ALL; pmap_kenter((vm_offset_t) pmap->pm_pdir, VM_PAGE_TO_PHYS(ptdpg)); if ((ptdpg->flags & PG_ZERO) == 0) bzero(pmap->pm_pdir, PAGE_SIZE); /* wire in kernel global address entries */ bcopy(PTD + KPTDI, pmap->pm_pdir + KPTDI, nkpt * PTESIZE); /* install self-referential address mapping entry */ *(unsigned *) (pmap->pm_pdir + PTDPTDI) = VM_PAGE_TO_PHYS(ptdpg) | PG_V | PG_RW; + pmap->pm_flags = 0; pmap->pm_count = 1; + pmap->pm_ptphint = NULL; + TAILQ_INIT(&pmap->pm_pvlist); } static int pmap_release_free_page(pmap, p) struct pmap *pmap; vm_page_t p; { int s; unsigned *pde = (unsigned *) pmap->pm_pdir; /* * This code optimizes the case of freeing non-busy * page-table pages. Those pages are zero now, and * might as well be placed directly into the zero queue. */ s = splvm(); if (p->flags & PG_BUSY) { p->flags |= PG_WANTED; tsleep(p, PVM, "pmaprl", 0); splx(s); return 0; } + if (p->flags & PG_WANTED) { + p->flags &= ~PG_WANTED; + wakeup(p); + } + /* * Remove the page table page from the processes address space. */ pde[p->pindex] = 0; --pmap->pm_stats.resident_count; if (p->hold_count) { - int *kvap; - int i; -#if defined(PMAP_DIAGNOSTIC) panic("pmap_release: freeing held page table page"); -#else - printf("pmap_release: freeing held page table page:\n"); -#endif - kvap = (int *)vm_pager_map_page(p); - for(i=0;ipindex == PTDPTDI) { bzero(pde + KPTDI, nkpt * PTESIZE); pde[APTDPTDI] = 0; pmap_kremove((vm_offset_t) pmap->pm_pdir); } +#if defined(PTPHINT) + if (pmap->pm_ptphint && + (pmap->pm_ptphint->pindex == p->pindex)) + pmap->pm_ptphint = NULL; +#endif + vm_page_free_zero(p); splx(s); return 1; } /* * this routine is called if the page table page is not * mapped correctly. */ static vm_page_t _pmap_allocpte(pmap, ptepindex) pmap_t pmap; - int ptepindex; + unsigned ptepindex; { vm_offset_t pteva, ptepa; vm_page_t m; + int needszero = 0; /* * Find or fabricate a new pagetable page */ retry: m = vm_page_lookup(pmap->pm_pteobj, ptepindex); if (m == NULL) { - m = vm_page_alloc(pmap->pm_pteobj, ptepindex, VM_ALLOC_ZERO); - if (m == NULL) { - VM_WAIT; + m = pmap_page_alloc(pmap->pm_pteobj, ptepindex); + if (m == NULL) goto retry; - } if ((m->flags & PG_ZERO) == 0) - pmap_zero_page(VM_PAGE_TO_PHYS(m)); + needszero = 1; m->flags &= ~(PG_ZERO|PG_BUSY); m->valid = VM_PAGE_BITS_ALL; } else { if ((m->flags & PG_BUSY) || m->busy) { m->flags |= PG_WANTED; tsleep(m, PVM, "ptewai", 0); goto retry; } } - /* - * mark the object writeable - */ - pmap->pm_pteobj->flags |= OBJ_WRITEABLE; - if (m->queue != PQ_NONE) { int s = splvm(); vm_page_unqueue(m); splx(s); } - if (m->hold_count == 0) { - if (m->wire_count == 0) - ++cnt.v_wire_count; - ++m->wire_count; - } + if (m->wire_count == 0) + ++cnt.v_wire_count; + ++m->wire_count; + /* * Increment the hold count for the page table page * (denoting a new mapping.) */ ++m->hold_count; /* * Map the pagetable page into the process address space, if * it isn't already there. */ pmap->pm_stats.resident_count++; ptepa = VM_PAGE_TO_PHYS(m); pmap->pm_pdir[ptepindex] = (pd_entry_t) (ptepa | PG_U | PG_RW | PG_V); - pteva = UPT_MIN_ADDRESS + i386_ptob(ptepindex); - pmap_update_1pg(pteva); +#if defined(PTPHINT) + /* + * Set the page table hint + */ + pmap->pm_ptphint = m; +#endif + + /* + * Try to use the new mapping, but if we cannot, then + * do it with the routine that maps the page explicitly. + */ + if (needszero) { + if ((((unsigned)pmap->pm_pdir[PTDPTDI]) & PG_FRAME) == + (((unsigned) PTDpde) & PG_FRAME)) { + pteva = UPT_MIN_ADDRESS + i386_ptob(ptepindex); + bzero((caddr_t) pteva, PAGE_SIZE); + } else { + pmap_zero_page(ptepa); + } + } + + m->valid = VM_PAGE_BITS_ALL; m->flags |= PG_MAPPED; return m; } -static __inline vm_page_t +static vm_page_t pmap_allocpte(pmap, va) pmap_t pmap; vm_offset_t va; { - int ptepindex; + unsigned ptepindex; vm_offset_t ptepa; vm_page_t m; /* * Calculate pagetable page index */ ptepindex = va >> PDRSHIFT; /* * Get the page directory entry */ ptepa = (vm_offset_t) pmap->pm_pdir[ptepindex]; /* * If the page table page is mapped, we just increment the * hold count, and activate it. */ if (ptepa) { - m = PHYS_TO_VM_PAGE(ptepa); +#if defined(PTPHINT) + /* + * In order to get the page table page, try the + * hint first. + */ + if (pmap->pm_ptphint && + (pmap->pm_ptphint->pindex == ptepindex)) { + m = pmap->pm_ptphint; + } else { + m = pmap_page_lookup( pmap->pm_pteobj, ptepindex); + pmap->pm_ptphint = m; + } +#else + m = pmap_page_lookup( pmap->pm_pteobj, ptepindex); +#endif ++m->hold_count; return m; } /* * Here if the pte page isn't mapped, or if it has been deallocated. */ return _pmap_allocpte(pmap, ptepindex); } /*************************************************** * Pmap allocation/deallocation routines. ***************************************************/ /* * Release any resources held by the given physical map. * Called when a pmap initialized by pmap_pinit is being released. * Should only be called if the map contains no valid mappings. */ void pmap_release(pmap) register struct pmap *pmap; { vm_page_t p,n,ptdpg; vm_object_t object = pmap->pm_pteobj; if (object->ref_count != 1) panic("pmap_release: pteobj reference count != 1"); + pmap_lock(pmap); ptdpg = NULL; retry: for (p = TAILQ_FIRST(&object->memq); p != NULL; p = n) { n = TAILQ_NEXT(p, listq); if (p->pindex == PTDPTDI) { ptdpg = p; continue; } if (!pmap_release_free_page(pmap, p)) goto retry; } - if (ptdpg == NULL) - panic("pmap_release: missing page table directory page"); - if (!pmap_release_free_page(pmap, ptdpg)) + if (ptdpg && !pmap_release_free_page(pmap, ptdpg)) goto retry; vm_object_deallocate(object); if (pdstackptr < PDSTACKMAX) { pdstack[pdstackptr] = (vm_offset_t) pmap->pm_pdir; ++pdstackptr; } else { kmem_free(kernel_map, (vm_offset_t) pmap->pm_pdir, PAGE_SIZE); } + pmap->pm_pdir = 0; + pmap_update(); } /* * grow the number of kernel page table entries, if needed */ void pmap_growkernel(vm_offset_t addr) { struct proc *p; struct pmap *pmap; int s; s = splhigh(); if (kernel_vm_end == 0) { kernel_vm_end = KERNBASE; nkpt = 0; while (pdir_pde(PTD, kernel_vm_end)) { kernel_vm_end = (kernel_vm_end + PAGE_SIZE * NPTEPG) & ~(PAGE_SIZE * NPTEPG - 1); ++nkpt; } } addr = (addr + PAGE_SIZE * NPTEPG) & ~(PAGE_SIZE * NPTEPG - 1); while (kernel_vm_end < addr) { if (pdir_pde(PTD, kernel_vm_end)) { kernel_vm_end = (kernel_vm_end + PAGE_SIZE * NPTEPG) & ~(PAGE_SIZE * NPTEPG - 1); continue; } ++nkpt; if (!nkpg) { - nkpg = vm_page_alloc(kernel_object, 0, VM_ALLOC_SYSTEM); + vm_offset_t ptpkva = (vm_offset_t) vtopte(addr); + /* + * This index is bogus, but out of the way + */ + vm_pindex_t ptpidx = (ptpkva >> PAGE_SHIFT); + nkpg = vm_page_alloc(kernel_object, + ptpidx, VM_ALLOC_SYSTEM); if (!nkpg) panic("pmap_growkernel: no memory to grow kernel"); vm_page_wire(nkpg); vm_page_remove(nkpg); pmap_zero_page(VM_PAGE_TO_PHYS(nkpg)); } pdir_pde(PTD, kernel_vm_end) = (pd_entry_t) (VM_PAGE_TO_PHYS(nkpg) | PG_V | PG_RW); nkpg = NULL; for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) { if (p->p_vmspace) { pmap = &p->p_vmspace->vm_pmap; *pmap_pde(pmap, kernel_vm_end) = pdir_pde(PTD, kernel_vm_end); } } *pmap_pde(kernel_pmap, kernel_vm_end) = pdir_pde(PTD, kernel_vm_end); kernel_vm_end = (kernel_vm_end + PAGE_SIZE * NPTEPG) & ~(PAGE_SIZE * NPTEPG - 1); } splx(s); } /* * Retire the given physical map from service. * Should only be called if the map contains * no valid mappings. */ void pmap_destroy(pmap) register pmap_t pmap; { int count; if (pmap == NULL) return; count = --pmap->pm_count; if (count == 0) { pmap_release(pmap); free((caddr_t) pmap, M_VMPMAP); } } /* * Add a reference to the specified pmap. */ void pmap_reference(pmap) pmap_t pmap; { if (pmap != NULL) { pmap->pm_count++; } } /*************************************************** * page management routines. ***************************************************/ /* * free the pv_entry back to the free list */ -static __inline void +static PMAP_INLINE void free_pv_entry(pv) pv_entry_t pv; { ++pv_freelistcnt; - pv->pv_next = pv_freelist; - pv_freelist = pv; + TAILQ_INSERT_HEAD(&pv_freelist, pv, pv_list); } /* * get a new pv_entry, allocating a block from the system * when needed. * the memory allocation is performed bypassing the malloc code * because of the possibility of allocations at interrupt time. */ -static __inline pv_entry_t +static pv_entry_t get_pv_entry() { pv_entry_t tmp; /* * get more pv_entry pages if needed */ - if (pv_freelistcnt < PV_FREELIST_MIN || pv_freelist == 0) { + if (pv_freelistcnt < PV_FREELIST_MIN || !TAILQ_FIRST(&pv_freelist)) { pmap_alloc_pv_entry(); } /* * get a pv_entry off of the free list */ --pv_freelistcnt; - tmp = pv_freelist; - pv_freelist = tmp->pv_next; + tmp = TAILQ_FIRST(&pv_freelist); + TAILQ_REMOVE(&pv_freelist, tmp, pv_list); return tmp; } /* * This *strange* allocation routine eliminates the possibility of a malloc * failure (*FATAL*) for a pv_entry_t data structure. * also -- this code is MUCH MUCH faster than the malloc equiv... * We really need to do the slab allocator thingie here. */ static void pmap_alloc_pv_entry() { /* * do we have any pre-allocated map-pages left? */ if (npvvapg) { vm_page_t m; /* * allocate a physical page out of the vm system */ m = vm_page_alloc(kernel_object, OFF_TO_IDX(pvva - vm_map_min(kernel_map)), VM_ALLOC_INTERRUPT); if (m) { int newentries; int i; pv_entry_t entry; newentries = (PAGE_SIZE / sizeof(struct pv_entry)); /* * wire the page */ vm_page_wire(m); m->flags &= ~PG_BUSY; /* * let the kernel see it */ pmap_kenter(pvva, VM_PAGE_TO_PHYS(m)); entry = (pv_entry_t) pvva; /* * update the allocation pointers */ pvva += PAGE_SIZE; --npvvapg; /* * free the entries into the free list */ for (i = 0; i < newentries; i++) { free_pv_entry(entry); entry++; } } } - if (!pv_freelist) + if (!TAILQ_FIRST(&pv_freelist)) panic("get_pv_entry: cannot get a pv_entry_t"); } /* * init the pv_entry allocation system */ #define PVSPERPAGE 64 void init_pv_entries(npg) int npg; { /* * allocate enough kvm space for PVSPERPAGE entries per page (lots) * kvm space is fairly cheap, be generous!!! (the system can panic if * this is too small.) */ npvvapg = ((npg * PVSPERPAGE) * sizeof(struct pv_entry) + PAGE_SIZE - 1) / PAGE_SIZE; pvva = kmem_alloc_pageable(kernel_map, npvvapg * PAGE_SIZE); /* * get the first batch of entries */ pmap_alloc_pv_entry(); } /* * If it is the first entry on the list, it is actually * in the header and we must copy the following entry up * to the header. Otherwise we must search the list for * the entry. In either case we free the now unused entry. */ -static __inline int + +static int pmap_remove_entry(pmap, ppv, va) struct pmap *pmap; - pv_entry_t *ppv; + pv_table_t *ppv; vm_offset_t va; { - pv_entry_t npv; + pv_entry_t pv; + int rtval; int s; s = splvm(); - for (npv = *ppv; npv; (ppv = &npv->pv_next, npv = *ppv)) { - if (pmap == npv->pv_pmap && va == npv->pv_va) { - int rtval = pmap_unuse_pt(pmap, va, npv->pv_ptem); - *ppv = npv->pv_next; - free_pv_entry(npv); - splx(s); - return rtval; + if (ppv->pv_list_count < pmap->pm_stats.resident_count) { + for (pv = TAILQ_FIRST(&ppv->pv_list); + pv; + pv = TAILQ_NEXT(pv, pv_list)) { + if (pmap == pv->pv_pmap && va == pv->pv_va) + break; } + } else { + for (pv = TAILQ_FIRST(&pmap->pm_pvlist); + pv; + pv = TAILQ_NEXT(pv, pv_plist)) { + if (va == pv->pv_va) + break; + } } + + rtval = 0; + if (pv) { + rtval = pmap_unuse_pt(pmap, va, pv->pv_ptem); + TAILQ_REMOVE(&ppv->pv_list, pv, pv_list); + --ppv->pv_list_count; + TAILQ_REMOVE(&pmap->pm_pvlist, pv, pv_plist); + free_pv_entry(pv); + } + splx(s); - return 0; + return rtval; } /* * Create a pv entry for page at pa for * (pmap, va). */ -static __inline void +static void pmap_insert_entry(pmap, va, mpte, pa) pmap_t pmap; vm_offset_t va; vm_page_t mpte; vm_offset_t pa; { int s; - pv_entry_t *ppv, pv; + pv_entry_t pv; + pv_table_t *ppv; s = splvm(); pv = get_pv_entry(); pv->pv_va = va; pv->pv_pmap = pmap; pv->pv_ptem = mpte; + TAILQ_INSERT_TAIL(&pmap->pm_pvlist, pv, pv_plist); + ppv = pa_to_pvh(pa); - if (*ppv) - pv->pv_next = *ppv; - else - pv->pv_next = NULL; - *ppv = pv; + TAILQ_INSERT_TAIL(&ppv->pv_list, pv, pv_list); + ++ppv->pv_list_count; + splx(s); } /* * pmap_remove_pte: do the things to unmap a page in a process */ static int pmap_remove_pte(pmap, ptq, va) struct pmap *pmap; unsigned *ptq; vm_offset_t va; { unsigned oldpte; - pv_entry_t *ppv; + pv_table_t *ppv; oldpte = *ptq; *ptq = 0; if (oldpte & PG_W) pmap->pm_stats.wired_count -= 1; pmap->pm_stats.resident_count -= 1; if (oldpte & PG_MANAGED) { + ppv = pa_to_pvh(oldpte); if (oldpte & PG_M) { #if defined(PMAP_DIAGNOSTIC) if (pmap_nw_modified((pt_entry_t) oldpte)) { printf("pmap_remove: modified page not writable: va: 0x%lx, pte: 0x%lx\n", va, (int) oldpte); } #endif if (pmap_track_modified(va)) - PHYS_TO_VM_PAGE(oldpte)->dirty = VM_PAGE_BITS_ALL; + ppv->pv_vm_page->dirty = VM_PAGE_BITS_ALL; } - ppv = pa_to_pvh(oldpte); return pmap_remove_entry(pmap, ppv, va); } else { return pmap_unuse_pt(pmap, va, NULL); } return 0; } /* * Remove a single page from a process address space */ static void pmap_remove_page(pmap, va) struct pmap *pmap; register vm_offset_t va; { register unsigned *ptq; /* * if there is no pte for this address, just skip it!!! */ if (*pmap_pde(pmap, va) == 0) { return; } /* * get a local va for mappings for this pmap. */ ptq = get_ptbase(pmap) + i386_btop(va); if (*ptq) { (void) pmap_remove_pte(pmap, ptq, va); pmap_update_1pg(va); } return; } /* * Remove the given range of addresses from the specified map. * * It is assumed that the start and end are properly * rounded to the page size. */ void pmap_remove(pmap, sva, eva) struct pmap *pmap; register vm_offset_t sva; register vm_offset_t eva; { register unsigned *ptbase; vm_offset_t pdnxt; vm_offset_t ptpaddr; vm_offset_t sindex, eindex; - vm_page_t mpte; int anyvalid; - vm_offset_t vachanged[VATRACK]; if (pmap == NULL) return; + pmap_lock(pmap); /* * special handling of removing one page. a very * common operation and easy to short circuit some * code. */ if ((sva + PAGE_SIZE) == eva) { pmap_remove_page(pmap, sva); + pmap_unlock(pmap); return; } anyvalid = 0; /* * Get a local virtual address for the mappings that are being * worked with. */ ptbase = get_ptbase(pmap); sindex = i386_btop(sva); eindex = i386_btop(eva); for (; sindex < eindex; sindex = pdnxt) { /* * Calculate index for next page table. */ pdnxt = ((sindex + NPTEPG) & ~(NPTEPG - 1)); ptpaddr = (vm_offset_t) *pmap_pde(pmap, i386_ptob(sindex)); /* * Weed out invalid mappings. Note: we assume that the page * directory table is always allocated, and in kernel virtual. */ if (ptpaddr == 0) continue; - if (sindex < i386_btop(UPT_MIN_ADDRESS)) { /* - * get the vm_page_t for the page table page - */ - mpte = PHYS_TO_VM_PAGE(ptpaddr); - - /* - * if the pte isn't wired, just skip it. - */ - if (mpte->wire_count == 0) - continue; - } - - /* * Limit our scan to either the end of the va represented * by the current page table page, or to the end of the * range being removed. */ if (pdnxt > eindex) { pdnxt = eindex; } for ( ;sindex != pdnxt; sindex++) { vm_offset_t va; if (ptbase[sindex] == 0) { continue; } va = i386_ptob(sindex); - if (anyvalid < VATRACK) - vachanged[anyvalid] = va; anyvalid++; if (pmap_remove_pte(pmap, ptbase + sindex, va)) break; } } if (anyvalid) { - if (anyvalid <= VATRACK) { - int i; - for(i=0;ipv_next) { + while ((pv = TAILQ_FIRST(&ppv->pv_list)) != NULL) { + pmap_lock(pv->pv_pmap); pte = pmap_pte_quick(pv->pv_pmap, pv->pv_va); if (tpte = *pte) { pv->pv_pmap->pm_stats.resident_count--; *pte = 0; if (tpte & PG_W) pv->pv_pmap->pm_stats.wired_count--; /* * Update the vm_page_t clean and reference bits. */ if (tpte & PG_M) { #if defined(PMAP_DIAGNOSTIC) if (pmap_nw_modified((pt_entry_t) tpte)) { printf("pmap_remove_all: modified page not writable: va: 0x%lx, pte: 0x%lx\n", pv->pv_va, tpte); } #endif - if (pmap_track_modified(pv->pv_va)) { - if (m == NULL) - m = PHYS_TO_VM_PAGE(pa); - m->dirty = VM_PAGE_BITS_ALL; - } + if (pmap_track_modified(pv->pv_va)) + ppv->pv_vm_page->dirty = VM_PAGE_BITS_ALL; } + if (!update_needed && + ((!curproc || (&curproc->p_vmspace->vm_pmap == pv->pv_pmap)) || + (pv->pv_pmap == kernel_pmap))) { + update_needed = 1; + } } - } - - for (pv = *ppv; pv; pv = npv) { - npv = pv->pv_next; + TAILQ_REMOVE(&pv->pv_pmap->pm_pvlist, pv, pv_plist); + TAILQ_REMOVE(&ppv->pv_list, pv, pv_list); + --ppv->pv_list_count; pmap_unuse_pt(pv->pv_pmap, pv->pv_va, pv->pv_ptem); + pmap_unlock(pv->pv_pmap); free_pv_entry(pv); } - *ppv = NULL; + + if (update_needed) + pmap_update(); splx(s); + return; } /* * Set the physical protection on the * specified range of this map as requested. */ void pmap_protect(pmap, sva, eva, prot) register pmap_t pmap; vm_offset_t sva, eva; vm_prot_t prot; { register unsigned *ptbase; vm_offset_t pdnxt; vm_offset_t ptpaddr; vm_offset_t sindex, eindex; - vm_page_t mpte; - int anyvalid; + int anychanged; if (pmap == NULL) return; if ((prot & VM_PROT_READ) == VM_PROT_NONE) { pmap_remove(pmap, sva, eva); return; } - if (prot & VM_PROT_WRITE) + if (prot & VM_PROT_WRITE) { return; + } - anyvalid = 0; + pmap_lock(pmap); + anychanged = 0; ptbase = get_ptbase(pmap); sindex = i386_btop(sva); eindex = i386_btop(eva); for (; sindex < eindex; sindex = pdnxt) { pdnxt = ((sindex + NPTEPG) & ~(NPTEPG - 1)); ptpaddr = (vm_offset_t) *pmap_pde(pmap, i386_ptob(sindex)); /* * Weed out invalid mappings. Note: we assume that the page * directory table is always allocated, and in kernel virtual. */ if (ptpaddr == 0) continue; - /* - * Skip page ranges, where the page table page isn't wired. - * If the page table page is not wired, there are no page mappings - * there. - */ - if (sindex < i386_btop(UPT_MIN_ADDRESS)) { - mpte = PHYS_TO_VM_PAGE(ptpaddr); - - if (mpte->wire_count == 0) - continue; - } - if (pdnxt > eindex) { pdnxt = eindex; } for (; sindex != pdnxt; sindex++) { unsigned pbits = ptbase[sindex]; if (pbits & PG_RW) { if (pbits & PG_M) { vm_offset_t sva = i386_ptob(sindex); if (pmap_track_modified(sva)) { vm_page_t m = PHYS_TO_VM_PAGE(pbits); m->dirty = VM_PAGE_BITS_ALL; } } ptbase[sindex] = pbits & ~(PG_M|PG_RW); - anyvalid = 1; + anychanged = 1; } } } - if (anyvalid) + pmap_unlock(pmap); + if (anychanged) pmap_update(); } /* * Insert the given physical page (p) at * the specified virtual address (v) in the * target physical map with the protection requested. * * If specified, the page will be wired down, meaning * that the related pte can not be reclaimed. * * NB: This is the only routine which MAY NOT lazy-evaluate * or lose information. That is, this routine must actually * insert this page into the given map NOW. */ void pmap_enter(pmap, va, pa, prot, wired) register pmap_t pmap; vm_offset_t va; register vm_offset_t pa; vm_prot_t prot; boolean_t wired; { register unsigned *pte; vm_offset_t opa; vm_offset_t origpte, newpte; vm_page_t mpte; if (pmap == NULL) return; + pmap_lock(pmap); va &= PG_FRAME; #ifdef PMAP_DIAGNOSTIC if (va > VM_MAX_KERNEL_ADDRESS) panic("pmap_enter: toobig"); if ((va >= UPT_MIN_ADDRESS) && (va < UPT_MAX_ADDRESS)) panic("pmap_enter: invalid to pmap_enter page table pages (va: 0x%x)", va); #endif mpte = NULL; /* * In the case that a page table page is not * resident, we are creating it here. */ if (va < UPT_MIN_ADDRESS) mpte = pmap_allocpte(pmap, va); - pte = pmap_pte_quick(pmap, va); + pte = pmap_pte(pmap, va); /* * Page Directory table entry not valid, we need a new PT page */ if (pte == NULL) { panic("pmap_enter: invalid page directory, pdir=%p, va=0x%lx\n", pmap->pm_pdir[PTDPTDI], va); } origpte = *(vm_offset_t *)pte; pa &= PG_FRAME; opa = origpte & PG_FRAME; /* * Mapping has not changed, must be protection or wiring change. */ - if (opa == pa) { + if (origpte && (opa == pa)) { /* * Wiring change, just update stats. We don't worry about * wiring PT pages as they remain resident as long as there * are valid mappings in them. Hence, if a user page is wired, * the PT page will be also. */ if (wired && ((origpte & PG_W) == 0)) pmap->pm_stats.wired_count++; else if (!wired && (origpte & PG_W)) pmap->pm_stats.wired_count--; #if defined(PMAP_DIAGNOSTIC) if (pmap_nw_modified((pt_entry_t) origpte)) { printf("pmap_enter: modified page not writable: va: 0x%lx, pte: 0x%lx\n", va, origpte); } #endif /* * We might be turning off write access to the page, * so we go ahead and sense modify status. */ if (origpte & PG_MANAGED) { vm_page_t m; if (origpte & PG_M) { if (pmap_track_modified(va)) { m = PHYS_TO_VM_PAGE(pa); m->dirty = VM_PAGE_BITS_ALL; } } pa |= PG_MANAGED; } if (mpte) --mpte->hold_count; goto validate; } /* * Mapping has changed, invalidate old range and fall through to * handle validating new mapping. */ if (opa) { int err; err = pmap_remove_pte(pmap, pte, va); if (err) panic("pmap_enter: pte vanished, va: 0x%x", va); } /* * Enter on the PV list if part of our managed memory Note that we * raise IPL while manipulating pv_table since pmap_enter can be * called at interrupt time. */ if (pmap_is_managed(pa)) { pmap_insert_entry(pmap, va, mpte, pa); pa |= PG_MANAGED; } /* * Increment counters */ pmap->pm_stats.resident_count++; if (wired) pmap->pm_stats.wired_count++; validate: /* * Now validate mapping with desired protection/wiring. */ newpte = (vm_offset_t) (pa | pte_prot(pmap, prot) | PG_V); if (wired) newpte |= PG_W; if (va < UPT_MIN_ADDRESS) newpte |= PG_U; /* * if the mapping or permission bits are different, we need * to update the pte. */ if ((origpte & ~(PG_M|PG_A)) != newpte) { *pte = newpte; if (origpte) pmap_update_1pg(va); } + pmap_unlock(pmap); } /* * this code makes some *MAJOR* assumptions: * 1. Current pmap & pmap exists. * 2. Not wired. * 3. Read access. * 4. No page table pages. * 5. Tlbflush is deferred to calling procedure. * 6. Page IS managed. * but is *MUCH* faster than pmap_enter... */ -static void -pmap_enter_quick(pmap, va, pa) +static vm_page_t +pmap_enter_quick(pmap, va, pa, mpte) register pmap_t pmap; vm_offset_t va; register vm_offset_t pa; + vm_page_t mpte; { register unsigned *pte; - vm_page_t mpte; - mpte = NULL; /* * In the case that a page table page is not * resident, we are creating it here. */ - if (va < UPT_MIN_ADDRESS) - mpte = pmap_allocpte(pmap, va); + if (va < UPT_MIN_ADDRESS) { + unsigned ptepindex; + vm_offset_t ptepa; + /* + * Calculate pagetable page index + */ + ptepindex = va >> PDRSHIFT; + if (mpte && (mpte->pindex == ptepindex)) { + ++mpte->hold_count; + } else { +retry: + /* + * Get the page directory entry + */ + ptepa = (vm_offset_t) pmap->pm_pdir[ptepindex]; + + /* + * If the page table page is mapped, we just increment + * the hold count, and activate it. + */ + if (ptepa) { +#if defined(PTPHINT) + if (pmap->pm_ptphint && + (pmap->pm_ptphint->pindex == ptepindex)) { + mpte = pmap->pm_ptphint; + } else { + mpte = pmap_page_lookup( pmap->pm_pteobj, ptepindex); + pmap->pm_ptphint = mpte; + } +#else + mpte = pmap_page_lookup( pmap->pm_pteobj, ptepindex); +#endif + if (mpte == NULL) + goto retry; + ++mpte->hold_count; + } else { + mpte = _pmap_allocpte(pmap, ptepindex); + } + } + } else { + mpte = NULL; + } + /* * This call to vtopte makes the assumption that we are * entering the page into the current pmap. In order to support * quick entry into any pmap, one would likely use pmap_pte_quick. * But that isn't as quick as vtopte. */ pte = (unsigned *)vtopte(va); if (*pte) { if (mpte) pmap_unwire_pte_hold(pmap, mpte); - return; + return 0; } /* * Enter on the PV list if part of our managed memory Note that we * raise IPL while manipulating pv_table since pmap_enter can be * called at interrupt time. */ pmap_insert_entry(pmap, va, mpte, pa); /* * Increment counters */ pmap->pm_stats.resident_count++; /* * Now validate mapping with RO protection */ *pte = pa | PG_V | PG_U | PG_MANAGED; - return; + return mpte; } #define MAX_INIT_PT (96) /* * pmap_object_init_pt preloads the ptes for a given object * into the specified pmap. This eliminates the blast of soft * faults on process startup and immediately after an mmap. */ void pmap_object_init_pt(pmap, addr, object, pindex, size, limit) pmap_t pmap; vm_offset_t addr; vm_object_t object; vm_pindex_t pindex; vm_size_t size; int limit; { vm_offset_t tmpidx; int psize; - vm_page_t p; + vm_page_t p, mpte; int objpgs; psize = i386_btop(size); if (!pmap || (object->type != OBJT_VNODE) || (limit && (psize > MAX_INIT_PT) && (object->resident_page_count > MAX_INIT_PT))) { return; } + pmap_lock(pmap); if (psize + pindex > object->size) psize = object->size - pindex; + mpte = NULL; /* * if we are processing a major portion of the object, then scan the * entire thing. */ if (psize > (object->size >> 2)) { objpgs = psize; for (p = TAILQ_FIRST(&object->memq); ((objpgs > 0) && (p != NULL)); p = TAILQ_NEXT(p, listq)) { tmpidx = p->pindex; if (tmpidx < pindex) { continue; } tmpidx -= pindex; if (tmpidx >= psize) { continue; } if (((p->valid & VM_PAGE_BITS_ALL) == VM_PAGE_BITS_ALL) && (p->busy == 0) && (p->flags & (PG_BUSY | PG_FICTITIOUS)) == 0) { - if (p->queue == PQ_CACHE) + if ((p->queue - p->pc) == PQ_CACHE) vm_page_deactivate(p); p->flags |= PG_BUSY; - pmap_enter_quick(pmap, + mpte = pmap_enter_quick(pmap, addr + i386_ptob(tmpidx), - VM_PAGE_TO_PHYS(p)); + VM_PAGE_TO_PHYS(p), mpte); p->flags |= PG_MAPPED; PAGE_WAKEUP(p); } objpgs -= 1; } } else { /* * else lookup the pages one-by-one. */ for (tmpidx = 0; tmpidx < psize; tmpidx += 1) { p = vm_page_lookup(object, tmpidx + pindex); if (p && ((p->valid & VM_PAGE_BITS_ALL) == VM_PAGE_BITS_ALL) && (p->busy == 0) && (p->flags & (PG_BUSY | PG_FICTITIOUS)) == 0) { - if (p->queue == PQ_CACHE) + if ((p->queue - p->pc) == PQ_CACHE) vm_page_deactivate(p); p->flags |= PG_BUSY; - pmap_enter_quick(pmap, + mpte = pmap_enter_quick(pmap, addr + i386_ptob(tmpidx), - VM_PAGE_TO_PHYS(p)); + VM_PAGE_TO_PHYS(p), mpte); p->flags |= PG_MAPPED; PAGE_WAKEUP(p); } } } + pmap_unlock(pmap); return; } /* * pmap_prefault provides a quick way of clustering * pagefaults into a processes address space. It is a "cousin" * of pmap_object_init_pt, except it runs at page fault time instead * of mmap time. */ #define PFBAK 2 #define PFFOR 2 #define PAGEORDER_SIZE (PFBAK+PFFOR) static int pmap_prefault_pageorder[] = { -PAGE_SIZE, PAGE_SIZE, -2 * PAGE_SIZE, 2 * PAGE_SIZE }; void pmap_prefault(pmap, addra, entry, object) pmap_t pmap; vm_offset_t addra; vm_map_entry_t entry; vm_object_t object; { int i; vm_offset_t starta; vm_offset_t addr; vm_pindex_t pindex; - vm_page_t m; + vm_page_t m, mpte; if (entry->object.vm_object != object) return; if (!curproc || (pmap != &curproc->p_vmspace->vm_pmap)) return; + pmap_lock(pmap); starta = addra - PFBAK * PAGE_SIZE; if (starta < entry->start) { starta = entry->start; } else if (starta > addra) { starta = 0; } + mpte = NULL; for (i = 0; i < PAGEORDER_SIZE; i++) { vm_object_t lobject; unsigned *pte; addr = addra + pmap_prefault_pageorder[i]; if (addr < starta || addr >= entry->end) continue; if ((*pmap_pde(pmap, addr)) == NULL) continue; pte = (unsigned *) vtopte(addr); if (*pte) continue; pindex = ((addr - entry->start) + entry->offset) >> PAGE_SHIFT; lobject = object; for (m = vm_page_lookup(lobject, pindex); (!m && (lobject->type == OBJT_DEFAULT) && (lobject->backing_object)); lobject = lobject->backing_object) { if (lobject->backing_object_offset & PAGE_MASK) break; pindex += (lobject->backing_object_offset >> PAGE_SHIFT); m = vm_page_lookup(lobject->backing_object, pindex); } /* * give-up when a page is not in memory */ if (m == NULL) break; if (((m->valid & VM_PAGE_BITS_ALL) == VM_PAGE_BITS_ALL) && (m->busy == 0) && (m->flags & (PG_BUSY | PG_FICTITIOUS)) == 0) { - if (m->queue == PQ_CACHE) { + if ((m->queue - m->pc) == PQ_CACHE) { vm_page_deactivate(m); } m->flags |= PG_BUSY; - pmap_enter_quick(pmap, addr, VM_PAGE_TO_PHYS(m)); + mpte = pmap_enter_quick(pmap, addr, + VM_PAGE_TO_PHYS(m), mpte); m->flags |= PG_MAPPED; PAGE_WAKEUP(m); } } + pmap_unlock(pmap); } /* * Routine: pmap_change_wiring * Function: Change the wiring attribute for a map/virtual-address * pair. * In/out conditions: * The mapping must already exist in the pmap. */ void pmap_change_wiring(pmap, va, wired) register pmap_t pmap; vm_offset_t va; boolean_t wired; { register unsigned *pte; if (pmap == NULL) return; + pmap_lock(pmap); pte = pmap_pte(pmap, va); if (wired && !pmap_pte_w(pte)) pmap->pm_stats.wired_count++; else if (!wired && pmap_pte_w(pte)) pmap->pm_stats.wired_count--; /* * Wiring is not a hardware characteristic so there is no need to * invalidate TLB. */ pmap_pte_set_w(pte, wired); + pmap_unlock(pmap); } /* * Copy the range specified by src_addr/len * from the source map to the range dst_addr/len * in the destination map. * * This routine is only advisory and need not do anything. */ + void pmap_copy(dst_pmap, src_pmap, dst_addr, len, src_addr) pmap_t dst_pmap, src_pmap; vm_offset_t dst_addr; vm_size_t len; vm_offset_t src_addr; { vm_offset_t addr; vm_offset_t end_addr = src_addr + len; vm_offset_t pdnxt; unsigned src_frame, dst_frame; if (dst_addr != src_addr) return; + pmap_lock2(src_pmap, dst_pmap); src_frame = ((unsigned) src_pmap->pm_pdir[PTDPTDI]) & PG_FRAME; - if (src_frame != (((unsigned) PTDpde) & PG_FRAME)) + if (src_frame != (((unsigned) PTDpde) & PG_FRAME)) { + pmap_unlock(src_pmap); + pmap_unlock(dst_pmap); return; + } dst_frame = ((unsigned) dst_pmap->pm_pdir[PTDPTDI]) & PG_FRAME; if (dst_frame != (((unsigned) APTDpde) & PG_FRAME)) { APTDpde = (pd_entry_t) (dst_frame | PG_RW | PG_V); pmap_update(); } for(addr = src_addr; addr < end_addr; addr = pdnxt) { unsigned *src_pte, *dst_pte; vm_page_t dstmpte, srcmpte; vm_offset_t srcptepaddr; + unsigned ptepindex; if (addr >= UPT_MIN_ADDRESS) panic("pmap_copy: invalid to pmap_copy page tables\n"); + pdnxt = ((addr + PAGE_SIZE*NPTEPG) & ~(PAGE_SIZE*NPTEPG - 1)); - srcptepaddr = (vm_offset_t) src_pmap->pm_pdir[addr >> PDRSHIFT]; - if (srcptepaddr == 0) { + ptepindex = addr >> PDRSHIFT; + + srcptepaddr = (vm_offset_t) src_pmap->pm_pdir[ptepindex]; + if (srcptepaddr == 0) continue; - } - srcmpte = PHYS_TO_VM_PAGE(srcptepaddr); - if (srcmpte->hold_count == 0) + srcmpte = vm_page_lookup(src_pmap->pm_pteobj, ptepindex); + if ((srcmpte->hold_count == 0) || (srcmpte->flags & PG_BUSY)) continue; if (pdnxt > end_addr) pdnxt = end_addr; src_pte = (unsigned *) vtopte(addr); dst_pte = (unsigned *) avtopte(addr); while (addr < pdnxt) { unsigned ptetemp; ptetemp = *src_pte; /* * we only virtual copy managed pages */ if ((ptetemp & PG_MANAGED) != 0) { /* * We have to check after allocpte for the * pte still being around... allocpte can * block. */ dstmpte = pmap_allocpte(dst_pmap, addr); if ((*dst_pte == 0) && (ptetemp = *src_pte)) { /* - * Simply clear the modified and accessed (referenced) - * bits. + * Clear the modified and + * accessed (referenced) bits + * during the copy. */ *dst_pte = ptetemp & ~(PG_M|PG_A); dst_pmap->pm_stats.resident_count++; - pmap_insert_entry(dst_pmap, addr, dstmpte, + pmap_insert_entry(dst_pmap, addr, + dstmpte, (ptetemp & PG_FRAME)); } else { pmap_unwire_pte_hold(dst_pmap, dstmpte); } if (dstmpte->hold_count >= srcmpte->hold_count) break; } addr += PAGE_SIZE; ++src_pte; ++dst_pte; } } + pmap_unlock(src_pmap); + pmap_unlock(dst_pmap); } /* * Routine: pmap_kernel * Function: * Returns the physical map handle for the kernel. */ pmap_t pmap_kernel() { return (kernel_pmap); } /* * pmap_zero_page zeros the specified (machine independent) * page by mapping the page into virtual memory and using * bzero to clear its contents, one machine dependent page * at a time. */ void pmap_zero_page(phys) vm_offset_t phys; { if (*(int *) CMAP2) panic("pmap_zero_page: CMAP busy"); *(int *) CMAP2 = PG_V | PG_RW | (phys & PG_FRAME); bzero(CADDR2, PAGE_SIZE); *(int *) CMAP2 = 0; pmap_update_1pg((vm_offset_t) CADDR2); } /* * pmap_copy_page copies the specified (machine independent) * page by mapping the page into virtual memory and using * bcopy to copy the page, one machine dependent page at a * time. */ void pmap_copy_page(src, dst) vm_offset_t src; vm_offset_t dst; { if (*(int *) CMAP1 || *(int *) CMAP2) panic("pmap_copy_page: CMAP busy"); *(int *) CMAP1 = PG_V | PG_RW | (src & PG_FRAME); *(int *) CMAP2 = PG_V | PG_RW | (dst & PG_FRAME); bcopy(CADDR1, CADDR2, PAGE_SIZE); *(int *) CMAP1 = 0; *(int *) CMAP2 = 0; pmap_update_2pg( (vm_offset_t) CADDR1, (vm_offset_t) CADDR2); } /* * Routine: pmap_pageable * Function: * Make the specified pages (by pmap, offset) * pageable (or not) as requested. * * A page which is not pageable may not take * a fault; therefore, its page table entry * must remain valid for the duration. * * This routine is merely advisory; pmap_enter * will specify that these pages are to be wired * down (or not) as appropriate. */ void pmap_pageable(pmap, sva, eva, pageable) pmap_t pmap; vm_offset_t sva, eva; boolean_t pageable; { } /* * this routine returns true if a physical page resides * in the given pmap. */ boolean_t pmap_page_exists(pmap, pa) pmap_t pmap; vm_offset_t pa; { - register pv_entry_t *ppv, pv; + register pv_entry_t pv; + pv_table_t *ppv; int s; if (!pmap_is_managed(pa)) return FALSE; s = splvm(); ppv = pa_to_pvh(pa); /* * Not found, check current mappings returning immediately if found. */ - for (pv = *ppv; pv; pv = pv->pv_next) { + for (pv = TAILQ_FIRST(&ppv->pv_list); + pv; + pv = TAILQ_NEXT(pv, pv_list)) { if (pv->pv_pmap == pmap) { splx(s); return TRUE; } } splx(s); return (FALSE); } +#ifdef NOT_USED_YET +#define PMAP_REMOVE_PAGES_CURPROC_ONLY /* + * Remove all pages from specified address space + * this aids process exit speeds. Also, this code + * is special cased for current process only. + */ +void +pmap_remove_pages(pmap, sva, eva) + pmap_t pmap; + vm_offset_t sva, eva; +{ + unsigned *pte, tpte; + pv_table_t *ppv; + pv_entry_t pv, npv; + int s; + +#ifdef PMAP_REMOVE_PAGES_CURPROC_ONLY + if (!curproc || (pmap != &curproc->p_vmspace->vm_pmap)) { + printf("warning: pmap_remove_pages called with non-current pmap\n"); + return; + } +#endif + + pmap_lock(pmap); + s = splhigh(); + + for(pv = TAILQ_FIRST(&pmap->pm_pvlist); + pv; + pv = npv) { + + if (pv->pv_va >= eva || pv->pv_va < sva) { + npv = TAILQ_NEXT(pv, pv_plist); + continue; + } + +#ifdef PMAP_REMOVE_PAGES_CURPROC_ONLY + pte = (unsigned *)vtopte(pv->pv_va); +#else + pte = pmap_pte_quick(pv->pv_pmap, pv->pv_va); +#endif + tpte = *pte; + *pte = 0; + + ppv = pa_to_pvh(tpte); + + if (tpte) { + pv->pv_pmap->pm_stats.resident_count--; + if (tpte & PG_W) + pv->pv_pmap->pm_stats.wired_count--; + /* + * Update the vm_page_t clean and reference bits. + */ + if (tpte & PG_M) { + ppv->pv_vm_page->dirty = VM_PAGE_BITS_ALL; + } + } + + npv = TAILQ_NEXT(pv, pv_plist); + TAILQ_REMOVE(&pv->pv_pmap->pm_pvlist, pv, pv_plist); + + --ppv->pv_list_count; + TAILQ_REMOVE(&ppv->pv_list, pv, pv_list); + + pmap_unuse_pt(pv->pv_pmap, pv->pv_va, pv->pv_ptem); + free_pv_entry(pv); + } + splx(s); + pmap_update(); + pmap_unlock(pmap); +} +#endif + +/* * 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 +static boolean_t pmap_testbit(pa, bit) register vm_offset_t pa; int bit; { - register pv_entry_t *ppv, pv; + register pv_entry_t pv; + pv_table_t *ppv; unsigned *pte; int s; if (!pmap_is_managed(pa)) return FALSE; ppv = pa_to_pvh(pa); - if (*ppv == NULL) + if (TAILQ_FIRST(&ppv->pv_list) == NULL) return FALSE; s = splvm(); - /* - * Not found, check current mappings returning immediately if found. - */ - for (pv = *ppv ;pv; pv = pv->pv_next) { + for (pv = TAILQ_FIRST(&ppv->pv_list); + pv; + pv = TAILQ_NEXT(pv, pv_list)) { + /* * if the bit being tested is the modified bit, then * mark clean_map and ptes as never * modified. */ if (bit & (PG_A|PG_M)) { if (!pmap_track_modified(pv->pv_va)) continue; } - if (!pv->pv_pmap) { #if defined(PMAP_DIAGNOSTIC) + if (!pv->pv_pmap) { printf("Null pmap (tb) at va: 0x%lx\n", pv->pv_va); -#endif continue; } +#endif + pmap_lock(pv->pv_pmap); pte = pmap_pte_quick(pv->pv_pmap, pv->pv_va); - if (pte == NULL) + if (pte == NULL) { + pmap_unlock(pv->pv_pmap); continue; + } if (*pte & bit) { + pmap_unlock(pv->pv_pmap); splx(s); return TRUE; } + pmap_unlock(pv->pv_pmap); } splx(s); return (FALSE); } /* * this routine is used to modify bits in ptes */ -static __inline void +static void pmap_changebit(pa, bit, setem) vm_offset_t pa; int bit; boolean_t setem; { - register pv_entry_t pv, *ppv; + register pv_entry_t pv; + pv_table_t *ppv; register unsigned *pte; vm_offset_t va; int changed; int s; if (!pmap_is_managed(pa)) return; s = splvm(); changed = 0; ppv = pa_to_pvh(pa); + /* * Loop over all current mappings setting/clearing as appropos If * setting RO do we need to clear the VAC? */ - for ( pv = *ppv; pv; pv = pv->pv_next) { + for (pv = TAILQ_FIRST(&ppv->pv_list); + pv; + pv = TAILQ_NEXT(pv, pv_list)) { + va = pv->pv_va; /* * don't write protect pager mappings */ if (!setem && (bit == PG_RW)) { - if (va >= clean_sva && va < clean_eva) + if (!pmap_track_modified(pv->pv_va)) continue; } - if (!pv->pv_pmap) { + #if defined(PMAP_DIAGNOSTIC) + if (!pv->pv_pmap) { printf("Null pmap (cb) at va: 0x%lx\n", va); -#endif continue; } +#endif + pmap_lock(pv->pv_pmap); pte = pmap_pte_quick(pv->pv_pmap, va); - if (pte == NULL) + if (pte == NULL) { + pmap_unlock(pv->pv_pmap); continue; + } if (setem) { *(int *)pte |= bit; changed = 1; } else { vm_offset_t pbits = *(vm_offset_t *)pte; - if (pbits & bit) + if (pbits & bit) { changed = 1; - if (bit == PG_RW) { - if (pbits & PG_M) { - vm_page_t m; - vm_offset_t pa = pbits & PG_FRAME; - m = PHYS_TO_VM_PAGE(pa); - m->dirty = VM_PAGE_BITS_ALL; + if (bit == PG_RW) { + if (pbits & PG_M) { + ppv->pv_vm_page->dirty = VM_PAGE_BITS_ALL; + } + *(int *)pte = pbits & ~(PG_M|PG_RW); + } else { + *(int *)pte = pbits & ~bit; } - *(int *)pte = pbits & ~(PG_M|PG_RW); - } else { - *(int *)pte = pbits & ~bit; } } + pmap_unlock(pv->pv_pmap); } splx(s); if (changed) pmap_update(); } /* * pmap_page_protect: * * Lower the permission for all mappings to a given page. */ void pmap_page_protect(phys, prot) vm_offset_t phys; vm_prot_t prot; { if ((prot & VM_PROT_WRITE) == 0) { if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { pmap_changebit(phys, PG_RW, FALSE); } else { pmap_remove_all(phys); - pmap_update(); } } } vm_offset_t pmap_phys_address(ppn) int ppn; { return (i386_ptob(ppn)); } /* * pmap_is_referenced: * * Return whether or not the specified physical page was referenced * by any physical maps. */ boolean_t pmap_is_referenced(vm_offset_t pa) { - register pv_entry_t *ppv, pv, lpv; + register pv_entry_t pv; + pv_table_t *ppv; unsigned *pte; int s; if (!pmap_is_managed(pa)) return FALSE; ppv = pa_to_pvh(pa); s = splvm(); /* * Not found, check current mappings returning immediately if found. */ - for (lpv = NULL, pv = *ppv ;pv; lpv = pv, pv = pv->pv_next) { + for (pv = TAILQ_FIRST(&ppv->pv_list); + pv; + pv = TAILQ_NEXT(pv, pv_list)) { + /* * if the bit being tested is the modified bit, then * mark clean_map and ptes as never * modified. */ if (!pmap_track_modified(pv->pv_va)) continue; - if (!pv->pv_pmap) { - continue; - } + + pmap_lock(pv->pv_pmap); pte = pmap_pte_quick(pv->pv_pmap, pv->pv_va); - if (pte == NULL) + if (pte == NULL) { + pmap_unlock(pv->pv_pmap); continue; + } if ((int) *pte & PG_A) { - if (lpv) { - lpv->pv_next = pv->pv_next; - pv->pv_next = *ppv; - *ppv = pv; - } + pmap_unlock(pv->pv_pmap); splx(s); return TRUE; } + pmap_unlock(pv->pv_pmap); } splx(s); return (FALSE); } /* * pmap_ts_referenced: * * Return the count of reference bits for a page, clearing all of them. * */ int pmap_ts_referenced(vm_offset_t pa) { - register pv_entry_t *ppv, pv; + register pv_entry_t pv; + pv_table_t *ppv; unsigned *pte; int s; int rtval = 0; - vm_offset_t vachanged[VATRACK]; if (!pmap_is_managed(pa)) return FALSE; s = splvm(); ppv = pa_to_pvh(pa); - if (*ppv == NULL) { + if (TAILQ_FIRST(&ppv->pv_list) == NULL) { splx(s); return 0; } /* * Not found, check current mappings returning immediately if found. */ - for (pv = *ppv ;pv; pv = pv->pv_next) { + for (pv = TAILQ_FIRST(&ppv->pv_list); + pv; + pv = TAILQ_NEXT(pv, pv_list)) { /* * if the bit being tested is the modified bit, then * mark clean_map and ptes as never * modified. */ if (!pmap_track_modified(pv->pv_va)) continue; - if (!pv->pv_pmap) { - continue; - } + pmap_lock(pv->pv_pmap); pte = pmap_pte_quick(pv->pv_pmap, pv->pv_va); - if (pte == NULL) + if (pte == NULL) { + pmap_unlock(pv->pv_pmap); continue; + } if (*pte & PG_A) { - if (rtval < VATRACK) - vachanged[rtval] = pv->pv_va; rtval++; *pte &= ~PG_A; } + pmap_unlock(pv->pv_pmap); } splx(s); if (rtval) { - if (rtval <= VATRACK) { - int i; - for(i=0;ipm_pdir[PTDPTDI] & PG_FRAME; + if ((pmap == kernel_pmap) || + (frame == (((unsigned) PTDpde) & PG_FRAME))) { + pmap_update(); + } +} +#endif + /* * Miscellaneous support routines follow */ static void i386_protection_init() { register int *kp, prot; kp = protection_codes; for (prot = 0; prot < 8; prot++) { switch (prot) { case VM_PROT_NONE | VM_PROT_NONE | VM_PROT_NONE: /* * Read access is also 0. There isn't any execute bit, * so just make it readable. */ case VM_PROT_READ | VM_PROT_NONE | VM_PROT_NONE: case VM_PROT_READ | VM_PROT_NONE | VM_PROT_EXECUTE: case VM_PROT_NONE | VM_PROT_NONE | VM_PROT_EXECUTE: *kp++ = 0; break; case VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_NONE: case VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_EXECUTE: case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_NONE: case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE: *kp++ = PG_RW; break; } } } /* * Map a set of physical memory pages into the kernel virtual * address space. Return a pointer to where it is mapped. This * routine is intended to be used for mapping device memory, * NOT real memory. The non-cacheable bits are set on each * mapped page. */ void * pmap_mapdev(pa, size) vm_offset_t pa; vm_size_t size; { vm_offset_t va, tmpva; unsigned *pte; size = roundup(size, PAGE_SIZE); va = kmem_alloc_pageable(kernel_map, size); if (!va) panic("pmap_mapdev: Couldn't alloc kernel virtual memory"); pa = pa & PG_FRAME; for (tmpva = va; size > 0;) { pte = (unsigned *)vtopte(tmpva); *pte = pa | PG_RW | PG_V | PG_N; size -= PAGE_SIZE; tmpva += PAGE_SIZE; pa += PAGE_SIZE; } pmap_update(); return ((void *) va); } /* * perform the pmap work for mincore */ int pmap_mincore(pmap, addr) pmap_t pmap; vm_offset_t addr; { unsigned *ptep, pte; int val = 0; + pmap_lock(pmap); ptep = pmap_pte(pmap, addr); if (ptep == 0) { + pmap_unlock(pmap); return 0; } if (pte = *ptep) { vm_offset_t pa; val = MINCORE_INCORE; pa = pte & PG_FRAME; /* * Modified by us */ if (pte & PG_M) val |= MINCORE_MODIFIED|MINCORE_MODIFIED_OTHER; /* * Modified by someone */ else if (PHYS_TO_VM_PAGE(pa)->dirty || pmap_is_modified(pa)) val |= MINCORE_MODIFIED_OTHER; /* * Referenced by us */ if (pte & PG_U) val |= MINCORE_REFERENCED|MINCORE_REFERENCED_OTHER; /* * Referenced by someone */ else if ((PHYS_TO_VM_PAGE(pa)->flags & PG_REFERENCED) || pmap_is_referenced(pa)) val |= MINCORE_REFERENCED_OTHER; } + pmap_unlock(pmap); return val; } #if defined(PMAP_DEBUG) pmap_pid_dump(int pid) { pmap_t pmap; struct proc *p; int npte = 0; int index; for (p = allproc.lh_first; p != NULL; p = p->p_list.le_next) { if (p->p_pid != pid) continue; if (p->p_vmspace) { int i,j; index = 0; pmap = &p->p_vmspace->vm_pmap; for(i=0;i<1024;i++) { pd_entry_t *pde; unsigned *pte; unsigned base = i << PDRSHIFT; pde = &pmap->pm_pdir[i]; if (pde && pmap_pde_v(pde)) { for(j=0;j<1024;j++) { unsigned va = base + (j << PAGE_SHIFT); if (va >= (vm_offset_t) VM_MIN_KERNEL_ADDRESS) { if (index) { index = 0; printf("\n"); } return npte; } - pte = pmap_pte( pmap, va); + pte = pmap_pte_quick( pmap, va); if (pte && pmap_pte_v(pte)) { vm_offset_t pa; vm_page_t m; pa = *(int *)pte; m = PHYS_TO_VM_PAGE((pa & PG_FRAME)); printf("va: 0x%x, pt: 0x%x, h: %d, w: %d, f: 0x%x", va, pa, m->hold_count, m->wire_count, m->flags); npte++; index++; if (index >= 2) { index = 0; printf("\n"); } else { printf(" "); } } } } } } } return npte; } #endif #if defined(DEBUG) static void pads __P((pmap_t pm)); static void pmap_pvdump __P((vm_offset_t pa)); /* print address space of pmap*/ static void pads(pm) pmap_t pm; { unsigned va, i, j; unsigned *ptep; if (pm == kernel_pmap) return; for (i = 0; i < 1024; i++) if (pm->pm_pdir[i]) for (j = 0; j < 1024; j++) { va = (i << PDRSHIFT) + (j << PAGE_SHIFT); if (pm == kernel_pmap && va < KERNBASE) continue; if (pm != kernel_pmap && va > UPT_MAX_ADDRESS) continue; - ptep = pmap_pte(pm, va); + ptep = pmap_pte_quick(pm, va); if (pmap_pte_v(ptep)) printf("%x:%x ", va, *(int *) ptep); }; } static void pmap_pvdump(pa) vm_offset_t pa; { register pv_entry_t pv; printf("pa %x", pa); - for (pv = pa_to_pvh(pa); pv; pv = pv->pv_next) { + for (pv = TAILQ_FIRST(pa_to_pvh(pa)); + pv; + pv = TAILQ_NEXT(pv, pv_list)) { #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 Index: head/sys/pc98/i386/trap.c =================================================================== --- head/sys/pc98/i386/trap.c (revision 18264) +++ head/sys/pc98/i386/trap.c (revision 18265) @@ -1,992 +1,992 @@ /*- * Copyright (C) 1994, David Greenman * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the University of Utah, and William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)trap.c 7.4 (Berkeley) 5/13/91 - * $Id: trap.c,v 1.5 1996/09/04 09:52:19 asami Exp $ + * $Id: trap.c,v 1.6 1996/09/07 02:13:36 asami Exp $ */ /* * 386 Trap and System call handling */ #include "opt_ktrace.h" #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #ifdef KTRACE #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "isa.h" #ifdef POWERFAIL_NMI -# include -# include +#include +#include #endif #include "npx.h" int (*pmath_emulate) __P((struct trapframe *)); extern void trap __P((struct trapframe frame)); extern int trapwrite __P((unsigned addr)); extern void syscall __P((struct trapframe frame)); #ifdef CYRIX_486DLC static int trap_pfault __P((struct trapframe *, int, vm_offset_t)); #else static int trap_pfault __P((struct trapframe *, int)); #endif static void trap_fatal __P((struct trapframe *)); void dblfault_handler __P((void)); extern inthand_t IDTVEC(syscall); #define MAX_TRAP_MSG 28 static char *trap_msg[] = { "", /* 0 unused */ "privileged instruction fault", /* 1 T_PRIVINFLT */ "", /* 2 unused */ "breakpoint instruction fault", /* 3 T_BPTFLT */ "", /* 4 unused */ "", /* 5 unused */ "arithmetic trap", /* 6 T_ARITHTRAP */ "system forced exception", /* 7 T_ASTFLT */ "", /* 8 unused */ "general protection fault", /* 9 T_PROTFLT */ "trace trap", /* 10 T_TRCTRAP */ "", /* 11 unused */ "page fault", /* 12 T_PAGEFLT */ "", /* 13 unused */ "alignment fault", /* 14 T_ALIGNFLT */ "", /* 15 unused */ "", /* 16 unused */ "", /* 17 unused */ "integer divide fault", /* 18 T_DIVIDE */ "non-maskable interrupt trap", /* 19 T_NMI */ "overflow trap", /* 20 T_OFLOW */ "FPU bounds check fault", /* 21 T_BOUND */ "FPU device not available", /* 22 T_DNA */ "double fault", /* 23 T_DOUBLEFLT */ "FPU operand fetch fault", /* 24 T_FPOPFLT */ "invalid TSS fault", /* 25 T_TSSFLT */ "segment not present fault", /* 26 T_SEGNPFLT */ "stack fault", /* 27 T_STKFLT */ "machine check trap", /* 28 T_MCHK */ }; static void userret __P((struct proc *p, struct trapframe *frame, u_quad_t oticks)); static inline void userret(p, frame, oticks) struct proc *p; struct trapframe *frame; u_quad_t oticks; { int sig, s; while ((sig = CURSIG(p)) != 0) postsig(sig); p->p_priority = p->p_usrpri; if (want_resched) { /* * Since we are curproc, clock will normally just change * our priority without moving us from one queue to another * (since the running process is not on a queue.) * If that happened after we setrunqueue ourselves but before we * mi_switch()'ed, we might not be on the queue indicated by * our priority. */ s = splhigh(); setrunqueue(p); p->p_stats->p_ru.ru_nivcsw++; mi_switch(); splx(s); while ((sig = CURSIG(p)) != 0) postsig(sig); } /* * Charge system time if profiling. */ if (p->p_flag & P_PROFIL) addupc_task(p, frame->tf_eip, (u_int)(p->p_sticks - oticks) * psratio); curpriority = p->p_priority; } /* * Exception, fault, and trap interface to the FreeBSD kernel. * This common code is called from assembly language IDT gate entry * routines that prepare a suitable stack frame, and restore this * frame after the exception has been processed. */ void trap(frame) struct trapframe frame; { struct proc *p = curproc; u_quad_t sticks = 0; int i = 0, ucode = 0, type, code; #ifdef DEBUG u_long eva; #endif #ifdef CYRIX_486DLC vm_offset_t va; #endif type = frame.tf_trapno; code = frame.tf_err; #ifdef CYRIX_486DLC /* XXX: * CYRIX 486 CPU FIX. * If you use cyrix cpu, you often encouter strange signal 11's? * I think this is due to cyrix cpu bugs. * In any way, the following trick is effective for the problem. * As soon as possible, we must get the fault page address. */ va = (vm_offset_t)(rcr2()); if( type == T_PAGEFLT && ( frame.tf_eflags & PSL_I ) ) asm("sti"); #endif /* CYRIX_486DLC */ if (ISPL(frame.tf_cs) == SEL_UPL) { /* user trap */ sticks = p->p_sticks; p->p_md.md_regs = (int *)&frame; switch (type) { case T_PRIVINFLT: /* privileged instruction fault */ ucode = type; i = SIGILL; break; case T_BPTFLT: /* bpt instruction fault */ case T_TRCTRAP: /* trace trap */ frame.tf_eflags &= ~PSL_T; i = SIGTRAP; break; case T_ARITHTRAP: /* arithmetic trap */ ucode = code; i = SIGFPE; break; case T_ASTFLT: /* Allow process switch */ astoff(); cnt.v_soft++; if (p->p_flag & P_OWEUPC) { p->p_flag &= ~P_OWEUPC; addupc_task(p, p->p_stats->p_prof.pr_addr, p->p_stats->p_prof.pr_ticks); } goto out; case T_PROTFLT: /* general protection fault */ case T_SEGNPFLT: /* segment not present fault */ case T_STKFLT: /* stack fault */ case T_TSSFLT: /* invalid TSS fault */ case T_DOUBLEFLT: /* double fault */ default: ucode = code + BUS_SEGM_FAULT ; i = SIGBUS; break; case T_PAGEFLT: /* page fault */ #ifdef CYRIX_486DLC i = trap_pfault(&frame, TRUE, va); #else i = trap_pfault(&frame, TRUE); #endif if (i == -1) return; if (i == 0) goto out; ucode = T_PAGEFLT; break; case T_DIVIDE: /* integer divide fault */ ucode = FPE_INTDIV_TRAP; i = SIGFPE; break; #if NISA > 0 case T_NMI: #ifdef POWERFAIL_NMI goto handle_powerfail; #else /* !POWERFAIL_NMI */ #ifdef DDB /* NMI can be hooked up to a pushbutton for debugging */ printf ("NMI ... going to debugger\n"); if (kdb_trap (type, 0, &frame)) return; #endif /* DDB */ /* machine/parity/power fail/"kitchen sink" faults */ if (isa_nmi(code) == 0) return; panic("NMI indicates hardware failure"); #endif /* POWERFAIL_NMI */ #endif /* NISA > 0 */ case T_OFLOW: /* integer overflow fault */ ucode = FPE_INTOVF_TRAP; i = SIGFPE; break; case T_BOUND: /* bounds check fault */ ucode = FPE_SUBRNG_TRAP; i = SIGFPE; break; case T_DNA: #if NNPX > 0 /* if a transparent fault (due to context switch "late") */ if (npxdna()) return; #endif if (!pmath_emulate) { i = SIGFPE; ucode = FPE_FPU_NP_TRAP; break; } i = (*pmath_emulate)(&frame); if (i == 0) { if (!(frame.tf_eflags & PSL_T)) return; frame.tf_eflags &= ~PSL_T; i = SIGTRAP; } /* else ucode = emulator_only_knows() XXX */ break; case T_FPOPFLT: /* FPU operand fetch fault */ ucode = T_FPOPFLT; i = SIGILL; break; } } else { /* kernel trap */ switch (type) { case T_PAGEFLT: /* page fault */ #ifdef CYRIX_486DLC (void) trap_pfault(&frame, FALSE, va); #else (void) trap_pfault(&frame, FALSE); #endif return; case T_DNA: #if NNPX > 0 /* * The kernel is apparently using npx for copying. * XXX this should be fatal unless the kernel has * registered such use. */ if (npxdna()) return; #endif break; case T_PROTFLT: /* general protection fault */ case T_SEGNPFLT: /* segment not present fault */ /* * Invalid segment selectors and out of bounds * %eip's and %esp's can be set up in user mode. * This causes a fault in kernel mode when the * kernel tries to return to user mode. We want * to get this fault so that we can fix the * problem here and not have to check all the * selectors and pointers when the user changes * them. */ #define MAYBE_DORETI_FAULT(where, whereto) \ do { \ if (frame.tf_eip == (int)where) { \ frame.tf_eip = (int)whereto; \ return; \ } \ } while (0) if (intr_nesting_level == 0) { MAYBE_DORETI_FAULT(doreti_iret, doreti_iret_fault); MAYBE_DORETI_FAULT(doreti_popl_ds, doreti_popl_ds_fault); MAYBE_DORETI_FAULT(doreti_popl_es, doreti_popl_es_fault); } if (curpcb && curpcb->pcb_onfault) { frame.tf_eip = (int)curpcb->pcb_onfault; return; } break; case T_TSSFLT: /* * PSL_NT can be set in user mode and isn't cleared * automatically when the kernel is entered. This * causes a TSS fault when the kernel attempts to * `iret' because the TSS link is uninitialized. We * want to get this fault so that we can fix the * problem here and not every time the kernel is * entered. */ if (frame.tf_eflags & PSL_NT) { frame.tf_eflags &= ~PSL_NT; return; } break; case T_TRCTRAP: /* trace trap */ if (frame.tf_eip == (int)IDTVEC(syscall)) { /* * We've just entered system mode via the * syscall lcall. Continue single stepping * silently until the syscall handler has * saved the flags. */ return; } if (frame.tf_eip == (int)IDTVEC(syscall) + 1) { /* * The syscall handler has now saved the * flags. Stop single stepping it. */ frame.tf_eflags &= ~PSL_T; return; } /* * Fall through. */ case T_BPTFLT: /* * If DDB is enabled, let it handle the debugger trap. * Otherwise, debugger traps "can't happen". */ #ifdef DDB if (kdb_trap (type, 0, &frame)) return; #endif break; #if NISA > 0 case T_NMI: #ifdef POWERFAIL_NMI #ifndef TIMER_FREQ # define TIMER_FREQ 1193182 #endif handle_powerfail: { static unsigned lastalert = 0; if(time.tv_sec - lastalert > 10) { log(LOG_WARNING, "NMI: power fail\n"); sysbeep(TIMER_FREQ/880, hz); lastalert = time.tv_sec; } return; } #else /* !POWERFAIL_NMI */ #ifdef DDB /* NMI can be hooked up to a pushbutton for debugging */ printf ("NMI ... going to debugger\n"); if (kdb_trap (type, 0, &frame)) return; #endif /* DDB */ /* machine/parity/power fail/"kitchen sink" faults */ if (isa_nmi(code) == 0) return; /* FALL THROUGH */ #endif /* POWERFAIL_NMI */ #endif /* NISA > 0 */ } trap_fatal(&frame); return; } trapsignal(p, i, ucode); #ifdef DEBUG eva = rcr2(); if (type <= MAX_TRAP_MSG) { uprintf("fatal process exception: %s", trap_msg[type]); if ((type == T_PAGEFLT) || (type == T_PROTFLT)) uprintf(", fault VA = 0x%x", eva); uprintf("\n"); } #endif out: userret(p, &frame, sticks); } #ifdef notyet /* * This version doesn't allow a page fault to user space while * in the kernel. The rest of the kernel needs to be made "safe" * before this can be used. I think the only things remaining * to be made safe are the iBCS2 code and the process tracing/ * debugging code. */ static int #ifdef CYRIX_486DLC trap_pfault(frame, usermode,faultva) struct trapframe *frame; int usermode; vm_offset_t faultva; #else trap_pfault(frame, usermode) struct trapframe *frame; int usermode; #endif { vm_offset_t va; struct vmspace *vm = NULL; vm_map_t map = 0; int rv = 0; vm_prot_t ftype; int eva; struct proc *p = curproc; if (frame->tf_err & PGEX_W) ftype = VM_PROT_READ | VM_PROT_WRITE; else ftype = VM_PROT_READ; #ifdef CYRIX_486DLC eva = faultva; #else eva = rcr2(); #endif va = trunc_page((vm_offset_t)eva); if (va < VM_MIN_KERNEL_ADDRESS) { vm_offset_t v; vm_page_t mpte; if (p == NULL || (!usermode && va < VM_MAXUSER_ADDRESS && (curpcb == NULL || curpcb->pcb_onfault == NULL))) { trap_fatal(frame); return (-1); } /* * This is a fault on non-kernel virtual memory. * vm is initialized above to NULL. If curproc is NULL * or curproc->p_vmspace is NULL the fault is fatal. */ vm = p->p_vmspace; if (vm == NULL) goto nogo; map = &vm->vm_map; /* * Keep swapout from messing with us during this * critical time. */ ++p->p_lock; /* * Grow the stack if necessary */ if ((caddr_t)va > vm->vm_maxsaddr && (caddr_t)va < (caddr_t)USRSTACK) { if (!grow(p, va)) { rv = KERN_FAILURE; --p->p_lock; goto nogo; } } /* Fault in the user page: */ rv = vm_fault(map, va, ftype, FALSE); --p->p_lock; } else { /* * Don't allow user-mode faults in kernel address space. */ if (usermode) goto nogo; /* * Since we know that kernel virtual address addresses * always have pte pages mapped, we just have to fault * the page. */ rv = vm_fault(kernel_map, va, ftype, FALSE); } if (rv == KERN_SUCCESS) return (0); nogo: if (!usermode) { if (curpcb && curpcb->pcb_onfault) { frame->tf_eip = (int)curpcb->pcb_onfault; return (0); } trap_fatal(frame); return (-1); } /* kludge to pass faulting virtual address to sendsig */ frame->tf_err = eva; return((rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV); } #endif int #ifdef CYRIX_486DLC trap_pfault(frame, usermode,faultva) struct trapframe *frame; int usermode; vm_offset_t faultva; #else trap_pfault(frame, usermode) struct trapframe *frame; int usermode; #endif { vm_offset_t va; struct vmspace *vm = NULL; vm_map_t map = 0; int rv = 0; vm_prot_t ftype; int eva; struct proc *p = curproc; #ifdef CYRIX_486DLC eva = faultva; #else eva = rcr2(); #endif va = trunc_page((vm_offset_t)eva); if (va >= KERNBASE) { /* * Don't allow user-mode faults in kernel address space. */ if (usermode) goto nogo; map = kernel_map; } else { /* * This is a fault on non-kernel virtual memory. * vm is initialized above to NULL. If curproc is NULL * or curproc->p_vmspace is NULL the fault is fatal. */ if (p != NULL) vm = p->p_vmspace; if (vm == NULL) goto nogo; map = &vm->vm_map; } if (frame->tf_err & PGEX_W) ftype = VM_PROT_READ | VM_PROT_WRITE; else ftype = VM_PROT_READ; if (map != kernel_map) { /* * Keep swapout from messing with us during this * critical time. */ ++p->p_lock; /* * Grow the stack if necessary */ if ((caddr_t)va > vm->vm_maxsaddr && (caddr_t)va < (caddr_t)USRSTACK) { if (!grow(p, va)) { rv = KERN_FAILURE; --p->p_lock; goto nogo; } } /* Fault in the user page: */ rv = vm_fault(map, va, ftype, FALSE); --p->p_lock; } else { /* * Since we know that kernel virtual address addresses * always have pte pages mapped, we just have to fault * the page. */ rv = vm_fault(map, va, ftype, FALSE); } if (rv == KERN_SUCCESS) return (0); nogo: if (!usermode) { if (curpcb && curpcb->pcb_onfault) { frame->tf_eip = (int)curpcb->pcb_onfault; return (0); } trap_fatal(frame); return (-1); } /* kludge to pass faulting virtual address to sendsig */ frame->tf_err = eva; return((rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV); } static void trap_fatal(frame) struct trapframe *frame; { int code, type, eva, ss, esp; struct soft_segment_descriptor softseg; code = frame->tf_err; type = frame->tf_trapno; eva = rcr2(); sdtossd(&gdt[IDXSEL(frame->tf_cs & 0xffff)].sd, &softseg); if (type <= MAX_TRAP_MSG) printf("\n\nFatal trap %d: %s while in %s mode\n", type, trap_msg[type], ISPL(frame->tf_cs) == SEL_UPL ? "user" : "kernel"); if (type == T_PAGEFLT) { printf("fault virtual address = 0x%x\n", eva); printf("fault code = %s %s, %s\n", code & PGEX_U ? "user" : "supervisor", code & PGEX_W ? "write" : "read", code & PGEX_P ? "protection violation" : "page not present"); } printf("instruction pointer = 0x%x:0x%x\n", frame->tf_cs & 0xffff, frame->tf_eip); if (ISPL(frame->tf_cs) == SEL_UPL) { ss = frame->tf_ss & 0xffff; esp = frame->tf_esp; } else { ss = GSEL(GDATA_SEL, SEL_KPL); esp = (int)&frame->tf_esp; } printf("stack pointer = 0x%x:0x%x\n", ss, esp); printf("frame pointer = 0x%x:0x%x\n", ss, frame->tf_ebp); printf("code segment = base 0x%x, limit 0x%x, type 0x%x\n", softseg.ssd_base, softseg.ssd_limit, softseg.ssd_type); printf(" = DPL %d, pres %d, def32 %d, gran %d\n", softseg.ssd_dpl, softseg.ssd_p, softseg.ssd_def32, softseg.ssd_gran); printf("processor eflags = "); if (frame->tf_eflags & PSL_T) printf("trace trap, "); if (frame->tf_eflags & PSL_I) printf("interrupt enabled, "); if (frame->tf_eflags & PSL_NT) printf("nested task, "); if (frame->tf_eflags & PSL_RF) printf("resume, "); if (frame->tf_eflags & PSL_VM) printf("vm86, "); printf("IOPL = %d\n", (frame->tf_eflags & PSL_IOPL) >> 12); printf("current process = "); if (curproc) { printf("%lu (%s)\n", (u_long)curproc->p_pid, curproc->p_comm ? curproc->p_comm : ""); } else { printf("Idle\n"); } printf("interrupt mask = "); if ((cpl & net_imask) == net_imask) printf("net "); if ((cpl & tty_imask) == tty_imask) printf("tty "); if ((cpl & bio_imask) == bio_imask) printf("bio "); if (cpl == 0) printf("none"); printf("\n"); #ifdef KDB if (kdb_trap(&psl)) return; #endif #ifdef DDB if (kdb_trap (type, 0, frame)) return; #endif if (type <= MAX_TRAP_MSG) panic(trap_msg[type]); else panic("unknown/reserved trap"); } /* * Double fault handler. Called when a fault occurs while writing * a frame for a trap/exception onto the stack. This usually occurs * when the stack overflows (such is the case with infinite recursion, * for example). * * XXX Note that the current PTD gets replaced by IdlePTD when the * task switch occurs. This means that the stack that was active at * the time of the double fault is not available at unless * the machine was idle when the double fault occurred. The downside * of this is that "trace " in ddb won't work. */ void dblfault_handler() { struct pcb *pcb = curpcb; if (pcb != NULL) { printf("\nFatal double fault:\n"); printf("eip = 0x%x\n", pcb->pcb_tss.tss_eip); printf("esp = 0x%x\n", pcb->pcb_tss.tss_esp); printf("ebp = 0x%x\n", pcb->pcb_tss.tss_ebp); } panic("double fault"); } /* * Compensate for 386 brain damage (missing URKR). * This is a little simpler than the pagefault handler in trap() because * it the page tables have already been faulted in and high addresses * are thrown out early for other reasons. */ int trapwrite(addr) unsigned addr; { struct proc *p; vm_offset_t va, v; struct vmspace *vm; int rv; va = trunc_page((vm_offset_t)addr); /* * XXX - MAX is END. Changed > to >= for temp. fix. */ if (va >= VM_MAXUSER_ADDRESS) return (1); p = curproc; vm = p->p_vmspace; ++p->p_lock; if ((caddr_t)va >= vm->vm_maxsaddr && (caddr_t)va < (caddr_t)USRSTACK) { if (!grow(p, va)) { --p->p_lock; return (1); } } v = trunc_page(vtopte(va)); /* * fault the data page */ rv = vm_fault(&vm->vm_map, va, VM_PROT_READ|VM_PROT_WRITE, FALSE); --p->p_lock; if (rv != KERN_SUCCESS) return 1; return (0); } /* * System call request from POSIX system call gate interface to kernel. * Like trap(), argument is call by reference. */ void syscall(frame) struct trapframe frame; { caddr_t params; int i; struct sysent *callp; struct proc *p = curproc; u_quad_t sticks; int error; int args[8], rval[2]; u_int code; sticks = p->p_sticks; if (ISPL(frame.tf_cs) != SEL_UPL) panic("syscall"); p->p_md.md_regs = (int *)&frame; params = (caddr_t)frame.tf_esp + sizeof(int); code = frame.tf_eax; if (p->p_sysent->sv_prepsyscall) { (*p->p_sysent->sv_prepsyscall)(&frame, args, &code, ¶ms); } else { /* * Need to check if this is a 32 bit or 64 bit syscall. */ if (code == SYS_syscall) { /* * Code is first argument, followed by actual args. */ code = fuword(params); params += sizeof(int); } else if (code == SYS___syscall) { /* * Like syscall, but code is a quad, so as to maintain * quad alignment for the rest of the arguments. */ code = fuword(params); params += sizeof(quad_t); } } if (p->p_sysent->sv_mask) code &= p->p_sysent->sv_mask; if (code >= p->p_sysent->sv_size) callp = &p->p_sysent->sv_table[0]; else callp = &p->p_sysent->sv_table[code]; if (params && (i = callp->sy_narg * sizeof(int)) && (error = copyin(params, (caddr_t)args, (u_int)i))) { #ifdef KTRACE if (KTRPOINT(p, KTR_SYSCALL)) ktrsyscall(p->p_tracep, code, callp->sy_narg, args); #endif goto bad; } #ifdef KTRACE if (KTRPOINT(p, KTR_SYSCALL)) ktrsyscall(p->p_tracep, code, callp->sy_narg, args); #endif rval[0] = 0; rval[1] = frame.tf_edx; error = (*callp->sy_call)(p, args, rval); switch (error) { case 0: /* * Reinitialize proc pointer `p' as it may be different * if this is a child returning from fork syscall. */ p = curproc; frame.tf_eax = rval[0]; frame.tf_edx = rval[1]; frame.tf_eflags &= ~PSL_C; break; case ERESTART: /* * Reconstruct pc, assuming lcall $X,y is 7 bytes, * int 0x80 is 2 bytes. We saved this in tf_err. */ frame.tf_eip -= frame.tf_err; break; case EJUSTRETURN: break; default: bad: if (p->p_sysent->sv_errsize) if (error >= p->p_sysent->sv_errsize) error = -1; /* XXX */ else error = p->p_sysent->sv_errtbl[error]; frame.tf_eax = error; frame.tf_eflags |= PSL_C; break; } if (frame.tf_eflags & PSL_T) { /* Traced syscall. */ frame.tf_eflags &= ~PSL_T; trapsignal(p, SIGTRAP, 0); } userret(p, &frame, sticks); #ifdef KTRACE if (KTRPOINT(p, KTR_SYSRET)) ktrsysret(p->p_tracep, code, error, rval[0]); #endif } Index: head/sys/pc98/i386/userconfig.c =================================================================== --- head/sys/pc98/i386/userconfig.c (revision 18264) +++ head/sys/pc98/i386/userconfig.c (revision 18265) @@ -1,2834 +1,2841 @@ /** ** Copyright (c) 1995 ** Michael Smith, msmith@atrad.adelaide.edu.au. All rights reserved. ** ** This code contains a module marked : * Copyright (c) 1991 Regents of the University of California. * All rights reserved. * Copyright (c) 1994 Jordan K. Hubbard * All rights reserved. * Copyright (c) 1994 David Greenman * All rights reserved. * * Many additional changes by Bruce Evans * * This code is derived from software contributed by the * University of California Berkeley, Jordan K. Hubbard, * David Greenman and Bruce Evans. ** As such, it contains code subject to the above copyrights. ** The module and its copyright can be found below. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer as ** the first lines of this file unmodified. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. All advertising materials mentioning features or use of this software ** must display the following acknowledgment: ** This product includes software developed by Michael Smith. ** 4. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY MICHAEL SMITH ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL MICHAEL SMITH BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** - ** $Id: userconfig.c,v 1.4 1996/09/03 10:23:18 asami Exp $ + ** $Id: userconfig.c,v 1.5 1996/09/10 09:37:38 asami Exp $ **/ /** ** USERCONFIG ** ** Kernel boot-time configuration manipulation tool for FreeBSD. ** ** Two modes of operation are supported : the default is the line-editor mode, ** the command "visual" invokes the fullscreen mode. ** ** The line-editor mode is the old favorite from FreeBSD 2.0/20.05 &c., the ** fullscreen mode requires syscons or a minimal-ansi serial console. **/ /** ** USERCONFIG, visual mode. ** ** msmith@atrad.adelaide.edu.au ** ** Look for "EDIT THIS LIST" to add to the list of known devices ** ** ** There are a number of assumptions made in this code. ** ** - That the console supports a minimal set of ANSI escape sequences ** (See the screen manipulation section for a summary) ** and has at least 24 rows. ** - That values less than or equal to zero for any of the device ** parameters indicate that the driver does not use the parameter. ** - That the only tunable parameter for PCI devices are their flags. ** - That flags are _always_ editable. ** ** Devices marked as disabled are imported as such. It is possible to move ** a PCI device onto the inactive list, but it is not possible to actually ** prevent the device from being probed. The ability to move is considered ** desirable in that people will complain otherwise 8) ** ** For this tool to be useful, the list of devices below _MUST_ be updated ** when a new driver is brought into the kernel. It is not possible to ** extract this information from the drivers in the kernel, as the devconf ** structure for the device is not registered until the device is probed, ** which is too late. ** ** XXX - TODO: ** ** - FIX OPERATION WITH PCVT! ** ** - Display _what_ a device conflicts with. ** - Implement page up/down (as what?) ** - Wizard mode (no restrictions) ** - Find out how to put syscons back into low-intensity mode so that the ** !b escape is useful on the console. ** - The min and max values used for editing parameters are probably ** very bogus - fix? ** ** - Only display headings with devices under them. (difficult) **/ /* * PC-9801 port by KATO Takenori */ #include #include #include #include #include #include #include #include #include +#include + +static struct isa_device *isa_devlist; /* list read by dset to extract changes */ + +#ifdef VISUAL_USERCONFIG static struct isa_device *devtabs[] = { isa_devtab_bio, isa_devtab_tty, isa_devtab_net, isa_devtab_null, NULL }; -static struct isa_device *isa_devlist; /* list read by dset to extract changes */ #define putchar(x) cnputc(x) #define getchar() cngetc() #ifndef FALSE #define FALSE (0) #define TRUE (!FALSE) #endif typedef struct { char dev[16]; /* device basename */ char name[60]; /* long name */ int attrib; /* things to do with the device */ int class; /* device classification */ } DEV_INFO; #define FLG_INVISIBLE (1<<0) /* device should not be shown */ #define FLG_MANDATORY (1<<1) /* device can be edited but not disabled */ #define FLG_FIXIRQ (1<<2) /* device IRQ cannot be changed */ #define FLG_FIXIOBASE (1<<3) /* device iobase cannot be changed */ #define FLG_FIXMADDR (1<<4) /* device maddr cannot be changed */ #define FLG_FIXMSIZE (1<<5) /* device msize cannot be changed */ #define FLG_FIXDRQ (1<<6) /* device DRQ cannot be changed */ #define FLG_FIXED (FLG_FIXIRQ|FLG_FIXIOBASE|FLG_FIXMADDR|FLG_FIXMSIZE|FLG_FIXDRQ) #define FLG_IMMUTABLE (FLG_FIXED|FLG_MANDATORY) #define CLS_STORAGE 1 /* storage devices */ #define CLS_NETWORK 2 /* network interfaces */ #define CLS_COMMS 3 /* serial, parallel ports */ #define CLS_INPUT 4 /* user input : mice, keyboards, joysticks etc */ #define CLS_MMEDIA 5 /* "multimedia" devices (sound, video, etc) */ #define CLS_MISC 255 /* none of the above */ typedef struct { char name[60]; int number; } DEVCLASS_INFO; static DEVCLASS_INFO devclass_names[] = { { "Storage : ", CLS_STORAGE}, { "Network : ", CLS_NETWORK}, { "Communications : ", CLS_COMMS}, { "Input : ", CLS_INPUT}, { "Multimedia : ", CLS_MMEDIA}, { "Miscellaneous : ", CLS_MISC}, { "",0}}; /********************* EDIT THIS LIST **********************/ /** Notes : ** ** - PCI devices should be marked FLG_FIXED, not FLG_IMMUTABLE. Whilst ** it's impossible to disable them, it should be possible to move them ** from one list to another for peace of mind. ** - Devices that shouldn't be seen or removed should be marked FLG_INVISIBLE. ** - XXX The list below should be reviewed by the driver authors to verify ** that the correct flags have been set for each driver, and that the ** descriptions are accurate. **/ static DEV_INFO device_info[] = { /*---Name----- ---Description---------------------------------------------- */ #ifdef PC98 {"sbic", "PC-9801-55 SCSI Interface", 0, CLS_STORAGE}, {"bs", "PC-9801-55 SCSI Interface", 0, CLS_STORAGE}, {"aic", "Adaptec 152x SCSI and compatible sound cards", 0, CLS_STORAGE}, {"ahc", "Adaptec 274x/284x/294x SCSI controller", 0, CLS_STORAGE}, {"ncr", "NCR 53C810 SCSI controller", FLG_FIXED, CLS_STORAGE}, {"wdc", "IDE/ESDI/MFM disk controller", 0, CLS_STORAGE}, {"fdc", "Floppy disk controller", FLG_FIXED, CLS_STORAGE}, {"scd", "Sony CD-ROM", 0, CLS_STORAGE}, {"mcd", "Mitsumi CD-ROM", 0, CLS_STORAGE}, {"matcdc", "Matsushita/Panasonic/Creative CDROM", 0, CLS_STORAGE}, {"ed", "NS8390 Ethernet adapters", 0, CLS_NETWORK}, {"el", "3C501 Ethernet adapter", 0, CLS_NETWORK}, {"ep", "3C509 Ethernet adapter", 0, CLS_NETWORK}, {"fe", "Fujitsu MD86960A/MB869685A Ethernet adapters", 0, CLS_NETWORK}, {"zp", "3COM PCMCIA Etherlink III Ethernet adapter", 0, CLS_NETWORK}, {"de", "DEC DC21040 Ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"fpa", "DEC DEFPA PCI FDDI adapter", FLG_FIXED, CLS_NETWORK}, {"sio", "8250/16450/16550 Serial port", 0, CLS_COMMS}, {"lpt", "Parallel printer port", 0, CLS_COMMS}, {"mse", "PC-9801 Bus Mouse", 0, CLS_INPUT}, {"sc", "Syscons console driver", FLG_FIXED, CLS_INPUT}, {"pcm", "PC-9801-86 Sound Board", 0, CLS_MMEDIA}, {"sb", "Soundblaster PCM (SB, SBPro, SB16, ProAudio Spectrum)",0,CLS_MMEDIA}, {"sbxvi", "Soundblaster 16", 0, CLS_MMEDIA}, {"sbmidi", "Soundblaster MIDI interface", 0, CLS_MMEDIA}, {"mss", "Microsoft Sound System", 0, CLS_MMEDIA}, {"opl", "OPL-2/3 FM, Soundblaster, SBPro, SB16, ProAudio Spectrum",0,CLS_MMEDIA}, {"mpu", "Roland MPU401 MIDI", 0, CLS_MMEDIA}, {"pca", "PC speaker PCM audio driver", FLG_FIXED, CLS_MMEDIA}, {"apm", "Advanced Power Management", FLG_FIXED, CLS_MISC}, {"npx", "Math coprocessor", FLG_INVISIBLE, CLS_MISC}, {"lkm", "Loadable PCI driver support", FLG_INVISIBLE, CLS_MISC}, {"vga", "Catchall PCI VGA driver", FLG_INVISIBLE, CLS_MISC}, {"chip", "PCI chipset support", FLG_INVISIBLE, CLS_MISC}, #else {"bt", "Buslogic SCSI controller", 0, CLS_STORAGE}, {"ahc", "Adaptec 274x/284x/294x SCSI controller", 0, CLS_STORAGE}, {"ahb", "Adaptec 174x SCSI controller", 0, CLS_STORAGE}, {"aha", "Adaptec 154x SCSI controller", 0, CLS_STORAGE}, {"uha", "Ultrastor 14F/24F/34F SCSI controller",0, CLS_STORAGE}, {"aic", "Adaptec 152x SCSI and compatible sound cards", 0, CLS_STORAGE}, {"nca", "ProAudio Spectrum SCSI and comaptibles", 0, CLS_STORAGE}, {"sea", "Seagate ST01/ST02 SCSI and compatibles", 0, CLS_STORAGE}, {"wds", "Western Digitial WD7000 SCSI controller", 0, CLS_STORAGE}, {"ncr", "NCR 53C810 SCSI controller", FLG_FIXED, CLS_STORAGE}, {"wdc", "IDE/ESDI/MFM disk controller", 0, CLS_STORAGE}, {"fdc", "Floppy disk controller", FLG_FIXED, CLS_STORAGE}, {"mcd", "Mitsumi CD-ROM", 0, CLS_STORAGE}, {"scd", "Sony CD-ROM", 0, CLS_STORAGE}, {"matcdc", "Matsushita/Panasonic/Creative CDROM", 0, CLS_STORAGE}, {"wt", "Wangtek/Archive QIC-02 Tape drive", 0, CLS_STORAGE}, {"ed", "NE1000,NE2000,3C503,WD/SMC80xx Ethernet adapters",0, CLS_NETWORK}, {"el", "3C501 Ethernet adapter", 0, CLS_NETWORK}, {"ep", "3C509 Ethernet adapter", 0, CLS_NETWORK}, {"fe", "Fujitsu MD86960A/MB869685A Ethernet adapters", 0, CLS_NETWORK}, {"fea", "DEC DEFEA EISA FDDI adapter", 0, CLS_NETWORK}, {"ie", "AT&T Starlan 10 and EN100, 3C507, NI5210 Ethernet adapters",0,CLS_NETWORK}, {"ix", "Intel EtherExpress Ethernet adapter", 0, CLS_NETWORK}, {"le", "DEC Etherworks 2 and 3 Ethernet adapters", 0, CLS_NETWORK}, {"lnc", "Isolan, Novell NE2100/NE32-VL Ethernet adapters", 0,CLS_NETWORK}, {"ze", "IBM/National Semiconductor PCMCIA Ethernet adapter",0, CLS_NETWORK}, {"zp", "3COM PCMCIA Etherlink III Ethernet adapter", 0, CLS_NETWORK}, {"de", "DEC DC21040 Ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"fpa", "DEC DEFPA PCI FDDI adapter", FLG_FIXED, CLS_NETWORK}, {"sio", "8250/16450/16550 Serial port", 0, CLS_COMMS}, {"cx", "Cronyx/Sigma multiport sync/async adapter",0, CLS_COMMS}, {"rc", "RISCom/8 multiport async adapter", 0, CLS_COMMS}, {"cy", "Cyclades multiport async adapter", 0, CLS_COMMS}, {"lpt", "Parallel printer port", 0, CLS_COMMS}, {"nic", "ISDN driver", 0, CLS_COMMS}, {"nnic", "ISDN driver", 0, CLS_COMMS}, {"gp", "National Instruments AT-GPIB/TNT driver", 0, CLS_COMMS}, {"mse", "Microsoft Bus Mouse", 0, CLS_INPUT}, {"psm", "PS/2 Mouse", 0, CLS_INPUT}, {"joy", "Joystick", FLG_FIXED, CLS_INPUT}, {"vt", "PCVT console driver", FLG_FIXED, CLS_INPUT}, {"sc", "Syscons console driver", FLG_FIXED, CLS_INPUT}, {"sb", "Soundblaster PCM (SB, SBPro, SB16, ProAudio Spectrum)",0,CLS_MMEDIA}, {"sbxvi", "Soundblaster 16", 0, CLS_MMEDIA}, {"sbmidi", "Soundblaster MIDI interface", 0, CLS_MMEDIA}, {"pas", "ProAudio Spectrum PCM and MIDI", 0, CLS_MMEDIA}, {"gus", "Gravis Ultrasound, Ultrasound 16 and Ultrasound MAX",0,CLS_MMEDIA}, {"gusxvi", "Gravis Ultrasound 16-bit PCM", 0, CLS_MMEDIA}, {"gusmax", "Gravis Ultrasound MAX", 0, CLS_MMEDIA}, {"mss", "Microsoft Sound System", 0, CLS_MMEDIA}, {"opl", "OPL-2/3 FM, Soundblaster, SBPro, SB16, ProAudio Spectrum",0,CLS_MMEDIA}, {"mpu", "Roland MPU401 MIDI", 0, CLS_MMEDIA}, {"uart", "6850 MIDI UART", 0, CLS_MMEDIA}, {"pca", "PC speaker PCM audio driver", FLG_FIXED, CLS_MMEDIA}, {"ctx", "Coretex-I frame grabber", 0, CLS_MMEDIA}, {"spigot", "Creative Labs Video Spigot video capture", 0, CLS_MMEDIA}, {"gsc", "Genius GS-4500 hand scanner", 0, CLS_MMEDIA}, {"qcam", "QuickCam parallel port camera", 0, CLS_MMEDIA}, {"apm", "Advanced Power Management", FLG_FIXED, CLS_MISC}, {"labpc", "National Instruments Lab-PC/Lab-PC+", 0, CLS_MISC}, {"npx", "Math coprocessor", FLG_INVISIBLE, CLS_MISC}, {"lkm", "Loadable PCI driver support", FLG_INVISIBLE, CLS_MISC}, {"vga", "Catchall PCI VGA driver", FLG_INVISIBLE, CLS_MISC}, {"chip", "PCI chipset support", FLG_INVISIBLE, CLS_MISC}, #endif {"","",0,0}}; typedef struct _devlist_struct { char name[80]; int attrib; /* flag values as per the FLG_* defines above */ int class; /* disk, etc as per the CLS_* defines above */ char dev[16]; int iobase,irq,drq,maddr,msize,unit,flags,conflict_ok,id; int comment; /* 0 = device, 1 = comment, 2 = collapsed comment */ int conflicts; /* set/reset by findconflict, count of conflicts */ int changed; /* nonzero if the device has been edited */ struct isa_device *device; struct _devlist_struct *prev,*next; } DEV_LIST; #define DEV_DEVICE 0 #define DEV_COMMENT 1 #define DEV_ZOOMED 2 #define LIST_CURRENT (1<<0) #define LIST_SELECTED (1<<1) #define KEY_EXIT 0 /* return codes from dolist() and friends */ #define KEY_DO 1 #define KEY_DEL 2 #define KEY_TAB 3 #define KEY_REDRAW 4 #define KEY_UP 5 /* these only returned from editval() */ #define KEY_DOWN 6 #define KEY_LEFT 7 #define KEY_RIGHT 8 #define KEY_NULL 9 /* this allows us to spin & redraw */ #define KEY_ZOOM 10 /* these for zoom all/collapse all */ #define KEY_UNZOOM 11 static void redraw(void); static void insdev(DEV_LIST *dev, DEV_LIST *list); static int devinfo(DEV_LIST *dev); static int visuserconfig(void); static DEV_LIST *active = NULL,*inactive = NULL; /* driver lists */ static DEV_LIST *alist,*ilist; /* visible heads of the driver lists */ static DEV_LIST scratch; /* scratch record */ static int conflicts; /* total conflict count */ static char lines[] = "--------------------------------------------------------------------------------"; static char spaces[] = " "; /** ** Device manipulation stuff : find, describe, configure. **/ /** ** setdev ** ** Sets the device referenced by (*dev) to the parameters in the struct, ** and the enable flag according to (enabled) **/ static void setdev(DEV_LIST *dev, int enabled) { if (!dev->device) /* PCI device */ return; dev->device->id_iobase = dev->iobase; /* copy happy */ dev->device->id_irq = (u_short)(dev->irq < 16 ? 1<irq : 0); /* IRQ is bitfield */ dev->device->id_drq = (short)dev->drq; dev->device->id_maddr = (caddr_t)dev->maddr; dev->device->id_msize = dev->msize; dev->device->id_flags = dev->flags; dev->device->id_enabled = enabled; } /** ** getdevs ** ** Walk the kernel device tables and build the active and inactive lists **/ static void getdevs(void) { int i,j; struct isa_device *ap; for (j = 0; devtabs[j]; j++) /* ISA devices */ { ap = devtabs[j]; /* pointer to array of devices */ for (i = 0; ap[i].id_id; i++) /* for each device in this table */ { scratch.unit = ap[i].id_unit; /* device parameters */ strcpy(scratch.dev,ap[i].id_driver->name); scratch.iobase = ap[i].id_iobase; scratch.irq = ffs(ap[i].id_irq)-1; scratch.drq = ap[i].id_drq; scratch.maddr = (int)ap[i].id_maddr; scratch.msize = ap[i].id_msize; scratch.flags = ap[i].id_flags; scratch.conflict_ok = ap[i].id_conflicts; scratch.comment = DEV_DEVICE; /* admin stuff */ scratch.conflicts = 0; scratch.device = &ap[i]; /* save pointer for later reference */ scratch.changed = 0; if (!devinfo(&scratch)) /* get more info on the device */ insdev(&scratch,ap[i].id_enabled?active:inactive); } } #if NPCI > 0 for (i = 0; i < pcidevice_set.ls_length; i++) { if (pcidevice_set.ls_items[i]) { if (((struct pci_device *)pcidevice_set.ls_items[i])->pd_name) { strcpy(scratch.dev,((struct pci_device *)pcidevice_set.ls_items[i])->pd_name); scratch.iobase = -2; /* mark as PCI for future reference */ scratch.irq = -2; scratch.drq = -2; scratch.maddr = -2; scratch.msize = -2; scratch.flags = 0; scratch.conflict_ok = 0; /* shouldn't conflict */ scratch.comment = DEV_DEVICE; /* is a device */ scratch.unit = 0; /* arbitrary number of them */ scratch.conflicts = 0; scratch.device = NULL; scratch.changed = 0; if (!devinfo(&scratch)) insdev(&scratch,active); /* always active */ } } } #endif /* NPCI > 0 */ } /** ** Devinfo ** ** Fill in (dev->name), (dev->attrib) and (dev->type) from the device_info array. ** If the device is unknown, put it in the CLS_MISC class, with no flags. ** ** If the device is marked "invisible", return nonzero; the caller should ** not insert any such device into either list. **/ static int devinfo(DEV_LIST *dev) { int i; for (i = 0; device_info[i].class; i++) { if (!strcmp(dev->dev,device_info[i].dev)) { if (device_info[i].attrib & FLG_INVISIBLE) return(1); strcpy(dev->name,device_info[i].name); dev->attrib = device_info[i].attrib; dev->class = device_info[i].class; return(0); } } strcpy(dev->name,"Unknown device"); dev->attrib = 0; dev->class = CLS_MISC; return(0); } /** ** List manipulation stuff : add, move, initialise, free, traverse ** ** Note that there are assumptions throughout this code that ** the first entry in a list will never move. (assumed to be ** a comment). **/ /** ** Adddev ** ** appends a copy of (dev) to the end of (*list) **/ static void addev(DEV_LIST *dev, DEV_LIST **list) { DEV_LIST *lp,*ap; lp = (DEV_LIST *)malloc(sizeof(DEV_LIST),M_DEVL,M_WAITOK); bcopy(dev,lp,sizeof(DEV_LIST)); /* create copied record */ if (*list) /* list exists */ { ap = *list; while(ap->next) ap = ap->next; /* scoot to end of list */ lp->prev = ap; lp->next = NULL; ap->next = lp; }else{ /* list does not yet exist */ *list = lp; lp->prev = lp->next = NULL; /* list now exists */ } } /** ** Findspot ** ** Finds the 'appropriate' place for (dev) in (list) ** ** 'Appropriate' means in numeric order with other devices of the same type, ** or in alphabetic order following a comment of the appropriate type. ** or at the end of the list if an appropriate comment is not found. (this should ** never happen) ** (Note that the appropriate point is never the top, but may be the bottom) **/ static DEV_LIST * findspot(DEV_LIST *dev, DEV_LIST *list) { DEV_LIST *ap; for (ap = list; ap; ap = ap->next) { if (ap->comment != DEV_DEVICE) /* ignore comments */ continue; if (!strcmp(dev->dev,ap->dev)) /* same base device */ { if ((dev->unit <= ap->unit) /* belongs before (equal is bad) */ || !ap->next) /* or end of list */ { ap = ap->prev; /* back up one */ break; /* done here */ } if (ap->next) /* if the next item exists */ { if (ap->next->comment != DEV_DEVICE) /* next is a comment */ break; if (strcmp(dev->dev,ap->next->dev)) /* next is a different device */ break; } } } if (!ap) { for (ap = list; ap; ap = ap->next) { if (ap->comment != DEV_DEVICE) /* look for simlar devices */ continue; if (dev->class != ap->class) /* of same class too 8) */ continue; if (strcmp(dev->dev,ap->dev) < 0) /* belongs before the current entry */ { ap = ap->prev; /* back up one */ break; /* done here */ } if (ap->next) /* if the next item exists */ if (ap->next->comment != DEV_DEVICE) /* next is a comment, go here */ break; } } if (!ap) /* didn't find a match */ { for (ap = list; ap->next; ap = ap->next) /* try for a matching comment */ if ((ap->comment != DEV_DEVICE) && (ap->class == dev->class)) /* appropriate place? */ break; } /* or just put up with last */ return(ap); } /** ** Insdev ** ** Inserts a copy of (dev) at the appropriate point in (list) **/ static void insdev(DEV_LIST *dev, DEV_LIST *list) { DEV_LIST *lp,*ap; lp = (DEV_LIST *)malloc(sizeof(DEV_LIST),M_DEVL,M_WAITOK); bcopy(dev,lp,sizeof(DEV_LIST)); /* create copied record */ ap = findspot(lp,list); /* find appropriate spot */ lp->next = ap->next; /* point to next */ if (ap->next) ap->next->prev = lp; /* point next to new */ lp->prev = ap; /* point new to current */ ap->next = lp; /* and current to new */ } /** ** Movedev ** ** Moves (dev) from its current list to an appropriate place in (list) ** (dev) may not come from the top of a list, but it may from the bottom. **/ static void movedev(DEV_LIST *dev, DEV_LIST *list) { DEV_LIST *ap; ap = findspot(dev,list); dev->prev->next = dev->next; /* remove from old list */ if (dev->next) dev->next->prev = dev->prev; dev->next = ap->next; /* insert in new list */ if (ap->next) ap->next->prev = dev; /* point next to new */ dev->prev = ap; /* point new to current */ ap->next = dev; /* and current to new */ } /** ** Initlist ** ** Initialises (*list) with the basic headings **/ static void initlist(DEV_LIST **list) { int i; for(i = 0; devclass_names[i].name[0]; i++) /* for each devtype name */ { strcpy(scratch.name,devclass_names[i].name); scratch.comment = DEV_ZOOMED; scratch.class = devclass_names[i].number; scratch.attrib = FLG_MANDATORY; /* can't be moved */ addev(&scratch,list); /* add to the list */ } } /** ** savelist ** ** Walks (list) and saves the settings of any entry marked as changed. ** ** The device's active field is set according to (active). ** ** Builds the isa_devlist used by dset to extract the changed device information. ** The code for this was taken almost verbatim from the original module. **/ static void savelist(DEV_LIST *list, int active) { struct isa_device *id_p,*id_pn; while (list) { if ((list->comment == DEV_DEVICE) && list->changed) { setdev(list,active); /* set the device itself */ id_pn = NULL; for (id_p=isa_devlist; id_p; id_p=id_p->id_next) { /* look on the list for it */ if (id_p->id_id == list->device->id_id) { id_pn = id_p->id_next; bcopy(list->device,id_p,sizeof(struct isa_device)); id_p->id_next = id_pn; break; } } if (!id_pn) /* not already on the list */ { id_pn = malloc(sizeof(struct isa_device),M_DEVL,M_WAITOK); bcopy(list->device,id_pn,sizeof(struct isa_device)); id_pn->id_next = isa_devlist; isa_devlist = id_pn; /* park at top of list */ } } list = list->next; } } /** ** nukelist ** ** Frees all storage in use by a (list). **/ static void nukelist(DEV_LIST *list) { DEV_LIST *dp; if (!list) return; while(list->prev) /* walk to head of list */ list = list->prev; while(list) { dp = list; list = list->next; free(dp,M_DEVL); } } /** ** prevent ** ** Returns the previous entry in (list), skipping zoomed regions. Returns NULL ** if there is no previous entry. (Only possible if list->prev == NULL given the ** premise that there is always a comment at the head of the list) **/ static DEV_LIST * prevent(DEV_LIST *list) { DEV_LIST *dp; if (!list) return(NULL); dp = list->prev; /* start back one */ while(dp) { if (dp->comment == DEV_ZOOMED) /* previous section is zoomed */ return(dp); /* so skip to comment */ if (dp->comment == DEV_COMMENT) /* not zoomed */ return(list->prev); /* one back as normal */ dp = dp->prev; /* backpedal */ } return(dp); /* NULL, we can assume */ } /** ** nextent ** ** Returns the next entry in (list), skipping zoomed regions. Returns NULL ** if there is no next entry. (Possible if the current entry is last, or ** if the current entry is the last heading and it's collapsed) **/ static DEV_LIST * nextent(DEV_LIST *list) { DEV_LIST *dp; if (!list) return(NULL); if (list->comment != DEV_ZOOMED) /* no reason to skip */ return(list->next); dp = list->next; while(dp) { if (dp->comment != DEV_DEVICE) /* found another heading */ break; dp = dp->next; } return(dp); /* back we go */ } /** ** ofsent ** ** Returns the (ofs)th entry down from (list), or NULL if it doesn't exist **/ static DEV_LIST * ofsent(int ofs, DEV_LIST *list) { while (ofs-- && list) list = nextent(list); return(list); } /** ** findconflict ** ** Scans every element of (list) and sets the conflict tags appropriately ** Returns the number of conflicts found. **/ static int findconflict(DEV_LIST *list) { int count = 0; /* number of conflicts found */ DEV_LIST *dp,*sp; for (dp = list; dp; dp = dp->next) /* over the whole list */ { if (dp->comment != DEV_DEVICE) /* comments don't usually conflict */ continue; dp->conflicts = 0; /* assume the best */ for (sp = list; sp; sp = sp->next) /* scan the entire list for conflicts */ { if (sp->comment != DEV_DEVICE) /* likewise */ continue; if (sp == dp) /* always conflict with itself */ continue; if (sp->conflict_ok && dp->conflict_ok) continue; /* both allowed to conflict */ if ((dp->iobase > 0) && /* iobase conflict? */ (dp->iobase == sp->iobase)) dp->conflicts = 1; if ((dp->irq > 0) && /* irq conflict? */ (dp->irq == sp->irq)) dp->conflicts = 1; if ((dp->drq > 0) && /* drq conflict? */ (dp->drq == sp->drq)) dp->conflicts = 1; if ((dp->maddr > 0) && /* maddr conflict? */ (dp->maddr == sp->maddr)) dp->conflicts = 1; if ((dp->msize > 0) && /* msize conflict? */ (dp->msize == sp->msize)) dp->conflicts = 1; } count += dp->conflicts; /* count conflicts */ } return(count); } /** ** expandlist ** ** Unzooms all headings in (list) **/ static void expandlist(DEV_LIST *list) { while(list) { if (list->comment == DEV_COMMENT) list->comment = DEV_ZOOMED; list = list->next; } } /** ** collapselist ** ** Zooms all headings in (list) **/ static void collapselist(DEV_LIST *list) { while(list) { if (list->comment == DEV_ZOOMED) list->comment = DEV_COMMENT; list = list->next; } } /** ** Screen-manipulation stuff ** ** This is the basic screen layout : ** ** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75 ** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|.... ** +--------------------------------------------------------------------------------+ ** 0 -|---Active Drivers----------------------------xx Conflicts------Dev---IRQ--Port--| ** 1 -| ........................ ....... .. 0x....| ** 2 -| ........................ ....... .. 0x....| ** 3 -| ........................ ....... .. 0x....| ** 4 -| ........................ ....... .. 0x....| ** 5 -| ........................ ....... .. 0x....| ** 6 -| ........................ ....... .. 0x....| ** 7 -| ........................ ....... .. 0x....| ** 8 -| ........................ ....... .. 0x....| ** 9 -|---Inactive Drivers--------------------------------------------Dev--------------| ** 10-| ........................ ....... | ** 11-| ........................ ....... | ** 12-| ........................ ....... | ** 13-| ........................ ....... | ** 14-| ........................ ....... | ** 15-| ........................ ....... | ** 16-| ........................ ....... | ** 17-|------------------------------------------------------UP-DOWN-------------------| ** 18-| Relevant parameters for the current device | ** 19-| | ** 20-| | ** 21-|--------------------------------------------------------------------------------| ** 22-| Help texts go here | ** 23-| | ** +--------------------------------------------------------------------------------+ ** ** Help texts ** ** On a collapsed comment : ** ** [Enter] Expand device list [z] Expand all lists ** [TAB] Change fields [Q] Save and Exit ** ** On an expanded comment : ** ** [Enter] Collapse device list [Z] Collapse all lists ** [TAB] Change fields [Q] Save and Exit ** ** On a comment with no followers ** ** ** [TAB] Change fields [Q] Save and Exit ** ** On a device in the active list ** ** [Enter] Edit device parameters [DEL] Disable device ** [TAB] Change fields [Q] Save and Exit ** ** On a device in the inactive list ** ** [Enter] Enable device ** [TAB] Change fields [Q] Save and Exit ** ** While editing parameters ** ** ** [TAB] Change fields [Q] Save device parameters **/ /** ** ** The base-level screen primitives : ** ** bold() - enter bold mode \E[1m (md) ** inverse() - enter inverse mode \E[7m (so) ** normal() - clear bold/inverse mode \E[m (se) ** clear() - clear the screen \E[H\E[J (ce) ** move(x,y) - move the cursor to x,y \E[y;xH: (cm) **/ static void bold(void) { printf("\033[1m"); } static void inverse(void) { printf("\033[7m"); } static void normal(void) { printf("\033[m"); } static void clear(void) { normal(); printf("\033[H\033[J"); } static void move(int x, int y) { printf("\033[%d;%dH",y+1,x+1); } /** ** ** High-level screen primitives : ** ** putxyl(x,y,str,len) - put (len) bytes of (str) at (x,y), supports embedded formatting ** putxy(x,y,str) - put (str) at (x,y), supports embedded formatting ** erase(x,y,w,h) - clear the box (x,y,w,h) ** txtbox(x,y,w,y,str) - put (str) in a region at (x,y,w,h) ** putmsg(str) - put (str) in the message area ** puthelp(str) - put (str) in the upper helpline ** pad(str,len) - pad (str) to (len) with spaces ** drawline(row,detail,list,inverse,*dhelp) ** - draws a line for (*list) at (row) onscreen. If (detail) is ** nonzero, include port, IRQ and maddr, if (inverse) is nonzero, ** draw the line in inverse video, and display (*dhelp) on the ** helpline. ** drawlist(row,num,detail,list) ** - draw (num) entries from (list) at (row) onscreen, passile (detail) ** through to drawline(). ** showparams(dev) - displays the relevant parameters for (dev) below the lists onscreen. ** yesno(str) - displays (str) in the message area, and returns nonzero on 'y' or 'Y' ** redraw(); - Redraws the entire screen layout, including the ** - two list panels. **/ /** ** putxy ** writes (str) at x,y onscreen ** putxyl ** writes up to (len) of (str) at x,y onscreen. ** ** Supports embedded formatting : ** !i - inverse mode. ** !b - bold mode. ** !n - normal mode. **/ static void putxyl(int x, int y, char *str, int len) { move(x,y); normal(); while((*str) && (len--)) { if (*str == '!') /* format escape? */ { switch(*(str+1)) /* depending on the next character */ { case 'i': inverse(); str +=2; /* skip formatting */ len++; /* doesn't count for length */ break; case 'b': bold(); str +=2; /* skip formatting */ len++; /* doesn't count for length */ break; case 'n': normal(); str +=2; /* skip formatting */ len++; /* doesn't count for length */ break; default: putchar(*str++); /* not an escape */ } }else{ putchar(*str++); /* emit the character */ } } } #define putxy(x,y,str) putxyl(x,y,str,-1) /** ** erase ** ** Erases the region (x,y,w,h) **/ static void erase(int x, int y, int w, int h) { int i; normal(); for (i = 0; i < h; i++) putxyl(x,y++,spaces,w); } /** ** txtbox ** ** Writes (str) into the region (x,y,w,h), supports embedded formatting using ** putxy. Lines are not wrapped, newlines must be forced with \n. **/ static void txtbox(int x, int y, int w, int h, char *str) { int i = 0; h--; while((str[i]) && h) { if (str[i] == '\n') /* newline */ { putxyl(x,y,str,(i= len) /* no padding needed */ return; while(i < len) /* pad */ str[i++] = ' '; str[i] = '\0'; } /** ** drawline ** ** Displays entry (ofs) of (list) in region at (row) onscreen, optionally displaying ** the port and IRQ fields if (detail) is nonzero. If (inverse), in inverse video. ** ** The text (dhelp) is displayed if the item is a normal device, otherwise ** help is shown for normal or zoomed comments **/ static void drawline(int row, int detail, DEV_LIST *list, int inverse, char *dhelp) { char lbuf[90],nb[70],db[20],ib[16],pb[16]; if (list->comment == DEV_DEVICE) { nb[0] = ' '; strncpy(nb+1,list->name,57); }else{ strncpy(nb,list->name,58); if ((list->comment == DEV_ZOOMED) && (list->next)) if (list->next->comment == DEV_DEVICE) /* only mention if there's something hidden */ strcat(nb," (Collapsed)"); } nb[58] = '\0'; pad(nb,60); if (list->conflicts) /* device in conflict? */ if (inverse) { strcpy(nb+54," !nCONF!i "); /* tag conflict, careful of length */ }else{ strcpy(nb+54," !iCONF!n "); /* tag conflict, careful of length */ } if (list->comment == DEV_DEVICE) { sprintf(db,"%s%d",list->dev,list->unit); pad(db,8); }else{ strcpy(db," "); } if ((list->irq > 0) && detail && (list->comment == DEV_DEVICE)) { sprintf(ib," %d",list->irq); pad(ib,4); }else{ strcpy(ib," "); } if ((list->iobase > 0) && detail && (list->comment == DEV_DEVICE)) { sprintf(pb,"0x%x",list->iobase); pad(pb,7); }else{ strcpy(pb," "); } sprintf(lbuf," %s%s%s%s%s",inverse?"!i":"",nb,db,ib,pb); putxyl(0,row,lbuf,80); if (dhelp) { switch(list->comment) { case DEV_DEVICE: /* ordinary device */ puthelp(dhelp); break; case DEV_COMMENT: puthelp(""); if (list->next) if (list->next->comment == DEV_DEVICE) puthelp(" [!bEnter!n] Collapse device list [!bC!n] Collapse all lists"); break; case DEV_ZOOMED: puthelp(""); if (list->next) if (list->next->comment == DEV_DEVICE) puthelp(" [!bEnter!n] Expand device list [!bX!n] Expand all lists"); break; default: puthelp(" WARNING: This list entry corrupted!"); break; } } move(0,row); /* put the cursor somewhere relevant */ } /** ** drawlist ** ** Displays (num) lines of the contents of (list) at (row), optionally displaying the ** port and IRQ fields as well if (detail) is nonzero ** ** printf in the kernel is essentially useless, so we do most of the hard work ourselves here. **/ static void drawlist(int row, int num, int detail, DEV_LIST *list) { int ofs; for(ofs = 0; ofs < num; ofs++) { if (list) { drawline(row+ofs,detail,list,0,""); list = nextent(list); /* move down visible list */ }else{ erase(0,row+ofs,80,1); } } } /** ** redrawactive ** ** Redraws the active list **/ static void redrawactive(void) { char cbuf[16]; if (conflicts) { sprintf(cbuf,"!i%d conflict%s",conflicts,(conflicts>1)?"s":""); putxy(45,0,cbuf); }else{ putxyl(45,0,lines,16); } drawlist(1,8,1,alist); /* draw device lists */ } /** ** redrawinactive ** ** Redraws the inactive list **/ static void redrawinactive(void) { drawlist(10,7,0,ilist); /* draw device lists */ } /** ** redraw ** ** Clear the screen and redraw the entire layout **/ static void redraw(void) { clear(); putxy(0,0,lines); putxy(3,0,"!bActive!n-!bDrivers"); putxy(63,0,"!bDev!n---!bIRQ!n--!bPort"); putxy(0,9,lines); putxy(3,9,"!bInactive!n-!bDrivers"); putxy(63,9,"!bDev"); putxy(0,17,lines); putxy(0,21,lines); masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save and Exit"); redrawactive(); redrawinactive(); } /** ** yesnocancel ** ** Put (str) in the message area, and return 1 if the user hits 'y' or 'Y', ** 2 if they hit 'c' or 'C', or 0 for 'n' or 'N'. **/ static int yesnocancel(char *str) { putmsg(str); for(;;) switch(getchar()) { case 'n': case 'N': return(0); case 'y': case 'Y': return(1); case 'c': case 'C': return(2); } } /** ** showparams ** ** Show device parameters in the region below the lists ** ** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75 ** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|.... ** +--------------------------------------------------------------------------------+ ** 17-|--------------------------------------------------------------------------------| ** 18-| Port address : 0x0000 Memory address : 0x00000 Conflict allowed | ** 19-| IRQ number : 00 Memory size : 0x0000 | ** 20-| Flags : 0x0000 DRQ number : 00 | ** 21-|--------------------------------------------------------------------------------| **/ static void showparams(DEV_LIST *dev) { char buf[80]; erase(0,18,80,3); /* clear area */ if (!dev) return; if (dev->comment != DEV_DEVICE) return; if (dev->iobase > 0) { sprintf(buf,"Port address : 0x%x",dev->iobase); putxy(1,18,buf); } if (dev->irq > 0) { sprintf(buf,"IRQ number : %d",dev->irq); putxy(1,19,buf); } sprintf(buf,"Flags : 0x%x",dev->flags); putxy(1,20,buf); if (dev->maddr > 0) { sprintf(buf,"Memory address : 0x%x",dev->maddr); putxy(26,18,buf); } if (dev->msize > 0) { sprintf(buf,"Memory size : 0x%x",dev->msize); putxy(26,19,buf); } if (dev->drq > 0) { sprintf(buf,"DRQ number : %d",dev->drq); putxy(26,20,buf); } if (dev->conflict_ok) putxy(54,18,"Conflict allowed"); } /** ** Editing functions for device parameters ** ** editval(x,y,width,hex,min,max,val) - Edit (*val) in a field (width) wide at (x,y) ** onscreen. Refuse values outsise (min) and (max). ** editparams(dev) - Edit the parameters for (dev) **/ #define VetRet(code) \ { \ if ((i >= min) && (i <= max)) /* legit? */ \ { \ *val = i; \ sprintf(buf,hex?"0x%x":"%d",i); \ putxy(hex?x-2:x,y,buf); \ return(code); /* all done and exit */ \ } \ i = *val; /* restore original value */ \ delta = 1; /* restore other stuff */ \ } /** ** editval ** ** Edit (*val) at (x,y) in (hex)?hex:decimal mode, allowing values between (min) and (max) ** in a field (width) wide. (Allow one space) ** If (ro) is set, we're in "readonly" mode, so disallow edits. ** ** Return KEY_TAB on \t, KEY_EXIT on 'q' **/ static int editval(int x, int y, int width, int hex, int min, int max, int *val, int ro) { int i = *val; /* work with copy of the value */ char buf[10],tc[8]; /* display buffer, text copy */ int xp = 0; /* cursor offset into text copy */ int delta = 1; /* force redraw first time in */ int c; int extended = 0; /* stage counter for extended key sequences */ if (hex) /* we presume there's a leading 0x onscreen */ putxy(x-2,y,"!i0x"); /* coz there sure is now */ for (;;) { if (delta) /* only update if necessary */ { sprintf(tc,hex?"%x":"%d",i); /* make a text copy of the value */ sprintf(buf,"!i%s",tc); /* format for printing */ erase(x,y,width,1); /* clear the area */ putxy(x,y,buf); /* write */ xp = strlen(tc); /* cursor always at end */ move(x+xp,y); /* position the cursor */ } c = getchar(); switch(extended) /* escape handling */ { case 0: if (c == 0x1b) /* esc? */ { extended = 1; /* flag and spin */ continue; } extended = 0; break; /* nope, drop through */ case 1: /* there was an escape prefix */ if (c == '[' || c == 'O') /* second character in sequence */ { extended = 2; continue; } if (c == 0x1b) return(KEY_EXIT); /* double esc exits */ extended = 0; break; /* nup, not a sequence. */ case 2: extended = 0; switch(c) /* looks like the real McCoy */ { case 'A': VetRet(KEY_UP); /* leave if OK */ continue; case 'B': VetRet(KEY_DOWN); /* leave if OK */ continue; case 'C': VetRet(KEY_RIGHT); /* leave if OK */ continue; case 'D': VetRet(KEY_LEFT); /* leave if OK */ continue; default: continue; } } switch(c) { case '\t': /* trying to tab off */ VetRet(KEY_TAB); /* verify and maybe return */ break; case 'q': case 'Q': VetRet(KEY_EXIT); break; case '\b': case '\177': /* BS or DEL */ if (ro) /* readonly? */ { puthelp(" !iThis value cannot be edited (Press ESC)"); while(getchar() != 0x1b); /* wait for key */ return(KEY_NULL); /* spin */ } if (xp) /* still something left to delete */ { i = i / (hex?0x10:10); /* strip last digit */ delta = 1; /* force update */ } break; case 588: VetRet(KEY_UP); break; case 596: VetRet(KEY_DOWN); break; case 591: VetRet(KEY_LEFT); break; case 593: VetRet(KEY_RIGHT); break; default: if (ro) /* readonly? */ { puthelp(" !iThis value cannot be edited (Press ESC)"); while(getchar() != 0x1b); /* wait for key */ return(KEY_NULL); /* spin */ } if (xp >= width) /* no room for more characters anyway */ break; if (hex) { if ((c >= '0') && (c <= '9')) { i = i*0x10 + (c-'0'); /* update value */ delta = 1; break; } if ((c >= 'a') && (c <= 'f')) { i = i*0x10 + (c-'a'+0xa); delta = 1; break; } if ((c >= 'A') && (c <= 'F')) { i = i*0x10 + (c-'A'+0xa); delta = 1; break; } }else{ if ((c >= '0') && (c <= '9')) { i = i*10 + (c-'0'); /* update value */ delta = 1; /* force redraw */ break; } } break; } } } /** ** editparams ** ** Edit the parameters for (dev) ** ** Note that it's _always_ possible to edit the flags, otherwise it might be ** possible for this to spin in an endless loop... ** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75 ** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|.... ** +--------------------------------------------------------------------------------+ ** 17-|--------------------------------------------------------------------------------| ** 18-| Port address : 0x0000 Memory address : 0x00000 Conflict allowed | ** 19-| IRQ number : 00 Memory size : 0x0000 | ** 20-| Flags : 0x0000 DRQ number : 00 | ** 21-|--------------------------------------------------------------------------------| ** ** The "intelligence" in this function that hops around based on the directional ** returns from editval isn't very smart, and depends on the layout above. **/ static void editparams(DEV_LIST *dev) { int ret; char buf[16]; /* needs to fit the device name */ putxy(2,17,"!bParameters!n-!bfor!n-!bdevice!n-"); sprintf(buf,"!b%s",dev->dev); putxy(24,17,buf); erase(1,22,80,1); for (;;) { ep_iobase: if (dev->iobase > 0) { #ifdef PC98 puthelp(" IO Port address (Hexadecimal, 0x1-0xffff)"); ret = editval(18,18,5,1,0x1,0xffff,&(dev->iobase),(dev->attrib & FLG_FIXIOBASE)); #else puthelp(" IO Port address (Hexadecimal, 0x1-0x2000)"); ret = editval(18,18,5,1,0x1,0x2000,&(dev->iobase),(dev->attrib & FLG_FIXIOBASE)); #endif switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_RIGHT: if (dev->maddr > 0) goto ep_maddr; break; case KEY_TAB: case KEY_DOWN: goto ep_irq; } goto ep_iobase; } ep_irq: if (dev->irq > 0) { puthelp(" Interrupt number (Decimal, 1-15)"); ret = editval(16,19,3,0,1,15,&(dev->irq),(dev->attrib & FLG_FIXIRQ)); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_RIGHT: if (dev->msize > 0) goto ep_msize; break; case KEY_UP: if (dev->iobase > 0) goto ep_iobase; break; case KEY_TAB: case KEY_DOWN: goto ep_flags; } goto ep_irq; } ep_flags: puthelp(" Device-specific flag values."); ret = editval(18,20,5,1,0x0,0xffff,&(dev->flags),0); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_RIGHT: if (dev->drq > 0) goto ep_drq; break; case KEY_UP: if (dev->irq > 0) goto ep_irq; if (dev->iobase > 0) goto ep_iobase; break; case KEY_DOWN: if (dev->maddr > 0) goto ep_maddr; if (dev->msize > 0) goto ep_msize; if (dev->drq > 0) goto ep_drq; break; case KEY_TAB: goto ep_maddr; } goto ep_flags; ep_maddr: if (dev->maddr > 0) { puthelp(" Device memory start address (Hexadecimal, 0x1-0xfffff)"); ret = editval(45,18,6,1,0x1,0xfffff,&(dev->maddr),(dev->attrib & FLG_FIXMADDR)); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_LEFT: if (dev->iobase > 0) goto ep_iobase; break; case KEY_UP: goto ep_flags; case KEY_DOWN: if (dev->msize > 0) goto ep_msize; if (dev->drq > 0) goto ep_drq; break; case KEY_TAB: goto ep_msize; } goto ep_maddr; } ep_msize: if (dev->msize > 0) { puthelp(" Device memory size (Hexadecimal, 0x1-0x10000)"); ret = editval(45,19,5,1,0x1,0x10000,&(dev->msize),(dev->attrib & FLG_FIXMSIZE)); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_LEFT: if (dev->irq > 0) goto ep_irq; break; case KEY_UP: if (dev->maddr > 0) goto ep_maddr; goto ep_flags; case KEY_DOWN: if (dev->drq > 0) goto ep_drq; break; case KEY_TAB: goto ep_drq; } goto ep_msize; } ep_drq: if (dev->drq > 0) { puthelp(" Device DMA request number (Decimal, 1-7)"); ret = editval(43,20,2,0,1,7,&(dev->drq),(dev->attrib & FLG_FIXDRQ)); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_LEFT: goto ep_flags; case KEY_UP: if (dev->msize > 0) goto ep_msize; if (dev->maddr > 0) goto ep_maddr; goto ep_flags; case KEY_TAB: goto ep_iobase; } goto ep_drq; } } ep_exit: dev->changed = 1; /* mark as changed */ } /** ** High-level control functions **/ /** ** dolist ** ** Handle user movement within (*list) in the region starting at (row) onscreen with ** (num) lines, starting at (*ofs) offset from row onscreen. ** Pass (detail) on to drawing routines. ** ** If the user hits a key other than a cursor key, maybe return a code. ** ** (*list) points to the device at the top line in the region, (*ofs) is the ** position of the highlight within the region. All routines below ** this take only a device and an absolute row : use ofsent() to find the ** device, and add (*ofs) to (row) to find the absolute row. **/ static int dolist(int row, int num, int detail, int *ofs, DEV_LIST **list, char *dhelp) { int extended = 0; int c; DEV_LIST *lp; int delta = 1; for(;;) { if (delta) { showparams(ofsent(*ofs,*list)); /* show device parameters */ drawline(row+*ofs,detail,ofsent(*ofs,*list),1,dhelp); /* highlight current line */ delta = 0; } c = getchar(); /* get a character */ if ((extended == 2) || (c==588) || (c==596)) /* console gives "alternative" codes */ { extended = 0; /* no longer */ switch(c) { case 588: /* syscons' idea of 'up' */ case 'A': /* up */ if (*ofs) /* just a move onscreen */ { drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp);/* unhighlight current line */ (*ofs)--; /* move up */ }else{ lp = prevent(*list); /* can we go up? */ if (!lp) /* no */ break; *list = lp; /* yes, move up list */ drawlist(row,num,detail,*list); } delta = 1; break; case 596: /* dooby-do */ case 'B': /* down */ lp = ofsent(*ofs,*list); /* get current item */ if (!nextent(lp)) break; /* nothing more to move to */ drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp); /* unhighlight current line */ if (*ofs < (num-1)) /* room to move onscreen? */ { (*ofs)++; }else{ *list = nextent(*list); /* scroll region down */ drawlist(row,num,detail,*list); } delta = 1; break; } }else{ switch(c) { case '\033': extended=1; break; case '[': /* cheat : always preceeds cursor move */ case 'O': /* ANSI application key mode */ if (extended==1) extended=2; else extended=0; break; case 'Q': case 'q': return(KEY_EXIT); /* user requests exit */ case '\r': case '\n': return(KEY_DO); /* "do" something */ case '\b': case '\177': case 599: return(KEY_DEL); /* "delete" response */ case 'X': case 'x': return(KEY_UNZOOM); /* expand everything */ case 'C': case 'c': return(KEY_ZOOM); /* collapse everything */ case '\t': drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp); /* unhighlight current line */ return(KEY_TAB); /* "move" response */ case '\014': /* ^L, redraw */ return(KEY_REDRAW); } } } } /** ** visuserconfig ** ** Do the fullscreen config thang **/ static int visuserconfig(void) { int actofs = 0, inactofs = 0, mode = 0, ret = -1, i; DEV_LIST *dp; initlist(&active); initlist(&inactive); alist = active; ilist = inactive; getdevs(); conflicts = findconflict(active); /* find conflicts in the active list only */ redraw(); for(;;) { switch(mode) { case 0: /* active devices */ ret = dolist(1,8,1,&actofs,&alist, " [!bEnter!n] Edit device parameters [!bDEL!n] Disable device"); switch(ret) { case KEY_TAB: mode = 1; /* swap lists */ break; case KEY_REDRAW: redraw(); break; case KEY_ZOOM: alist = active; actofs = 0; expandlist(active); redrawactive(); break; case KEY_UNZOOM: alist = active; actofs = 0; collapselist(active); redrawactive(); break; case KEY_DEL: dp = ofsent(actofs,alist); /* get current device */ if (dp) /* paranoia... */ { if (dp->attrib & FLG_MANDATORY) /* can't be deleted */ break; if (dp == alist) /* moving top item on list? */ { if (dp->next) { alist = dp->next; /* point list to non-moving item */ }else{ alist = dp->prev; /* end of list, go back instead */ } }else{ if (!dp->next) /* moving last item on list? */ actofs--; } dp->conflicts = 0; /* no conflicts on the inactive list */ movedev(dp,inactive); /* shift to inactive list */ conflicts = findconflict(active); /* update conflict tags */ dp->changed = 1; redrawactive(); /* redraw */ redrawinactive(); } break; case KEY_DO: /* edit device parameters */ dp = ofsent(actofs,alist); /* get current device */ if (dp) /* paranoia... */ { if (dp->comment == DEV_DEVICE) /* can't edit comments, zoom? */ { masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save device parameters"); editparams(dp); masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save and Exit"); putxy(0,17,lines); conflicts = findconflict(active); /* update conflict tags */ }else{ /* DO on comment = zoom */ switch(dp->comment) /* Depends on current state */ { case DEV_COMMENT: /* not currently zoomed */ dp->comment = DEV_ZOOMED; break; case DEV_ZOOMED: dp->comment = DEV_COMMENT; break; } } redrawactive(); } break; } break; case 1: /* inactive devices */ ret = dolist(10,7,0,&inactofs,&ilist, " [!bEnter!n] Enable device "); switch(ret) { case KEY_TAB: mode = 0; break; case KEY_REDRAW: redraw(); break; case KEY_ZOOM: ilist = inactive; inactofs = 0; expandlist(inactive); redrawinactive(); break; case KEY_UNZOOM: ilist = inactive; inactofs = 0; collapselist(inactive); redrawinactive(); break; case KEY_DO: dp = ofsent(inactofs,ilist); /* get current device */ if (dp) /* paranoia... */ { if (dp->comment == DEV_DEVICE) /* can't move comments, zoom? */ { if (dp == ilist) /* moving top of list? */ { if (dp->next) { ilist = dp->next; /* point list to non-moving item */ }else{ ilist = dp->prev; /* can't go down, go up instead */ } }else{ if (!dp->next) /* last entry on list? */ inactofs--; /* shift cursor up one */ } movedev(dp,active); /* shift to active list */ conflicts = findconflict(active); /* update conflict tags */ dp->changed = 1; alist = dp; /* put at top and current */ actofs = 0; while(dp->comment == DEV_DEVICE) dp = dp->prev; /* forcibly unzoom section */ dp ->comment = DEV_COMMENT; mode = 0; /* and swap modes to follow it */ }else{ /* DO on comment = zoom */ switch(dp->comment) /* Depends on current state */ { case DEV_COMMENT: /* not currently zoomed */ dp->comment = DEV_ZOOMED; break; case DEV_ZOOMED: dp->comment = DEV_COMMENT; break; } } redrawactive(); /* redraw */ redrawinactive(); } break; default: /* nothing else relevant here */ break; } break; default: mode = 0; /* shouldn't happen... */ } if (ret == KEY_EXIT) { i = yesnocancel(" Save these parameters before exiting? (Yes/No/Cancel) "); switch(i) { case 2: /* cancel */ redraw(); break; case 1: /* save and exit */ savelist(active,1); savelist(inactive,0); case 0: /* exit */ nukelist(active); /* clean up after ourselves */ nukelist(inactive); normal(); clear(); return(1); } } } } +#endif /* VISUAL_USERCONFIG */ /* * Copyright (c) 1991 Regents of the University of California. * All rights reserved. * Copyright (c) 1994 Jordan K. Hubbard * All rights reserved. * Copyright (c) 1994 David Greenman * All rights reserved. * * Many additional changes by Bruce Evans * * This code is derived from software contributed by the * University of California Berkeley, Jordan K. Hubbard, * David Greenman and Bruce Evans. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: userconfig.c,v 1.4 1996/09/03 10:23:18 asami Exp $ + * $Id: userconfig.c,v 1.5 1996/09/10 09:37:38 asami Exp $ */ #include "scbus.h" #include #define PARM_DEVSPEC 0x1 #define PARM_INT 0x2 #define PARM_ADDR 0x3 typedef struct _cmdparm { int type; union { struct isa_device *dparm; int iparm; void *aparm; } parm; } CmdParm; typedef int (*CmdFunc)(CmdParm *); typedef struct _cmd { char *name; CmdFunc handler; CmdParm *parms; } Cmd; #if NSCBUS > 0 static void lsscsi(void); static int list_scsi(CmdParm *); #endif static void lsdevtab(struct isa_device *); static struct isa_device *find_device(char *, int); static struct isa_device *search_devtable(struct isa_device *, char *, int); static void cngets(char *, int); static Cmd *parse_cmd(char *); static int parse_args(char *, CmdParm *); static unsigned long strtoul(const char *, char **, int); static int save_dev(struct isa_device *); static int list_devices(CmdParm *); static int set_device_ioaddr(CmdParm *); static int set_device_irq(CmdParm *); static int set_device_drq(CmdParm *); static int set_device_iosize(CmdParm *); static int set_device_mem(CmdParm *); static int set_device_flags(CmdParm *); static int set_device_enable(CmdParm *); static int set_device_disable(CmdParm *); static int quitfunc(CmdParm *); static int helpfunc(CmdParm *); static int lineno; static CmdParm addr_parms[] = { { PARM_DEVSPEC, {} }, { PARM_ADDR, {} }, { -1, {} }, }; static CmdParm int_parms[] = { { PARM_DEVSPEC, {} }, { PARM_INT, {} }, { -1, {} }, }; static CmdParm dev_parms[] = { { PARM_DEVSPEC, {} }, { -1, {} }, }; static Cmd CmdList[] = { { "?", helpfunc, NULL }, /* ? (help) */ { "di", set_device_disable, dev_parms }, /* disable dev */ { "dr", set_device_drq, int_parms }, /* drq dev # */ { "en", set_device_enable, dev_parms }, /* enable dev */ { "ex", quitfunc, NULL }, /* exit (quit) */ { "f", set_device_flags, int_parms }, /* flags dev mask */ { "h", helpfunc, NULL }, /* help */ { "iom", set_device_mem, addr_parms }, /* iomem dev addr */ { "ios", set_device_iosize, int_parms }, /* iosize dev size */ { "ir", set_device_irq, int_parms }, /* irq dev # */ { "l", list_devices, NULL }, /* ls, list */ { "po", set_device_ioaddr, int_parms }, /* port dev addr */ { "res", (CmdFunc)cpu_reset, NULL }, /* reset CPU */ { "q", quitfunc, NULL }, /* quit */ #if NSCBUS > 0 { "s", list_scsi, NULL }, /* scsi */ #endif +#ifdef VISUAL_USERCONFIG { "v", (CmdFunc)visuserconfig, NULL }, /* visual mode */ +#endif { NULL, NULL, NULL }, }; void userconfig(void) { char input[80]; int rval; Cmd *cmd; #ifdef PC98 printf("\nFreeBSD(98) Kernel Configuration Utility - Version 1.0\n" " Type \"help\" for help or \"visual\" to go to the visual\n" " configuration interface.\n"); #else printf("\nFreeBSD Kernel Configuration Utility - Version 1.0\n" " Type \"help\" for help or \"visual\" to go to the visual\n" " configuration interface (requires MGA/VGA display or\n" " serial terminal capable of displaying ANSI graphics).\n"); #endif while (1) { printf("config> "); cngets(input, 80); if (input[0] == '\0') continue; cmd = parse_cmd(input); if (!cmd) { printf("Invalid command or syntax. Type `?' for help.\n"); continue; } rval = (*cmd->handler)(cmd->parms); if (rval) return; } } static Cmd * parse_cmd(char *cmd) { Cmd *cp; for (cp = CmdList; cp->name; cp++) { int len = strlen(cp->name); if (!strncmp(cp->name, cmd, len)) { while (*cmd && *cmd != ' ' && *cmd != '\t') ++cmd; if (parse_args(cmd, cp->parms)) return NULL; else return cp; } } return NULL; } static int parse_args(char *cmd, CmdParm *parms) { while (1) { char *ptr; if (*cmd == ' ' || *cmd == '\t') { ++cmd; continue; } if (parms == NULL || parms->type == -1) { if (*cmd == '\0') return 0; printf("Extra arg(s): %s\n", cmd); return 1; } if (parms->type == PARM_DEVSPEC) { int i = 0; char devname[64]; int unit = 0; while (*cmd && !(*cmd == ' ' || *cmd == '\t' || (*cmd >= '0' && *cmd <= '9'))) devname[i++] = *(cmd++); devname[i] = '\0'; if (*cmd >= '0' && *cmd <= '9') { unit = strtoul(cmd, &ptr, 10); if (cmd == ptr) { printf("Invalid device number\n"); /* XXX should print invalid token here and elsewhere. */ return 1; } /* XXX else should require end of token. */ cmd = ptr; } if ((parms->parm.dparm = find_device(devname, unit)) == NULL) { printf("No such device: %s%d\n", devname, unit); return 1; } ++parms; continue; } if (parms->type == PARM_INT) { parms->parm.iparm = strtoul(cmd, &ptr, 0); if (cmd == ptr) { printf("Invalid numeric argument\n"); return 1; } cmd = ptr; ++parms; continue; } if (parms->type == PARM_ADDR) { parms->parm.aparm = (void *)strtoul(cmd, &ptr, 0); if (cmd == ptr) { printf("Invalid address argument\n"); return 1; } cmd = ptr; ++parms; continue; } } return 0; } static int list_devices(CmdParm *parms) { lineno = 0; lsdevtab(&isa_devtab_bio[0]); lsdevtab(&isa_devtab_tty[0]); lsdevtab(&isa_devtab_net[0]); lsdevtab(&isa_devtab_null[0]); return 0; } static int set_device_ioaddr(CmdParm *parms) { parms[0].parm.dparm->id_iobase = parms[1].parm.iparm; save_dev(parms[0].parm.dparm); return 0; } static int set_device_irq(CmdParm *parms) { unsigned irq; irq = parms[1].parm.iparm; #ifndef PC98 if (irq == 2) { printf("Warning: Remapping IRQ 2 to IRQ 9 - see config(8)\n"); irq = 9; } else if (irq != -1 && irq > 15) { #else if (irq != -1 && irq > 15) { #endif printf("An IRQ > 15 would be invalid.\n"); return 0; } parms[0].parm.dparm->id_irq = (irq < 16 ? 1 << irq : 0); save_dev(parms[0].parm.dparm); return 0; } static int set_device_drq(CmdParm *parms) { unsigned drq; /* * The bounds checking is just to ensure that the value can be printed * in 5 characters. 32768 gets converted to -32768 and doesn't fit. */ drq = parms[1].parm.iparm; parms[0].parm.dparm->id_drq = (drq < 32768 ? drq : -1); save_dev(parms[0].parm.dparm); return 0; } static int set_device_iosize(CmdParm *parms) { parms[0].parm.dparm->id_msize = parms[1].parm.iparm; save_dev(parms[0].parm.dparm); return 0; } static int set_device_mem(CmdParm *parms) { parms[0].parm.dparm->id_maddr = parms[1].parm.aparm; save_dev(parms[0].parm.dparm); return 0; } static int set_device_flags(CmdParm *parms) { parms[0].parm.dparm->id_flags = parms[1].parm.iparm; save_dev(parms[0].parm.dparm); return 0; } static int set_device_enable(CmdParm *parms) { parms[0].parm.dparm->id_enabled = TRUE; save_dev(parms[0].parm.dparm); return 0; } static int set_device_disable(CmdParm *parms) { parms[0].parm.dparm->id_enabled = FALSE; save_dev(parms[0].parm.dparm); return 0; } static int quitfunc(CmdParm *parms) { return 1; } static int helpfunc(CmdParm *parms) { printf("Command\t\t\tDescription\n"); printf("-------\t\t\t-----------\n"); printf("ls\t\t\tList currently configured devices\n"); printf("port \tSet device port (i/o address)\n"); printf("irq \tSet device irq\n"); printf("drq \tSet device drq\n"); printf("iomem \tSet device maddr (memory address)\n"); printf("iosize \tSet device memory size\n"); printf("flags \tSet device flags\n"); printf("enable \tEnable device\n"); printf("disable \tDisable device (will not be probed)\n"); printf("quit\t\t\tExit this configuration utility\n"); printf("reset\t\t\tReset CPU\n"); printf("visual\t\t\tGo to fullscreen mode.\n"); printf("help\t\t\tThis message\n\n"); printf("Commands may be abbreviated to a unique prefix\n"); return 0; } static void lsdevtab(struct isa_device *dt) { for (; dt->id_id != 0; dt++) { int i; char line[80]; if (lineno >= 23) { printf(" "); (void)cngetc(); printf("\n"); lineno = 0; } if (lineno == 0) { printf( "Device port irq drq iomem iosize unit flags enabled\n"); ++lineno; } /* * printf() doesn't support %#, %- or even field widths for strings, * so formatting is not straightforward. */ bzero(line, sizeof line); sprintf(line, "%s%d", dt->id_driver->name, dt->id_unit); /* Missing: id_id (don't need it). */ /* Missing: id_driver (useful if we could show it by name). */ sprintf(line + 9, "0x%x", dt->id_iobase); sprintf(line + 20, "%d", ffs(dt->id_irq) - 1); sprintf(line + 26, "%d", dt->id_drq); sprintf(line + 32, "0x%x", dt->id_maddr); sprintf(line + 40, "%d", dt->id_msize); /* Missing: id_msize (0 at start, useful if we can get here later). */ /* Missing: id_intr (useful if we could show it by name). */ /* Display only: id_unit. */ sprintf(line + 49, "%d", dt->id_unit); sprintf(line + 55, "0x%x", dt->id_flags); /* Missing: id_scsiid, id_alive, id_ri_flags, id_reconfig (0 now...) */ sprintf(line + 66, "%s", dt->id_enabled ? "Yes" : "No"); for (i = 0; i < 66; ++i) if (line[i] == '\0') line[i] = ' '; printf("%s\n", line); ++lineno; } } static struct isa_device * find_device(char *devname, int unit) { struct isa_device *ret; if ((ret = search_devtable(&isa_devtab_bio[0], devname, unit)) != NULL) return ret; if ((ret = search_devtable(&isa_devtab_tty[0], devname, unit)) != NULL) return ret; if ((ret = search_devtable(&isa_devtab_net[0], devname, unit)) != NULL) return ret; if ((ret = search_devtable(&isa_devtab_null[0], devname, unit)) != NULL) return ret; return NULL; } static struct isa_device * search_devtable(struct isa_device *dt, char *devname, int unit) { int i; for (i = 0; dt->id_id != 0; dt++) if (!strcmp(dt->id_driver->name, devname) && dt->id_unit == unit) return dt; return NULL; } static void cngets(char *input, int maxin) { int c, nchars = 0; while (1) { c = cngetc(); /* Treat ^H or ^? as backspace */ if ((c == '\010' || c == '\177')) { if (nchars) { printf("\010 \010"); *--input = '\0', --nchars; } continue; } /* Treat ^U or ^X as kill line */ else if ((c == '\025' || c == '\030')) { while (nchars) { printf("\010 \010"); *--input = '\0', --nchars; } continue; } printf("%c", c); if ((++nchars == maxin) || (c == '\n') || (c == '\r')) { *input = '\0'; break; } *input++ = (u_char)c; } } /* * Kludges to get the library sources of strtoul.c to work in our * environment. isdigit() and isspace() could be used above too. */ #define isalpha(c) (((c) >= 'A' && (c) <= 'Z') \ || ((c) >= 'a' && (c) <= 'z')) /* unsafe */ #define isdigit(c) ((unsigned)((c) - '0') <= '9' - '0') #define isspace(c) ((c) == ' ' || (c) == '\t') /* unsafe */ #define isupper(c) ((unsigned)((c) - 'A') <= 'Z' - 'A') static int errno; /* * The following should be identical with the library sources for strtoul.c. */ /* * Convert a string to an unsigned long integer. * * Ignores `locale' stuff. Assumes that the upper and lower case * alphabets and digits are each contiguous. */ static unsigned long strtoul(nptr, endptr, base) const char *nptr; char **endptr; register int base; { register const char *s = nptr; register unsigned long acc; register int c; register unsigned long cutoff; register int neg = 0, any, cutlim; /* * See strtol for comments as to the logic used. */ do { c = *s++; } while (isspace(c)); if (c == '-') { neg = 1; c = *s++; } else if (c == '+') c = *s++; if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) { c = s[1]; s += 2; base = 16; } if (base == 0) base = c == '0' ? 8 : 10; cutoff = (unsigned long)ULONG_MAX / (unsigned long)base; cutlim = (unsigned long)ULONG_MAX % (unsigned long)base; for (acc = 0, any = 0;; c = *s++) { if (isdigit(c)) c -= '0'; else if (isalpha(c)) c -= isupper(c) ? 'A' - 10 : 'a' - 10; else break; if (c >= base) break; if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim) any = -1; else { any = 1; acc *= base; acc += c; } } if (any < 0) { acc = ULONG_MAX; errno = ERANGE; } else if (neg) acc = -acc; if (endptr != 0) *endptr = (char *)(any ? s - 1 : nptr); return (acc); } #if NSCBUS > 0 /* scsi: Support for displaying configured SCSI devices. * There is no way to edit them, and this is inconsistent * with the ISA method. This is here as a basis for further work. */ static char * type_text(char *name) /* XXX: This is bogus */ { if (strcmp(name, "sd") == 0) return "disk"; if (strcmp(name, "st") == 0) return "tape"; return "device"; } static void id_put(char *desc, int id) { if (id != SCCONF_UNSPEC) { if (desc) printf("%s", desc); if (id == SCCONF_ANY) printf("?"); else printf("%d", id); } } static void lsscsi(void) { int i; printf("scsi: (can't be edited):\n"); for (i = 0; scsi_cinit[i].driver; i++) { id_put("controller scbus", scsi_cinit[i].bus); if (scsi_cinit[i].unit != -1) { printf(" at "); id_put(scsi_cinit[i].driver, scsi_cinit[i].unit); } printf("\n"); } for (i = 0; scsi_dinit[i].name; i++) { printf("%s ", type_text(scsi_dinit[i].name)); id_put(scsi_dinit[i].name, scsi_dinit[i].unit); id_put(" at scbus", scsi_dinit[i].cunit); id_put(" target ", scsi_dinit[i].target); id_put(" lun ", scsi_dinit[i].lun); if (scsi_dinit[i].flags) printf("flags 0x%x\n", scsi_dinit[i].flags); printf("\n"); } } static int list_scsi(CmdParm *parms) { lineno = 0; lsscsi(); return 0; } #endif static int save_dev(idev) struct isa_device *idev; { struct isa_device *id_p,*id_pn; for (id_p=isa_devlist; id_p; id_p=id_p->id_next) { if (id_p->id_id == idev->id_id) { id_pn = id_p->id_next; bcopy(idev,id_p,sizeof(struct isa_device)); id_p->id_next = id_pn; return 1; } } id_pn = malloc(sizeof(struct isa_device),M_DEVL,M_WAITOK); bcopy(idev,id_pn,sizeof(struct isa_device)); id_pn->id_next = isa_devlist; isa_devlist = id_pn; return 0; } Index: head/sys/pc98/i386/vm_machdep.c =================================================================== --- head/sys/pc98/i386/vm_machdep.c (revision 18264) +++ head/sys/pc98/i386/vm_machdep.c (revision 18265) @@ -1,860 +1,864 @@ /*- * Copyright (c) 1982, 1986 The Regents of the University of California. * Copyright (c) 1989, 1990 William Jolitz * Copyright (c) 1994 John Dyson * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department, and William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)vm_machdep.c 7.3 (Berkeley) 5/13/91 * Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$ - * $Id: vm_machdep.c,v 1.2 1996/07/23 07:46:03 asami Exp $ + * $Id: vm_machdep.c,v 1.3 1996/09/03 10:23:21 asami Exp $ */ #include "npx.h" #include "opt_bounce.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PC98 #include #else #include #endif #ifdef BOUNCE_BUFFERS static vm_offset_t vm_bounce_kva __P((int size, int waitok)); static void vm_bounce_kva_free __P((vm_offset_t addr, vm_offset_t size, int now)); static vm_offset_t vm_bounce_page_find __P((int count)); static void vm_bounce_page_free __P((vm_offset_t pa, int count)); static volatile int kvasfreecnt; caddr_t bouncememory; int bouncepages; static int bpwait; static vm_offset_t *bouncepa; static int bmwait, bmfreeing; #define BITS_IN_UNSIGNED (8*sizeof(unsigned)) static int bounceallocarraysize; static unsigned *bounceallocarray; static int bouncefree; #if defined(PC98) && defined (EPSON_BOUNCEDMA) #define SIXTEENMEG (3840*4096) /* 15MB boundary */ #else #define SIXTEENMEG (4096*4096) #endif #define MAXBKVA 1024 int maxbkva = MAXBKVA*PAGE_SIZE; /* special list that can be used at interrupt time for eventual kva free */ static struct kvasfree { vm_offset_t addr; vm_offset_t size; } kvaf[MAXBKVA]; /* * get bounce buffer pages (count physically contiguous) * (only 1 inplemented now) */ static vm_offset_t vm_bounce_page_find(count) int count; { int bit; int s,i; if (count != 1) panic("vm_bounce_page_find -- no support for > 1 page yet!!!"); s = splbio(); retry: for (i = 0; i < bounceallocarraysize; i++) { if (bounceallocarray[i] != 0xffffffff) { bit = ffs(~bounceallocarray[i]); if (bit) { bounceallocarray[i] |= 1 << (bit - 1) ; bouncefree -= count; splx(s); return bouncepa[(i * BITS_IN_UNSIGNED + (bit - 1))]; } } } bpwait = 1; tsleep((caddr_t) &bounceallocarray, PRIBIO, "bncwai", 0); goto retry; } static void vm_bounce_kva_free(addr, size, now) vm_offset_t addr; vm_offset_t size; int now; { int s = splbio(); kvaf[kvasfreecnt].addr = addr; kvaf[kvasfreecnt].size = size; ++kvasfreecnt; if( now) { /* * this will do wakeups */ vm_bounce_kva(0,0); } else { if (bmwait) { /* * if anyone is waiting on the bounce-map, then wakeup */ wakeup((caddr_t) io_map); bmwait = 0; } } splx(s); } /* * free count bounce buffer pages */ static void vm_bounce_page_free(pa, count) vm_offset_t pa; int count; { int allocindex; int index; int bit; if (count != 1) panic("vm_bounce_page_free -- no support for > 1 page yet!!!"); for(index=0;indexb_flags & B_BOUNCE) { printf("vm_bounce_alloc: called recursively???\n"); return; } if (bp->b_bufsize < bp->b_bcount) { printf( "vm_bounce_alloc: b_bufsize(0x%lx) < b_bcount(0x%lx) !!\n", bp->b_bufsize, bp->b_bcount); panic("vm_bounce_alloc"); } /* * This is not really necessary * if( bp->b_bufsize != bp->b_bcount) { * printf("size: %d, count: %d\n", bp->b_bufsize, bp->b_bcount); * } */ vastart = (vm_offset_t) bp->b_data; vaend = (vm_offset_t) bp->b_data + bp->b_bufsize; vapstart = trunc_page(vastart); vapend = round_page(vaend); countvmpg = (vapend - vapstart) / PAGE_SIZE; /* * if any page is above 16MB, then go into bounce-buffer mode */ va = vapstart; for (i = 0; i < countvmpg; i++) { pa = pmap_kextract(va); if (pa >= SIXTEENMEG) ++dobounceflag; if( pa == 0) panic("vm_bounce_alloc: Unmapped page"); va += PAGE_SIZE; } if (dobounceflag == 0) return; if (bouncepages < dobounceflag) panic("Not enough bounce buffers!!!"); /* * allocate a replacement kva for b_addr */ kva = vm_bounce_kva(countvmpg*PAGE_SIZE, 1); #if 0 printf("%s: vapstart: %x, vapend: %x, countvmpg: %d, kva: %x ", (bp->b_flags & B_READ) ? "read":"write", vapstart, vapend, countvmpg, kva); #endif va = vapstart; for (i = 0; i < countvmpg; i++) { pa = pmap_kextract(va); if (pa >= SIXTEENMEG) { /* * allocate a replacement page */ vm_offset_t bpa = vm_bounce_page_find(1); pmap_kenter(kva + (PAGE_SIZE * i), bpa); #if 0 printf("r(%d): (%x,%x,%x) ", i, va, pa, bpa); #endif /* * if we are writing, the copy the data into the page */ if ((bp->b_flags & B_READ) == 0) { bcopy((caddr_t) va, (caddr_t) kva + (PAGE_SIZE * i), PAGE_SIZE); } } else { /* * use original page */ pmap_kenter(kva + (PAGE_SIZE * i), pa); } va += PAGE_SIZE; } /* * flag the buffer as being bounced */ bp->b_flags |= B_BOUNCE; /* * save the original buffer kva */ bp->b_savekva = bp->b_data; /* * put our new kva into the buffer (offset by original offset) */ bp->b_data = (caddr_t) (((vm_offset_t) kva) | ((vm_offset_t) bp->b_savekva & PAGE_MASK)); #if 0 printf("b_savekva: %x, newva: %x\n", bp->b_savekva, bp->b_data); #endif return; } /* * hook into biodone to free bounce buffer */ void vm_bounce_free(bp) struct buf *bp; { int i; vm_offset_t origkva, bouncekva, bouncekvaend; /* * if this isn't a bounced buffer, then just return */ if ((bp->b_flags & B_BOUNCE) == 0) return; /* * This check is not necessary * if (bp->b_bufsize != bp->b_bcount) { * printf("vm_bounce_free: b_bufsize=%d, b_bcount=%d\n", * bp->b_bufsize, bp->b_bcount); * } */ origkva = (vm_offset_t) bp->b_savekva; bouncekva = (vm_offset_t) bp->b_data; /* printf("free: %d ", bp->b_bufsize); */ /* * check every page in the kva space for b_addr */ for (i = 0; i < bp->b_bufsize; ) { vm_offset_t mybouncepa; vm_offset_t copycount; copycount = round_page(bouncekva + 1) - bouncekva; mybouncepa = pmap_kextract(trunc_page(bouncekva)); /* * if this is a bounced pa, then process as one */ if ( mybouncepa != pmap_kextract( trunc_page( origkva))) { vm_offset_t tocopy = copycount; if (i + tocopy > bp->b_bufsize) tocopy = bp->b_bufsize - i; /* * if this is a read, then copy from bounce buffer into original buffer */ if (bp->b_flags & B_READ) bcopy((caddr_t) bouncekva, (caddr_t) origkva, tocopy); /* * free the bounce allocation */ /* printf("(kva: %x, pa: %x)", bouncekva, mybouncepa); */ vm_bounce_page_free(mybouncepa, 1); } origkva += copycount; bouncekva += copycount; i += copycount; } /* printf("\n"); */ /* * add the old kva into the "to free" list */ bouncekva= trunc_page((vm_offset_t) bp->b_data); bouncekvaend= round_page((vm_offset_t)bp->b_data + bp->b_bufsize); /* printf("freeva: %d\n", (bouncekvaend - bouncekva) / PAGE_SIZE); */ vm_bounce_kva_free( bouncekva, (bouncekvaend - bouncekva), 0); bp->b_data = bp->b_savekva; bp->b_savekva = 0; bp->b_flags &= ~B_BOUNCE; return; } /* * init the bounce buffer system */ void vm_bounce_init() { int i; kvasfreecnt = 0; if (bouncepages == 0) return; bounceallocarraysize = (bouncepages + BITS_IN_UNSIGNED - 1) / BITS_IN_UNSIGNED; bounceallocarray = malloc(bounceallocarraysize * sizeof(unsigned), M_TEMP, M_NOWAIT); if (!bounceallocarray) panic("Cannot allocate bounce resource array"); bouncepa = malloc(bouncepages * sizeof(vm_offset_t), M_TEMP, M_NOWAIT); if (!bouncepa) panic("Cannot allocate physical memory array"); for(i=0;i= SIXTEENMEG) panic("bounce memory out of range"); if( pa == 0) panic("bounce memory not resident"); bouncepa[i] = pa; bounceallocarray[i/(8*sizeof(int))] &= ~(1<<(i%(8*sizeof(int)))); } bouncefree = bouncepages; } #endif /* BOUNCE_BUFFERS */ /* * quick version of vm_fault */ void vm_fault_quick(v, prot) caddr_t v; int prot; { if (prot & VM_PROT_WRITE) subyte(v, fubyte(v)); else fubyte(v); } /* * Finish a fork operation, with process p2 nearly set up. * Copy and update the kernel stack and pcb, making the child * ready to run, and marking it so that it can return differently * than the parent. Returns 1 in the child process, 0 in the parent. * We currently double-map the user area so that the stack is at the same * address in each process; in the future we will probably relocate * the frame pointers on the stack after copying. */ int cpu_fork(p1, p2) register struct proc *p1, *p2; { struct pcb *pcb2 = &p2->p_addr->u_pcb; int sp, offset; volatile int retval; /* * Copy pcb and stack from proc p1 to p2. * We do this as cheaply as possible, copying only the active * part of the stack. The stack and pcb need to agree; * this is tricky, as the final pcb is constructed by savectx, * but its frame isn't yet on the stack when the stack is copied. * This should be done differently, with a single call * that copies and updates the pcb+stack, * replacing the bcopy and savectx. */ __asm __volatile("movl %%esp,%0" : "=r" (sp)); offset = sp - (int)kstack; retval = 1; /* return 1 in child */ bcopy((caddr_t)kstack + offset, (caddr_t)p2->p_addr + offset, (unsigned) ctob(UPAGES) - offset); p2->p_md.md_regs = p1->p_md.md_regs; *pcb2 = p1->p_addr->u_pcb; pcb2->pcb_cr3 = vtophys(p2->p_vmspace->vm_pmap.pm_pdir); retval = 0; /* return 0 in parent */ savectx(pcb2); return (retval); } void cpu_exit(p) register struct proc *p; { #ifdef USER_LDT struct pcb *pcb; #endif #if NNPX > 0 npxexit(p); #endif /* NNPX */ #ifdef USER_LDT pcb = &p->p_addr->u_pcb; if (pcb->pcb_ldt != 0) { if (pcb == curpcb) lldt(GSEL(GUSERLDT_SEL, SEL_KPL)); kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ldt, pcb->pcb_ldt_len * sizeof(union descriptor)); pcb->pcb_ldt_len = (int)pcb->pcb_ldt = 0; } #endif cnt.v_swtch++; cpu_switch(p); panic("cpu_exit"); } void cpu_wait(p) struct proc *p; { /* drop per-process resources */ pmap_qremove((vm_offset_t) p->p_addr, UPAGES); kmem_free(u_map, (vm_offset_t)p->p_addr, ctob(UPAGES)); vmspace_free(p->p_vmspace); } /* * Dump the machine specific header information at the start of a core dump. */ int cpu_coredump(p, vp, cred) struct proc *p; struct vnode *vp; struct ucred *cred; { return (vn_rdwr(UIO_WRITE, vp, (caddr_t) p->p_addr, ctob(UPAGES), (off_t)0, UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, (int *)NULL, p)); } #ifdef notyet static void setredzone(pte, vaddr) u_short *pte; caddr_t vaddr; { /* eventually do this by setting up an expand-down stack segment for ss0: selector, allowing stack access down to top of u. this means though that protection violations need to be handled thru a double fault exception that must do an integral task switch to a known good context, within which a dump can be taken. a sensible scheme might be to save the initial context used by sched (that has physical memory mapped 1:1 at bottom) and take the dump while still in mapped mode */ } #endif /* * Convert kernel VA to physical address */ u_long kvtop(void *addr) { vm_offset_t va; va = pmap_kextract((vm_offset_t)addr); if (va == 0) panic("kvtop: zero page frame"); return((int)va); } /* * Map an IO request into kernel virtual address space. * * All requests are (re)mapped into kernel VA space. * Notice that we use b_bufsize for the size of the buffer * to be mapped. b_bcount might be modified by the driver. */ void vmapbuf(bp) register struct buf *bp; { register caddr_t addr, v, kva; vm_offset_t pa; if ((bp->b_flags & B_PHYS) == 0) panic("vmapbuf"); for (v = bp->b_saveaddr, addr = (caddr_t)trunc_page(bp->b_data); addr < bp->b_data + bp->b_bufsize; addr += PAGE_SIZE, v += PAGE_SIZE) { /* * Do the vm_fault if needed; do the copy-on-write thing * when reading stuff off device into memory. */ vm_fault_quick(addr, (bp->b_flags&B_READ)?(VM_PROT_READ|VM_PROT_WRITE):VM_PROT_READ); pa = trunc_page(pmap_kextract((vm_offset_t) addr)); if (pa == 0) panic("vmapbuf: page not present"); vm_page_hold(PHYS_TO_VM_PAGE(pa)); pmap_kenter((vm_offset_t) v, pa); } kva = bp->b_saveaddr; bp->b_saveaddr = bp->b_data; bp->b_data = kva + (((vm_offset_t) bp->b_data) & PAGE_MASK); } /* * Free the io map PTEs associated with this IO operation. * We also invalidate the TLB entries and restore the original b_addr. */ void vunmapbuf(bp) register struct buf *bp; { register caddr_t addr; vm_offset_t pa; if ((bp->b_flags & B_PHYS) == 0) panic("vunmapbuf"); for (addr = (caddr_t)trunc_page(bp->b_data); addr < bp->b_data + bp->b_bufsize; addr += PAGE_SIZE) { pa = trunc_page(pmap_kextract((vm_offset_t) addr)); pmap_kremove((vm_offset_t) addr); vm_page_unhold(PHYS_TO_VM_PAGE(pa)); } bp->b_data = bp->b_saveaddr; } /* * Force reset the processor by invalidating the entire address space! */ void cpu_reset() { #ifdef PC98 asm(" cli "); outb(0x37, 0x0f); /* SHUT 0 = 0 */ outb(0x37, 0x0b); /* SHUT 1 = 0 */ if ((pc98_machine_type & M_EPSON_PC98) && (epson_machine_id == 0x20 /*note A*/)) { epson_outb(0xc17, epson_inb(0xc17) | 0x40); /* reset port for NOTE_A */ } outb(0xf0, 0x00); /* reset port */ #else /* IBM-PC */ /* * Attempt to do a CPU reset via the keyboard controller, * do not turn of the GateA20, as any machine that fails * to do the reset here would then end up in no man's land. */ #ifndef BROKEN_KEYBOARD_RESET outb(IO_KBD + 4, 0xFE); DELAY(500000); /* wait 0.5 sec to see if that did it */ printf("Keyboard reset did not work, attempting CPU shutdown\n"); DELAY(1000000); /* wait 1 sec for printf to complete */ #endif /* force a shutdown by unmapping entire address space ! */ bzero((caddr_t) PTD, PAGE_SIZE); /* "good night, sweet prince .... " */ pmap_update(); #endif /* NOTREACHED */ while(1); } /* * Grow the user stack to allow for 'sp'. This version grows the stack in * chunks of SGROWSIZ. */ int grow(p, sp) struct proc *p; u_int sp; { unsigned int nss; caddr_t v; struct vmspace *vm = p->p_vmspace; if ((caddr_t)sp <= vm->vm_maxsaddr || (unsigned)sp >= (unsigned)USRSTACK) return (1); nss = roundup(USRSTACK - (unsigned)sp, PAGE_SIZE); if (nss > p->p_rlimit[RLIMIT_STACK].rlim_cur) return (0); if (vm->vm_ssize && roundup(vm->vm_ssize << PAGE_SHIFT, SGROWSIZ) < nss) { int grow_amount; /* * If necessary, grow the VM that the stack occupies * to allow for the rlimit. This allows us to not have * to allocate all of the VM up-front in execve (which * is expensive). * Grow the VM by the amount requested rounded up to * the nearest SGROWSIZ to provide for some hysteresis. */ grow_amount = roundup((nss - (vm->vm_ssize << PAGE_SHIFT)), SGROWSIZ); v = (char *)USRSTACK - roundup(vm->vm_ssize << PAGE_SHIFT, SGROWSIZ) - grow_amount; /* * If there isn't enough room to extend by SGROWSIZ, then * just extend to the maximum size */ if (v < vm->vm_maxsaddr) { v = vm->vm_maxsaddr; grow_amount = MAXSSIZ - (vm->vm_ssize << PAGE_SHIFT); } if ((grow_amount == 0) || (vm_map_find(&vm->vm_map, NULL, 0, (vm_offset_t *)&v, grow_amount, FALSE, VM_PROT_ALL, VM_PROT_ALL, 0) != KERN_SUCCESS)) { return (0); } vm->vm_ssize += grow_amount >> PAGE_SHIFT; } return (1); } /* * prototype routine to implement the pre-zeroed page mechanism * this routine is called from the idle loop. */ int vm_page_zero_idle() { vm_page_t m; + static int free_rover = 0; if ((cnt.v_free_count > cnt.v_interrupt_free_min) && - (m = TAILQ_FIRST(&vm_page_queue_free))) { - TAILQ_REMOVE(&vm_page_queue_free, m, pageq); + (m = vm_page_list_find(PQ_FREE, free_rover))) { + --(*vm_page_queues[m->queue].lcnt); + TAILQ_REMOVE(vm_page_queues[m->queue].pl, m, pageq); enable_intr(); pmap_zero_page(VM_PAGE_TO_PHYS(m)); disable_intr(); - TAILQ_INSERT_HEAD(&vm_page_queue_zero, m, pageq); - m->queue = PQ_ZERO; + m->queue = PQ_ZERO + m->pc; + ++(*vm_page_queues[m->queue].lcnt); + TAILQ_INSERT_HEAD(vm_page_queues[m->queue].pl, m, pageq); + free_rover = (free_rover + PQ_PRIME3) & PQ_L2_MASK; ++vm_page_zero_count; return 1; } return 0; } Index: head/sys/pc98/pc98/ic/wd33c93.h =================================================================== --- head/sys/pc98/pc98/ic/wd33c93.h (revision 18264) +++ head/sys/pc98/pc98/ic/wd33c93.h (nonexistent) @@ -1,127 +0,0 @@ -/* - * PC9801 SCSI I/F (PC-9801-55) - * modified for PC9801 by A.Kojima - * Kyoto University Microcomputer Club (KMC) - */ - -/* I/O address */ - -/* WD33C93 */ -#define SCSI_ADR_REG 0xcc0 /* write Address Register */ -#define SCSI_AUX_REG 0xcc0 /* read Aux. Status Register */ -#define SCSI_CTL_REG 0xcc2 /* read/write Control Registers */ - -/* Port */ -#define SCSI_STAT_RD 0xcc4 /* read Status Read */ -#define SCSI_CMD_WRT 0xcc4 /* write Command Write */ - -#if 0 /* H98 extended mode */ -/* WD33C93 */ -#define SCSI_ADR_REG 0xee0 /* write Address Register */ -#define SCSI_AUX_REG 0xee0 /* read Control Register */ -#define SCSI_CTL_REG 0xee2 /* read/write Registers */ - -/* Port */ -#define SCSI_STAT_RD 0xee4 /* read Status Read */ -#define SCSI_CMD_WRT 0xee4 /* write Command Write */ -#endif - -/****************************************************************/ - -/* WD33C93 Registers */ -#define REG_OWN_ID 0x00 /* Own ID */ -#define REG_CONTROL 0x01 /* Control */ -#define REG_TIMEOUT_PERIOD 0x02 /* Timeout Period */ -#define REG_TOTAL_SECTORS 0x03 /* Total Sectors */ -#define REG_TOTAL_HEADS 0x04 /* Total Heads */ -#define REG_TOTAL_CYL_H 0x05 /* Total Cylinders (MSB) */ -#define REG_TOTAL_CYL_L 0x06 /* Total Cylinders (LSB) */ -#define REG_LOG_SECTOR_HH 0x07 /* Logical Address (MSB) */ -#define REG_LOG_SECTOR_HL 0x08 /* Logical Address */ -#define REG_LOG_SECTOR_LH 0x09 /* Logical Address */ -#define REG_LOG_SECTOR_LL 0x0a /* Logical Address (LSB) */ -#define REG_SECTOR_NUMBER 0x0b /* Sector Number */ -#define REG_HEAD_NUMBER 0x0c /* Head Number */ -#define REG_CYL_NUMBER_H 0x0d /* Cylinder Number (MSB) */ -#define REG_CYL_NUMBER_L 0x0e /* Cylinder Number (LSB) */ -#define REG_TARGET_LUN 0x0f /* Target LUN */ -#define REG_CMD_PHASE 0x10 /* Command Phase */ -#define REG_SYNC_TFR 0x11 /* Synchronous Transfer */ -#define REG_TFR_COUNT_H 0x12 /* Transfer Count (MSB) */ -#define REG_TFR_COUNT_M 0x13 /* Transfer Count */ -#define REG_TFR_COUNT_L 0x14 /* Transfer Count (LSB) */ -#define REG_DST_ID 0x15 /* Destination ID */ -#define REG_SRC_ID 0x16 /* Source ID */ -#define REG_SCSI_STATUS 0x17 /* SCSI Status (Read Only) */ -#define REG_COMMAND 0x18 /* Command */ -#define REG_DATA 0x19 /* Data */ - -/* PC98 only */ -#define REG_MEM_BANK 0x30 /* Memory Bank */ -#define REG_MEM_WIN 0x31 /* Memery Window */ -#define REG_RESERVED1 0x32 /* NEC Reserved 1 */ -#define REG_RESET_INT 0x33 /* Reset/Int */ -#define REG_RESERVED2 0x34 /* NEC Reserved 2 */ - -/****************************************************************/ - -/* WD33C93 Commands */ -#define CMD_RESET 0x00 /* Reset */ -#define CMD_ABORT 0x01 /* Abort */ -#define CMD_ASSERT_ATN 0x02 /* Assert ATN */ -#define CMD_NEGATE_ATN 0x03 /* Negate ATN */ -#define CMD_DISCONNECT 0x04 /* Disconnect */ -#define CMD_RESELECT 0x05 /* Reselect */ -#define CMD_SELECT_ATN 0x06 /* Select with ATN */ -#define CMD_SELECT_NO_ATN 0x07 /* Select without ATN */ -#define CMD_SELECT_ATN_TFR 0x08 /* Select with ATN and Transfer */ -#define CMD_SELECT_NO_ATN_TFR 0x09 /* Select without ATN and Transfer */ -#define CMD_RESELECT_RCV_DATA 0x0a /* Reselect and Recieve Data */ -#define CMD_RESELECT_SEND_DATA 0x0b /* Reselect and Send Data */ -#define CMD_WAIT_SELECT_RCV 0x0c /* Wait for Select and Recieve */ -#define CMD_RCV_CMD 0x10 /* Recieve Command */ -#define CMD_RCV_DATA 0x11 /* Recieve Data */ -#define CMD_RCV_MSG_OUT 0x12 /* Recieve Message Info Out*/ -#define CMD_RCV_UNSP_INFO_OUT 0x13 /* Recieve Unspecified Info Out */ -#define CMD_SEND_STATUS 0x14 /* Send Status */ -#define CMD_SEND_DATA 0x15 /* Send Data */ -#define CMD_SEND_MSG_IN 0x16 /* Send Message In */ -#define CMD_SEND_UNSP_INFO_IN 0x17 /* Send Unspecified Info In */ -#define CMD_TRANSLATE_ADDRESS 0x18 /* Translate Address */ -#define CMD_TFR_INFO 0x20 /* Transfer Info */ -#define CMD_TFR_PAD 0x21 /* Transfer Pad */ -#define CMD_SBT_SFX 0x80 /* single byte suffix */ - -/* WD33C93 bus status register (lower nibble) */ -#define STAT_DATAOUT 0x08 /* Data out phase */ -#define STAT_DATAIN 0x09 /* Data in phase */ -#define STAT_CMDOUT 0x0a /* Command out phase */ -#define STAT_STATIN 0x0b /* Status in phase */ -#define STAT_MSGOUT 0x0e /* Message out phase */ -#define STAT_MSGIN 0x0f /* Message in phase */ - -/* SCSI Status byte */ -#define SS_GOOD 0x00 /* Good status */ -#define SS_CHKCOND 0x02 -#define SS_MET 0x04 -#define SS_BUSY 0x08 -#define SS_INTERGOOD 0x10 -#define SS_INTERMET 0x14 -#define SS_CONFLICT 0x18 - -/* SCSI message system */ -#define MSG_COMPLETE 0x00 /* Command complete message */ -#define MSG_EXTEND 0x01 /* Extend message */ -#define MSG_SAVEPTR 0x02 /* Save data pointer message */ -#define MSG_RESTORE 0x03 /* Restore data pointer message */ -#define MSG_DISCON 0x04 /* Disconnect message */ -#define MSG_INIERROR 0x05 -#define MSG_ABORT 0x06 -#define MSG_REJECT 0x07 -#define MSG_NOP 0x08 -#define MSG_PARERROR 0x09 -#define MSG_LCOMPLETE 0x0a -#define MSG_LCOMPLETEF 0x0b -#define MSG_DEVRESET 0x0c -#define MSG_IDENTIFY 0x80 /* Identify message */ - Property changes on: head/sys/pc98/pc98/ic/wd33c93.h ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/sys/pc98/pc98/ic/i8251.h =================================================================== --- head/sys/pc98/pc98/ic/i8251.h (revision 18264) +++ head/sys/pc98/pc98/ic/i8251.h (nonexistent) @@ -1,79 +0,0 @@ -/*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)ns16550.h 7.1 (Berkeley) 5/9/91 - */ - -/* - * modified for PC9801 by M.Ishii - * Kyoto University Microcomputer Club (KMC) - */ - -/* define command and status code */ -#define CMD8251_TxEN 0x01 /* transmit enable */ -#define CMD8251_DTR 0x02 /* assert DTR */ -#define CMD8251_RxEN 0x04 /* receive enable */ -#define CMD8251_SBRK 0x08 /* send break */ -#define CMD8251_ER 0x10 /* error reset */ -#define CMD8251_RTS 0x20 /* assert RTS */ -#define CMD8251_RESET 0x40 /* internal reset */ -#define CMD8251_EH 0x80 /* enter hunt mode (only synchronous mode)*/ - -#define STS8251_TxRDY 0x01 /* transmit READY */ -#define STS8251_RxRDY 0x02 /* data exists in receive buffer */ -#define STS8251_TxEMP 0x04 /* transmit buffer EMPTY */ -#define STS8251_PE 0x08 /* perity error */ -#define STS8251_OE 0x10 /* overrun error */ -#define STS8251_FE 0x20 /* framing error */ -#define STS8251_BD_SD 0x40 /* break detect (async) / sync detect (sync) */ -#define STS8251_DSR 0x80 /* DSR is asserted */ - -#define MOD8251_5BITS 0x00 -#define MOD8251_6BITS 0x04 -#define MOD8251_7BITS 0x08 -#define MOD8251_8BITS 0x0c -#define MOD8251_PDISAB 0x00 /* parity disable */ -#define MOD8251_PODD 0x10 /* parity odd */ -#define MOD8251_PEVEN 0x30 /* parity even */ -#define MOD8251_STOP1 0x40 /* stop bit len = 1bit */ -#define MOD8251_STOP2 0xc0 /* stop bit len = 2bit */ -#define MOD8251_CLKX16 0x02 /* x16 */ -#define MOD8251_CLKX1 0x01 /* x1 */ - -#define CICSCD_CI 0x80 /* CI */ -#define CICSCD_CS 0x40 /* CS */ -#define CICSCD_CD 0x20 /* CD */ - -/* interrupt mask control */ -#define IEN_Rx 0x01 -#define IEN_TxEMP 0x02 -#define IEN_Tx 0x04 Property changes on: head/sys/pc98/pc98/ic/i8251.h ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/sys/pc98/pc98/ic/ns16550.h =================================================================== --- head/sys/pc98/pc98/ic/ns16550.h (revision 18264) +++ head/sys/pc98/pc98/ic/ns16550.h (nonexistent) @@ -1,68 +0,0 @@ -/*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * from: @(#)ns16550.h 7.1 (Berkeley) 5/9/91 - * ns16550.h,v 1.2 1993/10/16 13:48:52 rgrimes Exp - */ - -/* - * NS16550 UART registers - */ -/* - * modified for MC16550II - */ - -#ifdef PC98 -#define com_data 0x000 /* data register (R/W) */ -#define com_dlbl 0x000 /* divisor latch low (W) */ -#define com_dlbh 0x100 /* divisor latch high (W) */ -#define com_ier 0x100 /* interrupt enable (W) */ -#define com_iir 0x200 /* interrupt identification (R) */ -#define com_fifo 0x200 /* FIFO control (W) */ -#define com_lctl 0x300 /* line control register (R/W) */ -#define com_cfcr 0x300 /* line control register (R/W) */ -#define com_mcr 0x400 /* modem control register (R/W) */ -#define com_lsr 0x500 /* line status register (R/W) */ -#define com_msr 0x600 /* modem status register (R/W) */ -#else -#define com_data 0 /* data register (R/W) */ -#define com_dlbl 0 /* divisor latch low (W) */ -#define com_dlbh 1 /* divisor latch high (W) */ -#define com_ier 1 /* interrupt enable (W) */ -#define com_iir 2 /* interrupt identification (R) */ -#define com_fifo 2 /* FIFO control (W) */ -#define com_lctl 3 /* line control register (R/W) */ -#define com_cfcr 3 /* line control register (R/W) */ -#define com_mcr 4 /* modem control register (R/W) */ -#define com_lsr 5 /* line status register (R/W) */ -#define com_msr 6 /* modem status register (R/W) */ -#endif Property changes on: head/sys/pc98/pc98/ic/ns16550.h ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/sys/pc98/pc98/ic/i8237.h =================================================================== --- head/sys/pc98/pc98/ic/i8237.h (revision 18264) +++ head/sys/pc98/pc98/ic/i8237.h (nonexistent) @@ -1,11 +0,0 @@ -/* - * Intel 8237 DMA Controller - * - * i8237.h,v 1.3 1994/11/01 17:26:47 ache Exp - */ - -#define DMA37MD_SINGLE 0x40 /* single pass mode */ -#define DMA37MD_CASCADE 0xc0 /* cascade mode */ -#define DMA37MD_AUTO 0x50 /* autoinitialise single pass mode */ -#define DMA37MD_WRITE 0x04 /* read the device, write memory operation */ -#define DMA37MD_READ 0x08 /* write the device, read memory operation */ Property changes on: head/sys/pc98/pc98/ic/i8237.h ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/sys/pc98/pc98/atapi.c =================================================================== --- head/sys/pc98/pc98/atapi.c (revision 18264) +++ head/sys/pc98/pc98/atapi.c (revision 18265) @@ -1,1075 +1,1074 @@ /* * Device-independent level for ATAPI drivers. * * Copyright (C) 1995 Cronyx Ltd. * Author Serge Vakulenko, * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Authors grant any other persons or organisations permission to use * or modify this software as long as this message is kept with the software, * all derivative works or modified versions. * * Version 1.9, Mon Oct 9 22:34:47 MSK 1995 */ /* * The ATAPI level is implemented as a machine-dependent layer * between the device driver and the IDE controller. * All the machine- and controller dependency is isolated inside * the ATAPI level, while all the device dependency is located * in the device subdriver. * * It seems that an ATAPI bus will became popular for medium-speed * storage devices such as CD-ROMs, magneto-optical disks, tape streamers etc. * * To ease the development of new ATAPI drivers, the subdriver * interface was designed to be as simple as possible. * * Three routines are available for the subdriver to access the device: * * struct atapires atapi_request_wait (ata, unit, cmd, a1, a2, a3, a4, a5, * a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, addr, count); * struct atapi *ata; -- atapi controller descriptor * int unit; -- device unit number on the IDE bus * u_char cmd; -- ATAPI command code * u_char a1..a15; -- ATAPI command arguments * char *addr; -- address of the data buffer for i/o * int count; -- data length, >0 for read ops, <0 for write ops * * The atapi_request_wait() function puts the op in the queue of ATAPI * commands for the IDE controller, starts the controller, the waits for * operation to be completed (using tsleep). * The function should be called from the user phase only (open(), close(), * ioctl() etc). * Ata and unit args are the values which the subdriver gets from the ATAPI * level via attach() call. * Buffer pointed to by *addr should be placed in core memory, static * or dynamic, but not in stack. * The function returns the error code structure, which consists of: * - atapi driver code value * - controller status port value * - controller error port value * * struct atapires atapi_request_immediate (ata, unit, cmd, a1, a2, a3, * a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, * addr, count); * * The atapi_request_immediate() function is similar to atapi_request_wait(), * but it does not use interrupts for performing the request. * It should be used during an attach phase to get parameters from the device. * * void atapi_request_callback (ata, unit, cmd, a1, a2, a3, a4, a5, * a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, * addr, count, done, x, y); * struct atapi *ata; -- atapi controller descriptor * int unit; -- device unit number on the IDE bus * u_char cmd; -- ATAPI command code * u_char a1..a15; -- ATAPI command arguments * char *addr; -- address of the data buffer for i/o * int count; -- data length, >0 for read ops, <0 for write ops * void (*done)(); -- function to call when op finished * void *x, *y; -- arguments for done() function * * The atapi_request_callback() function puts the op in the queue of ATAPI * commands for the IDE controller, starts the controller, then returns. * When the operation finishes, then the callback function done() * will be called on the interrupt level. * The function is designed to be callable from the interrupt phase. * The done() functions is called with the following arguments: * (void) (*done) (x, y, count, errcode) * void *x, *y; -- arguments from the atapi_request_callback() * int count; -- the data residual count * struct atapires errcode; -- error code structure, see above * * The new driver could be added in three steps: * 1. Add entries for the new driver to bdevsw and cdevsw tables in conf.c. * You will need to make at least three routines: open(), close(), * strategy() and possibly ioctl(). * 2. Make attach() routine, which should allocate all the needed data * structures and print the device description string (see wcdattach()). * 3. Add an appropriate case to the switch in atapi_attach() routine, * call attach() routine of the new driver here. Add the appropriate * #include line at the top of attach.c. * That's all! * * Use #define DEBUG in atapi.c to enable tracing of all i/o operations * on the IDE bus. */ #undef DEBUG #include "wdc.h" #include "opt_atapi.h" #ifndef ATAPI_MODULE # include "wcd.h" /* # include "wmt.h" -- add your driver here */ /* # include "wmd.h" -- add your driver here */ #endif #if NWDC > 0 && defined (ATAPI) #include #include #include #include #include #include #ifdef ATAPI_MODULE # define ATAPI_STATIC #endif #ifdef PC98 #include #else #include #endif #ifndef ATAPI_STATIC /* this code is compiled as part of the kernel if options ATAPI */ /* * In the case of loadable ATAPI driver we need to store * the probe info for delayed attaching. */ struct atapidrv atapi_drvtab[4]; int atapi_ndrv; struct atapi *atapi_tab; -int atapi_attach (int ctlr, int unit, int port, struct kern_devconf *parent) +int atapi_attach (int ctlr, int unit, int port) { atapi_drvtab[atapi_ndrv].ctlr = ctlr; atapi_drvtab[atapi_ndrv].unit = unit; atapi_drvtab[atapi_ndrv].port = port; - atapi_drvtab[atapi_ndrv].parent = parent; atapi_drvtab[atapi_ndrv].attached = 0; ++atapi_ndrv; return (1); } #else /* ATAPI_STATIC */ /* this code is compiled part of the module */ #ifdef DEBUG # define print(s) printf s #else # define print(s) {/*void*/} #endif /* * ATAPI packet command phase. */ #define PHASE_CMDOUT (ARS_DRQ | ARI_CMD) #define PHASE_DATAIN (ARS_DRQ | ARI_IN) #define PHASE_DATAOUT ARS_DRQ #define PHASE_COMPLETED (ARI_IN | ARI_CMD) #define PHASE_ABORTED 0 /* nonstandard - for NEC 260 */ struct atapi atapitab[NWDC]; static struct atapi_params *atapi_probe (int port, int unit); static int atapi_wait (int port, u_char bits_wanted); static void atapi_send_cmd (struct atapi *ata, struct atapicmd *ac); static int atapi_io (struct atapi *ata, struct atapicmd *ac); static int atapi_start_cmd (struct atapi *ata, struct atapicmd *ac); static int atapi_wait_cmd (struct atapi *ata, struct atapicmd *ac); extern int wdstart (int ctrlr); extern int wcdattach(struct atapi*, int, struct atapi_params*, int); /* * Probe the ATAPI device at IDE controller `ctlr', drive `unit'. * Called at splbio(). */ #ifdef ATAPI_MODULE static #endif int atapi_attach (int ctlr, int unit, int port) { struct atapi *ata = atapitab + ctlr; struct atapi_params *ap; char buf [sizeof(ap->model) + 1]; char revbuf [sizeof(ap->revision) + 1]; struct atapicmd *ac; print (("atapi%d.%d at 0x%x: attach called\n", ctlr, unit, port)); ap = atapi_probe (port, unit); if (! ap) return (0); bcopy (ap->model, buf, sizeof(buf)-1); buf[sizeof(buf)-1] = 0; bcopy (ap->revision, revbuf, sizeof(revbuf)-1); revbuf[sizeof(revbuf)-1] = 0; printf ("wdc%d: unit %d (atapi): <%s/%s>", ctlr, unit, buf, revbuf); /* device is removable */ if (ap->removable) printf (", removable"); /* packet command size */ switch (ap->cmdsz) { case AT_PSIZE_12: break; case AT_PSIZE_16: printf (", cmd16"); ata->cmd16 = 1; break; default: printf (", cmd%d", ap->cmdsz); } /* DRQ type */ switch (ap->drqtype) { case AT_DRQT_MPROC: ata->slow = 1; break; case AT_DRQT_INTR: printf (", intr"); ata->intrcmd = 1; break; case AT_DRQT_ACCEL: printf (", accel"); break; default: printf (", drq%d", ap->drqtype); } /* overlap operation supported */ if (ap->ovlapflag) printf (", ovlap"); /* interleaved DMA supported */ if (ap->idmaflag) printf (", idma"); /* DMA supported */ else if (ap->dmaflag) printf (", dma"); /* IORDY can be disabled */ if (ap->iordydis) printf (", iordis"); /* IORDY supported */ else if (ap->iordyflag) printf (", iordy"); printf ("\n"); ata->port = port; ata->ctrlr = ctlr; ata->attached[unit] = 0; #ifdef DEBUG ata->debug = 1; #else ata->debug = 0; #endif /* Initialize free queue. */ ata->cmdrq[15].next = 0; for (ac = ata->cmdrq+14; ac >= ata->cmdrq; --ac) ac->next = ac+1; ata->free = ata->cmdrq; if (ap->proto != AT_PROTO_ATAPI) { printf ("wdc%d: unit %d: unknown ATAPI protocol=%d\n", ctlr, unit, ap->proto); free (ap, M_TEMP); return (0); } #ifdef ATAPI_MODULE ata->params[unit] = ap; return (1); #else switch (ap->devtype) { default: /* unknown ATAPI device */ printf ("wdc%d: unit %d: unknown ATAPI type=%d\n", ctlr, unit, ap->devtype); break; case AT_TYPE_DIRECT: /* direct-access */ case AT_TYPE_CDROM: /* CD-ROM device */ #if NWCD > 0 /* ATAPI CD-ROM */ if (wcdattach (ata, unit, ap, ata->debug) < 0) break; /* Device attached successfully. */ ata->attached[unit] = 1; return (1); #else printf ("wdc%d: ATAPI CD-ROMs not configured\n", ctlr); break; #endif case AT_TYPE_TAPE: /* streaming tape (QIC-121 model) */ #if NWMT > 0 /* Add your driver here */ #else printf ("wdc%d: ATAPI streaming tapes not supported yet\n", ctlr); #endif break; case AT_TYPE_OPTICAL: /* optical disk */ #if NWMD > 0 /* Add your driver here */ #else printf ("wdc%d: ATAPI optical disks not supported yet\n", ctlr); #endif break; } /* Attach failed. */ free (ap, M_TEMP); return (0); #endif /* ATAPI_MODULE */ } static char *cmdname (u_char cmd) { static char buf[8]; switch (cmd) { case 0x00: return ("TEST_UNIT_READY"); case 0x03: return ("REQUEST_SENSE"); case 0x1b: return ("START_STOP"); case 0x1e: return ("PREVENT_ALLOW"); case 0x25: return ("READ_CAPACITY"); case 0x28: return ("READ_BIG"); case 0x43: return ("READ_TOC"); case 0x42: return ("READ_SUBCHANNEL"); case 0x55: return ("MODE_SELECT_BIG"); case 0x5a: return ("MODE_SENSE"); case 0xb4: return ("PLAY_CD"); case 0x47: return ("PLAY_MSF"); case 0x4b: return ("PAUSE"); case 0x48: return ("PLAY_TRACK"); case 0xa5: return ("PLAY_BIG"); } sprintf (buf, "[0x%x]", cmd); return (buf); } static void bswap (char *buf, int len) { u_short *p = (u_short*) (buf + len); while (--p >= (u_short*) buf) *p = ntohs (*p); } static void btrim (char *buf, int len) { char *p; /* Remove the trailing spaces. */ for (p=buf; p=buf && *p==' '; --p) *p = 0; } /* * Issue IDENTIFY command to ATAPI drive to ask it what it is. */ static struct atapi_params *atapi_probe (int port, int unit) { struct atapi_params *ap; char tb [DEV_BSIZE]; int cnt; #ifdef PC98 outb(0x432,unit%2); print(("unit = %d,select %d\n",unit,unit%2)); #endif /* Wait for controller not busy. */ outb (port + AR_DRIVE, unit ? ARD_DRIVE1 : ARD_DRIVE0); if (atapi_wait (port, 0) < 0) { print (("atapiX.%d at 0x%x: controller busy, status=%b\n", unit, port, inb (port + AR_STATUS), ARS_BITS)); return (0); } /* Issue ATAPI IDENTIFY command. */ #ifdef PC98 outb (port + AR_DRIVE, unit/2 ? ARD_DRIVE1 : ARD_DRIVE0); /* Wait for DRQ deassert. */ for (cnt=2000; cnt>0; --cnt) if (! (inb (0x640 + AR_STATUS) & ARS_DRQ)) break; outb (port + AR_COMMAND, ATAPIC_IDENTIFY); DELAY(500); #else outb (port + AR_DRIVE, unit ? ARD_DRIVE1 : ARD_DRIVE0); outb (port + AR_COMMAND, ATAPIC_IDENTIFY); #endif /* Check that device is present. */ if (inb (port + AR_STATUS) == 0xff) { print (("atapiX.%d at 0x%x: no device\n", unit, port)); if (unit == 1) /* Select unit 0. */ outb (port + AR_DRIVE, ARD_DRIVE0); return (0); } /* Wait for data ready. */ if (atapi_wait (port, ARS_DRQ) != 0) { print (("atapiX.%d at 0x%x: identify not ready, status=%b\n", unit, port, inb (port + AR_STATUS), ARS_BITS)); if (unit == 1) /* Select unit 0. */ outb (port + AR_DRIVE, ARD_DRIVE0); return (0); } /* Obtain parameters. */ insw (port + AR_DATA, tb, sizeof(tb) / sizeof(short)); ap = malloc (sizeof *ap, M_TEMP, M_NOWAIT); if (! ap) return (0); bcopy (tb, ap, sizeof *ap); /* * Shuffle string byte order. * Mitsumi and NEC drives don't need this. */ if (! ((ap->model[0] == 'N' && ap->model[1] == 'E') || (ap->model[0] == 'F' && ap->model[1] == 'X'))) bswap (ap->model, sizeof(ap->model)); bswap (ap->serial, sizeof(ap->serial)); bswap (ap->revision, sizeof(ap->revision)); /* Clean up the model name, serial and revision numbers. */ btrim (ap->model, sizeof(ap->model)); btrim (ap->serial, sizeof(ap->serial)); btrim (ap->revision, sizeof(ap->revision)); return (ap); } /* * Wait uninterruptibly until controller is not busy and certain * status bits are set. * The wait is usually short unless it is for the controller to process * an entire critical command. * Return 1 for (possibly stale) controller errors, -1 for timeout errors, * or 0 for no errors. */ static int atapi_wait (int port, u_char bits_wanted) { int cnt; u_char s; /* Wait 5 sec for BUSY deassert. */ for (cnt=500000; cnt>0; --cnt) { s = inb (port + AR_STATUS); if (! (s & ARS_BSY)) break; DELAY (10); } if (cnt <= 0) return (-1); if (! bits_wanted) return (s & ARS_CHECK); /* Wait 50 msec for bits wanted. */ for (cnt=5000; cnt>0; --cnt) { s = inb (port + AR_STATUS); if ((s & bits_wanted) == bits_wanted) return (s & ARS_CHECK); DELAY (10); } return (-1); } void atapi_debug (struct atapi *ata, int on) { ata->debug = on; } static struct atapicmd *atapi_alloc (struct atapi *ata) { struct atapicmd *ac; while (! ata->free) tsleep ((caddr_t)ata, PRIBIO, "atacmd", 0); ac = ata->free; ata->free = ac->next; ac->busy = 1; return (ac); } static void atapi_free (struct atapi *ata, struct atapicmd *ac) { if (! ata->free) wakeup ((caddr_t)&ata); ac->busy = 0; ac->next = ata->free; ata->free = ac; } /* * Add new command request to the end of the queue. */ static void atapi_enqueue (struct atapi *ata, struct atapicmd *ac) { ac->next = 0; if (ata->tail) ata->tail->next = ac; else ata->queue = ac; ata->tail = ac; } static void atapi_done (struct atapi *ata) { struct atapicmd *ac = ata->queue; if (! ac) return; /* cannot happen */ ata->queue = ac->next; if (! ata->queue) ata->tail = 0; if (ac->callback) { (*ac->callback) (ac->cbarg1, ac->cbarg2, ac->count, ac->result); atapi_free (ata, ac); } else wakeup ((caddr_t)ac); } /* * Start new packet op. Called from wdstart(). * Return 1 if op started, and we are waiting for interrupt. * Return 0 when idle. */ int atapi_start (int ctrlr) { struct atapi *ata = atapitab + ctrlr; struct atapicmd *ac; again: ac = ata->queue; if (! ac) return (0); /* Start packet command. */ if (atapi_start_cmd (ata, ac) < 0) { atapi_done (ata); goto again; } if (ata->intrcmd) /* Wait for interrupt before sending packet command */ return (1); /* Wait for DRQ. */ if (atapi_wait_cmd (ata, ac) < 0) { atapi_done (ata); goto again; } /* Send packet command. */ atapi_send_cmd (ata, ac); return (1); } /* * Start new packet op. Returns -1 on errors. */ int atapi_start_cmd (struct atapi *ata, struct atapicmd *ac) { ac->result.error = 0; ac->result.status = 0; #ifdef PC98 outb(0x432,(ac->unit)%2); print(("(ac->unit) = %d,select %d (2) \n",(ac->unit),(ac->unit)%2)); outb (ata->port + AR_DRIVE, (ac->unit)/2 ? ARD_DRIVE1 : ARD_DRIVE0); #else outb (ata->port + AR_DRIVE, ac->unit ? ARD_DRIVE1 : ARD_DRIVE0); #endif if (atapi_wait (ata->port, 0) < 0) { printf ("atapi%d.%d: controller not ready for cmd\n", ata->ctrlr, ac->unit); ac->result.code = RES_NOTRDY; return (-1); } /* Set up the controller registers. */ outb (ata->port + AR_FEATURES, 0); outb (ata->port + AR_IREASON, 0); outb (ata->port + AR_TAG, 0); outb (ata->port + AR_CNTLO, ac->count & 0xff); outb (ata->port + AR_CNTHI, ac->count >> 8); outb (ata->port + AR_COMMAND, ATAPIC_PACKET); if (ata->debug) printf ("atapi%d.%d: start\n", ata->ctrlr, ac->unit); return (0); } /* * Wait for DRQ before sending packet cmd. Returns -1 on errors. */ int atapi_wait_cmd (struct atapi *ata, struct atapicmd *ac) { /* Wait for DRQ from 50 usec to 3 msec for slow devices */ int cnt = ata->intrcmd ? 10000 : ata->slow ? 3000 : 50; int ireason = 0, phase = 0; /* Wait for command phase. */ for (; cnt>0; cnt-=10) { ireason = inb (ata->port + AR_IREASON); ac->result.status = inb (ata->port + AR_STATUS); phase = (ireason & (ARI_CMD | ARI_IN)) | (ac->result.status & ARS_DRQ); if (phase == PHASE_CMDOUT) break; DELAY (10); } if (phase != PHASE_CMDOUT) { ac->result.code = RES_NODRQ; ac->result.error = inb (ata->port + AR_ERROR); printf ("atapi%d.%d: invalid command phase, ireason=0x%x, status=%b, error=%b\n", ata->ctrlr, ac->unit, ireason, ac->result.status, ARS_BITS, ac->result.error, AER_BITS); return (-1); } return (0); } /* * Send packet cmd. */ void atapi_send_cmd (struct atapi *ata, struct atapicmd *ac) { outsw (ata->port + AR_DATA, ac->cmd, ata->cmd16 ? 8 : 6); if (ata->debug) printf ("atapi%d.%d: send cmd %s %x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x\n", ata->ctrlr, ac->unit, cmdname (ac->cmd[0]), ac->cmd[0], ac->cmd[1], ac->cmd[2], ac->cmd[3], ac->cmd[4], ac->cmd[5], ac->cmd[6], ac->cmd[7], ac->cmd[8], ac->cmd[9], ac->cmd[10], ac->cmd[11], ac->cmd[12], ac->cmd[13], ac->cmd[14], ac->cmd[15]); } /* * Interrupt routine for the controller. Called from wdintr(). * Finish the started op, wakeup wait-type commands, * run callbacks for callback-type commands, then return. * Do not start new op here, it will be done by wdstart, * which is called just after us. * Return 1 if op continues, and we are waiting for new interrupt. * Return 0 when idle. */ int atapi_intr (int ctrlr) { struct atapi *ata = atapitab + ctrlr; struct atapicmd *ac = ata->queue; #ifdef PC98 outb(0x432,(ac->unit)%2); print(("atapi_intr:(ac->unit)= %d,select %d\n",ac->unit,(ac->unit)%2)); #endif if (! ac) { printf ("atapi%d: stray interrupt\n", ata->ctrlr); return (0); } if (atapi_io (ata, ac) > 0) return (1); atapi_done (ata); return (0); } /* * Process the i/o phase, transferring the command/data to/from the device. * Return 1 if op continues, and we are waiting for new interrupt. * Return 0 when idle. */ int atapi_io (struct atapi *ata, struct atapicmd *ac) { u_char ireason; u_short len, i; if (atapi_wait (ata->port, 0) < 0) { ac->result.status = inb (ata->port + AR_STATUS); ac->result.error = inb (ata->port + AR_ERROR); ac->result.code = RES_NOTRDY; printf ("atapi%d.%d: controller not ready, status=%b, error=%b\n", ata->ctrlr, ac->unit, ac->result.status, ARS_BITS, ac->result.error, AER_BITS); return (0); } ac->result.status = inb (ata->port + AR_STATUS); ac->result.error = inb (ata->port + AR_ERROR); len = inb (ata->port + AR_CNTLO); len |= inb (ata->port + AR_CNTHI) << 8; ireason = inb (ata->port + AR_IREASON); if (ata->debug) { printf ("atapi%d.%d: intr ireason=0x%x, len=%d, status=%b, error=%b\n", ata->ctrlr, ac->unit, ireason, len, ac->result.status, ARS_BITS, ac->result.error, AER_BITS); } switch ((ireason & (ARI_CMD | ARI_IN)) | (ac->result.status & ARS_DRQ)) { default: printf ("atapi%d.%d: unknown phase\n", ata->ctrlr, ac->unit); ac->result.code = RES_ERR; break; case PHASE_CMDOUT: /* Send packet command. */ if (! (ac->result.status & ARS_DRQ)) { printf ("atapi%d.%d: no cmd drq\n", ata->ctrlr, ac->unit); ac->result.code = RES_NODRQ; break; } atapi_send_cmd (ata, ac); return (1); case PHASE_DATAOUT: /* Write data */ if (ac->count > 0) { printf ("atapi%d.%d: invalid data direction\n", ata->ctrlr, ac->unit); ac->result.code = RES_INVDIR; break; } if (-ac->count < len) { print (("atapi%d.%d: send data underrun, %d bytes left\n", ata->ctrlr, ac->unit, -ac->count)); ac->result.code = RES_UNDERRUN; outsw (ata->port + AR_DATA, ac->addr, -ac->count / sizeof(short)); for (i= -ac->count; iport + AR_DATA, 0); } else outsw (ata->port + AR_DATA, ac->addr, len / sizeof(short)); ac->addr += len; ac->count += len; return (1); case PHASE_DATAIN: /* Read data */ if (ac->count < 0) { printf ("atapi%d.%d: invalid data direction\n", ata->ctrlr, ac->unit); ac->result.code = RES_INVDIR; break; } if (ac->count < len) { print (("atapi%d.%d: recv data overrun, %d bytes left\n", ata->ctrlr, ac->unit, ac->count)); ac->result.code = RES_OVERRUN; insw (ata->port + AR_DATA, ac->addr, ac->count / sizeof(short)); for (i=ac->count; iport + AR_DATA); } else insw (ata->port + AR_DATA, ac->addr, len / sizeof(short)); ac->addr += len; ac->count -= len; return (1); case PHASE_ABORTED: case PHASE_COMPLETED: if (ac->result.status & (ARS_CHECK | ARS_DF)) ac->result.code = RES_ERR; else if (ac->count < 0) { print (("atapi%d.%d: send data overrun, %d bytes left\n", ata->ctrlr, ac->unit, -ac->count)); ac->result.code = RES_OVERRUN; } else if (ac->count > 0) { print (("atapi%d.%d: recv data underrun, %d bytes left\n", ata->ctrlr, ac->unit, ac->count)); ac->result.code = RES_UNDERRUN; bzero (ac->addr, ac->count); } else ac->result.code = RES_OK; break; } return (0); } /* * Queue new packet request, then call wdstart(). * Called on splbio(). */ void atapi_request_callback (struct atapi *ata, int unit, u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4, u_char a5, u_char a6, u_char a7, u_char a8, u_char a9, u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15, char *addr, int count, atapi_callback_t *done, void *x, void *y) { struct atapicmd *ac; ac = atapi_alloc (ata); ac->cmd[0] = cmd; ac->cmd[1] = a1; ac->cmd[2] = a2; ac->cmd[3] = a3; ac->cmd[4] = a4; ac->cmd[5] = a5; ac->cmd[6] = a6; ac->cmd[7] = a7; ac->cmd[8] = a8; ac->cmd[9] = a9; ac->cmd[10] = a10; ac->cmd[11] = a11; ac->cmd[12] = a12; ac->cmd[13] = a13; ac->cmd[14] = a14; ac->cmd[15] = a15; ac->unit = unit; ac->addr = addr; ac->count = count; ac->callback = done; ac->cbarg1 = x; ac->cbarg2 = y; if (ata->debug) printf ("atapi%d.%d: req cb %x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x len=%d\n", ata->ctrlr, ac->unit, ac->cmd[0], ac->cmd[1], ac->cmd[2], ac->cmd[3], ac->cmd[4], ac->cmd[5], ac->cmd[6], ac->cmd[7], ac->cmd[8], ac->cmd[9], ac->cmd[10], ac->cmd[11], ac->cmd[12], ac->cmd[13], ac->cmd[14], ac->cmd[15], count); atapi_enqueue (ata, ac); wdstart (ata->ctrlr); } /* * Queue new packet request, then call wdstart(). * Wait until the request is finished. * Called on spl0(). * Return atapi error. * Buffer pointed to by *addr should be placed in core memory, not in stack! */ struct atapires atapi_request_wait (struct atapi *ata, int unit, u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4, u_char a5, u_char a6, u_char a7, u_char a8, u_char a9, u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15, char *addr, int count) { struct atapicmd *ac; int x = splbio (); struct atapires result; ac = atapi_alloc (ata); ac->cmd[0] = cmd; ac->cmd[1] = a1; ac->cmd[2] = a2; ac->cmd[3] = a3; ac->cmd[4] = a4; ac->cmd[5] = a5; ac->cmd[6] = a6; ac->cmd[7] = a7; ac->cmd[8] = a8; ac->cmd[9] = a9; ac->cmd[10] = a10; ac->cmd[11] = a11; ac->cmd[12] = a12; ac->cmd[13] = a13; ac->cmd[14] = a14; ac->cmd[15] = a15; ac->unit = unit; ac->addr = addr; ac->count = count; ac->callback = 0; ac->cbarg1 = 0; ac->cbarg2 = 0; if (ata->debug) printf ("atapi%d.%d: req w %x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x len=%d\n", ata->ctrlr, ac->unit, ac->cmd[0], ac->cmd[1], ac->cmd[2], ac->cmd[3], ac->cmd[4], ac->cmd[5], ac->cmd[6], ac->cmd[7], ac->cmd[8], ac->cmd[9], ac->cmd[10], ac->cmd[11], ac->cmd[12], ac->cmd[13], ac->cmd[14], ac->cmd[15], count); atapi_enqueue (ata, ac); wdstart (ata->ctrlr); tsleep ((caddr_t)ac, PRIBIO, "atareq", 0); result = ac->result; atapi_free (ata, ac); splx (x); return (result); } /* * Perform a packet command on the device. * Should be called on splbio(). * Return atapi error. */ struct atapires atapi_request_immediate (struct atapi *ata, int unit, u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4, u_char a5, u_char a6, u_char a7, u_char a8, u_char a9, u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15, char *addr, int count) { struct atapicmd cmdbuf, *ac = &cmdbuf; int cnt; ac->cmd[0] = cmd; ac->cmd[1] = a1; ac->cmd[2] = a2; ac->cmd[3] = a3; ac->cmd[4] = a4; ac->cmd[5] = a5; ac->cmd[6] = a6; ac->cmd[7] = a7; ac->cmd[8] = a8; ac->cmd[9] = a9; ac->cmd[10] = a10; ac->cmd[11] = a11; ac->cmd[12] = a12; ac->cmd[13] = a13; ac->cmd[14] = a14; ac->cmd[15] = a15; ac->unit = unit; ac->addr = addr; ac->count = count; ac->callback = 0; ac->cbarg1 = 0; ac->cbarg2 = 0; if (ata->debug) printf ("atapi%d.%d: req im %x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x len=%d\n", ata->ctrlr, ac->unit, ac->cmd[0], ac->cmd[1], ac->cmd[2], ac->cmd[3], ac->cmd[4], ac->cmd[5], ac->cmd[6], ac->cmd[7], ac->cmd[8], ac->cmd[9], ac->cmd[10], ac->cmd[11], ac->cmd[12], ac->cmd[13], ac->cmd[14], ac->cmd[15], count); /* Start packet command, wait for DRQ. */ if (atapi_start_cmd (ata, ac) >= 0 && atapi_wait_cmd (ata, ac) >= 0) { /* Send packet command. */ atapi_send_cmd (ata, ac); /* Wait for data i/o phase. */ for (cnt=20000; cnt>0; --cnt) if (((inb (ata->port + AR_IREASON) & (ARI_CMD | ARI_IN)) | (inb (ata->port + AR_STATUS) & ARS_DRQ)) != PHASE_CMDOUT) break; /* Do all needed i/o. */ while (atapi_io (ata, ac)) /* Wait for DRQ deassert. */ for (cnt=2000; cnt>0; --cnt) if (! (inb (ata->port + AR_STATUS) & ARS_DRQ)) break; } return (ac->result); } #endif /* ATAPI_STATIC */ #if defined (ATAPI_MODULE) || !defined(ATAPI_STATIC) int (*atapi_start_ptr) (int ctrlr); int (*atapi_intr_ptr) (int ctrlr); void (*atapi_debug_ptr) (struct atapi *ata, int on); struct atapires (*atapi_request_wait_ptr) (struct atapi *ata, int unit, u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4, u_char a5, u_char a6, u_char a7, u_char a8, u_char a9, u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15, char *addr, int count); void (*atapi_request_callback_ptr) (struct atapi *ata, int unit, u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4, u_char a5, u_char a6, u_char a7, u_char a8, u_char a9, u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15, char *addr, int count, atapi_callback_t *done, void *x, void *y); struct atapires (*atapi_request_immediate_ptr) (struct atapi *ata, int unit, u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4, u_char a5, u_char a6, u_char a7, u_char a8, u_char a9, u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15, char *addr, int count); #endif #ifdef ATAPI_MODULE /* * ATAPI loadable driver stubs. */ #include #include #include #include extern int atapi_lock (int ctlr); /* * XXX "ioconf.h" is not included by for lkms, so we need this * misplaced declaration. */ extern void wdintr (int); /* * Construct lkm_misc structure (see lkm.h). */ MOD_MISC(atapi); int atapi_locked; int atapi_lock (int ctlr) { atapi_locked = 1; wakeup (&atapi_locked); return (1); } /* * Function called when loading the driver. */ static int atapi_load (struct lkm_table *lkmtp, int cmd) { struct atapidrv *d; int n, x; /* * Probe all free IDE units, searching for ATAPI drives. */ n = 0; for (d=atapi_drvtab; dport; ++d) { /* Lock the controller. */ x = splbio (); atapi_locked = 0; atapi_start_ptr = atapi_lock; wdstart (d->ctlr); while (! atapi_locked) tsleep (&atapi_locked, PRIBIO, "atach", 0); /* Probe the drive. */ - if (atapi_attach (d->ctlr, d->unit, d->port, d->parent)) { + if (atapi_attach (d->ctlr, d->unit, d->port)) { d->attached = 1; ++n; } /* Unlock the controller. */ atapi_start_ptr = 0; wdintr (d->ctlr); splx (x); } if (! n) return ENXIO; atapi_start_ptr = atapi_start; atapi_intr_ptr = atapi_intr; atapi_debug_ptr = atapi_debug; atapi_request_wait_ptr = atapi_request_wait; atapi_request_callback_ptr = atapi_request_callback; atapi_request_immediate_ptr = atapi_request_immediate; atapi_tab = atapitab; return 0; } /* * Function called when unloading the driver. */ static int atapi_unload (struct lkm_table *lkmtp, int cmd) { struct atapi *ata; int u; for (ata=atapi_tab; ataport) for (u=0; u<2; ++u) if (ata->attached[u]) return EBUSY; for (ata=atapi_tab; ataport) for (u=0; u<2; ++u) if (ata->params[u]) { free (ata->params[u], M_TEMP); ata->params[u] = 0; } atapi_start_ptr = 0; atapi_intr_ptr = 0; atapi_debug_ptr = 0; atapi_request_wait_ptr = 0; atapi_request_callback_ptr = 0; atapi_request_immediate_ptr = 0; atapi_tab = 0; return 0; } /* * Dispatcher function for the module (load/unload/stat). */ int atapi_mod (struct lkm_table *lkmtp, int cmd, int ver) { DISPATCH (lkmtp, cmd, ver, atapi_load, atapi_unload, lkm_nullcmd); } #endif /* ATAPI_MODULE */ #endif /* NWDC && ATAPI */ Index: head/sys/pc98/pc98/if_fe.c =================================================================== --- head/sys/pc98/pc98/if_fe.c (revision 18264) +++ head/sys/pc98/pc98/if_fe.c (revision 18265) @@ -1,3161 +1,3160 @@ /* * All Rights Reserved, Copyright (C) Fujitsu Limited 1995 * * This software may be used, modified, copied, distributed, and sold, in * both source and binary form provided that the above copyright, these * terms and the following disclaimer are retained. The name of the author * and/or the contributor may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND THE CONTRIBUTOR ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE CONTRIBUTOR BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* - * $Id: if_fe.c,v 1.6 1996/09/07 02:13:52 asami Exp $ + * $Id: if_fe.c,v 1.7 1996/09/10 09:38:08 asami Exp $ * * Device driver for Fujitsu MB86960A/MB86965A based Ethernet cards. * To be used with FreeBSD 2.x * Contributed by M. Sekiguchi. * * This version is intended to be a generic template for various * MB86960A/MB86965A based Ethernet cards. It currently supports * Fujitsu FMV-180 series for ISA and Allied-Telesis AT1700/RE2000 * series for ISA, as well as Fujitsu MBH10302 PC card. * There are some currently- * unused hooks embedded, which are primarily intended to support * other types of Ethernet cards, but the author is not sure whether * they are useful. * * This version also includes some alignments for * RE1000/RE1000+/ME1500 support. It is incomplete, however, since the * cards are not for AT-compatibles. (They are for PC98 bus -- a * proprietary bus architecture available only in Japan.) Further * work for PC98 version will be available as a part of FreeBSD(98) * project. * * This software is a derivative work of if_ed.c version 1.56 by David * Greenman available as a part of FreeBSD 2.0 RELEASE source distribution. * * The following lines are retained from the original if_ed.c: * * Copyright (C) 1993, David Greenman. This software may be used, modified, * copied, distributed, and sold, in both source and binary form provided * that the above copyright and these terms are retained. Under no * circumstances is the author responsible for the proper functioning * of this software, nor does the author assume any responsibility * for damages incurred with its use. */ /* * Modified for Allied-Telesis RE1000 series. */ /* * TODO: * o To support MBH10304 PC card. It is another MB8696x based * PCMCIA Ethernet card by Fujitsu, which is not compatible with * MBH10302. * o To merge FreeBSD(98) efforts into a single source file. * o To support ISA PnP auto configuration for FMV-183/184. * o To reconsider mbuf usage. * o To reconsider transmission buffer usage, including * transmission buffer size (currently 4KB x 2) and pros-and- * cons of multiple frame transmission. * o To test IPX codes. */ #include "fe.h" #include "bpfilter.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif /* IPX code is not tested. FIXME. */ #ifdef IPX #include #include #endif /* To be used with IPv6 package of INRIA. */ #ifdef INET6 /* IPv6 added by shin 96.2.6 */ #include #endif /* XNS code is not tested. FIXME. */ #ifdef NS #include #include #endif #if NBPFILTER > 0 #include #include #endif #include #include #include /* PCCARD suport */ #include "crd.h" #if NCRD > 0 #include #include #include #include #endif #include #include /* * This version of fe is an ISA device driver. * Override the following macro to adapt it to another bus. * (E.g., PC98.) */ #define DEVICE struct isa_device /* * Default settings for fe driver specific options. * They can be set in config file by "options" statements. */ /* * Debug control. * 0: No debug at all. All debug specific codes are stripped off. * 1: Silent. No debug messages are logged except emergent ones. * 2: Brief. Lair events and/or important information are logged. * 3: Detailed. Logs all information which *may* be useful for debugging. * 4: Trace. All actions in the driver is logged. Super verbose. */ #ifndef FE_DEBUG #define FE_DEBUG 1 #endif /* * Transmit just one packet per a "send" command to 86960. * This option is intended for performance test. An EXPERIMENTAL option. */ #ifndef FE_SINGLE_TRANSMISSION #define FE_SINGLE_TRANSMISSION 0 #endif /* * Device configuration flags. */ /* DLCR6 settings. */ #define FE_FLAGS_DLCR6_VALUE 0x007F /* Force DLCR6 override. */ #define FE_FLAGS_OVERRIDE_DLCR6 0x0080 /* Shouldn't these be defined somewhere else such as isa_device.h? */ #define NO_IOADDR 0xFFFFFFFF #define NO_IRQ 0 /* * Data type for a multicast address filter on 8696x. */ struct fe_filter { u_char data [ FE_FILTER_LEN ]; }; /* * Special filter values. */ static struct fe_filter const fe_filter_nothing = { FE_FILTER_NOTHING }; static struct fe_filter const fe_filter_all = { FE_FILTER_ALL }; /* How many registers does an fe-supported adapter have at maximum? */ #define MAXREGISTERS 32 /* * fe_softc: per line info and status */ static struct fe_softc { /* Used by "common" codes. */ struct arpcom arpcom; /* Ethernet common */ /* Used by config codes. */ /* Set by probe() and not modified in later phases. */ char * typestr; /* printable name of the interface. */ u_short iobase; /* base I/O address of the adapter. */ u_short ioaddr [ MAXREGISTERS ]; /* I/O addresses of register. */ u_short txb_size; /* size of TX buffer, in bytes */ u_char proto_dlcr4; /* DLCR4 prototype. */ u_char proto_dlcr5; /* DLCR5 prototype. */ u_char proto_dlcr6; /* DLCR6 prototype. */ u_char proto_dlcr7; /* DLCR7 prototype. */ u_char proto_bmpr13; /* BMPR13 prototype. */ /* Vendor specific hooks. */ void ( * init )( struct fe_softc * ); /* Just before fe_init(). */ void ( * stop )( struct fe_softc * ); /* Just after fe_stop(). */ /* Transmission buffer management. */ u_short txb_free; /* free bytes in TX buffer */ u_char txb_count; /* number of packets in TX buffer */ u_char txb_sched; /* number of scheduled packets */ u_char txb_padding; /* number of delayed padding bytes */ /* Multicast address filter management. */ u_char filter_change; /* MARs must be changed ASAP. */ struct fe_filter filter;/* new filter value. */ } fe_softc[NFE]; -/* Frequently accessed members in arpcom and kdc. */ #define sc_if arpcom.ac_if #define sc_unit arpcom.ac_if.if_unit #define sc_enaddr arpcom.ac_enaddr /* Standard driver entry points. These can be static. */ static int fe_probe ( DEVICE * ); static int fe_attach ( DEVICE * ); static void fe_init ( int ); static int fe_ioctl ( struct ifnet *, int, caddr_t ); static void fe_start ( struct ifnet * ); static void fe_reset ( int ); static void fe_watchdog ( struct ifnet * ); /* Local functions. Order of declaration is confused. FIXME. */ #ifdef PC98 static int fe_probe_re1000 ( DEVICE *, struct fe_softc * ); static int fe_probe_re1000p( DEVICE *, struct fe_softc * ); #else static int fe_probe_fmv ( DEVICE *, struct fe_softc * ); static int fe_probe_ati ( DEVICE *, struct fe_softc * ); static int fe_probe_mbh ( DEVICE *, struct fe_softc * ); static void fe_init_mbh ( struct fe_softc * ); #endif static int fe_get_packet ( struct fe_softc *, u_short ); static void fe_stop ( int ); static void fe_tint ( struct fe_softc *, u_char ); static void fe_rint ( struct fe_softc *, u_char ); static void fe_xmit ( struct fe_softc * ); static void fe_write_mbufs ( struct fe_softc *, struct mbuf * ); static struct fe_filter fe_mcaf ( struct fe_softc * ); static int fe_hash ( u_char * ); static void fe_setmode ( struct fe_softc * ); static void fe_loadmar ( struct fe_softc * ); #if FE_DEBUG >= 1 static void fe_dump ( int, struct fe_softc *, char * ); #endif /* Driver struct used in the config code. This must be public (external.) */ struct isa_driver fedriver = { fe_probe, fe_attach, "fe", 1 /* It's safe to mark as "sensitive" */ }; /* * Fe driver specific constants which relate to 86960/86965. */ /* Interrupt masks */ #define FE_TMASK ( FE_D2_COLL16 | FE_D2_TXDONE ) #define FE_RMASK ( FE_D3_OVRFLO | FE_D3_CRCERR \ | FE_D3_ALGERR | FE_D3_SRTPKT | FE_D3_PKTRDY ) /* Maximum number of iterations for a receive interrupt. */ #define FE_MAX_RECV_COUNT ( ( 65536 - 2048 * 2 ) / 64 ) /* * Maximum size of SRAM is 65536, * minimum size of transmission buffer in fe is 2x2KB, * and minimum amount of received packet including headers * added by the chip is 64 bytes. * Hence FE_MAX_RECV_COUNT is the upper limit for number * of packets in the receive buffer. */ /* * Routines to access contiguous I/O ports. */ static void inblk ( struct fe_softc * sc, int offs, u_char * mem, int len ) { while ( --len >= 0 ) { *mem++ = inb( sc->ioaddr[ offs++ ] ); } } static void outblk ( struct fe_softc * sc, int offs, u_char const * mem, int len ) { while ( --len >= 0 ) { outb( sc->ioaddr[ offs++ ], *mem++ ); } } /* PCCARD Support */ #if NCRD > 0 /* * PC-Card (PCMCIA) specific code. */ static int fe_card_intr(struct pccard_dev *); /* Interrupt handler */ static void feunload(struct pccard_dev *); /* Disable driver */ static void fesuspend(struct pccard_dev *); /* Suspend driver */ static int feinit(struct pccard_dev *, int); /* init device */ static struct pccard_drv fe_info = { "fe", fe_card_intr, feunload, fesuspend, feinit, 0, /* Attributes - presently unused */ &net_imask /* Interrupt mask for device */ /* XXX - Should this also include net_imask? */ }; /* * Called when a power down is requested. Shuts down the * device and configures the device as unavailable (but * still loaded...). A resume is done by calling * feinit with first=0. This is called when the user suspends * the system, or the APM code suspends the system. */ static void fesuspend(struct pccard_dev *dp) { printf("fe%d: suspending\n", dp->isahd.id_unit); } /* * Initialize the device - called from Slot manager. * if first is set, then initially check for * the device's existence before initializing it. * Once initialized, the device table may be set up. */ static int feinit(struct pccard_dev *dp, int first) { /* validate unit number. */ if (first) { if (dp->isahd.id_unit >= NFE) return (ENODEV); /* * Probe the device. If a value is returned, * the device was found at the location. */ #if FE_DEBUG >= 2 printf("Start Probe\n"); #endif if (fe_probe(&dp->isahd) == 0) return (ENXIO); #if FE_DEBUG >= 2 printf("Start attach\n"); #endif if (fe_attach(&dp->isahd) == 0) return (ENXIO); } /* * XXX TODO: * If it was initialized before, the device structure * should also be initialized. We should * reset (and possibly restart) the hardware, but * I am not sure of the best way to do this... */ return (0); } /* * feunload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void feunload(struct pccard_dev *dp) { printf("fe%d: unload\n", dp->isahd.id_unit); fe_stop(dp->isahd.id_unit); } /* * fe_card_intr - Shared interrupt called from * front end of PC-Card handler. */ static int fe_card_intr(struct pccard_dev *dp) { feintr(dp->isahd.id_unit); return (1); } #endif /* NCRD > 0 */ /* * Hardware probe routines. */ /* How and where to probe; to support automatic I/O address detection. */ struct fe_probe_list { int ( * probe ) ( DEVICE *, struct fe_softc * ); u_short const * addresses; }; /* Lists of possible addresses. */ #ifdef PC98 static u_short const fe_re1000_addr [] = { 0x0D0, 0x0D2, 0x0D4, 0x0D6, 0x0D8, 0x0DA, 0x0DC, 0x0DE, 0x1D0, 0x1D2, 0x1D4, 0x1D6, 0x1D8, 0x1DA, 0x1DC, 0x1DE, 0 }; static u_short const fe_re1000p_addr [] = { 0x0D0, 0x0D2, 0x0D4, 0x0D8, 0x1D4, 0x1D6, 0x1D8, 0x1DA, 0 }; #else static u_short const fe_fmv_addr [] = { 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x300, 0x340, 0 }; static u_short const fe_ati_addr [] = { 0x240, 0x260, 0x280, 0x2A0, 0x300, 0x320, 0x340, 0x380, 0 }; #endif static struct fe_probe_list const fe_probe_list [] = { #ifdef PC98 { fe_probe_re1000, fe_re1000_addr }, { fe_probe_re1000p, fe_re1000p_addr }, #else { fe_probe_fmv, fe_fmv_addr }, { fe_probe_ati, fe_ati_addr }, { fe_probe_mbh, NULL }, /* PCMCIAs cannot be auto-detected. */ #endif { NULL, NULL } }; /* * Determine if the device is present * * on entry: * a pointer to an isa_device struct * on exit: * zero if device not found * or number of i/o addresses used (if found) */ static int fe_probe ( DEVICE * dev ) { #if NCRD > 0 static int fe_already_init; #endif struct fe_softc * sc; int u; int nports; struct fe_probe_list const * list; u_short const * addr; u_short single [ 2 ]; /* Initialize "minimum" parts of our softc. */ sc = &fe_softc[ dev->id_unit ]; sc->sc_unit = dev->id_unit; #if NCRD > 0 /* * If PC-Card probe required, then register driver with * slot manager. */ if (fe_already_init != 1) { pccard_add_driver(&fe_info); fe_already_init = 1; } #endif /* NCRD > 0 */ /* Probe each possibility, one at a time. */ for ( list = fe_probe_list; list->probe != NULL; list++ ) { if ( dev->id_iobase != NO_IOADDR ) { /* Probe one specific address. */ single[ 0 ] = dev->id_iobase; single[ 1 ] = 0; addr = single; } else if ( list->addresses != NULL ) { /* Auto detect. */ addr = list->addresses; } else { /* We need a list of addresses to do auto detect. */ continue; } /* Probe all possible addresses for the board. */ while ( *addr != 0 ) { /* See if the address is already in use. */ for ( u = 0; u < NFE; u++ ) { if ( fe_softc[u].iobase == *addr ) break; } #if FE_DEBUG >= 3 if ( u == NFE ) { log( LOG_INFO, "fe%d: probing %d at 0x%x\n", sc->sc_unit, list - fe_probe_list, *addr ); } else if ( u == sc->sc_unit ) { log( LOG_INFO, "fe%d: re-probing %d at 0x%x?\n", sc->sc_unit, list - fe_probe_list, *addr ); } else { log( LOG_INFO, "fe%d: skipping %d at 0x%x\n", sc->sc_unit, list - fe_probe_list, *addr ); } #endif /* Probe the address if it is free. */ if ( u == NFE || u == sc->sc_unit ) { /* Probe an address. */ sc->iobase = *addr; nports = list->probe( dev, sc ); if ( nports > 0 ) { /* Found. */ dev->id_iobase = *addr; return ( nports ); } sc->iobase = 0; } /* Try next. */ addr++; } } /* Probe failed. */ return ( 0 ); } /* * Check for specific bits in specific registers have specific values. */ struct fe_simple_probe_struct { u_char port; /* Offset from the base I/O address. */ u_char mask; /* Bits to be checked. */ u_char bits; /* Values to be compared against. */ }; static int fe_simple_probe ( struct fe_softc const * sc, struct fe_simple_probe_struct const * sp ) { struct fe_simple_probe_struct const * p; for ( p = sp; p->mask != 0; p++ ) { #if FE_DEBUG >=2 printf("Probe Port:%x,Value:%x,Mask:%x.Bits:%x\n", p->port,inb(sc->ioaddr[ p->port]),p->mask,p->bits); #endif if ( ( inb( sc->ioaddr[ p->port ] ) & p->mask ) != p->bits ) { return ( 0 ); } } return ( 1 ); } /* * Routines to read all bytes from the config EEPROM through MB86965A. * I'm not sure what exactly I'm doing here... I was told just to follow * the steps, and it worked. Could someone tell me why the following * code works? (Or, why all similar codes I tried previously doesn't * work.) FIXME. */ static void fe_strobe_eeprom ( u_short bmpr16 ) { /* * We must guarantee 800ns (or more) interval to access slow * EEPROMs. The following redundant code provides enough * delay with ISA timing. (Even if the bus clock is "tuned.") * Some modification will be needed on faster busses. */ outb( bmpr16, FE_B16_SELECT ); outb( bmpr16, FE_B16_SELECT ); outb( bmpr16, FE_B16_SELECT | FE_B16_CLOCK ); outb( bmpr16, FE_B16_SELECT | FE_B16_CLOCK ); outb( bmpr16, FE_B16_SELECT ); outb( bmpr16, FE_B16_SELECT ); } static void fe_read_eeprom ( struct fe_softc * sc, u_char * data ) { u_short bmpr16 = sc->ioaddr[ FE_BMPR16 ]; u_short bmpr17 = sc->ioaddr[ FE_BMPR17 ]; u_char n, val, bit; /* Read bytes from EEPROM; two bytes per an iteration. */ for ( n = 0; n < FE_EEPROM_SIZE / 2; n++ ) { /* Reset the EEPROM interface. */ outb( bmpr16, 0x00 ); outb( bmpr17, 0x00 ); /* Start EEPROM access. */ outb( bmpr16, FE_B16_SELECT ); outb( bmpr17, FE_B17_DATA ); fe_strobe_eeprom( bmpr16 ); /* Pass the iteration count to the chip. */ val = 0x80 | n; for ( bit = 0x80; bit != 0x00; bit >>= 1 ) { outb( bmpr17, ( val & bit ) ? FE_B17_DATA : 0 ); fe_strobe_eeprom( bmpr16 ); } outb( bmpr17, 0x00 ); /* Read a byte. */ val = 0; for ( bit = 0x80; bit != 0x00; bit >>= 1 ) { fe_strobe_eeprom( bmpr16 ); if ( inb( bmpr17 ) & FE_B17_DATA ) { val |= bit; } } *data++ = val; /* Read one more byte. */ val = 0; for ( bit = 0x80; bit != 0x00; bit >>= 1 ) { fe_strobe_eeprom( bmpr16 ); if ( inb( bmpr17 ) & FE_B17_DATA ) { val |= bit; } } *data++ = val; } /* Reset the EEPROM interface, again. */ outb( bmpr16, 0x00 ); outb( bmpr17, 0x00 ); #if FE_DEBUG >= 3 /* Report what we got. */ data -= FE_EEPROM_SIZE; log( LOG_INFO, "fe%d: EEPROM:" " %02x%02x%02x%02x %02x%02x%02x%02x -" " %02x%02x%02x%02x %02x%02x%02x%02x -" " %02x%02x%02x%02x %02x%02x%02x%02x -" " %02x%02x%02x%02x %02x%02x%02x%02x\n", sc->sc_unit, data[ 0], data[ 1], data[ 2], data[ 3], data[ 4], data[ 5], data[ 6], data[ 7], data[ 8], data[ 9], data[10], data[11], data[12], data[13], data[14], data[15], data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23], data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31] ); #endif } /* * Hardware (vendor) specific probe routines. */ #ifdef PC98 /* * Probe and initialization for Allied-Telesis RE1000 series. */ static int fe_probe_re1000 ( DEVICE * isa_dev, struct fe_softc * sc ) { int i, n; int dlcr6, dlcr7; u_char c = 0; static u_short const irqmap [ 4 ] = { IRQ3, IRQ5, IRQ6, IRQ12 }; #if FE_DEBUG >= 3 log( LOG_INFO, "fe%d: probe (0x%x) for RE1000\n", sc->sc_unit, sc->iobase ); fe_dump( LOG_INFO, sc, NULL ); #endif /* Setup an I/O address mapping table. */ for ( i = 0; i < MAXREGISTERS; i++ ) { sc->ioaddr[ i ] = sc->iobase + (i/2)*0x200 + (i%2); } /* * RE1000 does not use 86965 EEPROM interface. */ c ^= sc->sc_enaddr[0] = inb(sc->ioaddr[FE_RE1000_MAC0]); c ^= sc->sc_enaddr[1] = inb(sc->ioaddr[FE_RE1000_MAC1]); c ^= sc->sc_enaddr[2] = inb(sc->ioaddr[FE_RE1000_MAC2]); c ^= sc->sc_enaddr[3] = inb(sc->ioaddr[FE_RE1000_MAC3]); c ^= sc->sc_enaddr[4] = inb(sc->ioaddr[FE_RE1000_MAC4]); c ^= sc->sc_enaddr[5] = inb(sc->ioaddr[FE_RE1000_MAC5]); c ^= inb(sc->ioaddr[FE_RE1000_MACCHK]); if (c != 0) return 0; if ( sc->sc_enaddr[ 0 ] != 0x00 || sc->sc_enaddr[ 1 ] != 0x00 || sc->sc_enaddr[ 2 ] != 0xF4 ) return 0; /* * check interrupt configure */ for (n=0; n<4; n++) { if (isa_dev->id_irq == irqmap[n]) break; } if (n == 4) return 0; /* * set irq */ c = inb(sc->ioaddr[FE_RE1000_IRQCONF]); c &= (~ FE_RE1000_IRQCONF_IRQ); c |= (1 << (n + FE_RE1000_IRQCONF_IRQSHIFT)); outb(sc->ioaddr[FE_RE1000_IRQCONF], c); sc->typestr = "RE1000"; sc->sc_description = "Ethernet adapter: RE1000"; /* * Program the 86965 as follows: * SRAM: 32KB, 100ns, byte-wide access. * Transmission buffer: 4KB x 2. * System bus interface: 16 bits. */ sc->proto_dlcr4 = FE_D4_LBC_DISABLE | FE_D4_CNTRL; /* FIXME */ sc->proto_dlcr5 = 0; sc->proto_dlcr6 = FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x4KB | FE_D6_BBW_BYTE | FE_D6_SBW_WORD | FE_D6_SRAM_100ns; sc->proto_dlcr7 = FE_D7_BYTSWP_LH | FE_D7_IDENT_EC; sc->proto_bmpr13 = FE_B13_TPTYPE_UTP | FE_B13_PORT_AUTO; #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "RE1000 found" ); #endif /* Initialize 86965. */ outb( sc->ioaddr[FE_DLCR6], sc->proto_dlcr6 | FE_D6_DLC_DISABLE ); DELAY(200); /* Disable all interrupts. */ outb( sc->ioaddr[FE_DLCR2], 0 ); outb( sc->ioaddr[FE_DLCR3], 0 ); #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "end of fe_probe_re1000()" ); #endif /* * That's all. RE1000 occupies 2*16 I/O addresses, by the way. */ return 2; /* ??? */ } /* * Probe and initialization for Allied-Telesis RE1000Plus/ME1500 series. */ static int fe_probe_re1000p ( DEVICE * isa_dev, struct fe_softc * sc ) { int i, n, signature; int dlcr6, dlcr7; u_char eeprom [ FE_EEPROM_SIZE ]; static u_short const irqmap [ 4 ] = { IRQ3, IRQ5, IRQ6, IRQ12 }; static struct fe_simple_probe_struct const probe_signature1 [] = { { FE_DLCR0, 0xBF, 0x00 }, { FE_DLCR2, 0xFF, 0x00 }, { FE_DLCR4, 0x0F, 0x06 }, { FE_DLCR6, 0x0F, 0x06 }, { 0 } }; static struct fe_simple_probe_struct const probe_signature2 [] = { { FE_DLCR1, 0xFF, 0x00 }, { FE_DLCR3, 0xFF, 0x00 }, { FE_DLCR5, 0xFF, 0x41 }, { 0 } }; static struct fe_simple_probe_struct const probe_table [] = { { FE_DLCR2, 0x71, 0x00 }, { FE_DLCR4, 0x08, 0x00 }, { FE_DLCR5, 0x80, 0x00 }, { 0 } }; static struct fe_simple_probe_struct const vendor_code [] = { { FE_DLCR8, 0xFF, 0x00 }, { FE_DLCR9, 0xFF, 0x00 }, { FE_DLCR10, 0xFF, 0xF4 }, { 0 } }; #if FE_DEBUG >= 3 log( LOG_INFO, "fe%d: probe (0x%x) for RE1000Plus/ME1500\n", sc->sc_unit, sc->iobase ); fe_dump( LOG_INFO, sc, NULL ); #endif /* Setup an I/O address mapping table. */ for ( i = 0; i < 16; i++ ) { sc->ioaddr[ i ] = sc->iobase + (i/2)*0x200 + (i%2); } for ( i = 16; i < MAXREGISTERS; i++ ) { sc->ioaddr[ i ] = sc->iobase + i*0x200 - 0x1000; } /* First, check the "signature" */ signature = 0; if (fe_simple_probe(sc, probe_signature1)) { outb(sc->ioaddr[FE_DLCR6], (inb(sc->ioaddr[FE_DLCR6]) & 0xCF) | 0x16); if (fe_simple_probe(sc, probe_signature2)) signature = 1; } /* * If the "signature" not detected, 86965 *might* be previously * initialized. So, check the Ethernet address here. * * Allied-Telesis uses 00 00 F4 ?? ?? ??. */ if (signature == 0) { /* Simple check */ if (!fe_simple_probe(sc, probe_table)) return 0; /* Disable DLC */ dlcr6 = inb(sc->ioaddr[FE_DLCR6]); outb(sc->ioaddr[FE_DLCR6], dlcr6 | FE_D6_DLC_DISABLE); /* Select register bank for DLCR */ dlcr7 = inb(sc->ioaddr[FE_DLCR7]); outb(sc->ioaddr[FE_DLCR7], dlcr7 & 0xF3 | FE_D7_RBS_DLCR); /* Check the Ethernet address */ if (!fe_simple_probe(sc, vendor_code)) return 0; /* Restore configuration registers */ DELAY(200); outb(sc->ioaddr[FE_DLCR6], dlcr6); outb(sc->ioaddr[FE_DLCR7], dlcr7); } /* * We are now almost sure we have an 86965 at the given * address. So, read EEPROM through 86965. We have to write * into LSI registers to read from EEPROM. I want to avoid it * at this stage, but I cannot test the presense of the chip * any further without reading EEPROM. FIXME. */ fe_read_eeprom( sc, eeprom ); /* Make sure that config info in EEPROM and 86965 agree. */ if ( eeprom[ FE_EEPROM_CONF ] != inb( sc->ioaddr[FE_BMPR19] ) ) { return 0; } /* * Initialize constants in the per-line structure. */ /* Get our station address from EEPROM. */ bcopy( eeprom + FE_ATI_EEP_ADDR, sc->sc_enaddr, ETHER_ADDR_LEN ); sc->typestr = "RE1000Plus/ME1500"; sc->sc_description = "Ethernet adapter: RE1000Plus/ME1500"; /* * Read IRQ configuration. */ n = (inb(sc->ioaddr[FE_BMPR19]) & FE_B19_IRQ ) >> FE_B19_IRQ_SHIFT; isa_dev->id_irq = irqmap[n]; /* * Program the 86965 as follows: * SRAM: 32KB, 100ns, byte-wide access. * Transmission buffer: 4KB x 2. * System bus interface: 16 bits. */ sc->proto_dlcr4 = FE_D4_LBC_DISABLE | FE_D4_CNTRL; /* FIXME */ sc->proto_dlcr5 = 0; sc->proto_dlcr6 = FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x4KB | FE_D6_BBW_BYTE | FE_D6_SBW_WORD | FE_D6_SRAM_100ns; sc->proto_dlcr7 = FE_D7_BYTSWP_LH | FE_D7_IDENT_EC; sc->proto_bmpr13 = FE_B13_TPTYPE_UTP | FE_B13_PORT_AUTO; #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "RE1000Plus/ME1500 found" ); #endif /* Initialize 86965. */ outb( sc->ioaddr[FE_DLCR6], sc->proto_dlcr6 | FE_D6_DLC_DISABLE ); DELAY(200); /* Disable all interrupts. */ outb( sc->ioaddr[FE_DLCR2], 0 ); outb( sc->ioaddr[FE_DLCR3], 0 ); #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "end of fe_probe_re1000p()" ); #endif /* * That's all. RE1000Plus/ME1500 occupies 2*16 I/O addresses, by the way. */ return 2; /* ??? */ } #else /* * Probe and initialization for Fujitsu FMV-180 series boards */ static int fe_probe_fmv ( DEVICE * dev, struct fe_softc * sc ) { int i, n; static u_short const baseaddr [ 8 ] = { 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x300, 0x340 }; static u_short const irqmap [ 4 ] = { IRQ3, IRQ7, IRQ10, IRQ15 }; static struct fe_simple_probe_struct const probe_table [] = { { FE_DLCR2, 0x70, 0x00 }, { FE_DLCR4, 0x08, 0x00 }, /* { FE_DLCR5, 0x80, 0x00 }, Doesn't work. */ { FE_FMV0, 0x78, 0x50 }, /* ERRDY+PRRDY */ { FE_FMV1, 0xB0, 0x00 }, /* FMV-183/184 has 0x48 bits. */ { FE_FMV3, 0x7F, 0x00 }, #if 1 /* * Test *vendor* part of the station address for Fujitsu. * The test will gain reliability of probe process, but * it rejects FMV-180 clone boards manufactured by other vendors. * We have to turn the test off when such cards are made available. */ { FE_FMV4, 0xFF, 0x00 }, { FE_FMV5, 0xFF, 0x00 }, { FE_FMV6, 0xFF, 0x0E }, #else /* * We can always verify the *first* 2 bits (in Ethernet * bit order) are "no multicast" and "no local" even for * unknown vendors. */ { FE_FMV4, 0x03, 0x00 }, #endif { 0 } }; /* "Hardware revision ID" */ int revision; /* * See if the specified address is possible for FMV-180 series. */ for ( i = 0; i < 8; i++ ) { if ( baseaddr[ i ] == sc->iobase ) break; } if ( i == 8 ) return 0; /* Setup an I/O address mapping table. */ for ( i = 0; i < MAXREGISTERS; i++ ) { sc->ioaddr[ i ] = sc->iobase + i; } /* Simple probe. */ if ( !fe_simple_probe( sc, probe_table ) ) return 0; /* Check if our I/O address matches config info. on EEPROM. */ n = ( inb( sc->ioaddr[ FE_FMV2 ] ) & FE_FMV2_IOS ) >> FE_FMV2_IOS_SHIFT; if ( baseaddr[ n ] != sc->iobase ) return 0; /* Find the "hardware revision." */ revision = inb( sc->ioaddr[ FE_FMV1 ] ) & FE_FMV1_REV; /* Determine the card type. */ sc->typestr = NULL; switch ( inb( sc->ioaddr[ FE_FMV0 ] ) & FE_FMV0_MEDIA ) { case 0: /* No interface? This doesn't seem to be an FMV-180... */ return 0; case FE_FMV0_MEDIUM_T: switch ( revision ) { case 8: sc->typestr = "FMV-183"; break; } break; case FE_FMV0_MEDIUM_T | FE_FMV0_MEDIUM_5: switch ( revision ) { case 0: sc->typestr = "FMV-181"; break; case 1: sc->typestr = "FMV-181A"; break; } break; case FE_FMV0_MEDIUM_2: switch ( revision ) { case 8: sc->typestr = "FMV-184 (CSR = 2)"; break; } break; case FE_FMV0_MEDIUM_5: switch ( revision ) { case 8: sc->typestr = "FMV-184 (CSR = 1)"; break; } break; case FE_FMV0_MEDIUM_2 | FE_FMV0_MEDIUM_5: switch ( revision ) { case 0: sc->typestr = "FMV-182"; break; case 1: sc->typestr = "FMV-182A"; break; case 8: sc->typestr = "FMV-184 (CSR = 3)"; break; } break; } if ( sc->typestr == NULL ) { /* Unknown card type... Hope the driver works. */ sc->typestr = "unknown FMV-180 version"; log( LOG_WARNING, "fe%d: %s: %x-%x-%x-%x\n", sc->sc_unit, sc->typestr, inb( sc->ioaddr[ FE_FMV0 ] ), inb( sc->ioaddr[ FE_FMV1 ] ), inb( sc->ioaddr[ FE_FMV2 ] ), inb( sc->ioaddr[ FE_FMV3 ] ) ); } /* * An FMV-180 has been proved. * Determine which IRQ to be used. * * In this version, we give a priority to the kernel config file. * If the EEPROM and config don't match, say it to the user for * an attention. */ n = ( inb( sc->ioaddr[ FE_FMV2 ] ) & FE_FMV2_IRS ) >> FE_FMV2_IRS_SHIFT; if ( dev->id_irq == NO_IRQ ) { /* Just use the probed value. */ dev->id_irq = irqmap[ n ]; } else if ( dev->id_irq != irqmap[ n ] ) { /* Don't match. */ log( LOG_WARNING, "fe%d: check IRQ in config; it may be incorrect", sc->sc_unit ); } /* * Initialize constants in the per-line structure. */ /* Get our station address from EEPROM. */ inblk( sc, FE_FMV4, sc->sc_enaddr, ETHER_ADDR_LEN ); /* Make sure we got a valid station address. */ if ( ( sc->sc_enaddr[ 0 ] & 0x03 ) != 0x00 || ( sc->sc_enaddr[ 0 ] == 0x00 && sc->sc_enaddr[ 1 ] == 0x00 && sc->sc_enaddr[ 2 ] == 0x00 ) ) return 0; /* * Register values which (may) depend on board design. * * Program the 86960 as follows: * SRAM: 32KB, 100ns, byte-wide access. * Transmission buffer: 4KB x 2. * System bus interface: 16 bits. */ sc->proto_dlcr4 = FE_D4_LBC_DISABLE | FE_D4_CNTRL; sc->proto_dlcr5 = 0; sc->proto_dlcr6 = FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x4KB | FE_D6_BBW_BYTE | FE_D6_SBW_WORD | FE_D6_SRAM_100ns; sc->proto_dlcr7 = FE_D7_BYTSWP_LH | FE_D7_IDENT_EC; sc->proto_bmpr13 = FE_B13_TPTYPE_UTP | FE_B13_PORT_AUTO; /* * Minimum initialization of the hardware. * We write into registers; hope I/O ports have no * overlap with other boards. */ /* Initialize ASIC. */ outb( sc->ioaddr[ FE_FMV3 ], 0 ); outb( sc->ioaddr[ FE_FMV10 ], 0 ); /* Initialize 86960. */ DELAY( 200 ); outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_DISABLE ); DELAY( 200 ); /* Disable all interrupts. */ outb( sc->ioaddr[ FE_DLCR2 ], 0 ); outb( sc->ioaddr[ FE_DLCR3 ], 0 ); /* "Refresh" hardware configuration. FIXME. */ outb( sc->ioaddr[ FE_FMV2 ], inb( sc->ioaddr[ FE_FMV2 ] ) ); /* Turn the "master interrupt control" flag of ASIC on. */ outb( sc->ioaddr[ FE_FMV3 ], FE_FMV3_IRQENB ); /* * That's all. FMV-180 occupies 32 I/O addresses, by the way. */ return 32; } /* * Probe and initialization for Allied-Telesis AT1700/RE2000 series. */ static int fe_probe_ati ( DEVICE * dev, struct fe_softc * sc ) { int i, n; u_char eeprom [ FE_EEPROM_SIZE ]; u_char save16, save17; static u_short const baseaddr [ 8 ] = { 0x260, 0x280, 0x2A0, 0x240, 0x340, 0x320, 0x380, 0x300 }; static u_short const irqmaps [ 4 ][ 4 ] = { { IRQ3, IRQ4, IRQ5, IRQ9 }, { IRQ10, IRQ11, IRQ12, IRQ15 }, { IRQ3, IRQ11, IRQ5, IRQ15 }, { IRQ10, IRQ11, IRQ14, IRQ15 }, }; static struct fe_simple_probe_struct const probe_table [] = { { FE_DLCR2, 0x70, 0x00 }, { FE_DLCR4, 0x08, 0x00 }, { FE_DLCR5, 0x80, 0x00 }, #if 0 { FE_BMPR16, 0x1B, 0x00 }, { FE_BMPR17, 0x7F, 0x00 }, #endif { 0 } }; /* Assume we have 86965 and no need to restore these. */ save16 = 0; save17 = 0; #if FE_DEBUG >= 3 log( LOG_INFO, "fe%d: probe (0x%x) for ATI\n", sc->sc_unit, sc->iobase ); fe_dump( LOG_INFO, sc, NULL ); #endif /* * See if the specified address is possible for MB86965A JLI mode. */ for ( i = 0; i < 8; i++ ) { if ( baseaddr[ i ] == sc->iobase ) break; } if ( i == 8 ) goto NOTFOUND; /* Setup an I/O address mapping table. */ for ( i = 0; i < MAXREGISTERS; i++ ) { sc->ioaddr[ i ] = sc->iobase + i; } /* * We should test if MB86965A is on the base address now. * Unfortunately, it is very hard to probe it reliably, since * we have no way to reset the chip under software control. * On cold boot, we could check the "signature" bit patterns * described in the Fujitsu document. On warm boot, however, * we can predict almost nothing about register values. */ if ( !fe_simple_probe( sc, probe_table ) ) goto NOTFOUND; /* Check if our I/O address matches config info on 86965. */ n = ( inb( sc->ioaddr[ FE_BMPR19 ] ) & FE_B19_ADDR ) >> FE_B19_ADDR_SHIFT; if ( baseaddr[ n ] != sc->iobase ) goto NOTFOUND; /* * We are now almost sure we have an AT1700 at the given * address. So, read EEPROM through 86965. We have to write * into LSI registers to read from EEPROM. I want to avoid it * at this stage, but I cannot test the presence of the chip * any further without reading EEPROM. FIXME. */ save16 = inb( sc->ioaddr[ FE_BMPR16 ] ); save17 = inb( sc->ioaddr[ FE_BMPR17 ] ); fe_read_eeprom( sc, eeprom ); /* Make sure the EEPROM is turned off. */ outb( sc->ioaddr[ FE_BMPR16 ], 0 ); outb( sc->ioaddr[ FE_BMPR17 ], 0 ); /* Make sure that config info in EEPROM and 86965 agree. */ if ( eeprom[ FE_EEPROM_CONF ] != inb( sc->ioaddr[ FE_BMPR19 ] ) ) { goto NOTFOUND; } /* * The following model identification codes are stolen from * from the NetBSD port of the fe driver. My reviewers * suggested minor revision. */ /* Determine the card type. */ switch (eeprom[FE_ATI_EEP_MODEL]) { case FE_ATI_MODEL_AT1700T: sc->typestr = "AT-1700T/RE2001"; break; case FE_ATI_MODEL_AT1700BT: sc->typestr = "AT-1700BT/RE2003"; break; case FE_ATI_MODEL_AT1700FT: sc->typestr = "AT-1700FT/RE2009"; break; case FE_ATI_MODEL_AT1700AT: sc->typestr = "AT-1700AT/RE2005"; break; default: sc->typestr = "unknown AT-1700/RE2000 ?"; break; } /* * Try to determine IRQ settings. * Different models use different ranges of IRQs. */ if ( dev->id_irq == NO_IRQ ) { n = ( inb( sc->ioaddr[ FE_BMPR19 ] ) & FE_B19_IRQ ) >> FE_B19_IRQ_SHIFT; switch ( eeprom[ FE_ATI_EEP_REVISION ] & 0xf0 ) { case 0x30: dev->id_irq = irqmaps[ 3 ][ n ]; break; case 0x10: case 0x50: dev->id_irq = irqmaps[ 2 ][ n ]; break; case 0x40: case 0x60: if ( eeprom[ FE_ATI_EEP_MAGIC ] & 0x04 ) { dev->id_irq = irqmaps[ 1 ][ n ]; } else { dev->id_irq = irqmaps[ 0 ][ n ]; } break; default: dev->id_irq = irqmaps[ 0 ][ n ]; break; } } /* * Initialize constants in the per-line structure. */ /* Get our station address from EEPROM. */ bcopy( eeprom + FE_ATI_EEP_ADDR, sc->sc_enaddr, ETHER_ADDR_LEN ); #if 1 /* * This test doesn't work well for AT1700 look-alike by * other vendors. */ /* Make sure the vendor part is for Allied-Telesis. */ if ( sc->sc_enaddr[ 0 ] != 0x00 || sc->sc_enaddr[ 1 ] != 0x00 || sc->sc_enaddr[ 2 ] != 0xF4 ) return 0; #else /* Make sure we got a valid station address. */ if ( ( sc->sc_enaddr[ 0 ] & 0x03 ) != 0x00 || ( sc->sc_enaddr[ 0 ] == 0x00 && sc->sc_enaddr[ 1 ] == 0x00 && sc->sc_enaddr[ 2 ] == 0x00 ) ) return 0; #endif /* * Program the 86960 as follows: * SRAM: 32KB, 100ns, byte-wide access. * Transmission buffer: 4KB x 2. * System bus interface: 16 bits. */ sc->proto_dlcr4 = FE_D4_LBC_DISABLE | FE_D4_CNTRL; /* FIXME */ sc->proto_dlcr5 = 0; sc->proto_dlcr6 = FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x4KB | FE_D6_BBW_BYTE | FE_D6_SBW_WORD | FE_D6_SRAM_100ns; sc->proto_dlcr7 = FE_D7_BYTSWP_LH | FE_D7_IDENT_EC; #if 0 /* XXXX Should we use this? FIXME. */ sc->proto_bmpr13 = eeprom[ FE_ATI_EEP_MEDIA ]; #else sc->proto_bmpr13 = FE_B13_TPTYPE_UTP | FE_B13_PORT_AUTO; #endif #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "ATI found" ); #endif /* Initialize 86965. */ DELAY( 200 ); outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_DISABLE ); DELAY( 200 ); /* Disable all interrupts. */ outb( sc->ioaddr[ FE_DLCR2 ], 0 ); outb( sc->ioaddr[ FE_DLCR3 ], 0 ); #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "end of fe_probe_ati()" ); #endif /* * That's all. AT1700 occupies 32 I/O addresses, by the way. */ return 32; NOTFOUND: /* * We have no AT1700 at a given address. * Restore BMPR16 and BMPR17 if we have destroyed them, * hoping that the hardware on the address didn't get * bad side effect. */ if ( save16 != 0 | save17 != 0 ) { outb( sc->ioaddr[ FE_BMPR16 ], save16 ); outb( sc->ioaddr[ FE_BMPR17 ], save17 ); } return ( 0 ); } /* * Probe and initialization for Fujitsu MBH10302 PCMCIA Ethernet interface. */ static int fe_probe_mbh ( DEVICE * dev, struct fe_softc * sc ) { int i; static struct fe_simple_probe_struct probe_table [] = { { FE_DLCR2, 0x70, 0x00 }, { FE_DLCR4, 0x08, 0x00 }, /* { FE_DLCR5, 0x80, 0x00 }, Does not work well. */ #if 0 /* * Test *vendor* part of the address for Fujitsu. * The test will gain reliability of probe process, but * it rejects clones by other vendors, or OEM product * supplied by retailer other than Fujitsu. */ { FE_MBH10, 0xFF, 0x00 }, { FE_MBH11, 0xFF, 0x00 }, { FE_MBH12, 0xFF, 0x0E }, #else /* * We can always verify the *first* 2 bits (in Ethernet * bit order) are "global" and "unicast" even for * unknown vendors. */ { FE_MBH10, 0x03, 0x00 }, #endif /* Just a gap? Seems reliable, anyway. */ { 0x12, 0xFF, 0x00 }, { 0x13, 0xFF, 0x00 }, { 0x14, 0xFF, 0x00 }, { 0x15, 0xFF, 0x00 }, { 0x16, 0xFF, 0x00 }, { 0x17, 0xFF, 0x00 }, #if 0 { 0x18, 0xFF, 0xFF }, { 0x19, 0xFF, 0xFF }, #endif /* 0 */ { 0 } }; /* * We need explicit IRQ and supported address. */ if ( dev->id_irq == NO_IRQ || ( sc->iobase & ~0x3E0 ) != 0 ) { return ( 0 ); } #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "top of probe" ); #endif /* Setup an I/O address mapping table. */ for ( i = 0; i < MAXREGISTERS; i++ ) { sc->ioaddr[ i ] = sc->iobase + i; } /* * See if MBH10302 is on its address. * I'm not sure the following probe code works. FIXME. */ if ( !fe_simple_probe( sc, probe_table ) ) return 0; /* Determine the card type. */ sc->typestr = "MBH10302 (PCMCIA)"; /* * Initialize constants in the per-line structure. */ /* Get our station address from EEPROM. */ inblk( sc, FE_MBH10, sc->sc_enaddr, ETHER_ADDR_LEN ); /* Make sure we got a valid station address. */ if ( ( sc->sc_enaddr[ 0 ] & 0x03 ) != 0x00 || ( sc->sc_enaddr[ 0 ] == 0x00 && sc->sc_enaddr[ 1 ] == 0x00 && sc->sc_enaddr[ 2 ] == 0x00 ) ) return 0; /* * Program the 86960 as follows: * SRAM: 32KB, 100ns, byte-wide access. * Transmission buffer: 4KB x 2. * System bus interface: 16 bits. */ sc->proto_dlcr4 = FE_D4_LBC_DISABLE | FE_D4_CNTRL; sc->proto_dlcr5 = 0; sc->proto_dlcr6 = FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x4KB | FE_D6_BBW_BYTE | FE_D6_SBW_WORD | FE_D6_SRAM_100ns; sc->proto_dlcr7 = FE_D7_BYTSWP_LH | FE_D7_IDENT_NICE; sc->proto_bmpr13 = FE_B13_TPTYPE_UTP | FE_B13_PORT_AUTO; /* Setup hooks. We need a special initialization procedure. */ sc->init = fe_init_mbh; /* * Minimum initialization. */ /* Minimal initialization of 86960. */ DELAY( 200 ); outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_DISABLE ); DELAY( 200 ); /* Disable all interrupts. */ outb( sc->ioaddr[ FE_DLCR2 ], 0 ); outb( sc->ioaddr[ FE_DLCR3 ], 0 ); #if 1 /* FIXME. */ /* Initialize system bus interface and encoder/decoder operation. */ outb( sc->ioaddr[ FE_MBH0 ], FE_MBH0_MAGIC | FE_MBH0_INTR_DISABLE ); #endif /* * That's all. MBH10302 occupies 32 I/O addresses, by the way. */ return 32; } /* MBH specific initialization routine. */ static void fe_init_mbh ( struct fe_softc * sc ) { /* Minimal initialization of 86960. */ DELAY( 200 ); outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_DISABLE ); DELAY( 200 ); /* Disable all interrupts. */ outb( sc->ioaddr[ FE_DLCR2 ], 0 ); outb( sc->ioaddr[ FE_DLCR3 ], 0 ); /* Enable master interrupt flag. */ outb( sc->ioaddr[ FE_MBH0 ], FE_MBH0_MAGIC | FE_MBH0_INTR_ENABLE ); } #endif /* PC98 */ /* * Install interface into kernel networking data structures */ static int fe_attach ( DEVICE * dev ) { #if NCRD > 0 static int already_ifattach[NFE]; #endif struct fe_softc *sc = &fe_softc[dev->id_unit]; /* * Initialize ifnet structure */ sc->sc_if.if_softc = sc; sc->sc_if.if_unit = sc->sc_unit; sc->sc_if.if_name = "fe"; sc->sc_if.if_output = ether_output; sc->sc_if.if_start = fe_start; sc->sc_if.if_ioctl = fe_ioctl; sc->sc_if.if_watchdog = fe_watchdog; /* * Set default interface flags. */ sc->sc_if.if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; /* * Set maximum size of output queue, if it has not been set. * It is done here as this driver may be started after the * system initialization (i.e., the interface is PCMCIA.) * * I'm not sure this is really necessary, but, even if it is, * it should be done somewhere else, e.g., in if_attach(), * since it must be a common workaround for all network drivers. * FIXME. */ if ( sc->sc_if.if_snd.ifq_maxlen == 0 ) { sc->sc_if.if_snd.ifq_maxlen = ifqmaxlen; } #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "attach()" ); #endif #if FE_SINGLE_TRANSMISSION /* Override txb config to allocate minimum. */ sc->proto_dlcr6 &= ~FE_D6_TXBSIZ sc->proto_dlcr6 |= FE_D6_TXBSIZ_2x2KB; #endif /* Modify hardware config if it is requested. */ if ( dev->id_flags & FE_FLAGS_OVERRIDE_DLCR6 ) { sc->proto_dlcr6 = dev->id_flags & FE_FLAGS_DLCR6_VALUE; } /* Find TX buffer size, based on the hardware dependent proto. */ switch ( sc->proto_dlcr6 & FE_D6_TXBSIZ ) { case FE_D6_TXBSIZ_2x2KB: sc->txb_size = 2048; break; case FE_D6_TXBSIZ_2x4KB: sc->txb_size = 4096; break; case FE_D6_TXBSIZ_2x8KB: sc->txb_size = 8192; break; default: /* Oops, we can't work with single buffer configuration. */ #if FE_DEBUG >= 2 log( LOG_WARNING, "fe%d: strange TXBSIZ config; fixing\n", sc->sc_unit ); #endif sc->proto_dlcr6 &= ~FE_D6_TXBSIZ; sc->proto_dlcr6 |= FE_D6_TXBSIZ_2x2KB; sc->txb_size = 2048; break; } /* Attach and stop the interface. */ #if NCRD > 0 if (already_ifattach[dev->id_unit] != 1) { if_attach(&sc->sc_if); already_ifattach[dev->id_unit] = 1; } #else if_attach(&sc->sc_if); #endif /* NCRD > 0 */ fe_stop(sc->sc_unit); /* This changes the state to IDLE. */ ether_ifattach(&sc->sc_if); /* Print additional info when attached. */ printf( "fe%d: address %6D, type %s\n", sc->sc_unit, sc->sc_enaddr, ":" , sc->typestr ); #if FE_DEBUG >= 3 { int buf, txb, bbw, sbw, ram; buf = txb = bbw = sbw = ram = -1; switch ( sc->proto_dlcr6 & FE_D6_BUFSIZ ) { case FE_D6_BUFSIZ_8KB: buf = 8; break; case FE_D6_BUFSIZ_16KB: buf = 16; break; case FE_D6_BUFSIZ_32KB: buf = 32; break; case FE_D6_BUFSIZ_64KB: buf = 64; break; } switch ( sc->proto_dlcr6 & FE_D6_TXBSIZ ) { case FE_D6_TXBSIZ_2x2KB: txb = 2; break; case FE_D6_TXBSIZ_2x4KB: txb = 4; break; case FE_D6_TXBSIZ_2x8KB: txb = 8; break; } switch ( sc->proto_dlcr6 & FE_D6_BBW ) { case FE_D6_BBW_BYTE: bbw = 8; break; case FE_D6_BBW_WORD: bbw = 16; break; } switch ( sc->proto_dlcr6 & FE_D6_SBW ) { case FE_D6_SBW_BYTE: sbw = 8; break; case FE_D6_SBW_WORD: sbw = 16; break; } switch ( sc->proto_dlcr6 & FE_D6_SRAM ) { case FE_D6_SRAM_100ns: ram = 100; break; case FE_D6_SRAM_150ns: ram = 150; break; } printf( "fe%d: SRAM %dKB %dbit %dns, TXB %dKBx2, %dbit I/O\n", sc->sc_unit, buf, bbw, ram, txb, sbw ); } #endif #if NBPFILTER > 0 /* If BPF is in the kernel, call the attach for it. */ bpfattach( &sc->sc_if, DLT_EN10MB, sizeof(struct ether_header)); #endif return 1; } /* * Reset interface. */ static void fe_reset ( int unit ) { /* * Stop interface and re-initialize. */ fe_stop(unit); fe_init(unit); } /* * Stop everything on the interface. * * All buffered packets, both transmitting and receiving, * if any, will be lost by stopping the interface. */ static void fe_stop ( int unit ) { struct fe_softc *sc = &fe_softc[unit]; int s; s = splimp(); #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "stop()" ); #endif /* Disable interrupts. */ outb( sc->ioaddr[ FE_DLCR2 ], 0x00 ); outb( sc->ioaddr[ FE_DLCR3 ], 0x00 ); /* Stop interface hardware. */ DELAY( 200 ); outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_DISABLE ); DELAY( 200 ); /* Clear all interrupt status. */ outb( sc->ioaddr[ FE_DLCR0 ], 0xFF ); outb( sc->ioaddr[ FE_DLCR1 ], 0xFF ); /* Put the chip in stand-by mode. */ DELAY( 200 ); outb( sc->ioaddr[ FE_DLCR7 ], sc->proto_dlcr7 | FE_D7_POWER_DOWN ); DELAY( 200 ); /* Reset transmitter variables and interface flags. */ sc->sc_if.if_flags &= ~( IFF_OACTIVE | IFF_RUNNING ); sc->sc_if.if_timer = 0; sc->txb_free = sc->txb_size; sc->txb_count = 0; sc->txb_sched = 0; /* MAR loading can be delayed. */ sc->filter_change = 0; /* Update config status also. */ /* Call a hook. */ if ( sc->stop ) sc->stop( sc ); #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "end of stop()" ); #endif (void) splx(s); } /* * Device timeout/watchdog routine. Entered if the device neglects to * generate an interrupt after a transmit has been started on it. */ static void fe_watchdog ( struct ifnet *ifp ) { struct fe_softc *sc = (struct fe_softc *)ifp; #if FE_DEBUG >= 1 /* A "debug" message. */ log( LOG_ERR, "fe%d: transmission timeout (%d+%d)%s\n", ifp->if_unit, sc->txb_sched, sc->txb_count, ( ifp->if_flags & IFF_UP ) ? "" : " when down" ); #endif /* Suggest users a possible cause. */ if ( ifp->if_oerrors > 0 ) { log( LOG_WARNING, "fe%d: wrong IRQ setting in config?", ifp->if_unit ); } #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, NULL ); #endif /* Record how many packets are lost by this accident. */ ifp->if_oerrors += sc->txb_sched + sc->txb_count; /* Put the interface into known initial state. */ if ( ifp->if_flags & IFF_UP ) { fe_reset( ifp->if_unit ); } else { fe_stop( ifp->if_unit ); } } /* * Initialize device. */ static void fe_init ( int unit ) { struct fe_softc *sc = &fe_softc[unit]; int i, s; #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "init()" ); #endif /* We need an address. */ if (sc->sc_if.if_addrlist == 0) { #if FE_DEBUG >= 1 log( LOG_ERR, "fe%d: init() without any address\n", sc->sc_unit ); #endif return; } #if FE_DEBUG >= 1 /* * Make sure we have a valid station address. * The following test is applicable for any Ethernet interfaces. * It can be done in somewhere common to all of them. FIXME. */ if ( ( sc->sc_enaddr[ 0 ] & 0x01 ) != 0 || ( sc->sc_enaddr[ 0 ] == 0x00 && sc->sc_enaddr[ 1 ] == 0x00 && sc->sc_enaddr[ 2 ] == 0x00 ) ) { log( LOG_ERR, "fe%d: invalid station address (%6D)\n", sc->sc_unit, sc->sc_enaddr, ":" ); return; } #endif /* Start initializing 86960. */ s = splimp(); /* Call a hook. */ if ( sc->init ) sc->init( sc ); #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "after init hook" ); #endif /* * Make sure to disable the chip, also. * This may also help re-programming the chip after * hot insertion of PCMCIAs. */ DELAY( 200 ); outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_DISABLE ); DELAY( 200 ); /* Power up the chip and select register bank for DLCRs. */ DELAY( 200 ); outb( sc->ioaddr[ FE_DLCR7 ], sc->proto_dlcr7 | FE_D7_RBS_DLCR | FE_D7_POWER_UP ); DELAY( 200 ); /* Feed the station address. */ outblk( sc, FE_DLCR8, sc->sc_enaddr, ETHER_ADDR_LEN ); /* Clear multicast address filter to receive nothing. */ outb( sc->ioaddr[ FE_DLCR7 ], sc->proto_dlcr7 | FE_D7_RBS_MAR | FE_D7_POWER_UP ); outblk( sc, FE_MAR8, fe_filter_nothing.data, FE_FILTER_LEN ); /* Select the BMPR bank for runtime register access. */ outb( sc->ioaddr[ FE_DLCR7 ], sc->proto_dlcr7 | FE_D7_RBS_BMPR | FE_D7_POWER_UP ); /* Initialize registers. */ outb( sc->ioaddr[ FE_DLCR0 ], 0xFF ); /* Clear all bits. */ outb( sc->ioaddr[ FE_DLCR1 ], 0xFF ); /* ditto. */ outb( sc->ioaddr[ FE_DLCR2 ], 0x00 ); outb( sc->ioaddr[ FE_DLCR3 ], 0x00 ); outb( sc->ioaddr[ FE_DLCR4 ], sc->proto_dlcr4 ); outb( sc->ioaddr[ FE_DLCR5 ], sc->proto_dlcr5 ); outb( sc->ioaddr[ FE_BMPR10 ], 0x00 ); outb( sc->ioaddr[ FE_BMPR11 ], FE_B11_CTRL_SKIP | FE_B11_MODE1 ); outb( sc->ioaddr[ FE_BMPR12 ], 0x00 ); outb( sc->ioaddr[ FE_BMPR13 ], sc->proto_bmpr13 ); outb( sc->ioaddr[ FE_BMPR14 ], 0x00 ); outb( sc->ioaddr[ FE_BMPR15 ], 0x00 ); #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "just before enabling DLC" ); #endif /* Enable interrupts. */ outb( sc->ioaddr[ FE_DLCR2 ], FE_TMASK ); outb( sc->ioaddr[ FE_DLCR3 ], FE_RMASK ); /* Enable transmitter and receiver. */ DELAY( 200 ); outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_ENABLE ); DELAY( 200 ); #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "just after enabling DLC" ); #endif /* * Make sure to empty the receive buffer. * * This may be redundant, but *if* the receive buffer were full * at this point, the driver would hang. I have experienced * some strange hang-up just after UP. I hope the following * code solve the problem. * * I have changed the order of hardware initialization. * I think the receive buffer cannot have any packets at this * point in this version. The following code *must* be * redundant now. FIXME. */ for ( i = 0; i < FE_MAX_RECV_COUNT; i++ ) { if ( inb( sc->ioaddr[ FE_DLCR5 ] ) & FE_D5_BUFEMP ) break; outb( sc->ioaddr[ FE_BMPR14 ], FE_B14_SKIP ); } #if FE_DEBUG >= 1 if ( i >= FE_MAX_RECV_COUNT ) { log( LOG_ERR, "fe%d: cannot empty receive buffer\n", sc->sc_unit ); } #endif #if FE_DEBUG >= 3 if ( i < FE_MAX_RECV_COUNT ) { log( LOG_INFO, "fe%d: receive buffer emptied (%d)\n", sc->sc_unit, i ); } #endif #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "after ERB loop" ); #endif /* Do we need this here? FIXME. */ outb( sc->ioaddr[ FE_DLCR0 ], 0xFF ); /* Clear all bits. */ outb( sc->ioaddr[ FE_DLCR1 ], 0xFF ); /* ditto. */ #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "after FIXME" ); #endif /* Set 'running' flag, because we are now running. */ sc->sc_if.if_flags |= IFF_RUNNING; /* * At this point, the interface is running properly, * except that it receives *no* packets. we then call * fe_setmode() to tell the chip what packets to be * received, based on the if_flags and multicast group * list. It completes the initialization process. */ fe_setmode( sc ); #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "after setmode" ); #endif /* ...and attempt to start output queued packets. */ fe_start( &sc->sc_if ); #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "init() done" ); #endif (void) splx(s); } /* * This routine actually starts the transmission on the interface */ static void fe_xmit ( struct fe_softc * sc ) { /* * Set a timer just in case we never hear from the board again. * We use longer timeout for multiple packet transmission. * I'm not sure this timer value is appropriate. FIXME. */ sc->sc_if.if_timer = 1 + sc->txb_count; /* Update txb variables. */ sc->txb_sched = sc->txb_count; sc->txb_count = 0; sc->txb_free = sc->txb_size; /* Start transmitter, passing packets in TX buffer. */ outb( sc->ioaddr[ FE_BMPR10 ], sc->txb_sched | FE_B10_START ); } /* * Start output on interface. * We make two assumptions here: * 1) that the current priority is set to splimp _before_ this code * is called *and* is returned to the appropriate priority after * return * 2) that the IFF_OACTIVE flag is checked before this code is called * (i.e. that the output part of the interface is idle) */ void fe_start ( struct ifnet *ifp ) { struct fe_softc *sc = ifp->if_softc; struct mbuf *m; #if FE_DEBUG >= 1 /* Just a sanity check. */ if ( ( sc->txb_count == 0 ) != ( sc->txb_free == sc->txb_size ) ) { /* * Txb_count and txb_free co-works to manage the * transmission buffer. Txb_count keeps track of the * used potion of the buffer, while txb_free does unused * potion. So, as long as the driver runs properly, * txb_count is zero if and only if txb_free is same * as txb_size (which represents whole buffer.) */ log( LOG_ERR, "fe%d: inconsistent txb variables (%d, %d)\n", sc->sc_unit, sc->txb_count, sc->txb_free ); /* * So, what should I do, then? * * We now know txb_count and txb_free contradicts. We * cannot, however, tell which is wrong. More * over, we cannot peek 86960 transmission buffer or * reset the transmission buffer. (In fact, we can * reset the entire interface. I don't want to do it.) * * If txb_count is incorrect, leaving it as-is will cause * sending of garbage after next interrupt. We have to * avoid it. Hence, we reset the txb_count here. If * txb_free was incorrect, resetting txb_count just loose * some packets. We can live with it. */ sc->txb_count = 0; } #endif #if FE_DEBUG >= 1 /* * First, see if there are buffered packets and an idle * transmitter - should never happen at this point. */ if ( ( sc->txb_count > 0 ) && ( sc->txb_sched == 0 ) ) { log( LOG_ERR, "fe%d: transmitter idle with %d buffered packets\n", sc->sc_unit, sc->txb_count ); fe_xmit( sc ); } #endif /* * Stop accepting more transmission packets temporarily, when * a filter change request is delayed. Updating the MARs on * 86960 flushes the transmission buffer, so it is delayed * until all buffered transmission packets have been sent * out. */ if ( sc->filter_change ) { /* * Filter change request is delayed only when the DLC is * working. DLC soon raise an interrupt after finishing * the work. */ goto indicate_active; } for (;;) { /* * See if there is room to put another packet in the buffer. * We *could* do better job by peeking the send queue to * know the length of the next packet. Current version just * tests against the worst case (i.e., longest packet). FIXME. * * When adding the packet-peek feature, don't forget adding a * test on txb_count against QUEUEING_MAX. * There is a little chance the packet count exceeds * the limit. Assume transmission buffer is 8KB (2x8KB * configuration) and an application sends a bunch of small * (i.e., minimum packet sized) packets rapidly. An 8KB * buffer can hold 130 blocks of 62 bytes long... */ if ( sc->txb_free < ETHER_MAX_LEN - ETHER_CRC_LEN + FE_DATA_LEN_LEN ) { /* No room. */ goto indicate_active; } #if FE_SINGLE_TRANSMISSION if ( sc->txb_count > 0 ) { /* Just one packet per a transmission buffer. */ goto indicate_active; } #endif /* * Get the next mbuf chain for a packet to send. */ IF_DEQUEUE( &sc->sc_if.if_snd, m ); if ( m == NULL ) { /* No more packets to send. */ goto indicate_inactive; } /* * Copy the mbuf chain into the transmission buffer. * txb_* variables are updated as necessary. */ fe_write_mbufs( sc, m ); /* Start transmitter if it's idle. */ if ( sc->txb_sched == 0 ) fe_xmit( sc ); /* * Tap off here if there is a bpf listener, * and the device is *not* in promiscuous mode. * (86960 receives self-generated packets if * and only if it is in "receive everything" * mode.) */ #if NBPFILTER > 0 if ( sc->sc_if.if_bpf && !( sc->sc_if.if_flags & IFF_PROMISC ) ) { bpf_mtap( &sc->sc_if, m ); } #endif m_freem( m ); } indicate_inactive: /* * We are using the !OACTIVE flag to indicate to * the outside world that we can accept an * additional packet rather than that the * transmitter is _actually_ active. Indeed, the * transmitter may be active, but if we haven't * filled all the buffers with data then we still * want to accept more. */ sc->sc_if.if_flags &= ~IFF_OACTIVE; return; indicate_active: /* * The transmitter is active, and there are no room for * more outgoing packets in the transmission buffer. */ sc->sc_if.if_flags |= IFF_OACTIVE; return; } /* * Drop (skip) a packet from receive buffer in 86960 memory. */ static void fe_droppacket ( struct fe_softc * sc ) { outb( sc->ioaddr[ FE_BMPR14 ], FE_B14_SKIP ); } /* * Transmission interrupt handler * The control flow of this function looks silly. FIXME. */ static void fe_tint ( struct fe_softc * sc, u_char tstat ) { int left; int col; /* * Handle "excessive collision" interrupt. */ if ( tstat & FE_D0_COLL16 ) { /* * Find how many packets (including this collided one) * are left unsent in transmission buffer. */ left = inb( sc->ioaddr[ FE_BMPR10 ] ); #if FE_DEBUG >= 2 log( LOG_WARNING, "fe%d: excessive collision (%d/%d)\n", sc->sc_unit, left, sc->txb_sched ); #endif #if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, NULL ); #endif /* * Update statistics. */ sc->sc_if.if_collisions += 16; sc->sc_if.if_oerrors++; sc->sc_if.if_opackets += sc->txb_sched - left; /* * Collision statistics has been updated. * Clear the collision flag on 86960 now to avoid confusion. */ outb( sc->ioaddr[ FE_DLCR0 ], FE_D0_COLLID ); /* * Restart transmitter, skipping the * collided packet. * * We *must* skip the packet to keep network running * properly. Excessive collision error is an * indication of the network overload. If we * tried sending the same packet after excessive * collision, the network would be filled with * out-of-time packets. Packets belonging * to reliable transport (such as TCP) are resent * by some upper layer. */ outb( sc->ioaddr[ FE_BMPR11 ], FE_B11_CTRL_SKIP | FE_B11_MODE1 ); sc->txb_sched = left - 1; } /* * Handle "transmission complete" interrupt. */ if ( tstat & FE_D0_TXDONE ) { /* * Add in total number of collisions on last * transmission. We also clear "collision occurred" flag * here. * * 86960 has a design flaw on collision count on multiple * packet transmission. When we send two or more packets * with one start command (that's what we do when the * transmission queue is crowded), 86960 informs us number * of collisions occurred on the last packet on the * transmission only. Number of collisions on previous * packets are lost. I have told that the fact is clearly * stated in the Fujitsu document. * * I considered not to mind it seriously. Collision * count is not so important, anyway. Any comments? FIXME. */ if ( inb( sc->ioaddr[ FE_DLCR0 ] ) & FE_D0_COLLID ) { /* Clear collision flag. */ outb( sc->ioaddr[ FE_DLCR0 ], FE_D0_COLLID ); /* Extract collision count from 86960. */ col = inb( sc->ioaddr[ FE_DLCR4 ] ); col = ( col & FE_D4_COL ) >> FE_D4_COL_SHIFT; if ( col == 0 ) { /* * Status register indicates collisions, * while the collision count is zero. * This can happen after multiple packet * transmission, indicating that one or more * previous packet(s) had been collided. * * Since the accurate number of collisions * has been lost, we just guess it as 1; * Am I too optimistic? FIXME. */ col = 1; } sc->sc_if.if_collisions += col; #if FE_DEBUG >= 3 log( LOG_WARNING, "fe%d: %d collision(s) (%d)\n", sc->sc_unit, col, sc->txb_sched ); #endif } /* * Update total number of successfully * transmitted packets. */ sc->sc_if.if_opackets += sc->txb_sched; sc->txb_sched = 0; /* * The transmitter is no more active. * Reset output active flag and watchdog timer. */ sc->sc_if.if_flags &= ~IFF_OACTIVE; sc->sc_if.if_timer = 0; /* * If more data is ready to transmit in the buffer, start * transmitting them. Otherwise keep transmitter idle, * even if more data is queued. This gives receive * process a slight priority. */ if ( sc->txb_count > 0 ) fe_xmit( sc ); } } /* * Ethernet interface receiver interrupt. */ static void fe_rint ( struct fe_softc * sc, u_char rstat ) { u_short len; u_char status; int i; /* * Update statistics if this interrupt is caused by an error. */ if ( rstat & ( FE_D1_OVRFLO | FE_D1_CRCERR | FE_D1_ALGERR | FE_D1_SRTPKT ) ) { #if FE_DEBUG >= 3 log( LOG_WARNING, "fe%d: receive error: %s%s%s%s(%02x)\n", sc->sc_unit, rstat & FE_D1_OVRFLO ? "OVR " : "", rstat & FE_D1_CRCERR ? "CRC " : "", rstat & FE_D1_ALGERR ? "ALG " : "", rstat & FE_D1_SRTPKT ? "LEN " : "", rstat ); #endif sc->sc_if.if_ierrors++; } /* * MB86960 has a flag indicating "receive queue empty." * We just loop, checking the flag, to pull out all received * packets. * * We limit the number of iterations to avoid infinite-loop. * It can be caused by a very slow CPU (some broken * peripheral may insert incredible number of wait cycles) * or, worse, by a broken MB86960 chip. */ for ( i = 0; i < FE_MAX_RECV_COUNT; i++ ) { /* Stop the iteration if 86960 indicates no packets. */ if ( inb( sc->ioaddr[ FE_DLCR5 ] ) & FE_D5_BUFEMP ) break; /* * Extract A receive status byte. * As our 86960 is in 16 bit bus access mode, we have to * use inw() to get the status byte. The significant * value is returned in lower 8 bits. */ status = ( u_char )inw( sc->ioaddr[ FE_BMPR8 ] ); #if FE_DEBUG >= 4 log( LOG_INFO, "fe%d: receive status = %04x\n", sc->sc_unit, status ); #endif /* * If there was an error, update statistics and drop * the packet, unless the interface is in promiscuous * mode. */ if ( ( status & 0xF0 ) != 0x20 ) { if ( !( sc->sc_if.if_flags & IFF_PROMISC ) ) { sc->sc_if.if_ierrors++; fe_droppacket(sc); continue; } } /* * Extract the packet length. * It is a sum of a header (14 bytes) and a payload. * CRC has been stripped off by the 86960. */ len = inw( sc->ioaddr[ FE_BMPR8 ] ); /* * MB86965 checks the packet length and drop big packet * before passing it to us. There are no chance we can * get big packets through it, even if they are actually * sent over a line. Hence, if the length exceeds * the specified limit, it means some serious failure, * such as out-of-sync on receive buffer management. * * Is this statement true? FIXME. */ if ( len > ETHER_MAX_LEN - ETHER_CRC_LEN || len < ETHER_MIN_LEN- ETHER_CRC_LEN ) { #if FE_DEBUG >= 2 log( LOG_WARNING, "fe%d: received a %s packet? (%u bytes)\n", sc->sc_unit, len < ETHER_MIN_SIZE- ETHER_CRC_SIZE ? "partial" : "big", len ); #endif sc->sc_if.if_ierrors++; fe_droppacket( sc ); continue; } /* * Check for a short (RUNT) packet. We *do* check * but do nothing other than print a message. * Short packets are illegal, but does nothing bad * if it carries data for upper layer. */ #if FE_DEBUG >= 2 if ( len < ETHER_MIN_LEN - ETHER_CRC_LEN) { log( LOG_WARNING, "fe%d: received a short packet? (%u bytes)\n", sc->sc_unit, len ); } #endif /* * Go get a packet. */ if ( fe_get_packet( sc, len ) < 0 ) { /* Skip a packet, updating statistics. */ #if FE_DEBUG >= 2 log( LOG_WARNING, "%s%d: out of mbuf;" " dropping a packet (%u bytes)\n", sc->sc_unit, len ); #endif sc->sc_if.if_ierrors++; fe_droppacket( sc ); /* * We stop receiving packets, even if there are * more in the buffer. We hope we can get more * mbuf next time. */ return; } /* Successfully received a packet. Update stat. */ sc->sc_if.if_ipackets++; } } /* * Ethernet interface interrupt processor */ void feintr ( int unit ) { struct fe_softc *sc = &fe_softc[unit]; u_char tstat, rstat; /* * Loop until there are no more new interrupt conditions. */ for (;;) { #if FE_DEBUG >= 4 fe_dump( LOG_INFO, sc, "intr()" ); #endif /* * Get interrupt conditions, masking unneeded flags. */ tstat = inb( sc->ioaddr[ FE_DLCR0 ] ) & FE_TMASK; rstat = inb( sc->ioaddr[ FE_DLCR1 ] ) & FE_RMASK; if ( tstat == 0 && rstat == 0 ) break; /* * Reset the conditions we are acknowledging. */ outb( sc->ioaddr[ FE_DLCR0 ], tstat ); outb( sc->ioaddr[ FE_DLCR1 ], rstat ); /* * Handle transmitter interrupts. Handle these first because * the receiver will reset the board under some conditions. */ if ( tstat ) { fe_tint( sc, tstat ); } /* * Handle receiver interrupts */ if ( rstat ) { fe_rint( sc, rstat ); } /* * Update the multicast address filter if it is * needed and possible. We do it now, because * we can make sure the transmission buffer is empty, * and there is a good chance that the receive queue * is empty. It will minimize the possibility of * packet loss. */ if ( sc->filter_change && sc->txb_count == 0 && sc->txb_sched == 0 ) { fe_loadmar(sc); sc->sc_if.if_flags &= ~IFF_OACTIVE; } /* * If it looks like the transmitter can take more data, * attempt to start output on the interface. This is done * after handling the receiver interrupt to give the * receive operation priority. * * BTW, I'm not sure in what case the OACTIVE is on at * this point. Is the following test redundant? * * No. This routine polls for both transmitter and * receiver interrupts. 86960 can raise a receiver * interrupt when the transmission buffer is full. */ if ( ( sc->sc_if.if_flags & IFF_OACTIVE ) == 0 ) { fe_start( &sc->sc_if ); } } } /* * Process an ioctl request. This code needs some work - it looks * pretty ugly. */ static int fe_ioctl ( struct ifnet * ifp, int command, caddr_t data ) { struct fe_softc *sc = ifp->if_softc; int s, error = 0; #if FE_DEBUG >= 3 log( LOG_INFO, "fe%d: ioctl(%x)\n", sc->sc_unit, command ); #endif s = splimp(); switch (command) { case SIOCSIFADDR: { struct ifaddr * ifa = ( struct ifaddr * )data; sc->sc_if.if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: fe_init( sc->sc_unit ); /* before arp_ifinit */ arp_ifinit( &sc->arpcom, ifa ); break; #endif #ifdef IPX /* * XXX - This code is probably wrong */ case AF_IPX: { register struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); if (ipx_nullhost(*ina)) ina->x_host = *(union ipx_host *) (sc->sc_enaddr); else { bcopy((caddr_t) ina->x_host.c_host, (caddr_t) sc->sc_enaddr, sizeof(sc->sc_enaddr)); } /* * Set new address */ fe_init(sc->sc_unit); break; } #endif #ifdef INET6 case AF_INET6: /* IPV6 added by shin 96.2.6 */ fe_init(sc->sc_unit); ndp6_ifinit(&sc->arpcom, ifa); break; #endif #ifdef NS /* * XXX - This code is probably wrong */ case AF_NS: { register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); if (ns_nullhost(*ina)) ina->x_host = *(union ns_host *) (sc->sc_enaddr); else { bcopy((caddr_t) ina->x_host.c_host, (caddr_t) sc->sc_enaddr, sizeof(sc->sc_enaddr)); } /* * Set new address */ fe_init(sc->sc_unit); break; } #endif default: fe_init( sc->sc_unit ); break; } break; } #ifdef SIOCGIFADDR case SIOCGIFADDR: { struct ifreq * ifr = ( struct ifreq * )data; struct sockaddr * sa = ( struct sockaddr * )&ifr->ifr_data; bcopy((caddr_t)sc->sc_enaddr, (caddr_t)sa->sa_data, ETHER_ADDR_LEN); break; } #endif #ifdef SIOCGIFPHYSADDR case SIOCGIFPHYSADDR: { struct ifreq * ifr = ( struct ifreq * )data; bcopy((caddr_t)sc->sc_enaddr, (caddr_t)&ifr->ifr_data, ETHER_ADDR_LEN); break; } #endif #ifdef notdef #ifdef SIOCSIFPHYSADDR case SIOCSIFPHYSADDR: { /* * Set the physical (Ethernet) address of the interface. * When and by whom is this command used? FIXME. */ struct ifreq * ifr = ( struct ifreq * )data; bcopy((caddr_t)&ifr->ifr_data, (caddr_t)sc->sc_enaddr, ETHER_ADDR_LEN); fe_setlinkaddr( sc ); break; } #endif #endif /* notdef */ #ifdef SIOCSIFFLAGS case SIOCSIFFLAGS: { /* * Switch interface state between "running" and * "stopped", reflecting the UP flag. */ if ( sc->sc_if.if_flags & IFF_UP ) { if ( ( sc->sc_if.if_flags & IFF_RUNNING ) == 0 ) { fe_init( sc->sc_unit ); } } else { if ( ( sc->sc_if.if_flags & IFF_RUNNING ) != 0 ) { fe_stop( sc->sc_unit ); } } /* * Promiscuous and/or multicast flags may have changed, * so reprogram the multicast filter and/or receive mode. */ fe_setmode( sc ); #if FE_DEBUG >= 1 /* "ifconfig fe0 debug" to print register dump. */ if ( sc->sc_if.if_flags & IFF_DEBUG ) { fe_dump( LOG_DEBUG, sc, "SIOCSIFFLAGS(DEBUG)" ); } #endif break; } #endif #ifdef SIOCADDMULTI case SIOCADDMULTI: case SIOCDELMULTI: { /* * Update out multicast list. */ struct ifreq * ifr = ( struct ifreq * )data; error = ( command == SIOCADDMULTI ) ? ether_addmulti( ifr, &sc->arpcom ) : ether_delmulti( ifr, &sc->arpcom ); if ( error == ENETRESET ) { /* * Multicast list has changed; set the hardware filter * accordingly. */ fe_setmode( sc ); error = 0; } break; } #endif #ifdef SIOCSIFMTU case SIOCSIFMTU: { /* * Set the interface MTU. */ struct ifreq * ifr = ( struct ifreq * )data; if ( ifr->ifr_mtu > ETHERMTU ) { error = EINVAL; } else { sc->sc_if.if_mtu = ifr->ifr_mtu; } break; } #endif default: error = EINVAL; } (void) splx(s); return (error); } /* * Retrieve packet from receive buffer and send to the next level up via * ether_input(). If there is a BPF listener, give a copy to BPF, too. * Returns 0 if success, -1 if error (i.e., mbuf allocation failure). */ static int fe_get_packet ( struct fe_softc * sc, u_short len ) { struct ether_header *eh; struct mbuf *m; /* * NFS wants the data be aligned to the word (4 byte) * boundary. Ethernet header has 14 bytes. There is a * 2-byte gap. */ #define NFS_MAGIC_OFFSET 2 /* * This function assumes that an Ethernet packet fits in an * mbuf (with a cluster attached when necessary.) On FreeBSD * 2.0 for x86, which is the primary target of this driver, an * mbuf cluster has 4096 bytes, and we are happy. On ancient * BSDs, such as vanilla 4.3 for 386, a cluster size was 1024, * however. If the following #error message were printed upon * compile, you need to rewrite this function. */ #if ( MCLBYTES < ETHER_MAX_LEN - ETHER_CRC_LEN + NFS_MAGIC_OFFSET ) #error "Too small MCLBYTES to use fe driver." #endif /* * Our strategy has one more problem. There is a policy on * mbuf cluster allocation. It says that we must have at * least MINCLSIZE (208 bytes on FreeBSD 2.0 for x86) to * allocate a cluster. For a packet of a size between * (MHLEN - 2) to (MINCLSIZE - 2), our code violates the rule... * On the other hand, the current code is short, simple, * and fast, however. It does no harmful thing, just waists * some memory. Any comments? FIXME. */ /* Allocate an mbuf with packet header info. */ MGETHDR(m, M_DONTWAIT, MT_DATA); if ( m == NULL ) return -1; /* Attach a cluster if this packet doesn't fit in a normal mbuf. */ if ( len > MHLEN - NFS_MAGIC_OFFSET ) { MCLGET( m, M_DONTWAIT ); if ( !( m->m_flags & M_EXT ) ) { m_freem( m ); return -1; } } /* Initialize packet header info. */ m->m_pkthdr.rcvif = &sc->sc_if; m->m_pkthdr.len = len; /* Set the length of this packet. */ m->m_len = len; /* The following silliness is to make NFS happy */ m->m_data += NFS_MAGIC_OFFSET; /* Get a packet. */ insw( sc->ioaddr[ FE_BMPR8 ], m->m_data, ( len + 1 ) >> 1 ); /* Get (actually just point to) the header part. */ eh = mtod( m, struct ether_header *); #define ETHER_ADDR_IS_MULTICAST(A) (*(char *)(A) & 1) #if NBPFILTER > 0 /* * Check if there's a BPF listener on this interface. * If it is, hand off the raw packet to bpf. */ if ( sc->sc_if.if_bpf ) { bpf_mtap( &sc->sc_if, m ); } #endif /* * Make sure this packet is (or may be) directed to us. * That is, the packet is either unicasted to our address, * or broad/multi-casted. If any other packets are * received, it is an indication of an error -- probably * 86960 is in a wrong operation mode. * Promiscuous mode is an exception. Under the mode, all * packets on the media must be received. (We must have * programmed the 86960 so.) */ if ( ( sc->sc_if.if_flags & IFF_PROMISC ) && !ETHER_ADDR_IS_MULTICAST( eh->ether_dhost ) && bcmp( eh->ether_dhost, sc->sc_enaddr, ETHER_ADDR_LEN ) != 0 ) { /* * The packet was not for us. This is normal since * we are now in promiscuous mode. Just drop the packet. */ m_freem( m ); return 0; } #if FE_DEBUG >= 3 if ( !ETHER_ADDR_IS_MULTICAST( eh->ether_dhost ) && bcmp( eh->ether_dhost, sc->sc_enaddr, ETHER_ADDR_LEN ) != 0 ) { /* * This packet was not for us. We can't be in promiscuous * mode since the case was handled by above test. * We found an error (of this driver.) */ log( LOG_WARNING, "fe%d: got an unwanted packet, dst = %6D\n", sc->sc_unit, eh->ether_dhost , ":" ); m_freem( m ); return 0; } #endif /* Strip off the Ethernet header. */ m->m_pkthdr.len -= sizeof ( struct ether_header ); m->m_len -= sizeof ( struct ether_header ); m->m_data += sizeof ( struct ether_header ); /* Feed the packet to upper layer. */ ether_input( &sc->sc_if, eh, m ); return 0; } /* * Write an mbuf chain to the transmission buffer memory using 16 bit PIO. * Returns number of bytes actually written, including length word. * * If an mbuf chain is too long for an Ethernet frame, it is not sent. * Packets shorter than Ethernet minimum are legal, and we pad them * before sending out. An exception is "partial" packets which are * shorter than mandatory Ethernet header. * * I wrote a code for an experimental "delayed padding" technique. * When employed, it postpones the padding process for short packets. * If xmit() occurred at the moment, the padding process is omitted, and * garbage is sent as pad data. If next packet is stored in the * transmission buffer before xmit(), write_mbuf() pads the previous * packet before transmitting new packet. This *may* gain the * system performance (slightly). */ static void fe_write_mbufs ( struct fe_softc *sc, struct mbuf *m ) { u_short addr_bmpr8 = sc->ioaddr[ FE_BMPR8 ]; u_short length, len; struct mbuf *mp; u_char *data; u_short savebyte; /* WARNING: Architecture dependent! */ #define NO_PENDING_BYTE 0xFFFF #if FE_DEBUG >= 2 /* First, count up the total number of bytes to copy */ length = 0; for ( mp = m; mp != NULL; mp = mp->m_next ) { length += mp->m_len; } /* Check if this matches the one in the packet header. */ if ( length != m->m_pkthdr.len ) { log( LOG_WARNING, "fe%d: packet length mismatch? (%d/%d)\n", sc->sc_unit, length, m->m_pkthdr.len ); } #else /* Just use the length value in the packet header. */ length = m->m_pkthdr.len; #endif #if FE_DEBUG >= 1 /* * Should never send big packets. If such a packet is passed, * it should be a bug of upper layer. We just ignore it. * ... Partial (too short) packets, neither. */ if ( ETHER_IS_VALID_LEN(length + ETHER_CRC_LEN)) { log( LOG_ERR, "fe%d: got a out-of-spes packet (%u bytes) to send\n", sc->sc_unit, length ); sc->sc_if.if_oerrors++; return; } #endif /* * Put the length word for this frame. * Does 86960 accept odd length? -- Yes. * Do we need to pad the length to minimum size by ourselves? * -- Generally yes. But for (or will be) the last * packet in the transmission buffer, we can skip the * padding process. It may gain performance slightly. FIXME. */ outw( addr_bmpr8, max( length, ETHER_MIN_LEN - ETHER_CRC_LEN ) ); /* * Update buffer status now. * Truncate the length up to an even number, since we use outw(). */ length = ( length + 1 ) & ~1; sc->txb_free -= FE_DATA_LEN_LEN + max( length, ETHER_MIN_LEN - ETHER_CRC_LEN); sc->txb_count++; /* * Transfer the data from mbuf chain to the transmission buffer. * MB86960 seems to require that data be transferred as words, and * only words. So that we require some extra code to patch * over odd-length mbufs. */ savebyte = NO_PENDING_BYTE; for ( mp = m; mp != 0; mp = mp->m_next ) { /* Ignore empty mbuf. */ len = mp->m_len; if ( len == 0 ) continue; /* Find the actual data to send. */ data = mtod(mp, caddr_t); /* Finish the last byte. */ if ( savebyte != NO_PENDING_BYTE ) { outw( addr_bmpr8, savebyte | ( *data << 8 ) ); data++; len--; savebyte = NO_PENDING_BYTE; } /* output contiguous words */ if (len > 1) { outsw( addr_bmpr8, data, len >> 1); data += len & ~1; len &= 1; } /* Save a remaining byte, if there is one. */ if ( len > 0 ) { savebyte = *data; } } /* Spit the last byte, if the length is odd. */ if ( savebyte != NO_PENDING_BYTE ) { outw( addr_bmpr8, savebyte ); } } /* * Compute hash value for an Ethernet address */ static int fe_hash ( u_char * ep ) { #define FE_HASH_MAGIC_NUMBER 0xEDB88320L u_long hash = 0xFFFFFFFFL; int i, j; u_char b; u_long m; for ( i = ETHER_ADDR_LEN; --i >= 0; ) { b = *ep++; for ( j = 8; --j >= 0; ) { m = hash; hash >>= 1; if ( ( m ^ b ) & 1 ) hash ^= FE_HASH_MAGIC_NUMBER; b >>= 1; } } return ( ( int )( hash >> 26 ) ); } /* * Compute the multicast address filter from the * list of multicast addresses we need to listen to. */ static struct fe_filter fe_mcaf ( struct fe_softc *sc ) { int index; struct fe_filter filter; struct ether_multi *enm; struct ether_multistep step; filter = fe_filter_nothing; ETHER_FIRST_MULTI(step, &sc->arpcom, enm); while ( enm != NULL) { if ( bcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN) ) { return ( fe_filter_all ); } index = fe_hash( enm->enm_addrlo ); #if FE_DEBUG >= 4 log( LOG_INFO, "fe%d: hash(%6D) == %d\n", sc->sc_unit, enm->enm_addrlo , ":", index ); #endif filter.data[index >> 3] |= 1 << (index & 7); ETHER_NEXT_MULTI(step, enm); } return ( filter ); } /* * Calculate a new "multicast packet filter" and put the 86960 * receiver in appropriate mode. */ static void fe_setmode ( struct fe_softc *sc ) { int flags = sc->sc_if.if_flags; /* * If the interface is not running, we postpone the update * process for receive modes and multicast address filter * until the interface is restarted. It reduces some * complicated job on maintaining chip states. (Earlier versions * of this driver had a bug on that point...) * * To complete the trick, fe_init() calls fe_setmode() after * restarting the interface. */ if ( !( flags & IFF_RUNNING ) ) return; /* * Promiscuous mode is handled separately. */ if ( flags & IFF_PROMISC ) { /* * Program 86960 to receive all packets on the segment * including those directed to other stations. * Multicast filter stored in MARs are ignored * under this setting, so we don't need to update it. * * Promiscuous mode in FreeBSD 2 is used solely by * BPF, and BPF only listens to valid (no error) packets. * So, we ignore erroneous ones even in this mode. * (Older versions of fe driver mistook the point.) */ outb( sc->ioaddr[ FE_DLCR5 ], sc->proto_dlcr5 | FE_D5_AFM0 | FE_D5_AFM1 ); sc->filter_change = 0; #if FE_DEBUG >= 3 log( LOG_INFO, "fe%d: promiscuous mode\n", sc->sc_unit ); #endif return; } /* * Turn the chip to the normal (non-promiscuous) mode. */ outb( sc->ioaddr[ FE_DLCR5 ], sc->proto_dlcr5 | FE_D5_AFM1 ); /* * Find the new multicast filter value. * I'm not sure we have to handle modes other than MULTICAST. * Who sets ALLMULTI? Who turns MULTICAST off? FIXME. */ if ( flags & IFF_ALLMULTI ) { sc->filter = fe_filter_all; } else if ( flags & IFF_MULTICAST ) { sc->filter = fe_mcaf( sc ); } else { sc->filter = fe_filter_nothing; } sc->filter_change = 1; #if FE_DEBUG >= 3 log( LOG_INFO, "fe%d: address filter: [%8D]\n", sc->sc_unit, sc->filter.data, " " ); #endif /* * We have to update the multicast filter in the 86960, A.S.A.P. * * Note that the DLC (Data Link Control unit, i.e. transmitter * and receiver) must be stopped when feeding the filter, and * DLC trashes all packets in both transmission and receive * buffers when stopped. * * ... Are the above sentences correct? I have to check the * manual of the MB86960A. FIXME. * * To reduce the packet loss, we delay the filter update * process until buffers are empty. */ if ( sc->txb_sched == 0 && sc->txb_count == 0 && !( inb( sc->ioaddr[ FE_DLCR1 ] ) & FE_D1_PKTRDY ) ) { /* * Buffers are (apparently) empty. Load * the new filter value into MARs now. */ fe_loadmar(sc); } else { /* * Buffers are not empty. Mark that we have to update * the MARs. The new filter will be loaded by feintr() * later. */ #if FE_DEBUG >= 4 log( LOG_INFO, "fe%d: filter change delayed\n", sc->sc_unit ); #endif } } /* * Load a new multicast address filter into MARs. * * The caller must have splimp'ed before fe_loadmar. * This function starts the DLC upon return. So it can be called only * when the chip is working, i.e., from the driver's point of view, when * a device is RUNNING. (I mistook the point in previous versions.) */ static void fe_loadmar ( struct fe_softc * sc ) { /* Stop the DLC (transmitter and receiver). */ DELAY( 200 ); outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_DISABLE ); DELAY( 200 ); /* Select register bank 1 for MARs. */ outb( sc->ioaddr[ FE_DLCR7 ], sc->proto_dlcr7 | FE_D7_RBS_MAR | FE_D7_POWER_UP ); /* Copy filter value into the registers. */ outblk( sc, FE_MAR8, sc->filter.data, FE_FILTER_LEN ); /* Restore the bank selection for BMPRs (i.e., runtime registers). */ outb( sc->ioaddr[ FE_DLCR7 ], sc->proto_dlcr7 | FE_D7_RBS_BMPR | FE_D7_POWER_UP ); /* Restart the DLC. */ DELAY( 200 ); outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_ENABLE ); DELAY( 200 ); /* We have just updated the filter. */ sc->filter_change = 0; #if FE_DEBUG >= 3 log( LOG_INFO, "fe%d: address filter changed\n", sc->sc_unit ); #endif } #if FE_DEBUG >= 1 static void fe_dump ( int level, struct fe_softc * sc, char * message ) { log( level, "fe%d: %s," " DLCR = %02x %02x %02x %02x %02x %02x %02x %02x," " BMPR = xx xx %02x %02x %02x %02x %02x %02x," " asic = %02x %02x %02x %02x %02x %02x %02x %02x" " + %02x %02x %02x %02x %02x %02x %02x %02x\n", sc->sc_unit, message ? message : "registers", inb( sc->ioaddr[ FE_DLCR0 ] ), inb( sc->ioaddr[ FE_DLCR1 ] ), inb( sc->ioaddr[ FE_DLCR2 ] ), inb( sc->ioaddr[ FE_DLCR3 ] ), inb( sc->ioaddr[ FE_DLCR4 ] ), inb( sc->ioaddr[ FE_DLCR5 ] ), inb( sc->ioaddr[ FE_DLCR6 ] ), inb( sc->ioaddr[ FE_DLCR7 ] ), inb( sc->ioaddr[ FE_BMPR10 ] ), inb( sc->ioaddr[ FE_BMPR11 ] ), inb( sc->ioaddr[ FE_BMPR12 ] ), inb( sc->ioaddr[ FE_BMPR13 ] ), inb( sc->ioaddr[ FE_BMPR14 ] ), inb( sc->ioaddr[ FE_BMPR15 ] ), inb( sc->ioaddr[ 0x10 ] ), inb( sc->ioaddr[ 0x11 ] ), inb( sc->ioaddr[ 0x12 ] ), inb( sc->ioaddr[ 0x13 ] ), inb( sc->ioaddr[ 0x14 ] ), inb( sc->ioaddr[ 0x15 ] ), inb( sc->ioaddr[ 0x16 ] ), inb( sc->ioaddr[ 0x17 ] ), inb( sc->ioaddr[ 0x18 ] ), inb( sc->ioaddr[ 0x19 ] ), inb( sc->ioaddr[ 0x1A ] ), inb( sc->ioaddr[ 0x1B ] ), inb( sc->ioaddr[ 0x1C ] ), inb( sc->ioaddr[ 0x1D ] ), inb( sc->ioaddr[ 0x1E ] ), inb( sc->ioaddr[ 0x1F ] ) ); } #endif Index: head/sys/pc98/pc98/machdep.c =================================================================== --- head/sys/pc98/pc98/machdep.c (revision 18264) +++ head/sys/pc98/pc98/machdep.c (revision 18265) @@ -1,1576 +1,1568 @@ /*- * Copyright (c) 1992 Terrence R. Lambert. * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 - * $Id: machdep.c,v 1.6 1996/09/07 02:13:32 asami Exp $ + * $Id: machdep.c,v 1.7 1996/09/10 09:37:35 asami Exp $ */ #include "npx.h" #include "opt_sysvipc.h" #include "opt_ddb.h" #include "opt_bounce.h" #include "opt_machdep.h" #include "opt_perfmon.h" +#include "opt_userconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SYSVSHM #include #endif #ifdef SYSVMSG #include #endif #ifdef SYSVSEM #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PERFMON #include #endif #include #ifdef PC98 #include #else #include #endif #include extern void init386 __P((int first)); extern int ptrace_set_pc __P((struct proc *p, unsigned int addr)); extern int ptrace_single_step __P((struct proc *p)); extern int ptrace_write_u __P((struct proc *p, vm_offset_t off, int data)); extern void dblfault_handler __P((void)); extern void identifycpu(void); /* XXX header file */ extern void earlysetcpuclass(void); /* same header file */ static void cpu_startup __P((void *)); SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL) #ifdef BOUNCE_BUFFERS extern char *bouncememory; extern int maxbkva; #ifdef BOUNCEPAGES int bouncepages = BOUNCEPAGES; #else int bouncepages = 0; #endif #endif /* BOUNCE_BUFFERS */ extern int freebufspace; int msgbufmapped = 0; /* set when safe to use msgbuf */ int _udatasel, _ucodesel; u_int atdevbase; int physmem = 0; int cold = 1; static int sysctl_hw_physmem SYSCTL_HANDLER_ARGS { int error = sysctl_handle_int(oidp, 0, ctob(physmem), req); return (error); } SYSCTL_PROC(_hw, HW_PHYSMEM, physmem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_physmem, "I", ""); static int sysctl_hw_usermem SYSCTL_HANDLER_ARGS { int error = sysctl_handle_int(oidp, 0, ctob(physmem - cnt.v_wire_count), req); return (error); } SYSCTL_PROC(_hw, HW_USERMEM, usermem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_usermem, "I", ""); int boothowto = 0, bootverbose = 0, Maxmem = 0; static int badpages = 0; #ifdef PC98 int Maxmem_under16M = 0; #endif long dumplo; extern int bootdev; vm_offset_t phys_avail[10]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) static void setup_netisrs __P((struct linker_set *)); /* XXX declare elsewhere */ static vm_offset_t buffer_sva, buffer_eva; vm_offset_t clean_sva, clean_eva; static vm_offset_t pager_sva, pager_eva; extern struct linker_set netisr_set; #define offsetof(type, member) ((size_t)(&((type *)0)->member)) static void cpu_startup(dummy) void *dummy; { register unsigned i; register caddr_t v; vm_offset_t maxaddr; vm_size_t size = 0; int firstaddr; vm_offset_t minaddr; if (boothowto & RB_VERBOSE) bootverbose++; /* * Initialize error message buffer (at end of core). */ /* avail_end was pre-decremented in init386() to compensate */ for (i = 0; i < btoc(sizeof (struct msgbuf)); i++) pmap_enter(pmap_kernel(), (vm_offset_t)msgbufp, avail_end + i * PAGE_SIZE, VM_PROT_ALL, TRUE); msgbufmapped = 1; /* * Good {morning,afternoon,evening,night}. */ printf(version); earlysetcpuclass(); startrtclock(); identifycpu(); #ifdef PERFMON perfmon_init(); #endif printf("real memory = %d (%dK bytes)\n", ptoa(Maxmem), ptoa(Maxmem) / 1024); /* * Display any holes after the first chunk of extended memory. */ if (badpages != 0) { int indx = 1; /* * XXX skip reporting ISA hole & unmanaged kernel memory */ if (phys_avail[0] == PAGE_SIZE) indx += 2; printf("Physical memory hole(s):\n"); for (; phys_avail[indx + 1] != 0; indx += 2) { int size = phys_avail[indx + 1] - phys_avail[indx]; printf("0x%08lx - 0x%08lx, %d bytes (%d pages)\n", phys_avail[indx], phys_avail[indx + 1] - 1, size, size / PAGE_SIZE); } } /* * Quickly wire in netisrs. */ setup_netisrs(&netisr_set); /* #ifdef ISDN DONET(isdnintr, NETISR_ISDN); #endif */ /* * Allocate space for system data structures. * The first available kernel virtual address is in "v". * As pages of kernel virtual memory are allocated, "v" is incremented. * As pages of memory are allocated and cleared, * "firstaddr" is incremented. * An index into the kernel page table corresponding to the * virtual memory address maintained in "v" is kept in "mapaddr". */ /* * Make two passes. The first pass calculates how much memory is * needed and allocates it. The second pass assigns virtual * addresses to the various data structures. */ firstaddr = 0; again: v = (caddr_t)firstaddr; #define valloc(name, type, num) \ (name) = (type *)v; v = (caddr_t)((name)+(num)) #define valloclim(name, type, num, lim) \ (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num))) valloc(callout, struct callout, ncallout); #ifdef SYSVSHM valloc(shmsegs, struct shmid_ds, shminfo.shmmni); #endif #ifdef SYSVSEM valloc(sema, struct semid_ds, seminfo.semmni); valloc(sem, struct sem, seminfo.semmns); /* This is pretty disgusting! */ valloc(semu, int, (seminfo.semmnu * seminfo.semusz) / sizeof(int)); #endif #ifdef SYSVMSG valloc(msgpool, char, msginfo.msgmax); valloc(msgmaps, struct msgmap, msginfo.msgseg); valloc(msghdrs, struct msg, msginfo.msgtql); valloc(msqids, struct msqid_ds, msginfo.msgmni); #endif if (nbuf == 0) { nbuf = 30; if( physmem > 1024) nbuf += min((physmem - 1024) / 12, 1024); } nswbuf = min(nbuf, 128); valloc(swbuf, struct buf, nswbuf); valloc(buf, struct buf, nbuf); #ifdef BOUNCE_BUFFERS /* * If there is more than 16MB of memory, allocate some bounce buffers */ if (Maxmem > 4096) { if (bouncepages == 0) { bouncepages = 64; bouncepages += ((Maxmem - 4096) / 2048) * 32; } v = (caddr_t)((vm_offset_t)round_page(v)); valloc(bouncememory, char, bouncepages * PAGE_SIZE); } #endif /* * End of first pass, size has been calculated so allocate memory */ if (firstaddr == 0) { size = (vm_size_t)(v - firstaddr); firstaddr = (int)kmem_alloc(kernel_map, round_page(size)); if (firstaddr == 0) panic("startup: no room for tables"); goto again; } /* * End of second pass, addresses have been assigned */ if ((vm_size_t)(v - firstaddr) != size) panic("startup: table size inconsistency"); #ifdef BOUNCE_BUFFERS clean_map = kmem_suballoc(kernel_map, &clean_sva, &clean_eva, (nbuf*MAXBSIZE) + (nswbuf*MAXPHYS) + maxbkva + pager_map_size, TRUE); io_map = kmem_suballoc(clean_map, &minaddr, &maxaddr, maxbkva, FALSE); #else clean_map = kmem_suballoc(kernel_map, &clean_sva, &clean_eva, (nbuf*MAXBSIZE) + (nswbuf*MAXPHYS) + pager_map_size, TRUE); #endif buffer_map = kmem_suballoc(clean_map, &buffer_sva, &buffer_eva, (nbuf*MAXBSIZE), TRUE); pager_map = kmem_suballoc(clean_map, &pager_sva, &pager_eva, (nswbuf*MAXPHYS) + pager_map_size, TRUE); exec_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, (16*ARG_MAX), TRUE); exech_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, (32*ARG_MAX), TRUE); u_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, (maxproc*UPAGES*PAGE_SIZE), FALSE); /* * Finally, allocate mbuf pool. Since mclrefcnt is an off-size * we use the more space efficient malloc in place of kmem_alloc. */ mclrefcnt = (char *)malloc(nmbclusters+PAGE_SIZE/MCLBYTES, M_MBUF, M_NOWAIT); bzero(mclrefcnt, nmbclusters+PAGE_SIZE/MCLBYTES); mcl_map = kmem_suballoc(kmem_map, (vm_offset_t *)&mbutl, &maxaddr, nmbclusters * MCLBYTES, FALSE); { vm_size_t mb_map_size; mb_map_size = nmbufs * MSIZE; mb_map = kmem_suballoc(kmem_map, &minaddr, &maxaddr, round_page(mb_map_size), FALSE); } /* * Initialize callouts */ callfree = callout; for (i = 1; i < ncallout; i++) callout[i-1].c_next = &callout[i]; if (boothowto & RB_CONFIG) { +#ifdef USERCONFIG userconfig(); cninit(); /* the preferred console may have changed */ +#else + printf("Sorry! no userconfig in this kernel\n"); +#endif } #ifdef BOUNCE_BUFFERS /* * init bounce buffers */ vm_bounce_init(); #endif printf("avail memory = %d (%dK bytes)\n", ptoa(cnt.v_free_count), ptoa(cnt.v_free_count) / 1024); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); /* * In verbose mode, print out the BIOS's idea of the disk geometries. */ if (bootverbose) { printf("BIOS Geometries:\n"); for (i = 0; i < N_BIOS_GEOM; i++) { unsigned long bios_geom; int max_cylinder, max_head, max_sector; bios_geom = bootinfo.bi_bios_geom[i]; /* * XXX the bootstrap punts a 1200K floppy geometry * when the get-disk-geometry interrupt fails. Skip * drives that have this geometry. */ if (bios_geom == 0x4f010f) continue; printf(" %x:%08lx ", i, bios_geom); max_cylinder = bios_geom >> 16; max_head = (bios_geom >> 8) & 0xff; max_sector = bios_geom & 0xff; printf( "0..%d=%d cylinders, 0..%d=%d heads, 1..%d=%d sectors\n", max_cylinder, max_cylinder + 1, max_head, max_head + 1, max_sector, max_sector); } printf(" %d accounted for\n", bootinfo.bi_n_bios_used); } } int register_netisr(num, handler) int num; netisr_t *handler; { if (num < 0 || num >= (sizeof(netisrs)/sizeof(*netisrs)) ) { printf("register_netisr: bad isr number: %d\n", num); return (EINVAL); } netisrs[num] = handler; return (0); } static void setup_netisrs(ls) struct linker_set *ls; { int i; const struct netisrtab *nit; for(i = 0; ls->ls_items[i]; i++) { nit = (const struct netisrtab *)ls->ls_items[i]; register_netisr(nit->nit_num, nit->nit_isr); } } /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * at top to call routine, followed by kcall * to sigreturn routine below. After sigreturn * resets the signal mask, the stack, and the * frame pointer, it returns to the user * specified pc, psl. */ void sendsig(catcher, sig, mask, code) sig_t catcher; int sig, mask; u_long code; { register struct proc *p = curproc; register int *regs; register struct sigframe *fp; struct sigframe sf; struct sigacts *psp = p->p_sigacts; int oonstack; regs = p->p_md.md_regs; oonstack = psp->ps_sigstk.ss_flags & SS_ONSTACK; /* * Allocate and validate space for the signal handler context. */ if ((psp->ps_flags & SAS_ALTSTACK) && !oonstack && (psp->ps_sigonstack & sigmask(sig))) { fp = (struct sigframe *)(psp->ps_sigstk.ss_sp + psp->ps_sigstk.ss_size - sizeof(struct sigframe)); psp->ps_sigstk.ss_flags |= SS_ONSTACK; } else { fp = (struct sigframe *)regs[tESP] - 1; } /* * grow() will return FALSE if the fp will not fit inside the stack * and the stack can not be grown. useracc will return FALSE * if access is denied. */ if ((grow(p, (int)fp) == FALSE) || (useracc((caddr_t)fp, sizeof (struct sigframe), B_WRITE) == FALSE)) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ SIGACTION(p, SIGILL) = SIG_DFL; sig = sigmask(SIGILL); p->p_sigignore &= ~sig; p->p_sigcatch &= ~sig; p->p_sigmask &= ~sig; psignal(p, SIGILL); return; } /* * Build the argument list for the signal handler. */ if (p->p_sysent->sv_sigtbl) { if (sig < p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[sig]; else sig = p->p_sysent->sv_sigsize + 1; } sf.sf_signum = sig; sf.sf_code = code; sf.sf_scp = &fp->sf_sc; sf.sf_addr = (char *) regs[tERR]; sf.sf_handler = catcher; /* save scratch registers */ sf.sf_sc.sc_eax = regs[tEAX]; sf.sf_sc.sc_ebx = regs[tEBX]; sf.sf_sc.sc_ecx = regs[tECX]; sf.sf_sc.sc_edx = regs[tEDX]; sf.sf_sc.sc_esi = regs[tESI]; sf.sf_sc.sc_edi = regs[tEDI]; sf.sf_sc.sc_cs = regs[tCS]; sf.sf_sc.sc_ds = regs[tDS]; sf.sf_sc.sc_ss = regs[tSS]; sf.sf_sc.sc_es = regs[tES]; sf.sf_sc.sc_isp = regs[tISP]; /* * Build the signal context to be used by sigreturn. */ sf.sf_sc.sc_onstack = oonstack; sf.sf_sc.sc_mask = mask; sf.sf_sc.sc_sp = regs[tESP]; sf.sf_sc.sc_fp = regs[tEBP]; sf.sf_sc.sc_pc = regs[tEIP]; sf.sf_sc.sc_ps = regs[tEFLAGS]; /* * Copy the sigframe out to the user's stack. */ if (copyout(&sf, fp, sizeof(struct sigframe)) != 0) { /* * Something is wrong with the stack pointer. * ...Kill the process. */ sigexit(p, SIGILL); }; regs[tESP] = (int)fp; regs[tEIP] = (int)(((char *)PS_STRINGS) - *(p->p_sysent->sv_szsigcode)); regs[tEFLAGS] &= ~PSL_VM; regs[tCS] = _ucodesel; regs[tDS] = _udatasel; regs[tES] = _udatasel; regs[tSS] = _udatasel; } /* * System call to cleanup state after a signal * has been taken. Reset signal mask and * stack state from context left by sendsig (above). * Return to previous pc and psl as specified by * context left by sendsig. Check carefully to * make sure that the user has not modified the * state to gain improper privileges. */ int sigreturn(p, uap, retval) struct proc *p; struct sigreturn_args /* { struct sigcontext *sigcntxp; } */ *uap; int *retval; { register struct sigcontext *scp; register struct sigframe *fp; register int *regs = p->p_md.md_regs; int eflags; /* * (XXX old comment) regs[tESP] points to the return address. * The user scp pointer is above that. * The return address is faked in the signal trampoline code * for consistency. */ scp = uap->sigcntxp; fp = (struct sigframe *) ((caddr_t)scp - offsetof(struct sigframe, sf_sc)); if (useracc((caddr_t)fp, sizeof (*fp), 0) == 0) return(EINVAL); /* * Don't allow users to change privileged or reserved flags. */ #define EFLAGS_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) eflags = scp->sc_ps; /* * XXX do allow users to change the privileged flag PSL_RF. The * cpu sets PSL_RF in tf_eflags for faults. Debuggers should * sometimes set it there too. tf_eflags is kept in the signal * context during signal handling and there is no other place * to remember it, so the PSL_RF bit may be corrupted by the * signal handler without us knowing. Corruption of the PSL_RF * bit at worst causes one more or one less debugger trap, so * allowing it is fairly harmless. */ if (!EFLAGS_SECURE(eflags & ~PSL_RF, regs[tEFLAGS] & ~PSL_RF)) { #ifdef DEBUG printf("sigreturn: eflags = 0x%x\n", eflags); #endif return(EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) if (!CS_SECURE(scp->sc_cs)) { #ifdef DEBUG printf("sigreturn: cs = 0x%x\n", scp->sc_cs); #endif trapsignal(p, SIGBUS, T_PROTFLT); return(EINVAL); } /* restore scratch registers */ regs[tEAX] = scp->sc_eax; regs[tEBX] = scp->sc_ebx; regs[tECX] = scp->sc_ecx; regs[tEDX] = scp->sc_edx; regs[tESI] = scp->sc_esi; regs[tEDI] = scp->sc_edi; regs[tCS] = scp->sc_cs; regs[tDS] = scp->sc_ds; regs[tES] = scp->sc_es; regs[tSS] = scp->sc_ss; regs[tISP] = scp->sc_isp; if (useracc((caddr_t)scp, sizeof (*scp), 0) == 0) return(EINVAL); if (scp->sc_onstack & 01) p->p_sigacts->ps_sigstk.ss_flags |= SS_ONSTACK; else p->p_sigacts->ps_sigstk.ss_flags &= ~SS_ONSTACK; p->p_sigmask = scp->sc_mask &~ (sigmask(SIGKILL)|sigmask(SIGCONT)|sigmask(SIGSTOP)); regs[tEBP] = scp->sc_fp; regs[tESP] = scp->sc_sp; regs[tEIP] = scp->sc_pc; regs[tEFLAGS] = eflags; return(EJUSTRETURN); } /* * Machine depdnetnt boot() routine * * I haven't seen anything too put here yet * Possibly some stuff might be grafted back here from boot() */ void cpu_boot(int howto) { } /* * Clear registers on exec */ void setregs(p, entry, stack) struct proc *p; u_long entry; u_long stack; { int *regs = p->p_md.md_regs; #ifdef USER_LDT struct pcb *pcb = &p->p_addr->u_pcb; /* was i386_user_cleanup() in NetBSD */ if (pcb->pcb_ldt) { if (pcb == curpcb) lldt(GSEL(GUSERLDT_SEL, SEL_KPL)); kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ldt, pcb->pcb_ldt_len * sizeof(union descriptor)); pcb->pcb_ldt_len = (int)pcb->pcb_ldt = 0; } #endif bzero(regs, sizeof(struct trapframe)); regs[tEIP] = entry; regs[tESP] = stack; regs[tEFLAGS] = PSL_USER | (regs[tEFLAGS] & PSL_T); regs[tSS] = _udatasel; regs[tDS] = _udatasel; regs[tES] = _udatasel; regs[tCS] = _ucodesel; p->p_addr->u_pcb.pcb_flags = 0; /* no fp at all */ load_cr0(rcr0() | CR0_TS); /* start emulating */ #if NNPX > 0 npxinit(__INITIAL_NPXCW__); #endif /* NNPX > 0 */ } static int sysctl_machdep_adjkerntz SYSCTL_HANDLER_ARGS { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) resettodr(); return (error); } SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW, &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", ""); SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_STRUCT(_machdep, CPU_BOOTINFO, bootinfo, CTLFLAG_RD, &bootinfo, bootinfo, ""); SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock, CTLFLAG_RW, &wall_cmos_clock, 0, ""); /* * Initialize 386 and configure to run kernel */ /* * Initialize segments & interrupt table */ int currentldt; int _default_ldt; union descriptor gdt[NGDT]; /* global descriptor table */ struct gate_descriptor idt[NIDT]; /* interrupt descriptor table */ union descriptor ldt[NLDT]; /* local descriptor table */ static struct i386tss dblfault_tss; static char dblfault_stack[PAGE_SIZE]; extern struct user *proc0paddr; /* software prototypes -- in more palatable form */ struct soft_segment_descriptor gdt_segs[] = { /* GNULL_SEL 0 Null Descriptor */ { 0x0, /* segment base address */ 0x0, /* length */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GCODE_SEL 1 Code Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GDATA_SEL 2 Data Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GLDT_SEL 3 LDT Descriptor */ { (int) ldt, /* segment base address */ sizeof(ldt)-1, /* length - all address space */ SDT_SYSLDT, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GTGATE_SEL 4 Null Descriptor - Placeholder */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GPANIC_SEL 5 Panic Tss Descriptor */ { (int) &dblfault_tss, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GPROC0_SEL 6 Proc 0 Tss Descriptor */ { (int) kstack, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GUSERLDT_SEL 7 User LDT Descriptor per process */ { (int) ldt, /* segment base address */ (512 * sizeof(union descriptor)-1), /* length */ SDT_SYSLDT, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GAPMCODE32_SEL 8 APM BIOS 32-bit interface (32bit Code) */ { 0, /* segment base address (overwritten by APM) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GAPMCODE16_SEL 9 APM BIOS 32-bit interface (16bit Code) */ { 0, /* segment base address (overwritten by APM) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GAPMDATA_SEL 10 APM BIOS 32-bit interface (Data) */ { 0, /* segment base address (overwritten by APM) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; static struct soft_segment_descriptor ldt_segs[] = { /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Code Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* Data Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; void setidt(idx, func, typ, dpl, selec) int idx; inthand_t *func; int typ; int dpl; int selec; { struct gate_descriptor *ip = idt + idx; ip->gd_looffset = (int)func; ip->gd_selector = selec; ip->gd_stkcpy = 0; ip->gd_xx = 0; ip->gd_type = typ; ip->gd_dpl = dpl; ip->gd_p = 1; ip->gd_hioffset = ((int)func)>>16 ; } #define IDTVEC(name) __CONCAT(X,name) extern inthand_t IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(fpusegm), IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot), IDTVEC(page), IDTVEC(mchk), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align), IDTVEC(syscall), IDTVEC(int0x80_syscall); void sdtossd(sd, ssd) struct segment_descriptor *sd; struct soft_segment_descriptor *ssd; { ssd->ssd_base = (sd->sd_hibase << 24) | sd->sd_lobase; ssd->ssd_limit = (sd->sd_hilimit << 16) | sd->sd_lolimit; ssd->ssd_type = sd->sd_type; ssd->ssd_dpl = sd->sd_dpl; ssd->ssd_p = sd->sd_p; ssd->ssd_def32 = sd->sd_def32; ssd->ssd_gran = sd->sd_gran; } void init386(first) int first; { int x; unsigned biosbasemem, biosextmem; struct gate_descriptor *gdp; int gsel_tss; /* table descriptors - used to load tables by microp */ struct region_descriptor r_gdt, r_idt; int pagesinbase, pagesinext; int target_page, pa_indx; proc0.p_addr = proc0paddr; atdevbase = ISA_HOLE_START + KERNBASE; /* * Initialize the console before we print anything out. */ cninit(); #ifdef PC98 /* * Initialize DMAC */ init_pc98_dmac(); #endif /* * make gdt memory segments, the code segment goes up to end of the * page with etext in it, the data segment goes to the end of * the address space */ /* * XXX text protection is temporarily (?) disabled. The limit was * i386_btop(round_page(etext)) - 1. */ gdt_segs[GCODE_SEL].ssd_limit = i386_btop(0) - 1; gdt_segs[GDATA_SEL].ssd_limit = i386_btop(0) - 1; for (x = 0; x < NGDT; x++) ssdtosd(&gdt_segs[x], &gdt[x].sd); /* make ldt memory segments */ /* * The data segment limit must not cover the user area because we * don't want the user area to be writable in copyout() etc. (page * level protection is lost in kernel mode on 386's). Also, we * don't want the user area to be writable directly (page level * protection of the user area is not available on 486's with * CR0_WP set, because there is no user-read/kernel-write mode). * * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it * should be spelled ...MAX_USER... */ #define VM_END_USER_RW_ADDRESS VM_MAXUSER_ADDRESS /* * The code segment limit has to cover the user area until we move * the signal trampoline out of the user area. This is safe because * the code segment cannot be written to directly. */ #define VM_END_USER_R_ADDRESS (VM_END_USER_RW_ADDRESS + UPAGES * PAGE_SIZE) ldt_segs[LUCODE_SEL].ssd_limit = i386_btop(VM_END_USER_R_ADDRESS) - 1; ldt_segs[LUDATA_SEL].ssd_limit = i386_btop(VM_END_USER_RW_ADDRESS) - 1; /* Note. eventually want private ldts per process */ for (x = 0; x < NLDT; x++) ssdtosd(&ldt_segs[x], &ldt[x].sd); /* exceptions */ for (x = 0; x < NIDT; x++) setidt(x, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0, &IDTVEC(div), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(1, &IDTVEC(dbg), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(2, &IDTVEC(nmi), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(3, &IDTVEC(bpt), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(4, &IDTVEC(ofl), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(5, &IDTVEC(bnd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(7, &IDTVEC(dna), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(8, 0, SDT_SYSTASKGT, SEL_KPL, GSEL(GPANIC_SEL, SEL_KPL)); setidt(9, &IDTVEC(fpusegm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(10, &IDTVEC(tss), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(11, &IDTVEC(missing), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(12, &IDTVEC(stk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #ifdef CYRIX_486DLC setidt(14, &IDTVEC(page), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #else setidt(14, &IDTVEC(page), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif setidt(15, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(16, &IDTVEC(fpu), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(17, &IDTVEC(align), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(18, &IDTVEC(mchk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0x80, &IDTVEC(int0x80_syscall), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); #include "isa.h" #if NISA >0 isa_defaultirq(); #endif rand_initialize(); r_gdt.rd_limit = sizeof(gdt) - 1; r_gdt.rd_base = (int) gdt; lgdt(&r_gdt); r_idt.rd_limit = sizeof(idt) - 1; r_idt.rd_base = (int) idt; lidt(&r_idt); _default_ldt = GSEL(GLDT_SEL, SEL_KPL); lldt(_default_ldt); currentldt = _default_ldt; #ifdef DDB kdb_init(); if (boothowto & RB_KDB) Debugger("Boot flags requested debugger"); #endif #ifdef PC98 #ifdef EPSON_MEMWIN init_epson_memwin(); #endif biosbasemem = 640; /* 640KB */ biosextmem = (Maxmem * PAGE_SIZE - 0x100000)/1024; /* extent memory */ #else /* IBM-PC */ /* Use BIOS values stored in RTC CMOS RAM, since probing * breaks certain 386 AT relics. */ biosbasemem = rtcin(RTC_BASELO)+ (rtcin(RTC_BASEHI)<<8); biosextmem = rtcin(RTC_EXTLO)+ (rtcin(RTC_EXTHI)<<8); /* * If BIOS tells us that it has more than 640k in the basemem, * don't believe it - set it to 640k. */ if (biosbasemem > 640) { printf("Preposterous RTC basemem of %dK, truncating to 640K\n", biosbasemem); biosbasemem = 640; } if (bootinfo.bi_memsizes_valid && bootinfo.bi_basemem > 640) { printf("Preposterous BIOS basemem of %dK, truncating to 640K\n", bootinfo.bi_basemem); bootinfo.bi_basemem = 640; } /* * Warn if the official BIOS interface disagrees with the RTC * interface used above about the amount of base memory or the * amount of extended memory. Prefer the BIOS value for the base * memory. This is necessary for machines that `steal' base * memory for use as BIOS memory, at least if we are going to use * the BIOS for apm. Prefer the RTC value for extended memory. * Eventually the hackish interface shouldn't even be looked at. */ if (bootinfo.bi_memsizes_valid) { if (bootinfo.bi_basemem != biosbasemem) { vm_offset_t pa; printf( "BIOS basemem (%ldK) != RTC basemem (%dK), setting to BIOS value\n", biosbasemem = bootinfo.bi_basemem; /* * XXX if biosbasemem is now < 640, there is `hole' * between the end of base memory and the start of * ISA memory. The hole may be empty or it may * contain BIOS code or data. Map it read/write so * that the BIOS can write to it. (Memory from 0 to * the physical end of the kernel is mapped read-only * to begin with and then parts of it are remapped. * The parts that aren't remapped form holes that * remain read-only and are unused by the kernel. * The base memory area is below the physical end of * the kernel and right now forms a read-only hole. * The part of it from 0 to * (trunc_page(biosbasemem * 1024) - 1) will be * remapped and used by the kernel later.) * * This code is similar to the code used in * pmap_mapdev, but since no memory needs to be * allocated we simply change the mapping. */ for (pa = trunc_page(biosbasemem * 1024); pa < ISA_HOLE_START; pa += PAGE_SIZE) { unsigned *pte; pte = (unsigned *)vtopte(pa + KERNBASE); *pte = pa | PG_RW | PG_V; } } if (bootinfo.bi_extmem != biosextmem) printf("BIOS extmem (%ldK) != RTC extmem (%dK)\n", bootinfo.bi_extmem, biosextmem); } -#endif - - /* - * Some 386 machines might give us a bogus number for extended - * mem. If this happens, stop now. - */ -#ifndef PC98 -#ifndef LARGEMEM - if (biosextmem > 65536) { - panic("extended memory beyond limit of 64MB"); - /* NOTREACHED */ - } -#endif #endif pagesinbase = biosbasemem * 1024 / PAGE_SIZE; pagesinext = biosextmem * 1024 / PAGE_SIZE; /* * Special hack for chipsets that still remap the 384k hole when * there's 16MB of memory - this really confuses people that * are trying to use bus mastering ISA controllers with the * "16MB limit"; they only have 16MB, but the remapping puts * them beyond the limit. */ #ifndef PC98 /* * If extended memory is between 15-16MB (16-17MB phys address range), * chop it to 15MB. */ if ((pagesinext > 3840) && (pagesinext < 4096)) pagesinext = 3840; #endif /* * Maxmem isn't the "maximum memory", it's one larger than the * highest page of the physical address space. It should be * called something like "Maxphyspage". */ Maxmem = pagesinext + 0x100000/PAGE_SIZE; #ifdef MAXMEM Maxmem = MAXMEM/4; #endif /* call pmap initialization to make new kernel address space */ pmap_bootstrap (first, 0); /* * Size up each available chunk of physical memory. */ /* * We currently don't bother testing base memory. * XXX ...but we probably should. */ pa_indx = 0; badpages = 0; if (pagesinbase > 1) { phys_avail[pa_indx++] = PAGE_SIZE; /* skip first page of memory */ phys_avail[pa_indx] = ptoa(pagesinbase);/* memory up to the ISA hole */ physmem = pagesinbase - 1; } else { /* point at first chunk end */ pa_indx++; } #ifdef PC98 #ifdef notyet init_cpu_accel_mem(); #endif #endif for (target_page = avail_start; target_page < ptoa(Maxmem); target_page += PAGE_SIZE) { int tmp, page_bad = FALSE; #ifdef PC98 /* skip system area */ if (target_page>=ptoa(Maxmem_under16M) && target_page < ptoa(4096)) page_bad = TRUE; #endif /* * map page into kernel: valid, read/write, non-cacheable */ *(int *)CMAP1 = PG_V | PG_RW | PG_N | target_page; pmap_update(); tmp = *(int *)CADDR1; /* * Test for alternating 1's and 0's */ *(volatile int *)CADDR1 = 0xaaaaaaaa; if (*(volatile int *)CADDR1 != 0xaaaaaaaa) { page_bad = TRUE; } /* * Test for alternating 0's and 1's */ *(volatile int *)CADDR1 = 0x55555555; if (*(volatile int *)CADDR1 != 0x55555555) { page_bad = TRUE; } /* * Test for all 1's */ *(volatile int *)CADDR1 = 0xffffffff; if (*(volatile int *)CADDR1 != 0xffffffff) { page_bad = TRUE; } /* * Test for all 0's */ *(volatile int *)CADDR1 = 0x0; if (*(volatile int *)CADDR1 != 0x0) { /* * test of page failed */ page_bad = TRUE; } /* * Restore original value. */ *(int *)CADDR1 = tmp; /* * Adjust array of valid/good pages. */ if (page_bad == FALSE) { /* * If this good page is a continuation of the * previous set of good pages, then just increase * the end pointer. Otherwise start a new chunk. * Note that "end" points one higher than end, * making the range >= start and < end. */ if (phys_avail[pa_indx] == target_page) { phys_avail[pa_indx] += PAGE_SIZE; } else { pa_indx++; if (pa_indx == PHYS_AVAIL_ARRAY_END) { printf("Too many holes in the physical address space, giving up\n"); pa_indx--; break; } phys_avail[pa_indx++] = target_page; /* start */ phys_avail[pa_indx] = target_page + PAGE_SIZE; /* end */ } physmem++; } else { badpages++; page_bad = FALSE; } } *(int *)CMAP1 = 0; pmap_update(); /* * XXX * The last chunk must contain at least one page plus the message * buffer to avoid complicating other code (message buffer address * calculation, etc.). */ while (phys_avail[pa_indx - 1] + PAGE_SIZE + round_page(sizeof(struct msgbuf)) >= phys_avail[pa_indx]) { physmem -= atop(phys_avail[pa_indx] - phys_avail[pa_indx - 1]); phys_avail[pa_indx--] = 0; phys_avail[pa_indx--] = 0; } Maxmem = atop(phys_avail[pa_indx]); /* Trim off space for the message buffer. */ phys_avail[pa_indx] -= round_page(sizeof(struct msgbuf)); avail_end = phys_avail[pa_indx]; /* now running on new page tables, configured,and u/iom is accessible */ /* make a initial tss so microp can get interrupt stack on syscall! */ proc0.p_addr->u_pcb.pcb_tss.tss_esp0 = (int) kstack + UPAGES*PAGE_SIZE; proc0.p_addr->u_pcb.pcb_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL) ; gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); dblfault_tss.tss_esp = dblfault_tss.tss_esp0 = dblfault_tss.tss_esp1 = dblfault_tss.tss_esp2 = (int) &dblfault_stack[sizeof(dblfault_stack)]; dblfault_tss.tss_ss = dblfault_tss.tss_ss0 = dblfault_tss.tss_ss1 = dblfault_tss.tss_ss2 = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_cr3 = IdlePTD; dblfault_tss.tss_eip = (int) dblfault_handler; dblfault_tss.tss_eflags = PSL_KERNEL; dblfault_tss.tss_ds = dblfault_tss.tss_es = dblfault_tss.tss_fs = dblfault_tss.tss_gs = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_cs = GSEL(GCODE_SEL, SEL_KPL); dblfault_tss.tss_ldt = GSEL(GLDT_SEL, SEL_KPL); ((struct i386tss *)gdt_segs[GPROC0_SEL].ssd_base)->tss_ioopt = (sizeof(struct i386tss))<<16; ltr(gsel_tss); /* make a call gate to reenter kernel with */ gdp = &ldt[LSYS5CALLS_SEL].gd; x = (int) &IDTVEC(syscall); gdp->gd_looffset = x++; gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL); gdp->gd_stkcpy = 1; gdp->gd_type = SDT_SYS386CGT; gdp->gd_dpl = SEL_UPL; gdp->gd_p = 1; gdp->gd_hioffset = ((int) &IDTVEC(syscall)) >>16; /* transfer to user mode */ _ucodesel = LSEL(LUCODE_SEL, SEL_UPL); _udatasel = LSEL(LUDATA_SEL, SEL_UPL); /* setup proc 0's pcb */ proc0.p_addr->u_pcb.pcb_flags = 0; proc0.p_addr->u_pcb.pcb_cr3 = IdlePTD; } /* * The registers are in the frame; the frame is in the user area of * the process in question; when the process is active, the registers * are in "the kernel stack"; when it's not, they're still there, but * things get flipped around. So, since p->p_md.md_regs is the whole address * of the register set, take its offset from the kernel stack, and * index into the user block. Don't you just *love* virtual memory? * (I'm starting to think seymour is right...) */ #define TF_REGP(p) ((struct trapframe *) \ ((char *)(p)->p_addr \ + ((char *)(p)->p_md.md_regs - kstack))) int ptrace_set_pc(p, addr) struct proc *p; unsigned int addr; { TF_REGP(p)->tf_eip = addr; return (0); } int ptrace_single_step(p) struct proc *p; { TF_REGP(p)->tf_eflags |= PSL_T; return (0); } int ptrace_write_u(p, off, data) struct proc *p; vm_offset_t off; int data; { struct trapframe frame_copy; vm_offset_t min; struct trapframe *tp; /* * Privileged kernel state is scattered all over the user area. * Only allow write access to parts of regs and to fpregs. */ min = (char *)p->p_md.md_regs - kstack; if (off >= min && off <= min + sizeof(struct trapframe) - sizeof(int)) { tp = TF_REGP(p); frame_copy = *tp; *(int *)((char *)&frame_copy + (off - min)) = data; if (!EFLAGS_SECURE(frame_copy.tf_eflags, tp->tf_eflags) || !CS_SECURE(frame_copy.tf_cs)) return (EINVAL); *(int*)((char *)p->p_addr + off) = data; return (0); } min = offsetof(struct user, u_pcb) + offsetof(struct pcb, pcb_savefpu); if (off >= min && off <= min + sizeof(struct save87) - sizeof(int)) { *(int*)((char *)p->p_addr + off) = data; return (0); } return (EFAULT); } int fill_regs(p, regs) struct proc *p; struct reg *regs; { struct trapframe *tp; tp = TF_REGP(p); regs->r_es = tp->tf_es; regs->r_ds = tp->tf_ds; regs->r_edi = tp->tf_edi; regs->r_esi = tp->tf_esi; regs->r_ebp = tp->tf_ebp; regs->r_ebx = tp->tf_ebx; regs->r_edx = tp->tf_edx; regs->r_ecx = tp->tf_ecx; regs->r_eax = tp->tf_eax; regs->r_eip = tp->tf_eip; regs->r_cs = tp->tf_cs; regs->r_eflags = tp->tf_eflags; regs->r_esp = tp->tf_esp; regs->r_ss = tp->tf_ss; return (0); } int set_regs(p, regs) struct proc *p; struct reg *regs; { struct trapframe *tp; tp = TF_REGP(p); if (!EFLAGS_SECURE(regs->r_eflags, tp->tf_eflags) || !CS_SECURE(regs->r_cs)) return (EINVAL); tp->tf_es = regs->r_es; tp->tf_ds = regs->r_ds; tp->tf_edi = regs->r_edi; tp->tf_esi = regs->r_esi; tp->tf_ebp = regs->r_ebp; tp->tf_ebx = regs->r_ebx; tp->tf_edx = regs->r_edx; tp->tf_ecx = regs->r_ecx; tp->tf_eax = regs->r_eax; tp->tf_eip = regs->r_eip; tp->tf_cs = regs->r_cs; tp->tf_eflags = regs->r_eflags; tp->tf_esp = regs->r_esp; tp->tf_ss = regs->r_ss; return (0); } #ifndef DDB void Debugger(const char *msg) { printf("Debugger(\"%s\") called.\n", msg); } #endif /* no DDB */ #include #define b_cylin b_resid /* * Determine the size of the transfer, and make sure it is * within the boundaries of the partition. Adjust transfer * if needed, and signal errors or early completion. */ int bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel) { struct partition *p = lp->d_partitions + dkpart(bp->b_dev); int labelsect = lp->d_partitions[0].p_offset; int maxsz = p->p_size, sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; /* overwriting disk label ? */ /* XXX should also protect bootstrap in first 8K */ if (bp->b_blkno + p->p_offset <= LABELSECTOR + labelsect && #if LABELSECTOR != 0 bp->b_blkno + p->p_offset + sz > LABELSECTOR + labelsect && #endif (bp->b_flags & B_READ) == 0 && wlabel == 0) { bp->b_error = EROFS; goto bad; } #if defined(DOSBBSECTOR) && defined(notyet) /* overwriting master boot record? */ if (bp->b_blkno + p->p_offset <= DOSBBSECTOR && (bp->b_flags & B_READ) == 0 && wlabel == 0) { bp->b_error = EROFS; goto bad; } #endif /* beyond partition? */ if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) { /* if exactly at end of disk, return an EOF */ if (bp->b_blkno == maxsz) { bp->b_resid = bp->b_bcount; return(0); } /* or truncate if part of it fits */ sz = maxsz - bp->b_blkno; if (sz <= 0) { bp->b_error = EINVAL; goto bad; } bp->b_bcount = sz << DEV_BSHIFT; } /* calculate cylinder for disksort to order transfers with */ bp->b_pblkno = bp->b_blkno + p->p_offset; bp->b_cylin = bp->b_pblkno / lp->d_secpercyl; return(1); bad: bp->b_flags |= B_ERROR; return(-1); } Index: head/sys/pc98/pc98/pc98.c =================================================================== --- head/sys/pc98/pc98/pc98.c (revision 18264) +++ head/sys/pc98/pc98/pc98.c (revision 18265) @@ -1,1158 +1,1157 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 - * $Id: pc98.c,v 1.5 1996/09/07 02:14:09 asami Exp $ + * $Id: pc98.c,v 1.6 1996/09/10 09:38:19 asami Exp $ */ /* * code to manage AT bus * * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com): * Fixed uninitialized variable problem and added code to deal * with DMA page boundaries in isa_dmarangecheck(). Fixed word * mode DMA count compution and reorganized DMA setup code in * isa_dmastart() */ /* * modified for PC9801 by A.Kojima F.Ukai M.Ishii * Kyoto University Microcomputer Club (KMC) */ #include "opt_auto_eoi.h" #include "opt_ddb.h" #include #include -#include #include #include #include #include #include #include #include #include #include #ifdef PC98 #include #else #include #endif #include #include #include "vector.h" #ifdef PC98 unsigned char hireso; #endif /* ** Register definitions for DMA controller 1 (channels 0..3): */ #ifdef PC98 #define DMA1_CHN(c) (IO_DMA + (chan<<2)) /* addr reg for channel c */ #define DMA1_SMSK (IO_DMA + 0x14) /* single mask register */ #define DMA1_MODE (IO_DMA + 0x16) /* mode register */ #define DMA1_FFC (IO_DMA + 0x18) /* clear first/last FF */ #else #define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */ #define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */ #define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */ #define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */ #endif /* ** Register definitions for DMA controller 2 (channels 4..7): */ #define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */ #define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */ #define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */ #define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */ u_long *intr_countp[ICU_LEN]; inthand2_t *intr_handler[ICU_LEN]; u_int intr_mask[ICU_LEN]; u_int* intr_mptr[ICU_LEN]; int intr_unit[ICU_LEN]; #ifdef DDB unsigned int ddb_inb __P((unsigned int addr)); void ddb_outb __P((unsigned int addr, unsigned char dt)); #endif static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), &IDTVEC(fastintr2), &IDTVEC(fastintr3), &IDTVEC(fastintr4), &IDTVEC(fastintr5), &IDTVEC(fastintr6), &IDTVEC(fastintr7), &IDTVEC(fastintr8), &IDTVEC(fastintr9), &IDTVEC(fastintr10), &IDTVEC(fastintr11), &IDTVEC(fastintr12), &IDTVEC(fastintr13), &IDTVEC(fastintr14), &IDTVEC(fastintr15) }; static inthand_t *slowintr[ICU_LEN] = { &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) }; static void config_isadev __P((struct isa_device *isdp, u_int *mp)); static void config_isadev_c __P((struct isa_device *isdp, u_int *mp, int reconfig)); static void conflict __P((struct isa_device *dvp, struct isa_device *tmpdvp, int item, char const *whatnot, char const *reason, char const *format)); static int haveseen __P((struct isa_device *dvp, struct isa_device *tmpdvp, u_int checkbits)); static int isa_dmarangecheck __P((caddr_t va, u_int length, int chan)); static inthand2_t isa_strayintr; static void register_imask __P((struct isa_device *dvp, u_int mask)); /* * print a conflict message */ static void conflict(dvp, tmpdvp, item, whatnot, reason, format) struct isa_device *dvp; struct isa_device *tmpdvp; int item; char const *whatnot; char const *reason; char const *format; { printf("%s%d not %sed due to %s conflict with %s%d at ", dvp->id_driver->name, dvp->id_unit, whatnot, reason, tmpdvp->id_driver->name, tmpdvp->id_unit); printf(format, item); printf("\n"); } /* * Check to see if things are already in use, like IRQ's, I/O addresses * and Memory addresses. */ static int haveseen(dvp, tmpdvp, checkbits) struct isa_device *dvp; struct isa_device *tmpdvp; u_int checkbits; { /* * Only check against devices that have already been found and are not * unilaterally allowed to conflict anyway. */ if (tmpdvp->id_alive && !dvp->id_conflicts) { char const *whatnot; whatnot = checkbits & CC_ATTACH ? "attach" : "prob"; /* * Check for I/O address conflict. We can only check the * starting address of the device against the range of the * device that has already been probed since we do not * know how many I/O addresses this device uses. */ if (checkbits & CC_IOADDR && tmpdvp->id_alive != -1) { if ((dvp->id_iobase >= tmpdvp->id_iobase) && (dvp->id_iobase <= (tmpdvp->id_iobase + tmpdvp->id_alive - 1))) { conflict(dvp, tmpdvp, dvp->id_iobase, whatnot, "I/O address", "0x%x"); return 1; } } /* * Check for Memory address conflict. We can check for * range overlap, but it will not catch all cases since the * driver may adjust the msize paramater during probe, for * now we just check that the starting address does not * fall within any allocated region. * XXX could add a second check after the probe for overlap, * since at that time we would know the full range. * XXX KERNBASE is a hack, we should have vaddr in the table! */ if (checkbits & CC_MEMADDR && tmpdvp->id_maddr) { if ((KERNBASE + dvp->id_maddr >= tmpdvp->id_maddr) && (KERNBASE + dvp->id_maddr <= (tmpdvp->id_maddr + tmpdvp->id_msize - 1))) { conflict(dvp, tmpdvp, (int)dvp->id_maddr, whatnot, "maddr", "0x%x"); return 1; } } /* * Check for IRQ conflicts. */ if (checkbits & CC_IRQ && tmpdvp->id_irq) { if (tmpdvp->id_irq == dvp->id_irq) { conflict(dvp, tmpdvp, ffs(dvp->id_irq) - 1, whatnot, "irq", "%d"); return 1; } } /* * Check for DRQ conflicts. */ if (checkbits & CC_DRQ && tmpdvp->id_drq != -1) { if (tmpdvp->id_drq == dvp->id_drq) { conflict(dvp, tmpdvp, dvp->id_drq, whatnot, "drq", "%d"); return 1; } } } return 0; } /* * Search through all the isa_devtab_* tables looking for anything that * conflicts with the current device. */ int haveseen_isadev(dvp, checkbits) struct isa_device *dvp; u_int checkbits; { struct isa_device *tmpdvp; int status = 0; for (tmpdvp = isa_devtab_tty; tmpdvp->id_driver; tmpdvp++) { status |= haveseen(dvp, tmpdvp, checkbits); if (status) return status; } for (tmpdvp = isa_devtab_bio; tmpdvp->id_driver; tmpdvp++) { status |= haveseen(dvp, tmpdvp, checkbits); if (status) return status; } for (tmpdvp = isa_devtab_net; tmpdvp->id_driver; tmpdvp++) { status |= haveseen(dvp, tmpdvp, checkbits); if (status) return status; } for (tmpdvp = isa_devtab_null; tmpdvp->id_driver; tmpdvp++) { status |= haveseen(dvp, tmpdvp, checkbits); if (status) return status; } return(status); } /* * Configure all ISA devices */ void isa_configure() { struct isa_device *dvp; splhigh(); printf("Probing for devices on the ISA bus:\n"); /* First probe all the sensitive probes */ for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) if (dvp->id_driver->sensitive_hw) config_isadev(dvp, &tty_imask); for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) if (dvp->id_driver->sensitive_hw) config_isadev(dvp, &bio_imask); for (dvp = isa_devtab_net; dvp->id_driver; dvp++) if (dvp->id_driver->sensitive_hw) config_isadev(dvp, &net_imask); for (dvp = isa_devtab_null; dvp->id_driver; dvp++) if (dvp->id_driver->sensitive_hw) config_isadev(dvp, (u_int *)NULL); /* Then all the bad ones */ for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) if (!dvp->id_driver->sensitive_hw) config_isadev(dvp, &tty_imask); for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) if (!dvp->id_driver->sensitive_hw) config_isadev(dvp, &bio_imask); for (dvp = isa_devtab_net; dvp->id_driver; dvp++) if (!dvp->id_driver->sensitive_hw) config_isadev(dvp, &net_imask); for (dvp = isa_devtab_null; dvp->id_driver; dvp++) if (!dvp->id_driver->sensitive_hw) 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 net_imask when the line is * switched to SLIPDISC, and then remove it when it is switched away from * SLIPDISC. No need to block out ALL ttys during a splimp when only one * of them is running slip. * * XXX actually, blocking all ttys during a splimp doesn't matter so much * with sio because the serial interrupt layer doesn't use tty_imask. Only * non-serial ttys suffer. It's more stupid that ALL 'net's are blocked * during spltty. */ #include "sl.h" #if NSL > 0 net_imask |= tty_imask; tty_imask = net_imask; #endif /* bio_imask |= tty_imask ; can some tty devices use buffers? */ if (bootverbose) printf("imasks: bio %x, tty %x, net %x\n", bio_imask, tty_imask, net_imask); /* * Finish initializing intr_mask[]. Note that the partly * constructed masks aren't actually used since we're at splhigh. * For fully dynamic initialization, register_intr() and * unregister_intr() will have to adjust the masks for _all_ * interrupts and for tty_imask, etc. */ for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) register_imask(dvp, tty_imask); for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) register_imask(dvp, bio_imask); for (dvp = isa_devtab_net; dvp->id_driver; dvp++) register_imask(dvp, net_imask); for (dvp = isa_devtab_null; dvp->id_driver; dvp++) register_imask(dvp, SWI_CLOCK_MASK); spl0(); } /* * Configure an ISA device. */ static void config_isadev(isdp, mp) struct isa_device *isdp; u_int *mp; { config_isadev_c(isdp, mp, 0); } void reconfig_isadev(isdp, mp) struct isa_device *isdp; u_int *mp; { config_isadev_c(isdp, mp, 1); } static void config_isadev_c(isdp, mp, reconfig) struct isa_device *isdp; u_int *mp; int reconfig; { u_int checkbits; int id_alive; int last_alive; struct isa_driver *dp = isdp->id_driver; if (!isdp->id_enabled) { printf("%s%d: disabled, not probed.\n", dp->name, isdp->id_unit); return; } checkbits = CC_DRQ | CC_IOADDR | CC_MEMADDR; if (!reconfig && haveseen_isadev(isdp, checkbits)) return; if (!reconfig && isdp->id_maddr) { isdp->id_maddr -= 0xa0000; /* XXX should be a define */ isdp->id_maddr += atdevbase; } if (reconfig) { last_alive = isdp->id_alive; isdp->id_reconfig = 1; } else { last_alive = 0; isdp->id_reconfig = 0; } id_alive = (*dp->probe)(isdp); if (id_alive) { /* * Only print the I/O address range if id_alive != -1 * Right now this is a temporary fix just for the new * NPX code so that if it finds a 486 that can use trap * 16 it will not report I/O addresses. * Rod Grimes 04/26/94 */ if (!isdp->id_reconfig) { printf("%s%d", dp->name, isdp->id_unit); if (id_alive != -1) { printf(" at 0x%x", isdp->id_iobase); if (isdp->id_iobase + id_alive - 1 != isdp->id_iobase) { printf("-0x%x", isdp->id_iobase + id_alive - 1); } } if (isdp->id_irq) printf(" irq %d", ffs(isdp->id_irq) - 1); if (isdp->id_drq != -1) printf(" drq %d", isdp->id_drq); if (isdp->id_maddr) printf(" maddr 0x%lx", kvtop(isdp->id_maddr)); if (isdp->id_msize) printf(" msize %d", isdp->id_msize); if (isdp->id_flags) printf(" flags 0x%x", isdp->id_flags); #ifdef PC98 printf (" on isa"); #else if (isdp->id_iobase && !(isdp->id_iobase & 0xf300)) { printf(" on motherboard"); } else if (isdp->id_iobase >= 0x1000 && !(isdp->id_iobase & 0x300)) { printf (" on eisa slot %d", isdp->id_iobase >> 12); } else { printf (" on isa"); } #endif printf("\n"); /* * Check for conflicts again. The driver may have * changed *dvp. We should weaken the early check * since the driver may have been able to change * *dvp to avoid conflicts if given a chance. We * already skip the early check for IRQs and force * a check for IRQs in the next group of checks. */ checkbits |= CC_IRQ; if (haveseen_isadev(isdp, checkbits)) return; isdp->id_alive = id_alive; } (*dp->attach)(isdp); if (isdp->id_irq) { if (mp) INTRMASK(*mp, isdp->id_irq); register_intr(ffs(isdp->id_irq) - 1, isdp->id_id, isdp->id_ri_flags, isdp->id_intr, mp, isdp->id_unit); INTREN(isdp->id_irq); } } else { if (isdp->id_reconfig) { (*dp->attach)(isdp); /* reconfiguration attach */ } if (!last_alive) { if (!isdp->id_reconfig) { printf("%s%d not found", dp->name, isdp->id_unit); if (isdp->id_iobase) { printf(" at 0x%x", isdp->id_iobase); } printf("\n"); } } else { /* This code has not been tested.... */ if (isdp->id_irq) { INTRDIS(isdp->id_irq); unregister_intr(ffs(isdp->id_irq) - 1, isdp->id_intr); if (mp) INTRUNMASK(*mp, isdp->id_irq); } } } } /* * Fill in default interrupt table (in case of spuruious interrupt * during configuration of kernel, setup interrupt control unit */ void isa_defaultirq() { int i; /* icu vectors */ for (i = 0; i < ICU_LEN; i++) unregister_intr(i, (inthand2_t *)NULL); /* initialize 8259's */ #ifdef PC98 outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ outb(IO_ICU1+2, NRSVIDT); /* starting at this vector index */ outb(IO_ICU1+2, 1<<7); /* slave on line 7 */ #ifdef AUTO_EOI_1 outb(IO_ICU1+2, 0x1f); /* (master) auto EOI, 8086 mode */ #else outb(IO_ICU1+2, 0x1d); /* (master) 8086 mode */ #endif outb(IO_ICU1+2, 0x7f); /* leave interrupts masked */ outb(IO_ICU1, 0x0a); /* default to IRR on read */ outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ outb(IO_ICU2+2, NRSVIDT+8); /* staring at this vector index */ outb(IO_ICU2+2,7); /* my slave id is 7 */ outb(IO_ICU2+2,9); /* 8086 mode */ outb(IO_ICU2+2, 0xff); /* leave interrupts masked */ outb(IO_ICU2, 0x0a); /* default to IRR on read */ #else outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ outb(IO_ICU1+1, NRSVIDT); /* starting at this vector index */ outb(IO_ICU1+1, 1<<2); /* slave on line 2 */ #ifdef AUTO_EOI_1 outb(IO_ICU1+1, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU1+1, 1); /* 8086 mode */ #endif outb(IO_ICU1+1, 0xff); /* leave interrupts masked */ outb(IO_ICU1, 0x0a); /* default to IRR on read */ outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ outb(IO_ICU2+1, NRSVIDT+8); /* staring at this vector index */ outb(IO_ICU2+1,2); /* my slave id is 2 */ #ifdef AUTO_EOI_2 outb(IO_ICU2+1, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU2+1,1); /* 8086 mode */ #endif outb(IO_ICU2+1, 0xff); /* leave interrupts masked */ outb(IO_ICU2, 0x0a); /* default to IRR on read */ #endif } #ifdef PC98 caddr_t dma_bouncebuf[4]; static u_int dma_bouncebufsize[4]; #else caddr_t dma_bouncebuf[8]; static u_int dma_bouncebufsize[8]; #endif static u_int8_t dma_bounced = 0; static u_int8_t dma_busy = 0; /* Used in isa_dmastart() */ static u_int8_t dma_inuse = 0; /* User for acquire/release */ #ifdef PC98 #define VALID_DMA_MASK (3) #else #define VALID_DMA_MASK (7) #endif /* high byte of address is stored in this port for i-th dma channel */ #ifdef PC98 short dmapageport[4] = { 0x27, 0x21, 0x23, 0x25 }; #else /* IBM-PC */ static short dmapageport[8] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; #endif /* * Setup a DMA channel's bounce buffer. */ void isa_dmainit(chan, bouncebufsize) int chan; u_int bouncebufsize; { void *buf; #ifdef DIAGNOSTIC if (chan & ~VALID_DMA_MASK) panic("isa_dmainit: channel out of range"); if (dma_bouncebuf[chan] != NULL) panic("isa_dmainit: impossible request"); #endif dma_bouncebufsize[chan] = bouncebufsize; /* Try malloc() first. It works better if it works. */ buf = malloc(bouncebufsize, M_DEVBUF, M_NOWAIT); if (buf != NULL) { if (isa_dmarangecheck(buf, bouncebufsize, chan) == 0) { dma_bouncebuf[chan] = buf; return; } free(buf, M_DEVBUF); } buf = contigmalloc(bouncebufsize, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 1ul, chan & 4 ? 0x20000ul : 0x10000ul); if (buf == NULL) printf("isa_dmainit(%d, %d) failed\n", chan, bouncebufsize); else dma_bouncebuf[chan] = buf; } /* * Register a DMA channel's usage. Usually called from a device driver * in open() or during it's initialization. */ int isa_dma_acquire(chan) int chan; { #ifdef DIAGNOSTIC if (chan & ~VALID_DMA_MASK) panic("isa_dma_acquire: channel out of range"); #endif if (dma_inuse & (1 << chan)) { printf("isa_dma_acquire: channel %d already in use\n", chan); return (EBUSY); } dma_inuse |= (1 << chan); return (0); } /* * Unregister a DMA channel's usage. Usually called from a device driver * during close() or during it's shutdown. */ void isa_dma_release(chan) int chan; { #ifdef DIAGNOSTIC if (chan & ~VALID_DMA_MASK) panic("isa_dma_release: channel out of range"); if (dma_inuse & (1 << chan) == 0) printf("isa_dma_release: channel %d not in use\n", chan); #endif if (dma_busy & (1 << chan)) { dma_busy &= ~(1 << chan); /* * XXX We should also do "dma_bounced &= (1 << chan);" * because we are acting on behalf of isa_dmadone() which * was not called to end the last DMA operation. This does * not matter now, but it may in the future. */ } dma_inuse &= ~(1 << chan); } #ifndef PC98 /* * isa_dmacascade(): program 8237 DMA controller channel to accept * external dma control by a board. */ void isa_dmacascade(chan) int chan; { #ifdef DIAGNOSTIC if (chan & ~VALID_DMA_MASK) panic("isa_dmacascade: channel out of range"); #endif /* set dma channel mode, and set dma channel mode */ if ((chan & 4) == 0) { outb(DMA1_MODE, DMA37MD_CASCADE | chan); outb(DMA1_SMSK, chan); } else { outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3)); outb(DMA2_SMSK, chan & 3); } } #endif /* * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment * problems by using a bounce buffer. */ void isa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan) { vm_offset_t phys; int waport; caddr_t newaddr; #ifdef DIAGNOSTIC if (chan & ~VALID_DMA_MASK) panic("isa_dmastart: channel out of range"); if ((chan < 4 && nbytes > (1<<16)) || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1))) panic("isa_dmastart: impossible request"); if (dma_inuse & (1 << chan) == 0) printf("isa_dmastart: channel %d not acquired\n", chan); #endif if (dma_busy & (1 << chan)) printf("isa_dmastart: channel %d busy\n", chan); dma_busy |= (1 << chan); if (isa_dmarangecheck(addr, nbytes, chan)) { if (dma_bouncebuf[chan] == NULL || dma_bouncebufsize[chan] < nbytes) panic("isa_dmastart: bad bounce buffer"); dma_bounced |= (1 << chan); newaddr = dma_bouncebuf[chan]; /* copy bounce buffer on write */ if (!(flags & B_READ)) bcopy(addr, newaddr, nbytes); addr = newaddr; } /* translate to physical */ phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); #ifdef CYRIX_5X86 asm("wbinvd"); /* wbinvd (WB cache flush) */ #endif #ifndef PC98 if ((chan & 4) == 0) { #endif /* set dma channel mode, and reset address ff */ /* If B_RAW flag is set, then use autoinitialise mode */ if (flags & B_RAW) { if (flags & B_READ) outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_WRITE|chan); else outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_READ|chan); } else if (flags & B_READ) outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan); else outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan); outb(DMA1_FFC, 0); /* send start address */ waport = DMA1_CHN(chan); outb(waport, phys); outb(waport, phys>>8); outb(dmapageport[chan], phys>>16); /* send count */ outb(waport + 2, --nbytes); /* 0x3, 0x7, 0xb, 0xf */ outb(waport + 2, nbytes>>8); /* unmask channel */ outb(DMA1_SMSK, chan & 3); #ifndef PC98 } else { /* * Program one of DMA channels 4..7. These are * word mode channels. */ /* set dma channel mode, and reset address ff */ /* If B_RAW flag is set, then use autoinitialise mode */ if (flags & B_RAW) { if (flags & B_READ) outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_WRITE|(chan&3)); else outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_READ|(chan&3)); } else if (flags & B_READ) outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); else outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); outb(DMA2_FFC, 0); /* send start address */ waport = DMA2_CHN(chan - 4); outb(waport, phys>>1); outb(waport, phys>>9); outb(dmapageport[chan], phys>>16); /* send count */ nbytes >>= 1; outb(waport + 2, --nbytes); outb(waport + 2, nbytes>>8); /* unmask channel */ outb(DMA2_SMSK, chan); } #endif } void isa_dmadone(int flags, caddr_t addr, int nbytes, int chan) { #if defined(CYRIX_486DLC) || defined(IBM_486SLC) if (flags & B_READ) { /* cache flush only after reading 92/12/9 by A.Kojima */ asm(" .byte 0x0f,0x08"); /* invd (cache flush) */ } #endif #ifdef DIAGNOSTIC if (chan & ~VALID_DMA_MASK) panic("isa_dmadone: channel out of range"); if (dma_inuse & (1 << chan) == 0) printf("isa_dmadone: channel %d not acquired\n", chan); #endif #if 0 /* * XXX This should be checked, but drivers like ad1848 only call * isa_dmastart() once because they use Auto DMA mode. If we * leave this in, drivers that do this will print this continuously. */ if (dma_busy & (1 << chan) == 0) printf("isa_dmadone: channel %d not busy\n", chan); #endif if (dma_bounced & (1 << chan)) { /* copy bounce buffer on read */ if (flags & B_READ) bcopy(dma_bouncebuf[chan], addr, nbytes); dma_bounced &= ~(1 << chan); } dma_busy &= ~(1 << chan); } /* * Check for problems with the address range of a DMA transfer * (non-contiguous physical pages, outside of bus address space, * crossing DMA page boundaries). * Return true if special handling needed. */ int isa_dmarangecheck(caddr_t va, u_int length, int chan) { vm_offset_t phys, priorpage = 0, endva; u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); endva = (vm_offset_t)round_page(va + length); for (; va < (caddr_t) endva ; va += PAGE_SIZE) { phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va)); #ifdef EPSON_BOUNCEDMA #define ISARAM_END 0xf00000 #else #define ISARAM_END RAM_END #endif if (phys == 0) panic("isa_dmacheck: no physical page present"); if (phys >= ISARAM_END) return (1); if (priorpage) { if (priorpage + PAGE_SIZE != phys) return (1); /* check if crossing a DMA page boundary */ if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk) return (1); } priorpage = phys; } return (0); } #ifdef PC98 #define NMI_PARITY 0x04 #define NMI_EPARITY 0x02 #else #define NMI_PARITY (1 << 7) #define NMI_IOCHAN (1 << 6) #define ENMI_WATCHDOG (1 << 7) #define ENMI_BUSTIMER (1 << 6) #define ENMI_IOSTATUS (1 << 5) #endif /* * Handle a NMI, possibly a machine check. * return true to panic system, false to ignore. */ int isa_nmi(cd) int cd; { #ifdef PC98 int port = inb(0x33); if (epson_machine_id == 0x20) epson_outb(0xc16, epson_inb(0xc16) | 0x1); if (port & NMI_PARITY) { panic("BASE RAM parity error, likely hardware failure."); } else if (port & NMI_EPARITY) { panic("EXTENDED RAM parity error, likely hardware failure."); } else { printf("\nNMI Resume ??\n"); return(0); } #else /* IBM-PC */ int isa_port = inb(0x61); int eisa_port = inb(0x461); if(isa_port & NMI_PARITY) { panic("RAM parity error, likely hardware failure."); } else if(isa_port & NMI_IOCHAN) { panic("I/O channel check, likely hardware failure."); } else if(eisa_port & ENMI_WATCHDOG) { panic("EISA watchdog timer expired, likely hardware failure."); } else if(eisa_port & ENMI_BUSTIMER) { panic("EISA bus timeout, likely hardware failure."); } else if(eisa_port & ENMI_IOSTATUS) { panic("EISA I/O port status error."); } else { printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port); return(0); } #endif } /* * Caught a stray interrupt, notify */ static void isa_strayintr(d) int d; { /* DON'T BOTHER FOR NOW! */ /* for some reason, we get bursts of intr #7, even if not enabled! */ /* * Well the reason you got bursts of intr #7 is because someone * raised an interrupt line and dropped it before the 8259 could * prioritize it. This is documented in the intel data book. This * means you have BAD hardware! I have changed this so that only * the first 5 get logged, then it quits logging them, and puts * out a special message. rgrimes 3/25/1993 */ /* * XXX TODO print a different message for #7 if it is for a * glitch. Glitches can be distinguished from real #7's by * testing that the in-service bit is _not_ set. The test * must be done before sending an EOI so it can't be done if * we are using AUTO_EOI_1. */ if (intrcnt[NR_DEVICES + d] <= 5) log(LOG_ERR, "stray irq %d\n", d); if (intrcnt[NR_DEVICES + d] == 5) log(LOG_CRIT, "too many stray irq %d's; not logging any more\n", d); } /* * Find the highest priority enabled display device. Since we can't * distinguish display devices from ttys, depend on display devices * being sensitive and before sensitive non-display devices (if any) * in isa_devtab_tty. * * XXX we should add capability flags IAMDISPLAY and ISUPPORTCONSOLES. */ struct isa_device * find_display() { struct isa_device *dvp; for (dvp = isa_devtab_tty; dvp->id_driver != NULL; dvp++) if (dvp->id_driver->sensitive_hw && dvp->id_enabled) return (dvp); return (NULL); } /* * find an ISA device in a given isa_devtab_* table, given * the table to search, the expected id_driver entry, and the unit number. * * this function is defined in isa_device.h, and this location is debatable; * i put it there because it's useless w/o, and directly operates on * the other stuff in that file. * */ struct isa_device *find_isadev(table, driverp, unit) struct isa_device *table; struct isa_driver *driverp; int unit; { if (driverp == NULL) /* sanity check */ return NULL; while ((table->id_driver != driverp) || (table->id_unit != unit)) { if (table->id_driver == 0) return NULL; table++; } return table; } /* * Return nonzero if a (masked) irq is pending for a given device. */ int isa_irq_pending(dvp) struct isa_device *dvp; { unsigned id_irq; id_irq = dvp->id_irq; if (id_irq & 0xff) return (inb(IO_ICU1) & id_irq); return (inb(IO_ICU2) & (id_irq >> 8)); } int update_intr_masks(void) { int intr, n=0; u_int mask,*maskptr; for (intr=0; intr < ICU_LEN; intr ++) { #ifdef PC98 if (intr==7) continue; #else if (intr==2) continue; #endif maskptr = intr_mptr[intr]; if (!maskptr) continue; *maskptr |= 1 << intr; mask = *maskptr; if (mask != intr_mask[intr]) { #if 0 printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n", intr, intr_mask[intr], mask, maskptr); #endif intr_mask[intr]=mask; n++; } } return (n); } int register_intr(intr, device_id, flags, handler, maskptr, unit) int intr; int device_id; u_int flags; inthand2_t *handler; u_int *maskptr; int unit; { char *cp; u_long ef; int id; u_int mask = (maskptr ? *maskptr : 0); #ifdef PC98 if ((u_int)intr >= ICU_LEN || intr == 7 #else if ((u_int)intr >= ICU_LEN || intr == 2 #endif || (u_int)device_id >= NR_DEVICES) return (EINVAL); if (intr_handler[intr] != isa_strayintr) return (EBUSY); ef = read_eflags(); disable_intr(); intr_countp[intr] = &intrcnt[device_id]; intr_handler[intr] = handler; intr_mptr[intr] = maskptr; intr_mask[intr] = mask | (1 << intr); intr_unit[intr] = unit; setidt(ICU_OFFSET + intr, flags & RI_FAST ? fastintr[intr] : slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); write_eflags(ef); for (cp = intrnames, id = 0; id <= device_id; id++) while (*cp++ != '\0') ; if (cp > eintrnames) return (0); if (intr < 10) { cp[-3] = intr + '0'; cp[-2] = ' '; } else { cp[-3] = '1'; cp[-2] = intr - 10 + '0'; } return (0); } static void register_imask(dvp, mask) struct isa_device *dvp; u_int mask; { if (dvp->id_alive && dvp->id_irq) { int intr; intr = ffs(dvp->id_irq) - 1; intr_mask[intr] = mask | (1 <= ICU_LEN || handler != intr_handler[intr]) return (EINVAL); ef = read_eflags(); disable_intr(); intr_countp[intr] = &intrcnt[NR_DEVICES + intr]; intr_handler[intr] = isa_strayintr; intr_mptr[intr] = NULL; intr_mask[intr] = HWI_MASK | SWI_MASK; intr_unit[intr] = intr; setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); write_eflags(ef); return (0); } #ifdef DDB unsigned int ddb_inb(unsigned int addr) { return inb(addr); } void ddb_outb(unsigned int addr, unsigned char dt) { outb(addr, dt); } #endif Index: head/sys/pc98/pc98/pc98.h =================================================================== --- head/sys/pc98/pc98/pc98.h (revision 18264) +++ head/sys/pc98/pc98/pc98.h (revision 18265) @@ -1,223 +1,270 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)isa.h 5.7 (Berkeley) 5/9/91 - * $Id: pc98.h,v 1.2 1996/09/03 10:23:48 asami Exp $ + * $Id: pc98.h,v 1.3 1996/09/10 09:38:21 asami Exp $ */ #ifndef _PC98_PC98_PC98_H_ #define _PC98_PC98_PC98_H_ /* BEWARE: Included in both assembler and C code */ /* * PC98 Bus conventions */ /* * PC98 Bus conventions * modified for PC9801 by A.Kojima F.Ukai M.Ishii * Kyoto University Microcomputer Club (KMC) */ /* * Input / Output Port Assignments */ #ifndef IO_ISABEGIN #define IO_ISABEGIN 0x000 /* 0x000 - Beginning of I/O Registers */ /* PC98 IO address ... very dirty (^_^; */ #define IO_ICU1 0x000 /* 8259A Interrupt Controller #1 */ #define IO_DMA 0x001 /* 8237A DMA Controller */ #define IO_ICU2 0x008 /* 8259A Interrupt Controller #2 */ #define IO_RTC 0x020 /* 4990A RTC */ #define IO_DMAPG 0x021 /* DMA Page Registers */ #define IO_COM1 0x030 /* 8251A RS232C serial I/O (int) */ #define IO_SYSPORT 0x031 /* 8255A System Port */ #define IO_PPI 0x035 /* Programmable Peripheral Interface */ #define IO_LPT 0x040 /* 8255A Printer Port */ #define IO_KBD 0x041 /* 8251A Keyboard */ #define IO_NMI 0x050 /* NMI Control */ #define IO_WAIT 0x05F /* WAIT 0.6 us */ #define IO_GDC1 0x060 /* 7220 GDC Text Control */ #define IO_TIMER 0x071 /* 8253C Timer */ #define IO_SASI 0x080 /* SASI Hard Disk Controller */ #define IO_FD1 0x090 /* 765A 1MB FDC */ #define IO_GDC2 0x0a0 /* 7220 GDC Graphic Control */ #define IO_CGROM 0x0a1 /* Character ROM */ #define IO_COM2 0x0b1 /* 8251A RS232C serial I/O (ext) */ #define IO_COM3 0x0b9 /* 8251A RS232C serial I/O (ext) */ #define IO_FDPORT 0x0be /* FD I/F port (1M<->640K,EMTON) */ #define IO_FD2 0x0c8 /* 765A 640KB FDC */ #define IO_SIO1 0x0d0 /* MC16550II ext RS232C */ #define IO_REEST 0x0F0 /* CPU FPU reset */ #define IO_A2OEN 0x0F2 /* A20 enable */ #define IO_A20CT 0x0F6 /* A20 control enable/disable */ #define IO_NPX 0x0F8 /* Numeric Coprocessor */ #define IO_SOUND 0x188 /* YM2203 FM sound board */ #define IO_EGC 0x4a0 /* 7220 GDC Graphic Control */ #define IO_SCSI 0xcc0 /* SCSI Controller */ #define IO_SIO2 0x8d0 /* MC16550II ext RS232C */ #define IO_BEEPF 0x3fdb /* beep frequency */ #define IO_MOUSE 0x7fd9 /* mouse */ #define IO_BMS 0x7fd9 /* Bus Mouse */ #define IO_MSE 0x7fd9 /* Bus Mouse */ #define IO_MOUSETM 0xdfbd /* mouse timer */ #define IO_WD1_NEC 0x640 /* 98note IDE Hard disk controller */ #define IO_WD1_EPSON 0x80 /* 386note Hard disk controller */ #define IO_WD1 IO_WD1_NEC /* IDE Hard disk controller */ #define IO_ISAEND 0xFFFF /* - 0x3FF End of I/O Registers */ #endif /* !IO_ISABEGIN */ /* * Input / Output Port Sizes - these are from several sources, and tend * to be the larger of what was found, ie COM ports can be 4, but some * boards do not fully decode the address, thus 8 ports are used. */ #ifndef IO_ISASIZES #define IO_ISASIZES #define IO_COMSIZE 8 /* 8250, 16X50 com controllers (4?) */ #define IO_CGASIZE 16 /* CGA controllers */ #define IO_DMASIZE 16 /* 8237 DMA controllers */ #define IO_DPGSIZE 32 /* 74LS612 DMA page registers */ #define IO_FDCSIZE 8 /* Nec765 floppy controllers */ #define IO_WDCSIZE 8 /* WD compatible disk controllers */ #define IO_GAMSIZE 16 /* AT compatible game controllers */ #define IO_ICUSIZE 16 /* 8259A interrupt controllers */ #define IO_KBDSIZE 16 /* 8042 Keyboard controllers */ #define IO_LPTSIZE 8 /* LPT controllers, some use only 4 */ #define IO_MDASIZE 16 /* Monochrome display controllers */ #define IO_RTCSIZE 16 /* CMOS real time clock, NMI control */ #define IO_TMRSIZE 16 /* 8253 programmable timers */ #define IO_NPXSIZE 16 /* 80387/80487 NPX registers */ #define IO_VGASIZE 16 /* VGA controllers */ #define IO_EISASIZE 4096 /* EISA controllers */ #define IO_PMPSIZE 2 /* 82347 power management peripheral */ #endif /* !IO_ISASIZES */ /* * Input / Output Memory Physical Addresses */ #ifndef IOM_BEGIN #define IOM_BEGIN 0x0a0000 /* Start of I/O Memory "hole" */ #define IOM_END 0x100000 /* End of I/O Memory "hole" */ #define IOM_SIZE (IOM_END - IOM_BEGIN) #endif /* !RAM_BEGIN */ /* * RAM Physical Address Space (ignoring the above mentioned "hole") */ #ifndef RAM_BEGIN #define RAM_BEGIN 0x0000000 /* Start of RAM Memory */ #ifdef EPSON_BOUNCEDMA #define RAM_END 0x0f00000 /* End of EPSON GR?? RAM Memory */ #else #define RAM_END 0x1000000 /* End of RAM Memory */ #endif #define RAM_SIZE (RAM_END - RAM_BEGIN) #endif /* !RAM_BEGIN */ #ifndef PC98 /* IBM-PC */ /* * Oddball Physical Memory Addresses */ #ifndef COMPAQ_RAMRELOC #define COMPAQ_RAMRELOC 0x80c00000 /* Compaq RAM relocation/diag */ #define COMPAQ_RAMSETUP 0x80c00002 /* Compaq RAM setup */ #define WEITEK_FPU 0xC0000000 /* WTL 2167 */ #define CYRIX_EMC 0xC0000000 /* Cyrix EMC */ #endif COMPAQ_RAMRELOC #endif #define PC98_VECTOR_SIZE (0x400) #define PC98_SYSTEM_PARAMETER_SIZE (0x230) #define PC98_SAVE_AREA(highreso_flag) (0xa1000) #define PC98_SAVE_AREA_ADDRESS (0x10) #define OFS_BOOT_boothowto 0x210 #define OFS_BOOT_bootdev 0x214 #define OFS_BOOT_cyloffset 0x218 #define OFS_WD_BIOS_SECSIZE(i) (0x200+(i)*6) #define OFS_WD_BIOS_NCYL(i) (0x202+(i)*6) #define OFS_WD_BIOS_HEAD(i) (0x205+(i)*6) #define OFS_WD_BIOS_SEC(i) (0x204+(i)*6) #define OFS_pc98_machine_type 0x220 #define OFS_epson_machine_id 0x224 #define OFS_epson_bios_id 0x225 #define OFS_epson_system_type 0x226 #define M_NEC_PC98 0x0001 #define M_EPSON_PC98 0x0002 #define M_NOT_H98 0x0010 #define M_H98 0x0020 #define M_NOTE 0x0040 #define M_NORMAL 0x1000 #define M_HIGHRESO 0x2000 #define M_8M 0x8000 #if defined(KERNEL) && !defined(LOCORE) /* BIOS parameter block */ extern unsigned char pc98_system_parameter[]; /* in locore.c */ #define PC98_SYSTEM_PARAMETER(x) pc98_system_parameter[(x)-0x400] #define BOOT_boothowto (*(unsigned long*)(&pc98_system_parameter[OFS_BOOT_boothowto])) #define BOOT_bootdev (*(unsigned long*)(&pc98_system_parameter[OFS_BOOT_bootdev])) #define BOOT_cyloffset (*(unsigned long*)(&pc98_system_parameter[OFS_BOOT_cyloffset])) #define WD_BIOS_SECSIZE(i) (*(unsigned short*)(&pc98_system_parameter[OFS_WD_BIOS_SECSIZE(i)])) #define WD_BIOS_NCYL(i) (*(unsigned short*)(&pc98_system_parameter[OFS_WD_BIOS_NCYL(i)])) #define WD_BIOS_HEAD(i) (pc98_system_parameter[OFS_WD_BIOS_HEAD(i)]) #define WD_BIOS_SEC(i) (pc98_system_parameter[OFS_WD_BIOS_SEC(i)]) #define pc98_machine_type (*(unsigned long*)&pc98_system_parameter[OFS_pc98_machine_type]) #define epson_machine_id (pc98_system_parameter[OFS_epson_machine_id]) #define epson_bios_id (pc98_system_parameter[OFS_epson_bios_id]) #define epson_system_type (pc98_system_parameter[OFS_epson_system_type]) # define PC98_TYPE_CHECK(x) ((pc98_machine_type & (x)) == (x)) + +#include + +static inline u_char +epson_inb(u_int port) +{ + u_char data; + + outb(0x43f, 0x42); + data = inb(port); + outb(0x43f, 0x40); + return (data); +} + +static inline void +epson_outb(u_int port, u_char data) +{ + outb(0x43f, 0x42); + outb(port,data); + outb(0x43f, 0x40); +} + +static inline void +epson_insw(u_int port, void *addr, size_t cnt) +{ + int s; + + s = splbio(); + outb(0x43f, 0x42); + disable_intr(); + insw((u_int)port, (void *)addr, (size_t)cnt); + outb(0x43f, 0x40); + splx(s); +} + +static inline void +epson_outsw(u_int port, void *addr, size_t cnt) +{ + int s; + + s = splbio(); + outb(0x43f, 0x42); + disable_intr(); + outsw((u_int)port, (void *)addr, (size_t)cnt); + outb(0x43f, 0x40); + splx(s); +} #endif /* KERNEL */ /* * Obtained from NetBSD/pc98 */ #define MADDRUNK -1 #endif /* !_PC98_PC98_PC98_H_ */ Index: head/sys/pc98/pc98/sio.c =================================================================== --- head/sys/pc98/pc98/sio.c (revision 18264) +++ head/sys/pc98/pc98/sio.c (revision 18265) @@ -1,3838 +1,3837 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.5 1996/09/07 02:14:23 asami Exp $ + * $Id: sio.c,v 1.6 1996/09/10 09:38:34 asami Exp $ */ #include "opt_comconsole.h" #include "opt_ddb.h" #include "opt_sio.h" #include "sio.h" /* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. * * Changes for PC-Card integration: * - Added PC-Card driver table and handlers */ /*=============================================================== * 386BSD(98),FreeBSD-1.1x(98) com driver. * ----- * modified for PC9801 by M.Ishii * Kyoto University Microcomputer Club (KMC) * Chou "TEFUTEFU" Hirotomi * Kyoto Univ. the faculty of medicine *=============================================================== * FreeBSD-2.0.1(98) sio driver. * ----- * modified for pc98 Internal i8251 and MICRO CORE MC16550II * T.Koike(hfc01340@niftyserve.or.jp) * implement kernel device configuration * aizu@orient.center.nitech.ac.jp * * Notes. * ----- * PC98 localization based on 386BSD(98) com driver. Using its PC98 local * functions. * This driver is under debugging,has bugs. * * 1) config * options COM_MULTIPORT #if using MC16550II * device sio0 at nec? port 0x30 tty irq 4 vector siointr #internal * device sio1 at nec? port 0xd2 tty irq 5 flags 0x101 vector siointr #mc1 * device sio2 at nec? port 0x8d2 tty flags 0x101 vector siointr #mc2 * # ~~~~~iobase ~~multi port flag * # ~ master device is sio1 * 2) device * cd /dev; MAKEDEV ttyd0 ttyd1 .. * 3) /etc/rc.serial * 57600bps is too fast for sio0(internal8251) * my ex. * #set default speed 9600 * modem() * : * stty last update: 15 Sep.1995 * * How to configure... * # options COM_MULTIPORT # support for MICROCORE MC16550II * ... comment-out this line, which will conflict with B98_01. * options "B98_01" # support for AIWA B98-01 * device sio1 at nec? port 0x00d1 tty irq ? vector siointr * device sio2 at nec? port 0x00d5 tty irq ? vector siointr * ... you can leave these lines `irq ?', irq will be autodetected. */ #ifdef PC98 #define MC16550 0 #define COM_IF_INTERNAL 1 #if 0 #define COM_IF_PC9861K 2 #define COM_IF_PIO9032B 3 #endif #ifdef B98_01 #undef COM_MULTIPORT /* COM_MULTIPORT will conflict with B98_01 */ #define COM_IF_B98_01 4 #endif /* B98_01 */ #endif /* PC98 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #ifdef PC98 #include #include #include #include -#include -#include +#include #else #include #include #include +#endif #ifdef COM_ESP #include #endif #include -#endif #include "crd.h" #if NCRD > 0 #include #include #include #endif #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RB_I_HIGH_WATER (TTYHOG - 2 * RS_IBUFSIZE) #define RS_IBUFSIZE 256 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) #ifdef COM_MULTIPORT /* checks in flags for multiport and which is multiport "master chip" * for a given card */ #define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01) #define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff) #define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) #endif /* COM_MULTIPORT */ #define COM_LOSESOUTINTS(dev) ((dev)->id_flags & 0x08) #define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) #define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) #ifndef PC98 #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ #endif /* !PC98 */ /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches * high water, or when the high level requests it. * The high level is notified immediately (rather than at a later clock tick) * when this watermark is reached. * The buffer size is chosen so the watermark should almost never be reached. * The low watermark is invisibly 0 since the buffer is always emptied all at * once. */ #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * siostop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_int Port_t; /* hardware port */ typedef u_char bool_t; /* boolean */ /* queue of linear buffers */ struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */ }; /* com device structure */ struct com_s { u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ #ifdef COM_ESP bool_t esp; /* is this unit a hayes esp board? */ #endif u_char fifo_image; /* copy of value written to FIFO */ bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t loses_outints; /* nonzero if device loses output interrupts */ u_char mcr_image; /* copy of value written to MCR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t gone; /* hardware disappeared */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ #ifdef PC98 Port_t cmd_port; Port_t sts_port; Port_t in_modem_port; Port_t intr_ctrl_port; int intr_enable; int pc98_prev_modem_status; int pc98_modem_delta; int modem_car_chg_timer; int pc98_prev_siocmd; int pc98_prev_siomod; int modem_checking; int pc98_if_type; #endif /* PC98 */ Port_t data_port; /* i/o ports */ #ifdef COM_ESP Port_t esp_port; #endif Port_t int_id_port; Port_t iobase; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; /* * Ping-pong input buffers. The extra factor of 2 in the sizes is * to allow for an error byte for each input byte. */ #define CE_INPUT_OFFSET RS_IBUFSIZE u_char ibuf1[2 * RS_IBUFSIZE]; u_char ibuf2[2 * RS_IBUFSIZE]; /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ u_char obuf1[256]; u_char obuf2[256]; #ifdef DEVFS void *devfs_token_ttyd; void *devfs_token_ttyl; void *devfs_token_ttyi; void *devfs_token_cuaa; void *devfs_token_cual; void *devfs_token_cuai; #endif }; /* * XXX public functions in drivers should be declared in headers produced * by `config', not here. */ /* Interrupt handling entry point. */ void siopoll __P((void)); /* Device switch entry points. */ #define sioreset noreset #define siommap nommap #define siostrategy nostrategy #ifdef COM_ESP static int espattach __P((struct isa_device *isdp, struct com_s *com, Port_t esp_port)); #endif static int sioattach __P((struct isa_device *dev)); static timeout_t siodtrwakeup; static void comhardclose __P((struct com_s *com)); static void siointr1 __P((struct com_s *com)); static int commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); static int sioprobe __P((struct isa_device *dev)); static void siosettimeout __P((void)); static void comstart __P((struct tty *tp)); static timeout_t comwakeup; static int tiocm_xxx2mcr __P((int tiocm_xxx)); static void disc_optim __P((struct tty *tp, struct termios *t, struct com_s *com)); #ifdef DSI_SOFT_MODEM static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr)); #endif /* DSI_SOFT_MODEM */ static char driver_name[] = "sio"; /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIO]; #define com_addr(unit) (p_com_addr[unit]) struct isa_driver siodriver = { sioprobe, sioattach, driver_name }; static d_open_t sioopen; static d_close_t sioclose; static d_read_t sioread; static d_write_t siowrite; static d_ioctl_t sioioctl; static d_stop_t siostop; static d_devtotty_t siodevtotty; #define CDEV_MAJOR 28 static struct cdevsw sio_cdevsw = { sioopen, sioclose, sioread, siowrite, sioioctl, siostop, noreset, siodevtotty, ttselect, nommap, NULL, driver_name, NULL, -1, }; static int comconsole = -1; static speed_t comdefaultrate = TTYDEF_SPEED; static u_int com_events; /* input chars + weighted output completions */ static int sio_timeout; static int sio_timeouts_until_log; #if 0 /* XXX */ static struct tty *sio_tty[NSIO]; #else static struct tty sio_tty[NSIO]; #endif static const int nsio_tty = NSIO; #ifdef PC98 struct siodev { short if_type; short irq; Port_t cmd, sts, ctrl, mod; }; static int sysclock; static short port_table[5][3] = { {0x30, 0xb1, 0xb9}, {0x32, 0xb3, 0xbb}, {0x32, 0xb3, 0xbb}, {0x33, 0xb0, 0xb2}, {0x35, 0xb0, 0xb2} }; #define PC98SIO_data_port(ch) port_table[0][ch] #define PC98SIO_cmd_port(ch) port_table[1][ch] #define PC98SIO_sts_port(ch) port_table[2][ch] #define PC98SIO_in_modem_port(ch) port_table[3][ch] #define PC98SIO_intr_ctrl_port(ch) port_table[4][ch] #ifdef COM_IF_PIO9032B #define IO_COM_PIO9032B_2 0x0b8 #define IO_COM_PIO9032B_3 0x0ba #endif /* COM_IF_PIO9032B */ #ifdef COM_IF_B98_01 #define IO_COM_B98_01_2 0x0d1 #define IO_COM_B98_01_3 0x0d5 #endif /* COM_IF_B98_01 */ #define COM_INT_DISABLE {int previpri; previpri=spltty(); #define COM_INT_ENABLE splx(previpri);} #define IEN_TxFLAG IEN_Tx #define COM_CARRIER_DETECT_EMULATE 0 #define PC98_CHECK_MODEM_INTERVAL (hz/10) #define DCD_OFF_TOLERANCE 2 #define DCD_ON_RECOGNITION 2 #define IS_8251(type) (type != MC16550) #define IS_PC98IN(adr) (adr == 0x30) static void commint __P((dev_t dev)); static void com_tiocm_set __P((struct com_s *com, int msr)); static void com_tiocm_bis __P((struct com_s *com, int msr)); static void com_tiocm_bic __P((struct com_s *com, int msr)); static int com_tiocm_get __P((struct com_s *com)); static int com_tiocm_get_delta __P((struct com_s *com)); static void pc98_msrint_start __P((dev_t dev)); static void com_cflag_and_speed_set __P((struct com_s *com, int cflag, int speed)); static int pc98_ttspeedtab __P((struct com_s *com, int speed)); static int pc98_get_modem_status __P((struct com_s *com)); static timeout_t pc98_check_msr; static void pc98_set_baud_rate __P((struct com_s *com, int count)); static void pc98_i8251_reset __P((struct com_s *com, int mode, int command)); static void pc98_disable_i8251_interrupt __P((struct com_s *com, int mod)); static void pc98_enable_i8251_interrupt __P((struct com_s *com, int mod)); static int pc98_check_i8251_interrupt __P((struct com_s *com)); static int pc98_i8251_get_cmd __P((struct com_s *com)); static int pc98_i8251_get_mod __P((struct com_s *com)); static void pc98_i8251_set_cmd __P((struct com_s *com, int x)); static void pc98_i8251_or_cmd __P((struct com_s *com, int x)); static void pc98_i8251_clear_cmd __P((struct com_s *com, int x)); static void pc98_i8251_clear_or_cmd __P((struct com_s *com, int clr, int x)); static int pc98_check_if_type __P((int iobase, struct siodev *iod)); static void pc98_check_sysclock __P((void)); static int pc98_set_ioport __P((struct com_s *com, int io_base)); #define com_int_Tx_disable(com) \ pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP) #define com_int_Tx_enable(com) \ pc98_enable_i8251_interrupt(com,IEN_TxFLAG) #define com_int_Rx_disable(com) \ pc98_disable_i8251_interrupt(com,IEN_Rx) #define com_int_Rx_enable(com) \ pc98_enable_i8251_interrupt(com,IEN_Rx) #define com_int_TxRx_disable(com) \ pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP|IEN_Rx) #define com_int_TxRx_enable(com) \ pc98_enable_i8251_interrupt(com,IEN_TxFLAG|IEN_Rx) #define com_send_break_on(com) \ pc98_i8251_or_cmd(com,CMD8251_SBRK) #define com_send_break_off(com) \ pc98_i8251_clear_cmd(com,CMD8251_SBRK) struct speedtab pc98speedtab[] = { /* internal RS232C interface */ 0, 0, 50, 50, 75, 75, 150, 150, 200, 200, 300, 300, 600, 600, 1200, 1200, 2400, 2400, 4800, 4800, 9600, 9600, 19200, 19200, 38400, 38400, 76800, 76800, 20800, 20800, 41600, 41600, 15600, 15600, 31200, 31200, 62400, 62400, -1, -1 }; #ifdef COM_IF_PIO9032B struct speedtab comspeedtab_pio9032b[] = { 300, 6, 600, 5, 1200, 4, 2400, 3, 4800, 2, 9600, 1, 19200, 0, 38400, 7, -1, -1 }; #endif #ifdef COM_IF_B98_01 struct speedtab comspeedtab_b98_01[] = { 0, 0, 75, 15, 150, 14, 300, 13, 600, 12, 1200, 11, 2400, 10, 4800, 9, 9600, 8, 19200, 7, 38400, 6, 76800, 5, 153600, 4, -1, -1 }; #endif #endif /* PC98 */ static struct speedtab comspeedtab[] = { { 0, 0 }, { 50, COMBRD(50) }, { 75, COMBRD(75) }, { 110, COMBRD(110) }, { 134, COMBRD(134) }, { 150, COMBRD(150) }, { 200, COMBRD(200) }, { 300, COMBRD(300) }, { 600, COMBRD(600) }, { 1200, COMBRD(1200) }, { 1800, COMBRD(1800) }, { 2400, COMBRD(2400) }, { 4800, COMBRD(4800) }, { 9600, COMBRD(9600) }, { 19200, COMBRD(19200) }, { 38400, COMBRD(38400) }, { 57600, COMBRD(57600) }, { 115200, COMBRD(115200) }, { -1, -1 } }; #ifdef COM_ESP /* XXX configure this properly. */ static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; #endif #if NCRD > 0 /* * PC-Card (PCMCIA) specific code. */ static int card_intr(struct pccard_dev *); /* Interrupt handler */ static void siounload(struct pccard_dev *); /* Disable driver */ static void siosuspend(struct pccard_dev *); /* Suspend driver */ static int sioinit(struct pccard_dev *, int); /* init device */ static struct pccard_drv sio_info = { driver_name, card_intr, siounload, siosuspend, sioinit, 0, /* Attributes - presently unused */ &tty_imask /* Interrupt mask for device */ /* XXX - Should this also include net_imask? */ }; /* * Called when a power down is requested. Shuts down the * device and configures the device as unavailable (but * still loaded...). A resume is done by calling * sioinit with first=0. This is called when the user suspends * the system, or the APM code suspends the system. */ static void siosuspend(struct pccard_dev *dp) { printf("sio%d: suspending\n", dp->isahd.id_unit); } /* * Initialize the device - called from Slot manager. * If first is set, then check for the device's existence * before initializing it. Once initialized, the device table may * be set up. */ int sioinit(struct pccard_dev *dp, int first) { /* validate unit number. */ if (first) { if (dp->isahd.id_unit >= NSIO) return(ENODEV); /* Make sure it isn't already probed. */ if (com_addr(dp->isahd.id_unit)) return(EBUSY); /* * Probe the device. If a value is returned, the * device was found at the location. */ if (sioprobe(&dp->isahd)==0) return(ENXIO); if (sioattach(&dp->isahd)==0) return(ENXIO); } /* * XXX TODO: * If it was initialized before, the device structure * should also be initialized. We should * reset (and possibly restart) the hardware, but * I am not sure of the best way to do this... */ return(0); } /* * siounload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void siounload(struct pccard_dev *dp) { struct com_s *com; com = com_addr(dp->isahd.id_unit); if (!com->iobase) { printf("sio%d already unloaded!\n",dp->isahd.id_unit); return; } if (com->tp && (com->tp->t_state & TS_ISOPEN)) { com->gone = 1; printf("sio%d: unload\n", dp->isahd.id_unit); com->tp->t_gen++; ttyclose(com->tp); ttwakeup(com->tp); ttwwakeup(com->tp); } else { com_addr(com->unit) = NULL; bzero(com, sizeof *com); free(com,M_TTYS); printf("sio%d: unload,gone\n", dp->isahd.id_unit); } } /* * card_intr - Shared interrupt called from * front end of PC-Card handler. */ static int card_intr(struct pccard_dev *dp) { struct com_s *com; com = com_addr(dp->isahd.id_unit); if (com && !com_addr(dp->isahd.id_unit)->gone) siointr1(com_addr(dp->isahd.id_unit)); return(1); } #endif /* NCRD > 0 */ static int sioprobe(dev) struct isa_device *dev; { static bool_t already_init; bool_t failures[10]; int fn; struct isa_device *idev; Port_t iobase; u_char mcr_image; int result; #ifdef PC98 struct isa_device *xdev; int irqout=0; int ret = 0; int tmp; struct siodev iod; #else struct isa_device *xdev; #endif if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ for (xdev = isa_devtab_tty; xdev->id_driver != NULL; xdev++) if (xdev->id_driver == &siodriver && xdev->id_enabled) #ifdef PC98 if (IS_PC98IN(xdev->id_iobase)) outb(xdev->id_iobase + 2, 0xf2); else #else outb(xdev->id_iobase + com_mcr, 0); #endif #if NCRD > 0 /* * If PC-Card probe required, then register driver with * slot manager. */ pccard_add_driver(&sio_info); #endif already_init = TRUE; } #ifdef PC98 /* * If the port is i8251 UART (internal, B98_01) */ if(pc98_check_if_type(dev->id_iobase, &iod) == -1) return 0; if(IS_8251(iod.if_type)){ if ( iod.irq > 0 ) dev->id_irq = (1 << iod.irq); outb(iod.cmd, 0); DELAY(10); outb(iod.cmd, 0); DELAY(10); outb(iod.cmd, 0); DELAY(10); outb(iod.cmd, CMD8251_RESET); DELAY(1000); /* for a while...*/ outb(iod.cmd, 0xf2); /* MODE (dummy) */ DELAY(10); outb(iod.cmd, 0x01); /* CMD (dummy) */ DELAY(1000); /* for a while...*/ if (( inb(iod.sts) & STS8251_TxEMP ) == 0 ) { ret = 0; } switch (iod.if_type) { case COM_IF_INTERNAL: COM_INT_DISABLE tmp = ( inb( iod.ctrl ) & ~(IEN_Rx|IEN_TxEMP|IEN_Tx)); outb( iod.ctrl, tmp|IEN_TxEMP ); ret = isa_irq_pending(dev) ? 4 : 0; outb( iod.ctrl, tmp ); COM_INT_ENABLE break; #ifdef COM_IF_B98_01 case COM_IF_B98_01: /* B98_01 doesn't activate TxEMP interrupt line when being reset, so we can't check irq pending.*/ ret = 4; break; #endif } if (epson_machine_id==0x20) { /* XXX */ ret = 4; } return ret; } #endif /* PC98 */ /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. */ idev = dev; mcr_image = MCR_IENABLE; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(dev)) { idev = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(dev)); if (idev == NULL) { printf("sio%d: master device %d not configured\n", dev->id_unit, COM_MPMASTER(dev)); return (0); } #ifndef PC98 if (!COM_NOTAST4(dev)) { outb(idev->id_iobase + com_scr, idev->id_irq ? 0x80 : 0); mcr_image = 0; } #endif /* !PC98 */ } #endif /* COM_MULTIPORT */ if (idev->id_irq == 0) mcr_image = 0; #ifdef PC98 switch(idev->id_irq){ case IRQ3: irqout = 4; break; case IRQ5: irqout = 5; break; case IRQ6: irqout = 6; break; case IRQ12: irqout = 7; break; default: printf("sio%d: irq configuration error\n",dev->id_unit); return (0); } outb(dev->id_iobase+0x1000, irqout); #endif bzero(failures, sizeof failures); iobase = dev->id_iobase; /* * We don't want to get actual interrupts, just masked ones. * Interrupts from this line should already be masked in the ICU, * but mask them in the processor as well in case there are some * (misconfigured) shared interrupts. */ disable_intr(); /* EXTRA DELAY? */ /* * Initialize the speed and the word size and wait long enough to * drain the maximum of 16 bytes of junk in device output queues. * The speed is undefined after a master reset and must be set * before relying on anything related to output. There may be * junk after a (very fast) soft reboot and (apparently) after * master reset. * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ outb(iobase + com_cfcr, CFCR_DLAB); outb(iobase + com_dlbl, COMBRD(9600) & 0xff); outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8); outb(iobase + com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (9600 / 10)); /* * Enable the interrupt gate and disable device interupts. This * should leave the device driving the interrupt line low and * guarantee an edge trigger if an interrupt can be generated. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); outb(iobase + com_ier, 0); /* * Attempt to set loopback mode so that we can send a null byte * without annoying any external device. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); /* * Attempt to generate an output interrupt. On 8250's, setting * IER_ETXRDY generates an interrupt independent of the current * setting and independent of whether the THR is empty. On 16450's, * setting IER_ETXRDY generates an interrupt independent of the * current setting. On 16550A's, setting IER_ETXRDY only * generates an interrupt when IER_ETXRDY is not already set. */ outb(iobase + com_ier, IER_ETXRDY); /* * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate * an interrupt. They'd better generate one for actually doing * output. Loopback may be broken on the same incompatibles but * it's unlikely to do more than allow the null byte out. */ outb(iobase + com_data, 0); DELAY((1 + 2) * 1000000 / (9600 / 10)); /* * Turn off loopback mode so that the interrupt gate works again * (MCR_IENABLE was hidden). This should leave the device driving * an interrupt line high. It doesn't matter if the interrupt * line oscillates while we are not looking at it, since interrupts * are disabled. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); /* * Check that * o the CFCR, IER and MCR in UART hold the values written to them * (the values happen to be all distinct - this is good for * avoiding false positive tests from bus echoes). * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ /* EXTRA DELAY? */ failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; failures[1] = inb(iobase + com_ier) - IER_ETXRDY; failures[2] = inb(iobase + com_mcr) - mcr_image; DELAY(10000); /* Some internal modems need this time */ if (idev->id_irq != 0) failures[3] = isa_irq_pending(idev) ? 0 : 1; failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; DELAY(1000); /* XXX */ if (idev->id_irq != 0) 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 alone. For ports without a master port, it gates * the OUT2 output of the UART to * the ICU input. Closing the gate would give a floating ICU input * (unless there is another device driving at) and spurious interrupts. * (On the system that this was first tested on, the input floats high * and gives a (masked) interrupt as soon as the gate is closed.) */ outb(iobase + com_ier, 0); outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = inb(iobase + com_ier); DELAY(1000); /* XXX */ if (idev->id_irq != 0) failures[8] = isa_irq_pending(idev) ? 1 : 0; failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; enable_intr(); result = IO_COMSIZE; for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) { outb(iobase + com_mcr, 0); result = 0; if (COM_VERBOSE(dev)) printf("sio%d: probe test %d failed\n", dev->id_unit, fn); } return (result); } #ifdef COM_ESP static int espattach(isdp, com, esp_port) struct isa_device *isdp; struct com_s *com; Port_t esp_port; { u_char dips; u_char val; /* * Check the ESP-specific I/O port to see if we're an ESP * card. If not, return failure immediately. */ if ((inb(esp_port) & 0xf3) == 0) { printf(" port 0x%x is not an ESP board?\n", esp_port); return (0); } /* * We've got something that claims to be a Hayes ESP card. * Let's hope so. */ /* Get the dip-switch configuration */ outb(esp_port + ESP_CMD1, ESP_GETDIPS); dips = inb(esp_port + ESP_STATUS1); /* * Bits 0,1 of dips say which COM port we are. */ if (com->iobase == likely_com_ports[dips & 0x03]) printf(" : ESP"); else { printf(" esp_port has com %d\n", dips & 0x03); return (0); } /* * Check for ESP version 2.0 or later: bits 4,5,6 = 010. */ outb(esp_port + ESP_CMD1, ESP_GETTEST); val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ val = inb(esp_port + ESP_STATUS2); if ((val & 0x70) < 0x20) { printf("-old (%o)", val & 0x70); return (0); } /* * Check for ability to emulate 16550: bit 7 == 1 */ if ((dips & 0x80) == 0) { printf(" slave"); return (0); } /* * Okay, we seem to be a Hayes ESP card. Whee. */ com->esp = TRUE; com->esp_port = esp_port; return (1); } #endif /* COM_ESP */ static int sioattach(isdp) struct isa_device *isdp; { struct com_s *com; dev_t dev; #ifdef COM_ESP Port_t *espp; #endif Port_t iobase; int s; int unit; isdp->id_ri_flags |= RI_FAST; iobase = isdp->id_iobase; unit = isdp->id_unit; com = malloc(sizeof *com, M_TTYS, M_NOWAIT); if (com == NULL) return (0); /* * sioprobe() has initialized the device registers as follows: * o cfcr = CFCR_8BITS. * It is most important that CFCR_DLAB is off, so that the * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->unit = unit; com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; com->loses_outints = COM_LOSESOUTINTS(isdp) != 0; com->no_irq = isdp->id_irq == 0; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; com->iobase = iobase; #ifdef PC98 if(pc98_set_ioport(com, iobase) == -1) if((iobase & 0x0f0) == 0xd0) { com->pc98_if_type = MC16550; com->data_port = iobase + com_data; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; } #else /* not PC98 */ com->data_port = iobase + com_data; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; #endif /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole) { #ifdef PC98 if(IS_8251(com->pc98_if_type)) DELAY(100000); #endif com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; } termioschars(&com->it_in); com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); #ifdef DSI_SOFT_MODEM if((inb(iobase+7) ^ inb(iobase+7)) & 0x80) { printf(" Digicom Systems, Inc. SoftModem"); goto determined_type; } #endif /* DSI_SOFT_MODEM */ #ifndef PC98 #ifdef COM_MULTIPORT if (!COM_ISMULTIPORT(isdp)) #endif { u_char scr; u_char scr1; u_char scr2; scr = inb(iobase + com_scr); outb(iobase + com_scr, 0xa5); scr1 = inb(iobase + com_scr); outb(iobase + com_scr, 0x5a); scr2 = inb(iobase + com_scr); outb(iobase + com_scr, scr); if (scr1 != 0xa5 || scr2 != 0x5a) { printf(" 8250"); goto determined_type; } } #endif /* !PC98 */ #ifdef PC98 if(IS_8251(com->pc98_if_type)){ com_int_TxRx_disable( com ); com_cflag_and_speed_set( com, com->it_in.c_cflag, comdefaultrate ); com_tiocm_bic( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE ); com_send_break_off( com ); switch(com->pc98_if_type){ case COM_IF_INTERNAL: printf(" 8251 (internal)"); break; #ifdef COM_IF_PC9861K case COM_IF_PC9861K: printf(" 8251 (PC9861K)"); break; #endif #ifdef COM_IF_PIO9032B case COM_IF_PIO9032B: printf(" 8251 (PIO9032B)"); break; #endif #ifdef COM_IF_B98_01 case COM_IF_B98_01: printf(" 8251 (B98_01)"); break; #endif } } else { #endif /* PC98 */ outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); DELAY(100); switch (inb(com->int_id_port) & IIR_FIFO_MASK) { case FIFO_RX_LOW: printf(" 16450"); break; case FIFO_RX_MEDL: printf(" 16450?"); break; case FIFO_RX_MEDH: printf(" 16550?"); break; case FIFO_RX_HIGH: printf(" 16550A"); if (COM_NOFIFO(isdp)) { printf(" fifo disabled"); } else { com->hasfifo = TRUE; com->tx_fifo_size = 16; #ifdef COM_ESP for (espp = likely_esp_ports; *espp != 0; espp++) if (espattach(isdp, com, *espp)) { com->tx_fifo_size = 1024; break; } #endif } #if 0 /* * Check for the Startech ST16C650 chip. * it has a shadow register under the com_iir, * which can only be accessed when cfcr == 0xff */ { u_char i, j; i = inb(iobase + com_iir); outb(iobase + com_cfcr, 0xff); outb(iobase + com_iir, 0x0); outb(iobase + com_cfcr, CFCR_8BITS); j = inb(iobase + com_iir); outb(iobase + com_iir, i); if (i != j) { printf(" 16550A"); } else { com->tx_fifo_size = 32; printf(" 16650"); } if (!com->tx_fifo_size) printf(" fifo disabled"); } #endif break; } #ifdef COM_ESP if (com->esp) { outb(iobase + com_fifo, FIFO_DMA_MODE | FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_RX_MEDH); /* Set 16550 compatibility mode. */ outb(com->esp_port + ESP_CMD1, ESP_SETMODE); outb(com->esp_port + ESP_CMD2, ESP_MODE_SCALE | ESP_MODE_RTS | ESP_MODE_FIFO); /* Set RTS/CTS flow control. */ outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); /* Set flow-control levels. */ outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); outb(com->esp_port + ESP_CMD2, HIBYTE(768)); outb(com->esp_port + ESP_CMD2, LOBYTE(768)); outb(com->esp_port + ESP_CMD2, HIBYTE(512)); outb(com->esp_port + ESP_CMD2, LOBYTE(512)); } #endif /* COM_ESP */ outb(iobase + com_fifo, 0); determined_type: ; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(isdp)) { com->multiport = TRUE; printf(" (multiport"); if (unit == COM_MPMASTER(isdp)) printf(" master"); printf(")"); com->no_irq = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(isdp))->id_irq == 0; } #endif /* COM_MULTIPORT */ #ifdef PC98 } #endif printf("\n"); s = spltty(); com_addr(unit) = com; splx(s); dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev, &sio_cdevsw, NULL); #ifdef DEVFS /* devsw, minor, type, uid, gid, perm, fmt, ... */ com->devfs_token_ttyd = devfs_add_devswf(&sio_cdevsw, unit, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyd%n", unit); com->devfs_token_ttyi = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_INIT_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyid%n", unit); com->devfs_token_ttyl = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_LOCK_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyld%n", unit); com->devfs_token_cuaa = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaa%n", unit); com->devfs_token_cuai = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaia%n", unit); com->devfs_token_cual = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuala%n", unit); #endif return (1); } static int sioopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); if (com->gone) return (ENXIO); if (mynor & CONTROL_MASK) return (0); #if 0 /* XXX */ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else tp = com->tp = &sio_tty[unit]; #endif s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "siobi", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; #ifdef PC98 if(!IS_8251(com->pc98_if_type)) #endif (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); com->poll = com->no_irq; com->poll_output = com->loses_outints; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; #ifdef PC98 if(IS_8251(com->pc98_if_type)){ com_tiocm_bis(com, TIOCM_DTR|TIOCM_RTS); pc98_msrint_start(dev); } #endif /* * XXX we should goto open_top if comparam() slept. */ ttsetwater(tp); iobase = com->iobase; if (com->hasfifo) { /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); DELAY(100); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(100); (void) inb(com->data_port); } } disable_intr(); #ifdef PC98 if(IS_8251(com->pc98_if_type)){ com_tiocm_bis(com, TIOCM_LE); com->pc98_prev_modem_status = pc98_get_modem_status(com); com_int_Rx_enable(com); } else { #endif (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); #ifdef PC98 } #endif enable_intr(); /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "siobi" * instead of "siodcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ #ifdef PC98 if ((IS_8251(com->pc98_if_type) && (pc98_get_modem_status(com) & TIOCM_CAR)) || (!IS_8251(com->pc98_if_type) && (com->prev_modem_status & MSR_DCD)) || mynor & CALLOUT_MASK) #else if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) #endif (*linesw[tp->t_line].l_modem)(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); if (com_addr(unit) == NULL) return (ENXIO); --com->wopeners; if (error != 0 || com->gone) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); disc_optim(tp, &tp->t_termios, com); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout(); out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } static int sioclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); #ifdef PC98 com->modem_checking = 0; #endif disc_optim(tp, &tp->t_termios, com); siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s); if (com->gone) { printf("sio%d: gone\n", com->unit); s = spltty(); com_addr(com->unit) = 0; bzero(tp,sizeof *tp); bzero(com,sizeof *com); free(com,M_TTYS); splx(s); } return (0); } static void comhardclose(com) struct com_s *com; { Port_t iobase; int s; struct tty *tp; int unit; unit = com->unit; iobase = com->iobase; s = spltty(); com->poll = FALSE; com->poll_output = FALSE; com->do_timestamp = 0; #ifdef PC98 if(IS_8251(com->pc98_if_type)) com_send_break_off(com); else #endif outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); { #ifdef PC98 int tmp; if(IS_8251(com->pc98_if_type)) com_int_TxRx_disable(com); else #endif outb(iobase + com_ier, 0); tp = com->tp; #ifdef PC98 if(IS_8251(com->pc98_if_type)) tmp = pc98_get_modem_status(com) & TIOCM_CAR; else tmp = com->prev_modem_status & MSR_DCD; #endif if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || !com->active_out #ifdef PC98 && !(tmp) #else && !(com->prev_modem_status & MSR_DCD) #endif && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { #ifdef PC98 if(IS_8251(com->pc98_if_type)) com_tiocm_bic(com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE); else #endif (void)commctl(com, TIOCM_DTR, DMBIC); if (com->dtr_wait != 0) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } #ifdef PC98 else { if(IS_8251(com->pc98_if_type)) com_tiocm_bic(com, TIOCM_LE ); } #endif } if (com->hasfifo) { /* * Disable fifos so that they are off after controlled * reboots. Some BIOSes fail to detect 16550s when the * fifos are enabled. */ outb(iobase + com_fifo, 0); } com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } static int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; int unit; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); if (com_addr(unit)->gone) return (ENODEV); tp = com_addr(unit)->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } static int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); if (com_addr(unit)->gone) return (ENODEV); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty != NULL && unit == comconsole) constty = NULL; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); } void siointr(unit) int unit; { #ifndef COM_MULTIPORT siointr1(com_addr(unit)); #else /* COM_MULTIPORT */ struct com_s *com; bool_t possibly_more_intrs; /* * Loop until there is no activity on any port. This is necessary * to get an interrupt edge more than to avoid another interrupt. * If the IRQ signal is just an OR of the IRQ signals from several * devices, then the edge from one may be lost because another is * on. */ do { possibly_more_intrs = FALSE; for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); #ifdef PC98 if (com != NULL && !com->gone && IS_8251(com->pc98_if_type)){ siointr1(com); } else #endif /* PC98 */ if (com != NULL && !com->gone && (inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { siointr1(com); possibly_more_intrs = TRUE; } } } while (possibly_more_intrs); #endif /* COM_MULTIPORT */ } static void siointr1(com) struct com_s *com; { u_char line_status; u_char modem_status; u_char *ioptr; u_char recv_data; #ifdef PC98 u_char tmp=0; recv_data=0; #endif /* PC98 */ while (TRUE) { #ifdef PC98 status_read:; if (IS_8251(com->pc98_if_type)) { tmp = inb(com->sts_port); more_intr: line_status = 0; if (tmp & STS8251_TxRDY) line_status |= LSR_TXRDY; if (tmp & STS8251_RxRDY) line_status |= LSR_RXRDY; if (tmp & STS8251_TxEMP) line_status |= LSR_TSRE; if (tmp & STS8251_PE) line_status |= LSR_PE; if (tmp & STS8251_OE) line_status |= LSR_OE; if (tmp & STS8251_FE) line_status |= LSR_FE; if (tmp & STS8251_BD_SD) line_status |= LSR_BI; } else #endif /* PC98 */ line_status = inb(com->line_status_port); /* input event? (check first to help avoid overruns) */ while (line_status & LSR_RCV_MASK) { /* break/unnattached error bits or real input? */ #ifdef PC98 if(IS_8251(com->pc98_if_type)){ recv_data = inb(com->data_port); if(tmp & 0x78){ pc98_i8251_or_cmd(com,CMD8251_ER); recv_data = 0; } } else { #endif /* PC98 */ if (!(line_status & LSR_RXRDY)) recv_data = 0; else recv_data = inb(com->data_port); #ifdef PC98 } #endif if (line_status & (LSR_PE|LSR_FE|LSR_BI)) { #ifdef DDB #ifdef BREAK_TO_DEBUGGER if (line_status & LSR_BI && com->unit == comconsole) { Debugger("serial console break"); goto cont; } #endif #endif /* Don't store PE if IGNPAR and BI if IGNBRK, this hack allows "raw" tty optimization works even if IGN* is set. */ if ( com->tp == NULL || !(com->tp->t_state & TS_ISOPEN) || (line_status & (LSR_PE|LSR_FE)) && (com->tp->t_iflag & IGNPAR) || (line_status & LSR_BI) && (com->tp->t_iflag & IGNBRK)) goto cont; if ( (line_status & (LSR_PE|LSR_FE)) && (com->tp->t_state & TS_CAN_BYPASS_L_RINT) && ((line_status & LSR_FE) || (line_status & LSR_PE) && (com->tp->t_iflag & INPCK))) recv_data = 0; } ++com->bytes_in; if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { if (com->do_timestamp) microtime(&com->timestamp); ++com_events; schedsofttty(); #if 0 /* for testing input latency vs efficiency */ if (com->iptr - com->ibuf == 8) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) #ifdef PC98 if(IS_8251(com->pc98_if_type)) com_tiocm_bic(com, TIOCM_RTS); else #endif outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } cont: /* * "& 0x7F" is to avoid the gcc-1.40 generating a slow * jump from the top of the loop to here */ #ifdef PC98 if(IS_8251(com->pc98_if_type)) goto status_read; else #endif line_status = inb(com->line_status_port) & 0x7F; } /* modem status change? (always check before doing output) */ #ifdef PC98 if(!IS_8251(com->pc98_if_type)){ #endif modem_status = inb(com->modem_status_port); if (modem_status != com->last_modem_status) { if (com->do_dcd_timestamp && !(com->last_modem_status & MSR_DCD) && modem_status & MSR_DCD) microtime(&com->dcd_timestamp); /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; setsofttty(); } /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } } #ifdef PC98 } #endif /* output queued and everything ready? */ if (line_status & LSR_TXRDY && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { ioptr = com->obufq.l_head; if (com->tx_fifo_size > 1) { u_int ocount; ocount = com->obufq.l_tail - ioptr; if (ocount > com->tx_fifo_size) ocount = com->tx_fifo_size; com->bytes_out += ocount; do outb(com->data_port, *ioptr++); while (--ocount != 0); } else { outb(com->data_port, *ioptr++); ++com->bytes_out; } #ifdef PC98 if(IS_8251(com->pc98_if_type)) if ( !(pc98_check_i8251_interrupt(com) & IEN_TxFLAG) ) com_int_Tx_enable(com); #endif com->obufq.l_head = ioptr; if (ioptr >= com->obufq.l_tail) { struct lbq *qp; qp = com->obufq.l_next; qp->l_queued = FALSE; qp = qp->l_next; if (qp != NULL) { com->obufq.l_head = qp->l_head; com->obufq.l_tail = qp->l_tail; com->obufq.l_next = qp; } else { /* output just completed */ com->state &= ~CS_BUSY; #if defined(PC98) if(IS_8251(com->pc98_if_type)) if ( pc98_check_i8251_interrupt(com) & IEN_TxFLAG ) com_int_Tx_disable(com); #endif } if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; setsofttty(); /* handle at high level ASAP */ } } } #ifdef PC98 else if (line_status & LSR_TXRDY) { if(IS_8251(com->pc98_if_type)) if ( pc98_check_i8251_interrupt(com) & IEN_TxFLAG ) com_int_Tx_disable(com); } if(IS_8251(com->pc98_if_type)) if ((tmp = inb(com->sts_port)) & STS8251_RxRDY) goto more_intr; #endif /* finished? */ #ifndef COM_MULTIPORT #ifdef PC98 if(IS_8251(com->pc98_if_type)) return; #endif if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) #endif /* COM_MULTIPORT */ return; } } static int sioioctl(dev, cmd, data, flag, p) dev_t dev; int cmd; caddr_t data; int flag; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) int oldcmd; struct termios term; #endif mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); if (com->gone) return (ENODEV); iobase = com->iobase; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); #ifdef DSI_SOFT_MODEM /* * Download micro-code to Digicom modem. */ case TIOCDSIMICROCODE: { u_long l; u_char *p,*pi; pi = (u_char*)(*(caddr_t*)data); error = copyin(pi,&l,sizeof l); if(error) {return error;}; pi += sizeof l; p = malloc(l,M_TEMP,M_NOWAIT); if(!p) {return ENOBUFS;} error = copyin(pi,p,l); if(error) {free(p,M_TEMP); return error;}; if(error = LoadSoftModem( MINOR_TO_UNIT(mynor),iobase,l,p)) {free(p,M_TEMP); return error;} free(p,M_TEMP); return(0); } #endif /* DSI_SOFT_MODEM */ default: return (ENOTTY); } } tp = com->tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); s = spltty(); error = ttioctl(tp, cmd, data, flag); disc_optim(tp, &tp->t_termios, com); if (error >= 0) { splx(s); return (error); } #ifdef PC98 if(IS_8251(com->pc98_if_type)){ switch (cmd) { case TIOCSBRK: com_send_break_on( com ); break; case TIOCCBRK: com_send_break_off( com ); break; case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); return (ENOTTY); } } else { #endif switch (cmd) { case TIOCSBRK: outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; default: splx(s); return (ENOTTY); } #ifdef PC98 } #endif splx(s); return (0); } void siopoll() { int unit; if (com_events == 0) return; repeat: for (unit = 0; unit < NSIO; ++unit) { u_char *buf; struct com_s *com; u_char *ibuf; int incc; struct tty *tp; #ifdef PC98 int tmp; #endif com = com_addr(unit); if (com == NULL) continue; if (com->gone) continue; tp = com->tp; if (tp == NULL) { /* * XXX forget any events related to closed devices * (actually never opened devices) so that we don't * loop. */ disable_intr(); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; enable_intr(); if (incc != 0) log(LOG_DEBUG, "sio%d: %d events for device with no tp\n", unit, incc); continue; } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { buf = NULL; /* not used, but compiler can't tell */ incc = 0; } else { buf = ibuf; disable_intr(); incc = com->iptr - buf; com_events -= incc; if (ibuf == com->ibuf1) ibuf = com->ibuf2; else ibuf = com->ibuf1; com->ibufend = ibuf + RS_IBUFSIZE; com->ihighwater = ibuf + RS_IHIGHWATER; com->iptr = ibuf; /* * There is now room for another low-level buffer full * of input, so enable RTS if it is now disabled and * there is room in the high-level buffer. */ /* * XXX this used not to look at CS_RTS_IFLOW. The * change is to allow full control of MCR_RTS via * ioctls after turning CS_RTS_IFLOW off. Check * for races. We shouldn't allow the ioctls while * CS_RTS_IFLOW is on. */ #ifdef PC98 if(IS_8251(com->pc98_if_type)) tmp = com_tiocm_get(com) & TIOCM_RTS; else tmp = com->mcr_image & MCR_RTS; #endif if ((com->state & CS_RTS_IFLOW) #ifdef PC98 && !(tmp) #else && !(com->mcr_image & MCR_RTS) #endif && !(tp->t_state & TS_TBLOCK)) #ifdef PC98 if(IS_8251(com->pc98_if_type)) com_tiocm_bis(com, TIOCM_RTS); else #endif outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); enable_intr(); com->ibuf = ibuf; } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; #ifdef PC98 if(!IS_8251(com->pc98_if_type)){ #endif disable_intr(); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); if (delta_modem_status & MSR_DCD) (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); #ifdef PC98 } #endif } if (com->state & CS_ODONE) { disable_intr(); com_events -= LOTS_OF_EVENTS; com->state &= ~CS_ODONE; if (!(com->state & CS_BUSY)) com->tp->t_state &= ~TS_BUSY; enable_intr(); (*linesw[tp->t_line].l_start)(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) continue; /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (tp->t_rawq.c_cc + incc >= RB_I_HIGH_WATER && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; comstart(tp); } } else { do { u_char line_status; int recv_data; line_status = (u_char) buf[CE_INPUT_OFFSET]; recv_data = (u_char) *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } (*linesw[tp->t_line].l_rint)(recv_data, tp); } while (--incc > 0); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { u_int cfcr; int cflag; struct com_s *com; int divisor; int error; Port_t iobase; int s; int unit; int txtimeout; #ifdef PC98 Port_t tmp_port; int tmp_flg; #endif #ifdef PC98 cfcr = 0; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; if(IS_8251(com->pc98_if_type)) { divisor = pc98_ttspeedtab(com, t->c_ospeed); } else #endif /* do historical conversions */ if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; /* check requested parameters */ divisor = ttspeedtab(t->c_ospeed, comspeedtab); if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ #ifndef PC98 unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; #endif s = spltty(); #ifdef PC98 if(IS_8251(com->pc98_if_type)){ if(divisor == 0){ com_int_TxRx_disable( com ); com_tiocm_bic( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE ); } } else { #endif if (divisor == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ else (void)commctl(com, TIOCM_DTR, DMBIS); #ifdef PC98 } #endif cflag = t->c_cflag; #ifdef PC98 if(!IS_8251(com->pc98_if_type)){ #endif switch (cflag & CSIZE) { case CS5: cfcr = CFCR_5BITS; break; case CS6: cfcr = CFCR_6BITS; break; case CS7: cfcr = CFCR_7BITS; break; default: cfcr = CFCR_8BITS; break; } if (cflag & PARENB) { cfcr |= CFCR_PENAB; if (!(cflag & PARODD)) cfcr |= CFCR_PEVEN; } if (cflag & CSTOPB) cfcr |= CFCR_STOPB; if (com->hasfifo && divisor != 0) { /* * Use a fifo trigger level low enough so that the input * latency from the fifo is less than about 16 msec and * the total latency is less than about 30 msec. These * latencies are reasonable for humans. Serial comms * protocols shouldn't expect anything better since modem * latencies are larger. */ com->fifo_image = t->c_ospeed <= 4800 ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_HIGH; outb(iobase + com_fifo, com->fifo_image); } /* * Some UARTs lock up if the divisor latch registers are selected * while the UART is doing output (they refuse to transmit anything * more until given a hard reset). Fix this by stopping filling * the device buffers and waiting for them to drain. Reading the * line status port outside of siointr1() might lose some receiver * error bits, but that is acceptable here. */ #ifdef PC98 } #endif disable_intr(); retry: com->state &= ~CS_TTGO; txtimeout = tp->t_timeout; enable_intr(); #ifdef PC98 if(IS_8251(com->pc98_if_type)){ tmp_port = com->sts_port; tmp_flg = (STS8251_TxRDY|STS8251_TxEMP); } else { tmp_port = com->line_status_port; tmp_flg = (LSR_TSRE|LSR_TXRDY); } while ((inb(tmp_port) & tmp_flg) != tmp_flg) { #else while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) { #endif tp->t_state |= TS_SO_OCOMPLETE; error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH, "siotx", hz / 100); if ( txtimeout != 0 && (!error || error == EAGAIN) && (txtimeout -= hz / 100) <= 0 ) error = EIO; if (com->gone) error = ENODEV; if (error != 0 && error != EAGAIN) { if (!(tp->t_state & TS_TTSTOP)) { disable_intr(); com->state |= CS_TTGO; enable_intr(); } splx(s); return (error); } } disable_intr(); /* very important while com_data is hidden */ /* * XXX - clearing CS_TTGO is not sufficient to stop further output, * because siopoll() calls comstart() which usually sets it again * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be * sufficient, for similar reasons. */ #ifdef PC98 if ((inb(tmp_port) & tmp_flg) != tmp_flg) #else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) #endif goto retry; #ifdef PC98 if(!IS_8251(com->pc98_if_type)){ #endif if (divisor != 0) { outb(iobase + com_cfcr, cfcr | CFCR_DLAB); outb(iobase + com_dlbl, divisor & 0xFF); outb(iobase + com_dlbh, (u_int) divisor >> 8); } outb(iobase + com_cfcr, com->cfcr_image = cfcr); #ifdef PC98 } else com_cflag_and_speed_set(com, cflag, t->c_ospeed); #endif if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; if (cflag & CRTS_IFLOW) com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */ else com->state &= ~CS_RTS_IFLOW; /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state |= CS_ODEVREADY; com->state &= ~CS_CTS_OFLOW; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; #ifdef PC98 if(IS_8251(com->pc98_if_type)){ if (!(pc98_get_modem_status(com) & TIOCM_CTS)) com->state &= ~CS_ODEVREADY; } else { #endif if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; #ifdef PC98 } #endif } /* XXX shouldn't call functions while intrs are disabled. */ disc_optim(tp, t, com); /* * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). */ if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); enable_intr(); splx(s); return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; int s; int unit; #ifdef PC98 int tmp; #endif unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); s = spltty(); disable_intr(); if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; if (tp->t_state & TS_TBLOCK) { #ifdef PC98 if(IS_8251(com->pc98_if_type)) tmp = com_tiocm_get(com) & TIOCM_RTS; else tmp = com->mcr_image & MCR_RTS; if (tmp && (com->state & CS_RTS_IFLOW)) #else if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) #endif #ifdef PC98 if(IS_8251(com->pc98_if_type)) com_tiocm_bic(com, TIOCM_RTS); else #endif outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } else { /* * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it * appropriately in comparam() if RTS-flow is being changed. * Check for races. */ #ifdef PC98 if(IS_8251(com->pc98_if_type)) tmp = com_tiocm_get(com) & TIOCM_RTS; else tmp = com->mcr_image & MCR_RTS; if (!(tmp) && com->iptr < com->ihighwater) #else if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater) #endif #ifdef PC98 if(IS_8251(com->pc98_if_type)) com_tiocm_bis(com, TIOCM_RTS); else #endif outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { #ifdef PC98 /* if(IS_8251(com->pc98_if_type)) com_int_Tx_enable(com); */ #endif splx(s); return; } if (tp->t_outq.c_cc != 0) { struct lbq *qp; struct lbq *next; if (!com->obufs[0].l_queued) { com->obufs[0].l_tail = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, sizeof com->obuf1); com->obufs[0].l_next = NULL; com->obufs[0].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[0]; } else { com->obufq.l_head = com->obufs[0].l_head; com->obufq.l_tail = com->obufs[0].l_tail; com->obufq.l_next = &com->obufs[0]; com->state |= CS_BUSY; } enable_intr(); } if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { com->obufs[1].l_tail = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, sizeof com->obuf2); com->obufs[1].l_next = NULL; com->obufs[1].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[1]; } else { com->obufq.l_head = com->obufs[1].l_head; com->obufq.l_tail = com->obufs[1].l_tail; com->obufq.l_next = &com->obufs[1]; com->state |= CS_BUSY; } enable_intr(); } tp->t_state |= TS_BUSY; } disable_intr(); if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); /* fake interrupt to start output */ enable_intr(); #ifdef PC98 /* if(IS_8251(com->pc98_if_type)) com_int_Tx_enable(com); */ #endif ttwwakeup(tp); splx(s); } static void siostop(tp, rw) struct tty *tp; int rw; { struct com_s *com; com = com_addr(DEV_TO_UNIT(tp->t_dev)); if (com->gone) return; disable_intr(); if (rw & FWRITE) { com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); com->tp->t_state &= ~TS_BUSY; } if (rw & FREAD) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } enable_intr(); comstart(tp); /* XXX should clear h/w fifos too. */ } static struct tty * siodevtotty(dev) dev_t dev; { int mynor; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO) return (NULL); return (&sio_tty[unit]); } static int commctl(com, bits, how) struct com_s *com; int bits; int how; { int mcr; int msr; if (how == DMGET) { bits = TIOCM_LE; /* XXX - always enabled while open */ mcr = com->mcr_image; if (mcr & MCR_DTR) bits |= TIOCM_DTR; if (mcr & MCR_RTS) bits |= TIOCM_RTS; msr = com->prev_modem_status; if (msr & MSR_CTS) bits |= TIOCM_CTS; if (msr & MSR_DCD) bits |= TIOCM_CD; if (msr & MSR_DSR) bits |= TIOCM_DSR; /* * XXX - MSR_RI is naturally volatile, and we make MSR_TERI * more volatile by reading the modem status a lot. Perhaps * we should latch both bits until the status is read here. */ if (msr & (MSR_RI | MSR_TERI)) bits |= TIOCM_RI; return (bits); } mcr = 0; if (bits & TIOCM_DTR) mcr |= MCR_DTR; if (bits & TIOCM_RTS) mcr |= MCR_RTS; if (com->gone) return(0); disable_intr(); switch (how) { case DMSET: outb(com->modem_ctl_port, com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); break; case DMBIS: outb(com->modem_ctl_port, com->mcr_image |= mcr); break; case DMBIC: outb(com->modem_ctl_port, com->mcr_image &= ~mcr); break; } enable_intr(); return (0); } static void siosettimeout() { struct com_s *com; bool_t someopen; int unit; /* * Set our timeout period to 1 second if no polled devices are open. * Otherwise set it to max(1/200, 1/hz). * Enable timeouts iff some device is open. */ untimeout(comwakeup, (void *)NULL); sio_timeout = hz; someopen = FALSE; for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && com->tp != NULL && com->tp->t_state & TS_ISOPEN && !com->gone) { someopen = TRUE; if (com->poll || com->poll_output) { sio_timeout = hz > 200 ? hz / 200 : 1; break; } } } if (someopen) { sio_timeouts_until_log = hz / sio_timeout; timeout(comwakeup, (void *)NULL, sio_timeout); } else { /* Flush error messages, if any. */ sio_timeouts_until_log = 1; comwakeup((void *)NULL); untimeout(comwakeup, (void *)NULL); } } static void comwakeup(chan) void *chan; { struct com_s *com; int unit; timeout(comwakeup, (void *)NULL, sio_timeout); /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && !com->gone && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); } } /* * Check for and log errors, but not too often. */ if (--sio_timeouts_until_log > 0) return; sio_timeouts_until_log = hz / sio_timeout; for (unit = 0; unit < NSIO; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; if (com->gone) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; disable_intr(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; enable_intr(); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } } } #ifdef PC98 /* commint is called when modem control line changes */ static void commint(dev_t dev) { register struct tty *tp; int stat,delta; struct com_s *com; int mynor,unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); tp = com->tp; stat = com_tiocm_get(com); delta = com_tiocm_get_delta(com); if (com->state & CS_CTS_OFLOW) { if (stat & TIOCM_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } if ((delta & TIOCM_CAR) && (mynor & CALLOUT_MASK) == 0) { if (stat & TIOCM_CAR ) (void)(*linesw[tp->t_line].l_modem)(tp, 1); else if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0) { /* negate DTR, RTS */ com_tiocm_bic(com, (tp->t_cflag & HUPCL) ? TIOCM_DTR|TIOCM_RTS|TIOCM_LE : TIOCM_LE ); /* disable IENABLE */ com_int_TxRx_disable( com ); } } } #endif static void disc_optim(tp, t, com) struct tty *tp; struct termios *t; struct com_s *com; { if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; /* * Prepare to reduce input latency for packet * discplines with a end of packet character. */ if (tp->t_line == SLIPDISC) com->hotchar = 0xc0; else if (tp->t_line == PPPDISC) com->hotchar = 0x7e; else com->hotchar = 0; } /* * Following are all routines needed for SIO to act as console */ #include 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); /* * We don't want interrupts, but must be careful not to "disable" * them by clearing the MCR_IENABLE bit, since that might cause * an interrupt by floating the IRQ line. */ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void siocnclose(sp) struct siocnstate *sp; { Port_t iobase; /* * Restore the device control registers. */ siocntxwait(); iobase = siocniobase; outb(iobase + com_cfcr, CFCR_DLAB); outb(iobase + com_dlbl, sp->dlbl); outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); } void siocnprobe(cp) struct consdev *cp; { int unit; /* XXX: ick */ unit = DEV_TO_UNIT(CONUNIT); siocniobase = CONADDR; /* make sure hardware exists? XXX */ /* initialize required fields */ cp->cn_dev = makedev(CDEV_MAJOR, unit); #ifdef COMCONSOLE cp->cn_pri = CN_REMOTE; /* Force a serial port console */ #else cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL; #endif } void siocninit(cp) struct consdev *cp; { comconsole = DEV_TO_UNIT(cp->cn_dev); } int siocncheckc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); if (inb(iobase + com_lsr) & LSR_RXRDY) c = inb(iobase + com_data); else c = 0; siocnclose(&sp); splx(s); return (c); } int siocngetc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp); splx(s); return (c); } void siocnputc(dev, c) dev_t dev; int c; { int s; struct siocnstate sp; s = spltty(); siocnopen(&sp); siocntxwait(); outb(siocniobase + com_data, c); siocnclose(&sp); splx(s); } #ifdef DSI_SOFT_MODEM /* * The magic code to download microcode to a "Connection 14.4+Fax" * modem from Digicom Systems Inc. Very magic. */ #define DSI_ERROR(str) { ptr = str; goto error; } static int LoadSoftModem(int unit, int base_io, u_long size, u_char *ptr) { int int_c,int_k; int data_0188, data_0187; /* * First see if it is a DSI SoftModem */ if(!((inb(base_io+7) ^ inb(base_io+7) & 0x80))) return ENODEV; data_0188 = inb(base_io+4); data_0187 = inb(base_io+3); outb(base_io+3,0x80); outb(base_io+4,0x0C); outb(base_io+0,0x31); outb(base_io+1,0x8C); outb(base_io+7,0x10); outb(base_io+7,0x19); if(0x18 != (inb(base_io+7) & 0x1A)) DSI_ERROR("dsp bus not granted"); if(0x01 != (inb(base_io+7) & 0x01)) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x01 != (inb(base_io+7) & 0x01)) DSI_ERROR("program mem not granted"); } int_c = 0; while(1) { if(int_c >= 7 || size <= 0x1800) break; for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; int_c++; } if(size > 0x1800) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; while(size > 0x1800) { for(int_k = 0 ; int_k < 0xC00; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; } if(size < 0x1800) { for(int_k=0;int_k 0) { if(int_c == 7) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } else { for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } } outb(base_io+7,0x11); outb(base_io+7,3); outb(base_io+4,data_0188 & 0xfb); outb(base_io+3,data_0187); return 0; error: printf("sio%d: DSI SoftModem microcode load failed: <%s>\n",unit,ptr); outb(base_io+7,0x00); \ outb(base_io+3,data_0187); \ outb(base_io+4,data_0188); \ return EIO; } #endif /* DSI_SOFT_MODEM */ #ifdef PC98 /* * pc98 local function */ static void com_tiocm_set(struct com_s *com, int msr) { int s; int tmp = 0; int mask = CMD8251_TxEN|CMD8251_RxEN|CMD8251_DTR|CMD8251_RTS; s=spltty(); com->pc98_prev_modem_status = ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ) | ( com->pc98_prev_modem_status & ~(TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); tmp |= (CMD8251_TxEN|CMD8251_RxEN); if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; pc98_i8251_clear_or_cmd( com, mask, tmp ); splx(s); } static void com_tiocm_bis(struct com_s *com, int msr) { int s; int tmp = 0; s=spltty(); com->pc98_prev_modem_status |= ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); tmp |= CMD8251_TxEN|CMD8251_RxEN; if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; pc98_i8251_or_cmd( com, tmp ); splx(s); } static void com_tiocm_bic(struct com_s *com, int msr) { int s; int tmp = msr; s=spltty(); com->pc98_prev_modem_status &= ~( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; pc98_i8251_clear_cmd( com, tmp ); splx(s); } static int com_tiocm_get(struct com_s *com) { return( com->pc98_prev_modem_status ); } static int com_tiocm_get_delta(struct com_s *com) { int tmp; tmp = com->pc98_modem_delta; com->pc98_modem_delta = 0; return( tmp ); } /* convert to TIOCM_?? ( ioctl.h ) */ static int pc98_get_modem_status(struct com_s *com) { int stat, stat2; register int msr; stat = inb(com->sts_port); stat2 = inb(com->in_modem_port); msr = com->pc98_prev_modem_status & ~(TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS); if ( !(stat2 & CICSCD_CD) ) msr |= TIOCM_CAR; if ( !(stat2 & CICSCD_CI) ) msr |= TIOCM_RI; if ( stat & STS8251_DSR ) msr |= TIOCM_DSR; if ( !(stat2 & CICSCD_CS) ) msr |= TIOCM_CTS; #if COM_CARRIER_DETECT_EMULATE if ( msr & (TIOCM_DSR|TIOCM_CTS) ) { msr |= TIOCM_CAR; } #endif return(msr); } static void pc98_check_msr(void* chan) { int msr, delta; int s; register struct tty *tp; struct com_s *com; int mynor; int unit; dev_t dev; dev=(dev_t)chan; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); tp = com->tp; s = spltty(); msr = pc98_get_modem_status(com); /* make change flag */ delta = msr ^ com->pc98_prev_modem_status; if ( delta & TIOCM_CAR ) { if ( com->modem_car_chg_timer ) { if ( -- com->modem_car_chg_timer ) msr ^= TIOCM_CAR; } else { if ( com->modem_car_chg_timer = ( msr & TIOCM_CAR ) ? DCD_ON_RECOGNITION : DCD_OFF_TOLERANCE ) msr ^= TIOCM_CAR; } } else com->modem_car_chg_timer = 0; delta = ( msr ^ com->pc98_prev_modem_status ) & (TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS); com->pc98_prev_modem_status = msr; delta = ( com->pc98_modem_delta |= delta ); splx(s); if ( com->modem_checking || (tp->t_state & (TS_ISOPEN)) ) { if ( delta ) { commint(dev); } timeout(pc98_check_msr, (caddr_t)dev, PC98_CHECK_MODEM_INTERVAL); } else { com->modem_checking = 0; } } static void pc98_msrint_start(dev_t dev) { struct com_s *com; int mynor; int unit; int s = spltty(); mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); com = com_addr(unit); /* modem control line check routine envoke interval is 1/10 sec */ if ( com->modem_checking == 0 ) { com->pc98_prev_modem_status = pc98_get_modem_status(com); com->pc98_modem_delta = 0; timeout(pc98_check_msr, (caddr_t)dev, PC98_CHECK_MODEM_INTERVAL); com->modem_checking = 1; } splx(s); } static void pc98_disable_i8251_interrupt(struct com_s *com, int mod) { /* disable interrupt */ register int tmp; mod |= ~(IEN_Tx|IEN_TxEMP|IEN_Rx); COM_INT_DISABLE tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx); outb( com->intr_ctrl_port, (com->intr_enable&=~mod) | tmp ); COM_INT_ENABLE } static void pc98_enable_i8251_interrupt(struct com_s *com, int mod) { register int tmp; COM_INT_DISABLE tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx); outb( com->intr_ctrl_port, (com->intr_enable|=mod) | tmp ); COM_INT_ENABLE } static int pc98_check_i8251_interrupt(struct com_s *com) { return ( com->intr_enable & 0x07 ); } static void pc98_i8251_clear_cmd(struct com_s *com, int x) { int tmp; COM_INT_DISABLE tmp = com->pc98_prev_siocmd & ~(x); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); COM_INT_ENABLE } static void pc98_i8251_or_cmd(struct com_s *com, int x) { int tmp; COM_INT_DISABLE tmp = com->pc98_prev_siocmd | (x); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); COM_INT_ENABLE } static void pc98_i8251_set_cmd(struct com_s *com, int x) { int tmp; COM_INT_DISABLE tmp = (x); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); COM_INT_ENABLE } static void pc98_i8251_clear_or_cmd(struct com_s *com, int clr, int x) { int tmp; COM_INT_DISABLE tmp = com->pc98_prev_siocmd & ~(clr); tmp |= (x); outb(com->cmd_port, tmp); com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); COM_INT_ENABLE } static int pc98_i8251_get_cmd(struct com_s *com) { return com->pc98_prev_siocmd; } static int pc98_i8251_get_mod(struct com_s *com) { return com->pc98_prev_siomod; } static void pc98_i8251_reset(struct com_s *com, int mode, int command) { outb(com->cmd_port, 0); /* dummy */ DELAY(2); outb(com->cmd_port, 0); /* dummy */ DELAY(2); outb(com->cmd_port, 0); /* dummy */ DELAY(2); outb(com->cmd_port, CMD8251_RESET); /* internal reset */ DELAY(2); outb(com->cmd_port, mode ); /* mode register */ com->pc98_prev_siomod = mode; DELAY(2); pc98_i8251_set_cmd( com, (command|CMD8251_ER) ); } static void pc98_check_sysclock(void) { /* get system clock from port */ if ( pc98_machine_type & M_8M ) { /* 8 MHz system & H98 */ sysclock = 8; } else { /* 5 MHz system */ sysclock = 5; } } static void com_cflag_and_speed_set( struct com_s *com, int cflag, int speed) { int cfcr=0, count; int previnterrupt; count = pc98_ttspeedtab( com, speed ); if ( count < 0 ) return; previnterrupt = pc98_check_i8251_interrupt(com); pc98_disable_i8251_interrupt( com, IEN_Tx|IEN_TxEMP|IEN_Rx ); switch ( cflag&CSIZE ) { case CS5: cfcr = MOD8251_5BITS; break; case CS6: cfcr = MOD8251_6BITS; break; case CS7: cfcr = MOD8251_7BITS; break; case CS8: cfcr = MOD8251_8BITS; break; } if ( cflag&PARENB ) { if ( cflag&PARODD ) cfcr |= MOD8251_PODD; else cfcr |= MOD8251_PEVEN; } else cfcr |= MOD8251_PDISAB; if ( cflag&CSTOPB ) cfcr |= MOD8251_STOP2; else cfcr |= MOD8251_STOP1; if ( count & 0x10000 ) cfcr |= MOD8251_CLKX1; else cfcr |= MOD8251_CLKX16; if (epson_machine_id != 0x20) { /* XXX */ { int tmp; while (!((tmp = inb(com->sts_port)) & STS8251_TxEMP)) ; } } /* set baud rate from ospeed */ pc98_set_baud_rate( com, count ); if ( cfcr != pc98_i8251_get_mod(com) ) pc98_i8251_reset(com, cfcr, pc98_i8251_get_cmd(com) ); pc98_enable_i8251_interrupt( com, previnterrupt ); } static int pc98_ttspeedtab(struct com_s *com, int speed) { int effect_sp, count=-1, mod; switch ( com->pc98_if_type ) { case COM_IF_INTERNAL: /* for *1CLK asynchronous! mode , TEFUTEFU */ effect_sp = ttspeedtab( speed, pc98speedtab ); if ( effect_sp < 0 ) effect_sp = ttspeedtab( (speed-1), pc98speedtab ); if ( effect_sp <= 0 ) return effect_sp; mod = (sysclock == 5 ? 2457600 : 1996800); if ( effect_sp == speed ) mod /= 16; count = mod / effect_sp; if ( count > 65535 ) return(-1); if ( effect_sp >= 2400 ) if ( !(sysclock != 5 && (effect_sp == 19200 || effect_sp == 38400)) ) if ( ( mod % effect_sp ) != 0 ) return(-1); if ( effect_sp != speed ) count |= 0x10000; break; #ifdef COM_IF_PC9861K case COM_IF_PC9861K: effect_sp = speed; count = 1; break; #endif #ifdef COM_IF_PIO9032B case COM_IF_PIO9032B: if ( speed == 0 ) return 0; count = ttspeedtab( speed, comspeedtab_pio9032b ); if ( count < 0 ) return count; effect_sp = speed; break; #endif #ifdef COM_IF_B98_01 case COM_IF_B98_01: effect_sp=speed; count = ttspeedtab( speed, comspeedtab_b98_01 ); if ( count <= 3 ) return -1; /* invalid speed/count */ if ( count <= 5 ) count |= 0x10000; /* x1 mode for 76800 and 153600 */ else count -= 4; /* x16 mode for slower */ break; #endif } return count; } static void pc98_set_baud_rate( struct com_s *com, int count) { int s; switch ( com->pc98_if_type ) { case COM_IF_INTERNAL: if ( count < 0 ) { printf( "[ Illegal count : %d ]", count ); return; } else if ( count == 0) return; /* set i8253 */ s = splclock(); outb( 0x77, 0xb6 ); outb( 0x5f, 0); outb( 0x75, count & 0xff ); outb( 0x5f, 0); outb( 0x75, (count >> 8) & 0xff ); splx(s); break; #if 0 #ifdef COM_IF_PC9861K case COM_IF_PC9861K: break; /* ext. RS232C board: speed is determined by DIP switch */ #endif #endif /* 0 */ #ifdef COM_IF_PIO9032B case COM_IF_PIO9032B: outb( com_addr[unit], count & 0x07 ); break; #endif #ifdef COM_IF_B98_01 case COM_IF_B98_01: outb( com->iobase, count & 0x0f ); #ifdef B98_01_OLD /* some old board should be controlled in different way, but this hasn't been tested yet.*/ outb( com->iobase+2, ( count & 0x10000 ) ? 0xf0 : 0xf2 ); #endif break; #endif } } static int pc98_check_if_type( int iobase, struct siodev *iod) { int irr = 0, tmp = 0; int ret = 0; static short irq_tab[2][8] = { { 3, 5, 6, 9, 10, 12, 13, -1}, { 3, 10, 12, 13, 5, 6, 9, -1} }; iod->irq = 0; switch ( iobase & 0xff ) { case IO_COM1: iod->if_type = COM_IF_INTERNAL; ret = 0; iod->irq = 4; break; #ifdef COM_IF_PC9861K case IO_COM2: iod->if_type = COM_IF_PC9861K; ret = 1; irr = 0; tmp = 3; break; case IO_COM3: iod->if_type = COM_IF_PC9861K; ret = 2; irr = 1; tmp = 3; break; #endif #ifdef COM_IF_PIO9032B case IO_COM_PIO9032B_2: iod->if_type = COM_IF_PIO9032B; ret = 1; irr = 0; tmp = 7; break; case IO_COM_PIO9032B_3: iod->if_type = COM_IF_PIO9032B; ret = 2; irr = 1; tmp = 7; break; #endif #ifdef COM_IF_B98_01 case IO_COM_B98_01_2: iod->if_type = COM_IF_B98_01; ret = 1; irr = 0; tmp = 7; outb(iobase + 2, 0xf2); outb(iobase, 4); break; case IO_COM_B98_01_3: iod->if_type = COM_IF_B98_01; ret = 2; irr = 1; tmp = 7; outb(iobase + 2, 0xf2); outb(iobase , 4); break; #endif default: if((iobase & 0x0f0) == 0xd0){ iod->if_type = MC16550; return 0; } return -1; } iod->cmd = ( iobase & 0xff00 )|PC98SIO_cmd_port(ret); iod->sts = ( iobase & 0xff00 )|PC98SIO_sts_port(ret); iod->mod = ( iobase & 0xff00 )|PC98SIO_in_modem_port(ret); iod->ctrl = ( iobase & 0xff00 )|PC98SIO_intr_ctrl_port(ret); if ( iod->irq == 0 ) { tmp &= inb( iod->mod ); iod->irq = irq_tab[irr][tmp]; if ( iod->irq == -1 ) return -1; } return 0; } static int pc98_set_ioport( struct com_s *com, int io_base ) { int a, io, type; switch ( io_base & 0xff ) { case IO_COM1: a = 0; io = 0; type = COM_IF_INTERNAL; pc98_check_sysclock(); break; #ifdef COM_IF_PC9861K case IO_COM2: a = 1; io = 0; type = COM_IF_PC9861K; break; case IO_COM3: a = 2; io = 0; type = COM_IF_PC9861K; break; #endif /* COM_IF_PC9861K */ #ifdef COM_IF_PIO9032B /* PIO9032B : I/O address is changeable */ case IO_COM_PIO9032B_2: a = 1; io = io_base & 0xff00; type = COM_IF_PIO9032B; break; case IO_COM_PIO9032B_3: a = 2; io = io_base & 0xff00; type = COM_IF_PIO9032B; break; #endif /* COM_IF_PIO9032B */ #ifdef COM_IF_B98_01 case IO_COM_B98_01_2: a = 1; io = 0; type = COM_IF_B98_01; break; case IO_COM_B98_01_3: a = 2; io = 0; type = COM_IF_B98_01; break; #endif /* COM_IF_B98_01*/ default: /* i/o address not match */ return -1; } com->pc98_if_type = type; com->data_port = io | PC98SIO_data_port(a); com->cmd_port = io | PC98SIO_cmd_port(a); com->sts_port = io | PC98SIO_sts_port(a); com->in_modem_port = io | PC98SIO_in_modem_port(a); com->intr_ctrl_port = io | PC98SIO_intr_ctrl_port(a); return 0; } #endif /* PC98 defined */ Index: head/sys/pc98/pc98/sound/ad1848.c =================================================================== --- head/sys/pc98/pc98/sound/ad1848.c (revision 18264) +++ head/sys/pc98/pc98/sound/ad1848.c (revision 18265) @@ -1,1525 +1,1525 @@ /* * sound/ad1848.c * * The low level driver for the AD1848/CS4248 codec chip which * is used for example in the MS Sound System. * * The CS4231 which is used in the GUS MAX and some other cards is * upwards compatible with AD1848 and this driver is able to drive it. * * Copyright by Hannu Savolainen 1994, 1995 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Modified: * Riccardo Facchetti 24 Mar 1995 * - Added the Audio Excel DSP 16 initialization routine. */ #define DEB(x) #define DEB1(x) -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AD1848) -#include "ad1848_mixer.h" +#include #define IMODE_NONE 0 #define IMODE_OUTPUT 1 #define IMODE_INPUT 2 #define IMODE_INIT 3 #define IMODE_MIDI 4 typedef struct { int base; int irq; int dma_capture, dma_playback; unsigned char MCE_bit; unsigned char saved_regs[16]; int speed; unsigned char speed_bits; int channels; int audio_format; unsigned char format_bits; int xfer_count; int irq_mode; int intr_active; int opened; char *chip_name; int mode; /* Mixer parameters */ int recmask; int supported_devices; int supported_rec_devices; unsigned short levels[32]; } ad1848_info; static int nr_ad1848_devs = 0; static char irq2dev[16] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; static char mixer2codec[MAX_MIXER_DEV] = {0}; static int ad_format_mask[3 /*devc->mode */ ] = { 0, AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | AFMT_IMA_ADPCM }; static ad1848_info dev_info[MAX_AUDIO_DEV]; #define io_Index_Addr(d) ((d)->base) #define io_Indexed_Data(d) ((d)->base+1) #define io_Status(d) ((d)->base+2) #define io_Polled_IO(d) ((d)->base+3) static int ad1848_open (int dev, int mode); static void ad1848_close (int dev); static int ad1848_ioctl (int dev, unsigned int cmd, unsigned int arg, int local); static void ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart); static void ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart); static int ad1848_prepare_for_IO (int dev, int bsize, int bcount); static void ad1848_reset (int dev); static void ad1848_halt (int dev); static int ad_read (ad1848_info * devc, int reg) { unsigned long flags; int x; int timeout = 100; while (timeout > 0 && INB (devc->base) == 0x80) /*Are we initializing */ timeout--; DISABLE_INTR (flags); OUTB ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc)); x = INB (io_Indexed_Data (devc)); /* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */ RESTORE_INTR (flags); return x; } static void ad_write (ad1848_info * devc, int reg, int data) { unsigned long flags; int timeout = 100; while (timeout > 0 && INB (devc->base) == 0x80) /*Are we initializing */ timeout--; DISABLE_INTR (flags); OUTB ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc)); OUTB ((unsigned char) (data & 0xff), io_Indexed_Data (devc)); /* printk("(%02x->%02x) ", reg|devc->MCE_bit, data); */ RESTORE_INTR (flags); } static void wait_for_calibration (ad1848_info * devc) { int timeout = 0; /* * Wait until the auto calibration process has finished. * * 1) Wait until the chip becomes ready (reads don't return 0x80). * 2) Wait until the ACI bit of I11 gets on and then off. */ timeout = 100000; #ifdef PC98 while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80) timeout--; if ((INB (devc->base) & 0x80) == 0x80) #else while (timeout > 0 && INB (devc->base) & 0x80) timeout--; if (INB (devc->base) & 0x80) #endif printk ("ad1848: Auto calibration timed out(1).\n"); timeout = 100; while (timeout > 0 && !(ad_read (devc, 11) & 0x20)) timeout--; if (!(ad_read (devc, 11) & 0x20)) return; timeout = 10000; while (timeout > 0 && ad_read (devc, 11) & 0x20) timeout--; if (ad_read (devc, 11) & 0x20) printk ("ad1848: Auto calibration timed out(3).\n"); } static void ad_mute (ad1848_info * devc) { int i; unsigned char prev; /* * Save old register settings and mute output channels */ for (i = 6; i < 8; i++) { prev = devc->saved_regs[i] = ad_read (devc, i); ad_write (devc, i, prev | 0x80); } } static void ad_unmute (ad1848_info * devc) { int i; /* * Restore back old volume registers (unmute) */ for (i = 6; i < 8; i++) { ad_write (devc, i, devc->saved_regs[i] & ~0x80); } } static void ad_enter_MCE (ad1848_info * devc) { unsigned long flags; int timeout = 1000; unsigned short prev; while (timeout > 0 && INB (devc->base) == 0x80) /*Are we initializing */ timeout--; DISABLE_INTR (flags); devc->MCE_bit = 0x40; prev = INB (io_Index_Addr (devc)); if (prev & 0x40) { RESTORE_INTR (flags); return; } OUTB (devc->MCE_bit, io_Index_Addr (devc)); RESTORE_INTR (flags); } static void ad_leave_MCE (ad1848_info * devc) { unsigned long flags; unsigned char prev; int timeout = 1000; while (timeout > 0 && INB (devc->base) == 0x80) /*Are we initializing */ timeout--; DISABLE_INTR (flags); devc->MCE_bit = 0x00; prev = INB (io_Index_Addr (devc)); OUTB (0x00, io_Index_Addr (devc)); /* Clear the MCE bit */ if (prev & 0x40 == 0) /* Not in MCE mode */ { RESTORE_INTR (flags); return; } OUTB (0x00, io_Index_Addr (devc)); /* Clear the MCE bit */ wait_for_calibration (devc); RESTORE_INTR (flags); } static int ad1848_set_recmask (ad1848_info * devc, int mask) { unsigned char recdev; int i, n; mask &= devc->supported_rec_devices; n = 0; for (i = 0; i < 32; i++) /* Count selected device bits */ if (mask & (1 << i)) n++; if (n == 0) mask = SOUND_MASK_MIC; else if (n != 1) /* Too many devices selected */ { mask &= ~devc->recmask; /* Filter out active settings */ n = 0; for (i = 0; i < 32; i++) /* Count selected device bits */ if (mask & (1 << i)) n++; if (n != 1) mask = SOUND_MASK_MIC; } switch (mask) { case SOUND_MASK_MIC: recdev = 2; break; case SOUND_MASK_LINE: case SOUND_MASK_LINE3: recdev = 0; break; case SOUND_MASK_CD: case SOUND_MASK_LINE1: recdev = 1; break; default: mask = SOUND_MASK_MIC; recdev = 2; } recdev <<= 6; ad_write (devc, 0, (ad_read (devc, 0) & 0x3f) | recdev); ad_write (devc, 1, (ad_read (devc, 1) & 0x3f) | recdev); devc->recmask = mask; return mask; } static void change_bits (unsigned char *regval, int dev, int chn, int newval) { unsigned char mask; int shift; if (mix_devices[dev][chn].polarity == 1) /* Reverse */ newval = 100 - newval; mask = (1 << mix_devices[dev][chn].nbits) - 1; shift = mix_devices[dev][chn].bitpos; newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ *regval &= ~(mask << shift); /* Clear bits */ *regval |= (newval & mask) << shift; /* Set new value */ } static int ad1848_mixer_get (ad1848_info * devc, int dev) { if (!((1 << dev) & devc->supported_devices)) return RET_ERROR (EINVAL); return devc->levels[dev]; } static int ad1848_mixer_set (ad1848_info * devc, int dev, int value) { int left = value & 0x000000ff; int right = (value & 0x0000ff00) >> 8; int regoffs; unsigned char val; if (left > 100) left = 100; if (right > 100) right = 100; if (dev > 31) return RET_ERROR (EINVAL); if (!(devc->supported_devices & (1 << dev))) return RET_ERROR (EINVAL); if (mix_devices[dev][LEFT_CHN].nbits == 0) return RET_ERROR (EINVAL); /* * Set the left channel */ regoffs = mix_devices[dev][LEFT_CHN].regno; val = ad_read (devc, regoffs); change_bits (&val, dev, LEFT_CHN, left); devc->levels[dev] = left | (left << 8); ad_write (devc, regoffs, val); devc->saved_regs[regoffs] = val; /* * Set the left right */ if (mix_devices[dev][RIGHT_CHN].nbits == 0) return left | (left << 8); /* Was just a mono channel */ regoffs = mix_devices[dev][RIGHT_CHN].regno; val = ad_read (devc, regoffs); change_bits (&val, dev, RIGHT_CHN, right); ad_write (devc, regoffs, val); devc->saved_regs[regoffs] = val; devc->levels[dev] = left | (right << 8); return left | (right << 8); } static void ad1848_mixer_reset (ad1848_info * devc) { int i; devc->recmask = 0; if (devc->mode == 2) devc->supported_devices = MODE2_MIXER_DEVICES; else devc->supported_devices = MODE1_MIXER_DEVICES; devc->supported_rec_devices = MODE1_REC_DEVICES; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) ad1848_mixer_set (devc, i, devc->levels[i] = default_mixer_levels[i]); ad1848_set_recmask (devc, SOUND_MASK_MIC); } static int ad1848_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) { ad1848_info *devc; int codec_dev = mixer2codec[dev]; if (!codec_dev) return RET_ERROR (ENXIO); codec_dev--; devc = (ad1848_info *) audio_devs[codec_dev]->devc; if (((cmd >> 8) & 0xff) == 'M') { if (cmd & IOC_IN) switch (cmd & 0xff) { case SOUND_MIXER_RECSRC: return IOCTL_OUT (arg, ad1848_set_recmask (devc, IOCTL_IN (arg))); break; default: return IOCTL_OUT (arg, ad1848_mixer_set (devc, cmd & 0xff, IOCTL_IN (arg))); } else switch (cmd & 0xff) /* * Return parameters */ { case SOUND_MIXER_RECSRC: return IOCTL_OUT (arg, devc->recmask); break; case SOUND_MIXER_DEVMASK: return IOCTL_OUT (arg, devc->supported_devices); break; case SOUND_MIXER_STEREODEVS: return IOCTL_OUT (arg, devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX)); break; case SOUND_MIXER_RECMASK: return IOCTL_OUT (arg, devc->supported_rec_devices); break; case SOUND_MIXER_CAPS: return IOCTL_OUT (arg, SOUND_CAP_EXCL_INPUT); break; default: return IOCTL_OUT (arg, ad1848_mixer_get (devc, cmd & 0xff)); } } else return RET_ERROR (EINVAL); } static struct audio_operations ad1848_pcm_operations[MAX_AUDIO_DEV] = { { "Generic AD1848 codec", DMA_AUTOMODE, AFMT_U8, /* Will be set later */ NULL, ad1848_open, ad1848_close, ad1848_output_block, ad1848_start_input, ad1848_ioctl, ad1848_prepare_for_IO, ad1848_prepare_for_IO, ad1848_reset, ad1848_halt, NULL, NULL }}; static struct mixer_operations ad1848_mixer_operations = { "AD1848/CS4248/CS4231", ad1848_mixer_ioctl }; static int ad1848_open (int dev, int mode) { int err; ad1848_info *devc = NULL; unsigned long flags; DEB (printk ("ad1848_open(int mode = %X)\n", mode)); if (dev < 0 || dev >= num_audiodevs) return RET_ERROR (ENXIO); devc = (ad1848_info *) audio_devs[dev]->devc; DISABLE_INTR (flags); if (devc->opened) { RESTORE_INTR (flags); printk ("ad1848: Already opened\n"); return RET_ERROR (EBUSY); } if (devc->irq) /* Not managed by another driver */ if ((err = snd_set_irq_handler (devc->irq, ad1848_interrupt, audio_devs[dev]->name)) < 0) { printk ("ad1848: IRQ in use\n"); RESTORE_INTR (flags); return err; } if (DMAbuf_open_dma (dev) < 0) { RESTORE_INTR (flags); printk ("ad1848: DMA in use\n"); return RET_ERROR (EBUSY); } devc->intr_active = 0; devc->opened = 1; RESTORE_INTR (flags); return 0; } static void ad1848_close (int dev) { unsigned long flags; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; DEB (printk ("ad1848_close(void)\n")); DISABLE_INTR (flags); devc->intr_active = 0; if (devc->irq) /* Not managed by another driver */ snd_release_irq (devc->irq); ad1848_reset (dev); DMAbuf_close_dma (dev); devc->opened = 0; RESTORE_INTR (flags); } static int set_speed (ad1848_info * devc, int arg) { /* * The sampling speed is encoded in the least significant nible of I8. The * LSB selects the clock source (0=24.576 MHz, 1=16.9344 Mhz) and other * three bits select the divisor (indirectly): * * The available speeds are in the following table. Keep the speeds in * the increasing order. */ typedef struct { int speed; unsigned char bits; } speed_struct; static speed_struct speed_table[] = { {5510, (0 << 1) | 1}, {5510, (0 << 1) | 1}, {6620, (7 << 1) | 1}, {8000, (0 << 1) | 0}, {9600, (7 << 1) | 0}, {11025, (1 << 1) | 1}, {16000, (1 << 1) | 0}, {18900, (2 << 1) | 1}, {22050, (3 << 1) | 1}, {27420, (2 << 1) | 0}, {32000, (3 << 1) | 0}, {33075, (6 << 1) | 1}, {37800, (4 << 1) | 1}, {44100, (5 << 1) | 1}, {48000, (6 << 1) | 0} }; int i, n, selected = -1; n = sizeof (speed_table) / sizeof (speed_struct); if (arg < speed_table[0].speed) selected = 0; if (arg > speed_table[n - 1].speed) selected = n - 1; for (i = 1 /*really */ ; selected == -1 && i < n; i++) if (speed_table[i].speed == arg) selected = i; else if (speed_table[i].speed > arg) { int diff1, diff2; diff1 = arg - speed_table[i - 1].speed; diff2 = speed_table[i].speed - arg; if (diff1 < diff2) selected = i - 1; else selected = i; } if (selected == -1) { printk ("ad1848: Can't find speed???\n"); selected = 3; } devc->speed = speed_table[selected].speed; devc->speed_bits = speed_table[selected].bits; return devc->speed; } static int set_channels (ad1848_info * devc, int arg) { if (arg != 1 && arg != 2) return devc->channels; devc->channels = arg; return arg; } static int set_format (ad1848_info * devc, int arg) { static struct format_tbl { int format; unsigned char bits; } format2bits[] = { { 0, 0 } , { AFMT_MU_LAW, 1 } , { AFMT_A_LAW, 3 } , { AFMT_IMA_ADPCM, 5 } , { AFMT_U8, 0 } , { AFMT_S16_LE, 2 } , { AFMT_S16_BE, 6 } , { AFMT_S8, 0 } , { AFMT_U16_LE, 0 } , { AFMT_U16_BE, 0 } }; int i, n = sizeof (format2bits) / sizeof (struct format_tbl); if (!(arg & ad_format_mask[devc->mode])) arg = AFMT_U8; devc->audio_format = arg; for (i = 0; i < n; i++) if (format2bits[i].format == arg) { if ((devc->format_bits = format2bits[i].bits) == 0) return devc->audio_format = AFMT_U8; /* Was not supported */ return arg; } /* Still hanging here. Something must be terribly wrong */ devc->format_bits = 0; return devc->audio_format = AFMT_U8; } static int ad1848_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) { ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; switch (cmd) { case SOUND_PCM_WRITE_RATE: if (local) return set_speed (devc, arg); return IOCTL_OUT (arg, set_speed (devc, IOCTL_IN (arg))); case SOUND_PCM_READ_RATE: if (local) return devc->speed; return IOCTL_OUT (arg, devc->speed); case SNDCTL_DSP_STEREO: if (local) return set_channels (devc, arg + 1) - 1; return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg) + 1) - 1); case SOUND_PCM_WRITE_CHANNELS: if (local) return set_channels (devc, arg); return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg))); case SOUND_PCM_READ_CHANNELS: if (local) return devc->channels; return IOCTL_OUT (arg, devc->channels); case SNDCTL_DSP_SAMPLESIZE: if (local) return set_format (devc, arg); return IOCTL_OUT (arg, set_format (devc, IOCTL_IN (arg))); case SOUND_PCM_READ_BITS: if (local) return devc->audio_format; return IOCTL_OUT (arg, devc->audio_format); default:; } return RET_ERROR (EINVAL); } static void ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart) { unsigned long flags, cnt; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; cnt = count; if (devc->audio_format == AFMT_IMA_ADPCM) { cnt /= 4; } else { if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ cnt >>= 1; } if (devc->channels > 1) cnt >>= 1; cnt--; if (audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == devc->xfer_count) { devc->irq_mode = IMODE_OUTPUT; devc->intr_active = 1; return; /* * Auto DMA mode on. No need to react */ } DISABLE_INTR (flags); if (dma_restart) { /* ad1848_halt (dev); */ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); } ad_enter_MCE (devc); ad_write (devc, 15, (unsigned char) (cnt & 0xff)); ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); ad_write (devc, 9, 0x0d); /* * Playback enable, single DMA channel mode, * auto calibration on. */ ad_leave_MCE (devc); /* * Starts the calibration process and * enters playback mode after it. */ ad_unmute (devc); devc->xfer_count = cnt; devc->irq_mode = IMODE_OUTPUT; devc->intr_active = 1; INB (io_Status (devc)); OUTB (0, io_Status (devc)); /* Clear pending interrupts */ RESTORE_INTR (flags); } static void ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart) { unsigned long flags, cnt; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; int count_reg = 14; /* (devc->mode == 1) ? 14 : 30; */ cnt = count; if (devc->audio_format == AFMT_IMA_ADPCM) { cnt /= 4; } else { if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ cnt >>= 1; } if (devc->channels > 1) cnt >>= 1; cnt--; if (audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == devc->xfer_count) { devc->irq_mode = IMODE_INPUT; devc->intr_active = 1; return; /* * Auto DMA mode on. No need to react */ } DISABLE_INTR (flags); if (dma_restart) { /* ad1848_halt (dev); */ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); } ad_enter_MCE (devc); ad_write (devc, count_reg + 1, (unsigned char) (cnt & 0xff)); ad_write (devc, count_reg, (unsigned char) ((cnt >> 8) & 0xff)); ad_write (devc, 9, 0x0e); /* * Capture enable, single DMA channel mode, * auto calibration on. */ ad_leave_MCE (devc); /* * Starts the calibration process and * enters playback mode after it. */ ad_unmute (devc); devc->xfer_count = cnt; devc->irq_mode = IMODE_INPUT; devc->intr_active = 1; INB (io_Status (devc)); OUTB (0, io_Status (devc)); /* Clear interrupt status */ RESTORE_INTR (flags); } static int ad1848_prepare_for_IO (int dev, int bsize, int bcount) { int timeout; unsigned char fs; unsigned long flags; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; DISABLE_INTR (flags); ad_enter_MCE (devc); /* Enables changes to the format select reg */ fs = devc->speed_bits | (devc->format_bits << 5); if (devc->channels > 1) fs |= 0x10; ad_write (devc, 8, fs); /* * Write to I8 starts resyncronization. Wait until it completes. */ timeout = 10000; #ifdef PC98 while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80) #else while (timeout > 0 && INB (devc->base) == 0x80) #endif timeout--; #ifdef PC98 ad_write (devc, 8, fs); /* * Write to I8 starts resyncronization. Wait until it completes. */ timeout = 10000; while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80) timeout--; #endif /* * If mode == 2 (CS4231), set I28 also. It's the capture format register. */ if (devc->mode == 2) { ad_write (devc, 28, fs); /* * Write to I28 starts resyncronization. Wait until it completes. */ timeout = 10000; #ifdef PC98 while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80) #else while (timeout > 0 && INB (devc->base) == 0x80) #endif timeout--; } #ifdef PC98 ad_write (devc, 28, fs); /* * Write to I28 starts resyncronization. Wait until it completes. */ timeout = 10000; while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80) timeout--; #endif ad_leave_MCE (devc); /* * Starts the calibration process and * enters playback mode after it. */ RESTORE_INTR (flags); devc->xfer_count = 0; return 0; } static void ad1848_reset (int dev) { ad1848_halt (dev); } static void ad1848_halt (int dev) { ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; ad_mute (devc); #ifdef PC98 ad_enter_MCE (devc); #endif ad_write (devc, 9, ad_read (devc, 9) & ~0x03); /* Stop DMA */ #ifdef PC98 ad_leave_MCE (devc); #endif OUTB (0, io_Status (devc)); /* Clear interrupt status */ ad_enter_MCE (devc); OUTB (0, io_Status (devc)); /* Clear interrupt status */ ad_write (devc, 15, 0); /* Clear DMA counter */ ad_write (devc, 14, 0); /* Clear DMA counter */ if (devc->mode == 2) { ad_write (devc, 30, 0); /* Clear DMA counter */ ad_write (devc, 31, 0); /* Clear DMA counter */ } ad_write (devc, 9, ad_read (devc, 9) & ~0x03); /* Stop DMA */ OUTB (0, io_Status (devc)); /* Clear interrupt status */ OUTB (0, io_Status (devc)); /* Clear interrupt status */ ad_leave_MCE (devc); DMAbuf_reset_dma (dev); } int ad1848_detect (int io_base) { unsigned char tmp; int i; ad1848_info *devc = &dev_info[nr_ad1848_devs]; unsigned char tmp1 = 0xff, tmp2 = 0xff; if (nr_ad1848_devs >= MAX_AUDIO_DEV) { AUDIO_DDB (printk ("ad1848 detect error - step 0\n")); return 0; } devc->base = io_base; devc->MCE_bit = 0x40; devc->irq = 0; devc->dma_capture = 0; devc->dma_playback = 0; devc->opened = 0; devc->chip_name = "AD1848"; devc->mode = 1; /* MODE1 = original AD1848 */ /* * Check that the I/O address is in use. * * The bit 0x80 of the base I/O port is known to be 0 after the * chip has performed it's power on initialization. Just assume * this has happened before the OS is starting. * * If the I/O address is unused, it typically returns 0xff. */ if ((INB (devc->base) & 0x80) != 0x00) /* Not a AD1884 */ { AUDIO_DDB (printk ("ad1848 detect error - step A\n")); return 0; } /* * Test if it's possible to change contents of the indirect registers. * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only * so try to avoid using it. */ ad_write (devc, 0, 0xaa); ad_write (devc, 1, 0x45); /* 0x55 with bit 0x10 clear */ if ((tmp1 = ad_read (devc, 0)) != 0xaa || (tmp2 = ad_read (devc, 1)) != 0x45) { AUDIO_DDB (printk ("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2)); return 0; } ad_write (devc, 0, 0x45); ad_write (devc, 1, 0xaa); if ((tmp1 = ad_read (devc, 0)) != 0x45 || (tmp2 = ad_read (devc, 1)) != 0xaa) { AUDIO_DDB (printk ("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2)); return 0; } /* * The indirect register I12 has some read only bits. Lets * try to change them. */ tmp = ad_read (devc, 12); ad_write (devc, 12, (~tmp) & 0x0f); if ((tmp & 0x0f) != ((tmp1 = ad_read (devc, 12)) & 0x0f)) { AUDIO_DDB (printk ("ad1848 detect error - step D (%x)\n", tmp1)); return 0; } /* * NOTE! Last 4 bits of the reg I12 tell the chip revision. * 0x01=RevB and 0x0A=RevC. */ /* * The original AD1848/CS4248 has just 15 indirect registers. This means * that I0 and I16 should return the same value (etc.). * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails * with CS4231. */ ad_write (devc, 12, 0); /* Mode2=disabled */ for (i = 0; i < 16; i++) if ((tmp1 = ad_read (devc, i)) != (tmp2 = ad_read (devc, i + 16))) { AUDIO_DDB (printk ("ad1848 detect error - step F(%d/%x/%x)\n", i, tmp1, tmp2)); return 0; } /* * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40). * The bit 0x80 is always 1 in CS4248 and CS4231. */ ad_write (devc, 12, 0x40); /* Set mode2, clear 0x80 */ tmp1 = ad_read (devc, 12); if (tmp1 & 0x80) devc->chip_name = "CS4248"; /* Our best knowledge just now */ if ((tmp1 & 0xc0) == (0x80 | 0x40)) { /* * CS4231 detected - is it? * * Verify that setting I0 doesn't change I16. */ ad_write (devc, 16, 0); /* Set I16 to known value */ ad_write (devc, 0, 0x45); if ((tmp1 = ad_read (devc, 16)) != 0x45) /* No change -> CS4231? */ { ad_write (devc, 0, 0xaa); if ((tmp1 = ad_read (devc, 16)) == 0xaa) /* Rotten bits? */ { AUDIO_DDB (printk ("ad1848 detect error - step H(%x)\n", tmp1)); return 0; } /* * Verify that some bits of I25 are read only. */ tmp1 = ad_read (devc, 25); /* Original bits */ ad_write (devc, 25, ~tmp1); /* Invert all bits */ if ((ad_read (devc, 25) & 0xe7) == (tmp1 & 0xe7)) { /* * It's a CS4231 */ devc->chip_name = "CS4231"; #ifdef MOZART_PORT if (devc->base != MOZART_PORT) #endif devc->mode = 2; } ad_write (devc, 25, tmp1); /* Restore bits */ } } return 1; } void ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture) { /* * NOTE! If irq < 0, there is another driver which has allocated the IRQ * so that this driver doesn't need to allocate/deallocate it. * The actually used IRQ is ABS(irq). */ /* * Initial values for the indirect registers of CS4248/AD1848. */ static int init_values[] = { 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x80, 0x80, 0x00, 0x08, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00, /* Positions 16 to 31 just for CS4231 */ 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; int i, my_dev; ad1848_info *devc = &dev_info[nr_ad1848_devs]; if (!ad1848_detect (io_base)) return; devc->irq = (irq > 0) ? irq : 0; devc->dma_capture = dma_playback; devc->dma_playback = dma_capture; devc->opened = 0; if (nr_ad1848_devs != 0) { memcpy ((char *) &ad1848_pcm_operations[nr_ad1848_devs], (char *) &ad1848_pcm_operations[0], sizeof (struct audio_operations)); } for (i = 0; i < 16; i++) ad_write (devc, i, init_values[i]); ad_mute (devc); if (devc->mode == 2) { ad_write (devc, 12, ad_read (devc, 12) | 0x40); /* Mode2 = enabled */ for (i = 16; i < 32; i++) ad_write (devc, i, init_values[i]); } OUTB (0, io_Status (devc)); /* Clear pending interrupts */ if (name[0] != 0) sprintf (ad1848_pcm_operations[nr_ad1848_devs].name, "%s (%s)", name, devc->chip_name); else sprintf (ad1848_pcm_operations[nr_ad1848_devs].name, "Generic audio codec (%s)", devc->chip_name); #if defined(__FreeBSD__) if (strcmp(name, "MS Sound System")) /* *sigh* */ printk ("\ngus0: <%s>", ad1848_pcm_operations[nr_ad1848_devs].name); else printk ("mss0: <%s>", ad1848_pcm_operations[nr_ad1848_devs].name); #else printk (" <%s>", ad1848_pcm_operations[nr_ad1848_devs].name); #endif if (num_audiodevs < MAX_AUDIO_DEV) { audio_devs[my_dev = num_audiodevs++] = &ad1848_pcm_operations[nr_ad1848_devs]; if (irq > 0) irq2dev[irq] = my_dev; else if (irq < 0) irq2dev[-irq] = my_dev; audio_devs[my_dev]->dmachan = dma_playback; audio_devs[my_dev]->buffcount = 1; audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; audio_devs[my_dev]->devc = devc; audio_devs[my_dev]->format_mask = ad_format_mask[devc->mode]; nr_ad1848_devs++; /* * Toggle the MCE bit. It completes the initialization phase. */ ad_enter_MCE (devc); /* In case the bit was off */ ad_leave_MCE (devc); if (num_mixers < MAX_MIXER_DEV) { mixer2codec[num_mixers] = my_dev + 1; audio_devs[my_dev]->mixer_dev = num_mixers; mixer_devs[num_mixers++] = &ad1848_mixer_operations; ad1848_mixer_reset (devc); } } else printk ("AD1848: Too many PCM devices available\n"); } void ad1848_interrupt (INT_HANDLER_PARMS (irq, dummy)) { unsigned char status; ad1848_info *devc; int dev; if (irq < 0 || irq > 15) return; /* Bogus irq */ dev = irq2dev[irq]; if (dev < 0 || dev >= num_audiodevs) return; /* Bogus dev */ devc = (ad1848_info *) audio_devs[dev]->devc; status = INB (io_Status (devc)); if (status == 0x80) printk ("ad1848_interrupt: Why?\n"); if (status & 0x01) { if (devc->opened && devc->irq_mode == IMODE_OUTPUT) { DMAbuf_outputintr (dev, 1); } if (devc->opened && devc->irq_mode == IMODE_INPUT) DMAbuf_inputintr (dev); } OUTB (0, io_Status (devc)); /* Clear interrupt status */ status = INB (io_Status (devc)); if (status == 0x80 || status & 0x01) { printk ("ad1848: Problems when clearing interrupt, status=%x\n", status); OUTB (0, io_Status (devc)); /* Try again */ } } #ifdef MOZART_PORT /* * Experimental initialization sequence for Mozart soundcard * (OAK OTI-601 sound chip). * by Gregor Hoffleit * Some comments by Hannu Savolainen. */ int mozart_init (int io_base) { int i; unsigned char byte; static int mozart_detected_here = 0; /* * Valid ports are 0x530 and 0xf40. The DOS based software doesn't allow * other ports. The OTI-601 preliminary specification says that * 0xe80 and 0x604 are also possible but it's safest to ignore them. */ if ((io_base != 0x530) && (io_base != 0xf40)) { printk ("Mozart: invalid io_base(%x)\n", io_base); return 0; } if (mozart_detected_here == io_base) /* Already detected this card */ return 1; if (mozart_detected_here != 0) return 0; /* Don't allow detecting another Mozart card. */ /* * The Mozart chip (OAK OTI-601) must be enabled before _each_ write * by writing a secret password (0xE2) to the password register (0xf8f). * Any I/O cycle after writing the password closes the gate and disbles * further access. */ if (INB (0xf88) != 0) /* Appears to return 0 while the gate is closed */ { AUDIO_DDB (printk ("No Mozart signature detected on port 0xf88\n")); return 0; } OUTB (0xe2, 0xf8f); /* A secret password which opens the gate */ OUTB (0x10, 0xf91); /* Enable access to codec registers during SB mode */ for (i = 0; i < 100; i++) /* Delay */ tenmicrosec (); OUTB (0xe2, 0xf8f); /* Sesam */ byte = INB (0xf8d); /* Read MC1 (Mode control register) */ /* Read the same register again but with gate closed at this time. */ if (INB (0xf8d) == 0xff) /* Bus float. Should be 0 if Mozart present */ { AUDIO_DDB (printk ("Seems to be no Mozart chip set\n")); return 0; } AUDIO_DDB (printk ("mozart_init: read 0x%x on 0xf8d\n", byte)); byte = byte | 0x80; /* Switch to WSS mode (disables SB) */ byte = byte & 0xcf; /* Clear sound base, disable CD, enable joystick */ if (io_base == 0xf40) byte = byte | 0x20; for (i = 0; i < 100; i++) tenmicrosec (); OUTB (0xe2, 0xf8f); /* Open the gate again */ OUTB (byte, 0xf8d); /* Write the modified value back to MC1 */ AUDIO_DDB (printk ("mozart_init: wrote 0x%x on 0xf8d\n", byte)); OUTB (0xe2, 0xf8f); /* Here we come again */ OUTB (0x20, 0xf91); /* Protect WSS shadow registers against write */ for (i = 0; i < 1000; i++) tenmicrosec (); return 1; } #endif /* MOZART_PORT */ #ifdef OPTI_MAD16_PORT -#include "mad16.h" +#include #endif /* * Some extra code for the MS Sound System */ int probe_ms_sound (struct address_info *hw_config) { #if !defined(EXCLUDE_AEDSP16) && defined(AEDSP16_MSS) /* * Initialize Audio Excel DSP 16 to MSS: before any operation * we must enable MSS I/O ports. */ InitAEDSP16_MSS (hw_config); #endif /* * Check if the IO port returns valid signature. The original MS Sound * system returns 0x04 while some cards (AudioTriX Pro for example) * return 0x00. */ #ifdef MOZART_PORT if (hw_config->io_base == MOZART_PORT) mozart_init (hw_config->io_base); #endif #ifdef OPTI_MAD16_PORT if (hw_config->io_base == OPTI_MAD16_PORT) mad16init (hw_config->io_base); #endif if ((INB (hw_config->io_base + 3) & 0x3f) != 0x04 && (INB (hw_config->io_base + 3) & 0x3f) != 0x00) { AUDIO_DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, INB (hw_config->io_base + 3))); return 0; } #ifdef PC98 if (hw_config->irq > 12) #else if (hw_config->irq > 11) #endif { printk ("MSS: Bad IRQ %d\n", hw_config->irq); return 0; } if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) { printk ("MSS: Bad DMA %d\n", hw_config->dma); return 0; } /* * Check that DMA0 is not in use with a 8 bit board. */ if (hw_config->dma == 0 && INB (hw_config->io_base + 3) & 0x80) { printk ("MSS: Can't use DMA0 with a 8 bit card/slot\n"); return 0; } if (hw_config->irq > 7 && hw_config->irq != 9 && INB (hw_config->io_base + 3) & 0x80) { printk ("MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); return 0; } return ad1848_detect (hw_config->io_base + 4); } long attach_ms_sound (long mem_start, struct address_info *hw_config) { #ifdef PC98 static char interrupt_bits[13] = { -1, -1, -1, 0x08, -1, 0x10, -1, -1, -1, -1, 0x18, -1, 0x20 }; #else static char interrupt_bits[12] = { -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20 }; #endif char bits; static char dma_bits[4] = { 1, 2, 0, 3 }; int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3; if (!ad1848_detect (hw_config->io_base + 4)) return mem_start; /* * Set the IRQ and DMA addresses. */ bits = interrupt_bits[hw_config->irq]; if (bits == -1) return mem_start; OUTB (bits | 0x40, config_port); if ((INB (version_port) & 0x40) == 0) printk ("[IRQ Conflict?]"); OUTB (bits | dma_bits[hw_config->dma], config_port); /* Write IRQ+DMA setup */ ad1848_init ("MS Sound System", hw_config->io_base + 4, hw_config->irq, hw_config->dma, hw_config->dma); return mem_start; } #endif Index: head/sys/pc98/pc98/sound/adlib_card.c =================================================================== --- head/sys/pc98/pc98/sound/adlib_card.c (revision 18264) +++ head/sys/pc98/pc98/sound/adlib_card.c (revision 18265) @@ -1,51 +1,51 @@ /* * sound/adlib_card.c * * Detection routine for the AdLib card. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812) long attach_adlib_card (long mem_start, struct address_info *hw_config) { if (opl3_detect (FM_MONO)) { mem_start = opl3_init (mem_start); } return mem_start; } int probe_adlib (struct address_info *hw_config) { return opl3_detect (FM_MONO); } #endif Index: head/sys/pc98/pc98/sound/aedsp16.c =================================================================== --- head/sys/pc98/pc98/sound/aedsp16.c (revision 18264) +++ head/sys/pc98/pc98/sound/aedsp16.c (revision 18265) @@ -1,838 +1,838 @@ /* sound/aedsp16.c Audio Excel DSP 16 software configuration routines Copyright (C) 1995 Riccardo Facchetti (riccardo@cdc8g5.cdc.polimi.it) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. READ THIS This module is intended for Audio Excel DSP 16 Sound Card. Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401 compatible card. It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq), so before this module, the only way to configure the DSP under linux was boot the MS-BAU loading the sound.sys device driver (this driver soft- configure the sound board hardware by massaging someone of its registers), and then ctrl-alt-del to boot linux with the DSP configured by the DOG driver. This module works configuring your Audio Excel DSP 16's irq, dma and mpu-401-irq. The voxware probe routines rely on the fact that if the hardware is there, they can detect it. The problem with AEDSP16 is that no hardware can be found by the probe routines if the sound card is not well configured. Sometimes the kernel probe routines can find an SBPRO even when the card is not configured (this is the standard setup of the card), but the SBPRO emulation don't work well if the card is not properly initialized. For this reason InitAEDSP16_...() routines are called before the voxware probe routines try to detect the hardware. NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS) The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS; the voxware sound driver can be configured for SBPRO and MSS cards at the same time, but the aedsp16 can't be two cards!! When we configure it, we have to choose the SBPRO or the MSS emulation for AEDSP16. We also can install a *REAL* card of the other type (see [1], not tested but I can't see any reason for it to fail). NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO please let me know if it works. The MPU-401 support can be compiled in together with one of the other two operating modes. The board configuration calls, are in the probe_...() routines because we have to configure the board before probing it for a particular hardware. After card configuration, we can probe the hardware. NOTE: This is something like plug-and-play: we have only to plug the AEDSP16 board in the socket, and then configure and compile a kernel that uses the AEDSP16 software configuration capability. No jumper setting is needed! For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3 you have just to make config the voxware package, configuring the SBPro sound card with that parameters, then when configure asks if you have an AEDSP16, answer yes. That's it. Compile the kernel and run it. NOTE: This means that you can choose irq and dma, but not the I/O addresses. To change I/O addresses you have to set them with jumpers. NOTE: InitAEDSP16_...() routines get as parameter the hw_config, the hardware configuration of the - to be configured - board. The InitAEDSP16() routine, configure the board following our wishes, that are in the hw_config structure. You can change the irq/dma/mirq settings WITHOUT THE NEED to open your computer and massage the jumpers (there are no irq/dma/mirq jumpers to be configured anyway, only I/O port ones have to be configured with jumpers) For some ununderstandable reason, the card default of irq 7, dma 1, don't work for me. Seems to be an IRQ or DMA conflict. Under heavy HDD work, the kernel start to erupt out a lot of messages like: 'Sound: DMA timed out - IRQ/DRQ config error?' For what I can say, I have NOT any conflict at irq 7 (under linux I'm using the lp polling driver), and dma line 1 is unused as stated by /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows! Anyway a setting of irq 10, dma 3 works really fine. NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know the emulation mode, all the installed hardware and the hardware configuration (irq and dma settings of all the hardware). This init module should work with SBPRO+MSS, when one of the two is the AEDSP16 emulation and the other the real card. (see [1]) For example: AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other MPU401 should work. (see [1]) [1] Not tested by me for lack of hardware. TODO, WISHES AND TECH May be there's lot of redundant delays, but for now I want to leave it this way. Should be interesting eventually write down a new ioctl for the aedsp16, to let the suser() change the irq/dma/mirq on the fly. The thing is not trivial. In the real world, there's no need to have such an ioctl because when we configure the kernel for compile, we can choose the config parameters. If we change our mind, we can easily re-config the kernel and re-compile. Why let the suser() change the config parameters on the fly ? If anyone have a reasonable answer to this question, I will write down the code to do it. More integration with voxware, using voxware low level routines to read-write dsp is not possible because you may want to have MSS support and in that case we can not rely on the functions included in sb_dsp.c to control 0x2yy I/O ports. I will continue to use my own I/O functions. - About I/O ports allocation - The request_region should be done at device probe in every sound card module. This module is not the best site for requesting regions. When the request_region code will be added to the main modules such as sb, adlib, gus, ad1848, etc, the requesting code in this module should go away. I think the request regions should be done this way: if (check_region(...)) return ERR; // I/O region alredy reserved device_probe(...); device_attach(...); request_region(...); // reserve only when we are sure all is okay Request the 2x0h region in any case if we are using this card. NOTE: the "(sbpro)" string with which we are requesting the aedsp16 region (see code) does not mean necessarly that we are emulating sbpro. It mean that the region is the sbpro I/O ports region. We use this region to access the control registers of the card, and if emulating sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro registers are not used, in no way, to emulate an sbpro: they are used only for configuration pourposes. Someone pointed out that should be possible use both the SBPRO and MSS modes because the sound card have all the two chipsets, supposing that the card is really two cards. I have tried something to have the two modes work together, but, for some reason unknown to me, without success. I think all the soft-config only cards have an init sequence similar to this. If you have a card that is not an aedsp16, you can try to start with this module changing it (mainly in the CMD? I think) to fit your needs. Started Fri Mar 17 16:13:18 MET 1995 v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c) - Initial code. v0.2 (ALPHA) - Cleanups. - Integrated with Linux voxware v 2.90-2 kernel sound driver. - SoundBlaster Pro mode configuration. - Microsoft Sound System mode configuration. - MPU-401 mode configuration. v0.3 (ALPHA) - Cleanups. - Rearranged the code to let InitAEDSP16 be more general. - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h inclusion too. We rely on os.h - Used the INB and OUTB #defined in os.h instead of inb and outb. - Corrected the code for GetCardName (DSP Copyright) to get a variable len string (we are not sure about the len of Copyright string). This works with any SB and compatible. - Added the code to request_region at device init (should go in the main body of voxware). v0.4 (BETA) - Better configure.c patch for aedsp16 configuration (better logic of inclusion of AEDSP16 support) - Modified the conditional compilation to better support more than one sound card of the emulated type (read the NOTES above) - Moved the sb init routine from the attach to the very first probe in sb_card.c - Rearrangemens and cleanups - Wiped out some unnecessary code and variables: this is kernel code so it is better save some TEXT and DATA - Fixed the request_region code. We must allocate the aedsp16 (sbpro) I/O ports in any case because they are used to access the DSP configuration registers and we can not allow anyone to get them. v0.5 - cleanups on comments - prep for diffs against v3.0-proto-950402 */ /* * Include the main voxware header file. It include all the os/voxware/etc * headers needed by this source. */ -#include "sound_config.h" +#include /* * all but ioport.h :) */ #include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AEDSP16) #define VERSION "0.5" /* Version of Audio Excel DSP 16 driver */ #undef AEDSP16_DEBUG /* Define this to enable debug code */ /* Actually no debug code is activated */ /* * Hardware related defaults */ #define IRQ 7 /* 5 7(default) 9 10 11 */ #define MIRQ 0 /* 5 7 9 10 0(default), 0 means disable */ #define DMA 1 /* 0 1(default) 3 */ /* * Commands of AEDSP16's DSP (SBPRO+special). * For now they are CMDn, in the future they may change. */ #define CMD1 0xe3 /* Get DSP Copyright */ #define CMD2 0xe1 /* Get DSP Version */ #define CMD3 0x88 /* */ #define CMD4 0x5c /* */ #define CMD5 0x50 /* Set M&I&DRQ mask (the real config) */ #define CMD6 0x8c /* Enable Microsoft Sound System mode */ /* * Offsets of AEDSP16 DSP I/O ports. The offest is added to portbase * to have the actual I/O port. * Register permissions are: * (wo) == Write Only * (ro) == Read Only * (w-) == Write * (r-) == Read */ #define DSP_RESET 0x06 /* offset of DSP RESET (wo) */ #define DSP_READ 0x0a /* offset of DSP READ (ro) */ #define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */ #define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */ #define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */ #define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */ #define RETRY 10 /* Various retry values on I/O opera- */ #define STATUSRETRY 1000 /* tions. Sometimes we have to */ #define HARDRETRY 500000 /* wait for previous cmd to complete */ /* * Size of character arrays that store name and version of sound card */ #define CARDNAMELEN 15 /* Size of the card's name in chars */ #define CARDVERLEN 2 /* Size of the card's version in chars */ /* * Bit mapped flags for calling InitAEDSP16(), and saving the current * emulation mode. */ #define INIT_NONE (0 ) #define INIT_SBPRO (1<<0) #define INIT_MSS (1<<1) #define INIT_MPU401 (1<<2) #define RESET_DSP16 (1<<3) /* Base HW Port for Audio Card */ static int portbase = AEDSP16_BASE; static int irq = IRQ; /* irq for DSP I/O */ static int mirq = MIRQ; /* irq for MPU-401 I/O */ static int dma = DMA; /* dma for DSP I/O */ /* Init status of the card */ static int ae_init = INIT_NONE; /* (bitmapped variable) */ static int oredparams = 0; /* Will contain or'ed values of params */ static int gc = 0; /* generic counter (utility counter) */ struct orVals { /* Contain the values to be or'ed */ int val; /* irq|mirq|dma */ int or; /* oredparams |= TheStruct.or */ }; /* * Magic values that the DSP will eat when configuring irq/mirq/dma */ /* DSP IRQ conversion array */ static struct orVals orIRQ[] = { {0x05, 0x28}, {0x07, 0x08}, {0x09, 0x10}, {0x0a, 0x18}, {0x0b, 0x20}, {0x00, 0x00} }; /* MPU-401 IRQ conversion array */ static struct orVals orMIRQ[] = { {0x05, 0x04}, {0x07, 0x44}, {0x09, 0x84}, {0x0a, 0xc4}, {0x00, 0x00} }; /* DMA Channels conversion array */ static struct orVals orDMA[] = { {0x00, 0x01}, {0x01, 0x02}, {0x03, 0x03}, {0x00, 0x00} }; /* * Buffers to store audio card informations */ static char AudioExcelName[CARDNAMELEN + 1]; static char AudioExcelVersion[CARDVERLEN + 1]; static void tenmillisec (void) { for (gc = 0; gc < 1000; gc++) tenmicrosec (); } static int WaitForDataAvail (int port) { int loop = STATUSRETRY; unsigned char ret = 0; do { ret = INB (port + DSP_DATAVAIL); /* * Wait for data available (bit 7 of ret == 1) */ } while (!(ret & 0x80) && loop--); if (ret & 0x80) return 0; return -1; } static int ReadData (int port) { if (WaitForDataAvail (port)) return -1; return INB (port + DSP_READ); } static int CheckDSPOkay (int port) { return ((ReadData (port) == 0xaa) ? 0 : -1); } static int ResetBoard (int port) { /* * Reset DSP */ OUTB (1, (port + DSP_RESET)); tenmicrosec (); OUTB (0, (port + DSP_RESET)); tenmicrosec (); tenmicrosec (); return CheckDSPOkay (port); } static int WriteDSPCommand (int port, int cmd) { unsigned char ret; int loop = HARDRETRY; do { ret = INB (port + DSP_STATUS); /* * DSP ready to receive data if bit 7 of ret == 0 */ if (!(ret & 0x80)) { OUTB (cmd, port + DSP_COMMAND); return 0; } } while (loop--); printk ("[aedsp16] DSP Command (0x%x) timeout.\n", cmd); return -1; } int InitMSS (int port) { tenmillisec (); if (WriteDSPCommand (port, CMD6)) { printk ("[aedsp16] CMD 0x%x: failed!\n", CMD6); return -1; } tenmillisec (); return 0; } static int SetUpBoard (int port) { int loop = RETRY; do { if (WriteDSPCommand (portbase, CMD3)) { printk ("[aedsp16] CMD 0x%x: failed!\n", CMD3); return -1; } tenmillisec (); } while (WaitForDataAvail (port) && loop--); #if defined(THIS_SHOULD_GO_AWAY) if (CheckDSPOkay (port)) { printk ("[aedsp16] CheckDSPOkay: failed\n"); return -1; } #else if (ReadData (port) == -1) { printk ("[aedsp16] ReadData after CMD 0x%x: failed\n", CMD3); return -1; } #endif if (WriteDSPCommand (portbase, CMD4)) { printk ("[aedsp16] CMD 0x%x: failed!\n", CMD4); return -1; } if (WriteDSPCommand (portbase, CMD5)) { printk ("[aedsp16] CMD 0x%x: failed!\n", CMD5); return -1; } if (WriteDSPCommand (portbase, oredparams)) { printk ("[aedsp16] Initialization of (M)IRQ and DMA: failed!\n"); return -1; } return 0; } static int GetCardVersion (int port) { int len = 0; int ret; int ver[3]; do { if ((ret = ReadData (port)) == -1) return -1; /* * We alredy know how many int are stored (2), so we know when the * string is finished. */ ver[len++] = ret; } while (len < CARDVERLEN); sprintf (AudioExcelVersion, "%d.%d", ver[0], ver[1]); return 0; } static int GetCardName (int port) { int len = 0; int ret; do { if ((ret = ReadData (port)) == -1) /* * If no more data availabe, return to the caller, no error if len>0. * We have no other way to know when the string is finished. */ return (len ? 0 : -1); AudioExcelName[len++] = ret; } while (len < CARDNAMELEN); return 0; } static void InitializeHardParams (void) { memset (AudioExcelName, 0, CARDNAMELEN + 1); memset (AudioExcelVersion, 0, CARDVERLEN + 1); for (gc = 0; orIRQ[gc].or; gc++) if (orIRQ[gc].val == irq) oredparams |= orIRQ[gc].or; for (gc = 0; orMIRQ[gc].or; gc++) if (orMIRQ[gc].or == mirq) oredparams |= orMIRQ[gc].or; for (gc = 0; orDMA[gc].or; gc++) if (orDMA[gc].val == dma) oredparams |= orDMA[gc].or; } static int InitAEDSP16 (int which) { static char *InitName = NULL; InitializeHardParams (); if (ResetBoard (portbase)) { printk ("[aedsp16] ResetBoard: failed!\n"); return -1; } #if defined(THIS_SHOULD_GO_AWAY) if (CheckDSPOkay (portbase)) { printk ("[aedsp16] CheckDSPOkay: failed!\n"); return -1; } #endif if (WriteDSPCommand (portbase, CMD1)) { printk ("[aedsp16] CMD 0x%x: failed!\n", CMD1); return -1; } if (GetCardName (portbase)) { printk ("[aedsp16] GetCardName: failed!\n"); return -1; } /* * My AEDSP16 card return SC-6000 in AudioExcelName, so * if we have something different, we have to be warned. */ if (strcmp ("SC-6000", AudioExcelName)) printk ("[aedsp16] Warning: non SC-6000 audio card!\n"); if (WriteDSPCommand (portbase, CMD2)) { printk ("[aedsp16] CMD 0x%x: failed!\n", CMD2); return -1; } if (GetCardVersion (portbase)) { printk ("[aedsp16] GetCardVersion: failed!\n"); return -1; } if (SetUpBoard (portbase)) { printk ("[aedsp16] SetUpBoard: failed!\n"); return -1; } if (which == INIT_MSS) { if (InitMSS (portbase)) { printk ("[aedsp16] Can't initialize Microsoft Sound System mode.\n"); return -1; } } /* * If we are resetting, do not print any message because we may be * in playing and we do not want lost too much time. */ if (!(which & RESET_DSP16)) { if (which & INIT_MPU401) InitName = "MPU401"; else if (which & INIT_SBPRO) InitName = "SBPro"; else if (which & INIT_MSS) InitName = "MSS"; else InitName = "None"; printk ("Audio Excel DSP 16 init v%s (%s %s) [%s]\n", VERSION, AudioExcelName, AudioExcelVersion, InitName); } tenmillisec (); return 0; } #if defined(AEDSP16_SBPRO) int InitAEDSP16_SBPRO (struct address_info *hw_config) { /* * If the card is alredy init'ed MSS, we can not init it to SBPRO too * because the board can not emulate simultaneously MSS and SBPRO. */ if (ae_init & INIT_MSS) return -1; if (ae_init & INIT_SBPRO) return 0; /* * For now we will leave this * code included only when INCLUDE_AEDSP16 is configured in, but it should * be better include it every time. */ if (!(ae_init & INIT_MPU401)) { if (check_region (hw_config->io_base, 0x0f)) { printk ("AEDSP16/SBPRO I/O port region is alredy in use.\n"); return -1; } } /* * Set up the internal hardware parameters, to let the driver reach * the Sound Card. */ portbase = hw_config->io_base; irq = hw_config->irq; dma = hw_config->dma; if (InitAEDSP16 (INIT_SBPRO)) return -1; if (!(ae_init & INIT_MPU401)) request_region (hw_config->io_base, 0x0f, "aedsp16 (sbpro)"); ae_init |= INIT_SBPRO; return 0; } #endif /* AEDSP16_SBPRO */ #if defined(AEDSP16_MSS) int InitAEDSP16_MSS (struct address_info *hw_config) { /* * If the card is alredy init'ed SBPRO, we can not init it to MSS too * because the board can not emulate simultaneously MSS and SBPRO. */ if (ae_init & INIT_SBPRO) return -1; if (ae_init & INIT_MSS) return 0; /* * For now we will leave this * code included only when INCLUDE_AEDSP16 is configured in, but it should * be better include it every time. */ if (check_region (hw_config->io_base, 0x08)) { printk ("MSS I/O port region is alredy in use.\n"); return -1; } /* * We must allocate the AEDSP16 region too because these are the I/O ports * to access card's control registers. */ if (!(ae_init & INIT_MPU401)) { if (check_region (AEDSP16_BASE, 0x0f)) { printk ("AEDSP16 I/O port region is alredy in use.\n"); return -1; } } /* * If we are configuring the card for MSS, the portbase for card configuration * is the default one (0x220 unless you have changed the factory default * with board switches), so no need to modify the portbase variable. * The default is AEDSP16_BASE, that is the right value. */ irq = hw_config->irq; dma = hw_config->dma; if (InitAEDSP16 (INIT_MSS)) return -1; request_region (hw_config->io_base, 0x08, "aedsp16 (mss)"); if (!(ae_init & INIT_MPU401)) request_region (AEDSP16_BASE, 0x0f, "aedsp16 (sbpro)"); ae_init |= INIT_MSS; return 0; } #endif /* AEDSP16_MSS */ #if defined(AEDSP16_MPU401) int InitAEDSP16_MPU401 (struct address_info *hw_config) { if (ae_init & INIT_MPU401) return 0; /* * For now we will leave this * code included only when INCLUDE_AEDSP16 is configured in, but it should * be better include it every time. */ if (check_region (hw_config->io_base, 0x02)) { printk ("SB I/O port region is alredy in use.\n"); return -1; } /* * We must allocate the AEDSP16 region too because these are the I/O ports * to access card's control registers. */ if (!(ae_init & (INIT_MSS | INIT_SBPRO))) { if (check_region (AEDSP16_BASE, 0x0f)) { printk ("AEDSP16 I/O port region is alredy in use.\n"); return -1; } } /* * If mpu401, the irq and dma are not important, do not touch it * because we may use the default if sbpro is not yet configured, * we may use the sbpro ones if configured, and nothing wrong * should happen. * * The mirq default is 0, but once set it to non-0 value, we should * not touch it anymore (unless I write an ioctl to do it, of course). */ mirq = hw_config->irq; if (InitAEDSP16 (INIT_MPU401)) return -1; request_region (hw_config->io_base, 0x02, "aedsp16 (mpu401)"); if (!(ae_init & (INIT_MSS | INIT_SBPRO))) request_region (AEDSP16_BASE, 0x0f, "aedsp16 (sbpro)"); ae_init |= INIT_MPU401; return 0; } #endif /* AEDSP16_MPU401 */ #if 0 /* Leave it out for now. We are not using this portion of code. */ /* * Entry point for a reset function. * May be I will write the infamous ioctl :) */ int ResetAEDSP16 (void) { #if defined(AEDSP16_DEBUG) printk ("[aedsp16] ResetAEDSP16 called.\n"); #endif return InitAEDSP16 (RESET_DSP16); } #endif /* 0 */ #endif /* !EXCLUDE_AEDSP16 */ Index: head/sys/pc98/pc98/sound/audio.c =================================================================== --- head/sys/pc98/pc98/sound/audio.c (revision 18264) +++ head/sys/pc98/pc98/sound/audio.c (revision 18265) @@ -1,583 +1,583 @@ /* * sound/audio.c * * Device file manager for /dev/audio * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #ifdef CONFIGURE_SOUNDCARD #ifndef EXCLUDE_AUDIO -#include "ulaw.h" -#include "coproc.h" +#include +#include #define ON 1 #define OFF 0 static int wr_buff_no[MAX_AUDIO_DEV]; /* * != -1, if there is * a incomplete output * block in the queue. */ static int wr_buff_size[MAX_AUDIO_DEV], wr_buff_ptr[MAX_AUDIO_DEV]; static int audio_mode[MAX_AUDIO_DEV]; static int dev_nblock[MAX_AUDIO_DEV]; /* 1 if in noblocking mode */ #define AM_NONE 0 #define AM_WRITE 1 #define AM_READ 2 static char *wr_dma_buf[MAX_AUDIO_DEV]; static int audio_format[MAX_AUDIO_DEV]; static int local_conversion[MAX_AUDIO_DEV]; static int set_format (int dev, int fmt) { if (fmt != AFMT_QUERY) { local_conversion[dev] = 0; if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */ if (fmt == AFMT_MU_LAW) { fmt = AFMT_U8; local_conversion[dev] = AFMT_MU_LAW; } else fmt = AFMT_U8; /* This is always supported */ audio_format[dev] = DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, fmt, 1); } if (local_conversion[dev]) /* This shadows the HW format */ return local_conversion[dev]; return audio_format[dev]; } int audio_open (int dev, struct fileinfo *file) { int ret; int bits; int dev_type = dev & 0x0f; int mode = file->mode & O_ACCMODE; dev = dev >> 4; if (dev_type == SND_DEV_DSP16) bits = 16; else bits = 8; if ((ret = DMAbuf_open (dev, mode)) < 0) return ret; if (audio_devs[dev]->coproc) if ((ret = audio_devs[dev]->coproc-> open (audio_devs[dev]->coproc->devc, COPR_PCM)) < 0) { audio_release (dev, file); printk ("Sound: Can't access coprocessor device\n"); return ret; } local_conversion[dev] = 0; if (DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, bits, 1) != bits) { audio_release (dev, file); return RET_ERROR (ENXIO); } if (dev_type == SND_DEV_AUDIO) { set_format (dev, AFMT_MU_LAW); } else set_format (dev, bits); wr_buff_no[dev] = -1; audio_mode[dev] = AM_NONE; wr_buff_size[dev] = wr_buff_ptr[dev] = 0; dev_nblock[dev] = 0; return ret; } void audio_release (int dev, struct fileinfo *file) { int mode; dev = dev >> 4; mode = file->mode & O_ACCMODE; if (wr_buff_no[dev] >= 0) { DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); wr_buff_no[dev] = -1; } if (audio_devs[dev]->coproc) audio_devs[dev]->coproc->close (audio_devs[dev]->coproc->devc, COPR_PCM); DMAbuf_release (dev, mode); } #ifdef NO_INLINE_ASM static void translate_bytes (const unsigned char *table, unsigned char *buff, unsigned long n) { unsigned long i; for (i = 0; i < n; ++i) buff[i] = table[buff[i]]; } #else static inline void translate_bytes (const void *table, void *buff, unsigned long n) { __asm__ ("cld\n" "1:\tlodsb\n\t" "xlatb\n\t" "stosb\n\t" "loop 1b\n\t": : "b" ((long) table), "c" (n), "D" ((long) buff), "S" ((long) buff) : "bx", "cx", "di", "si", "ax"); } #endif int audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { int c, p, l; int err; dev = dev >> 4; p = 0; c = count; if (audio_mode[dev] == AM_READ) /* * Direction changed */ { wr_buff_no[dev] = -1; } audio_mode[dev] = AM_WRITE; if (!count) /* * Flush output */ { if (wr_buff_no[dev] >= 0) { DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); wr_buff_no[dev] = -1; } return 0; } while (c) { /* * Perform output blocking */ if (wr_buff_no[dev] < 0) /* * There is no incomplete buffers */ { if ((wr_buff_no[dev] = DMAbuf_getwrbuffer (dev, &wr_dma_buf[dev], &wr_buff_size[dev], dev_nblock[dev])) < 0) { /* Handle nonblocking mode */ #if defined(__FreeBSD__) if (dev_nblock[dev] && wr_buff_no[dev] == RET_ERROR (EWOULDBLOCK)) return wr_buff_no[dev]; /* * XXX Return error, write() will * supply # of accepted bytes. * In fact, in FreeBSD the check * above should not be needed */ #else if (dev_nblock[dev] && wr_buff_no[dev] == RET_ERROR (EAGAIN)) return p; /* No more space. Return # of accepted bytes */ #endif return wr_buff_no[dev]; } wr_buff_ptr[dev] = 0; } l = c; if (l > (wr_buff_size[dev] - wr_buff_ptr[dev])) l = (wr_buff_size[dev] - wr_buff_ptr[dev]); if (!audio_devs[dev]->copy_from_user) { /* * No device specific copy routine */ COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l); } else audio_devs[dev]->copy_from_user (dev, wr_dma_buf[dev], wr_buff_ptr[dev], buf, p, l); /* * Insert local processing here */ if (local_conversion[dev] == AFMT_MU_LAW) { #ifdef linux /* * This just allows interrupts while the conversion is running */ __asm__ ("sti"); #endif translate_bytes (ulaw_dsp, (unsigned char *) &wr_dma_buf[dev][wr_buff_ptr[dev]], l); } c -= l; p += l; wr_buff_ptr[dev] += l; if (wr_buff_ptr[dev] >= wr_buff_size[dev]) { if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev])) < 0) { return err; } wr_buff_no[dev] = -1; } } return count; } int audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { int c, p, l; char *dmabuf; int buff_no; dev = dev >> 4; p = 0; c = count; if (audio_mode[dev] == AM_WRITE) { if (wr_buff_no[dev] >= 0) { DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); wr_buff_no[dev] = -1; } } audio_mode[dev] = AM_READ; while (c) { if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l, dev_nblock[dev])) < 0) { /* Nonblocking mode handling. Return current # of bytes */ #if defined(__FreeBSD__) if (dev_nblock[dev] && buff_no == RET_ERROR (EWOULDBLOCK)) return buff_no; /* * XXX Return error, read() will supply * # of bytes actually read. In fact, * in FreeBSD the check above should not * be needed */ #else if (dev_nblock[dev] && buff_no == RET_ERROR (EAGAIN)) return p; #endif return buff_no; } if (l > c) l = c; /* * Insert any local processing here. */ if (local_conversion[dev] == AFMT_MU_LAW) { #ifdef linux /* * This just allows interrupts while the conversion is running */ __asm__ ("sti"); #endif translate_bytes (dsp_ulaw, (unsigned char *) dmabuf, l); } COPY_TO_USER (buf, p, dmabuf, l); DMAbuf_rmchars (dev, buff_no, l); p += l; c -= l; } return count - c; } int audio_ioctl (int dev, struct fileinfo *file, unsigned int cmd, unsigned int arg) { dev = dev >> 4; if (((cmd >> 8) & 0xff) == 'C') { if (audio_devs[dev]->coproc) /* Coprocessor ioctl */ return audio_devs[dev]->coproc->ioctl (audio_devs[dev]->coproc->devc, cmd, arg, 0); else printk ("/dev/dsp%d: No coprocessor for this device\n", dev); return RET_ERROR (EREMOTEIO); } else switch (cmd) { case SNDCTL_DSP_SYNC: if (wr_buff_no[dev] >= 0) { DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); wr_buff_no[dev] = -1; } return DMAbuf_ioctl (dev, cmd, arg, 0); break; case SNDCTL_DSP_POST: if (wr_buff_no[dev] >= 0) { DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); wr_buff_no[dev] = -1; } return 0; break; case SNDCTL_DSP_RESET: wr_buff_no[dev] = -1; return DMAbuf_ioctl (dev, cmd, arg, 0); break; case SNDCTL_DSP_GETFMTS: return IOCTL_OUT (arg, audio_devs[dev]->format_mask); break; case SNDCTL_DSP_SETFMT: return IOCTL_OUT (arg, set_format (dev, IOCTL_IN (arg))); case SNDCTL_DSP_GETISPACE: if (audio_mode[dev] == AM_WRITE) return RET_ERROR (EBUSY); { audio_buf_info info; int err = DMAbuf_ioctl (dev, cmd, (unsigned long) &info, 1); if (err < 0) return err; if (wr_buff_no[dev] != -1) info.bytes += wr_buff_ptr[dev]; IOCTL_TO_USER ((char *) arg, 0, (char *) &info, sizeof (info)); return 0; } case SNDCTL_DSP_GETOSPACE: if (audio_mode[dev] == AM_READ) return RET_ERROR (EBUSY); { audio_buf_info info; int err = DMAbuf_ioctl (dev, cmd, (unsigned long) &info, 1); if (err < 0) return err; if (wr_buff_no[dev] != -1) info.bytes += wr_buff_size[dev] - wr_buff_ptr[dev]; IOCTL_TO_USER ((char *) arg, 0, (char *) &info, sizeof (info)); return 0; } case SNDCTL_DSP_NONBLOCK: dev_nblock[dev] = 1; return 0; break; #ifdef __FreeBSD__ case FIONBIO: /* XXX Is this the same in Linux? */ if (*(int *)arg) dev_nblock[dev] = 1; else dev_nblock[dev] = 0; return 0; break; case FIOASYNC: return 0; /* XXX Useful for ampling input notification? */ break; #endif default: return DMAbuf_ioctl (dev, cmd, arg, 0); } } long audio_init (long mem_start) { /* * NOTE! This routine could be called several times during boot. */ return mem_start; } #ifdef ALLOW_SELECT int audio_select (int dev, struct fileinfo *file, int sel_type, select_table * wait) { int l; char *dmabuf; dev = dev >> 4; switch (sel_type) { case SEL_IN: if (audio_mode[dev] != AM_READ && /* Wrong direction */ audio_mode[dev] != AM_NONE) return 0; if (DMAbuf_getrdbuffer (dev, &dmabuf, &l, 1 /* Don't block */ ) >= 0) return 1; /* We have data */ return DMAbuf_select (dev, file, sel_type, wait); break; case SEL_OUT: if (audio_mode[dev] != AM_WRITE && /* Wrong direction */ audio_mode[dev] != AM_NONE) return 0; if (wr_buff_no[dev] != -1) return 1; /* There is space in the current buffer */ return DMAbuf_select (dev, file, sel_type, wait); break; case SEL_EX: return 0; } return 0; } #endif /* ALLOW_SELECT */ #else /* EXCLUDE_AUDIO */ /* * Stub versions */ int audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { return RET_ERROR (EIO); } int audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { return RET_ERROR (EIO); } int audio_open (int dev, struct fileinfo *file) { return RET_ERROR (ENXIO); } void audio_release (int dev, struct fileinfo *file) { }; int audio_ioctl (int dev, struct fileinfo *file, unsigned int cmd, unsigned int arg) { return RET_ERROR (EIO); } int audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig) { return RET_ERROR (EIO); } long audio_init (long mem_start) { return mem_start; } #endif #endif Index: head/sys/pc98/pc98/sound/dev_table.c =================================================================== --- head/sys/pc98/pc98/sound/dev_table.c (revision 18264) +++ head/sys/pc98/pc98/sound/dev_table.c (revision 18265) @@ -1,182 +1,182 @@ /* * sound/dev_table.c * * Device call tables. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #define _DEV_TABLE_C_ -#include "sound_config.h" +#include #ifdef CONFIGURE_SOUNDCARD int snd_find_driver (int type) { int i, n = sizeof (sound_drivers) / sizeof (struct driver_info); for (i = 0; i < (n - 1); i++) if (sound_drivers[i].card_type == type) return i; return -1; /* * Not found */ } static long sndtable_init (long mem_start) { int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info); int drv; printk ("Sound initialization started\n"); for (i = 0; i < (n - 1); i++) if (snd_installed_cards[i].enabled) if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1) snd_installed_cards[i].enabled = 0; /* * Mark as not detected */ else if (sound_drivers[drv].probe (&snd_installed_cards[i].config)) { #ifndef SHORT_BANNERS printk ("snd%d", snd_installed_cards[i].card_type); #endif mem_start = sound_drivers[drv].attach (mem_start, &snd_installed_cards[i].config); #ifndef SHORT_BANNERS printk (" at 0x%x irq %d drq %d\n", snd_installed_cards[i].config.io_base, snd_installed_cards[i].config.irq, snd_installed_cards[i].config.dma); #endif } else snd_installed_cards[i].enabled = 0; /* * Mark as not detected */ printk ("Sound initialization complete\n"); return mem_start; } int sndtable_probe (int unit, struct address_info *hw_config) { int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info); if (!unit) return TRUE; for (i = 0; i < (n - 1); i++) if (snd_installed_cards[i].enabled) if (snd_installed_cards[i].card_type == unit) { int drv; snd_installed_cards[i].config.io_base = hw_config->io_base; snd_installed_cards[i].config.irq = hw_config->irq; snd_installed_cards[i].config.dma = hw_config->dma; if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1) snd_installed_cards[i].enabled = 0; /* * Mark as not * detected */ else if (sound_drivers[drv].probe (hw_config)) return 1; snd_installed_cards[i].enabled = 0; /* * Mark as not detected */ return 0; } return FALSE; } int sndtable_init_card (int unit, struct address_info *hw_config) { int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info); if (!unit) { if (sndtable_init (0) != 0) panic ("snd: Invalid memory allocation\n"); return TRUE; } for (i = 0; i < (n - 1); i++) if (snd_installed_cards[i].card_type == unit) { int drv; snd_installed_cards[i].config.io_base = hw_config->io_base; snd_installed_cards[i].config.irq = hw_config->irq; snd_installed_cards[i].config.dma = hw_config->dma; if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1) snd_installed_cards[i].enabled = 0; /* * Mark as not detected */ else if (sound_drivers[drv].attach (0, hw_config) != 0) panic ("snd#: Invalid memory allocation\n"); return TRUE; } return FALSE; } int sndtable_get_cardcount (void) { return num_audiodevs + num_mixers + num_synths + num_midis; } struct address_info * sound_getconf (int card_type) { int j, ptr; int n = sizeof (snd_installed_cards) / sizeof (struct card_info); ptr = -1; for (j = 0; j < n && ptr == -1; j++) if (snd_installed_cards[j].card_type == card_type) ptr = j; if (ptr == -1) return (struct address_info *) NULL; return &snd_installed_cards[ptr].config; } #else void sound_setup (char *str, int *ints) { } #endif Index: head/sys/pc98/pc98/sound/dmabuf.c =================================================================== --- head/sys/pc98/pc98/sound/dmabuf.c (revision 18264) +++ head/sys/pc98/pc98/sound/dmabuf.c (revision 18265) @@ -1,1115 +1,1115 @@ /* * sound/dmabuf.c * * The DMA buffer manager for digitized voice applications * * Copyright by Hannu Savolainen 1993, 1994, 1995 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #ifdef CONFIGURE_SOUNDCARD #if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS) DEFINE_WAIT_QUEUES (dev_sleeper[MAX_AUDIO_DEV], dev_sleep_flag[MAX_AUDIO_DEV]); static struct dma_buffparms dmaps[MAX_AUDIO_DEV] = { {0}}; /* * Primitive way to allocate * such a large array. * Needs dynamic run-time alloction. */ static void reorganize_buffers (int dev) { /* * This routine breaks the physical device buffers to logical ones. */ struct dma_buffparms *dmap = audio_devs[dev]->dmap; struct audio_operations *dsp_dev = audio_devs[dev]; unsigned i, p, n; unsigned sr, nc, sz, bsz; if (dmap->fragment_size == 0) { /* Compute the fragment size using the default algorithm */ sr = dsp_dev->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1); nc = dsp_dev->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1); sz = dsp_dev->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1); if (sr < 1 || nc < 1 || sz < 1) { printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz); sr = DSP_DEFAULT_SPEED; nc = 1; sz = 8; } sz = sr * nc * sz; sz /= 8; /* #bits -> #bytes */ /* * Compute a buffer size for time not exeeding 1 second. * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds * of sound (using the current speed, sample size and #channels). */ bsz = dsp_dev->buffsize; while (bsz > sz) bsz /= 2; if (dsp_dev->buffcount == 1 && bsz == dsp_dev->buffsize) bsz /= 2; /* Needs at least 2 buffers */ if (dmap->subdivision == 0) /* Not already set */ dmap->subdivision = 1; /* Init to default value */ else bsz /= dmap->subdivision; if (bsz < 16) bsz = 16; /* Just a sanity check */ while ((dsp_dev->buffsize * dsp_dev->buffcount) / bsz > MAX_SUB_BUFFERS) bsz *= 2; dmap->fragment_size = bsz; } else { /* * The process has specified the buffer sice with SNDCTL_DSP_SETFRAGMENT or * the buffer sice computation has already been done. */ if (dmap->fragment_size > (audio_devs[dev]->buffsize / 2)) dmap->fragment_size = (audio_devs[dev]->buffsize / 2); bsz = dmap->fragment_size; } bsz &= ~0x03; /* Force size which is multiple of 4 bytes */ /* * Now computing addresses for the logical buffers */ n = 0; for (i = 0; i < dmap->raw_count && n < dmap->max_fragments && n < MAX_SUB_BUFFERS; i++) { p = 0; while ((p + bsz) <= dsp_dev->buffsize && n < dmap->max_fragments && n < MAX_SUB_BUFFERS) { dmap->buf[n] = dmap->raw_buf[i] + p; dmap->buf_phys[n] = dmap->raw_buf_phys[i] + p; p += bsz; n++; } } dmap->nbufs = n; dmap->bytes_in_use = n * bsz; for (i = 0; i < dmap->nbufs; i++) { dmap->counts[i] = 0; } dmap->flags |= DMA_ALLOC_DONE; } static void dma_init_buffers (int dev) { struct dma_buffparms *dmap = audio_devs[dev]->dmap = &dmaps[dev]; RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]); dmap->flags = DMA_BUSY; /* Other flags off */ dmap->qlen = dmap->qhead = dmap->qtail = 0; dmap->nbufs = 1; dmap->bytes_in_use = audio_devs[dev]->buffsize; dmap->dma_mode = DMODE_NONE; } int DMAbuf_open (int dev, int mode) { int retval; struct dma_buffparms *dmap = NULL; if (dev >= num_audiodevs) { printk ("PCM device %d not installed.\n", dev); return RET_ERROR (ENXIO); } if (!audio_devs[dev]) { printk ("PCM device %d not initialized\n", dev); return RET_ERROR (ENXIO); } dmap = audio_devs[dev]->dmap = &dmaps[dev]; if (dmap->flags & DMA_BUSY) return RET_ERROR (EBUSY); #ifdef USE_RUNTIME_DMAMEM dmap->raw_buf[0] = NULL; sound_dma_malloc (dev); #endif if (dmap->raw_buf[0] == NULL) return RET_ERROR (ENOSPC); /* Memory allocation failed during boot */ if ((retval = audio_devs[dev]->open (dev, mode)) < 0) return retval; dmap->open_mode = mode; dmap->subdivision = dmap->underrun_count = 0; dmap->fragment_size = 0; dmap->max_fragments = 65536; /* Just a large value */ dma_init_buffers (dev); audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_BITS, 8, 1); audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_CHANNELS, 1, 1); audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_RATE, DSP_DEFAULT_SPEED, 1); return 0; } static void dma_reset (int dev) { int retval; unsigned long flags; DISABLE_INTR (flags); audio_devs[dev]->reset (dev); audio_devs[dev]->close (dev); if ((retval = audio_devs[dev]->open (dev, audio_devs[dev]->dmap->open_mode)) < 0) printk ("Sound: Reset failed - Can't reopen device\n"); RESTORE_INTR (flags); dma_init_buffers (dev); reorganize_buffers (dev); } static int dma_sync (int dev) { unsigned long flags; if (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT) { DISABLE_INTR (flags); while (!PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) && audio_devs[dev]->dmap->qlen) { DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 10 * HZ); if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev])) { RESTORE_INTR (flags); return audio_devs[dev]->dmap->qlen; } } RESTORE_INTR (flags); /* * Some devices such as GUS have huge amount of on board RAM for the * audio data. We have to wait until the device has finished playing. */ DISABLE_INTR (flags); if (audio_devs[dev]->local_qlen) /* Device has hidden buffers */ { while (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])) && audio_devs[dev]->local_qlen (dev)) { DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], HZ); } } RESTORE_INTR (flags); } return audio_devs[dev]->dmap->qlen; } int DMAbuf_release (int dev, int mode) { unsigned long flags; if (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])) && (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT)) { dma_sync (dev); } #ifdef USE_RUNTIME_DMAMEM sound_dma_free (dev); #endif DISABLE_INTR (flags); audio_devs[dev]->reset (dev); audio_devs[dev]->close (dev); audio_devs[dev]->dmap->dma_mode = DMODE_NONE; audio_devs[dev]->dmap->flags &= ~DMA_BUSY; RESTORE_INTR (flags); return 0; } int DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock) { unsigned long flags; int err = EIO; struct dma_buffparms *dmap = audio_devs[dev]->dmap; DISABLE_INTR (flags); if (!dmap->qlen) { if (dmap->flags & DMA_RESTART) { dma_reset (dev); dmap->flags &= ~DMA_RESTART; } if (dmap->dma_mode == DMODE_OUTPUT) /* Direction change */ { dma_sync (dev); dma_reset (dev); dmap->dma_mode = DMODE_NONE; } if (!(dmap->flags & DMA_ALLOC_DONE)) reorganize_buffers (dev); if (!dmap->dma_mode) { int err; if ((err = audio_devs[dev]->prepare_for_input (dev, dmap->fragment_size, dmap->nbufs)) < 0) { RESTORE_INTR (flags); return err; } dmap->dma_mode = DMODE_INPUT; } if (!(dmap->flags & DMA_ACTIVE)) { audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail], dmap->fragment_size, 0, !(audio_devs[dev]->flags & DMA_AUTOMODE) || !(dmap->flags & DMA_STARTED)); dmap->flags |= DMA_ACTIVE | DMA_STARTED; } if (dontblock) { RESTORE_INTR (flags); #if defined(__FreeBSD__) return RET_ERROR (EWOULDBLOCK); #else return RET_ERROR (EAGAIN); #endif } /* Wait for the next block */ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ); if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev])) { printk ("Sound: DMA timed out - IRQ/DRQ config error?\n"); dma_reset (dev); err = EIO; SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]); } else err = EINTR; } RESTORE_INTR (flags); if (!dmap->qlen) return RET_ERROR (err); *buf = &dmap->buf[dmap->qhead][dmap->counts[dmap->qhead]]; *len = dmap->fragment_size - dmap->counts[dmap->qhead]; return dmap->qhead; } int DMAbuf_rmchars (int dev, int buff_no, int c) { struct dma_buffparms *dmap = audio_devs[dev]->dmap; int p = dmap->counts[dmap->qhead] + c; if (p >= dmap->fragment_size) { /* This buffer is completely empty */ dmap->counts[dmap->qhead] = 0; if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) printk ("\nSound: Audio queue1 corrupted for dev%d (%d/%d)\n", dev, dmap->qlen, dmap->nbufs); dmap->qlen--; dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; } else dmap->counts[dmap->qhead] = p; return 0; } int DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) { struct dma_buffparms *dmap = audio_devs[dev]->dmap; switch (cmd) { case SNDCTL_DSP_RESET: dma_reset (dev); return 0; break; case SNDCTL_DSP_SYNC: dma_sync (dev); dma_reset (dev); return 0; break; case SNDCTL_DSP_GETBLKSIZE: if (!(dmap->flags & DMA_ALLOC_DONE)) reorganize_buffers (dev); return IOCTL_OUT (arg, dmap->fragment_size); break; case SNDCTL_DSP_SETBLKSIZE: { int size = IOCTL_IN(arg); if(!(dmap->flags & DMA_ALLOC_DONE) && size) { dmap->fragment_size = size; return 0; } else return RET_ERROR (EINVAL); /* Too late to change */ } break; case SNDCTL_DSP_SUBDIVIDE: { int fact = IOCTL_IN (arg); if (fact == 0) { fact = dmap->subdivision; if (fact == 0) fact = 1; return IOCTL_OUT (arg, fact); } if (dmap->subdivision != 0 || dmap->fragment_size) /* Loo late to change */ return RET_ERROR (EINVAL); if (fact > MAX_REALTIME_FACTOR) return RET_ERROR (EINVAL); if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16) return RET_ERROR (EINVAL); dmap->subdivision = fact; return IOCTL_OUT (arg, fact); } break; case SNDCTL_DSP_SETFRAGMENT: { int fact = IOCTL_IN (arg); int bytes, count; if (fact == 0) return RET_ERROR (EIO); if (dmap->subdivision != 0 || dmap->fragment_size) /* Loo late to change */ return RET_ERROR (EINVAL); bytes = fact & 0xffff; count = (fact >> 16) & 0xffff; if (count == 0) count = MAX_SUB_BUFFERS; if (bytes < 7 || bytes > 17) /* <64 || > 128k */ return RET_ERROR (EINVAL); if (count < 2) return RET_ERROR (EINVAL); dmap->fragment_size = (1 << bytes); dmap->max_fragments = count; if (dmap->fragment_size > audio_devs[dev]->buffsize) dmap->fragment_size = audio_devs[dev]->buffsize; if (dmap->fragment_size == audio_devs[dev]->buffsize && audio_devs[dev]->flags & DMA_AUTOMODE) dmap->fragment_size /= 2; /* Needs at least 2 buffers */ dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */ return IOCTL_OUT (arg, bytes | (count << 16)); } break; case SNDCTL_DSP_GETISPACE: case SNDCTL_DSP_GETOSPACE: if (!local) return RET_ERROR (EINVAL); { audio_buf_info *info = (audio_buf_info *) arg; info->fragments = dmap->qlen; info->fragsize = dmap->fragment_size; info->bytes = dmap->qlen * dmap->fragment_size; } return 0; default: return audio_devs[dev]->ioctl (dev, cmd, arg, local); } } static int space_in_queue (int dev) { int len, max, tmp; struct dma_buffparms *dmap = audio_devs[dev]->dmap; if (dmap->qlen >= dmap->nbufs) /* No space at all */ return 0; /* * Verify that there are no more pending buffers than the limit * defined by the process. */ max = dmap->max_fragments; len = dmap->qlen; if (audio_devs[dev]->local_qlen) { tmp = audio_devs[dev]->local_qlen (dev); if (tmp & len) tmp--; /* * This buffer has been counted twice */ len += tmp; } if (len >= max) return 0; return 1; } int DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock) { unsigned long flags; int abort, err = EIO; struct dma_buffparms *dmap = audio_devs[dev]->dmap; if (dmap->dma_mode == DMODE_INPUT) /* Direction change */ { dma_reset (dev); dmap->dma_mode = DMODE_NONE; } else if (dmap->flags & DMA_RESTART) /* Restart buffering */ { dma_sync (dev); dma_reset (dev); } dmap->flags &= ~DMA_RESTART; if (!(dmap->flags & DMA_ALLOC_DONE)) reorganize_buffers (dev); if (!dmap->dma_mode) { int err; dmap->dma_mode = DMODE_OUTPUT; if ((err = audio_devs[dev]->prepare_for_output (dev, dmap->fragment_size, dmap->nbufs)) < 0) return err; } DISABLE_INTR (flags); abort = 0; while (!space_in_queue (dev) && !abort) { if (dontblock) { RESTORE_INTR (flags); return RET_ERROR (EAGAIN); } /* * Wait for free space */ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ); if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev])) { printk ("Sound: DMA timed out - IRQ/DRQ config error?\n"); dma_reset (dev); err = EIO; abort = 1; SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]); } else if (PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])) { err = EINTR; abort = 1; } } RESTORE_INTR (flags); if (!space_in_queue (dev)) { return RET_ERROR (err); /* Caught a signal ? */ } *buf = dmap->buf[dmap->qtail]; *size = dmap->fragment_size; dmap->counts[dmap->qtail] = 0; return dmap->qtail; } int DMAbuf_start_output (int dev, int buff_no, int l) { struct dma_buffparms *dmap = audio_devs[dev]->dmap; if (buff_no != dmap->qtail) printk ("Sound warning: DMA buffers out of sync %d != %d\n", buff_no, dmap->qtail); dmap->qlen++; if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) printk ("\nSound: Audio queue2 corrupted for dev%d (%d/%d)\n", dev, dmap->qlen, dmap->nbufs); dmap->counts[dmap->qtail] = l; if ((l != dmap->fragment_size) && ((audio_devs[dev]->flags & DMA_AUTOMODE) && audio_devs[dev]->flags & NEEDS_RESTART)) dmap->flags |= DMA_RESTART; else dmap->flags &= ~DMA_RESTART; dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; if (!(dmap->flags & DMA_ACTIVE)) { dmap->flags |= DMA_ACTIVE; audio_devs[dev]->output_block (dev, dmap->buf_phys[dmap->qhead], dmap->counts[dmap->qhead], 0, !(audio_devs[dev]->flags & DMA_AUTOMODE) || !(dmap->flags & DMA_STARTED)); dmap->flags |= DMA_STARTED; } return 0; } int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) { int chan = audio_devs[dev]->dmachan; struct dma_buffparms *dmap = audio_devs[dev]->dmap; #ifdef linux unsigned long flags; #endif /* * This function is not as portable as it should be. */ /* * The count must be one less than the actual size. This is handled by * set_dma_addr() */ if (audio_devs[dev]->flags & DMA_AUTOMODE) { /* * Auto restart mode. Transfer the whole * * buffer */ #ifdef linux DISABLE_INTR (flags); disable_dma (chan); clear_dma_ff (chan); set_dma_mode (chan, dma_mode | DMA_AUTOINIT); set_dma_addr (chan, dmap->raw_buf_phys[0]); set_dma_count (chan, dmap->bytes_in_use); enable_dma (chan); RESTORE_INTR (flags); #else #if defined(__FreeBSD__) isa_dmastart (B_RAW | ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE), (caddr_t)dmap->raw_buf_phys[0], dmap->bytes_in_use, chan); #else /* else __FreeBSD__ */ #if defined(GENERIC_SYSV) #ifndef DMAMODE_AUTO printk ("sound: Invalid DMA mode for device %d\n", dev); #endif #if defined(SVR42) /* ** send full count to snd_dma_prog, it will take care of subtracting ** one if it is required. */ snd_dma_prog (chan, dmap->raw_buf_phys[0], dmap->bytes_in_use, dma_mode, TRUE); #else /* !SVR42 */ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode) #ifdef DMAMODE_AUTO | DMAMODE_AUTO #endif , dmap->raw_buf_phys[0], dmap->bytes_in_use - 1); dma_enable (chan); #endif /* ! SVR42 */ #else #error This routine is not valid for this OS. #endif #endif #endif } else { #ifdef linux DISABLE_INTR (flags); disable_dma (chan); clear_dma_ff (chan); set_dma_mode (chan, dma_mode); set_dma_addr (chan, physaddr); set_dma_count (chan, count); enable_dma (chan); RESTORE_INTR (flags); #else #if defined(__FreeBSD__) isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE, (caddr_t)physaddr, count, chan); #else /* FreeBSD */ #if defined(GENERIC_SYSV) #if defined(SVR42) snd_dma_prog (chan, physaddr, count, dma_mode, FALSE); #else /* ! SVR42 */ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode), physaddr, count); dma_enable (chan); #endif /* SVR42 */ #else #error This routine is not valid for this OS. #endif /* GENERIC_SYSV */ #endif #endif } return count; } long DMAbuf_init (long mem_start) { int dev; #if defined(SVR42) snd_dma_init (); #endif /* SVR42 */ /* * NOTE! This routine could be called several times. */ for (dev = 0; dev < num_audiodevs; dev++) audio_devs[dev]->dmap = &dmaps[dev]; return mem_start; } void DMAbuf_outputintr (int dev, int event_type) { /* * Event types: * 0 = DMA transfer done. Device still has more data in the local * buffer. * 1 = DMA transfer done. Device doesn't have local buffer or it's * empty now. * 2 = No DMA transfer but the device has now more space in it's local * buffer. */ unsigned long flags; struct dma_buffparms *dmap = audio_devs[dev]->dmap; #if defined(SVR42) snd_dma_intr (audio_devs[dev]->dmachan); #endif /* SVR42 */ if (event_type != 2) { if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) { printk ("\nSound: Audio queue3 corrupted for dev%d (%d/%d)\n", dev, dmap->qlen, dmap->nbufs); return; } dmap->qlen--; dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; dmap->flags &= ~DMA_ACTIVE; #ifdef __FreeBSD__ isa_dmadone(0, 0, 0, audio_devs[dev]->dmachan); #endif if (dmap->qlen) { audio_devs[dev]->output_block (dev, dmap->buf_phys[dmap->qhead], dmap->counts[dmap->qhead], 1, !(audio_devs[dev]->flags & DMA_AUTOMODE)); dmap->flags |= DMA_ACTIVE; } else if (event_type == 1) { dmap->underrun_count++; audio_devs[dev]->halt_xfer (dev); if ((audio_devs[dev]->flags & DMA_AUTOMODE) && audio_devs[dev]->flags & NEEDS_RESTART) dmap->flags |= DMA_RESTART; else dmap->flags &= ~DMA_RESTART; } } /* event_type != 2 */ DISABLE_INTR (flags); if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev])) { WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]); } RESTORE_INTR (flags); #if defined(__FreeBSD__) if(selinfo[dev].si_pid) selwakeup(&selinfo[dev]); #endif } void DMAbuf_inputintr (int dev) { unsigned long flags; struct dma_buffparms *dmap = audio_devs[dev]->dmap; #if defined(SVR42) snd_dma_intr (audio_devs[dev]->dmachan); #endif /* SVR42 */ #ifdef __FreeBSD__ isa_dmadone(0, 0, 0, audio_devs[dev]->dmachan); #endif if (dmap->qlen == (dmap->nbufs - 1)) { #if !defined(__FreeBSD__) /* ignore console message. */ printk ("Sound: Recording overrun\n"); #endif dmap->underrun_count++; audio_devs[dev]->halt_xfer (dev); dmap->flags &= ~DMA_ACTIVE; if (audio_devs[dev]->flags & DMA_AUTOMODE) dmap->flags |= DMA_RESTART; else dmap->flags &= ~DMA_RESTART; } else { dmap->qlen++; if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) printk ("\nSound: Audio queue4 corrupted for dev%d (%d/%d)\n", dev, dmap->qlen, dmap->nbufs); dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail], dmap->fragment_size, 1, !(audio_devs[dev]->flags & DMA_AUTOMODE)); dmap->flags |= DMA_ACTIVE; } DISABLE_INTR (flags); if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev])) { WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]); } RESTORE_INTR (flags); #if defined(__FreeBSD__) if(selinfo[dev].si_pid) selwakeup(&selinfo[dev]); #endif } int DMAbuf_open_dma (int dev) { unsigned long flags; int chan = audio_devs[dev]->dmachan; if (ALLOC_DMA_CHN (chan, audio_devs[dev]->name)) { #if 0 /* Enough already! This error is reported twice elsewhere */ printk ("Unable to grab DMA%d for the audio driver\n", chan); #endif return RET_ERROR (EBUSY); } DISABLE_INTR (flags); #ifdef linux disable_dma (chan); clear_dma_ff (chan); #endif RESTORE_INTR (flags); return 0; } void DMAbuf_close_dma (int dev) { int chan = audio_devs[dev]->dmachan; DMAbuf_reset_dma (dev); RELEASE_DMA_CHN (chan); } void DMAbuf_reset_dma (int dev) { } #ifdef ALLOW_SELECT int DMAbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait) { struct dma_buffparms *dmap = audio_devs[dev]->dmap; unsigned long flags; switch (sel_type) { case SEL_IN: if (dmap->dma_mode != DMODE_INPUT) return 0; DISABLE_INTR (flags); if (!dmap->qlen) { #if defined(__FreeBSD__) selrecord(wait, &selinfo[dev]); #else dev_sleep_flag[dev].mode = WK_SLEEP; select_wait (&dev_sleeper[dev], wait); #endif RESTORE_INTR (flags); return 0; } RESTORE_INTR (flags); return 1; break; case SEL_OUT: if (dmap->dma_mode == DMODE_INPUT) return 0; if (dmap->dma_mode == DMODE_NONE) return 1; DISABLE_INTR (flags); if (!space_in_queue (dev)) { #if defined(__FreeBSD__) selrecord(wait, &selinfo[dev]); #else dev_sleep_flag[dev].mode = WK_SLEEP; select_wait (&dev_sleeper[dev], wait); #endif RESTORE_INTR (flags); return 0; } RESTORE_INTR (flags); return 1; break; case SEL_EX: return 0; } return 0; } #endif /* ALLOW_SELECT */ #else /* EXCLUDE_AUDIO */ /* * Stub versions if audio services not included */ int DMAbuf_open (int dev, int mode) { return RET_ERROR (ENXIO); } int DMAbuf_release (int dev, int mode) { return 0; } int DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock) { return RET_ERROR (EIO); } int DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock) { return RET_ERROR (EIO); } int DMAbuf_rmchars (int dev, int buff_no, int c) { return RET_ERROR (EIO); } int DMAbuf_start_output (int dev, int buff_no, int l) { return RET_ERROR (EIO); } int DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) { return RET_ERROR (EIO); } long DMAbuf_init (long mem_start) { return mem_start; } int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) { return RET_ERROR (EIO); } int DMAbuf_open_dma (int dev) { return RET_ERROR (ENXIO); } void DMAbuf_close_dma (int dev) { return; } void DMAbuf_reset_dma (int dev) { return; } void DMAbuf_inputintr (int dev) { return; } void DMAbuf_outputintr (int dev, int underrun_flag) { return; } #endif #endif Index: head/sys/pc98/pc98/sound/gus_card.c =================================================================== --- head/sys/pc98/pc98/sound/gus_card.c (revision 18264) +++ head/sys/pc98/pc98/sound/gus_card.c (revision 18265) @@ -1,197 +1,197 @@ /* * sound/gus_card.c * * Detection routine for the Gravis Ultrasound. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS) -#include "gus_hw.h" +#include int gus_base, gus_irq, gus_dma; extern int gus_wave_volume; extern int gus_pcm_volume; extern int have_gus_max; long attach_gus_card (long mem_start, struct address_info *hw_config) { int io_addr; snd_set_irq_handler (hw_config->irq, gusintr, "Gravis Ultrasound"); if (gus_wave_detect (hw_config->io_base)) /* * Try first the default */ { mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma, hw_config->dma_read); #ifndef EXCLUDE_MIDI mem_start = gus_midi_init (mem_start); #endif #ifndef EXCLUDE_SEQUENCER sound_timer_init (hw_config->io_base + 8); #endif return mem_start; } #ifndef EXCLUDE_GUS_IODETECT /* * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6) */ for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) if (io_addr != hw_config->io_base) /* * Already tested */ if (gus_wave_detect (io_addr)) { printk (" WARNING! GUS found at %x, config was %x ", io_addr, hw_config->io_base); mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma, hw_config->dma_read); #ifndef EXCLUDE_MIDI mem_start = gus_midi_init (mem_start); #endif #ifndef EXCLUDE_SEQUENCER sound_timer_init (io_addr + 8); #endif return mem_start; } #endif return mem_start; /* * Not detected */ } int probe_gus (struct address_info *hw_config) { int io_addr; if (gus_wave_detect (hw_config->io_base)) return 1; #ifndef EXCLUDE_GUS_IODETECT /* * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6) */ for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) if (io_addr != hw_config->io_base) /* * Already tested */ if (gus_wave_detect (io_addr)) return 1; #endif return 0; } void gusintr (INT_HANDLER_PARMS (irq, dummy)) { unsigned char src; #ifdef linux sti (); #endif #ifndef EXCLUDE_GUSMAX if (have_gus_max) # if defined(__FreeBSD__) ad1848_interrupt (INT_HANDLER_CALL (gus_irq)); # else ad1848_interrupt (INT_HANDLER_CALL (irq)); # endif #endif while (1) { if (!(src = INB (u_IrqStatus))) return; if (src & DMA_TC_IRQ) { guswave_dma_irq (); } if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ)) { #ifndef EXCLUDE_MIDI gus_midi_interrupt (0); #endif } if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ)) { #ifndef EXCLUDE_SEQUENCER sound_timer_interrupt (); #else gus_write8 (0x45, 0); /* Stop timers */ #endif } if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ)) { gus_voice_irq (); } } } #endif /* * Some extra code for the 16 bit sampling option */ #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS16) int probe_gus_db16 (struct address_info *hw_config) { return ad1848_detect (hw_config->io_base); } long attach_gus_db16 (long mem_start, struct address_info *hw_config) { gus_pcm_volume = 100; gus_wave_volume = 90; ad1848_init ("GUS 16 bit sampling", hw_config->io_base, hw_config->irq, hw_config->dma, hw_config->dma); return mem_start; } #endif Index: head/sys/pc98/pc98/sound/gus_midi.c =================================================================== --- head/sys/pc98/pc98/sound/gus_midi.c (revision 18264) +++ head/sys/pc98/pc98/sound/gus_midi.c (revision 18265) @@ -1,312 +1,312 @@ /* * sound/gus2_midi.c * * The low level driver for the GUS Midi Interface. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #ifdef CONFIGURE_SOUNDCARD -#include "gus_hw.h" +#include #if !defined(EXCLUDE_GUS) && !defined(EXCLUDE_MIDI) static int midi_busy = 0, input_opened = 0; static int my_dev; static int output_used = 0; static volatile unsigned char gus_midi_control; static void (*midi_input_intr) (int dev, unsigned char data); static unsigned char tmp_queue[256]; static volatile int qlen; static volatile unsigned char qhead, qtail; extern int gus_base, gus_irq, gus_dma; #define GUS_MIDI_STATUS() INB(u_MidiStatus) static int gus_midi_open (int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev) ) { if (midi_busy) { printk ("GUS: Midi busy\n"); return RET_ERROR (EBUSY); } OUTB (MIDI_RESET, u_MidiControl); gus_delay (); gus_midi_control = 0; input_opened = 0; if (mode == OPEN_READ || mode == OPEN_READWRITE) { gus_midi_control |= MIDI_ENABLE_RCV; input_opened = 1; } if (mode == OPEN_WRITE || mode == OPEN_READWRITE) { gus_midi_control |= MIDI_ENABLE_XMIT; } OUTB (gus_midi_control, u_MidiControl); /* * Enable */ midi_busy = 1; qlen = qhead = qtail = output_used = 0; midi_input_intr = input; return 0; } static int dump_to_midi (unsigned char midi_byte) { unsigned long flags; int ok = 0; output_used = 1; DISABLE_INTR (flags); if (GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY) { ok = 1; OUTB (midi_byte, u_MidiData); } else { /* * Enable Midi xmit interrupts (again) */ gus_midi_control |= MIDI_ENABLE_XMIT; OUTB (gus_midi_control, u_MidiControl); } RESTORE_INTR (flags); return ok; } static void gus_midi_close (int dev) { /* * Reset FIFO pointers, disable intrs */ OUTB (MIDI_RESET, u_MidiControl); midi_busy = 0; } static int gus_midi_out (int dev, unsigned char midi_byte) { unsigned long flags; /* * Drain the local queue first */ DISABLE_INTR (flags); while (qlen && dump_to_midi (tmp_queue[qhead])) { qlen--; qhead++; } RESTORE_INTR (flags); /* * Output the byte if the local queue is empty. */ if (!qlen) if (dump_to_midi (midi_byte)) return 1; /* * OK */ /* * Put to the local queue */ if (qlen >= 256) return 0; /* * Local queue full */ DISABLE_INTR (flags); tmp_queue[qtail] = midi_byte; qlen++; qtail++; RESTORE_INTR (flags); return 1; } static int gus_midi_start_read (int dev) { return 0; } static int gus_midi_end_read (int dev) { return 0; } static int gus_midi_ioctl (int dev, unsigned cmd, unsigned arg) { return RET_ERROR (EINVAL); } static void gus_midi_kick (int dev) { } static int gus_midi_buffer_status (int dev) { unsigned long flags; if (!output_used) return 0; DISABLE_INTR (flags); if (qlen && dump_to_midi (tmp_queue[qhead])) { qlen--; qhead++; } RESTORE_INTR (flags); return (qlen > 0) | !(GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY); } #define MIDI_SYNTH_NAME "Gravis Ultrasound Midi" #define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" +#include static struct midi_operations gus_midi_operations = { {"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS}, &std_midi_synth, {0}, gus_midi_open, gus_midi_close, gus_midi_ioctl, gus_midi_out, gus_midi_start_read, gus_midi_end_read, gus_midi_kick, NULL, /* * command */ gus_midi_buffer_status, NULL }; long gus_midi_init (long mem_start) { if (num_midis >= MAX_MIDI_DEV) { printk ("Sound: Too many midi devices detected\n"); return mem_start; } OUTB (MIDI_RESET, u_MidiControl); std_midi_synth.midi_dev = my_dev = num_midis; midi_devs[num_midis++] = &gus_midi_operations; return mem_start; } void gus_midi_interrupt (int dummy) { unsigned char stat, data; unsigned long flags; DISABLE_INTR (flags); stat = GUS_MIDI_STATUS (); if (stat & MIDI_RCV_FULL) { data = INB (u_MidiData); if (input_opened) midi_input_intr (my_dev, data); } if (stat & MIDI_XMIT_EMPTY) { while (qlen && dump_to_midi (tmp_queue[qhead])) { qlen--; qhead++; } if (!qlen) { /* * Disable Midi output interrupts, since no data in the buffer */ gus_midi_control &= ~MIDI_ENABLE_XMIT; OUTB (gus_midi_control, u_MidiControl); } } #if 0 if (stat & MIDI_FRAME_ERR) printk ("GUS: Midi framing error\n"); if (stat & MIDI_OVERRUN && input_opened) printk ("GUS: Midi input overrun\n"); #endif RESTORE_INTR (flags); } #endif #endif Index: head/sys/pc98/pc98/sound/gus_vol.c =================================================================== --- head/sys/pc98/pc98/sound/gus_vol.c (revision 18264) +++ head/sys/pc98/pc98/sound/gus_vol.c (revision 18265) @@ -1,150 +1,150 @@ /* * gus_vol.c - Compute volume for GUS. * * Greg Lee 1993. */ -#include "sound_config.h" +#include #ifndef EXCLUDE_GUS -#include "gus_linearvol.h" +#include extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev); extern unsigned short gus_linear_vol (int vol, int mainvol); #define GUS_VOLUME gus_wave_volume extern int gus_wave_volume; /* * Calculate gus volume from note velocity, main volume, expression, and * intrinsic patch volume given in patch library. Expression is multiplied * in, so it emphasizes differences in note velocity, while main volume is * added in -- I don't know whether this is right, but it seems reasonable to * me. (In the previous stage, main volume controller messages were changed * to expression controller messages, if they were found to be used for * dynamic volume adjustments, so here, main volume can be assumed to be * constant throughout a song.) * * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so * we can give a big boost to very weak voices like nylon guitar and the * basses. The normal value is 64. Strings are assigned lower values. */ unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev) { int i, m, n, x; /* * A voice volume of 64 is considered neutral, so adjust the main volume if * something other than this neutral value was assigned in the patch * library. */ x = 256 + 6 * (voicev - 64); /* * Boost expression by voice volume above neutral. */ if (voicev > 65) xpn += voicev - 64; xpn += (voicev - 64) / 2; /* * Combine multiplicative and level components. */ x = vel * xpn * 6 + (voicev / 4) * x; #ifdef GUS_VOLUME /* * Further adjustment by installation-specific master volume control * (default 60). */ x = (x * GUS_VOLUME * GUS_VOLUME) / 10000; #endif #ifdef GUS_USE_CHN_MAIN_VOLUME /* * Experimental support for the channel main volume */ mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */ x = (x * mainv * mainv) / 16384; #endif if (x < 2) return (0); else if (x >= 65535) return ((15 << 8) | 255); /* * Convert to gus's logarithmic form with 4 bit exponent i and 8 bit * mantissa m. */ n = x; i = 7; if (n < 128) { while (i > 0 && n < (1 << i)) i--; } else while (n > 255) { n >>= 1; i++; } /* * Mantissa is part of linear volume not expressed in exponent. (This is * not quite like real logs -- I wonder if it's right.) */ m = x - (1 << i); /* * Adjust mantissa to 8 bits. */ if (m > 0) { if (i > 8) m >>= i - 8; else if (i < 8) m <<= 8 - i; } return ((i << 8) + m); } /* * Volume-values are interpreted as linear values. Volume is based on the * value supplied with SEQ_START_NOTE(), channel main volume (if compiled in) * and the volume set by the mixer-device (default 60%). */ unsigned short gus_linear_vol (int vol, int mainvol) { int mixer_mainvol; if (vol <= 0) vol = 0; else if (vol >= 127) vol = 127; #ifdef GUS_VOLUME mixer_mainvol = GUS_VOLUME; #else mixer_mainvol = 100; #endif #ifdef GUS_USE_CHN_MAIN_VOLUME if (mainvol <= 0) mainvol = 0; else if (mainvol >= 127) mainvol = 127; #else mainvol = 128; #endif return gus_linearvol[(((vol * mainvol) / 128) * mixer_mainvol) / 100]; } #endif Index: head/sys/pc98/pc98/sound/gus_wave.c =================================================================== --- head/sys/pc98/pc98/sound/gus_wave.c (revision 18264) +++ head/sys/pc98/pc98/sound/gus_wave.c (revision 18265) @@ -1,3445 +1,3445 @@ /* * sound/gus_wave.c * * Driver for the Gravis UltraSound wave table synth. * * Copyright by Hannu Savolainen 1993, 1994 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #include -#include "gus_hw.h" +#include static unsigned char gus_look8 __P((int reg)); static unsigned short gus_read16 __P((int reg)); static void gus_write_addr __P((int reg, unsigned long address, int is16bit)); static void gus_write16 __P((int reg, unsigned int data)); #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS) #define MAX_SAMPLE 150 #define MAX_PATCH 256 struct voice_info { unsigned long orig_freq; unsigned long current_freq; unsigned long mode; int bender; int bender_range; int panning; int midi_volume; unsigned int initial_volume; unsigned int current_volume; int loop_irq_mode, loop_irq_parm; #define LMODE_FINISH 1 #define LMODE_PCM 2 #define LMODE_PCM_STOP 3 int volume_irq_mode, volume_irq_parm; #define VMODE_HALT 1 #define VMODE_ENVELOPE 2 #define VMODE_START_NOTE 3 int env_phase; unsigned char env_rate[6]; unsigned char env_offset[6]; /* * Volume computation parameters for gus_adagio_vol() */ int main_vol, expression_vol, patch_vol; /* Variables for "Ultraclick" removal */ int dev_pending, note_pending, volume_pending, sample_pending; char kill_pending; long offset_pending; }; static struct voice_alloc_info *voice_alloc; extern int gus_base; extern int gus_irq, gus_dma; static long gus_mem_size = 0; static long free_mem_ptr = 0; static int gus_busy[MAX_AUDIO_DEV], gus_dspnum=0; static int gus_dma_read=0; static int nr_voices = 0; static int gus_devnum = 0; static int volume_base, volume_scale, volume_method; static int gus_recmask = SOUND_MASK_MIC; static int recording_active = 0; static int only_read_access = 0; int gus_wave_volume = 60; static int gus_pcm_volume = 80; int have_gus_max = 0; static int gus_line_vol = 100, gus_mic_vol = 0; static unsigned char mix_image = 0x00; /* * Current version of this driver doesn't allow synth and PCM functions * at the same time. The active_device specifies the active driver */ static int active_device = 0; #define GUS_DEV_WAVE 1 /* Wave table synth */ #define GUS_DEV_PCM_DONE 2 /* PCM device, transfer done */ #define GUS_DEV_PCM_CONTINUE 3 /* PCM device, transfer done ch. 1/2 */ static int gus_sampling_speed; static int gus_sampling_channels; static int gus_sampling_bits; DEFINE_WAIT_QUEUE (dram_sleeper, dram_sleep_flag); /* * Variables and buffers for PCM output */ #define MAX_PCM_BUFFERS (32*MAX_REALTIME_FACTOR) /* Don't change */ static int pcm_bsize, pcm_nblk, pcm_banksize; static int pcm_datasize[MAX_PCM_BUFFERS]; static volatile int pcm_head, pcm_tail, pcm_qlen; static volatile int pcm_active; static volatile int dma_active; static int pcm_opened = 0; static int pcm_current_dev; static int pcm_current_block; static unsigned long pcm_current_buf; static int pcm_current_count; static int pcm_current_intrflag; #if defined(__FreeBSD__) static char *gus_copy_buf; #endif static struct voice_info voices[32]; static int freq_div_table[] = { 44100, /* 14 */ 41160, /* 15 */ 38587, /* 16 */ 36317, /* 17 */ 34300, /* 18 */ 32494, /* 19 */ 30870, /* 20 */ 29400, /* 21 */ 28063, /* 22 */ 26843, /* 23 */ 25725, /* 24 */ 24696, /* 25 */ 23746, /* 26 */ 22866, /* 27 */ 22050, /* 28 */ 21289, /* 29 */ 20580, /* 30 */ 19916, /* 31 */ 19293 /* 32 */ }; static struct patch_info *samples; static long sample_ptrs[MAX_SAMPLE + 1]; static int sample_map[32]; static int free_sample; static int patch_table[MAX_PATCH]; static int patch_map[32]; static struct synth_info gus_info = {"Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH}; static void gus_poke (long addr, unsigned char data); static void compute_and_set_volume (int voice, int volume, int ramp_time); extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev); extern unsigned short gus_linear_vol (int vol, int mainvol); static void compute_volume (int voice, int volume); static void do_volume_irq (int voice); static void set_input_volumes (void); #define INSTANT_RAMP -1 /* Instant change. No ramping */ #define FAST_RAMP 0 /* Fastest possible ramp */ static void reset_sample_memory (void) { int i; for (i = 0; i <= MAX_SAMPLE; i++) sample_ptrs[i] = -1; for (i = 0; i < 32; i++) sample_map[i] = -1; for (i = 0; i < 32; i++) patch_map[i] = -1; gus_poke (0, 0); /* Put a silent sample to the beginning */ gus_poke (1, 0); free_mem_ptr = 2; free_sample = 0; for (i = 0; i < MAX_PATCH; i++) patch_table[i] = -1; } void gus_delay (void) { int i; for (i = 0; i < 7; i++) INB (u_DRAMIO); } static void gus_poke (long addr, unsigned char data) { /* Writes a byte to the DRAM */ unsigned long flags; DISABLE_INTR (flags); OUTB (0x43, u_Command); OUTB (addr & 0xff, u_DataLo); OUTB ((addr >> 8) & 0xff, u_DataHi); OUTB (0x44, u_Command); OUTB ((addr >> 16) & 0xff, u_DataHi); OUTB (data, u_DRAMIO); RESTORE_INTR (flags); } static unsigned char gus_peek (long addr) { /* Reads a byte from the DRAM */ unsigned long flags; unsigned char tmp; DISABLE_INTR (flags); OUTB (0x43, u_Command); OUTB (addr & 0xff, u_DataLo); OUTB ((addr >> 8) & 0xff, u_DataHi); OUTB (0x44, u_Command); OUTB ((addr >> 16) & 0xff, u_DataHi); tmp = INB (u_DRAMIO); RESTORE_INTR (flags); return tmp; } void gus_write8 (int reg, unsigned int data) { /* Writes to an indirect register (8 bit) */ unsigned long flags; DISABLE_INTR (flags); OUTB (reg, u_Command); OUTB ((unsigned char) (data & 0xff), u_DataHi); RESTORE_INTR (flags); } static unsigned char gus_read8 (int reg) { /* Reads from an indirect register (8 bit). Offset 0x80. */ unsigned long flags; unsigned char val; DISABLE_INTR (flags); OUTB (reg | 0x80, u_Command); val = INB (u_DataHi); RESTORE_INTR (flags); return val; } static unsigned char gus_look8 (int reg) { /* Reads from an indirect register (8 bit). No additional offset. */ unsigned long flags; unsigned char val; DISABLE_INTR (flags); OUTB (reg, u_Command); val = INB (u_DataHi); RESTORE_INTR (flags); return val; } static void gus_write16 (int reg, unsigned int data) { /* Writes to an indirect register (16 bit) */ unsigned long flags; DISABLE_INTR (flags); OUTB (reg, u_Command); OUTB ((unsigned char) (data & 0xff), u_DataLo); OUTB ((unsigned char) ((data >> 8) & 0xff), u_DataHi); RESTORE_INTR (flags); } static unsigned short gus_read16 (int reg) { /* Reads from an indirect register (16 bit). Offset 0x80. */ unsigned long flags; unsigned char hi, lo; DISABLE_INTR (flags); OUTB (reg | 0x80, u_Command); lo = INB (u_DataLo); hi = INB (u_DataHi); RESTORE_INTR (flags); return ((hi << 8) & 0xff00) | lo; } static void gus_write_addr (int reg, unsigned long address, int is16bit) { /* Writes an 24 bit memory address */ unsigned long hold_address; unsigned long flags; DISABLE_INTR (flags); if (is16bit) { /* * Special processing required for 16 bit patches */ hold_address = address; address = address >> 1; address &= 0x0001ffffL; address |= (hold_address & 0x000c0000L); } gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff)); gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff)); /* Could writing twice fix problems with GUS_VOICE_POS() ? Lets try... */ gus_delay (); gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff)); gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff)); RESTORE_INTR (flags); } static void gus_select_voice (int voice) { if (voice < 0 || voice > 31) return; OUTB (voice, u_Voice); } static void gus_select_max_voices (int nvoices) { if (nvoices < 14) nvoices = 14; if (nvoices > 32) nvoices = 32; voice_alloc->max_voice = nr_voices = nvoices; gus_write8 (0x0e, (nvoices - 1) | 0xc0); } static void gus_voice_on (unsigned int mode) { gus_write8 (0x00, (unsigned char) (mode & 0xfc)); gus_delay (); gus_write8 (0x00, (unsigned char) (mode & 0xfc)); } static void gus_voice_off (void) { gus_write8 (0x00, gus_read8 (0x00) | 0x03); } static void gus_voice_mode (unsigned int m) { unsigned char mode = (unsigned char) (m & 0xff); gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); /* Don't touch last two bits */ gus_delay (); gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); } static void gus_voice_freq (unsigned long freq) { unsigned long divisor = freq_div_table[nr_voices - 14]; unsigned short fc; fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor); fc = fc << 1; gus_write16 (0x01, fc); } static void gus_voice_volume (unsigned int vol) { gus_write8 (0x0d, 0x03); /* Stop ramp before setting volume */ gus_write16 (0x09, (unsigned short) (vol << 4)); } static void gus_voice_balance (unsigned int balance) { gus_write8 (0x0c, (unsigned char) (balance & 0xff)); } static void gus_ramp_range (unsigned int low, unsigned int high) { gus_write8 (0x07, (unsigned char) ((low >> 4) & 0xff)); gus_write8 (0x08, (unsigned char) ((high >> 4) & 0xff)); } static void gus_ramp_rate (unsigned int scale, unsigned int rate) { gus_write8 (0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f))); } static void gus_rampon (unsigned int m) { unsigned char mode = (unsigned char) (m & 0xff); gus_write8 (0x0d, mode & 0xfc); gus_delay (); gus_write8 (0x0d, mode & 0xfc); } static void gus_ramp_mode (unsigned int m) { unsigned char mode = (unsigned char) (m & 0xff); gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); /* Leave the last 2 bits alone */ gus_delay (); gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); } static void gus_rampoff (void) { gus_write8 (0x0d, 0x03); } static void gus_set_voice_pos (int voice, long position) { int sample_no; if ((sample_no = sample_map[voice]) != -1) if (position < samples[sample_no].len) if (voices[voice].volume_irq_mode == VMODE_START_NOTE) voices[voice].offset_pending = position; else gus_write_addr (0x0a, sample_ptrs[sample_no] + position, samples[sample_no].mode & WAVE_16_BITS); } static void gus_voice_init (int voice) { unsigned long flags; DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_volume (0); gus_voice_off (); gus_write_addr (0x0a, 0, 0); /* Set current position to 0 */ gus_write8 (0x00, 0x03); /* Voice off */ gus_write8 (0x0d, 0x03); /* Ramping off */ voice_alloc->map[voice] = 0; voice_alloc->alloc_times[voice] = 0; RESTORE_INTR (flags); } static void gus_voice_init2 (int voice) { voices[voice].panning = 0; voices[voice].mode = 0; voices[voice].orig_freq = 20000; voices[voice].current_freq = 20000; voices[voice].bender = 0; voices[voice].bender_range = 200; voices[voice].initial_volume = 0; voices[voice].current_volume = 0; voices[voice].loop_irq_mode = 0; voices[voice].loop_irq_parm = 0; voices[voice].volume_irq_mode = 0; voices[voice].volume_irq_parm = 0; voices[voice].env_phase = 0; voices[voice].main_vol = 127; voices[voice].patch_vol = 127; voices[voice].expression_vol = 127; voices[voice].sample_pending = -1; } static void step_envelope (int voice) { unsigned vol, prev_vol, phase; unsigned char rate; long int flags; if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) { DISABLE_INTR (flags); gus_select_voice (voice); gus_rampoff (); RESTORE_INTR (flags); return; /* * Sustain phase begins. Continue envelope after receiving note off. */ } if (voices[voice].env_phase >= 5) { /* Envelope finished. Shoot the voice down */ gus_voice_init (voice); return; } prev_vol = voices[voice].current_volume; phase = ++voices[voice].env_phase; compute_volume (voice, voices[voice].midi_volume); vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255; rate = voices[voice].env_rate[phase]; DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_volume (prev_vol); gus_write8 (0x06, rate); /* Ramping rate */ voices[voice].volume_irq_mode = VMODE_ENVELOPE; if (((vol - prev_vol) / 64) == 0) /* No significant volume change */ { RESTORE_INTR (flags); step_envelope (voice); /* Continue the envelope on the next step */ return; } if (vol > prev_vol) { if (vol >= (4096 - 64)) vol = 4096 - 65; gus_ramp_range (0, vol); gus_rampon (0x20); /* Increasing volume, with IRQ */ } else { if (vol <= 64) vol = 65; gus_ramp_range (vol, 4030); gus_rampon (0x60); /* Decreasing volume, with IRQ */ } voices[voice].current_volume = vol; RESTORE_INTR (flags); } static void init_envelope (int voice) { voices[voice].env_phase = -1; voices[voice].current_volume = 64; step_envelope (voice); } static void start_release (int voice, long int flags) { if (gus_read8 (0x00) & 0x03) return; /* Voice already stopped */ voices[voice].env_phase = 2; /* Will be incremented by step_envelope */ voices[voice].current_volume = voices[voice].initial_volume = gus_read16 (0x09) >> 4; /* Get current volume */ voices[voice].mode &= ~WAVE_SUSTAIN_ON; gus_rampoff (); RESTORE_INTR (flags); step_envelope (voice); } static void gus_voice_fade (int voice) { int instr_no = sample_map[voice], is16bits; long int flags; DISABLE_INTR (flags); gus_select_voice (voice); if (instr_no < 0 || instr_no > MAX_SAMPLE) { gus_write8 (0x00, 0x03); /* Hard stop */ voice_alloc->map[voice] = 0; RESTORE_INTR (flags); return; } is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */ if (voices[voice].mode & WAVE_ENVELOPES) { start_release (voice, flags); return; } /* * Ramp the volume down but not too quickly. */ if ((int) (gus_read16 (0x09) >> 4) < 100) /* Get current volume */ { gus_voice_off (); gus_rampoff (); gus_voice_init (voice); return; } gus_ramp_range (65, 4030); gus_ramp_rate (2, 4); gus_rampon (0x40 | 0x20); /* Down, once, with IRQ */ voices[voice].volume_irq_mode = VMODE_HALT; RESTORE_INTR (flags); } static void gus_reset (void) { int i; gus_select_max_voices (24); volume_base = 3071; volume_scale = 4; volume_method = VOL_METHOD_ADAGIO; for (i = 0; i < 32; i++) { gus_voice_init (i); /* Turn voice off */ gus_voice_init2 (i); } INB (u_Status); /* Touch the status register */ gus_look8 (0x41); /* Clear any pending DMA IRQs */ gus_look8 (0x49); /* Clear any pending sample IRQs */ gus_read8 (0x0f); /* Clear pending IRQs */ } static void gus_initialize (void) { unsigned long flags; unsigned char dma_image, irq_image, tmp; static unsigned char gus_irq_map[16] = {0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7}; static unsigned char gus_dma_map[8] = {0, 1, 0, 2, 0, 3, 4, 5}; DISABLE_INTR (flags); gus_write8 (0x4c, 0); /* Reset GF1 */ gus_delay (); gus_delay (); gus_write8 (0x4c, 1); /* Release Reset */ gus_delay (); gus_delay (); /* * Clear all interrupts */ gus_write8 (0x41, 0); /* DMA control */ gus_write8 (0x45, 0); /* Timer control */ gus_write8 (0x49, 0); /* Sample control */ gus_select_max_voices (24); INB (u_Status); /* Touch the status register */ gus_look8 (0x41); /* Clear any pending DMA IRQs */ gus_look8 (0x49); /* Clear any pending sample IRQs */ gus_read8 (0x0f); /* Clear pending IRQs */ gus_reset (); /* Resets all voices */ gus_look8 (0x41); /* Clear any pending DMA IRQs */ gus_look8 (0x49); /* Clear any pending sample IRQs */ gus_read8 (0x0f); /* Clear pending IRQs */ gus_write8 (0x4c, 7); /* Master reset | DAC enable | IRQ enable */ /* * Set up for Digital ASIC */ OUTB (0x05, gus_base + 0x0f); mix_image |= 0x02; /* Disable line out */ OUTB (mix_image, u_Mixer); OUTB (0x00, u_IRQDMAControl); OUTB (0x00, gus_base + 0x0f); /* * Now set up the DMA and IRQ interface * * The GUS supports two IRQs and two DMAs. * * Just one DMA channel is used. This prevents simultaneous ADC and DAC. * Adding this support requires significant changes to the dmabuf.c, dsp.c * and audio.c also. */ irq_image = 0; tmp = gus_irq_map[gus_irq]; if (!tmp) printk ("Warning! GUS IRQ not selected\n"); irq_image |= tmp; irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */ dma_image = gus_dma_map[gus_dma_read] << 3; if(!dma_image) printk ("Warning! GUS DMA read channel not selected.\n"); if(gus_dma_read == gus_dma) { dma_image = 0x40; /* dual dma inhibited Combine DMA1 (DRAM) and IRQ2 (ADC) */ } tmp = gus_dma_map[gus_dma]; if (!tmp) printk ("Warning! GUS DMA not selected\n"); dma_image |= tmp; /* * For some reason the IRQ and DMA addresses must be written twice */ /* * Doing it first time */ OUTB (mix_image, u_Mixer); /* Select DMA control */ OUTB (dma_image | 0x80, u_IRQDMAControl); /* Set DMA address */ OUTB (mix_image | 0x40, u_Mixer); /* Select IRQ control */ OUTB (irq_image, u_IRQDMAControl); /* Set IRQ address */ /* * Doing it second time */ OUTB (mix_image, u_Mixer); /* Select DMA control */ OUTB (dma_image, u_IRQDMAControl); /* Set DMA address */ OUTB (mix_image | 0x40, u_Mixer); /* Select IRQ control */ OUTB (irq_image, u_IRQDMAControl); /* Set IRQ address */ gus_select_voice (0); /* This disables writes to IRQ/DMA reg */ mix_image &= ~0x02; /* Enable line out */ mix_image |= 0x08; /* Enable IRQ */ OUTB (mix_image, u_Mixer); /* * Turn mixer channels on * Note! Mic in is left off. */ gus_select_voice (0); /* This disables writes to IRQ/DMA reg */ gusintr (INT_HANDLER_CALL (0)); /* Serve pending interrupts */ RESTORE_INTR (flags); } int gus_wave_detect (int baseaddr) { unsigned long i; unsigned long loc; gus_base = baseaddr; gus_write8 (0x4c, 0); /* Reset GF1 */ gus_delay (); gus_delay (); gus_write8 (0x4c, 1); /* Release Reset */ gus_delay (); gus_delay (); /* See if there is first block there.... */ gus_poke (0L, 0xaa); if (gus_peek (0L) != 0xaa) return (0); /* Now zero it out so that I can check for mirroring .. */ gus_poke (0L, 0x00); for (i = 1L; i < 1024L; i++) { int n, failed; /* check for mirroring ... */ if (gus_peek (0L) != 0) break; loc = i << 10; for (n = loc - 1, failed = 0; n <= loc; n++) { gus_poke (loc, 0xaa); if (gus_peek (loc) != 0xaa) failed = 1; gus_poke (loc, 0x55); if (gus_peek (loc) != 0x55) failed = 1; } if (failed) break; } gus_mem_size = i << 10; return 1; } static int guswave_ioctl (int dev, unsigned int cmd, unsigned int arg) { switch (cmd) { case SNDCTL_SYNTH_INFO: gus_info.nr_voices = nr_voices; IOCTL_TO_USER ((char *) arg, 0, &gus_info, sizeof (gus_info)); return 0; break; case SNDCTL_SEQ_RESETSAMPLES: reset_sample_memory (); return 0; break; case SNDCTL_SEQ_PERCMODE: return 0; break; case SNDCTL_SYNTH_MEMAVL: return gus_mem_size - free_mem_ptr - 32; default: return RET_ERROR (EINVAL); } } static int guswave_set_instr (int dev, int voice, int instr_no) { int sample_no; if (instr_no < 0 || instr_no > MAX_PATCH) return RET_ERROR (EINVAL); if (voice < 0 || voice > 31) return RET_ERROR (EINVAL); if (voices[voice].volume_irq_mode == VMODE_START_NOTE) { voices[voice].sample_pending = instr_no; return 0; } sample_no = patch_table[instr_no]; patch_map[voice] = -1; if (sample_no < 0) { printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice); return RET_ERROR (EINVAL); /* Patch not defined */ } if (sample_ptrs[sample_no] == -1) /* Sample not loaded */ { printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice); return RET_ERROR (EINVAL); } sample_map[voice] = sample_no; patch_map[voice] = instr_no; return 0; } static int guswave_kill_note (int dev, int voice, int note, int velocity) { unsigned long flags; DISABLE_INTR (flags); /* voice_alloc->map[voice] = 0xffff; */ if (voices[voice].volume_irq_mode == VMODE_START_NOTE) { voices[voice].kill_pending = 1; RESTORE_INTR (flags); } else { RESTORE_INTR (flags); gus_voice_fade (voice); } RESTORE_INTR (flags); return 0; } static void guswave_aftertouch (int dev, int voice, int pressure) { #if 0 short lo_limit, hi_limit; unsigned long flags; if (voice < 0 || voice > 31) return; if (voices[voice].mode & WAVE_ENVELOPES && voices[voice].env_phase != 2) return; /* Don't mix with envelopes */ if (pressure < 32) { DISABLE_INTR (flags); gus_select_voice (voice); gus_rampoff (); compute_and_set_volume (voice, 255, 0); /* Back to original volume */ RESTORE_INTR (flags); return; } hi_limit = voices[voice].current_volume; lo_limit = hi_limit * 99 / 100; if (lo_limit < 65) lo_limit = 65; DISABLE_INTR (flags); gus_select_voice (voice); if (hi_limit > (4095 - 65)) { hi_limit = 4095 - 65; gus_voice_volume (hi_limit); } gus_ramp_range (lo_limit, hi_limit); gus_ramp_rate (3, 8); gus_rampon (0x58); /* Bidirectional, dow, loop */ RESTORE_INTR (flags); #endif /* 0 */ } static void guswave_panning (int dev, int voice, int value) { if (voice >= 0 || voice < 32) voices[voice].panning = value; } static void guswave_volume_method (int dev, int mode) { if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO) volume_method = mode; } static void compute_volume (int voice, int volume) { if (volume < 128) voices[voice].midi_volume = volume; switch (volume_method) { case VOL_METHOD_ADAGIO: voices[voice].initial_volume = gus_adagio_vol (voices[voice].midi_volume, voices[voice].main_vol, voices[voice].expression_vol, voices[voice].patch_vol); break; case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */ voices[voice].initial_volume = gus_linear_vol (volume, voices[voice].main_vol); break; default: voices[voice].initial_volume = volume_base + (voices[voice].midi_volume * volume_scale); } if (voices[voice].initial_volume > 4030) voices[voice].initial_volume = 4030; } static void compute_and_set_volume (int voice, int volume, int ramp_time) { int current, target, rate; unsigned long flags; compute_volume (voice, volume); voices[voice].current_volume = voices[voice].initial_volume; DISABLE_INTR (flags); /* * CAUTION! Interrupts disabled. Enable them before returning */ gus_select_voice (voice); current = gus_read16 (0x09) >> 4; target = voices[voice].initial_volume; if (ramp_time == INSTANT_RAMP) { gus_rampoff (); gus_voice_volume (target); RESTORE_INTR (flags); return; } if (ramp_time == FAST_RAMP) rate = 63; else rate = 16; gus_ramp_rate (0, rate); if ((target - current) / 64 == 0) /* Close enough to target. */ { gus_rampoff (); gus_voice_volume (target); RESTORE_INTR (flags); return; } if (target > current) { if (target > (4095 - 65)) target = 4095 - 65; gus_ramp_range (current, target); gus_rampon (0x00); /* Ramp up, once, no IRQ */ } else { if (target < 65) target = 65; gus_ramp_range (target, current); gus_rampon (0x40); /* Ramp down, once, no irq */ } RESTORE_INTR (flags); } static void dynamic_volume_change (int voice) { unsigned char status; unsigned long flags; DISABLE_INTR (flags); gus_select_voice (voice); status = gus_read8 (0x00); /* Get voice status */ RESTORE_INTR (flags); if (status & 0x03) return; /* Voice was not running */ if (!(voices[voice].mode & WAVE_ENVELOPES)) { compute_and_set_volume (voice, voices[voice].midi_volume, 1); return; } /* * Voice is running and has envelopes. */ DISABLE_INTR (flags); gus_select_voice (voice); status = gus_read8 (0x0d); /* Ramping status */ RESTORE_INTR (flags); if (status & 0x03) /* Sustain phase? */ { compute_and_set_volume (voice, voices[voice].midi_volume, 1); return; } if (voices[voice].env_phase < 0) return; compute_volume (voice, voices[voice].midi_volume); } static void guswave_controller (int dev, int voice, int ctrl_num, int value) { unsigned long flags; unsigned long freq; if (voice < 0 || voice > 31) return; switch (ctrl_num) { case CTRL_PITCH_BENDER: voices[voice].bender = value; if (voices[voice].volume_irq_mode != VMODE_START_NOTE) { freq = compute_finetune (voices[voice].orig_freq, value, voices[voice].bender_range); voices[voice].current_freq = freq; DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_freq (freq); RESTORE_INTR (flags); } break; case CTRL_PITCH_BENDER_RANGE: voices[voice].bender_range = value; break; case CTL_EXPRESSION: value /= 128; case CTRL_EXPRESSION: if (volume_method == VOL_METHOD_ADAGIO) { voices[voice].expression_vol = value; if (voices[voice].volume_irq_mode != VMODE_START_NOTE) dynamic_volume_change (voice); } break; case CTL_PAN: voices[voice].panning = (value * 2) - 128; break; case CTL_MAIN_VOLUME: value = (value * 100) / 16383; case CTRL_MAIN_VOLUME: voices[voice].main_vol = value; if (voices[voice].volume_irq_mode != VMODE_START_NOTE) dynamic_volume_change (voice); break; default: break; } } static int guswave_start_note2 (int dev, int voice, int note_num, int volume) { int sample, best_sample, best_delta, delta_freq; int is16bits, samplep, patch, pan; unsigned long note_freq, base_note, freq, flags; unsigned char mode = 0; if (voice < 0 || voice > 31) { printk ("GUS: Invalid voice\n"); return RET_ERROR (EINVAL); } if (note_num == 255) { if (voices[voice].mode & WAVE_ENVELOPES) { voices[voice].midi_volume = volume; dynamic_volume_change (voice); return 0; } compute_and_set_volume (voice, volume, 1); return 0; } if ((patch = patch_map[voice]) == -1) { return RET_ERROR (EINVAL); } if ((samplep = patch_table[patch]) == -1) { return RET_ERROR (EINVAL); } note_freq = note_to_freq (note_num); /* * Find a sample within a patch so that the note_freq is between low_note * and high_note. */ sample = -1; best_sample = samplep; best_delta = 1000000; while (samplep >= 0 && sample == -1) { delta_freq = note_freq - samples[samplep].base_note; if (delta_freq < 0) delta_freq = -delta_freq; if (delta_freq < best_delta) { best_sample = samplep; best_delta = delta_freq; } if (samples[samplep].low_note <= note_freq && note_freq <= samples[samplep].high_note) sample = samplep; else samplep = samples[samplep].key; /* * Follow link */ } if (sample == -1) sample = best_sample; if (sample == -1) { printk ("GUS: Patch %d not defined for note %d\n", patch, note_num); return 0; /* Should play default patch ??? */ } is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; voices[voice].mode = samples[sample].mode; voices[voice].patch_vol = samples[sample].volume; if (voices[voice].mode & WAVE_ENVELOPES) { int i; for (i = 0; i < 6; i++) { voices[voice].env_rate[i] = samples[sample].env_rate[i]; voices[voice].env_offset[i] = samples[sample].env_offset[i]; } } sample_map[voice] = sample; base_note = samples[sample].base_note / 100; /* Try to avoid overflows */ note_freq /= 100; freq = samples[sample].base_freq * note_freq / base_note; voices[voice].orig_freq = freq; /* * Since the pitch bender may have been set before playing the note, we * have to calculate the bending now. */ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range); voices[voice].current_freq = freq; pan = (samples[sample].panning + voices[voice].panning) / 32; pan += 7; if (pan < 0) pan = 0; if (pan > 15) pan = 15; if (samples[sample].mode & WAVE_16_BITS) { mode |= 0x04; /* 16 bits */ if ((sample_ptrs[sample] >> 18) != ((sample_ptrs[sample] + samples[sample].len) >> 18)) printk ("GUS: Sample address error\n"); } /************************************************************************* * CAUTION! Interrupts disabled. Don't return before enabling *************************************************************************/ DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_off (); gus_rampoff (); RESTORE_INTR (flags); if (voices[voice].mode & WAVE_ENVELOPES) { compute_volume (voice, volume); init_envelope (voice); } else compute_and_set_volume (voice, volume, 0); DISABLE_INTR (flags); gus_select_voice (voice); if (samples[sample].mode & WAVE_LOOP_BACK) gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len - voices[voice].offset_pending, is16bits); /* start=end */ else gus_write_addr (0x0a, sample_ptrs[sample] + voices[voice].offset_pending, is16bits); /* Sample start=begin */ if (samples[sample].mode & WAVE_LOOPING) { mode |= 0x08; if (samples[sample].mode & WAVE_BIDIR_LOOP) mode |= 0x10; if (samples[sample].mode & WAVE_LOOP_BACK) { gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].loop_end - voices[voice].offset_pending, is16bits); mode |= 0x40; } gus_write_addr (0x02, sample_ptrs[sample] + samples[sample].loop_start, is16bits); /* Loop start location */ gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].loop_end, is16bits); /* Loop end location */ } else { mode |= 0x20; /* Loop IRQ at the end */ voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */ voices[voice].loop_irq_parm = 1; gus_write_addr (0x02, sample_ptrs[sample], is16bits); /* Loop start location */ gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].len - 1, is16bits); /* Loop end location */ } gus_voice_freq (freq); gus_voice_balance (pan); gus_voice_on (mode); RESTORE_INTR (flags); return 0; } /* * New guswave_start_note by Andrew J. Robinson attempts to minimize clicking * when the note playing on the voice is changed. It uses volume * ramping. */ static int guswave_start_note (int dev, int voice, int note_num, int volume) { long int flags; int mode; int ret_val = 0; DISABLE_INTR (flags); if (note_num == 255) { if (voices[voice].volume_irq_mode == VMODE_START_NOTE) voices[voice].volume_pending = volume; else { ret_val = guswave_start_note2 (gus_devnum, voice, note_num, volume); } } else { gus_select_voice (voice); mode = gus_read8 (0x00); if (mode & 0x20) gus_write8 (0x00, mode & 0xdf); /* No interrupt! */ voices[voice].offset_pending = 0; voices[voice].kill_pending = 0; voices[voice].volume_irq_mode = 0; voices[voice].loop_irq_mode = 0; if (voices[voice].sample_pending >= 0) { RESTORE_INTR (flags); /* Run temporarily with interrupts enabled */ guswave_set_instr (voices[voice].dev_pending, voice, voices[voice].sample_pending); voices[voice].sample_pending = -1; DISABLE_INTR (flags); gus_select_voice (voice); /* Reselect the voice (just to be sure) */ } if ((mode & 0x01) || (int) ((gus_read16 (0x09) >> 4) < 2065)) { ret_val = guswave_start_note2 (gus_devnum, voice, note_num, volume); } else { voices[voice].dev_pending = gus_devnum; voices[voice].note_pending = note_num; voices[voice].volume_pending = volume; voices[voice].volume_irq_mode = VMODE_START_NOTE; gus_rampoff (); gus_ramp_range (2000, 4065); gus_ramp_rate (0, 63); /* Fastest possible rate */ gus_rampon (0x20 | 0x40); /* Ramp down, once, irq */ } } RESTORE_INTR (flags); return ret_val; } static void guswave_reset (int dev) { int i; for (i = 0; i < 32; i++) { gus_voice_init (i); gus_voice_init2 (i); } } static int guswave_open (int dev, int mode) { int err; if (mode & OPEN_WRITE && gus_busy[gus_devnum] || mode & OPEN_READ && gus_busy[gus_dspnum]) return RET_ERROR (EBUSY); if(gus_busy[gus_devnum] == 0 && gus_busy[gus_dspnum] == 0) gus_initialize (); voice_alloc->timestamp = 0; if ((err = DMAbuf_open_dma (gus_devnum)) < 0) return err; RESET_WAIT_QUEUE (dram_sleeper, dram_sleep_flag); gus_busy[gus_devnum] = 1; active_device = GUS_DEV_WAVE; gus_reset (); return 0; } static void guswave_close (int dev) { gus_busy[gus_devnum] = 0; active_device = 0; gus_reset (); DMAbuf_close_dma (gus_devnum); } static int guswave_load_patch (int dev, int format, snd_rw_buf * addr, int offs, int count, int pmgr_flag) { struct patch_info patch; int instr; long sizeof_patch; unsigned long blk_size, blk_end, left, src_offs, target; sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */ if (format != GUS_PATCH) { printk ("GUS Error: Invalid patch format (key) 0x%x\n", format); return RET_ERROR (EINVAL); } if (count < sizeof_patch) { printk ("GUS Error: Patch header too short\n"); return RET_ERROR (EINVAL); } count -= sizeof_patch; if (free_sample >= MAX_SAMPLE) { printk ("GUS: Sample table full\n"); return RET_ERROR (ENOSPC); } /* * Copy the header from user space but ignore the first bytes which have * been transferred already. */ COPY_FROM_USER (&((char *) &patch)[offs], addr, offs, sizeof_patch - offs); instr = patch.instr_no; if (instr < 0 || instr > MAX_PATCH) { printk ("GUS: Invalid patch number %d\n", instr); return RET_ERROR (EINVAL); } if (count < patch.len) { printk ("GUS Warning: Patch record too short (%d<%d)\n", count, (int) patch.len); patch.len = count; } if (patch.len <= 0 || patch.len > gus_mem_size) { printk ("GUS: Invalid sample length %d\n", (int) patch.len); return RET_ERROR (EINVAL); } if (patch.mode & WAVE_LOOPING) { if (patch.loop_start < 0 || patch.loop_start >= patch.len) { printk ("GUS: Invalid loop start\n"); return RET_ERROR (EINVAL); } if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len) { printk ("GUS: Invalid loop end\n"); return RET_ERROR (EINVAL); } } free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */ #define GUS_BANK_SIZE (256*1024) if (patch.mode & WAVE_16_BITS) { /* * 16 bit samples must fit one 256k bank. */ if (patch.len >= GUS_BANK_SIZE) { printk ("GUS: Sample (16 bit) too long %d\n", (int) patch.len); return RET_ERROR (ENOSPC); } if ((free_mem_ptr / GUS_BANK_SIZE) != ((free_mem_ptr + patch.len) / GUS_BANK_SIZE)) { unsigned long tmp_mem = /* Aling to 256K */ ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE; if ((tmp_mem + patch.len) > gus_mem_size) return RET_ERROR (ENOSPC); free_mem_ptr = tmp_mem; /* This leaves unusable memory */ } } if ((free_mem_ptr + patch.len) > gus_mem_size) return RET_ERROR (ENOSPC); sample_ptrs[free_sample] = free_mem_ptr; /* * Tremolo is not possible with envelopes */ if (patch.mode & WAVE_ENVELOPES) patch.mode &= ~WAVE_TREMOLO; memcpy ((char *) &samples[free_sample], &patch, sizeof_patch); /* * Link this_one sample to the list of samples for patch 'instr'. */ samples[free_sample].key = patch_table[instr]; patch_table[instr] = free_sample; /* * Use DMA to transfer the wave data to the DRAM */ left = patch.len; src_offs = 0; target = free_mem_ptr; while (left) /* Not completely transferred yet */ { blk_size = audio_devs[gus_devnum]->buffsize; if (blk_size > left) blk_size = left; /* * DMA cannot cross 256k bank boundaries. Check for that. */ blk_end = target + blk_size; if ((target >> 18) != (blk_end >> 18)) { /* Split the block */ blk_end &= ~(256 * 1024 - 1); blk_size = blk_end - target; } #if defined(GUS_NO_DMA) || defined(GUS_PATCH_NO_DMA) /* * For some reason the DMA is not possible. We have to use PIO. */ { long i; unsigned char data; for (i = 0; i < blk_size; i++) { GET_BYTE_FROM_USER (data, addr, sizeof_patch + i); if (patch.mode & WAVE_UNSIGNED) if (!(patch.mode & WAVE_16_BITS) || (i & 0x01)) data ^= 0x80; /* Convert to signed */ gus_poke (target + i, data); } } #else /* GUS_NO_DMA */ { unsigned long address, hold_address; unsigned char dma_command; unsigned long flags; /* * OK, move now. First in and then out. */ COPY_FROM_USER (audio_devs[gus_devnum]->dmap->raw_buf[0], addr, sizeof_patch + src_offs, blk_size); DISABLE_INTR (flags); /******** INTERRUPTS DISABLED NOW ********/ gus_write8 (0x41, 0); /* Disable GF1 DMA */ DMAbuf_start_dma (gus_devnum, audio_devs[gus_devnum]->dmap->raw_buf_phys[0], blk_size, DMA_MODE_WRITE); /* * Set the DRAM address for the wave data */ address = target; if (audio_devs[gus_devnum]->dmachan > 3) { hold_address = address; address = address >> 1; address &= 0x0001ffffL; address |= (hold_address & 0x000c0000L); } gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ /* * Start the DMA transfer */ dma_command = 0x21; /* IRQ enable, DMA start */ if (patch.mode & WAVE_UNSIGNED) dma_command |= 0x80; /* Invert MSB */ if (patch.mode & WAVE_16_BITS) dma_command |= 0x40; /* 16 bit _DATA_ */ if (audio_devs[gus_devnum]->dmachan > 3) dma_command |= 0x04; /* 16 bit DMA _channel_ */ gus_write8 (0x41, dma_command); /* Lets bo luteet (=bugs) */ /* * Sleep here until the DRAM DMA done interrupt is served */ active_device = GUS_DEV_WAVE; DO_SLEEP (dram_sleeper, dram_sleep_flag, HZ); if (TIMED_OUT (dram_sleeper, dram_sleep_flag)) printk ("GUS: DMA Transfer timed out\n"); RESTORE_INTR (flags); } #endif /* GUS_NO_DMA */ /* * Now the next part */ left -= blk_size; src_offs += blk_size; target += blk_size; gus_write8 (0x41, 0); /* Stop DMA */ } free_mem_ptr += patch.len; if (!pmgr_flag) pmgr_inform (gus_devnum, PM_E_PATCH_LOADED, instr, free_sample, 0, 0); free_sample++; return 0; } static void guswave_hw_control (int dev, unsigned char *event) { int voice, cmd; unsigned short p1, p2; unsigned long plong, flags; cmd = event[2]; voice = event[3]; p1 = *(unsigned short *) &event[4]; p2 = *(unsigned short *) &event[6]; plong = *(unsigned long *) &event[4]; if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) && (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS)) do_volume_irq (voice); switch (cmd) { case _GUS_NUMVOICES: DISABLE_INTR (flags); gus_select_voice (voice); gus_select_max_voices (p1); RESTORE_INTR (flags); break; case _GUS_VOICESAMPLE: guswave_set_instr (dev, voice, p1); break; case _GUS_VOICEON: DISABLE_INTR (flags); gus_select_voice (voice); p1 &= ~0x20; /* Don't allow interrupts */ gus_voice_on (p1); RESTORE_INTR (flags); break; case _GUS_VOICEOFF: DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_off (); RESTORE_INTR (flags); break; case _GUS_VOICEFADE: gus_voice_fade (voice); break; case _GUS_VOICEMODE: DISABLE_INTR (flags); gus_select_voice (voice); p1 &= ~0x20; /* Don't allow interrupts */ gus_voice_mode (p1); RESTORE_INTR (flags); break; case _GUS_VOICEBALA: DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_balance (p1); RESTORE_INTR (flags); break; case _GUS_VOICEFREQ: DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_freq (plong); RESTORE_INTR (flags); break; case _GUS_VOICEVOL: DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_volume (p1); RESTORE_INTR (flags); break; case _GUS_VOICEVOL2: /* Just update the software voice level */ voices[voice].initial_volume = voices[voice].current_volume = p1; break; case _GUS_RAMPRANGE: if (voices[voice].mode & WAVE_ENVELOPES) break; /* NO-NO */ DISABLE_INTR (flags); gus_select_voice (voice); gus_ramp_range (p1, p2); RESTORE_INTR (flags); break; case _GUS_RAMPRATE: if (voices[voice].mode & WAVE_ENVELOPES) break; /* NJET-NJET */ DISABLE_INTR (flags); gus_select_voice (voice); gus_ramp_rate (p1, p2); RESTORE_INTR (flags); break; case _GUS_RAMPMODE: if (voices[voice].mode & WAVE_ENVELOPES) break; /* NO-NO */ DISABLE_INTR (flags); gus_select_voice (voice); p1 &= ~0x20; /* Don't allow interrupts */ gus_ramp_mode (p1); RESTORE_INTR (flags); break; case _GUS_RAMPON: if (voices[voice].mode & WAVE_ENVELOPES) break; /* EI-EI */ DISABLE_INTR (flags); gus_select_voice (voice); p1 &= ~0x20; /* Don't allow interrupts */ gus_rampon (p1); RESTORE_INTR (flags); break; case _GUS_RAMPOFF: if (voices[voice].mode & WAVE_ENVELOPES) break; /* NEJ-NEJ */ DISABLE_INTR (flags); gus_select_voice (voice); gus_rampoff (); RESTORE_INTR (flags); break; case _GUS_VOLUME_SCALE: volume_base = p1; volume_scale = p2; break; case _GUS_VOICE_POS: DISABLE_INTR (flags); gus_select_voice (voice); gus_set_voice_pos (voice, plong); RESTORE_INTR (flags); break; default:; } } static int gus_sampling_set_speed (int speed) { if (speed <= 0) speed = gus_sampling_speed; if (speed < 4000) speed = 4000; if (speed > 44100) speed = 44100; gus_sampling_speed = speed; if (only_read_access) { /* Compute nearest valid recording speed and return it */ speed = (9878400 / (gus_sampling_speed + 2)) / 16; speed = (9878400 / (speed * 16)) - 2; } return speed; } static int gus_sampling_set_channels (int channels) { if (!channels) return gus_sampling_channels; if (channels > 2) channels = 2; if (channels < 1) channels = 1; gus_sampling_channels = channels; return channels; } static int gus_sampling_set_bits (int bits) { if (!bits) return gus_sampling_bits; if (bits != 8 && bits != 16) bits = 8; gus_sampling_bits = bits; return bits; } static int gus_sampling_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) { switch (cmd) { case SOUND_PCM_WRITE_RATE: if (local) return gus_sampling_set_speed (arg); return IOCTL_OUT (arg, gus_sampling_set_speed (IOCTL_IN (arg))); break; case SOUND_PCM_READ_RATE: if (local) return gus_sampling_speed; return IOCTL_OUT (arg, gus_sampling_speed); break; case SNDCTL_DSP_STEREO: if (local) return gus_sampling_set_channels (arg + 1) - 1; return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg) + 1) - 1); break; case SOUND_PCM_WRITE_CHANNELS: if (local) return gus_sampling_set_channels (arg); return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg))); break; case SOUND_PCM_READ_CHANNELS: if (local) return gus_sampling_channels; return IOCTL_OUT (arg, gus_sampling_channels); break; case SNDCTL_DSP_SETFMT: if (local) return gus_sampling_set_bits (arg); return IOCTL_OUT (arg, gus_sampling_set_bits (IOCTL_IN (arg))); break; case SOUND_PCM_READ_BITS: if (local) return gus_sampling_bits; return IOCTL_OUT (arg, gus_sampling_bits); case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */ return IOCTL_OUT (arg, RET_ERROR (EINVAL)); break; case SOUND_PCM_READ_FILTER: return IOCTL_OUT (arg, RET_ERROR (EINVAL)); break; } return RET_ERROR (EINVAL); } static void gus_sampling_reset (int dev) { } static int gus_sampling_open (int dev, int mode) { int dev_flag; int init_flag; #ifdef GUS_NO_DMA printk ("GUS: DMA mode not enabled. Device not supported\n"); return RET_ERROR (ENXIO); #endif dev_flag = 0; init_flag = (gus_busy[gus_devnum] == 0 && gus_busy[gus_dspnum] == 0); if(mode & OPEN_WRITE) { if (gus_busy[gus_devnum]) return RET_ERROR(EBUSY); if(dev != gus_devnum) return RET_ERROR(ENXIO); dev_flag = gus_busy[gus_devnum] = 1; } if(mode & OPEN_READ) { if(gus_busy[gus_dspnum]) { if (dev_flag) gus_busy[gus_devnum] = 0; return RET_ERROR(EBUSY); } if(dev != gus_dspnum) { if (dev_flag) gus_busy[gus_devnum] = 0; return RET_ERROR(ENXIO); } } if(init_flag) { gus_initialize (); active_device = 0; gus_reset (); reset_sample_memory (); gus_select_max_voices (14); pcm_active = 0; dma_active = 0; pcm_opened = 1; } if (mode & OPEN_READ) { recording_active = 1; set_input_volumes (); } only_read_access = !(mode & OPEN_WRITE); return 0; } static void gus_sampling_close (int dev) { gus_busy[dev] = 0; if (gus_busy[gus_devnum] == 0 && gus_busy[gus_dspnum] == 0) { active_device = 0; gus_reset(); pcm_opened = 0; } if (recording_active) set_input_volumes (); recording_active = 0; } static void gus_sampling_update_volume (void) { unsigned long flags; int voice; if (pcm_active && pcm_opened) for (voice = 0; voice < gus_sampling_channels; voice++) { DISABLE_INTR (flags); gus_select_voice (voice); gus_rampoff (); gus_voice_volume (1530 + (25 * gus_pcm_volume)); gus_ramp_range (65, 1530 + (25 * gus_pcm_volume)); RESTORE_INTR (flags); } } static void play_next_pcm_block (void) { unsigned long flags; int speed = gus_sampling_speed; int this_one, is16bits, chn; unsigned long dram_loc; unsigned char mode[2], ramp_mode[2]; if (!pcm_qlen) return; this_one = pcm_head; for (chn = 0; chn < gus_sampling_channels; chn++) { mode[chn] = 0x00; ramp_mode[chn] = 0x03; /* Ramping and rollover off */ if (chn == 0) { mode[chn] |= 0x20; /* Loop IRQ */ voices[chn].loop_irq_mode = LMODE_PCM; } if (gus_sampling_bits != 8) { is16bits = 1; mode[chn] |= 0x04; /* 16 bit data */ } else is16bits = 0; dram_loc = this_one * pcm_bsize; dram_loc += chn * pcm_banksize; if (this_one == (pcm_nblk - 1)) /* Last fragment of the DRAM buffer */ { mode[chn] |= 0x08; /* Enable loop */ ramp_mode[chn] = 0x03; /* Disable rollover bit */ } else { if (chn == 0) ramp_mode[chn] = 0x04; /* Enable rollover bit */ } DISABLE_INTR (flags); gus_select_voice (chn); gus_voice_freq (speed); if (gus_sampling_channels == 1) gus_voice_balance (7); /* mono */ else if (chn == 0) gus_voice_balance (0); /* left */ else gus_voice_balance (15); /* right */ if (!pcm_active) /* Playback not already active */ { /* * The playback was not started yet (or there has been a pause). * Start the voice (again) and ask for a rollover irq at the end of * this_one block. If this_one one is last of the buffers, use just * the normal loop with irq. */ gus_voice_off (); gus_rampoff (); gus_voice_volume (1530 + (25 * gus_pcm_volume)); gus_ramp_range (65, 1530 + (25 * gus_pcm_volume)); gus_write_addr (0x0a, dram_loc, is16bits); /* Starting position */ gus_write_addr (0x02, chn * pcm_banksize, is16bits); /* Loop start */ if (chn != 0) gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1, is16bits); /* Loop end location */ } if (chn == 0) gus_write_addr (0x04, dram_loc + pcm_datasize[this_one] - 1, is16bits); /* Loop end location */ else mode[chn] |= 0x08; /* Enable looping */ if (pcm_datasize[this_one] != pcm_bsize) { /* * Incompletely filled block. Possibly the last one. */ if (chn == 0) { mode[chn] &= ~0x08; /* Disable looping */ mode[chn] |= 0x20; /* Enable IRQ at the end */ voices[0].loop_irq_mode = LMODE_PCM_STOP; ramp_mode[chn] = 0x03; /* No rollover bit */ } else { gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /* Loop end location */ mode[chn] &= ~0x08; /* Disable looping */ } } RESTORE_INTR (flags); } for (chn = 0; chn < gus_sampling_channels; chn++) { DISABLE_INTR (flags); gus_select_voice (chn); gus_write8 (0x0d, ramp_mode[chn]); gus_voice_on (mode[chn]); RESTORE_INTR (flags); } pcm_active = 1; } static void gus_transfer_output_block (int dev, unsigned long buf, int total_count, int intrflag, int chn) { /* * This routine transfers one block of audio data to the DRAM. In mono mode * it's called just once. When in stereo mode, this_one routine is called * once for both channels. * * The left/mono channel data is transferred to the beginning of dram and the * right data to the area pointed by gus_page_size. */ int this_one, count; unsigned long flags; unsigned char dma_command; unsigned long address, hold_address; DISABLE_INTR (flags); count = total_count / gus_sampling_channels; if (chn == 0) { if (pcm_qlen >= pcm_nblk) printk ("GUS Warning: PCM buffers out of sync\n"); this_one = pcm_current_block = pcm_tail; pcm_qlen++; pcm_tail = (pcm_tail + 1) % pcm_nblk; pcm_datasize[this_one] = count; } else this_one = pcm_current_block; gus_write8 (0x41, 0); /* Disable GF1 DMA */ DMAbuf_start_dma (gus_devnum, buf + (chn * count), count, DMA_MODE_WRITE); address = this_one * pcm_bsize; address += chn * pcm_banksize; if (audio_devs[gus_devnum]->dmachan > 3) { hold_address = address; address = address >> 1; address &= 0x0001ffffL; address |= (hold_address & 0x000c0000L); } gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ dma_command = 0x21; /* IRQ enable, DMA start */ if (gus_sampling_bits != 8) dma_command |= 0x40; /* 16 bit _DATA_ */ else dma_command |= 0x80; /* Invert MSB */ if (audio_devs[gus_devnum]->dmachan > 3) dma_command |= 0x04; /* 16 bit DMA channel */ gus_write8 (0x41, dma_command); /* Kickstart */ if (chn == (gus_sampling_channels - 1)) /* Last channel */ { /* * Last (right or mono) channel data */ dma_active = 1; /* DMA started. There is a unacknowledged buffer */ active_device = GUS_DEV_PCM_DONE; if (!pcm_active && (pcm_qlen > 0 || count < pcm_bsize)) { play_next_pcm_block (); } } else { /* * Left channel data. The right channel * is transferred after DMA interrupt */ active_device = GUS_DEV_PCM_CONTINUE; } RESTORE_INTR (flags); } static void gus_sampling_output_block (int dev, unsigned long buf, int total_count, int intrflag, int restart_dma) { pcm_current_buf = buf; pcm_current_count = total_count; pcm_current_intrflag = intrflag; pcm_current_dev = gus_devnum; gus_transfer_output_block (gus_devnum, buf, total_count, intrflag, 0); } static void gus_sampling_start_input (int dev, unsigned long buf, int count, int intrflag, int restart_dma) { unsigned long flags; unsigned char mode; DISABLE_INTR (flags); DMAbuf_start_dma (gus_dspnum, buf, count, DMA_MODE_READ); mode = 0xa0; /* DMA IRQ enabled, invert MSB */ if (audio_devs[gus_dspnum]->dmachan > 3) mode |= 0x04; /* 16 bit DMA channel */ if (gus_sampling_channels > 1) mode |= 0x02; /* Stereo */ mode |= 0x01; /* DMA enable */ gus_write8 (0x49, mode); RESTORE_INTR (flags); } static int gus_sampling_prepare_for_input (int dev, int bsize, int bcount) { unsigned int rate; rate = (9878400 / (gus_sampling_speed + 2)) / 16; gus_write8 (0x48, rate & 0xff); /* Set sampling rate */ if (gus_sampling_bits != 8) { printk ("GUS Error: 16 bit recording not supported\n"); return RET_ERROR (EINVAL); } return 0; } static int gus_sampling_prepare_for_output (int dev, int bsize, int bcount) { int i; long mem_ptr, mem_size; mem_ptr = 0; mem_size = gus_mem_size / gus_sampling_channels; if (mem_size > (256 * 1024)) mem_size = 256 * 1024; pcm_bsize = bsize / gus_sampling_channels; pcm_head = pcm_tail = pcm_qlen = 0; pcm_nblk = MAX_PCM_BUFFERS; if ((pcm_bsize * pcm_nblk) > mem_size) pcm_nblk = mem_size / pcm_bsize; for (i = 0; i < pcm_nblk; i++) pcm_datasize[i] = 0; pcm_banksize = pcm_nblk * pcm_bsize; if (gus_sampling_bits != 8 && pcm_banksize == (256 * 1024)) pcm_nblk--; return 0; } static int gus_local_qlen (int dev) { return pcm_qlen; } static void gus_copy_from_user (int dev, char *localbuf, int localoffs, snd_rw_buf * userbuf, int useroffs, int len) { if (gus_sampling_channels == 1) { COPY_FROM_USER (&localbuf[localoffs], userbuf, useroffs, len); } else if (gus_sampling_bits == 8) #if defined(__FreeBSD__) { char *in_left = gus_copy_buf; char *in_right = in_left + 1; char *out_left = localbuf + (localoffs / 2); char *out_right = out_left + pcm_bsize; int i; COPY_FROM_USER (gus_copy_buf, userbuf, useroffs, len); len /= 2; for (i = 0; i < len; i++) { *out_left++ = *in_left++; in_left++; *out_right++ = *in_right++; in_right++; } } else { short *in_left = (short *)gus_copy_buf; short *in_right = in_left + 1; short *out_left = (short *)localbuf + (localoffs / 4); short *out_right = out_left + (pcm_bsize / 2); int i; COPY_FROM_USER (gus_copy_buf, userbuf, useroffs, len); len /= 4; for (i = 0; i < len; i++) { *out_left++ = *in_left++; in_left++; *out_right++ = *in_right++; in_right++; } } #else { int in_left = useroffs; int in_right = useroffs + 1; char *out_left, *out_right; int i; len /= 2; localoffs /= 2; out_left = &localbuf[localoffs]; out_right = out_left + pcm_bsize; for (i = 0; i < len; i++) { GET_BYTE_FROM_USER (*out_left++, userbuf, in_left); in_left += 2; GET_BYTE_FROM_USER (*out_right++, userbuf, in_right); in_right += 2; } } else { int in_left = useroffs / 2; int in_right = useroffs / 2 + 1; short *out_left, *out_right; int i; len /= 4; localoffs /= 2; out_left = (short *) &localbuf[localoffs]; out_right = out_left + (pcm_bsize / 2); for (i = 0; i < len; i++) { #ifdef __FreeBSD__ GET_SHORT_FROM_USER (*out_left++, userbuf, in_left); in_left += 2; GET_SHORT_FROM_USER (*out_right++, userbuf, in_right); in_right += 2; #else GET_SHORT_FROM_USER (*out_left++, (short *) userbuf, in_left); in_left += 2; GET_SHORT_FROM_USER (*out_right++, (short *) userbuf, in_right); in_right += 2; #endif } } #endif } static struct audio_operations gus_sampling_operations = { "Gravis UltraSound", NEEDS_RESTART, AFMT_U8 | AFMT_S16_LE, NULL, gus_sampling_open, gus_sampling_close, gus_sampling_output_block, gus_sampling_start_input, gus_sampling_ioctl, gus_sampling_prepare_for_input, gus_sampling_prepare_for_output, gus_sampling_reset, gus_sampling_reset, gus_local_qlen, gus_copy_from_user }; static struct audio_operations gus_sampling_operations_read = { "Gravis UltraSound - read only", NEEDS_RESTART, AFMT_U8 | AFMT_S16_LE, NULL, gus_sampling_open, gus_sampling_close, gus_sampling_output_block, gus_sampling_start_input, gus_sampling_ioctl, gus_sampling_prepare_for_input, gus_sampling_prepare_for_output, gus_sampling_reset, gus_sampling_reset, gus_local_qlen, gus_copy_from_user }; static void guswave_setup_voice (int dev, int voice, int chn) { struct channel_info *info = &synth_devs[gus_devnum]->chn_info[chn]; guswave_set_instr (gus_devnum, voice, info->pgm_num); voices[voice].expression_vol = info->controllers[CTL_EXPRESSION]; /* Just msb */ voices[voice].main_vol = (info->controllers[CTL_MAIN_VOLUME] * 100) / 128; voices[voice].panning = (info->controllers[CTL_PAN] * 2) - 128; voices[voice].bender = info->bender_value; } static void guswave_bender (int dev, int voice, int value) { int freq; unsigned long flags; voices[voice].bender = value - 8192; freq = compute_finetune (voices[voice].orig_freq, value, voices[voice].bender_range); voices[voice].current_freq = freq; DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_freq (freq); RESTORE_INTR (flags); } static int guswave_patchmgr (int dev, struct patmgr_info *rec) { int i, n; switch (rec->command) { case PM_GET_DEVTYPE: rec->parm1 = PMTYPE_WAVE; return 0; break; case PM_GET_NRPGM: rec->parm1 = MAX_PATCH; return 0; break; case PM_GET_PGMMAP: rec->parm1 = MAX_PATCH; for (i = 0; i < MAX_PATCH; i++) { int ptr = patch_table[i]; rec->data.data8[i] = 0; while (ptr >= 0 && ptr < free_sample) { rec->data.data8[i]++; ptr = samples[ptr].key; /* Follow link */ } } return 0; break; case PM_GET_PGM_PATCHES: { int ptr = patch_table[rec->parm1]; n = 0; while (ptr >= 0 && ptr < free_sample) { rec->data.data32[n++] = ptr; ptr = samples[ptr].key; /* Follow link */ } } rec->parm1 = n; return 0; break; case PM_GET_PATCH: { int ptr = rec->parm1; struct patch_info *pat; if (ptr < 0 || ptr >= free_sample) return RET_ERROR (EINVAL); memcpy (rec->data.data8, (char *) &samples[ptr], sizeof (struct patch_info)); pat = (struct patch_info *) rec->data.data8; pat->key = GUS_PATCH; /* Restore patch type */ rec->parm1 = sample_ptrs[ptr]; /* DRAM location */ rec->parm2 = sizeof (struct patch_info); } return 0; break; case PM_SET_PATCH: { int ptr = rec->parm1; struct patch_info *pat; if (ptr < 0 || ptr >= free_sample) return RET_ERROR (EINVAL); pat = (struct patch_info *) rec->data.data8; if (pat->len > samples[ptr].len) /* Cannot expand sample */ return RET_ERROR (EINVAL); pat->key = samples[ptr].key; /* Ensure the link is correct */ memcpy ((char *) &samples[ptr], rec->data.data8, sizeof (struct patch_info)); pat->key = GUS_PATCH; } return 0; break; case PM_READ_PATCH: /* Returns a block of wave data from the DRAM */ { int sample = rec->parm1; int n; long offs = rec->parm2; int l = rec->parm3; if (sample < 0 || sample >= free_sample) return RET_ERROR (EINVAL); if (offs < 0 || offs >= samples[sample].len) return RET_ERROR (EINVAL); /* Invalid offset */ n = samples[sample].len - offs; /* Num of bytes left */ if (l > n) l = n; if (l > sizeof (rec->data.data8)) l = sizeof (rec->data.data8); if (l <= 0) return RET_ERROR (EINVAL); /* * Was there a bug? */ offs += sample_ptrs[sample]; /* * Begin offsess + offset to DRAM */ for (n = 0; n < l; n++) rec->data.data8[n] = gus_peek (offs++); rec->parm1 = n; /* * Nr of bytes copied */ } return 0; break; case PM_WRITE_PATCH: /* * Writes a block of wave data to the DRAM */ { int sample = rec->parm1; int n; long offs = rec->parm2; int l = rec->parm3; if (sample < 0 || sample >= free_sample) return RET_ERROR (EINVAL); if (offs < 0 || offs >= samples[sample].len) return RET_ERROR (EINVAL); /* * Invalid offset */ n = samples[sample].len - offs; /* * Nr of bytes left */ if (l > n) l = n; if (l > sizeof (rec->data.data8)) l = sizeof (rec->data.data8); if (l <= 0) return RET_ERROR (EINVAL); /* * Was there a bug? */ offs += sample_ptrs[sample]; /* * Begin offsess + offset to DRAM */ for (n = 0; n < l; n++) gus_poke (offs++, rec->data.data8[n]); rec->parm1 = n; /* * Nr of bytes copied */ } return 0; break; default: return RET_ERROR (EINVAL); } } static int guswave_alloc (int dev, int chn, int note, struct voice_alloc_info *alloc) { int i, p, best = -1, best_time = 0x7fffffff; p = alloc->ptr; /* * First look for a completely stopped voice */ for (i = 0; i < alloc->max_voice; i++) { if (alloc->map[p] == 0) { alloc->ptr = p; return p; } if (alloc->alloc_times[p] < best_time) { best = p; best_time = alloc->alloc_times[p]; } p = (p + 1) % alloc->max_voice; } /* * Then look for a releasing voice */ for (i = 0; i < alloc->max_voice; i++) { if (alloc->map[p] == 0xffff) { alloc->ptr = p; return p; } p = (p + 1) % alloc->max_voice; } if (best >= 0) p = best; alloc->ptr = p; return p; } static struct synth_operations guswave_operations = { &gus_info, 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, guswave_open, guswave_close, guswave_ioctl, guswave_kill_note, guswave_start_note, guswave_set_instr, guswave_reset, guswave_hw_control, guswave_load_patch, guswave_aftertouch, guswave_controller, guswave_panning, guswave_volume_method, guswave_patchmgr, guswave_bender, guswave_alloc, guswave_setup_voice }; static void set_input_volumes (void) { unsigned long flags; unsigned char mask = 0xff & ~0x06; /* Just line out enabled */ DISABLE_INTR (flags); /* * Enable channels having vol > 10% * Note! bit 0x01 means line in DISABLED while 0x04 means * mic in ENABLED. */ if (gus_line_vol > 10) mask &= ~0x01; if (gus_mic_vol > 10) mask |= 0x04; if (recording_active) { /* * Disable channel, if not selected for recording */ if (!(gus_recmask & SOUND_MASK_LINE)) mask |= 0x01; if (!(gus_recmask & SOUND_MASK_MIC)) mask &= ~0x04; } mix_image &= ~0x07; mix_image |= mask & 0x07; OUTB (mix_image, u_Mixer); RESTORE_INTR (flags); } int gus_default_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) { #define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ SOUND_MASK_SYNTH|SOUND_MASK_PCM) if (((cmd >> 8) & 0xff) == 'M') { if (cmd & IOC_IN) switch (cmd & 0xff) { case SOUND_MIXER_RECSRC: gus_recmask = IOCTL_IN (arg) & MIX_DEVS; if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE))) gus_recmask = SOUND_MASK_MIC; /* Note! Input volumes are updated during next open for recording */ return IOCTL_OUT (arg, gus_recmask); break; case SOUND_MIXER_MIC: { int vol = IOCTL_IN (arg) & 0xff; if (vol < 0) vol = 0; if (vol > 100) vol = 100; gus_mic_vol = vol; set_input_volumes (); return IOCTL_OUT (arg, vol | (vol << 8)); } break; case SOUND_MIXER_LINE: { int vol = IOCTL_IN (arg) & 0xff; if (vol < 0) vol = 0; if (vol > 100) vol = 100; gus_line_vol = vol; set_input_volumes (); return IOCTL_OUT (arg, vol | (vol << 8)); } break; case SOUND_MIXER_PCM: gus_pcm_volume = IOCTL_IN (arg) & 0xff; if (gus_pcm_volume < 0) gus_pcm_volume = 0; if (gus_pcm_volume > 100) gus_pcm_volume = 100; gus_sampling_update_volume (); return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8)); break; case SOUND_MIXER_SYNTH: { int voice; gus_wave_volume = IOCTL_IN (arg) & 0xff; if (gus_wave_volume < 0) gus_wave_volume = 0; if (gus_wave_volume > 100) gus_wave_volume = 100; if (active_device == GUS_DEV_WAVE) for (voice = 0; voice < nr_voices; voice++) dynamic_volume_change (voice); /* Apply the new vol */ return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8)); } break; default: return RET_ERROR (EINVAL); } else switch (cmd & 0xff) /* * Return parameters */ { case SOUND_MIXER_RECSRC: return IOCTL_OUT (arg, gus_recmask); break; case SOUND_MIXER_DEVMASK: return IOCTL_OUT (arg, MIX_DEVS); break; case SOUND_MIXER_STEREODEVS: return IOCTL_OUT (arg, 0); break; case SOUND_MIXER_RECMASK: return IOCTL_OUT (arg, SOUND_MASK_MIC | SOUND_MASK_LINE); break; case SOUND_MIXER_CAPS: return IOCTL_OUT (arg, 0); break; case SOUND_MIXER_MIC: return IOCTL_OUT (arg, gus_mic_vol | (gus_mic_vol << 8)); break; case SOUND_MIXER_LINE: return IOCTL_OUT (arg, gus_line_vol | (gus_line_vol << 8)); break; case SOUND_MIXER_PCM: return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8)); break; case SOUND_MIXER_SYNTH: return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8)); break; default: return RET_ERROR (EINVAL); } } else return RET_ERROR (EINVAL); } static struct mixer_operations gus_mixer_operations = { "Gravis Ultrasound", gus_default_mixer_ioctl }; static long gus_default_mixer_init (long mem_start) { if (num_mixers < MAX_MIXER_DEV) /* * Don't install if there is another * mixer */ mixer_devs[num_mixers++] = &gus_mixer_operations; return mem_start; } long gus_wave_init (long mem_start, int irq, int dma, int dma_read) { unsigned long flags; unsigned char val; char *model_num = "2.4"; int gus_type = 0x24; /* 2.4 */ int mixer_type = 0; if (irq < 0 || irq > 15) { printk ("ERROR! Invalid IRQ#%d. GUS Disabled", irq); return mem_start; } if (dma < 0 || dma > 7) { printk ("ERROR! Invalid DMA#%d. GUS Disabled", dma); return mem_start; } if (dma_read == 0) dma_read = dma; if (dma_read < 0 || dma_read > 7) { printk ("ERROR! Invalid DMA#%d. GUS DMA-read disabled", dma_read); dma_read = dma; } /* * Try to identify the GUS model. * * Versions < 3.6 don't have the digital ASIC. Try to probe it first. */ DISABLE_INTR (flags); OUTB (0x20, gus_base + 0x0f); val = INB (gus_base + 0x0f); RESTORE_INTR (flags); if (val != 0xff && (val & 0x06)) /* Should be 0x02?? */ { /* * It has the digital ASIC so the card is at least v3.4. * Next try to detect the true model. */ val = INB (u_MixSelect); /* * Value 255 means pre-3.7 which don't have mixer. * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer. * 10 and above is GUS MAX which has the CS4231 codec/mixer. * * Sorry. No GUS max support yet but it should be available * soon after the SDK for GUS MAX is available. */ if (val == 255 || val < 5) { model_num = "3.4"; gus_type = 0x34; } else if (val < 10) { model_num = "3.7"; gus_type = 0x37; mixer_type = ICS2101; } else { model_num = "MAX"; gus_type = 0x40; mixer_type = CS4231; } } else { /* * ASIC not detected so the card must be 2.2 or 2.4. * There could still be the 16-bit/mixer daughter card. * It has the same codec/mixer than MAX. * At this time there is no support for it but it will appear soon. */ } #if defined(__FreeBSD__) printk ("gus0: ", model_num, (int) gus_mem_size / 1024); #else printk (" ", model_num, (int) gus_mem_size / 1024); #endif sprintf (gus_info.name, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024); gus_irq = irq; gus_dma = dma; gus_dma_read = dma_read; if (num_synths >= MAX_SYNTH_DEV) printk ("GUS Error: Too many synthesizers\n"); else { voice_alloc = &guswave_operations.alloc; synth_devs[num_synths++] = &guswave_operations; } #if defined(__FreeBSD__) PERMANENT_MALLOC (char *, gus_copy_buf, DSP_BUFFSIZE, mem_start); #endif PERMANENT_MALLOC (struct patch_info *, samples, (MAX_SAMPLE + 1) * sizeof (*samples), mem_start); reset_sample_memory (); gus_initialize (); if (num_audiodevs < MAX_AUDIO_DEV) { audio_devs[gus_devnum = num_audiodevs++] = &gus_sampling_operations; audio_devs[gus_devnum]->dmachan = dma; audio_devs[gus_devnum]->buffcount = 1; audio_devs[gus_devnum]->buffsize = DSP_BUFFSIZE; gus_dspnum = gus_devnum; gus_busy[gus_devnum] = 0; gus_busy[gus_dspnum] = 0; } else printk ("GUS: Too many PCM devices available\n"); if (num_audiodevs < MAX_AUDIO_DEV) { if(dma_read && dma != dma_read) { audio_devs[gus_dspnum = num_audiodevs++]= &gus_sampling_operations_read; audio_devs[gus_dspnum]->dmachan = gus_dma_read; audio_devs[gus_dspnum]->buffcount = 1; audio_devs[gus_dspnum]->buffsize = DSP_BUFFSIZE; gus_busy[gus_dspnum] = 0; } else { gus_dspnum = gus_devnum; } } else printk ("GUS READ: Too many PCM devices available\n"); /* * Mixer dependent initialization. */ switch (mixer_type) { case ICS2101: gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; gus_wave_volume = 90; return ics2101_mixer_init (mem_start); case CS4231: /* Initialized elsewhere (ad1848.c) */ #ifndef EXCLUDE_GUSMAX { unsigned char max_config = 0x40; /* Codec enable */ long mixer_init_return; if (dma > 3) max_config |= 0x30; /* 16 bit playback and capture DMAs */ max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */ OUTB (max_config, gus_base + 0x106); /* UltraMax control */ mixer_init_return = gus_default_mixer_init(mem_start); if (ad1848_detect (gus_base + 0x10c)) { gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; gus_wave_volume = 90; have_gus_max = 1; ad1848_init ("GUS MAX", gus_base + 0x10c, -irq, dma_read, /* read write reversed */ dma); } else printk ("[Where's the CS4231?]"); return mixer_init_return; } #endif default: return gus_default_mixer_init (mem_start); } } static void do_loop_irq (int voice) { unsigned char tmp; int mode, parm; unsigned long flags; DISABLE_INTR (flags); gus_select_voice (voice); tmp = gus_read8 (0x00); tmp &= ~0x20; /* * Disable wave IRQ for this_one voice */ gus_write8 (0x00, tmp); if (tmp & 0x03) /* Voice stopped */ voice_alloc->map[voice] = 0; mode = voices[voice].loop_irq_mode; voices[voice].loop_irq_mode = 0; parm = voices[voice].loop_irq_parm; switch (mode) { case LMODE_FINISH: /* * Final loop finished, shoot volume down */ if ((int) (gus_read16 (0x09) >> 4) < 100) /* * Get current volume */ { gus_voice_off (); gus_rampoff (); gus_voice_init (voice); break; } gus_ramp_range (65, 4065); gus_ramp_rate (0, 63); /* * Fastest possible rate */ gus_rampon (0x20 | 0x40); /* * Ramp down, once, irq */ voices[voice].volume_irq_mode = VMODE_HALT; break; case LMODE_PCM_STOP: pcm_active = 0; /* Signal to the play_next_pcm_block routine */ case LMODE_PCM: { int flag; /* 0 or 2 */ pcm_qlen--; pcm_head = (pcm_head + 1) % pcm_nblk; if (pcm_qlen && pcm_active) { play_next_pcm_block (); } else { /* Underrun. Just stop the voice */ gus_select_voice (0); /* Left channel */ gus_voice_off (); gus_rampoff (); gus_select_voice (1); /* Right channel */ gus_voice_off (); gus_rampoff (); pcm_active = 0; } /* * If the queue was full before this interrupt, the DMA transfer was * suspended. Let it continue now. */ if (dma_active) { if (pcm_qlen == 0) flag = 1; /* Underflow */ else flag = 0; dma_active = 0; } else flag = 2; /* Just notify the dmabuf.c */ DMAbuf_outputintr (gus_devnum, flag); } break; default:; } RESTORE_INTR (flags); } static void do_volume_irq (int voice) { unsigned char tmp; int mode, parm; unsigned long flags; DISABLE_INTR (flags); gus_select_voice (voice); tmp = gus_read8 (0x0d); tmp &= ~0x20; /* * Disable volume ramp IRQ */ gus_write8 (0x0d, tmp); mode = voices[voice].volume_irq_mode; voices[voice].volume_irq_mode = 0; parm = voices[voice].volume_irq_parm; switch (mode) { case VMODE_HALT: /* * Decay phase finished */ RESTORE_INTR (flags); gus_voice_init (voice); break; case VMODE_ENVELOPE: gus_rampoff (); RESTORE_INTR (flags); step_envelope (voice); break; case VMODE_START_NOTE: RESTORE_INTR (flags); guswave_start_note2 (voices[voice].dev_pending, voice, voices[voice].note_pending, voices[voice].volume_pending); if (voices[voice].kill_pending) guswave_kill_note (voices[voice].dev_pending, voice, voices[voice].note_pending, 0); if (voices[voice].sample_pending >= 0) { guswave_set_instr (voices[voice].dev_pending, voice, voices[voice].sample_pending); voices[voice].sample_pending = -1; } break; default:; } } void gus_voice_irq (void) { unsigned long wave_ignore = 0, volume_ignore = 0; unsigned long voice_bit; unsigned char src, voice; while (1) { src = gus_read8 (0x0f); /* * Get source info */ voice = src & 0x1f; src &= 0xc0; if (src == (0x80 | 0x40)) return; /* * No interrupt */ voice_bit = 1 << voice; if (!(src & 0x80)) /* * Wave IRQ pending */ if (!(wave_ignore & voice_bit) && (int) voice < nr_voices) /* * Not done * yet */ { wave_ignore |= voice_bit; do_loop_irq (voice); } if (!(src & 0x40)) /* * Volume IRQ pending */ if (!(volume_ignore & voice_bit) && (int) voice < nr_voices) /* * Not done * yet */ { volume_ignore |= voice_bit; do_volume_irq (voice); } } } void guswave_dma_irq (void) { unsigned char status; status = gus_look8 (0x41); /* Get DMA IRQ Status */ if (status & 0x40) /* DMA interrupt pending */ switch (active_device) { case GUS_DEV_WAVE: if (SOMEONE_WAITING (dram_sleeper, dram_sleep_flag)) WAKE_UP (dram_sleeper, dram_sleep_flag); break; case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */ gus_transfer_output_block (pcm_current_dev, pcm_current_buf, pcm_current_count, pcm_current_intrflag, 1); break; case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */ if (pcm_qlen < pcm_nblk) { int flag = (1 - dma_active) * 2; /* 0 or 2 */ if (pcm_qlen == 0) flag = 1; /* Underrun */ dma_active = 0; DMAbuf_outputintr (gus_devnum, flag); } break; default:; } status = gus_look8 (0x49); /* * Get Sampling IRQ Status */ if (status & 0x40) /* * Sampling Irq pending */ { if (gus_dma_read && gus_dma_read != gus_dma) DMAbuf_inputintr (gus_dspnum); else DMAbuf_inputintr (gus_devnum); } } #endif Index: head/sys/pc98/pc98/sound/ics2101.c =================================================================== --- head/sys/pc98/pc98/sound/ics2101.c (revision 18264) +++ head/sys/pc98/pc98/sound/ics2101.c (revision 18265) @@ -1,264 +1,264 @@ /* * sound/ics2101.c * * Driver for the ICS2101 mixer of GUS v3.7. * * Copyright by Hannu Savolainen 1994 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS) #include -#include "gus_hw.h" +#include #define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ SOUND_MASK_SYNTH| \ SOUND_MASK_CD | SOUND_MASK_VOLUME) extern int gus_base; static int volumes[ICS_MIXDEVS]; static int left_fix[ICS_MIXDEVS] = {1, 1, 1, 2, 1, 2}; static int right_fix[ICS_MIXDEVS] = {2, 2, 2, 1, 2, 1}; static int scale_vol (int vol) { #if 1 /* * Experimental volume scaling by Risto Kankkunen. * This should give smoother volume response than just * a plain multiplication. */ int e; if (vol < 0) vol = 0; if (vol > 100) vol = 100; vol = (31 * vol + 50) / 100; e = 0; if (vol) { while (vol < 16) { vol <<= 1; e--; } vol -= 16; e += 7; } return ((e << 4) + vol); #else return ((vol * 127) + 50) / 100; #endif } static void write_mix (int dev, int chn, int vol) { int *selector; unsigned long flags; int ctrl_addr = dev << 3; int attn_addr = dev << 3; vol = scale_vol (vol); if (chn == CHN_LEFT) { selector = left_fix; ctrl_addr |= 0x00; attn_addr |= 0x02; } else { selector = right_fix; ctrl_addr |= 0x01; attn_addr |= 0x03; } DISABLE_INTR (flags); OUTB (ctrl_addr, u_MixSelect); OUTB (selector[dev], u_MixData); OUTB (attn_addr, u_MixSelect); OUTB ((unsigned char) vol, u_MixData); RESTORE_INTR (flags); } static int set_volumes (int dev, int vol) { int left = vol & 0x00ff; int right = (vol >> 8) & 0x00ff; if (left < 0) left = 0; if (left > 100) left = 100; if (right < 0) right = 0; if (right > 100) right = 100; write_mix (dev, CHN_LEFT, left); write_mix (dev, CHN_RIGHT, right); vol = left + (right << 8); volumes[dev] = vol; return vol; } static int ics2101_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) { if (((cmd >> 8) & 0xff) == 'M') { if (cmd & IOC_IN) switch (cmd & 0xff) { case SOUND_MIXER_RECSRC: return gus_default_mixer_ioctl (dev, cmd, arg); break; case SOUND_MIXER_MIC: return IOCTL_OUT (arg, set_volumes (DEV_MIC, IOCTL_IN (arg))); break; case SOUND_MIXER_CD: return IOCTL_OUT (arg, set_volumes (DEV_CD, IOCTL_IN (arg))); break; case SOUND_MIXER_LINE: return IOCTL_OUT (arg, set_volumes (DEV_LINE, IOCTL_IN (arg))); break; case SOUND_MIXER_SYNTH: return IOCTL_OUT (arg, set_volumes (DEV_GF1, IOCTL_IN (arg))); break; case SOUND_MIXER_VOLUME: return IOCTL_OUT (arg, set_volumes (DEV_VOL, IOCTL_IN (arg))); break; default: return RET_ERROR (EINVAL); } else switch (cmd & 0xff) /* * Return parameters */ { case SOUND_MIXER_RECSRC: return gus_default_mixer_ioctl (dev, cmd, arg); break; case SOUND_MIXER_DEVMASK: return IOCTL_OUT (arg, MIX_DEVS); break; case SOUND_MIXER_STEREODEVS: return IOCTL_OUT (arg, SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC); break; case SOUND_MIXER_RECMASK: return IOCTL_OUT (arg, SOUND_MASK_MIC | SOUND_MASK_LINE); break; case SOUND_MIXER_CAPS: return IOCTL_OUT (arg, 0); break; case SOUND_MIXER_MIC: return IOCTL_OUT (arg, volumes[DEV_MIC]); break; case SOUND_MIXER_LINE: return IOCTL_OUT (arg, volumes[DEV_LINE]); break; case SOUND_MIXER_CD: return IOCTL_OUT (arg, volumes[DEV_CD]); break; case SOUND_MIXER_VOLUME: return IOCTL_OUT (arg, volumes[DEV_VOL]); break; case SOUND_MIXER_SYNTH: return IOCTL_OUT (arg, volumes[DEV_GF1]); break; default: return RET_ERROR (EINVAL); } } return RET_ERROR (EINVAL); } static struct mixer_operations ics2101_mixer_operations = { "ICS2101 Multimedia Mixer", ics2101_mixer_ioctl }; long ics2101_mixer_init (long mem_start) { int i; if (num_mixers < MAX_MIXER_DEV) { mixer_devs[num_mixers++] = &ics2101_mixer_operations; /* * Some GUS v3.7 cards had some channels flipped. Disable * the flipping feature if the model id is other than 5. */ if (INB (u_MixSelect) != 5) { for (i = 0; i < ICS_MIXDEVS; i++) left_fix[i] = 1; for (i = 0; i < ICS_MIXDEVS; i++) right_fix[i] = 2; } set_volumes (DEV_GF1, 0x5a5a); set_volumes (DEV_CD, 0x5a5a); set_volumes (DEV_MIC, 0x0000); set_volumes (DEV_LINE, 0x5a5a); set_volumes (DEV_VOL, 0x5a5a); set_volumes (DEV_UNUSED, 0x0000); } return mem_start; } #endif Index: head/sys/pc98/pc98/sound/midi_synth.c =================================================================== --- head/sys/pc98/pc98/sound/midi_synth.c (revision 18264) +++ head/sys/pc98/pc98/sound/midi_synth.c (revision 18265) @@ -1,644 +1,644 @@ /* * sound/midi_synth.c * * High level midi sequencer manager for dumb MIDI interfaces. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #define USE_SEQ_MACROS #define USE_SIMPLE_MACROS -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MIDI) #define _MIDI_SYNTH_C_ DEFINE_WAIT_QUEUE (sysex_sleeper, sysex_sleep_flag); -#include "midi_synth.h" +#include static int midi2synth[MAX_MIDI_DEV]; static unsigned char prev_out_status[MAX_MIDI_DEV]; #define STORE(cmd) \ { \ int len; \ unsigned char obuf[8]; \ cmd; \ seq_input_event(obuf, len); \ } #define _seqbuf obuf #define _seqbufptr 0 #define _SEQ_ADVBUF(x) len=x void do_midi_msg (int synthno, unsigned char *msg, int mlen) { switch (msg[0] & 0xf0) { case 0x90: if (msg[2] != 0) { STORE (SEQ_START_NOTE (synthno, msg[0] & 0x0f, msg[1], msg[2])); break; } msg[2] = 64; case 0x80: STORE (SEQ_STOP_NOTE (synthno, msg[0] & 0x0f, msg[1], msg[2])); break; case 0xA0: STORE (SEQ_KEY_PRESSURE (synthno, msg[0] & 0x0f, msg[1], msg[2])); break; case 0xB0: STORE (SEQ_CONTROL (synthno, msg[0] & 0x0f, msg[1], msg[2])); break; case 0xC0: STORE (SEQ_SET_PATCH (synthno, msg[0] & 0x0f, msg[1])); break; case 0xD0: STORE (SEQ_CHN_PRESSURE (synthno, msg[0] & 0x0f, msg[1])); break; case 0xE0: STORE (SEQ_BENDER (synthno, msg[0] & 0x0f, (msg[1] % 0x7f) | ((msg[2] & 0x7f) << 7))); break; default: printk ("MPU: Unknown midi channel message %02x\n", msg[0]); } } static void midi_outc (int midi_dev, int data) { int timeout; for (timeout = 0; timeout < 32000; timeout++) if (midi_devs[midi_dev]->putc (midi_dev, (unsigned char) (data & 0xff))) { if (data & 0x80) /* * Status byte */ prev_out_status[midi_dev] = (unsigned char) (data & 0xff); /* * Store for running status */ return; /* * Mission complete */ } /* * Sorry! No space on buffers. */ printk ("Midi send timed out\n"); } static int prefix_cmd (int midi_dev, unsigned char status) { if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL) return 1; return midi_devs[midi_dev]->prefix_cmd (midi_dev, status); } static void midi_synth_input (int dev, unsigned char data) { int orig_dev; struct midi_input_info *inc; static unsigned char len_tab[] = /* # of data bytes following a status */ { 2, /* 8x */ 2, /* 9x */ 2, /* Ax */ 2, /* Bx */ 1, /* Cx */ 1, /* Dx */ 2, /* Ex */ 0 /* Fx */ }; if (dev < 0 || dev > num_synths) return; if (data == 0xfe) /* Ignore active sensing */ return; orig_dev = midi2synth[dev]; inc = &midi_devs[orig_dev]->in_info; switch (inc->m_state) { case MST_INIT: if (data & 0x80) /* MIDI status byte */ { if ((data & 0xf0) == 0xf0) /* Common message */ { switch (data) { case 0xf0: /* Sysex */ inc->m_state = MST_SYSEX; break; /* Sysex */ case 0xf1: /* MTC quarter frame */ case 0xf3: /* Song select */ inc->m_state = MST_DATA; inc->m_ptr = 1; inc->m_left = 1; inc->m_buf[0] = data; break; case 0xf2: /* Song position pointer */ inc->m_state = MST_DATA; inc->m_ptr = 1; inc->m_left = 2; inc->m_buf[0] = data; break; default: inc->m_buf[0] = data; inc->m_ptr = 1; do_midi_msg (dev, inc->m_buf, inc->m_ptr); inc->m_ptr = 0; inc->m_left = 0; } } else { inc->m_state = MST_DATA; inc->m_ptr = 1; inc->m_left = len_tab[(data >> 4) - 8]; inc->m_buf[0] = inc->m_prev_status = data; } } else if (inc->m_prev_status & 0x80) /* Ignore if no previous status (yet) */ { /* Data byte (use running status) */ inc->m_state = MST_DATA; inc->m_ptr = 2; inc->m_left = len_tab[(data >> 4) - 8] - 1; inc->m_buf[0] = inc->m_prev_status; inc->m_buf[1] = data; } break; /* MST_INIT */ case MST_DATA: inc->m_buf[inc->m_ptr++] = data; if (--inc->m_left <= 0) { inc->m_state = MST_INIT; do_midi_msg (dev, inc->m_buf, inc->m_ptr); inc->m_ptr = 0; } break; /* MST_DATA */ case MST_SYSEX: if (data == 0xf7) /* Sysex end */ { inc->m_state = MST_INIT; inc->m_left = 0; inc->m_ptr = 0; } break; /* MST_SYSEX */ default: printk ("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, (int) data); inc->m_state = MST_INIT; } } static void midi_synth_output (int dev) { /* * Currently NOP */ } int midi_synth_ioctl (int dev, unsigned int cmd, unsigned int arg) { /* * int orig_dev = synth_devs[dev]->midi_dev; */ switch (cmd) { case SNDCTL_SYNTH_INFO: IOCTL_TO_USER ((char *) arg, 0, synth_devs[dev]->info, sizeof (struct synth_info)); return 0; break; case SNDCTL_SYNTH_MEMAVL: return 0x7fffffff; break; default: return RET_ERROR (EINVAL); } } int midi_synth_kill_note (int dev, int channel, int note, int velocity) { int orig_dev = synth_devs[dev]->midi_dev; int msg, chn; if (note < 0 || note > 127) return 0; if (channel < 0 || channel > 15) return 0; if (velocity < 0) velocity = 0; if (velocity > 127) velocity = 127; msg = prev_out_status[orig_dev] & 0xf0; chn = prev_out_status[orig_dev] & 0x0f; if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80)) { /* * Use running status */ if (!prefix_cmd (orig_dev, note)) return 0; midi_outc (orig_dev, note); if (msg == 0x90) /* * Running status = Note on */ midi_outc (orig_dev, 0); /* * Note on with velocity 0 == note * off */ else midi_outc (orig_dev, velocity); } else { if (velocity == 64) { if (!prefix_cmd (orig_dev, 0x90 | (channel & 0x0f))) return 0; midi_outc (orig_dev, 0x90 | (channel & 0x0f)); /* * Note on */ midi_outc (orig_dev, note); midi_outc (orig_dev, 0); /* * Zero G */ } else { if (!prefix_cmd (orig_dev, 0x80 | (channel & 0x0f))) return 0; midi_outc (orig_dev, 0x80 | (channel & 0x0f)); /* * Note off */ midi_outc (orig_dev, note); midi_outc (orig_dev, velocity); } } return 0; } int midi_synth_set_instr (int dev, int channel, int instr_no) { int orig_dev = synth_devs[dev]->midi_dev; if (instr_no < 0 || instr_no > 127) return 0; if (channel < 0 || channel > 15) return 0; if (!prefix_cmd (orig_dev, 0xc0 | (channel & 0x0f))) return 0; midi_outc (orig_dev, 0xc0 | (channel & 0x0f)); /* * Program change */ midi_outc (orig_dev, instr_no); return 0; } int midi_synth_start_note (int dev, int channel, int note, int velocity) { int orig_dev = synth_devs[dev]->midi_dev; int msg, chn; if (note < 0 || note > 127) return 0; if (channel < 0 || channel > 15) return 0; if (velocity < 0) velocity = 0; if (velocity > 127) velocity = 127; msg = prev_out_status[orig_dev] & 0xf0; chn = prev_out_status[orig_dev] & 0x0f; if (chn == channel && msg == 0x90) { /* * Use running status */ if (!prefix_cmd (orig_dev, note)) return 0; midi_outc (orig_dev, note); midi_outc (orig_dev, velocity); } else { if (!prefix_cmd (orig_dev, 0x90 | (channel & 0x0f))) return 0; midi_outc (orig_dev, 0x90 | (channel & 0x0f)); /* * Note on */ midi_outc (orig_dev, note); midi_outc (orig_dev, velocity); } return 0; } void midi_synth_reset (int dev) { } int midi_synth_open (int dev, int mode) { int orig_dev = synth_devs[dev]->midi_dev; int err; unsigned long flags; struct midi_input_info *inc; if (orig_dev < 0 || orig_dev > num_midis) return RET_ERROR (ENXIO); midi2synth[orig_dev] = dev; prev_out_status[orig_dev] = 0; if ((err = midi_devs[orig_dev]->open (orig_dev, mode, midi_synth_input, midi_synth_output)) < 0) return err; inc = &midi_devs[orig_dev]->in_info; DISABLE_INTR (flags); inc->m_busy = 0; inc->m_state = MST_INIT; inc->m_ptr = 0; inc->m_left = 0; inc->m_prev_status = 0x00; RESTORE_INTR (flags); return 1; } void midi_synth_close (int dev) { int orig_dev = synth_devs[dev]->midi_dev; /* * Shut up the synths by sending just single active sensing message. */ midi_devs[orig_dev]->putc (orig_dev, 0xfe); midi_devs[orig_dev]->close (orig_dev); } void midi_synth_hw_control (int dev, unsigned char *event) { } int midi_synth_load_patch (int dev, int format, snd_rw_buf * addr, int offs, int count, int pmgr_flag) { int orig_dev = synth_devs[dev]->midi_dev; struct sysex_info sysex; int i; unsigned long left, src_offs, eox_seen = 0; int first_byte = 1; int hdr_size = (unsigned long) &sysex.data[0] - (unsigned long) &sysex; if (!prefix_cmd (orig_dev, 0xf0)) return 0; if (format != SYSEX_PATCH) { printk ("MIDI Error: Invalid patch format (key) 0x%x\n", format); return RET_ERROR (EINVAL); } if (count < hdr_size) { printk ("MIDI Error: Patch header too short\n"); return RET_ERROR (EINVAL); } count -= hdr_size; /* * Copy the header from user space but ignore the first bytes which have * been transferred already. */ COPY_FROM_USER (&((char *) &sysex)[offs], addr, offs, hdr_size - offs); if (count < sysex.len) { printk ("MIDI Warning: Sysex record too short (%d<%d)\n", count, (int) sysex.len); sysex.len = count; } left = sysex.len; src_offs = 0; RESET_WAIT_QUEUE (sysex_sleeper, sysex_sleep_flag); for (i = 0; i < left && !PROCESS_ABORTING (sysex_sleeper, sysex_sleep_flag); i++) { unsigned char data; GET_BYTE_FROM_USER (data, addr, hdr_size + i); eox_seen = (i > 0 && data & 0x80); /* End of sysex */ if (eox_seen && data != 0xf7) data = 0xf7; if (i == 0) { if (data != 0xf0) { printk ("Error: Sysex start missing\n"); return RET_ERROR (EINVAL); } } while (!midi_devs[orig_dev]->putc (orig_dev, (unsigned char) (data & 0xff)) && !PROCESS_ABORTING (sysex_sleeper, sysex_sleep_flag)) DO_SLEEP (sysex_sleeper, sysex_sleep_flag, 1); /* Wait for timeout */ if (!first_byte && data & 0x80) return 0; first_byte = 0; } if (!eox_seen) midi_outc (orig_dev, 0xf7); return 0; } void midi_synth_panning (int dev, int channel, int pressure) { } void midi_synth_aftertouch (int dev, int channel, int pressure) { int orig_dev = synth_devs[dev]->midi_dev; int msg, chn; if (pressure < 0 || pressure > 127) return; if (channel < 0 || channel > 15) return; msg = prev_out_status[orig_dev] & 0xf0; chn = prev_out_status[orig_dev] & 0x0f; if (msg != 0xd0 || chn != channel) /* * Test for running status */ { if (!prefix_cmd (orig_dev, 0xd0 | (channel & 0x0f))) return; midi_outc (orig_dev, 0xd0 | (channel & 0x0f)); /* * Channel pressure */ } else if (!prefix_cmd (orig_dev, pressure)) return; midi_outc (orig_dev, pressure); } void midi_synth_controller (int dev, int channel, int ctrl_num, int value) { int orig_dev = synth_devs[dev]->midi_dev; int chn, msg; if (ctrl_num < 1 || ctrl_num > 127) return; /* NOTE! Controller # 0 ignored */ if (channel < 0 || channel > 15) return; msg = prev_out_status[orig_dev] & 0xf0; chn = prev_out_status[orig_dev] & 0x0f; if (msg != 0xb0 || chn != channel) { if (!prefix_cmd (orig_dev, 0xb0 | (channel & 0x0f))) return; midi_outc (orig_dev, 0xb0 | (channel & 0x0f)); } else if (!prefix_cmd (orig_dev, ctrl_num)) return; midi_outc (orig_dev, ctrl_num); midi_outc (orig_dev, value & 0x7f); } int midi_synth_patchmgr (int dev, struct patmgr_info *rec) { return RET_ERROR (EINVAL); } void midi_synth_bender (int dev, int channel, int value) { int orig_dev = synth_devs[dev]->midi_dev; int msg, prev_chn; if (channel < 0 || channel > 15) return; if (value < 0 || value > 16383) return; msg = prev_out_status[orig_dev] & 0xf0; prev_chn = prev_out_status[orig_dev] & 0x0f; if (msg != 0xd0 || prev_chn != channel) /* * Test for running status */ { if (!prefix_cmd (orig_dev, 0xe0 | (channel & 0x0f))) return; midi_outc (orig_dev, 0xe0 | (channel & 0x0f)); } else if (!prefix_cmd (orig_dev, value & 0x7f)) return; midi_outc (orig_dev, value & 0x7f); midi_outc (orig_dev, (value >> 7) & 0x7f); } void midi_synth_setup_voice (int dev, int voice, int channel) { } #endif Index: head/sys/pc98/pc98/sound/midibuf.c =================================================================== --- head/sys/pc98/pc98/sound/midibuf.c (revision 18264) +++ head/sys/pc98/pc98/sound/midibuf.c (revision 18265) @@ -1,470 +1,470 @@ /* * sound/midibuf.c * * Device file manager for /dev/midi# * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include static void drain_midi_queue __P((int dev)); #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MIDI) /* * Don't make MAX_QUEUE_SIZE larger than 4000 */ #define MAX_QUEUE_SIZE 4000 DEFINE_WAIT_QUEUES (midi_sleeper[MAX_MIDI_DEV], midi_sleep_flag[MAX_MIDI_DEV]); DEFINE_WAIT_QUEUES (input_sleeper[MAX_MIDI_DEV], input_sleep_flag[MAX_MIDI_DEV]); struct midi_buf { int len, head, tail; unsigned char queue[MAX_QUEUE_SIZE]; }; struct midi_parms { int prech_timeout; /* * Timeout before the first ch */ }; static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] = {NULL}; static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] = {NULL}; static struct midi_parms parms[MAX_MIDI_DEV]; static void midi_poll (unsigned long dummy); DEFINE_TIMER (poll_timer, midi_poll); static volatile int open_devs = 0; #define DATA_AVAIL(q) (q->len) #define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len) #define QUEUE_BYTE(q, data) \ if (SPACE_AVAIL(q)) \ { \ unsigned long flags; \ DISABLE_INTR(flags); \ q->queue[q->tail] = (data); \ q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \ RESTORE_INTR(flags); \ } #define REMOVE_BYTE(q, data) \ if (DATA_AVAIL(q)) \ { \ unsigned long flags; \ DISABLE_INTR(flags); \ data = q->queue[q->head]; \ q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \ RESTORE_INTR(flags); \ } static void drain_midi_queue (int dev) { /* * Give the Midi driver time to drain its output queues */ if (midi_devs[dev]->buffer_status != NULL) while (!PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev]) && midi_devs[dev]->buffer_status (dev)) DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], HZ / 10); } static void midi_input_intr (int dev, unsigned char data) { if (midi_in_buf[dev] == NULL) return; if (data == 0xfe) /* * Active sensing */ return; /* * Ignore */ if (SPACE_AVAIL (midi_in_buf[dev])) { QUEUE_BYTE (midi_in_buf[dev], data); if (SOMEONE_WAITING (input_sleeper[dev], input_sleep_flag[dev])) WAKE_UP (input_sleeper[dev], input_sleep_flag[dev]); } #if defined(__FreeBSD__) if (selinfo[dev].si_pid) selwakeup(&selinfo[dev]); #endif } static void midi_output_intr (int dev) { /* * Currently NOP */ #if defined(__FreeBSD__) if (selinfo[dev].si_pid) selwakeup(&selinfo[dev]); #endif } static void midi_poll (unsigned long dummy) { unsigned long flags; int dev; DISABLE_INTR (flags); if (open_devs) { for (dev = 0; dev < num_midis; dev++) if (midi_out_buf[dev] != NULL) { while (DATA_AVAIL (midi_out_buf[dev]) && midi_devs[dev]->putc (dev, midi_out_buf[dev]->queue[midi_out_buf[dev]->head])) { midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE; midi_out_buf[dev]->len--; } if (DATA_AVAIL (midi_out_buf[dev]) < 100 && SOMEONE_WAITING (midi_sleeper[dev], midi_sleep_flag[dev])) WAKE_UP (midi_sleeper[dev], midi_sleep_flag[dev]); } ACTIVATE_TIMER (poll_timer, midi_poll, 1); /* * Come back later */ } RESTORE_INTR (flags); } int MIDIbuf_open (int dev, struct fileinfo *file) { int mode, err; unsigned long flags; dev = dev >> 4; mode = file->mode & O_ACCMODE; if (num_midis > MAX_MIDI_DEV) { printk ("Sound: FATAL ERROR: Too many midi interfaces\n"); num_midis = MAX_MIDI_DEV; } if (dev < 0 || dev >= num_midis) { printk ("Sound: Nonexistent MIDI interface %d\n", dev); return RET_ERROR (ENXIO); } /* * Interrupts disabled. Be careful */ DISABLE_INTR (flags); if ((err = midi_devs[dev]->open (dev, mode, midi_input_intr, midi_output_intr)) < 0) { RESTORE_INTR (flags); return err; } parms[dev].prech_timeout = 0; RESET_WAIT_QUEUE (midi_sleeper[dev], midi_sleep_flag[dev]); RESET_WAIT_QUEUE (input_sleeper[dev], input_sleep_flag[dev]); midi_in_buf[dev] = (struct midi_buf *) KERNEL_MALLOC (sizeof (struct midi_buf)); if (midi_in_buf[dev] == NULL) { printk ("midi: Can't allocate buffer\n"); midi_devs[dev]->close (dev); RESTORE_INTR (flags); return RET_ERROR (EIO); } midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0; midi_out_buf[dev] = (struct midi_buf *) KERNEL_MALLOC (sizeof (struct midi_buf)); if (midi_out_buf[dev] == NULL) { printk ("midi: Can't allocate buffer\n"); midi_devs[dev]->close (dev); KERNEL_FREE (midi_in_buf[dev]); midi_in_buf[dev] = NULL; RESTORE_INTR (flags); return RET_ERROR (EIO); } midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0; if (!open_devs) ACTIVATE_TIMER (poll_timer, midi_poll, 1); /* * Come back later */ open_devs++; RESTORE_INTR (flags); return err; } void MIDIbuf_release (int dev, struct fileinfo *file) { int mode; unsigned long flags; dev = dev >> 4; mode = file->mode & O_ACCMODE; DISABLE_INTR (flags); /* * Wait until the queue is empty */ if (mode != OPEN_READ) { midi_devs[dev]->putc (dev, 0xfe); /* * Active sensing to shut the * devices */ while (!PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev]) && DATA_AVAIL (midi_out_buf[dev])) DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], 0); /* * Sync */ drain_midi_queue (dev); /* * Ensure the output queues are empty */ } midi_devs[dev]->close (dev); KERNEL_FREE (midi_in_buf[dev]); KERNEL_FREE (midi_out_buf[dev]); midi_in_buf[dev] = NULL; midi_out_buf[dev] = NULL; open_devs--; RESTORE_INTR (flags); } int MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { unsigned long flags; int c, n, i; unsigned char tmp_data; dev = dev >> 4; if (!count) return 0; DISABLE_INTR (flags); c = 0; while (c < count) { n = SPACE_AVAIL (midi_out_buf[dev]); if (n == 0) /* * No space just now. We have to sleep */ { DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], 0); if (PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev])) { RESTORE_INTR (flags); return RET_ERROR (EINTR); } n = SPACE_AVAIL (midi_out_buf[dev]); } if (n > (count - c)) n = count - c; for (i = 0; i < n; i++) { COPY_FROM_USER (&tmp_data, buf, c, 1); QUEUE_BYTE (midi_out_buf[dev], tmp_data); c++; } } RESTORE_INTR (flags); return c; } int MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { int n, c = 0; unsigned long flags; unsigned char tmp_data; dev = dev >> 4; DISABLE_INTR (flags); if (!DATA_AVAIL (midi_in_buf[dev])) /* * No data yet, wait */ { DO_SLEEP (input_sleeper[dev], input_sleep_flag[dev], parms[dev].prech_timeout); if (PROCESS_ABORTING (input_sleeper[dev], input_sleep_flag[dev])) c = RET_ERROR (EINTR); /* * The user is getting restless */ } if (c == 0 && DATA_AVAIL (midi_in_buf[dev])) /* * Got some bytes */ { n = DATA_AVAIL (midi_in_buf[dev]); if (n > count) n = count; c = 0; while (c < n) { REMOVE_BYTE (midi_in_buf[dev], tmp_data); COPY_TO_USER (buf, c, &tmp_data, 1); c++; } } RESTORE_INTR (flags); return c; } int MIDIbuf_ioctl (int dev, struct fileinfo *file, unsigned int cmd, unsigned int arg) { int val; dev = dev >> 4; if (((cmd >> 8) & 0xff) == 'C') { if (midi_devs[dev]->coproc) /* Coprocessor ioctl */ return midi_devs[dev]->coproc->ioctl (midi_devs[dev]->coproc->devc, cmd, arg, 0); else printk ("/dev/midi%d: No coprocessor for this device\n", dev); return RET_ERROR (EREMOTEIO); } else switch (cmd) { case SNDCTL_MIDI_PRETIME: val = IOCTL_IN (arg); if (val < 0) val = 0; val = (HZ * val) / 10; parms[dev].prech_timeout = val; return IOCTL_OUT (arg, val); break; default: return midi_devs[dev]->ioctl (dev, cmd, arg); } } #ifdef ALLOW_SELECT int MIDIbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait) { dev = dev >> 4; switch (sel_type) { case SEL_IN: if (!DATA_AVAIL (midi_in_buf[dev])) { #if defined(__FreeBSD__) selrecord(wait, &selinfo[dev]); #else input_sleep_flag[dev].mode = WK_SLEEP; select_wait (&input_sleeper[dev], wait); #endif return 0; } return 1; break; case SEL_OUT: if (SPACE_AVAIL (midi_out_buf[dev])) { #if defined(__FreeBSD__) selrecord(wait, &selinfo[dev]); #else midi_sleep_flag[dev].mode = WK_SLEEP; select_wait (&midi_sleeper[dev], wait); #endif return 0; } return 1; break; case SEL_EX: return 0; } return 0; } #endif /* ALLOW_SELECT */ long MIDIbuf_init (long mem_start) { return mem_start; } #endif Index: head/sys/pc98/pc98/sound/mpu401.c =================================================================== --- head/sys/pc98/pc98/sound/mpu401.c (revision 18264) +++ head/sys/pc98/pc98/sound/mpu401.c (revision 18265) @@ -1,1777 +1,1777 @@ /* * sound/mpu401.c * * The low level driver for Roland MPU-401 compatible Midi cards. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Modified: * Riccardo Facchetti 24 Mar 1995 * - Added the Audio Excel DSP 16 initialization routine. */ #define USE_SEQ_MACROS #define USE_SIMPLE_MACROS -#include "sound_config.h" +#include #ifdef CONFIGURE_SOUNDCARD #if (!defined(EXCLUDE_MPU401) || !defined(EXCLUDE_MPU_EMU)) && !defined(EXCLUDE_MIDI) -#include "coproc.h" +#include static int init_sequence[20]; /* NOTE! pos 0 = len, start pos 1. */ static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL; struct mpu_config { int base; /* * I/O base */ int irq; int opened; /* * Open mode */ int devno; int synthno; int uart_mode; int initialized; int mode; #define MODE_MIDI 1 #define MODE_SYNTH 2 unsigned char version, revision; unsigned int capabilities; #define MPU_CAP_INTLG 0x10000000 #define MPU_CAP_SYNC 0x00000010 #define MPU_CAP_FSK 0x00000020 #define MPU_CAP_CLS 0x00000040 #define MPU_CAP_SMPTE 0x00000080 #define MPU_CAP_2PORT 0x00000001 int timer_flag; #define MBUF_MAX 10 #define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \ {printk("MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;} int m_busy; unsigned char m_buf[MBUF_MAX]; int m_ptr; int m_state; int m_left; unsigned char last_status; void (*inputintr) (int dev, unsigned char data); int shared_irq; }; #define DATAPORT(base) (base) #define COMDPORT(base) (base+1) #define STATPORT(base) (base+1) #define mpu401_status(base) INB(STATPORT(base)) #define input_avail(base) (!(mpu401_status(base)&INPUT_AVAIL)) #define output_ready(base) (!(mpu401_status(base)&OUTPUT_READY)) #define write_command(base, cmd) OUTB(cmd, COMDPORT(base)) #define read_data(base) INB(DATAPORT(base)) #define write_data(base, byte) OUTB(byte, DATAPORT(base)) #define OUTPUT_READY 0x40 #define INPUT_AVAIL 0x80 #define MPU_ACK 0xF7 #define MPU_RESET 0xFF #define UART_MODE_ON 0x3F static struct mpu_config dev_conf[MAX_MIDI_DEV] = { {0}}; static int n_mpu_devs = 0; static int irq2dev[16] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; static int reset_mpu401 (struct mpu_config *devc); static void set_uart_mode (int dev, struct mpu_config *devc, int arg); static void mpu_timer_init (int midi_dev); static void mpu_timer_interrupt (void); static void timer_ext_event (struct mpu_config *devc, int event, int parm); static struct synth_info mpu_synth_info_proto = {"MPU-401 MIDI interface", 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, SYNTH_CAP_INPUT}; static struct synth_info mpu_synth_info[MAX_MIDI_DEV]; /* * States for the input scanner */ #define ST_INIT 0 /* Ready for timing byte or msg */ #define ST_TIMED 1 /* Leading timing byte rcvd */ #define ST_DATABYTE 2 /* Waiting for (nr_left) data bytes */ #define ST_SYSMSG 100 /* System message (sysx etc). */ #define ST_SYSEX 101 /* System exclusive msg */ #define ST_MTC 102 /* Midi Time Code (MTC) qframe msg */ #define ST_SONGSEL 103 /* Song select */ #define ST_SONGPOS 104 /* Song position pointer */ static unsigned char len_tab[] = /* # of data bytes following a status */ { 2, /* 8x */ 2, /* 9x */ 2, /* Ax */ 2, /* Bx */ 1, /* Cx */ 1, /* Dx */ 2, /* Ex */ 0 /* Fx */ }; #define STORE(cmd) \ { \ int len; \ unsigned char obuf[8]; \ cmd; \ seq_input_event(obuf, len); \ } #define _seqbuf obuf #define _seqbufptr 0 #define _SEQ_ADVBUF(x) len=x static int mpu_input_scanner (struct mpu_config *devc, unsigned char midic) { switch (devc->m_state) { case ST_INIT: switch (midic) { case 0xf8: /* Timer overflow */ break; case 0xfc: printk (""); break; case 0xfd: if (devc->timer_flag) mpu_timer_interrupt (); break; case 0xfe: return MPU_ACK; break; case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7: printk ("", midic & 0x0f); break; case 0xf9: printk (""); break; case 0xff: devc->m_state = ST_SYSMSG; break; default: if (midic <= 0xef) { /* printk("mpu time: %d ", midic); */ devc->m_state = ST_TIMED; } else printk (" ", midic); } break; case ST_TIMED: { int msg = (midic & 0xf0) >> 4; devc->m_state = ST_DATABYTE; if (msg < 8) /* Data byte */ { /* printk("midi msg (running status) "); */ msg = (devc->last_status & 0xf0) >> 4; msg -= 8; devc->m_left = len_tab[msg] - 1; devc->m_ptr = 2; devc->m_buf[0] = devc->last_status; devc->m_buf[1] = midic; if (devc->m_left <= 0) { devc->m_state = ST_INIT; do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr); devc->m_ptr = 0; } } else if (msg == 0xf) /* MPU MARK */ { devc->m_state = ST_INIT; switch (midic) { case 0xf8: /* printk("NOP "); */ break; case 0xf9: /* printk("meas end "); */ break; case 0xfc: /* printk("data end "); */ break; default: printk ("Unknown MPU mark %02x\n", midic); } } else { devc->last_status = midic; /* printk ("midi msg "); */ msg -= 8; devc->m_left = len_tab[msg]; devc->m_ptr = 1; devc->m_buf[0] = midic; if (devc->m_left <= 0) { devc->m_state = ST_INIT; do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr); devc->m_ptr = 0; } } } break; case ST_SYSMSG: switch (midic) { case 0xf0: printk (""); devc->m_state = ST_SYSEX; break; case 0xf1: devc->m_state = ST_MTC; break; case 0xf2: devc->m_state = ST_SONGPOS; devc->m_ptr = 0; break; case 0xf3: devc->m_state = ST_SONGSEL; break; case 0xf6: /* printk("tune_request\n"); */ devc->m_state = ST_INIT; /* * Real time messages */ case 0xf8: /* midi clock */ devc->m_state = ST_INIT; timer_ext_event (devc, TMR_CLOCK, 0); break; case 0xfA: devc->m_state = ST_INIT; timer_ext_event (devc, TMR_START, 0); break; case 0xFB: devc->m_state = ST_INIT; timer_ext_event (devc, TMR_CONTINUE, 0); break; case 0xFC: devc->m_state = ST_INIT; timer_ext_event (devc, TMR_STOP, 0); break; case 0xFE: /* active sensing */ devc->m_state = ST_INIT; break; case 0xff: /* printk("midi hard reset"); */ devc->m_state = ST_INIT; break; default: printk ("unknown MIDI sysmsg %0x\n", midic); devc->m_state = ST_INIT; } break; case ST_MTC: devc->m_state = ST_INIT; printk ("MTC frame %x02\n", midic); break; case ST_SYSEX: if (midic == 0xf7) { printk (""); devc->m_state = ST_INIT; } else printk ("%02x ", midic); break; case ST_SONGPOS: BUFTEST (devc); devc->m_buf[devc->m_ptr++] = midic; if (devc->m_ptr == 2) { devc->m_state = ST_INIT; devc->m_ptr = 0; timer_ext_event (devc, TMR_SPP, ((devc->m_buf[1] & 0x7f) << 7) | (devc->m_buf[0] & 0x7f)); } break; case ST_DATABYTE: BUFTEST (devc); devc->m_buf[devc->m_ptr++] = midic; if ((--devc->m_left) <= 0) { devc->m_state = ST_INIT; do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr); devc->m_ptr = 0; } break; default: printk ("Bad state %d ", devc->m_state); devc->m_state = ST_INIT; } return 1; } static void mpu401_input_loop (struct mpu_config *devc) { unsigned long flags; int busy; int n; DISABLE_INTR (flags); busy = devc->m_busy; devc->m_busy = 1; RESTORE_INTR (flags); if (busy) /* Already inside the scanner */ return; n = 50; while (input_avail (devc->base) && n-- > 0) { unsigned char c = read_data (devc->base); if (devc->mode == MODE_SYNTH) { mpu_input_scanner (devc, c); } else if (devc->opened & OPEN_READ && devc->inputintr != NULL) devc->inputintr (devc->devno, c); } devc->m_busy = 0; } void mpuintr (INT_HANDLER_PARMS (irq, dummy)) { struct mpu_config *devc; int dev; #ifdef linux sti (); #endif if (irq < 1 || irq > 15) { printk ("MPU-401: Interrupt #%d?\n", irq); return; } dev = irq2dev[irq]; if (dev == -1) { /* printk ("MPU-401: Interrupt #%d?\n", irq); */ return; } devc = &dev_conf[dev]; if (input_avail (devc->base)) if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH)) mpu401_input_loop (devc); else { /* Dummy read (just to acknowledge the interrupt) */ read_data (devc->base); } } static int mpu401_open (int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev) ) { int err; struct mpu_config *devc; if (dev < 0 || dev >= num_midis) return RET_ERROR (ENXIO); devc = &dev_conf[dev]; if (devc->opened) { printk ("MPU-401: Midi busy\n"); return RET_ERROR (EBUSY); } /* * Verify that the device is really running. * Some devices (such as Ensoniq SoundScape don't * work before the on board processor (OBP) is initialized * by downloadin it's microcode. */ if (!devc->initialized) { if (mpu401_status (devc->base) == 0xff) /* Bus float */ { printk ("MPU-401: Device not initialized properly\n"); return RET_ERROR (EIO); } reset_mpu401 (devc); } irq2dev[devc->irq] = dev; if (devc->shared_irq == 0) if ((err = snd_set_irq_handler (devc->irq, mpuintr, midi_devs[dev]->info.name) < 0)) { return err; } if (midi_devs[dev]->coproc) if ((err = midi_devs[dev]->coproc-> open (midi_devs[dev]->coproc->devc, COPR_MIDI)) < 0) { if (devc->shared_irq == 0) snd_release_irq (devc->irq); printk ("MPU-401: Can't access coprocessor device\n"); return err; } set_uart_mode (dev, devc, 1); devc->mode = MODE_MIDI; devc->synthno = 0; mpu401_input_loop (devc); devc->inputintr = input; devc->opened = mode; return 0; } static void mpu401_close (int dev) { struct mpu_config *devc; devc = &dev_conf[dev]; if (devc->uart_mode) reset_mpu401 (devc); /* * This disables the UART mode */ devc->mode = 0; if (devc->shared_irq == 0) snd_release_irq (devc->irq); devc->inputintr = NULL; if (midi_devs[dev]->coproc) midi_devs[dev]->coproc->close (midi_devs[dev]->coproc->devc, COPR_MIDI); devc->opened = 0; } static int mpu401_out (int dev, unsigned char midi_byte) { int timeout; unsigned long flags; struct mpu_config *devc; devc = &dev_conf[dev]; #if 0 /* * Test for input since pending input seems to block the output. */ if (input_avail (devc->base)) { mpu401_input_loop (devc); } #endif /* * Sometimes it takes about 13000 loops before the output becomes ready * (After reset). Normally it takes just about 10 loops. */ for (timeout = 3000; timeout > 0 && !output_ready (devc->base); timeout--); DISABLE_INTR (flags); if (!output_ready (devc->base)) { printk ("MPU-401: Send data timeout\n"); RESTORE_INTR (flags); return 0; } write_data (devc->base, midi_byte); RESTORE_INTR (flags); return 1; } static int mpu401_command (int dev, mpu_command_rec * cmd) { int i, timeout, ok; int ret = 0; unsigned long flags; struct mpu_config *devc; devc = &dev_conf[dev]; if (devc->uart_mode) /* * Not possible in UART mode */ { printk ("MPU-401 commands not possible in the UART mode\n"); return RET_ERROR (EINVAL); } /* * Test for input since pending input seems to block the output. */ if (input_avail (devc->base)) mpu401_input_loop (devc); /* * Sometimes it takes about 30000 loops before the output becomes ready * (After reset). Normally it takes just about 10 loops. */ timeout = 30000; retry: if (timeout-- <= 0) { printk ("MPU-401: Command (0x%x) timeout\n", (int) cmd->cmd); return RET_ERROR (EIO); } DISABLE_INTR (flags); if (!output_ready (devc->base)) { RESTORE_INTR (flags); goto retry; } write_command (devc->base, cmd->cmd); ok = 0; for (timeout = 50000; timeout > 0 && !ok; timeout--) if (input_avail (devc->base)) { if (mpu_input_scanner (devc, read_data (devc->base)) == MPU_ACK) ok = 1; } if (!ok) { RESTORE_INTR (flags); /* printk ("MPU: No ACK to command (0x%x)\n", (int) cmd->cmd); */ return RET_ERROR (EIO); } if (cmd->nr_args) for (i = 0; i < cmd->nr_args; i++) { for (timeout = 3000; timeout > 0 && !output_ready (devc->base); timeout--); if (!mpu401_out (dev, cmd->data[i])) { RESTORE_INTR (flags); printk ("MPU: Command (0x%x), parm send failed.\n", (int) cmd->cmd); return RET_ERROR (EIO); } } ret = 0; cmd->data[0] = 0; if (cmd->nr_returns) for (i = 0; i < cmd->nr_returns; i++) { ok = 0; for (timeout = 5000; timeout > 0 && !ok; timeout--) if (input_avail (devc->base)) { cmd->data[i] = read_data (devc->base); ok = 1; } if (!ok) { RESTORE_INTR (flags); /* printk ("MPU: No response(%d) to command (0x%x)\n", i, (int) cmd->cmd); */ return RET_ERROR (EIO); } } RESTORE_INTR (flags); return ret; } static int exec_cmd (int dev, int cmd, int data) { int ret; static mpu_command_rec rec; rec.cmd = cmd & 0xff; rec.nr_args = ((cmd & 0xf0) == 0xE0); rec.nr_returns = ((cmd & 0xf0) == 0xA0); rec.data[0] = data & 0xff; if ((ret = mpu401_command (dev, &rec)) < 0) return ret; return (unsigned char) rec.data[0]; } static int mpu401_prefix_cmd (int dev, unsigned char status) { struct mpu_config *devc = &dev_conf[dev]; if (devc->uart_mode) return 1; if (status < 0xf0) { if (exec_cmd (dev, 0xD0, 0) < 0) return 0; return 1; } switch (status) { case 0xF0: if (exec_cmd (dev, 0xDF, 0) < 0) return 0; return 1; break; default: return 0; } return 0; } static int mpu401_start_read (int dev) { return 0; } static int mpu401_end_read (int dev) { return 0; } static int mpu401_ioctl (int dev, unsigned cmd, unsigned arg) { struct mpu_config *devc; devc = &dev_conf[dev]; switch (cmd) { case 1: IOCTL_FROM_USER ((char *) &init_sequence, (char *) arg, 0, sizeof (init_sequence)); return 0; break; case SNDCTL_MIDI_MPUMODE: if (devc->version == 0) { printk ("MPU-401: Intelligent mode not supported by the HW\n"); return RET_ERROR (EINVAL); } set_uart_mode (dev, devc, !IOCTL_IN (arg)); return 0; break; case SNDCTL_MIDI_MPUCMD: { int ret; mpu_command_rec rec; IOCTL_FROM_USER ((char *) &rec, (char *) arg, 0, sizeof (rec)); if ((ret = mpu401_command (dev, &rec)) < 0) return ret; IOCTL_TO_USER ((char *) arg, 0, (char *) &rec, sizeof (rec)); return 0; } break; default: return RET_ERROR (EINVAL); } } static void mpu401_kick (int dev) { } static int mpu401_buffer_status (int dev) { return 0; /* * No data in buffers */ } static int mpu_synth_ioctl (int dev, unsigned int cmd, unsigned int arg) { int midi_dev; struct mpu_config *devc; midi_dev = synth_devs[dev]->midi_dev; if (midi_dev < 0 || midi_dev > num_midis) return RET_ERROR (ENXIO); devc = &dev_conf[midi_dev]; switch (cmd) { case SNDCTL_SYNTH_INFO: IOCTL_TO_USER ((char *) arg, 0, &mpu_synth_info[midi_dev], sizeof (struct synth_info)); return 0; break; case SNDCTL_SYNTH_MEMAVL: return 0x7fffffff; break; default: return RET_ERROR (EINVAL); } } static int mpu_synth_open (int dev, int mode) { int midi_dev, err; struct mpu_config *devc; midi_dev = synth_devs[dev]->midi_dev; if (midi_dev < 0 || midi_dev > num_midis) { return RET_ERROR (ENXIO); } devc = &dev_conf[midi_dev]; /* * Verify that the device is really running. * Some devices (such as Ensoniq SoundScape don't * work before the on board processor (OBP) is initialized * by downloadin it's microcode. */ if (!devc->initialized) { if (mpu401_status (devc->base) == 0xff) /* Bus float */ { printk ("MPU-401: Device not initialized properly\n"); return RET_ERROR (EIO); } reset_mpu401 (devc); } if (devc->opened) { printk ("MPU-401: Midi busy\n"); return RET_ERROR (EBUSY); } devc->mode = MODE_SYNTH; devc->synthno = dev; devc->inputintr = NULL; irq2dev[devc->irq] = midi_dev; if (devc->shared_irq == 0) if ((err = snd_set_irq_handler (devc->irq, mpuintr, midi_devs[midi_dev]->info.name) < 0)) { return err; } if (midi_devs[midi_dev]->coproc) if ((err = midi_devs[midi_dev]->coproc-> open (midi_devs[midi_dev]->coproc->devc, COPR_MIDI)) < 0) { if (devc->shared_irq == 0) snd_release_irq (devc->irq); printk ("MPU-401: Can't access coprocessor device\n"); return err; } devc->opened = mode; reset_mpu401 (devc); if (mode & OPEN_READ) { exec_cmd (midi_dev, 0x8B, 0); /* Enable data in stop mode */ exec_cmd (midi_dev, 0x34, 0); /* Return timing bytes in stop mode */ } return 0; } static void mpu_synth_close (int dev) { int midi_dev; struct mpu_config *devc; midi_dev = synth_devs[dev]->midi_dev; devc = &dev_conf[midi_dev]; exec_cmd (midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */ exec_cmd (midi_dev, 0x8a, 0); /* Disable data in stopped mode */ if (devc->shared_irq == 0) snd_release_irq (devc->irq); devc->inputintr = NULL; if (midi_devs[midi_dev]->coproc) midi_devs[midi_dev]->coproc->close (midi_devs[midi_dev]->coproc->devc, COPR_MIDI); devc->opened = 0; devc->mode = 0; } #define MIDI_SYNTH_NAME "MPU-401 UART Midi" #define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" +#include static struct synth_operations mpu401_synth_proto = { NULL, 0, SYNTH_TYPE_MIDI, 0, mpu_synth_open, mpu_synth_close, mpu_synth_ioctl, midi_synth_kill_note, midi_synth_start_note, midi_synth_set_instr, midi_synth_reset, midi_synth_hw_control, midi_synth_load_patch, midi_synth_aftertouch, midi_synth_controller, midi_synth_panning, NULL, midi_synth_patchmgr, midi_synth_bender, NULL, /* alloc */ midi_synth_setup_voice }; static struct synth_operations mpu401_synth_operations[MAX_MIDI_DEV]; static struct midi_operations mpu401_midi_proto = { {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, NULL, {0}, mpu401_open, mpu401_close, mpu401_ioctl, mpu401_out, mpu401_start_read, mpu401_end_read, mpu401_kick, NULL, mpu401_buffer_status, mpu401_prefix_cmd }; static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV]; static void mpu401_chk_version (struct mpu_config *devc) { int tmp; devc->version = devc->revision = 0; if ((tmp = exec_cmd (num_midis, 0xAC, 0)) < 0) return; if ((tmp & 0xf0) > 0x20) /* Why it's larger than 2.x ??? */ return; devc->version = tmp; if ((tmp = exec_cmd (num_midis, 0xAD, 0)) < 0) { devc->version = 0; return; } devc->revision = tmp; } long attach_mpu401 (long mem_start, struct address_info *hw_config) { unsigned long flags; char revision_char; struct mpu_config *devc; if (num_midis >= MAX_MIDI_DEV) { printk ("MPU-401: Too many midi devices detected\n"); return mem_start; } devc = &dev_conf[num_midis]; devc->base = hw_config->io_base; devc->irq = hw_config->irq; devc->opened = 0; devc->uart_mode = 0; devc->initialized = 0; devc->version = 0; devc->revision = 0; devc->capabilities = 0; devc->timer_flag = 0; devc->m_busy = 0; devc->m_state = ST_INIT; devc->shared_irq = hw_config->always_detect; if (!hw_config->always_detect) { /* Verify the hardware again */ if (!reset_mpu401 (devc)) return mem_start; DISABLE_INTR (flags); mpu401_chk_version (devc); if (devc->version == 0) mpu401_chk_version (devc); RESTORE_INTR (flags); } if (devc->version == 0) { memcpy ((char *) &mpu401_synth_operations[num_midis], (char *) &std_midi_synth, sizeof (struct synth_operations)); } else { devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */ memcpy ((char *) &mpu401_synth_operations[num_midis], (char *) &mpu401_synth_proto, sizeof (struct synth_operations)); } memcpy ((char *) &mpu401_midi_operations[num_midis], (char *) &mpu401_midi_proto, sizeof (struct midi_operations)); mpu401_midi_operations[num_midis].converter = &mpu401_synth_operations[num_midis]; memcpy ((char *) &mpu_synth_info[num_midis], (char *) &mpu_synth_info_proto, sizeof (struct synth_info)); n_mpu_devs++; if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */ { int ports = (devc->revision & 0x08) ? 32 : 16; devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE | MPU_CAP_CLS | MPU_CAP_2PORT; revision_char = (devc->revision == 0x7f) ? 'M' : ' '; #if defined(__FreeBSD__) printk ("mpu0: ", #else printk (" ", #endif ports, revision_char); sprintf (mpu_synth_info[num_midis].name, "MQX-%d%c MIDI Interface #%d", ports, revision_char, n_mpu_devs); } else { revision_char = devc->revision ? devc->revision + '@' : ' '; if (devc->revision > ('Z' - '@')) revision_char = '+'; devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK; #if defined(__FreeBSD__) printk ("mpu0: ", #else printk (" ", #endif (devc->version & 0xf0) >> 4, devc->version & 0x0f, revision_char); sprintf (mpu_synth_info[num_midis].name, "MPU-401 %d.%d%c Midi interface #%d", (devc->version & 0xf0) >> 4, devc->version & 0x0f, revision_char, n_mpu_devs); } strcpy (mpu401_midi_operations[num_midis].info.name, mpu_synth_info[num_midis].name); mpu401_synth_operations[num_midis].midi_dev = devc->devno = num_midis; mpu401_synth_operations[devc->devno].info = &mpu_synth_info[devc->devno]; if (devc->capabilities & MPU_CAP_INTLG) /* Has timer */ mpu_timer_init (num_midis); irq2dev[devc->irq] = num_midis; midi_devs[num_midis++] = &mpu401_midi_operations[devc->devno]; return mem_start; } static int reset_mpu401 (struct mpu_config *devc) { unsigned long flags; int ok, timeout, n; int timeout_limit; /* * Send the RESET command. Try again if no success at the first time. * (If the device is in the UART mode, it will not ack the reset cmd). */ ok = 0; timeout_limit = devc->initialized ? 30000 : 100000; devc->initialized = 1; for (n = 0; n < 2 && !ok; n++) { for (timeout = timeout_limit; timeout > 0 && !ok; timeout--) ok = output_ready (devc->base); write_command (devc->base, MPU_RESET); /* * Send MPU-401 RESET Command */ /* * Wait at least 25 msec. This method is not accurate so let's make the * loop bit longer. Cannot sleep since this is called during boot. */ for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--) { DISABLE_INTR (flags); if (input_avail (devc->base)) if (read_data (devc->base) == MPU_ACK) ok = 1; RESTORE_INTR (flags); } } devc->m_state = ST_INIT; devc->m_ptr = 0; devc->m_left = 0; devc->last_status = 0; devc->uart_mode = 0; return ok; } static void set_uart_mode (int dev, struct mpu_config *devc, int arg) { if (!arg && devc->version == 0) { return; } if ((devc->uart_mode == 0) == (arg == 0)) { return; /* Already set */ } reset_mpu401 (devc); /* This exits the uart mode */ if (arg) { if (exec_cmd (dev, UART_MODE_ON, 0) < 0) { printk ("MPU%d: Can't enter UART mode\n", devc->devno); devc->uart_mode = 0; return; } } devc->uart_mode = arg; } int probe_mpu401 (struct address_info *hw_config) { int ok = 0; struct mpu_config tmp_devc; tmp_devc.base = hw_config->io_base; tmp_devc.irq = hw_config->irq; tmp_devc.initialized = 0; #if !defined(EXCLUDE_AEDSP16) && defined(AEDSP16_MPU401) /* * Initialize Audio Excel DSP 16 to MPU-401, before any operation. */ InitAEDSP16_MPU401 (hw_config); #endif if (hw_config->always_detect) return 1; if (INB (hw_config->io_base + 1) == 0xff) return 0; /* Just bus float? */ ok = reset_mpu401 (&tmp_devc); return ok; } /***************************************************** * Timer stuff ****************************************************/ #if !defined(EXCLUDE_SEQUENCER) static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0; static volatile int curr_tempo, curr_timebase, hw_timebase; static int max_timebase = 8; /* 8*24=192 ppqn */ static volatile unsigned long next_event_time; static volatile unsigned long curr_ticks, curr_clocks; static unsigned long prev_event_time; static int metronome_mode; static unsigned long clocks2ticks (unsigned long clocks) { /* * The MPU-401 supports just a limited set of possible timebase values. * Since the applications require more choices, the driver has to * program the HW to do it's best and to convert between the HW and * actual timebases. */ return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase; } static void set_timebase (int midi_dev, int val) { int hw_val; if (val < 48) val = 48; if (val > 1000) val = 1000; hw_val = val; hw_val = (hw_val + 23) / 24; if (hw_val > max_timebase) hw_val = max_timebase; if (exec_cmd (midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0) { printk ("MPU: Can't set HW timebase to %d\n", hw_val * 24); return; } hw_timebase = hw_val * 24; curr_timebase = val; } static void tmr_reset (void) { unsigned long flags; DISABLE_INTR (flags); next_event_time = 0xffffffff; prev_event_time = 0; curr_ticks = curr_clocks = 0; RESTORE_INTR (flags); } static void set_timer_mode (int midi_dev) { if (timer_mode & TMR_MODE_CLS) exec_cmd (midi_dev, 0x3c, 0); /* Use CLS sync */ else if (timer_mode & TMR_MODE_SMPTE) exec_cmd (midi_dev, 0x3d, 0); /* Use SMPTE sync */ if (timer_mode & TMR_INTERNAL) { exec_cmd (midi_dev, 0x80, 0); /* Use MIDI sync */ } else { if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS)) { exec_cmd (midi_dev, 0x82, 0); /* Use MIDI sync */ exec_cmd (midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */ } else if (timer_mode & TMR_MODE_FSK) exec_cmd (midi_dev, 0x81, 0); /* Use FSK sync */ } } static void stop_metronome (int midi_dev) { exec_cmd (midi_dev, 0x84, 0); /* Disable metronome */ } static void setup_metronome (int midi_dev) { int numerator, denominator; int clks_per_click, num_32nds_per_beat; int beats_per_measure; numerator = ((unsigned) metronome_mode >> 24) & 0xff; denominator = ((unsigned) metronome_mode >> 16) & 0xff; clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff; num_32nds_per_beat = (unsigned) metronome_mode & 0xff; beats_per_measure = (numerator * 4) >> denominator; if (!metronome_mode) exec_cmd (midi_dev, 0x84, 0); /* Disable metronome */ else { exec_cmd (midi_dev, 0xE4, clks_per_click); exec_cmd (midi_dev, 0xE6, beats_per_measure); exec_cmd (midi_dev, 0x83, 0); /* Enable metronome without accents */ } } static int start_timer (int midi_dev) { tmr_reset (); set_timer_mode (midi_dev); if (tmr_running) return TIMER_NOT_ARMED; /* Already running */ if (timer_mode & TMR_INTERNAL) { exec_cmd (midi_dev, 0x02, 0); /* Send MIDI start */ tmr_running = 1; return TIMER_NOT_ARMED; } else { exec_cmd (midi_dev, 0x35, 0); /* Enable mode messages to PC */ exec_cmd (midi_dev, 0x38, 0); /* Enable sys common messages to PC */ exec_cmd (midi_dev, 0x39, 0); /* Enable real time messages to PC */ exec_cmd (midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */ } return TIMER_ARMED; } static int mpu_timer_open (int dev, int mode) { int midi_dev = sound_timer_devs[dev]->devlink; if (timer_open) return RET_ERROR (EBUSY); tmr_reset (); curr_tempo = 50; exec_cmd (midi_dev, 0xE0, 50); curr_timebase = hw_timebase = 120; set_timebase (midi_dev, 120); timer_open = 1; metronome_mode = 0; set_timer_mode (midi_dev); exec_cmd (midi_dev, 0xe7, 0x04); /* Send all clocks to host */ exec_cmd (midi_dev, 0x95, 0); /* Enable clock to host */ return 0; } static void mpu_timer_close (int dev) { int midi_dev = sound_timer_devs[dev]->devlink; timer_open = tmr_running = 0; exec_cmd (midi_dev, 0x15, 0); /* Stop all */ exec_cmd (midi_dev, 0x94, 0); /* Disable clock to host */ exec_cmd (midi_dev, 0x8c, 0); /* Disable measure end messages to host */ stop_metronome (midi_dev); } static int mpu_timer_event (int dev, unsigned char *event) { unsigned char command = event[1]; unsigned long parm = *(unsigned int *) &event[4]; int midi_dev = sound_timer_devs[dev]->devlink; switch (command) { case TMR_WAIT_REL: parm += prev_event_time; case TMR_WAIT_ABS: if (parm > 0) { long time; if (parm <= curr_ticks) /* It's the time */ return TIMER_NOT_ARMED; time = parm; next_event_time = prev_event_time = time; return TIMER_ARMED; } break; case TMR_START: if (tmr_running) break; return start_timer (midi_dev); break; case TMR_STOP: exec_cmd (midi_dev, 0x01, 0); /* Send MIDI stop */ stop_metronome (midi_dev); tmr_running = 0; break; case TMR_CONTINUE: if (tmr_running) break; exec_cmd (midi_dev, 0x03, 0); /* Send MIDI continue */ setup_metronome (midi_dev); tmr_running = 1; break; case TMR_TEMPO: if (parm) { if (parm < 8) parm = 8; if (parm > 250) parm = 250; if (exec_cmd (midi_dev, 0xE0, parm) < 0) printk ("MPU: Can't set tempo to %d\n", (int) parm); curr_tempo = parm; } break; case TMR_ECHO: seq_copy_to_input (event, 8); break; case TMR_TIMESIG: if (metronome_mode) /* Metronome enabled */ { metronome_mode = parm; setup_metronome (midi_dev); } break; default:; } return TIMER_NOT_ARMED; } static unsigned long mpu_timer_get_time (int dev) { if (!timer_open) return 0; return curr_ticks; } static int mpu_timer_ioctl (int dev, unsigned int command, unsigned int arg) { int midi_dev = sound_timer_devs[dev]->devlink; switch (command) { case SNDCTL_TMR_SOURCE: { int parm = IOCTL_IN (arg) & timer_caps; if (parm != 0) { timer_mode = parm; if (timer_mode & TMR_MODE_CLS) exec_cmd (midi_dev, 0x3c, 0); /* Use CLS sync */ else if (timer_mode & TMR_MODE_SMPTE) exec_cmd (midi_dev, 0x3d, 0); /* Use SMPTE sync */ } return IOCTL_OUT (arg, timer_mode); } break; case SNDCTL_TMR_START: start_timer (midi_dev); return 0; break; case SNDCTL_TMR_STOP: tmr_running = 0; exec_cmd (midi_dev, 0x01, 0); /* Send MIDI stop */ stop_metronome (midi_dev); return 0; break; case SNDCTL_TMR_CONTINUE: if (tmr_running) return 0; tmr_running = 1; exec_cmd (midi_dev, 0x03, 0); /* Send MIDI continue */ return 0; break; case SNDCTL_TMR_TIMEBASE: { int val = IOCTL_IN (arg); if (val) set_timebase (midi_dev, val); return IOCTL_OUT (arg, curr_timebase); } break; case SNDCTL_TMR_TEMPO: { int val = IOCTL_IN (arg); int ret; if (val) { if (val < 8) val = 8; if (val > 250) val = 250; if ((ret = exec_cmd (midi_dev, 0xE0, val)) < 0) { printk ("MPU: Can't set tempo to %d\n", (int) val); return ret; } curr_tempo = val; } return IOCTL_OUT (arg, curr_tempo); } break; case SNDCTL_SEQ_CTRLRATE: if (IOCTL_IN (arg) != 0) /* Can't change */ return RET_ERROR (EINVAL); return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60); break; case SNDCTL_TMR_METRONOME: metronome_mode = IOCTL_IN (arg); setup_metronome (midi_dev); return 0; break; default: } return RET_ERROR (EINVAL); } static void mpu_timer_arm (int dev, long time) { if (time < 0) time = curr_ticks + 1; else if (time <= curr_ticks) /* It's the time */ return; next_event_time = prev_event_time = time; return; } static struct sound_timer_operations mpu_timer = { {"MPU-401 Timer", 0}, 10, /* Priority */ 0, /* Local device link */ mpu_timer_open, mpu_timer_close, mpu_timer_event, mpu_timer_get_time, mpu_timer_ioctl, mpu_timer_arm }; static void mpu_timer_interrupt (void) { if (!timer_open) return; if (!tmr_running) return; curr_clocks++; curr_ticks = clocks2ticks (curr_clocks); if (curr_ticks >= next_event_time) { next_event_time = 0xffffffff; sequencer_timer (); } } static void timer_ext_event (struct mpu_config *devc, int event, int parm) { int midi_dev = devc->devno; if (!devc->timer_flag) return; switch (event) { case TMR_CLOCK: printk (""); break; case TMR_START: printk ("Ext MIDI start\n"); if (!tmr_running) if (timer_mode & TMR_EXTERNAL) { tmr_running = 1; setup_metronome (midi_dev); next_event_time = 0; STORE (SEQ_START_TIMER ()); } break; case TMR_STOP: printk ("Ext MIDI stop\n"); if (timer_mode & TMR_EXTERNAL) { tmr_running = 0; stop_metronome (midi_dev); STORE (SEQ_STOP_TIMER ()); } break; case TMR_CONTINUE: printk ("Ext MIDI continue\n"); if (timer_mode & TMR_EXTERNAL) { tmr_running = 1; setup_metronome (midi_dev); STORE (SEQ_CONTINUE_TIMER ()); } break; case TMR_SPP: printk ("Songpos: %d\n", parm); if (timer_mode & TMR_EXTERNAL) { STORE (SEQ_SONGPOS (parm)); } break; } } static void mpu_timer_init (int midi_dev) { struct mpu_config *devc; int n; devc = &dev_conf[midi_dev]; if (timer_initialized) return; /* There is already a similar timer */ timer_initialized = 1; mpu_timer.devlink = midi_dev; dev_conf[midi_dev].timer_flag = 1; #if 1 if (num_sound_timers >= MAX_TIMER_DEV) n = 0; /* Overwrite the system timer */ else n = num_sound_timers++; #else n = 0; #endif sound_timer_devs[n] = &mpu_timer; if (devc->version < 0x20) /* Original MPU-401 */ timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI; else { /* * The version number 2.0 is used (at least) by the * MusicQuest cards and the Roland Super-MPU. * * MusicQuest has given a special meaning to the bits of the * revision number. The Super-MPU returns 0. */ if (devc->revision) timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI; if (devc->revision & 0x02) timer_caps |= TMR_MODE_CLS; #if 0 if (devc->revision & 0x04) timer_caps |= TMR_MODE_SMPTE; #endif if (devc->revision & 0x40) max_timebase = 10; /* Has the 216 and 240 ppqn modes */ } timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps; } #endif #endif #endif Index: head/sys/pc98/pc98/sound/opl3.c =================================================================== --- head/sys/pc98/pc98/sound/opl3.c (revision 18264) +++ head/sys/pc98/pc98/sound/opl3.c (revision 18265) @@ -1,1292 +1,1292 @@ /* * sound/opl3.c * * A low level driver for Yamaha YM3812 and OPL-3 -chips * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * Major improvements to the FM handling 30AUG92 by Rob Hooft, */ /* * hooft@chem.ruu.nl */ -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812) -#include "opl3.h" +#include #define MAX_VOICE 18 #define OFFS_4OP 11 /* * * * Definitions for the operators OP3 and * * OP4 * * begin here */ static int opl3_enabled = 0; static int opl4_enabled = 0; #ifdef PC98 static int left_address = 0x28d2, right_address = 0x28d2, both_address = 0; #else static int left_address = 0x388, right_address = 0x388, both_address = 0; #endif static int nr_voices = 9; static int logical_voices[MAX_VOICE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; struct voice_info { unsigned char keyon_byte; long bender; long bender_range; unsigned long orig_freq; unsigned long current_freq; int mode; }; static struct voice_info voices[MAX_VOICE]; static struct voice_alloc_info *voice_alloc; static struct channel_info *chn_info; static struct sbi_instrument *instrmap; static struct sbi_instrument *active_instrument[MAX_VOICE] = {NULL}; static struct synth_info fm_info = {"OPL-2", 0, SYNTH_TYPE_FM, FM_TYPE_ADLIB, 0, 9, 0, SBFM_MAXINSTR, 0}; static int already_initialized = 0; static int opl3_ok = 0; static int opl3_busy = 0; static int fm_model = 0; /* * * * * 0=no fm, 1=mono, 2=SB Pro 1, 3=SB * Pro 2 * * */ static int store_instr (int instr_no, struct sbi_instrument *instr); static void freq_to_fnum (int freq, int *block, int *fnum); static void opl3_command (int io_addr, unsigned int addr, unsigned int val); static int opl3_kill_note (int dev, int voice, int note, int velocity); static unsigned char connection_mask = 0x00; void enable_opl3_mode (int left, int right, int both) { if (opl3_enabled) return; opl3_enabled = 1; left_address = left; right_address = right; both_address = both; fm_info.capabilities = SYNTH_CAP_OPL3; fm_info.synth_subtype = FM_TYPE_OPL3; } static void enter_4op_mode (void) { int i; static int voices_4op[MAX_VOICE] = {0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17}; connection_mask = 0x3f; /* Connect all possible 4 OP voices */ opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x3f); for (i = 0; i < 3; i++) physical_voices[i].voice_mode = 4; for (i = 3; i < 6; i++) physical_voices[i].voice_mode = 0; for (i = 9; i < 12; i++) physical_voices[i].voice_mode = 4; for (i = 12; i < 15; i++) physical_voices[i].voice_mode = 0; for (i = 0; i < 12; i++) logical_voices[i] = voices_4op[i]; voice_alloc->max_voice = nr_voices = 12; } static int opl3_ioctl (int dev, unsigned int cmd, unsigned int arg) { switch (cmd) { case SNDCTL_FM_LOAD_INSTR: { struct sbi_instrument ins; IOCTL_FROM_USER ((char *) &ins, (char *) arg, 0, sizeof (ins)); if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) { printk ("FM Error: Invalid instrument number %d\n", ins.channel); return RET_ERROR (EINVAL); } pmgr_inform (dev, PM_E_PATCH_LOADED, ins.channel, 0, 0, 0); return store_instr (ins.channel, &ins); } break; case SNDCTL_SYNTH_INFO: fm_info.nr_voices = (nr_voices == 12) ? 6 : nr_voices; IOCTL_TO_USER ((char *) arg, 0, &fm_info, sizeof (fm_info)); return 0; break; case SNDCTL_SYNTH_MEMAVL: return 0x7fffffff; break; case SNDCTL_FM_4OP_ENABLE: if (opl3_enabled) enter_4op_mode (); return 0; break; default: return RET_ERROR (EINVAL); } } int opl3_detect (int ioaddr) { /* * This function returns 1 if the FM chicp is present at the given I/O port * The detection algorithm plays with the timer built in the FM chip and * looks for a change in the status register. * * Note! The timers of the FM chip are not connected to AdLib (and compatible) * boards. * * Note2! The chip is initialized if detected. */ unsigned char stat1, stat2, signature; int i; if (already_initialized) { return 0; /* * Do avoid duplicate initializations */ } if (opl3_enabled) ioaddr = left_address; /* Reset timers 1 and 2 */ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); /* Reset the IRQ of the FM chip */ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); signature = stat1 = INB (ioaddr); /* Status register */ if ((stat1 & 0xE0) != 0x00) { return 0; /* * Should be 0x00 */ } opl3_command (ioaddr, TIMER1_REGISTER, 0xff); /* Set timer1 to 0xff */ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER2_MASK | TIMER1_START); /* * Unmask and start timer 1 */ /* * Now we have to delay at least 80 usec */ for (i = 0; i < 50; i++) tenmicrosec (); stat2 = INB (ioaddr); /* * Read status after timers have expired */ /* * Stop the timers */ /* Reset timers 1 and 2 */ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); /* Reset the IRQ of the FM chip */ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); if ((stat2 & 0xE0) != 0xc0) { return 0; /* * There is no YM3812 */ } /* * There is a FM chicp in this address. Detect the type (OPL2 to OPL4) */ if (signature == 0x06) /* OPL2 */ { opl3_enabled = 0; } else if (signature == 0x00) /* OPL3 or OPL4 */ { unsigned char tmp; if (!opl3_enabled) /* Was not already enabled */ { left_address = ioaddr; right_address = ioaddr + 2; opl3_enabled = 1; } /* * Detect availability of OPL4 (_experimental_). Works propably * only after a cold boot. In addition the OPL4 port * of the chip may not be connected to the PC bus at all. */ opl3_command (right_address, OPL3_MODE_REGISTER, 0x00); opl3_command (right_address, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE); if ((tmp = INB (ioaddr)) == 0x02) /* Have a OPL4 */ { opl4_enabled = 1; } opl3_command (right_address, OPL3_MODE_REGISTER, 0); } for (i = 0; i < 9; i++) opl3_command (ioaddr, KEYON_BLOCK + i, 0); /* * Note off */ opl3_command (ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT); opl3_command (ioaddr, PERCUSSION_REGISTER, 0x00); /* * Melodic mode. */ return 1; } static int opl3_kill_note (int dev, int voice, int note, int velocity) { struct physical_voice_info *map; if (voice < 0 || voice >= nr_voices) return 0; voice_alloc->map[voice] = 0; map = &physical_voices[logical_voices[voice]]; DEB (printk ("Kill note %d\n", voice)); if (map->voice_mode == 0) return 0; opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, voices[voice].keyon_byte & ~0x20); voices[voice].keyon_byte = 0; voices[voice].bender = 0; voices[voice].bender_range = 200; /* * 200 cents = 2 semitones */ voices[voice].orig_freq = 0; voices[voice].current_freq = 0; voices[voice].mode = 0; return 0; } #define HIHAT 0 #define CYMBAL 1 #define TOMTOM 2 #define SNARE 3 #define BDRUM 4 #define UNDEFINED TOMTOM #define DEFAULT TOMTOM static int store_instr (int instr_no, struct sbi_instrument *instr) { if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || !opl3_enabled)) printk ("FM warning: Invalid patch format field (key) 0x%x\n", instr->key); memcpy ((char *) &(instrmap[instr_no]), (char *) instr, sizeof (*instr)); return 0; } static int opl3_set_instr (int dev, int voice, int instr_no) { if (voice < 0 || voice >= nr_voices) return 0; if (instr_no < 0 || instr_no >= SBFM_MAXINSTR) return 0; active_instrument[voice] = &instrmap[instr_no]; return 0; } /* * The next table looks magical, but it certainly is not. Its values have * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception * for i=0. This log-table converts a linear volume-scaling (0..127) to a * logarithmic scaling as present in the FM-synthesizer chips. so : Volume * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative * volume -8 it was implemented as a table because it is only 128 bytes and * it saves a lot of log() calculations. (RH) */ static char fm_volume_table[128] = {-64, -48, -40, -35, -32, -29, -27, -26, /* * 0 - 7 */ -24, -23, -21, -20, -19, -18, -18, -17, /* * 8 - 15 */ -16, -15, -15, -14, -13, -13, -12, -12, /* * 16 - 23 */ -11, -11, -10, -10, -10, -9, -9, -8, /* * 24 - 31 */ -8, -8, -7, -7, -7, -6, -6, -6, /* * 32 - 39 */ -5, -5, -5, -5, -4, -4, -4, -4, /* * 40 - 47 */ -3, -3, -3, -3, -2, -2, -2, -2, /* * 48 - 55 */ -2, -1, -1, -1, -1, 0, 0, 0, /* * 56 - 63 */ 0, 0, 0, 1, 1, 1, 1, 1, /* * 64 - 71 */ 1, 2, 2, 2, 2, 2, 2, 2, /* * 72 - 79 */ 3, 3, 3, 3, 3, 3, 3, 4, /* * 80 - 87 */ 4, 4, 4, 4, 4, 4, 4, 5, /* * 88 - 95 */ 5, 5, 5, 5, 5, 5, 5, 5, /* * 96 - 103 */ 6, 6, 6, 6, 6, 6, 6, 6, /* * 104 - 111 */ 6, 7, 7, 7, 7, 7, 7, 7, /* * 112 - 119 */ 7, 7, 7, 8, 8, 8, 8, 8}; /* * * * * 120 - 127 */ static void calc_vol (unsigned char *regbyte, int volume) { int level = (~*regbyte & 0x3f); if (level) level += fm_volume_table[volume]; if (level > 0x3f) level = 0x3f; if (level < 0) level = 0; *regbyte = (*regbyte & 0xc0) | (~level & 0x3f); } static void set_voice_volume (int voice, int volume) { unsigned char vol1, vol2, vol3, vol4; struct sbi_instrument *instr; struct physical_voice_info *map; if (voice < 0 || voice >= nr_voices) return; map = &physical_voices[logical_voices[voice]]; instr = active_instrument[voice]; if (!instr) instr = &instrmap[0]; if (instr->channel < 0) return; if (voices[voice].mode == 0) return; if (voices[voice].mode == 2) { /* * 2 OP voice */ vol1 = instr->operators[2]; vol2 = instr->operators[3]; if ((instr->operators[10] & 0x01)) { /* * Additive synthesis */ calc_vol (&vol1, volume); calc_vol (&vol2, volume); } else { /* * FM synthesis */ calc_vol (&vol2, volume); } opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1); /* * Modulator * volume */ opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2); /* * Carrier * volume */ } else { /* * 4 OP voice */ int connection; vol1 = instr->operators[2]; vol2 = instr->operators[3]; vol3 = instr->operators[OFFS_4OP + 2]; vol4 = instr->operators[OFFS_4OP + 3]; /* * The connection method for 4 OP voices is defined by the rightmost * bits at the offsets 10 and 10+OFFS_4OP */ connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); switch (connection) { case 0: calc_vol (&vol4, volume); /* * Just the OP 4 is carrier */ break; case 1: calc_vol (&vol2, volume); calc_vol (&vol4, volume); break; case 2: calc_vol (&vol1, volume); calc_vol (&vol4, volume); break; case 3: calc_vol (&vol1, volume); calc_vol (&vol3, volume); calc_vol (&vol4, volume); break; default: /* * Why ?? */ ; } opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1); opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2); opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], vol3); opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], vol4); } } static int opl3_start_note (int dev, int voice, int note, int volume) { unsigned char data, fpc; int block, fnum, freq, voice_mode; struct sbi_instrument *instr; struct physical_voice_info *map; if (voice < 0 || voice >= nr_voices) return 0; map = &physical_voices[logical_voices[voice]]; if (map->voice_mode == 0) return 0; if (note == 255) /* * Just change the volume */ { set_voice_volume (voice, volume); return 0; } /* * Kill previous note before playing */ opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /* * Carrier * volume to * min */ opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /* * Modulator * volume to */ if (map->voice_mode == 4) { opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], 0xff); opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], 0xff); } opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /* * Note * off */ instr = active_instrument[voice]; if (!instr) instr = &instrmap[0]; if (instr->channel < 0) { printk ( "OPL3: Initializing voice %d with undefined instrument\n", voice); return 0; } if (map->voice_mode == 2 && instr->key == OPL3_PATCH) return 0; /* * Cannot play */ voice_mode = map->voice_mode; if (voice_mode == 4) { int voice_shift; voice_shift = (map->ioaddr == left_address) ? 0 : 3; voice_shift += map->voice_num; if (instr->key != OPL3_PATCH) /* * Just 2 OP patch */ { voice_mode = 2; connection_mask &= ~(1 << voice_shift); } else { connection_mask |= (1 << voice_shift); } opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask); } /* * Set Sound Characteristics */ opl3_command (map->ioaddr, AM_VIB + map->op[0], instr->operators[0]); opl3_command (map->ioaddr, AM_VIB + map->op[1], instr->operators[1]); /* * Set Attack/Decay */ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]); opl3_command (map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]); /* * Set Sustain/Release */ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]); opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]); /* * Set Wave Select */ opl3_command (map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]); opl3_command (map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]); /* * Set Feedback/Connection */ fpc = instr->operators[10]; if (!(fpc & 0x30)) fpc |= 0x30; /* * Ensure that at least one chn is enabled */ opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, fpc); /* * If the voice is a 4 OP one, initialize the operators 3 and 4 also */ if (voice_mode == 4) { /* * Set Sound Characteristics */ opl3_command (map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]); opl3_command (map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]); /* * Set Attack/Decay */ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]); opl3_command (map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]); /* * Set Sustain/Release */ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]); opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]); /* * Set Wave Select */ opl3_command (map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]); opl3_command (map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]); /* * Set Feedback/Connection */ fpc = instr->operators[OFFS_4OP + 10]; if (!(fpc & 0x30)) fpc |= 0x30; /* * Ensure that at least one chn is enabled */ opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc); } voices[voice].mode = voice_mode; set_voice_volume (voice, volume); freq = voices[voice].orig_freq = note_to_freq (note) / 1000; /* * Since the pitch bender may have been set before playing the note, we * have to calculate the bending now. */ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range); voices[voice].current_freq = freq; freq_to_fnum (freq, &block, &fnum); /* * Play note */ data = fnum & 0xff; /* * Least significant bits of fnumber */ opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data); data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); voices[voice].keyon_byte = data; opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data); if (voice_mode == 4) opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data); return 0; } static void freq_to_fnum (int freq, int *block, int *fnum) { int f, octave; /* * Converts the note frequency to block and fnum values for the FM chip */ /* * First try to compute the block -value (octave) where the note belongs */ f = freq; octave = 5; if (f == 0) octave = 0; else if (f < 261) { while (f < 261) { octave--; f <<= 1; } } else if (f > 493) { while (f > 493) { octave++; f >>= 1; } } if (octave > 7) octave = 7; *fnum = freq * (1 << (20 - octave)) / 49716; *block = octave; } static void opl3_command (int io_addr, unsigned int addr, unsigned int val) { int i; /* * The original 2-OP synth requires a quite long delay after writing to a * register. The OPL-3 survives with just two INBs */ OUTB ((unsigned char) (addr & 0xff), io_addr); /* * Select register * */ if (!opl3_enabled) tenmicrosec (); else for (i = 0; i < 2; i++) INB (io_addr); #ifdef PC98 OUTB ((unsigned char) (val & 0xff), io_addr + 0x100); #else OUTB ((unsigned char) (val & 0xff), io_addr + 1); /* * Write to register * */ #endif if (!opl3_enabled) { tenmicrosec (); tenmicrosec (); tenmicrosec (); } else for (i = 0; i < 2; i++) INB (io_addr); } static void opl3_reset (int dev) { int i; for (i = 0; i < nr_voices; i++) { opl3_command (physical_voices[logical_voices[i]].ioaddr, KSL_LEVEL + physical_voices[logical_voices[i]].op[0], 0xff); opl3_command (physical_voices[logical_voices[i]].ioaddr, KSL_LEVEL + physical_voices[logical_voices[i]].op[1], 0xff); if (physical_voices[logical_voices[i]].voice_mode == 4) { opl3_command (physical_voices[logical_voices[i]].ioaddr, KSL_LEVEL + physical_voices[logical_voices[i]].op[2], 0xff); opl3_command (physical_voices[logical_voices[i]].ioaddr, KSL_LEVEL + physical_voices[logical_voices[i]].op[3], 0xff); } opl3_kill_note (dev, i, 0, 64); } if (opl3_enabled) { voice_alloc->max_voice = nr_voices = 18; for (i = 0; i < 18; i++) logical_voices[i] = i; for (i = 0; i < 18; i++) physical_voices[i].voice_mode = 2; } } static int opl3_open (int dev, int mode) { int i; if (!opl3_ok) return RET_ERROR (ENXIO); if (opl3_busy) return RET_ERROR (EBUSY); opl3_busy = 1; voice_alloc->max_voice = nr_voices = opl3_enabled ? 18 : 9; voice_alloc->timestamp = 0; for (i = 0; i < 18; i++) { voice_alloc->map[i] = 0; voice_alloc->alloc_times[i] = 0; } connection_mask = 0x00; /* * Just 2 OP voices */ if (opl3_enabled) opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask); return 0; } static void opl3_close (int dev) { opl3_busy = 0; voice_alloc->max_voice = nr_voices = opl3_enabled ? 18 : 9; fm_info.nr_drums = 0; fm_info.perc_mode = 0; opl3_reset (dev); } static void opl3_hw_control (int dev, unsigned char *event) { } static int opl3_load_patch (int dev, int format, snd_rw_buf * addr, int offs, int count, int pmgr_flag) { struct sbi_instrument ins; if (count < sizeof (ins)) { printk ("FM Error: Patch record too short\n"); return RET_ERROR (EINVAL); } COPY_FROM_USER (&((char *) &ins)[offs], (char *) addr, offs, sizeof (ins) - offs); if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) { printk ("FM Error: Invalid instrument number %d\n", ins.channel); return RET_ERROR (EINVAL); } ins.key = format; return store_instr (ins.channel, &ins); } static void opl3_panning (int dev, int voice, int pressure) { } static void opl3_volume_method (int dev, int mode) { } #define SET_VIBRATO(cell) { \ tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \ if (pressure > 110) \ tmp |= 0x40; /* Vibrato on */ \ opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);} static void opl3_aftertouch (int dev, int voice, int pressure) { int tmp; struct sbi_instrument *instr; struct physical_voice_info *map; if (voice < 0 || voice >= nr_voices) return; map = &physical_voices[logical_voices[voice]]; DEB (printk ("Aftertouch %d\n", voice)); if (map->voice_mode == 0) return; /* * Adjust the amount of vibrato depending the pressure */ instr = active_instrument[voice]; if (!instr) instr = &instrmap[0]; if (voices[voice].mode == 4) { int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); switch (connection) { case 0: SET_VIBRATO (4); break; case 1: SET_VIBRATO (2); SET_VIBRATO (4); break; case 2: SET_VIBRATO (1); SET_VIBRATO (4); break; case 3: SET_VIBRATO (1); SET_VIBRATO (3); SET_VIBRATO (4); break; } /* * Not implemented yet */ } else { SET_VIBRATO (1); if ((instr->operators[10] & 0x01)) /* * Additive synthesis */ SET_VIBRATO (2); } } #undef SET_VIBRATO static void bend_pitch (int dev, int voice, int value) { unsigned char data; int block, fnum, freq; struct physical_voice_info *map; map = &physical_voices[logical_voices[voice]]; if (map->voice_mode == 0) return; voices[voice].bender = value; if (!value) return; if (!(voices[voice].keyon_byte & 0x20)) return; /* * Not keyed on */ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range); voices[voice].current_freq = freq; freq_to_fnum (freq, &block, &fnum); data = fnum & 0xff; /* * Least significant bits of fnumber */ opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data); data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); /* * * * KEYON|OCTAVE|MS * * * bits * * * of * f-num * */ voices[voice].keyon_byte = data; opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data); } static void opl3_controller (int dev, int voice, int ctrl_num, int value) { if (voice < 0 || voice >= nr_voices) return; switch (ctrl_num) { case CTRL_PITCH_BENDER: bend_pitch (dev, voice, value); break; case CTRL_PITCH_BENDER_RANGE: voices[voice].bender_range = value; break; } } static int opl3_patchmgr (int dev, struct patmgr_info *rec) { return RET_ERROR (EINVAL); } static void opl3_bender (int dev, int voice, int value) { if (voice < 0 || voice >= nr_voices) return; bend_pitch (dev, voice, value); } static int opl3_alloc_voice (int dev, int chn, int note, struct voice_alloc_info *alloc) { int i, p, best, first, avail_voices, best_time = 0x7fffffff; struct sbi_instrument *instr; int is4op; int instr_no; if (chn < 0 || chn > 15) instr_no = 0; else instr_no = chn_info[chn].pgm_num; instr = &instrmap[instr_no]; if (instr->channel < 0 || /* Instrument not loaded */ nr_voices != 12) /* Not in 4 OP mode */ is4op = 0; else if (nr_voices == 12) /* 4 OP mode */ is4op = (instr->key == OPL3_PATCH); else is4op = 0; if (is4op) { first = p = 0; avail_voices = 6; } else { if (nr_voices == 12) /* 4 OP mode. Use the '2 OP only' voices first */ first = p = 6; else first = p = 0; avail_voices = nr_voices; } /* * Now try to find a free voice */ best = first; for (i = 0; i < avail_voices; i++) { if (alloc->map[p] == 0) { return p; } if (alloc->alloc_times[p] < best_time) /* Find oldest playing note */ { best_time = alloc->alloc_times[p]; best = p; } p = (p + 1) % avail_voices; } /* * Insert some kind of priority mechanism here. */ if (best < 0) best = 0; if (best > nr_voices) best -= nr_voices; return best; /* All voices in use. Select the first one. */ } static void opl3_setup_voice (int dev, int voice, int chn) { struct channel_info *info = &synth_devs[dev]->chn_info[chn]; opl3_set_instr (dev, voice, info->pgm_num); voices[voice].bender = info->bender_value; } static struct synth_operations opl3_operations = { &fm_info, 0, SYNTH_TYPE_FM, FM_TYPE_ADLIB, opl3_open, opl3_close, opl3_ioctl, opl3_kill_note, opl3_start_note, opl3_set_instr, opl3_reset, opl3_hw_control, opl3_load_patch, opl3_aftertouch, opl3_controller, opl3_panning, opl3_volume_method, opl3_patchmgr, opl3_bender, opl3_alloc_voice, opl3_setup_voice }; long opl3_init (long mem_start) { int i; PERMANENT_MALLOC (struct sbi_instrument *, instrmap, SBFM_MAXINSTR * sizeof (*instrmap), mem_start); if (num_synths >= MAX_SYNTH_DEV) printk ("OPL3 Error: Too many synthesizers\n"); else { synth_devs[num_synths++] = &opl3_operations; voice_alloc = &opl3_operations.alloc; chn_info = &opl3_operations.chn_info[0]; } fm_model = 0; opl3_ok = 1; if (opl3_enabled) { if (opl4_enabled) #if defined(__FreeBSD__) printk ("opl0: "); else printk ("opl0: "); #else printk (" "); else printk (" "); #endif fm_model = 2; voice_alloc->max_voice = nr_voices = 18; fm_info.nr_drums = 0; fm_info.capabilities |= SYNTH_CAP_OPL3; strcpy (fm_info.name, "Yamaha OPL-3"); for (i = 0; i < 18; i++) if (physical_voices[i].ioaddr == USE_LEFT) physical_voices[i].ioaddr = left_address; else physical_voices[i].ioaddr = right_address; opl3_command (right_address, OPL3_MODE_REGISTER, OPL3_ENABLE); /* * Enable * OPL-3 * mode */ opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x00); /* * Select * all * 2-OP * * * voices */ } else { #if defined(__FreeBSD__) printk ("opl0: "); #else printk (" "); #endif fm_model = 1; voice_alloc->max_voice = nr_voices = 9; fm_info.nr_drums = 0; for (i = 0; i < 18; i++) physical_voices[i].ioaddr = left_address; }; already_initialized = 1; for (i = 0; i < SBFM_MAXINSTR; i++) instrmap[i].channel = -1; return mem_start; } #endif Index: head/sys/pc98/pc98/sound/pas2_card.c =================================================================== --- head/sys/pc98/pc98/sound/pas2_card.c (revision 18264) +++ head/sys/pc98/pc98/sound/pas2_card.c (revision 18265) @@ -1,420 +1,420 @@ #define _PAS2_CARD_C_ /* * sound/pas2_card.c * * Detection routine for the Pro Audio Spectrum cards. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS) #define DEFINE_TRANSLATIONS -#include "pas.h" +#include static int config_pas_hw __P((struct address_info *hw_config)); static int detect_pas_hw __P((struct address_info *hw_config)); static void pas2_msg __P((char *foo)); /* * The Address Translation code is used to convert I/O register addresses to * be relative to the given base -register */ int translat_code; static int pas_intr_mask = 0; static int pas_irq = 0; char pas_model; static char *pas_model_names[] = {"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16", "Pro AudioSpectrum 16D"}; extern void mix_write (unsigned char data, int ioaddr); /* * pas_read() and pas_write() are equivalents of INB() and OUTB() */ /* * These routines perform the I/O address translation required */ /* * to support other than the default base address */ unsigned char pas_read (int ioaddr) { return INB (ioaddr ^ translat_code); } void pas_write (unsigned char data, int ioaddr) { OUTB (data, ioaddr ^ translat_code); } static void pas2_msg (char *foo) { printk (" PAS2: %s.\n", foo); } /******************* Begin of the Interrupt Handler ********************/ void pasintr (INT_HANDLER_PARMS (irq, dummy)) { int status; status = pas_read (INTERRUPT_STATUS); pas_write (status, INTERRUPT_STATUS); /* * Clear interrupt */ if (status & I_S_PCM_SAMPLE_BUFFER_IRQ) { #ifndef EXCLUDE_AUDIO pas_pcm_interrupt (status, 1); #endif status &= ~I_S_PCM_SAMPLE_BUFFER_IRQ; } if (status & I_S_MIDI_IRQ) { #ifndef EXCLUDE_MIDI #ifdef EXCLUDE_PRO_MIDI pas_midi_interrupt (); #endif #endif status &= ~I_S_MIDI_IRQ; } } int pas_set_intr (int mask) { int err; if (!mask) return 0; if (!pas_intr_mask) { if ((err = snd_set_irq_handler (pas_irq, pasintr, "PAS16")) < 0) return err; } pas_intr_mask |= mask; pas_write (pas_intr_mask, INTERRUPT_MASK); return 0; } int pas_remove_intr (int mask) { if (!mask) return 0; pas_intr_mask &= ~mask; pas_write (pas_intr_mask, INTERRUPT_MASK); if (!pas_intr_mask) { snd_release_irq (pas_irq); } return 0; } /******************* End of the Interrupt handler **********************/ /******************* Begin of the Initialization Code ******************/ static int config_pas_hw (struct address_info *hw_config) { char ok = 1; unsigned int_ptrs; /* scsi/sound interrupt pointers */ pas_irq = hw_config->irq; pas_write (0x00, INTERRUPT_MASK); pas_write (0x36, SAMPLE_COUNTER_CONTROL); /* * Local timer control * * register */ pas_write (0x36, SAMPLE_RATE_TIMER); /* * Sample rate timer (16 bit) */ pas_write (0, SAMPLE_RATE_TIMER); pas_write (0x74, SAMPLE_COUNTER_CONTROL); /* * Local timer control * * register */ pas_write (0x74, SAMPLE_BUFFER_COUNTER); /* * Sample count register (16 * * bit) */ pas_write (0, SAMPLE_BUFFER_COUNTER); pas_write (F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER | F_F_MIXER_UNMUTE | 1, FILTER_FREQUENCY); pas_write (P_C_PCM_DMA_ENABLE | P_C_PCM_MONO | P_C_PCM_DAC_MODE | P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R, PCM_CONTROL); pas_write (S_M_PCM_RESET | S_M_FM_RESET | S_M_SB_RESET | S_M_MIXER_RESET /* * | * S_M_OPL3_DUAL_MONO */ , SERIAL_MIXER); pas_write (I_C_1_BOOT_RESET_ENABLE #ifdef PAS_JOYSTICK_ENABLE | I_C_1_JOYSTICK_ENABLE #endif ,IO_CONFIGURATION_1); if (pas_irq < 0 || pas_irq > 15) { printk ("PAS2: Invalid IRQ %d", pas_irq); ok = 0; } else { int_ptrs = pas_read (IO_CONFIGURATION_3); int_ptrs |= I_C_3_PCM_IRQ_translate[pas_irq] & 0xf; pas_write (int_ptrs, IO_CONFIGURATION_3); if (!I_C_3_PCM_IRQ_translate[pas_irq]) { printk ("PAS2: Invalid IRQ %d", pas_irq); ok = 0; } } if (hw_config->dma < 0 || hw_config->dma > 7) { printk ("PAS2: Invalid DMA selection %d", hw_config->dma); ok = 0; } else { pas_write (I_C_2_PCM_DMA_translate[hw_config->dma], IO_CONFIGURATION_2); if (!I_C_2_PCM_DMA_translate[hw_config->dma]) { printk ("PAS2: Invalid DMA selection %d", hw_config->dma); ok = 0; } } /* * This fixes the timing problems of the PAS due to the Symphony chipset * as per Media Vision. Only define this if your PAS doesn't work correctly. */ #ifdef SYMPHONY_PAS OUTB (0x05, 0xa8); OUTB (0x60, 0xa9); #endif #ifdef BROKEN_BUS_CLOCK pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND | S_C_1_FM_EMULATE_CLOCK, SYSTEM_CONFIGURATION_1); #else /* * pas_write(S_C_1_PCS_ENABLE, SYSTEM_CONFIGURATION_1); */ pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND, SYSTEM_CONFIGURATION_1); #endif pas_write (0x18, SYSTEM_CONFIGURATION_3); /* * ??? */ pas_write (F_F_MIXER_UNMUTE | 0x01, FILTER_FREQUENCY); /* * Sets mute * off and * * selects * filter * rate * of * 17.897 kHz */ if (pas_model == PAS_16 || pas_model == PAS_16D) pas_write (8, PRESCALE_DIVIDER); else pas_write (0, PRESCALE_DIVIDER); mix_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER); mix_write (5, PARALLEL_MIXER); #if !defined(EXCLUDE_SB_EMULATION) || !defined(EXCLUDE_SB) { struct address_info *sb_config; if ((sb_config = sound_getconf (SNDCARD_SB))) { unsigned char irq_dma; /* * Turn on Sound Blaster compatibility */ /* * bit 1 = SB emulation */ /* * bit 0 = MPU401 emulation (CDPC only :-( ) */ pas_write (0x02, COMPATIBILITY_ENABLE); /* * "Emulation address" */ pas_write ((sb_config->io_base >> 4) & 0x0f, EMULATION_ADDRESS); if (!E_C_SB_DMA_translate[sb_config->dma]) printk ("\n\nPAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma); if (!E_C_SB_IRQ_translate[sb_config->irq]) printk ("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq); irq_dma = E_C_SB_DMA_translate[sb_config->dma] | E_C_SB_IRQ_translate[sb_config->irq]; pas_write (irq_dma, EMULATION_CONFIGURATION); } } #endif if (!ok) pas2_msg ("Driver not enabled"); return ok; } static int detect_pas_hw (struct address_info *hw_config) { unsigned char board_id, foo; /* * WARNING: Setting an option like W:1 or so that disables warm boot reset * of the card will screw up this detect code something fierce. Adding code * to handle this means possibly interfering with other cards on the bus if * you have something on base port 0x388. SO be forewarned. */ OUTB (0xBC, MASTER_DECODE); /* * Talk to first board */ OUTB (hw_config->io_base >> 2, MASTER_DECODE); /* * Set base address */ translat_code = PAS_DEFAULT_BASE ^ hw_config->io_base; pas_write (1, WAIT_STATE); /* * One wait-state */ board_id = pas_read (INTERRUPT_MASK); if (board_id == 0xff) return 0; /* * We probably have a PAS-series board, now check for a PAS2-series board * by trying to change the board revision bits. PAS2-series hardware won't * let you do this - the bits are read-only. */ foo = board_id ^ 0xe0; pas_write (foo, INTERRUPT_MASK); foo = INB (INTERRUPT_MASK); pas_write (board_id, INTERRUPT_MASK); if (board_id != foo) /* * Not a PAS2 */ return 0; pas_model = pas_read (CHIP_REV); return pas_model; } long attach_pas_card (long mem_start, struct address_info *hw_config) { pas_irq = hw_config->irq; if (detect_pas_hw (hw_config)) { if (pas_model = pas_read (CHIP_REV)) { #ifdef __FreeBSD__ printk ("pas0: <%s rev %d>", pas_model_names[(int) pas_model], pas_read (BOARD_REV_ID)); #else printk (" <%s rev %d>", pas_model_names[(int) pas_model], pas_read (BOARD_REV_ID)); #endif } if (config_pas_hw (hw_config)) { #ifndef EXCLUDE_AUDIO mem_start = pas_pcm_init (mem_start, hw_config); #endif #if !defined(EXCLUDE_SB_EMULATION) && !defined(EXCLUDE_SB) sb_dsp_disable_midi (); /* * The SB emulation don't support * * midi */ #endif #ifndef EXCLUDE_YM3812 enable_opl3_mode (0x388, 0x38a, 0); #endif #ifndef EXCLUDE_MIDI #ifdef EXCLUDE_PRO_MIDI mem_start = pas_midi_init (mem_start); #endif #endif pas_init_mixer (); } } return mem_start; } int probe_pas (struct address_info *hw_config) { return detect_pas_hw (hw_config); } #endif Index: head/sys/pc98/pc98/sound/pas2_midi.c =================================================================== --- head/sys/pc98/pc98/sound/pas2_midi.c (revision 18264) +++ head/sys/pc98/pc98/sound/pas2_midi.c (revision 18265) @@ -1,341 +1,341 @@ /* * sound/pas2_midi.c * * The low level driver for the PAS Midi Interface. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #ifdef CONFIGURE_SOUNDCARD -#include "pas.h" +#include #if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_MIDI) && defined(EXCLUDE_PRO_MIDI) static int midi_busy = 0, input_opened = 0; static int my_dev; static volatile int ofifo_bytes = 0; static unsigned char tmp_queue[256]; static volatile int qlen; static volatile unsigned char qhead, qtail; static void (*midi_input_intr) (int dev, unsigned char data); static int pas_midi_open (int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev) ) { int err; unsigned long flags; unsigned char ctrl; if (midi_busy) { printk ("PAS2: Midi busy\n"); return RET_ERROR (EBUSY); } /* * Reset input and output FIFO pointers */ pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO, MIDI_CONTROL); DISABLE_INTR (flags); if ((err = pas_set_intr (I_M_MIDI_IRQ_ENABLE)) < 0) return err; /* * Enable input available and output FIFO empty interrupts */ ctrl = 0; input_opened = 0; midi_input_intr = input; if (mode == OPEN_READ || mode == OPEN_READWRITE) { ctrl |= M_C_ENA_INPUT_IRQ; /* * Enable input */ input_opened = 1; } if (mode == OPEN_WRITE || mode == OPEN_READWRITE) { ctrl |= M_C_ENA_OUTPUT_IRQ | /* * Enable output */ M_C_ENA_OUTPUT_HALF_IRQ; } pas_write (ctrl, MIDI_CONTROL); /* * Acknowledge any pending interrupts */ pas_write (0xff, MIDI_STATUS); ofifo_bytes = 0; RESTORE_INTR (flags); midi_busy = 1; qlen = qhead = qtail = 0; return 0; } static void pas_midi_close (int dev) { /* * Reset FIFO pointers, disable intrs */ pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO, MIDI_CONTROL); pas_remove_intr (I_M_MIDI_IRQ_ENABLE); midi_busy = 0; } static int dump_to_midi (unsigned char midi_byte) { int fifo_space, x; fifo_space = ((x = pas_read (MIDI_FIFO_STATUS)) >> 4) & 0x0f; if (fifo_space == 15 || (fifo_space < 2 && ofifo_bytes > 13)) /* * Fifo * full */ { return 0; /* * Upper layer will call again */ } ofifo_bytes++; pas_write (midi_byte, MIDI_DATA); return 1; } static int pas_midi_out (int dev, unsigned char midi_byte) { unsigned long flags; /* * Drain the local queue first */ DISABLE_INTR (flags); while (qlen && dump_to_midi (tmp_queue[qhead])) { qlen--; qhead++; } RESTORE_INTR (flags); /* * Output the byte if the local queue is empty. */ if (!qlen) if (dump_to_midi (midi_byte)) return 1; /* * OK */ /* * Put to the local queue */ if (qlen >= 256) return 0; /* * Local queue full */ DISABLE_INTR (flags); tmp_queue[qtail] = midi_byte; qlen++; qtail++; RESTORE_INTR (flags); return 1; } static int pas_midi_start_read (int dev) { return 0; } static int pas_midi_end_read (int dev) { return 0; } static int pas_midi_ioctl (int dev, unsigned cmd, unsigned arg) { return RET_ERROR (EINVAL); } static void pas_midi_kick (int dev) { ofifo_bytes = 0; } static int pas_buffer_status (int dev) { return !qlen; } #define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi" #define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" +#include static struct midi_operations pas_midi_operations = { {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS}, &std_midi_synth, {0}, pas_midi_open, pas_midi_close, pas_midi_ioctl, pas_midi_out, pas_midi_start_read, pas_midi_end_read, pas_midi_kick, NULL, /* * command */ pas_buffer_status, NULL }; long pas_midi_init (long mem_start) { if (num_midis >= MAX_MIDI_DEV) { printk ("Sound: Too many midi devices detected\n"); return mem_start; } std_midi_synth.midi_dev = my_dev = num_midis; midi_devs[num_midis++] = &pas_midi_operations; return mem_start; } void pas_midi_interrupt (void) { unsigned char stat; int i, incount; unsigned long flags; stat = pas_read (MIDI_STATUS); if (stat & M_S_INPUT_AVAIL) /* * Input byte available */ { incount = pas_read (MIDI_FIFO_STATUS) & 0x0f; /* * Input FIFO count */ if (!incount) incount = 16; for (i = 0; i < incount; i++) if (input_opened) { midi_input_intr (my_dev, pas_read (MIDI_DATA)); } else pas_read (MIDI_DATA); /* * Flush */ } if (stat & (M_S_OUTPUT_EMPTY | M_S_OUTPUT_HALF_EMPTY)) { if (!(stat & M_S_OUTPUT_EMPTY)) { ofifo_bytes = 8; } else { ofifo_bytes = 0; } DISABLE_INTR (flags); while (qlen && dump_to_midi (tmp_queue[qhead])) { qlen--; qhead++; } RESTORE_INTR (flags); } #if 0 if (stat & M_S_FRAMING_ERROR) printk ("MIDI framing error\n"); #endif if (stat & M_S_OUTPUT_OVERRUN) { printk ("MIDI output overrun %x,%x,%d \n", pas_read (MIDI_FIFO_STATUS), stat, ofifo_bytes); ofifo_bytes = 100; } pas_write (stat, MIDI_STATUS); /* * Acknowledge interrupts */ } #endif #endif Index: head/sys/pc98/pc98/sound/pas2_mixer.c =================================================================== --- head/sys/pc98/pc98/sound/pas2_mixer.c (revision 18264) +++ head/sys/pc98/pc98/sound/pas2_mixer.c (revision 18265) @@ -1,340 +1,340 @@ #define _PAS2_MIXER_C_ /* * sound/pas2_mixer.c * * Mixer routines for the Pro Audio Spectrum cards. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS) -#include "pas.h" +#include extern void mix_write __P((unsigned char data, int ioaddr)); static int pas_mixer_ioctl __P((int dev, unsigned int cmd, unsigned int arg)); static void set_mode __P((int new_mode)); #define TRACE(what) /* (what) */ extern int translat_code; extern char pas_model; static int rec_devices = (SOUND_MASK_MIC); /* Default recording source */ static int mode_control = 0; #define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ SOUND_MASK_CD | SOUND_MASK_ALTPCM) #define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \ SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV | \ SOUND_MASK_MUTE | SOUND_MASK_ENHANCE | SOUND_MASK_LOUD) static unsigned short levels[SOUND_MIXER_NRDEVICES] = { 0x3232, /* Master Volume */ 0x3232, /* Bass */ 0x3232, /* Treble */ 0x5050, /* FM */ 0x4b4b, /* PCM */ 0x3232, /* PC Speaker */ 0x4b4b, /* Ext Line */ 0x4b4b, /* Mic */ 0x4b4b, /* CD */ 0x6464, /* Recording monitor */ 0x4b4b, /* SB PCM */ 0x6464 /* Recording level */ }; void mix_write (unsigned char data, int ioaddr) { /* * The Revision D cards have a problem with their MVA508 interface. The * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and * MSBs out of the output byte and to do a 16-bit out to the mixer port - * 1. We need to do this because it isn't timing problem but chip access * sequence problem. */ if (pas_model == PAS_16D) { OUTW (data | (data << 8), (ioaddr ^ translat_code) - 1); OUTB (0x80, 0); } else pas_write (data, ioaddr); } static int mixer_output (int right_vol, int left_vol, int div, int bits, int mixer) /* Input or output mixer */ { int left = left_vol * div / 100; int right = right_vol * div / 100; if (bits & P_M_MV508_MIXER) { /* * Select input or output mixer */ left |= mixer; right |= mixer; } if (bits == P_M_MV508_BASS || bits == P_M_MV508_TREBLE) { /* * Bass and treble are mono devices */ mix_write (P_M_MV508_ADDRESS | bits, PARALLEL_MIXER); mix_write (left, PARALLEL_MIXER); right_vol = left_vol; } else { mix_write (P_M_MV508_ADDRESS | P_M_MV508_LEFT | bits, PARALLEL_MIXER); mix_write (left, PARALLEL_MIXER); mix_write (P_M_MV508_ADDRESS | P_M_MV508_RIGHT | bits, PARALLEL_MIXER); mix_write (right, PARALLEL_MIXER); } return (left_vol | (right_vol << 8)); } static void set_mode (int new_mode) { mix_write (P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER); mix_write (new_mode, PARALLEL_MIXER); mode_control = new_mode; } static int pas_mixer_set (int whichDev, unsigned int level) { int left, right, devmask, changed, i, mixer = 0; TRACE (printk ("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level)); left = level & 0x7f; right = (level & 0x7f00) >> 8; if (whichDev < SOUND_MIXER_NRDEVICES) if ((1 << whichDev) & rec_devices) mixer = P_M_MV508_INPUTMIX; else mixer = P_M_MV508_OUTPUTMIX; switch (whichDev) { case SOUND_MIXER_VOLUME: /* Master volume (0-63) */ levels[whichDev] = mixer_output (right, left, 63, P_M_MV508_MASTER_A, 0); break; /* * Note! Bass and Treble are mono devices. Will use just the left * channel. */ case SOUND_MIXER_BASS: /* Bass (0-12) */ levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_BASS, 0); break; case SOUND_MIXER_TREBLE: /* Treble (0-12) */ levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_TREBLE, 0); break; case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_FM, mixer); break; case SOUND_MIXER_PCM: /* PAS PCM (0-31) */ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_PCM, mixer); break; case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SB, mixer); break; case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SPEAKER, mixer); break; case SOUND_MIXER_LINE: /* External line (0-31) */ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_LINE, mixer); break; case SOUND_MIXER_CD: /* CD (0-31) */ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_CDROM, mixer); break; case SOUND_MIXER_MIC: /* External microphone (0-31) */ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_MIC, mixer); break; case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Output mixer only) */ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_IMIXER, P_M_MV508_OUTPUTMIX); break; case SOUND_MIXER_RECLEV: /* Recording level (0-15) */ levels[whichDev] = mixer_output (right, left, 15, P_M_MV508_MASTER_B, 0); break; case SOUND_MIXER_MUTE: return 0; break; case SOUND_MIXER_ENHANCE: i = 0; level &= 0x7f; if (level) i = (level / 20) - 1; mode_control &= ~P_M_MV508_ENHANCE_BITS; mode_control |= P_M_MV508_ENHANCE_BITS; set_mode (mode_control); if (i) i = (i + 1) * 20; return i; break; case SOUND_MIXER_LOUD: mode_control &= ~P_M_MV508_LOUDNESS; if (level) mode_control |= P_M_MV508_LOUDNESS; set_mode (mode_control); return !!level; /* 0 or 1 */ break; case SOUND_MIXER_RECSRC: devmask = level & POSSIBLE_RECORDING_DEVICES; changed = devmask ^ rec_devices; rec_devices = devmask; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (changed & (1 << i)) { pas_mixer_set (i, levels[i]); } return rec_devices; break; default: return RET_ERROR (EINVAL); } return (levels[whichDev]); } /*****/ static void pas_mixer_reset (void) { int foo; TRACE (printk ("pas2_mixer.c: void pas_mixer_reset(void)\n")); for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) pas_mixer_set (foo, levels[foo]); set_mode (P_M_MV508_LOUDNESS | P_M_MV508_ENHANCE_40); } int pas_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) { TRACE (printk ("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); if (((cmd >> 8) & 0xff) == 'M') { if (cmd & IOC_IN) return IOCTL_OUT (arg, pas_mixer_set (cmd & 0xff, IOCTL_IN (arg))); else { /* * Read parameters */ switch (cmd & 0xff) { case SOUND_MIXER_RECSRC: return IOCTL_OUT (arg, rec_devices); break; case SOUND_MIXER_STEREODEVS: return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE)); break; case SOUND_MIXER_DEVMASK: return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES); break; case SOUND_MIXER_RECMASK: return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES); break; case SOUND_MIXER_CAPS: return IOCTL_OUT (arg, 0); /* No special capabilities */ break; case SOUND_MIXER_MUTE: return IOCTL_OUT (arg, 0); /* No mute yet */ break; case SOUND_MIXER_ENHANCE: if (!(mode_control & P_M_MV508_ENHANCE_BITS)) return IOCTL_OUT (arg, 0); return IOCTL_OUT (arg, ((mode_control & P_M_MV508_ENHANCE_BITS) + 1) * 20); break; case SOUND_MIXER_LOUD: if (mode_control & P_M_MV508_LOUDNESS) return IOCTL_OUT (arg, 1); return IOCTL_OUT (arg, 0); break; default: return IOCTL_OUT (arg, levels[cmd & 0xff]); } } } return RET_ERROR (EINVAL); } static struct mixer_operations pas_mixer_operations = { "Pro Audio Spectrum 16", pas_mixer_ioctl }; int pas_init_mixer (void) { pas_mixer_reset (); if (num_mixers < MAX_MIXER_DEV) mixer_devs[num_mixers++] = &pas_mixer_operations; return 1; } #endif Index: head/sys/pc98/pc98/sound/pas2_pcm.c =================================================================== --- head/sys/pc98/pc98/sound/pas2_pcm.c (revision 18264) +++ head/sys/pc98/pc98/sound/pas2_pcm.c (revision 18265) @@ -1,457 +1,457 @@ #define _PAS2_PCM_C_ /* * sound/pas2_pcm.c * * The low level driver for the Pro Audio Spectrum ADC/DAC. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #ifdef CONFIGURE_SOUNDCARD -#include "pas.h" +#include static int pcm_set_bits __P((int arg)); static int pcm_set_channels __P((int arg)); static int pcm_set_speed __P((int arg)); #if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_AUDIO) #define TRACE(WHAT) /* * * * (WHAT) */ #define PAS_PCM_INTRBITS (0x08) /* * Sample buffer timer interrupt enable */ #define PCM_NON 0 #define PCM_DAC 1 #define PCM_ADC 2 static unsigned long pcm_speed = 0; /* sampling rate */ static unsigned char pcm_channels = 1; /* channels (1 or 2) */ static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */ static unsigned char pcm_filter = 0; /* filter FLAG */ static unsigned char pcm_mode = PCM_NON; static unsigned long pcm_count = 0; static unsigned short pcm_bitsok = 8; /* mask of OK bits */ static int my_devnum = 0; static int pcm_set_speed (int arg) { int foo, tmp; unsigned long flags; if (arg > 44100) arg = 44100; if (arg < 5000) arg = 5000; foo = (1193180 + (arg / 2)) / arg; arg = 1193180 / foo; if (pcm_channels & 2) foo = foo >> 1; pcm_speed = arg; tmp = pas_read (FILTER_FREQUENCY); /* * Set anti-aliasing filters according to sample rate. You reall *NEED* * to enable this feature for all normal recording unless you want to * experiment with aliasing effects. * These filters apply to the selected "recording" source. * I (pfw) don't know the encoding of these 5 bits. The values shown * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/. */ #if !defined NO_AUTO_FILTER_SET tmp &= 0xe0; if (pcm_speed >= 2 * 17897) tmp |= 0x21; else if (pcm_speed >= 2 * 15909) tmp |= 0x22; else if (pcm_speed >= 2 * 11931) tmp |= 0x29; else if (pcm_speed >= 2 * 8948) tmp |= 0x31; else if (pcm_speed >= 2 * 5965) tmp |= 0x39; else if (pcm_speed >= 2 * 2982) tmp |= 0x24; pcm_filter = tmp; #endif DISABLE_INTR (flags); pas_write (tmp & ~(F_F_PCM_RATE_COUNTER | F_F_PCM_BUFFER_COUNTER), FILTER_FREQUENCY); pas_write (S_C_C_SAMPLE_RATE | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); pas_write (foo & 0xff, SAMPLE_RATE_TIMER); pas_write ((foo >> 8) & 0xff, SAMPLE_RATE_TIMER); pas_write (tmp, FILTER_FREQUENCY); RESTORE_INTR (flags); return pcm_speed; } static int pcm_set_channels (int arg) { if ((arg != 1) && (arg != 2)) return pcm_channels; if (arg != pcm_channels) { pas_write (pas_read (PCM_CONTROL) ^ P_C_PCM_MONO, PCM_CONTROL); pcm_channels = arg; pcm_set_speed (pcm_speed); /* * The speed must be reinitialized */ } return pcm_channels; } static int pcm_set_bits (int arg) { if ((arg & pcm_bitsok) != arg) return pcm_bits; if (arg != pcm_bits) { pas_write (pas_read (SYSTEM_CONFIGURATION_2) ^ S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2); pcm_bits = arg; } return pcm_bits; } static int pas_pcm_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) { TRACE (printk ("pas2_pcm.c: static int pas_pcm_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); switch (cmd) { case SOUND_PCM_WRITE_RATE: if (local) return pcm_set_speed (arg); return IOCTL_OUT (arg, pcm_set_speed (IOCTL_IN (arg))); break; case SOUND_PCM_READ_RATE: if (local) return pcm_speed; return IOCTL_OUT (arg, pcm_speed); break; case SNDCTL_DSP_STEREO: if (local) return pcm_set_channels (arg + 1) - 1; return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg) + 1) - 1); break; case SOUND_PCM_WRITE_CHANNELS: if (local) return pcm_set_channels (arg); return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg))); break; case SOUND_PCM_READ_CHANNELS: if (local) return pcm_channels; return IOCTL_OUT (arg, pcm_channels); break; case SNDCTL_DSP_SETFMT: if (local) return pcm_set_bits (arg); return IOCTL_OUT (arg, pcm_set_bits (IOCTL_IN (arg))); break; case SOUND_PCM_READ_BITS: if (local) return pcm_bits; return IOCTL_OUT (arg, pcm_bits); case SOUND_PCM_WRITE_FILTER: /* * NOT YET IMPLEMENTED */ if (IOCTL_IN (arg) > 1) return IOCTL_OUT (arg, RET_ERROR (EINVAL)); break; pcm_filter = IOCTL_IN (arg); case SOUND_PCM_READ_FILTER: return IOCTL_OUT (arg, pcm_filter); break; default: return RET_ERROR (EINVAL); } return RET_ERROR (EINVAL); } static void pas_pcm_reset (int dev) { TRACE (printk ("pas2_pcm.c: static void pas_pcm_reset(void)\n")); pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL); } static int pas_pcm_open (int dev, int mode) { int err; TRACE (printk ("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode)); if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0) return err; if (DMAbuf_open_dma (dev) < 0) { pas_remove_intr (PAS_PCM_INTRBITS); return RET_ERROR (EBUSY); } pcm_count = 0; return 0; } static void pas_pcm_close (int dev) { unsigned long flags; TRACE (printk ("pas2_pcm.c: static void pas_pcm_close(void)\n")); DISABLE_INTR (flags); pas_pcm_reset (dev); DMAbuf_close_dma (dev); pas_remove_intr (PAS_PCM_INTRBITS); pcm_mode = PCM_NON; RESTORE_INTR (flags); } static void pas_pcm_output_block (int dev, unsigned long buf, int count, int intrflag, int restart_dma) { unsigned long flags, cnt; TRACE (printk ("pas2_pcm.c: static void pas_pcm_output_block(char *buf = %P, int count = %X)\n", buf, count)); cnt = count; if (audio_devs[dev]->dmachan > 3) cnt >>= 1; if (audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == pcm_count) return; /* * Auto mode on. No need to react */ DISABLE_INTR (flags); pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL); if (restart_dma) DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); if (audio_devs[dev]->dmachan > 3) count >>= 1; if (count != pcm_count) { pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER); pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER); pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); pcm_count = count; } pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY); pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL); pcm_mode = PCM_DAC; RESTORE_INTR (flags); } static void pas_pcm_start_input (int dev, unsigned long buf, int count, int intrflag, int restart_dma) { unsigned long flags; int cnt; TRACE (printk ("pas2_pcm.c: static void pas_pcm_start_input(char *buf = %P, int count = %X)\n", buf, count)); cnt = count; if (audio_devs[dev]->dmachan > 3) cnt >>= 1; if (audio_devs[my_devnum]->flags & DMA_AUTOMODE && intrflag && cnt == pcm_count) return; /* * Auto mode on. No need to react */ DISABLE_INTR (flags); if (restart_dma) DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); if (audio_devs[dev]->dmachan > 3) count >>= 1; if (count != pcm_count) { pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER); pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER); pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); pcm_count = count; } pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY); pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL); pcm_mode = PCM_ADC; RESTORE_INTR (flags); } static int pas_pcm_prepare_for_input (int dev, int bsize, int bcount) { return 0; } static int pas_pcm_prepare_for_output (int dev, int bsize, int bcount) { return 0; } static struct audio_operations pas_pcm_operations = { "Pro Audio Spectrum", DMA_AUTOMODE, AFMT_U8 | AFMT_S16_LE, NULL, pas_pcm_open, pas_pcm_close, pas_pcm_output_block, pas_pcm_start_input, pas_pcm_ioctl, pas_pcm_prepare_for_input, pas_pcm_prepare_for_output, pas_pcm_reset, pas_pcm_reset, NULL, NULL }; long pas_pcm_init (long mem_start, struct address_info *hw_config) { TRACE (printk ("pas2_pcm.c: long pas_pcm_init(long mem_start = %X)\n", mem_start)); pcm_bitsok = 8; if (pas_read (OPERATION_MODE_1) & O_M_1_PCM_TYPE) pcm_bitsok |= 16; pcm_set_speed (DSP_DEFAULT_SPEED); if (num_audiodevs < MAX_AUDIO_DEV) { audio_devs[my_devnum = num_audiodevs++] = &pas_pcm_operations; audio_devs[my_devnum]->dmachan = hw_config->dma; audio_devs[my_devnum]->buffcount = 1; audio_devs[my_devnum]->buffsize = DSP_BUFFSIZE; } else printk ("PAS2: Too many PCM devices available\n"); return mem_start; } void pas_pcm_interrupt (unsigned char status, int cause) { if (cause == 1) /* * PCM buffer done */ { /* * Halt the PCM first. Otherwise we don't have time to start a new * block before the PCM chip proceeds to the next sample */ if (!(audio_devs[my_devnum]->flags & DMA_AUTOMODE)) { pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL); } switch (pcm_mode) { case PCM_DAC: DMAbuf_outputintr (my_devnum, 1); break; case PCM_ADC: DMAbuf_inputintr (my_devnum); break; default: printk ("PAS: Unexpected PCM interrupt\n"); } } } #endif #endif Index: head/sys/pc98/pc98/sound/patmgr.c =================================================================== --- head/sys/pc98/pc98/sound/patmgr.c (revision 18264) +++ head/sys/pc98/pc98/sound/patmgr.c (revision 18265) @@ -1,268 +1,268 @@ /* * sound/patmgr.c * * The patch maneger interface for the /dev/sequencer * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #define PATMGR_C -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SEQUENCER) DEFINE_WAIT_QUEUES (server_procs[MAX_SYNTH_DEV], server_wait_flag[MAX_SYNTH_DEV]); static struct patmgr_info *mbox[MAX_SYNTH_DEV] = {NULL}; static volatile int msg_direction[MAX_SYNTH_DEV] = {0}; static int pmgr_opened[MAX_SYNTH_DEV] = {0}; #define A_TO_S 1 #define S_TO_A 2 DEFINE_WAIT_QUEUE (appl_proc, appl_wait_flag); int pmgr_open (int dev) { if (dev < 0 || dev >= num_synths) return RET_ERROR (ENXIO); if (pmgr_opened[dev]) return RET_ERROR (EBUSY); pmgr_opened[dev] = 1; RESET_WAIT_QUEUE (server_procs[dev], server_wait_flag[dev]); return 0; } void pmgr_release (int dev) { if (mbox[dev]) /* * Killed in action. Inform the client */ { mbox[dev]->key = PM_ERROR; mbox[dev]->parm1 = RET_ERROR (EIO); if (SOMEONE_WAITING (appl_proc, appl_wait_flag)) WAKE_UP (appl_proc, appl_wait_flag); } pmgr_opened[dev] = 0; } int pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { unsigned long flags; int ok = 0; if (count != sizeof (struct patmgr_info)) { printk ("PATMGR%d: Invalid read count\n", dev); return RET_ERROR (EIO); } while (!ok && !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev])) { DISABLE_INTR (flags); while (!(mbox[dev] && msg_direction[dev] == A_TO_S) && !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev])) { DO_SLEEP (server_procs[dev], server_wait_flag[dev], 0); } if (mbox[dev] && msg_direction[dev] == A_TO_S) { COPY_TO_USER (buf, 0, (char *) mbox[dev], count); msg_direction[dev] = 0; ok = 1; } RESTORE_INTR (flags); } if (!ok) return RET_ERROR (EINTR); return count; } int pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { unsigned long flags; if (count < 4) { printk ("PATMGR%d: Write count < 4\n", dev); return RET_ERROR (EIO); } COPY_FROM_USER (mbox[dev], buf, 0, 4); if (*(unsigned char *) mbox[dev] == SEQ_FULLSIZE) { int tmp_dev; tmp_dev = ((unsigned short *) mbox[dev])[2]; if (tmp_dev != dev) return RET_ERROR (ENXIO); return synth_devs[dev]->load_patch (dev, *(unsigned short *) mbox[dev], buf, 4, count, 1); } if (count != sizeof (struct patmgr_info)) { printk ("PATMGR%d: Invalid write count\n", dev); return RET_ERROR (EIO); } /* * If everything went OK, there should be a preallocated buffer in the * mailbox and a client waiting. */ DISABLE_INTR (flags); if (mbox[dev] && !msg_direction[dev]) { COPY_FROM_USER (&((char *) mbox[dev])[4], buf, 4, count - 4); msg_direction[dev] = S_TO_A; if (SOMEONE_WAITING (appl_proc, appl_wait_flag)) { WAKE_UP (appl_proc, appl_wait_flag); } } RESTORE_INTR (flags); return count; } int pmgr_access (int dev, struct patmgr_info *rec) { unsigned long flags; int err = 0; DISABLE_INTR (flags); if (mbox[dev]) printk (" PATMGR: Server %d mbox full. Why?\n", dev); else { rec->key = PM_K_COMMAND; mbox[dev] = rec; msg_direction[dev] = A_TO_S; if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev])) { WAKE_UP (server_procs[dev], server_wait_flag[dev]); } DO_SLEEP (appl_proc, appl_wait_flag, 0); if (msg_direction[dev] != S_TO_A) { rec->key = PM_ERROR; rec->parm1 = RET_ERROR (EIO); } else if (rec->key == PM_ERROR) { err = rec->parm1; if (err > 0) err = -err; } mbox[dev] = NULL; msg_direction[dev] = 0; } RESTORE_INTR (flags); return err; } int pmgr_inform (int dev, int event, unsigned long p1, unsigned long p2, unsigned long p3, unsigned long p4) { unsigned long flags; int err = 0; if (!pmgr_opened[dev]) return 0; DISABLE_INTR (flags); if (mbox[dev]) printk (" PATMGR: Server %d mbox full. Why?\n", dev); else { if ((mbox[dev] = (struct patmgr_info *) KERNEL_MALLOC (sizeof (struct patmgr_info))) == NULL) { printk ("pmgr: Couldn't allocate memory for a message\n"); return 0; } mbox[dev]->key = PM_K_EVENT; mbox[dev]->command = event; mbox[dev]->parm1 = p1; mbox[dev]->parm2 = p2; mbox[dev]->parm3 = p3; msg_direction[dev] = A_TO_S; if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev])) { WAKE_UP (server_procs[dev], server_wait_flag[dev]); } DO_SLEEP (appl_proc, appl_wait_flag, 0); if (mbox[dev]) KERNEL_FREE (mbox[dev]); mbox[dev] = NULL; msg_direction[dev] = 0; } RESTORE_INTR (flags); return err; } #endif Index: head/sys/pc98/pc98/sound/sb16_dsp.c =================================================================== --- head/sys/pc98/pc98/sound/sb16_dsp.c (revision 18264) +++ head/sys/pc98/pc98/sound/sb16_dsp.c (revision 18265) @@ -1,589 +1,589 @@ /* * sound/sb16_dsp.c * * The low level driver for the SoundBlaster DSP chip. * * (C) 1993 J. Schubert (jsb@sth.ruhr-uni-bochum.de) * * based on SB-driver by (C) Hannu Savolainen * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #define DEB(x) #define DEB1(x) /* * #define DEB_DMARES */ -#include "sound_config.h" -#include "sb.h" -#include "sb_mixer.h" +#include +#include +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_AUDIO) && !defined(EXCLUDE_SBPRO) extern int sbc_base; extern int sbc_major; extern int sbc_minor; static int sb16_dsp_ok = 0; /* * * * * Set to 1 after successful * * * initialization */ static int dsp_16bit = 0; static int dsp_stereo = 0; static int dsp_current_speed = 8000; /* * * * * DSP_DEFAULT_SPEED; */ static int dsp_busy = 0; static int dma16, dma8; static unsigned long dsp_count = 0; static int irq_mode = IMODE_NONE; /* * * * * IMODE_INPUT, IMODE_OUTPUT * or * * IMODE_NONE */ static int my_dev = 0; static volatile int intr_active = 0; static int sb16_dsp_open (int dev, int mode); static void sb16_dsp_close (int dev); static void sb16_dsp_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart); static void sb16_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart); static int sb16_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local); static int sb16_dsp_prepare_for_input (int dev, int bsize, int bcount); static int sb16_dsp_prepare_for_output (int dev, int bsize, int bcount); static void sb16_dsp_reset (int dev); static void sb16_dsp_halt (int dev); static int dsp_set_speed (int); static int dsp_set_stereo (int); static void dsp_cleanup (void); static struct audio_operations sb16_dsp_operations = { "SoundBlaster 16", DMA_AUTOMODE, AFMT_U8 | AFMT_S16_LE, NULL, sb16_dsp_open, sb16_dsp_close, sb16_dsp_output_block, sb16_dsp_start_input, sb16_dsp_ioctl, sb16_dsp_prepare_for_input, sb16_dsp_prepare_for_output, sb16_dsp_reset, sb16_dsp_halt, NULL, NULL }; static int sb_dsp_command01 (unsigned char val) { int i = 1 << 16; while (--i & (!INB (DSP_STATUS) & 0x80)); if (!i) printk ("SB16 sb_dsp_command01 Timeout\n"); return sb_dsp_command (val); } static int dsp_set_speed (int mode) { DEB (printk ("dsp_set_speed(%d)\n", mode)); if (mode) { if (mode < 5000) mode = 5000; if (mode > 44100) mode = 44100; dsp_current_speed = mode; } return mode; } static int dsp_set_stereo (int mode) { DEB (printk ("dsp_set_stereo(%d)\n", mode)); dsp_stereo = mode; return mode; } static int dsp_set_bits (int arg) { DEB (printk ("dsp_set_bits(%d)\n", arg)); if (arg) switch (arg) { case 8: dsp_16bit = 0; break; case 16: dsp_16bit = 1; break; default: dsp_16bit = 0; } return dsp_16bit ? 16 : 8; } static int sb16_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) { switch (cmd) { case SOUND_PCM_WRITE_RATE: if (local) return dsp_set_speed (arg); return IOCTL_OUT (arg, dsp_set_speed (IOCTL_IN (arg))); case SOUND_PCM_READ_RATE: if (local) return dsp_current_speed; return IOCTL_OUT (arg, dsp_current_speed); case SNDCTL_DSP_STEREO: if (local) return dsp_set_stereo (arg); return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg))); case SOUND_PCM_WRITE_CHANNELS: if (local) return dsp_set_stereo (arg - 1) + 1; return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1); case SOUND_PCM_READ_CHANNELS: if (local) return dsp_stereo + 1; return IOCTL_OUT (arg, dsp_stereo + 1); case SNDCTL_DSP_SETFMT: if (local) return dsp_set_bits (arg); return IOCTL_OUT (arg, dsp_set_bits (IOCTL_IN (arg))); case SOUND_PCM_READ_BITS: if (local) return dsp_16bit ? 16 : 8; return IOCTL_OUT (arg, dsp_16bit ? 16 : 8); case SOUND_PCM_WRITE_FILTER: /* * NOT YET IMPLEMENTED */ if (IOCTL_IN (arg) > 1) return IOCTL_OUT (arg, RET_ERROR (EINVAL)); default: return RET_ERROR (EINVAL); } return RET_ERROR (EINVAL); } static int sb16_dsp_open (int dev, int mode) { int retval; DEB (printk ("sb16_dsp_open()\n")); if (!sb16_dsp_ok) { printk ("SB16 Error: SoundBlaster board not installed\n"); return RET_ERROR (ENXIO); } if (intr_active) return RET_ERROR (EBUSY); retval = sb_get_irq (); if (retval < 0) return retval; sb_reset_dsp (); if (ALLOC_DMA_CHN (dma8, "SB16 (8bit)")) { printk ("SB16: Unable to grab DMA%d\n", dma8); sb_free_irq (); return RET_ERROR (EBUSY); } if (dma16 != dma8) if (ALLOC_DMA_CHN (dma16, "SB16 (16bit)")) { printk ("SB16: Unable to grab DMA%d\n", dma16); sb_free_irq (); RELEASE_DMA_CHN (dma8); return RET_ERROR (EBUSY); } irq_mode = IMODE_NONE; dsp_busy = 1; return 0; } static void sb16_dsp_close (int dev) { unsigned long flags; DEB (printk ("sb16_dsp_close()\n")); sb_dsp_command01 (0xd9); sb_dsp_command01 (0xd5); DISABLE_INTR (flags); RELEASE_DMA_CHN (dma8); if (dma16 != dma8) RELEASE_DMA_CHN (dma16); sb_free_irq (); dsp_cleanup (); dsp_busy = 0; RESTORE_INTR (flags); } static void sb16_dsp_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart) { unsigned long flags, cnt; cnt = count; if (dsp_16bit) cnt >>= 1; cnt--; #ifdef DEB_DMARES printk ("output_block: %x %d %d\n", buf, count, intrflag); if (intrflag) { int pos, chan = audio_devs[dev]->dmachan; DISABLE_INTR (flags); clear_dma_ff (chan); disable_dma (chan); pos = get_dma_residue (chan); enable_dma (chan); RESTORE_INTR (flags); printk ("dmapos=%d %x\n", pos, pos); } #endif if (audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == dsp_count) { irq_mode = IMODE_OUTPUT; intr_active = 1; return; /* * Auto mode on. No need to react */ } DISABLE_INTR (flags); if (dma_restart) { sb16_dsp_halt (dev); DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); } sb_dsp_command (0x41); sb_dsp_command ((unsigned char) ((dsp_current_speed >> 8) & 0xff)); sb_dsp_command ((unsigned char) (dsp_current_speed & 0xff)); sb_dsp_command ((unsigned char) (dsp_16bit ? 0xb6 : 0xc6)); sb_dsp_command ((unsigned char) ((dsp_stereo ? 0x20 : 0) + (dsp_16bit ? 0x10 : 0))); sb_dsp_command01 ((unsigned char) (cnt & 0xff)); sb_dsp_command ((unsigned char) (cnt >> 8)); dsp_count = cnt; irq_mode = IMODE_OUTPUT; intr_active = 1; RESTORE_INTR (flags); } static void sb16_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart) { unsigned long flags, cnt; cnt = count; if (dsp_16bit) cnt >>= 1; cnt--; #ifdef DEB_DMARES printk ("start_input: %x %d %d\n", buf, count, intrflag); if (intrflag) { int pos, chan = audio_devs[dev]->dmachan; DISABLE_INTR (flags); clear_dma_ff (chan); disable_dma (chan); pos = get_dma_residue (chan); enable_dma (chan); RESTORE_INTR (flags); printk ("dmapos=%d %x\n", pos, pos); } #endif if (audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == dsp_count) { irq_mode = IMODE_INPUT; intr_active = 1; return; /* * Auto mode on. No need to react */ } DISABLE_INTR (flags); if (dma_restart) { sb_reset_dsp (); DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); } sb_dsp_command (0x42); sb_dsp_command ((unsigned char) ((dsp_current_speed >> 8) & 0xff)); sb_dsp_command ((unsigned char) (dsp_current_speed & 0xff)); sb_dsp_command ((unsigned char) (dsp_16bit ? 0xbe : 0xce)); sb_dsp_command ((unsigned char) ((dsp_stereo ? 0x20 : 0) + (dsp_16bit ? 0x10 : 0))); sb_dsp_command01 ((unsigned char) (cnt & 0xff)); sb_dsp_command ((unsigned char) (cnt >> 8)); dsp_count = cnt; irq_mode = IMODE_INPUT; intr_active = 1; RESTORE_INTR (flags); } static int sb16_dsp_prepare_for_input (int dev, int bsize, int bcount) { audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8; dsp_count = 0; dsp_cleanup (); return 0; } static int sb16_dsp_prepare_for_output (int dev, int bsize, int bcount) { audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8; dsp_count = 0; dsp_cleanup (); return 0; } static void dsp_cleanup (void) { irq_mode = IMODE_NONE; intr_active = 0; } static void sb16_dsp_reset (int dev) { unsigned long flags; DISABLE_INTR (flags); sb_reset_dsp (); dsp_cleanup (); RESTORE_INTR (flags); } static void sb16_dsp_halt (int dev) { if (dsp_16bit) { sb_dsp_command01 (0xd9); sb_dsp_command01 (0xd5); } else { sb_dsp_command01 (0xda); sb_dsp_command01 (0xd0); } DMAbuf_reset_dma (dev); } static void set_irq_hw (int level) { int ival; switch (level) { #ifdef PC98 case 5: ival = 8; break; case 3: ival = 1; break; case 10: ival = 2; break; #else case 5: ival = 2; break; case 7: ival = 4; break; case 9: ival = 1; break; case 10: ival = 8; break; #endif default: printk ("SB16_IRQ_LEVEL %d does not exist\n", level); return; } sb_setmixer (IRQ_NR, ival); } long sb16_dsp_init (long mem_start, struct address_info *hw_config) { if (sbc_major < 4) return mem_start; /* Not a SB16 */ sprintf (sb16_dsp_operations.name, "SoundBlaster 16 %d.%d", sbc_major, sbc_minor); #if defined(__FreeBSD__) printk ("sbxvo0: <%s>", sb16_dsp_operations.name); #else printk (" <%s>", sb16_dsp_operations.name); #endif if (num_audiodevs < MAX_AUDIO_DEV) { audio_devs[my_dev = num_audiodevs++] = &sb16_dsp_operations; audio_devs[my_dev]->dmachan = hw_config->dma; audio_devs[my_dev]->buffcount = 1; audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; } else printk ("SB: Too many DSP devices available\n"); sb16_dsp_ok = 1; return mem_start; } int sb16_dsp_detect (struct address_info *hw_config) { struct address_info *sb_config; if (sb16_dsp_ok) return 1; /* Can't drive two cards */ if (!(sb_config = sound_getconf (SNDCARD_SB))) { printk ("SB16 Error: Plain SB not configured\n"); return 0; } /* * sb_setmixer(OPSW,0xf); if(sb_getmixer(OPSW)!=0xf) return 0; */ if (!sb_reset_dsp ()) return 0; if (sbc_major < 4) /* Set by the plain SB driver */ return 0; /* Not a SB16 */ #ifdef PC98 hw_config->dma = sb_config->dma; #else if (hw_config->dma < 4) if (hw_config->dma != sb_config->dma) { printk ("SB16 Error: Invalid DMA channel %d/%d\n", sb_config->dma, hw_config->dma); return 0; } #endif dma16 = hw_config->dma; dma8 = sb_config->dma; set_irq_hw (sb_config->irq); #ifdef PC98 sb_setmixer (DMA_NR, hw_config->dma == 0 ? 1 : 2); #else sb_setmixer (DMA_NR, (1 << hw_config->dma) | (1 << sb_config->dma)); #endif DEB (printk ("SoundBlaster 16: IRQ %d DMA %d OK\n", sb_config->irq, hw_config->dma)); /* * dsp_showmessage(0xe3,99); */ sb16_dsp_ok = 1; return 1; } void sb16_dsp_interrupt (int unused) { int data; data = INB (DSP_DATA_AVL16); /* * Interrupt acknowledge */ if (intr_active) switch (irq_mode) { case IMODE_OUTPUT: intr_active = 0; DMAbuf_outputintr (my_dev, 1); break; case IMODE_INPUT: intr_active = 0; DMAbuf_inputintr (my_dev); break; default: printk ("SoundBlaster: Unexpected interrupt\n"); } } #endif Index: head/sys/pc98/pc98/sound/sb16_midi.c =================================================================== --- head/sys/pc98/pc98/sound/sb16_midi.c (revision 18264) +++ head/sys/pc98/pc98/sound/sb16_midi.c (revision 18265) @@ -1,311 +1,311 @@ /* * sound/sb16_midi.c * * The low level driver for the MPU-401 UART emulation of the SB16. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #ifdef CONFIGURE_SOUNDCARD #if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_MIDI) -#include "sb.h" +#include #ifdef PC98 #define DATAPORT (sb16midi_base) #define COMDPORT (sb16midi_base+0x100) #define STATPORT (sb16midi_base+0x100) #else #define DATAPORT (sb16midi_base) #define COMDPORT (sb16midi_base+1) #define STATPORT (sb16midi_base+1) #endif #define sb16midi_status() INB(STATPORT) #define input_avail() (!(sb16midi_status()&INPUT_AVAIL)) #define output_ready() (!(sb16midi_status()&OUTPUT_READY)) #define sb16midi_cmd(cmd) OUTB(cmd, COMDPORT) #define sb16midi_read() INB(DATAPORT) #define sb16midi_write(byte) OUTB(byte, DATAPORT) #define OUTPUT_READY 0x40 #define INPUT_AVAIL 0x80 #define MPU_ACK 0xFE #define MPU_RESET 0xFF #define UART_MODE_ON 0x3F extern int sbc_major; static int sb16midi_opened = 0; static int sb16midi_base = 0x330; static int sb16midi_detected = 0; static int my_dev; extern int sbc_base; static int reset_sb16midi (void); static void (*midi_input_intr) (int dev, unsigned char data); static void sb16midi_input_loop (void) { while (input_avail ()) { unsigned char c = sb16midi_read (); if (sb16midi_opened & OPEN_READ) midi_input_intr (my_dev, c); } } void sb16midiintr (int unit) { if (input_avail ()) sb16midi_input_loop (); } static int sb16midi_open (int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev) ) { if (sb16midi_opened) { return RET_ERROR (EBUSY); } sb16midi_input_loop (); midi_input_intr = input; sb16midi_opened = mode; return 0; } static void sb16midi_close (int dev) { sb16midi_opened = 0; } static int sb16midi_out (int dev, unsigned char midi_byte) { int timeout; unsigned long flags; /* * Test for input since pending input seems to block the output. */ DISABLE_INTR (flags); if (input_avail ()) sb16midi_input_loop (); RESTORE_INTR (flags); /* * Sometimes it takes about 13000 loops before the output becomes ready * (After reset). Normally it takes just about 10 loops. */ for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /* * Wait */ if (!output_ready ()) { printk ("MPU-401: Timeout\n"); return 0; } sb16midi_write (midi_byte); return 1; } static int sb16midi_start_read (int dev) { return 0; } static int sb16midi_end_read (int dev) { return 0; } static int sb16midi_ioctl (int dev, unsigned cmd, unsigned arg) { return RET_ERROR (EINVAL); } static void sb16midi_kick (int dev) { } static int sb16midi_buffer_status (int dev) { return 0; /* * No data in buffers */ } #define MIDI_SYNTH_NAME "SoundBlaster 16 Midi" #define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" +#include static struct midi_operations sb16midi_operations = { {"SoundBlaster 16 Midi", 0, 0, SNDCARD_SB16MIDI}, &std_midi_synth, {0}, sb16midi_open, sb16midi_close, sb16midi_ioctl, sb16midi_out, sb16midi_start_read, sb16midi_end_read, sb16midi_kick, NULL, sb16midi_buffer_status, NULL }; long attach_sb16midi (long mem_start, struct address_info *hw_config) { int ok, timeout; unsigned long flags; sb16midi_base = hw_config->io_base; if (!sb16midi_detected) return RET_ERROR (EIO); DISABLE_INTR (flags); for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* * Wait */ sb16midi_cmd (UART_MODE_ON); ok = 0; for (timeout = 50000; timeout > 0 && !ok; timeout--) if (input_avail ()) if (sb16midi_read () == MPU_ACK) ok = 1; RESTORE_INTR (flags); if (num_midis >= MAX_MIDI_DEV) { printk ("Sound: Too many midi devices detected\n"); return mem_start; } printk (" "); std_midi_synth.midi_dev = my_dev = num_midis; midi_devs[num_midis++] = &sb16midi_operations; return mem_start; } static int reset_sb16midi (void) { unsigned long flags; int ok, timeout, n; /* * Send the RESET command. Try again if no success at the first time. */ ok = 0; DISABLE_INTR (flags); for (n = 0; n < 2 && !ok; n++) { for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* * Wait */ sb16midi_cmd (MPU_RESET); /* * Send MPU-401 RESET Command */ /* * Wait at least 25 msec. This method is not accurate so let's make the * loop bit longer. Cannot sleep since this is called during boot. */ for (timeout = 50000; timeout > 0 && !ok; timeout--) if (input_avail ()) if (sb16midi_read () == MPU_ACK) ok = 1; } sb16midi_opened = 0; if (ok) sb16midi_input_loop (); /* * Flush input before enabling interrupts */ RESTORE_INTR (flags); return ok; } int probe_sb16midi (struct address_info *hw_config) { int ok = 0; if (sbc_major < 4) return 0; /* Not a SB16 */ sb16midi_base = hw_config->io_base; if (sb_get_irq () < 0) return 0; ok = reset_sb16midi (); sb16midi_detected = ok; return ok; } #endif #endif Index: head/sys/pc98/pc98/sound/sb_card.c =================================================================== --- head/sys/pc98/pc98/sound/sb_card.c (revision 18264) +++ head/sys/pc98/pc98/sound/sb_card.c (revision 18265) @@ -1,61 +1,61 @@ /* * sound/sb_card.c * * Detection routine for the SoundBlaster cards. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Modified: * Riccardo Facchetti 24 Mar 1995 * - Added the Audio Excel DSP 16 initialization routine. */ -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) long attach_sb_card (long mem_start, struct address_info *hw_config) { #if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_MIDI) if (!sb_dsp_detect (hw_config)) return mem_start; mem_start = sb_dsp_init (mem_start, hw_config); #endif return mem_start; } int probe_sb (struct address_info *hw_config) { #if !defined(EXCLUDE_AEDSP16) && defined(AEDSP16_SBPRO) /* * Initialize Audio Excel DSP 16 to SBPRO. */ InitAEDSP16_SBPRO (hw_config); #endif return sb_dsp_detect (hw_config); } #endif Index: head/sys/pc98/pc98/sound/sb_dsp.c =================================================================== --- head/sys/pc98/pc98/sound/sb_dsp.c (revision 18264) +++ head/sys/pc98/pc98/sound/sb_dsp.c (revision 18265) @@ -1,1252 +1,1252 @@ /* * sound/sb_dsp.c * * The low level driver for the SoundBlaster DSP chip (SB1.0 to 2.1, SB Pro). * * Copyright by Hannu Savolainen 1994 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Modified: * Hunyue Yau Jan 6 1994 * Added code to support Sound Galaxy NX Pro * * JRA Gibson April 1995 * Code added for MV ProSonic/Jazz 16 in 16 bit mode */ -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) -#include "sb.h" -#include "sb_mixer.h" +#include +#include #undef SB_TEST_IRQ int sbc_base = 0; static int sbc_irq = 0; static int open_mode = 0; /* Read, write or both */ int Jazz16_detected = 0; /* * The DSP channel can be used either for input or output. Variable * 'sb_irq_mode' will be set when the program calls read or write first time * after open. Current version doesn't support mode changes without closing * and reopening the device. Support for this feature may be implemented in a * future version of this driver. */ int sb_dsp_ok = 0; /* * * * * Set to 1 after successful * initialization * */ static int midi_disabled = 0; int sb_dsp_highspeed = 0; int sbc_major = 1, sbc_minor = 0; /* * * * * DSP version */ static int dsp_stereo = 0; static int dsp_current_speed = DSP_DEFAULT_SPEED; static int sb16 = 0; static int irq_verified = 0; int sb_midi_mode = NORMAL_MIDI; int sb_midi_busy = 0; /* * * * * 1 if the process has output * to * * MIDI */ int sb_dsp_busy = 0; volatile int sb_irq_mode = IMODE_NONE; /* * * * * IMODE_INPUT, * * IMODE_OUTPUT * * or * * IMODE_NONE */ static volatile int irq_ok = 0; #ifdef JAZZ16 /* 16 bit support */ static int dsp_16bit = 0; static int dma8 = 1; static int dma16 = 5; static int dsp_set_bits (int arg); static int initialize_ProSonic16 (void); /* end of 16 bit support */ #endif int sb_duplex_midi = 0; static int my_dev = 0; volatile int sb_intr_active = 0; static int dsp_speed (int); static int dsp_set_stereo (int mode); #if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO) /* * Common code for the midi and pcm functions */ int sb_dsp_command (unsigned char val) { int i; unsigned long limit; limit = GET_TIME () + HZ / 10; /* * The timeout is 0.1 secods */ /* * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes * called while interrupts are disabled. This means that the timer is * disabled also. However the timeout situation is a abnormal condition. * Normally the DSP should be ready to accept commands after just couple of * loops. */ for (i = 0; i < 500000 && GET_TIME () < limit; i++) { if ((INB (DSP_STATUS) & 0x80) == 0) { OUTB (val, DSP_COMMAND); return 1; } } printk ("SoundBlaster: DSP Command(%x) Timeout.\n", val); printk ("IRQ conflict???\n"); return 0; } void sbintr (INT_HANDLER_PARMS (irq, dummy)) { int status; #ifndef EXCLUDE_SBPRO if (sb16) { unsigned char src = sb_getmixer (IRQ_STAT); /* Interrupt source register */ #ifndef EXCLUDE_SB16 if (src & 3) sb16_dsp_interrupt (irq); #ifndef EXCLUDE_MIDI if (src & 4) sb16midiintr (irq); /* * SB MPU401 interrupt */ #endif #endif if (!(src & 1)) return; /* * Not a DSP interupt */ } #endif status = INB (DSP_DATA_AVAIL); /* * Clear interrupt */ if (sb_intr_active) switch (sb_irq_mode) { case IMODE_OUTPUT: sb_intr_active = 0; DMAbuf_outputintr (my_dev, 1); break; case IMODE_INPUT: sb_intr_active = 0; DMAbuf_inputintr (my_dev); /* * A complete buffer has been input. Let's start new one */ break; case IMODE_INIT: sb_intr_active = 0; irq_ok = 1; break; case IMODE_MIDI: #ifndef EXCLUDE_MIDI sb_midi_interrupt (irq); #endif break; default: printk ("SoundBlaster: Unexpected interrupt\n"); } } static int sb_irq_usecount = 0; int sb_get_irq (void) { int ok; if (!sb_irq_usecount) if ((ok = snd_set_irq_handler (sbc_irq, sbintr, "SoundBlaster")) < 0) return ok; sb_irq_usecount++; return 0; } void sb_free_irq (void) { if (!sb_irq_usecount) return; sb_irq_usecount--; if (!sb_irq_usecount) snd_release_irq (sbc_irq); } int sb_reset_dsp (void) { int loopc; OUTB (1, DSP_RESET); tenmicrosec (); OUTB (0, DSP_RESET); tenmicrosec (); tenmicrosec (); tenmicrosec (); for (loopc = 0; loopc < 1000 && !(INB (DSP_DATA_AVAIL) & 0x80); loopc++); /* * Wait * for * data * * * available * status */ if (INB (DSP_READ) != 0xAA) return 0; /* * Sorry */ return 1; } #endif #ifndef EXCLUDE_AUDIO static void dsp_speaker (char state) { if (state) sb_dsp_command (DSP_CMD_SPKON); else sb_dsp_command (DSP_CMD_SPKOFF); } static int dsp_speed (int speed) { unsigned char tconst; unsigned long flags; int max_speed = 44100; if (speed < 4000) speed = 4000; /* * Older SB models don't support higher speeds than 22050. */ if (sbc_major < 2 || (sbc_major == 2 && sbc_minor == 0)) max_speed = 22050; /* * SB models earlier than SB Pro have low limit for the input speed. */ if (open_mode != OPEN_WRITE) /* Recording is possible */ if (sbc_major < 3) /* Limited input speed with these cards */ if (sbc_major == 2 && sbc_minor > 0) max_speed = 15000; else max_speed = 13000; if (speed > max_speed) speed = max_speed; /* * Invalid speed */ /* Logitech SoundMan Games and Jazz16 cards can support 44.1kHz stereo */ #if !defined (SM_GAMES) /* * Max. stereo speed is 22050 */ if (dsp_stereo && speed > 22050 && Jazz16_detected == 0) speed = 22050; #endif if ((speed > 22050) && sb_midi_busy) { printk ("SB Warning: High speed DSP not possible simultaneously with MIDI output\n"); speed = 22050; } if (dsp_stereo) speed *= 2; /* * Now the speed should be valid */ if (speed > 22050) { /* * High speed mode */ int tmp; tconst = (unsigned char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8); sb_dsp_highspeed = 1; DISABLE_INTR (flags); if (sb_dsp_command (0x40)) sb_dsp_command (tconst); RESTORE_INTR (flags); tmp = 65536 - (tconst << 8); speed = (256000000 + tmp / 2) / tmp; } else { int tmp; sb_dsp_highspeed = 0; tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; DISABLE_INTR (flags); if (sb_dsp_command (0x40)) /* * Set time constant */ sb_dsp_command (tconst); RESTORE_INTR (flags); tmp = 256 - tconst; speed = (1000000 + tmp / 2) / tmp; } if (dsp_stereo) speed /= 2; dsp_current_speed = speed; return speed; } static int dsp_set_stereo (int mode) { dsp_stereo = 0; #ifdef EXCLUDE_SBPRO return 0; #else if (sbc_major < 3 || sb16) return 0; /* * Sorry no stereo */ if (mode && sb_midi_busy) { printk ("SB Warning: Stereo DSP not possible simultaneously with MIDI output\n"); return 0; } dsp_stereo = !!mode; return dsp_stereo; #endif } static void sb_dsp_output_block (int dev, unsigned long buf, int count, int intrflag, int restart_dma) { unsigned long flags; if (!sb_irq_mode) dsp_speaker (ON); sb_irq_mode = IMODE_OUTPUT; DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); if (audio_devs[dev]->dmachan > 3) count >>= 1; count--; if (sb_dsp_highspeed) { DISABLE_INTR (flags); if (sb_dsp_command (0x48)) /* * High speed size */ { sb_dsp_command ((unsigned char) (count & 0xff)); sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); sb_dsp_command (0x91); /* * High speed 8 bit DAC */ } else printk ("SB Error: Unable to start (high speed) DAC\n"); RESTORE_INTR (flags); } else { DISABLE_INTR (flags); if (sb_dsp_command (0x14)) /* * 8-bit DAC (DMA) */ { sb_dsp_command ((unsigned char) (count & 0xff)); sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); } else printk ("SB Error: Unable to start DAC\n"); RESTORE_INTR (flags); } sb_intr_active = 1; } static void sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, int restart_dma) { /* * Start a DMA input to the buffer pointed by dmaqtail */ unsigned long flags; if (!sb_irq_mode) dsp_speaker (OFF); sb_irq_mode = IMODE_INPUT; DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); if (audio_devs[dev]->dmachan > 3) count >>= 1; count--; if (sb_dsp_highspeed) { DISABLE_INTR (flags); if (sb_dsp_command (0x48)) /* * High speed size */ { sb_dsp_command ((unsigned char) (count & 0xff)); sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); sb_dsp_command (0x99); /* * High speed 8 bit ADC */ } else printk ("SB Error: Unable to start (high speed) ADC\n"); RESTORE_INTR (flags); } else { DISABLE_INTR (flags); if (sb_dsp_command (0x24)) /* * 8-bit ADC (DMA) */ { sb_dsp_command ((unsigned char) (count & 0xff)); sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); } else printk ("SB Error: Unable to start ADC\n"); RESTORE_INTR (flags); } sb_intr_active = 1; } static void dsp_cleanup (void) { sb_intr_active = 0; } static int sb_dsp_prepare_for_input (int dev, int bsize, int bcount) { dsp_cleanup (); dsp_speaker (OFF); if (sbc_major == 3) /* * SB Pro */ { #ifdef JAZZ16 /* Select correct dma channel * for 16/8 bit acccess */ audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8; if (dsp_stereo) sb_dsp_command (dsp_16bit ? 0xac : 0xa8); else sb_dsp_command (dsp_16bit ? 0xa4 : 0xa0); #else /* 8 bit only cards use this */ if (dsp_stereo) sb_dsp_command (0xa8); else sb_dsp_command (0xa0); #endif dsp_speed (dsp_current_speed); /* * Speed must be recalculated if * #channels * changes */ } return 0; } static int sb_dsp_prepare_for_output (int dev, int bsize, int bcount) { dsp_cleanup (); dsp_speaker (ON); #ifndef EXCLUDE_SBPRO if (sbc_major == 3) /* * SB Pro */ { #ifdef JAZZ16 /* 16 bit specific instructions */ audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8; if (Jazz16_detected != 2) /* SM Wave */ sb_mixer_set_stereo (dsp_stereo); if (dsp_stereo) sb_dsp_command (dsp_16bit ? 0xac : 0xa8); else sb_dsp_command (dsp_16bit ? 0xa4 : 0xa0); #else sb_mixer_set_stereo (dsp_stereo); #endif dsp_speed (dsp_current_speed); /* * Speed must be recalculated if * #channels * changes */ } #endif return 0; } static void sb_dsp_halt_xfer (int dev) { } static int verify_irq (void) { #if 0 DEFINE_WAIT_QUEUE (testq, testf); irq_ok = 0; if (sb_get_irq () == -1) { printk ("*** SB Error: Irq %d already in use\n", sbc_irq); return 0; } sb_irq_mode = IMODE_INIT; sb_dsp_command (0xf2); /* * This should cause immediate interrupt */ DO_SLEEP (testq, testf, HZ / 5); sb_free_irq (); if (!irq_ok) { printk ("SB Warning: IRQ%d test not passed!", sbc_irq); irq_ok = 1; } #else irq_ok = 1; #endif return irq_ok; } static int sb_dsp_open (int dev, int mode) { int retval; if (!sb_dsp_ok) { printk ("SB Error: SoundBlaster board not installed\n"); return RET_ERROR (ENXIO); } if (sb_intr_active || (sb_midi_busy && sb_midi_mode == UART_MIDI)) { printk ("SB: PCM not possible during MIDI input\n"); return RET_ERROR (EBUSY); } if (!irq_verified) { verify_irq (); irq_verified = 1; } else if (!irq_ok) printk ("SB Warning: Incorrect IRQ setting %d\n", sbc_irq); retval = sb_get_irq (); if (retval) return retval; /* Allocate 8 bit dma */ if (DMAbuf_open_dma (dev) < 0) { sb_free_irq (); printk ("SB: DMA Busy\n"); return RET_ERROR (EBUSY); } #ifdef JAZZ16 /* Allocate 16 bit dma */ if (Jazz16_detected != 0) if (dma16 != dma8) { if (ALLOC_DMA_CHN (dma16, "Jazz16 16 bit")) { sb_free_irq (); RELEASE_DMA_CHN (dma8); return RET_ERROR (EBUSY); } } #endif sb_irq_mode = IMODE_NONE; sb_dsp_busy = 1; open_mode = mode; return 0; } static void sb_dsp_close (int dev) { #ifdef JAZZ16 /* Release 16 bit dma channel */ if (Jazz16_detected) RELEASE_DMA_CHN (dma16); #endif DMAbuf_close_dma (dev); sb_free_irq (); dsp_cleanup (); dsp_speaker (OFF); sb_dsp_busy = 0; sb_dsp_highspeed = 0; open_mode = 0; } #ifdef JAZZ16 /* Function dsp_set_bits() only required for 16 bit cards */ static int dsp_set_bits (int arg) { if (arg) if (Jazz16_detected == 0) dsp_16bit = 0; else switch (arg) { case 8: dsp_16bit = 0; break; case 16: dsp_16bit = 1; break; default: dsp_16bit = 0; } return dsp_16bit ? 16 : 8; } #endif /* ifdef JAZZ16 */ static int sb_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) { switch (cmd) { case SOUND_PCM_WRITE_RATE: if (local) return dsp_speed (arg); return IOCTL_OUT (arg, dsp_speed (IOCTL_IN (arg))); break; case SOUND_PCM_READ_RATE: if (local) return dsp_current_speed; return IOCTL_OUT (arg, dsp_current_speed); break; case SOUND_PCM_WRITE_CHANNELS: if (local) return dsp_set_stereo (arg - 1) + 1; return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1); break; case SOUND_PCM_READ_CHANNELS: if (local) return dsp_stereo + 1; return IOCTL_OUT (arg, dsp_stereo + 1); break; case SNDCTL_DSP_STEREO: if (local) return dsp_set_stereo (arg); return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg))); break; #ifdef JAZZ16 /* Word size specific cases here. * SNDCTL_DSP_SETFMT=SOUND_PCM_WRITE_BITS */ case SNDCTL_DSP_SETFMT: if (local) return dsp_set_bits (arg); return IOCTL_OUT (arg, dsp_set_bits (IOCTL_IN (arg))); break; case SOUND_PCM_READ_BITS: if (local) return dsp_16bit ? 16 : 8; return IOCTL_OUT (arg, dsp_16bit ? 16 : 8); break; #else case SOUND_PCM_WRITE_BITS: case SOUND_PCM_READ_BITS: if (local) return 8; return IOCTL_OUT (arg, 8); /* * Only 8 bits/sample supported */ break; #endif /* ifdef JAZZ16 */ case SOUND_PCM_WRITE_FILTER: case SOUND_PCM_READ_FILTER: return RET_ERROR (EINVAL); break; default: return RET_ERROR (EINVAL); } return RET_ERROR (EINVAL); } static void sb_dsp_reset (int dev) { unsigned long flags; DISABLE_INTR (flags); sb_reset_dsp (); dsp_speed (dsp_current_speed); dsp_cleanup (); RESTORE_INTR (flags); } #endif #ifdef JAZZ16 /* * Initialization of a Media Vision ProSonic 16 Soundcard. * The function initializes a ProSonic 16 like PROS.EXE does for DOS. It sets * the base address, the DMA-channels, interrupts and enables the joystickport. * * Also used by Jazz 16 (same card, different name) * * written 1994 by Rainer Vranken * E-Mail: rvranken@polaris.informatik.uni-essen.de */ #ifndef MPU_BASE /* take default values if not specified */ #define MPU_BASE 0x330 #endif #ifndef MPU_IRQ #define MPU_IRQ 9 #endif unsigned int get_sb_byte (void) { int i; for (i = 1000; i; i--) if (INB (DSP_DATA_AVAIL) & 0x80) { return INB (DSP_READ); } return 0xffff; } #ifdef SM_WAVE /* * Logitech Soundman Wave detection and initialization by Hannu Savolainen. * * There is a microcontroller (8031) in the SM Wave card for MIDI emulation. * it's located at address MPU_BASE+4. MPU_BASE+7 is a SM Wave specific * control register for MC reset, SCSI, OPL4 and DSP (future expansion) * address decoding. Otherwise the SM Wave is just a ordinary MV Jazz16 * based soundcard. */ static void smw_putmem (int base, int addr, unsigned char val) { unsigned long flags; DISABLE_INTR (flags); OUTB (addr & 0xff, base + 1); /* Low address bits */ OUTB (addr >> 8, base + 2); /* High address bits */ OUTB (val, base); /* Data */ RESTORE_INTR (flags); } static unsigned char smw_getmem (int base, int addr) { unsigned long flags; unsigned char val; DISABLE_INTR (flags); OUTB (addr & 0xff, base + 1); /* Low address bits */ OUTB (addr >> 8, base + 2); /* High address bits */ val = INB (base); /* Data */ RESTORE_INTR (flags); return val; } static int initialize_smw (void) { #ifdef SMW_MIDI0001_INCLUDED -#include "smw-midi0001.h" +#include #else unsigned char smw_ucode[1]; int smw_ucodeLen = 0; #endif int mp_base = MPU_BASE + 4; /* Microcontroller base */ int i; unsigned char control; /* * Reset the microcontroller so that the RAM can be accessed */ control = INB (MPU_BASE + 7); OUTB (control | 3, MPU_BASE + 7); /* Set last two bits to 1 (?) */ OUTB ((control & 0xfe) | 2, MPU_BASE + 7); /* xxxxxxx0 resets the mc */ for (i = 0; i < 300; i++) /* Wait at least 1ms */ tenmicrosec (); OUTB (control & 0xfc, MPU_BASE + 7); /* xxxxxx00 enables RAM */ /* * Detect microcontroller by probing the 8k RAM area */ smw_putmem (mp_base, 0, 0x00); smw_putmem (mp_base, 1, 0xff); tenmicrosec (); if (smw_getmem (mp_base, 0) != 0x00 || smw_getmem (mp_base, 1) != 0xff) { printk ("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem (mp_base, 0), smw_getmem (mp_base, 1)); return 0; /* No RAM */ } /* * There is RAM so assume it's really a SM Wave */ #ifdef SMW_MIDI0001_INCLUDED if (smw_ucodeLen != 8192) { printk ("\nSM Wave: Invalid microcode (MIDI0001.BIN) length\n"); return 1; } #endif /* * Download microcode */ for (i = 0; i < 8192; i++) smw_putmem (mp_base, i, smw_ucode[i]); /* * Verify microcode */ for (i = 0; i < 8192; i++) if (smw_getmem (mp_base, i) != smw_ucode[i]) { printk ("SM Wave: Microcode verification failed\n"); return 0; } control = 0; #ifdef SMW_SCSI_IRQ /* * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt * is disabled by default. * * Btw the Zilog 5380 SCSI controller is located at MPU base + 0x10. */ { static unsigned char scsi_irq_bits[] = {0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0}; control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6; } #endif #ifdef SMW_OPL4_ENABLE /* * Make the OPL4 chip visible on the PC bus at 0x380. * * There is no need to enable this feature since VoxWare * doesn't support OPL4 yet. Also there is no RAM in SM Wave so * enabling OPL4 is pretty useless. */ control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */ /* control |= 0x20; Uncomment this if you want to use IRQ7 */ #endif OUTB (control | 0x03, MPU_BASE + 7); /* xxxxxx11 restarts */ return 1; } #endif static int initialize_ProSonic16 (void) { int x; static unsigned char int_translat[16] = {0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6}, dma_translat[8] = {0, 1, 0, 2, 0, 3, 0, 4}; OUTB (0xAF, 0x201); /* ProSonic/Jazz16 wakeup */ for (x = 0; x < 1000; ++x) /* wait 10 milliseconds */ tenmicrosec (); OUTB (0x50, 0x201); OUTB ((sbc_base & 0x70) | ((MPU_BASE & 0x30) >> 4), 0x201); if (sb_reset_dsp ()) { /* OK. We have at least a SB */ /* Check the version number of ProSonic (I guess) */ if (!sb_dsp_command (0xFA)) return 1; if (get_sb_byte () != 0x12) return 1; if (sb_dsp_command (0xFB) && /* set DMA-channels and Interrupts */ sb_dsp_command ((dma_translat[JAZZ_DMA16] << 4) | dma_translat[SBC_DMA]) && sb_dsp_command ((int_translat[MPU_IRQ] << 4) | int_translat[sbc_irq])) { Jazz16_detected = 1; #ifdef SM_WAVE if (initialize_smw ()) Jazz16_detected = 2; #endif sb_dsp_disable_midi (); } return 1; /* There was at least a SB */ } return 0; /* No SB or ProSonic16 detected */ } #endif /* ifdef JAZZ16 */ int sb_dsp_detect (struct address_info *hw_config) { sbc_base = hw_config->io_base; sbc_irq = hw_config->irq; if (sb_dsp_ok) return 0; /* * Already initialized */ #ifdef JAZZ16 dma8 = hw_config->dma; dma16 = JAZZ_DMA16; if (!initialize_ProSonic16 ()) return 0; #else if (!sb_reset_dsp ()) return 0; #endif #ifdef PC98 switch (sbc_irq) { case 3: sb_setmixer (IRQ_NR, 1); break; case 5: sb_setmixer (IRQ_NR, 8); break; case 10: sb_setmixer (IRQ_NR, 2); break; } switch (hw_config->dma) { case 0: sb_setmixer (DMA_NR, 1); break; case 3: sb_setmixer (DMA_NR, 2); break; } #endif return 1; /* * Detected */ } #ifndef EXCLUDE_AUDIO static struct audio_operations sb_dsp_operations = { "SoundBlaster", NOTHING_SPECIAL, AFMT_U8, /* Just 8 bits. Poor old SB */ NULL, sb_dsp_open, sb_dsp_close, sb_dsp_output_block, sb_dsp_start_input, sb_dsp_ioctl, sb_dsp_prepare_for_input, sb_dsp_prepare_for_output, sb_dsp_reset, sb_dsp_halt_xfer, NULL, /* local_qlen */ NULL /* copy_from_user */ }; #endif long sb_dsp_init (long mem_start, struct address_info *hw_config) { int i; int mixer_type = 0; sbc_major = sbc_minor = 0; sb_dsp_command (0xe1); /* * Get version */ for (i = 1000; i; i--) { if (INB (DSP_DATA_AVAIL) & 0x80) { /* * wait for Data Ready */ if (sbc_major == 0) sbc_major = INB (DSP_READ); else { sbc_minor = INB (DSP_READ); break; } } } if (sbc_major == 2 || sbc_major == 3) sb_duplex_midi = 1; if (sbc_major == 4) sb16 = 1; #ifndef EXCLUDE_SBPRO if (sbc_major >= 3) mixer_type = sb_mixer_init (sbc_major); #else if (sbc_major >= 3) printk ("\n\n\n\nNOTE! SB Pro support is required with your soundcard!\n\n\n"); #endif #ifndef EXCLUDE_YM3812 #ifdef PC98 if (sbc_major > 3 || (sbc_major == 3 && INB (0x28d2) == 0x00)) #else if (sbc_major > 3 || (sbc_major == 3 && INB (0x388) == 0x00)) /* Should be 0x06 if not OPL-3 */ #endif enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH); #endif #ifndef EXCLUDE_AUDIO if (sbc_major >= 3) { if (Jazz16_detected) { if (Jazz16_detected == 2) sprintf (sb_dsp_operations.name, "SoundMan Wave %d.%d", sbc_major, sbc_minor); else sprintf (sb_dsp_operations.name, "MV Jazz16 %d.%d", sbc_major, sbc_minor); sb_dsp_operations.format_mask |= AFMT_S16_LE; /* Hurrah, 16 bits */ } else #ifdef __SGNXPRO__ if (mixer_type == 2) { sprintf (sb_dsp_operations.name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor); } else #endif if (sbc_major == 4) { sprintf (sb_dsp_operations.name, "SoundBlaster 16 %d.%d", sbc_major, sbc_minor); } else { sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor); } } else { sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", sbc_major, sbc_minor); } #if defined(__FreeBSD__) printk ("sb0: <%s>", sb_dsp_operations.name); #else printk (" <%s>", sb_dsp_operations.name); #endif #if !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SBPRO) if (!sb16) /* * There is a better driver for SB16 */ #endif if (num_audiodevs < MAX_AUDIO_DEV) { audio_devs[my_dev = num_audiodevs++] = &sb_dsp_operations; audio_devs[my_dev]->buffcount = DSP_BUFFCOUNT; audio_devs[my_dev]->buffsize = ( (sbc_major > 2 || sbc_major == 2 && sbc_minor > 0) ? 16 : 8) * 1024; audio_devs[my_dev]->dmachan = hw_config->dma; } else printk ("SB: Too many DSP devices available\n"); #else printk (" "); #endif #ifndef EXCLUDE_MIDI if (!midi_disabled && !sb16) /* * Midi don't work in the SB emulation mode * * of PAS, SB16 has better midi interface */ sb_midi_init (sbc_major); #endif sb_dsp_ok = 1; return mem_start; } void sb_dsp_disable_midi (void) { midi_disabled = 1; } #endif Index: head/sys/pc98/pc98/sound/sb_midi.c =================================================================== --- head/sys/pc98/pc98/sound/sb_midi.c (revision 18264) +++ head/sys/pc98/pc98/sound/sb_midi.c (revision 18265) @@ -1,253 +1,253 @@ /* * sound/sb_dsp.c * * The low level driver for the SoundBlaster DS chips. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_MIDI) -#include "sb.h" +#include #undef SB_TEST_IRQ /* * The DSP channel can be used either for input or output. Variable * 'sb_irq_mode' will be set when the program calls read or write first time * after open. Current version doesn't support mode changes without closing * and reopening the device. Support for this feature may be implemented in a * future version of this driver. */ extern int sb_dsp_ok; /* Set to 1 atfer successful initialization */ extern int sbc_base; extern int sb_midi_mode; extern int sb_midi_busy; /* * * * * 1 if the process has output to MIDI * */ extern int sb_dsp_busy; extern int sb_dsp_highspeed; extern volatile int sb_irq_mode; extern int sb_duplex_midi; extern int sb_intr_active; static int input_opened = 0; static int my_dev; void (*midi_input_intr) (int dev, unsigned char data); static int sb_midi_open (int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev) ) { int ret; if (!sb_dsp_ok) { printk ("SB Error: MIDI hardware not installed\n"); return RET_ERROR (ENXIO); } if (sb_midi_busy) return RET_ERROR (EBUSY); if (mode != OPEN_WRITE && !sb_duplex_midi) { if (num_midis == 1) printk ("SoundBlaster: Midi input not currently supported\n"); return RET_ERROR (EPERM); } sb_midi_mode = NORMAL_MIDI; if (mode != OPEN_WRITE) { if (sb_dsp_busy || sb_intr_active) return RET_ERROR (EBUSY); sb_midi_mode = UART_MIDI; } if (sb_dsp_highspeed) { printk ("SB Error: Midi output not possible during stereo or high speed audio\n"); return RET_ERROR (EBUSY); } if (sb_midi_mode == UART_MIDI) { sb_irq_mode = IMODE_MIDI; sb_reset_dsp (); if (!sb_dsp_command (0x35)) return RET_ERROR (EIO); /* * Enter the UART mode */ sb_intr_active = 1; if ((ret = sb_get_irq ()) < 0) { sb_reset_dsp (); return 0; /* * IRQ not free */ } input_opened = 1; midi_input_intr = input; } sb_midi_busy = 1; return 0; } static void sb_midi_close (int dev) { if (sb_midi_mode == UART_MIDI) { sb_reset_dsp (); /* * The only way to kill the UART mode */ sb_free_irq (); } sb_intr_active = 0; sb_midi_busy = 0; input_opened = 0; } static int sb_midi_out (int dev, unsigned char midi_byte) { unsigned long flags; if (sb_midi_mode == NORMAL_MIDI) { DISABLE_INTR (flags); if (sb_dsp_command (0x38)) sb_dsp_command (midi_byte); else printk ("SB Error: Unable to send a MIDI byte\n"); RESTORE_INTR (flags); } else sb_dsp_command (midi_byte); /* * UART write */ return 1; } static int sb_midi_start_read (int dev) { if (sb_midi_mode != UART_MIDI) { printk ("SoundBlaster: MIDI input not implemented.\n"); return RET_ERROR (EPERM); } return 0; } static int sb_midi_end_read (int dev) { if (sb_midi_mode == UART_MIDI) { sb_reset_dsp (); sb_intr_active = 0; } return 0; } static int sb_midi_ioctl (int dev, unsigned cmd, unsigned arg) { return RET_ERROR (EPERM); } void sb_midi_interrupt (int dummy) { unsigned long flags; unsigned char data; DISABLE_INTR (flags); data = INB (DSP_READ); if (input_opened) midi_input_intr (my_dev, data); RESTORE_INTR (flags); } #define MIDI_SYNTH_NAME "SoundBlaster Midi" #define MIDI_SYNTH_CAPS 0 -#include "midi_synth.h" +#include static struct midi_operations sb_midi_operations = { {"SoundBlaster", 0, 0, SNDCARD_SB}, &std_midi_synth, {0}, sb_midi_open, sb_midi_close, sb_midi_ioctl, sb_midi_out, sb_midi_start_read, sb_midi_end_read, NULL, /* * Kick */ NULL, /* * command */ NULL, /* * buffer_status */ NULL }; void sb_midi_init (int model) { if (num_midis >= MAX_MIDI_DEV) { printk ("Sound: Too many midi devices detected\n"); return; } std_midi_synth.midi_dev = num_midis; my_dev = num_midis; midi_devs[num_midis++] = &sb_midi_operations; } #endif Index: head/sys/pc98/pc98/sound/sb_mixer.c =================================================================== --- head/sys/pc98/pc98/sound/sb_mixer.c (revision 18264) +++ head/sys/pc98/pc98/sound/sb_mixer.c (revision 18265) @@ -1,587 +1,587 @@ /* * sound/sb_mixer.c * * The low level mixer driver for the SoundBlaster Pro and SB16 cards. * * Copyright by Hannu Savolainen 1994 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Modified: * Hunyue Yau Jan 6 1994 * Added code to support the Sound Galaxy NX Pro mixer. * */ -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_SBPRO) #define __SB_MIXER_C__ -#include "sb.h" -#include "sb_mixer.h" +#include +#include #undef SB_TEST_IRQ extern int sbc_base; extern int sbc_major; extern int Jazz16_detected; static int mixer_initialized = 0; static int supported_rec_devices; static int supported_devices; static int recmask = 0; static int mixer_model; static int mixer_caps; static mixer_tab *iomap; void sb_setmixer (unsigned int port, unsigned int value) { unsigned long flags; DISABLE_INTR (flags); OUTB ((unsigned char) (port & 0xff), MIXER_ADDR); /* * Select register */ tenmicrosec (); OUTB ((unsigned char) (value & 0xff), MIXER_DATA); tenmicrosec (); RESTORE_INTR (flags); } int sb_getmixer (unsigned int port) { int val; unsigned long flags; DISABLE_INTR (flags); OUTB ((unsigned char) (port & 0xff), MIXER_ADDR); /* * Select register */ tenmicrosec (); val = INB (MIXER_DATA); tenmicrosec (); RESTORE_INTR (flags); return val; } void sb_mixer_set_stereo (int mode) { if (!mixer_initialized) return; sb_setmixer (OUT_FILTER, ((sb_getmixer (OUT_FILTER) & ~STEREO_DAC) | (mode ? STEREO_DAC : MONO_DAC))); } /* * Returns: * 0 No mixer detected. * 1 Only a plain Sound Blaster Pro style mixer detected. * 2 The Sound Galaxy NX Pro mixer detected. */ static int detect_mixer (void) { #ifdef __SGNXPRO__ int oldbass, oldtreble; #endif int retcode = 1; /* * Detect the mixer by changing parameters of two volume channels. If the * values read back match with the values written, the mixer is there (is * it?) */ sb_setmixer (FM_VOL, 0xff); sb_setmixer (VOC_VOL, 0x33); if (sb_getmixer (FM_VOL) != 0xff) return 0; /* * No match */ if (sb_getmixer (VOC_VOL) != 0x33) return 0; #ifdef __SGNXPRO__ /* Attempt to detect the SG NX Pro by check for valid bass/treble * registers. */ oldbass = sb_getmixer (BASS_LVL); oldtreble = sb_getmixer (TREBLE_LVL); sb_setmixer (BASS_LVL, 0xaa); sb_setmixer (TREBLE_LVL, 0x55); if ((sb_getmixer (BASS_LVL) != 0xaa) || (sb_getmixer (TREBLE_LVL) != 0x55)) { retcode = 1; /* 1 == Only SB Pro detected */ } else retcode = 2; /* 2 == SG NX Pro detected */ /* Restore register in either case since SG NX Pro has EEPROM with * 'preferred' values stored. */ sb_setmixer (BASS_LVL, oldbass); sb_setmixer (TREBLE_LVL, oldtreble); /* * If the SB version is 3.X (SB Pro), assume we have a SG NX Pro 16. * In this case it's good idea to disable the Disney Sound Source * compatibility mode. It's useless and just causes noise every time the * LPT-port is accessed. * * Also place the card into WSS mode. */ if (sbc_major == 3) { OUTB (0x01, sbc_base + 0x1c); OUTB (0x00, sbc_base + 0x1a); } #endif return retcode; } static void change_bits (unsigned char *regval, int dev, int chn, int newval) { unsigned char mask; int shift; mask = (1 << (*iomap)[dev][chn].nbits) - 1; newval = (int) ((newval * mask) + 50) / 100; /* * Scale it */ shift = (*iomap)[dev][chn].bitoffs - (*iomap)[dev][LEFT_CHN].nbits + 1; *regval &= ~(mask << shift); /* * Filter out the previous value */ *regval |= (newval & mask) << shift; /* * Set the new value */ } static int sb_mixer_get (int dev) { if (!((1 << dev) & supported_devices)) return RET_ERROR (EINVAL); return levels[dev]; } #ifdef JAZZ16 static char smw_mix_regs[] = /* Left mixer registers */ { 0x0b, /* SOUND_MIXER_VOLUME */ 0x0d, /* SOUND_MIXER_BASS */ 0x0d, /* SOUND_MIXER_TREBLE */ 0x05, /* SOUND_MIXER_SYNTH */ 0x09, /* SOUND_MIXER_PCM */ 0x00, /* SOUND_MIXER_SPEAKER */ 0x03, /* SOUND_MIXER_LINE */ 0x01, /* SOUND_MIXER_MIC */ 0x07, /* SOUND_MIXER_CD */ 0x00, /* SOUND_MIXER_IMIX */ 0x00, /* SOUND_MIXER_ALTPCM */ 0x00, /* SOUND_MIXER_RECLEV */ 0x00, /* SOUND_MIXER_IGAIN */ 0x00, /* SOUND_MIXER_OGAIN */ 0x00, /* SOUND_MIXER_LINE1 */ 0x00, /* SOUND_MIXER_LINE2 */ 0x00 /* SOUND_MIXER_LINE3 */ }; static void smw_mixer_init (void) { int i; sb_setmixer (0x00, 0x18); /* Mute unused (Telephone) line */ sb_setmixer (0x10, 0x38); /* Config register 2 */ supported_devices = 0; for (i = 0; i < sizeof (smw_mix_regs); i++) if (smw_mix_regs[i] != 0) supported_devices |= (1 << i); supported_rec_devices = supported_devices & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | SOUND_MASK_VOLUME); } static int smw_mixer_set (int dev, int value) { int left = value & 0x000000ff; int right = (value & 0x0000ff00) >> 8; int reg, val; if (left > 100) left = 100; if (right > 100) right = 100; if (dev > 31) return RET_ERROR (EINVAL); if (!(supported_devices & (1 << dev))) /* Not supported */ return RET_ERROR (EINVAL); switch (dev) { case SOUND_MIXER_VOLUME: sb_setmixer (0x0b, 96 - (96 * left / 100)); /* 96=mute, 0=max */ sb_setmixer (0x0c, 96 - (96 * right / 100)); break; case SOUND_MIXER_BASS: case SOUND_MIXER_TREBLE: levels[dev] = left | (right << 8); /* Set left bass and treble values */ val = ((levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / 100) << 4; val |= ((levels[SOUND_MIXER_BASS] & 0xff) * 16 / 100) & 0x0f; sb_setmixer (0x0d, val); /* Set right bass and treble values */ val = (((levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / 100) << 4; val |= (((levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / 100) & 0x0f; sb_setmixer (0x0e, val); break; default: reg = smw_mix_regs[dev]; if (reg == 0) return RET_ERROR (EINVAL); sb_setmixer (reg, (24 - (24 * left / 100)) | 0x20); /* 24=mute, 0=max */ sb_setmixer (reg + 1, (24 - (24 * right / 100)) | 0x40); } levels[dev] = left | (right << 8); return left | (right << 8); } #endif static int sb_mixer_set (int dev, int value) { int left = value & 0x000000ff; int right = (value & 0x0000ff00) >> 8; int regoffs; unsigned char val; #ifdef JAZZ16 if (Jazz16_detected == 2) return smw_mixer_set (dev, value); #endif if (left > 100) left = 100; if (right > 100) right = 100; if (dev > 31) return RET_ERROR (EINVAL); if (!(supported_devices & (1 << dev))) /* * Not supported */ return RET_ERROR (EINVAL); regoffs = (*iomap)[dev][LEFT_CHN].regno; if (regoffs == 0) return RET_ERROR (EINVAL); val = sb_getmixer (regoffs); change_bits (&val, dev, LEFT_CHN, left); levels[dev] = left | (left << 8); if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) /* * Change register */ { sb_setmixer (regoffs, val); /* * Save the old one */ regoffs = (*iomap)[dev][RIGHT_CHN].regno; if (regoffs == 0) return left | (left << 8); /* * Just left channel present */ val = sb_getmixer (regoffs); /* * Read the new one */ } change_bits (&val, dev, RIGHT_CHN, right); sb_setmixer (regoffs, val); levels[dev] = left | (right << 8); return left | (right << 8); } static void set_recsrc (int src) { sb_setmixer (RECORD_SRC, (sb_getmixer (RECORD_SRC) & ~7) | (src & 0x7)); } static int set_recmask (int mask) { int devmask, i; unsigned char regimageL, regimageR; devmask = mask & supported_rec_devices; switch (mixer_model) { case 3: if (devmask != SOUND_MASK_MIC && devmask != SOUND_MASK_LINE && devmask != SOUND_MASK_CD) { /* * More than one devices selected. Drop the * * previous selection */ devmask &= ~recmask; } if (devmask != SOUND_MASK_MIC && devmask != SOUND_MASK_LINE && devmask != SOUND_MASK_CD) { /* * More than one devices selected. Default to * * mic */ devmask = SOUND_MASK_MIC; } if (devmask ^ recmask) /* * Input source changed */ { switch (devmask) { case SOUND_MASK_MIC: set_recsrc (SRC_MIC); break; case SOUND_MASK_LINE: set_recsrc (SRC_LINE); break; case SOUND_MASK_CD: set_recsrc (SRC_CD); break; default: set_recsrc (SRC_MIC); } } break; case 4: if (!devmask) devmask = SOUND_MASK_MIC; regimageL = regimageR = 0; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if ((1 << i) & devmask) { regimageL |= sb16_recmasks_L[i]; regimageR |= sb16_recmasks_R[i]; } sb_setmixer (SB16_IMASK_L, regimageL); sb_setmixer (SB16_IMASK_R, regimageR); break; } recmask = devmask; return recmask; } static int sb_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) { if (((cmd >> 8) & 0xff) == 'M') { if (cmd & IOC_IN) switch (cmd & 0xff) { case SOUND_MIXER_RECSRC: return IOCTL_OUT (arg, set_recmask (IOCTL_IN (arg))); break; default: return IOCTL_OUT (arg, sb_mixer_set (cmd & 0xff, IOCTL_IN (arg))); } else switch (cmd & 0xff) /* * Return parameters */ { case SOUND_MIXER_RECSRC: return IOCTL_OUT (arg, recmask); break; case SOUND_MIXER_DEVMASK: return IOCTL_OUT (arg, supported_devices); break; case SOUND_MIXER_STEREODEVS: if (Jazz16_detected) return IOCTL_OUT (arg, supported_devices); else return IOCTL_OUT (arg, supported_devices & ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER)); break; case SOUND_MIXER_RECMASK: return IOCTL_OUT (arg, supported_rec_devices); break; case SOUND_MIXER_CAPS: return IOCTL_OUT (arg, mixer_caps); break; default: return IOCTL_OUT (arg, sb_mixer_get (cmd & 0xff)); } } else return RET_ERROR (EINVAL); } static struct mixer_operations sb_mixer_operations = { "SoundBlaster", sb_mixer_ioctl }; static void sb_mixer_reset (void) { int i; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) sb_mixer_set (i, levels[i]); set_recmask (SOUND_MASK_MIC); } /* * Returns a code depending on whether a SG NX Pro was detected. * 1 == Plain SB Pro * 2 == SG NX Pro detected. * 3 == SB16 * * Used to update message. */ int sb_mixer_init (int major_model) { int mixer_type = 0; sb_setmixer (0x00, 0); /* Reset mixer */ if (!(mixer_type = detect_mixer ())) return 0; /* No mixer. Why? */ mixer_initialized = 1; mixer_model = major_model; switch (major_model) { case 3: mixer_caps = SOUND_CAP_EXCL_INPUT; #ifdef JAZZ16 if (Jazz16_detected == 2) /* SM Wave */ { supported_devices = 0; supported_rec_devices = 0; iomap = &sbpro_mix; smw_mixer_init (); mixer_type = 1; } else #endif #ifdef __SGNXPRO__ if (mixer_type == 2) /* A SGNXPRO was detected */ { supported_devices = SGNXPRO_MIXER_DEVICES; supported_rec_devices = SGNXPRO_RECORDING_DEVICES; iomap = &sgnxpro_mix; } else #endif { supported_devices = SBPRO_MIXER_DEVICES; supported_rec_devices = SBPRO_RECORDING_DEVICES; iomap = &sbpro_mix; mixer_type = 1; } break; case 4: mixer_caps = 0; supported_devices = SB16_MIXER_DEVICES; supported_rec_devices = SB16_RECORDING_DEVICES; iomap = &sb16_mix; mixer_type = 3; break; default: printk ("SB Warning: Unsupported mixer type\n"); return 0; } if (num_mixers < MAX_MIXER_DEV) mixer_devs[num_mixers++] = &sb_mixer_operations; sb_mixer_reset (); return mixer_type; } #endif Index: head/sys/pc98/pc98/sound/sequencer.c =================================================================== --- head/sys/pc98/pc98/sound/sequencer.c (revision 18264) +++ head/sys/pc98/pc98/sound/sequencer.c (revision 18265) @@ -1,1988 +1,1988 @@ /* * sound/sequencer.c * * The sequencer personality manager. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #define SEQUENCER_C -#include "sound_config.h" -#include "midi_ctrl.h" +#include +#include extern void seq_drain_midi_queues __P((void)); #ifdef CONFIGURE_SOUNDCARD #ifndef EXCLUDE_SEQUENCER static int sequencer_ok = 0; static struct sound_timer_operations *tmr; static int tmr_no = -1; /* Currently selected timer */ static int pending_timer = -1; /* For timer change operation */ /* * Local counts for number of synth and MIDI devices. These are initialized * by the sequencer_open. */ static int max_mididev = 0; static int max_synthdev = 0; /* * The seq_mode gives the operating mode of the sequencer: * 1 = level1 (the default) * 2 = level2 (extended capabilites) */ #define SEQ_1 1 #define SEQ_2 2 static int seq_mode = SEQ_1; DEFINE_WAIT_QUEUE (seq_sleeper, seq_sleep_flag); DEFINE_WAIT_QUEUE (midi_sleeper, midi_sleep_flag); static int midi_opened[MAX_MIDI_DEV] = {0}; static int midi_written[MAX_MIDI_DEV] = {0}; static unsigned long prev_input_time = 0; static int prev_event_time; static unsigned long seq_time = 0; -#include "tuning.h" +#include #define EV_SZ 8 #define IEV_SZ 8 static unsigned char *queue = NULL; static unsigned char *iqueue = NULL; static volatile int qhead = 0, qtail = 0, qlen = 0; static volatile int iqhead = 0, iqtail = 0, iqlen = 0; static volatile int seq_playing = 0; static int sequencer_busy = 0; static int output_treshold; static int pre_event_timeout; static unsigned synth_open_mask; static int seq_queue (unsigned char *note, char nonblock); static void seq_startplay (void); static int seq_sync (void); static void seq_reset (void); static int pmgr_present[MAX_SYNTH_DEV] = {0}; #if MAX_SYNTH_DEV > 15 #error Too many synthesizer devices enabled. #endif int sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { int c = count, p = 0; int ev_len; unsigned long flags; dev = dev >> 4; ev_len = seq_mode == SEQ_1 ? 4 : 8; if (dev) /* * Patch manager device */ return pmgr_read (dev - 1, file, buf, count); DISABLE_INTR (flags); if (!iqlen) { if (ISSET_FILE_FLAG (file, O_NONBLOCK)) { RESTORE_INTR (flags); return RET_ERROR (EAGAIN); } DO_SLEEP (midi_sleeper, midi_sleep_flag, pre_event_timeout); if (!iqlen) { RESTORE_INTR (flags); return 0; } } while (iqlen && c >= ev_len) { COPY_TO_USER (buf, p, &iqueue[iqhead * IEV_SZ], ev_len); p += ev_len; c -= ev_len; iqhead = (iqhead + 1) % SEQ_MAX_QUEUE; iqlen--; } RESTORE_INTR (flags); return count - c; } static void sequencer_midi_output (int dev) { /* * Currently NOP */ } void seq_copy_to_input (unsigned char *event, int len) { unsigned long flags; /* * Verify that the len is valid for the current mode. */ if (len != 4 && len != 8) return; if ((seq_mode == SEQ_1) != (len == 4)) return; if (iqlen >= (SEQ_MAX_QUEUE - 1)) return; /* Overflow */ DISABLE_INTR (flags); memcpy (&iqueue[iqtail * IEV_SZ], event, len); iqlen++; iqtail = (iqtail + 1) % SEQ_MAX_QUEUE; if (SOMEONE_WAITING (midi_sleeper, midi_sleep_flag)) { WAKE_UP (midi_sleeper, midi_sleep_flag); } RESTORE_INTR (flags); #if defined(__FreeBSD__) if (selinfo[0].si_pid) selwakeup(&selinfo[0]); #endif } static void sequencer_midi_input (int dev, unsigned char data) { unsigned int tstamp; unsigned char event[4]; if (data == 0xfe) /* Ignore active sensing */ return; tstamp = GET_TIME () - seq_time; if (tstamp != prev_input_time) { tstamp = (tstamp << 8) | SEQ_WAIT; seq_copy_to_input ((unsigned char *) &tstamp, 4); prev_input_time = tstamp; } event[0] = SEQ_MIDIPUTC; event[1] = data; event[2] = dev; event[3] = 0; seq_copy_to_input (event, 4); } void seq_input_event (unsigned char *event, int len) { unsigned long this_time; if (seq_mode == SEQ_2) this_time = tmr->get_time (tmr_no); else this_time = GET_TIME () - seq_time; if (this_time != prev_input_time) { unsigned char tmp_event[8]; tmp_event[0] = EV_TIMING; tmp_event[1] = TMR_WAIT_ABS; tmp_event[2] = 0; tmp_event[3] = 0; *(unsigned long *) &tmp_event[4] = this_time; seq_copy_to_input (tmp_event, 8); prev_input_time = this_time; } seq_copy_to_input (event, len); } int sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { unsigned char event[EV_SZ], ev_code; int p = 0, c, ev_size; int err; int mode = file->mode & O_ACCMODE; dev = dev >> 4; DEB (printk ("sequencer_write(dev=%d, count=%d)\n", dev, count)); if (mode == OPEN_READ) return RET_ERROR (EIO); if (dev) /* * Patch manager device */ return pmgr_write (dev - 1, file, buf, count); c = count; while (c >= 4) { COPY_FROM_USER (event, buf, p, 4); ev_code = event[0]; if (ev_code == SEQ_FULLSIZE) { int err; dev = *(unsigned short *) &event[2]; if (dev < 0 || dev >= max_synthdev) return RET_ERROR (ENXIO); if (!(synth_open_mask & (1 << dev))) return RET_ERROR (ENXIO); err = synth_devs[dev]->load_patch (dev, *(short *) &event[0], buf, p + 4, c, 0); if (err < 0) return err; return err; } if (ev_code >= 128) { if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED) { printk ("Sequencer: Invalid level 2 event %x\n", ev_code); return RET_ERROR (EINVAL); } ev_size = 8; if (c < ev_size) { if (!seq_playing) seq_startplay (); return count - c; } COPY_FROM_USER (&event[4], buf, p + 4, 4); } else { if (seq_mode == SEQ_2) { printk ("Sequencer: 4 byte event in level 2 mode\n"); return RET_ERROR (EINVAL); } ev_size = 4; } if (event[0] == SEQ_MIDIPUTC) { if (!midi_opened[event[2]]) { int mode; int dev = event[2]; if (dev >= max_mididev) { printk ("Sequencer Error: Nonexistent MIDI device %d\n", dev); return RET_ERROR (ENXIO); } mode = file->mode & O_ACCMODE; if ((err = midi_devs[dev]->open (dev, mode, sequencer_midi_input, sequencer_midi_output)) < 0) { seq_reset (); printk ("Sequencer Error: Unable to open Midi #%d\n", dev); return err; } midi_opened[dev] = 1; } } if (!seq_queue (event, ISSET_FILE_FLAG (file, O_NONBLOCK))) { int processed = count - c; if (!seq_playing) seq_startplay (); if (!processed && ISSET_FILE_FLAG (file, O_NONBLOCK)) return RET_ERROR (EAGAIN); else return processed; } p += ev_size; c -= ev_size; } if (!seq_playing) seq_startplay (); return count; /* This will "eat" chunks shorter than 4 bytes (if written * alone) Should we really do that ? */ } static int seq_queue (unsigned char *note, char nonblock) { /* * Test if there is space in the queue */ if (qlen >= SEQ_MAX_QUEUE) if (!seq_playing) seq_startplay (); /* * Give chance to drain the queue */ if (!nonblock && qlen >= SEQ_MAX_QUEUE && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) { /* * Sleep until there is enough space on the queue */ DO_SLEEP (seq_sleeper, seq_sleep_flag, 0); } if (qlen >= SEQ_MAX_QUEUE) { return 0; /* * To be sure */ } memcpy (&queue[qtail * EV_SZ], note, EV_SZ); qtail = (qtail + 1) % SEQ_MAX_QUEUE; qlen++; return 1; } static int extended_event (unsigned char *q) { int dev = q[2]; if (dev < 0 || dev >= max_synthdev) return RET_ERROR (ENXIO); if (!(synth_open_mask & (1 << dev))) return RET_ERROR (ENXIO); switch (q[1]) { case SEQ_NOTEOFF: synth_devs[dev]->kill_note (dev, q[3], q[4], q[5]); break; case SEQ_NOTEON: if (q[4] > 127 && q[4] != 255) return 0; synth_devs[dev]->start_note (dev, q[3], q[4], q[5]); break; case SEQ_PGMCHANGE: synth_devs[dev]->set_instr (dev, q[3], q[4]); break; case SEQ_AFTERTOUCH: synth_devs[dev]->aftertouch (dev, q[3], q[4]); break; case SEQ_BALANCE: synth_devs[dev]->panning (dev, q[3], (char) q[4]); break; case SEQ_CONTROLLER: synth_devs[dev]->controller (dev, q[3], q[4], *(short *) &q[5]); break; case SEQ_VOLMODE: if (synth_devs[dev]->volume_method != NULL) synth_devs[dev]->volume_method (dev, q[3]); break; default: return RET_ERROR (EINVAL); } return 0; } static int find_voice (int dev, int chn, int note) { unsigned short key; int i; key = (chn << 8) | (note + 1); for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) if (synth_devs[dev]->alloc.map[i] == key) return i; return -1; } static int alloc_voice (int dev, int chn, int note) { unsigned short key; int voice; key = (chn << 8) | (note + 1); voice = synth_devs[dev]->alloc_voice (dev, chn, note, &synth_devs[dev]->alloc); synth_devs[dev]->alloc.map[voice] = key; synth_devs[dev]->alloc.alloc_times[voice] = synth_devs[dev]->alloc.timestamp++; return voice; } static void seq_chn_voice_event (unsigned char *event) { unsigned char dev = event[1]; unsigned char cmd = event[2]; unsigned char chn = event[3]; unsigned char note = event[4]; unsigned char parm = event[5]; int voice = -1; if ((int) dev > max_synthdev) return; if (!(synth_open_mask & (1 << dev))) return; if (!synth_devs[dev]) return; if (seq_mode == SEQ_2) { if (synth_devs[dev]->alloc_voice) voice = find_voice (dev, chn, note); if (cmd == MIDI_NOTEON && parm == 0) { cmd = MIDI_NOTEOFF; parm = 64; } } switch (cmd) { case MIDI_NOTEON: if (note > 127 && note != 255) /* Not a seq2 feature */ return; if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice) { /* Internal synthesizer (FM, GUS, etc) */ voice = alloc_voice (dev, chn, note); } if (voice == -1) voice = chn; if (seq_mode == SEQ_2 && dev < num_synths) { /* * The MIDI channel 10 is a percussive channel. Use the note * number to select the proper patch (128 to 255) to play. */ if (chn == 9) { synth_devs[dev]->set_instr (dev, voice, 128 + note); note = 60; /* Middle C */ } } if (seq_mode == SEQ_2) { synth_devs[dev]->setup_voice (dev, voice, chn); } synth_devs[dev]->start_note (dev, voice, note, parm); break; case MIDI_NOTEOFF: if (voice == -1) voice = chn; synth_devs[dev]->kill_note (dev, voice, note, parm); break; case MIDI_KEY_PRESSURE: if (voice == -1) voice = chn; synth_devs[dev]->aftertouch (dev, voice, parm); break; default:; } } static void seq_chn_common_event (unsigned char *event) { unsigned char dev = event[1]; unsigned char cmd = event[2]; unsigned char chn = event[3]; unsigned char p1 = event[4]; /* unsigned char p2 = event[5]; */ unsigned short w14 = *(short *) &event[6]; if ((int) dev > max_synthdev) return; if (!(synth_open_mask & (1 << dev))) return; if (!synth_devs[dev]) return; switch (cmd) { case MIDI_PGM_CHANGE: if (seq_mode == SEQ_2) { synth_devs[dev]->chn_info[chn].pgm_num = p1; if (dev >= num_synths) synth_devs[dev]->set_instr (dev, chn, p1); } else synth_devs[dev]->set_instr (dev, chn, p1); break; case MIDI_CTL_CHANGE: if (seq_mode == SEQ_2) { if (chn > 15 || p1 > 127) break; synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f; if (dev < num_synths) { int val = w14 & 0x7f; int i, key; if (p1 < 64) /* Combine MSB and LSB */ { val = ((synth_devs[dev]-> chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7) | (synth_devs[dev]-> chn_info[chn].controllers[p1 | 32] & 0x7f); p1 &= ~32; } /* Handle all playing notes on this channel */ key = (chn << 8); for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key) synth_devs[dev]->controller (dev, i, p1, val); } else synth_devs[dev]->controller (dev, chn, p1, w14); } else /* Mode 1 */ synth_devs[dev]->controller (dev, chn, p1, w14); break; case MIDI_PITCH_BEND: if (seq_mode == SEQ_2) { synth_devs[dev]->chn_info[chn].bender_value = w14; if (dev < num_synths) { /* Handle all playing notes on this channel */ int i, key; key = (chn << 8); for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key) synth_devs[dev]->bender (dev, i, w14); } else synth_devs[dev]->bender (dev, chn, w14); } else /* MODE 1 */ synth_devs[dev]->bender (dev, chn, w14); break; default:; } } static int seq_timing_event (unsigned char *event) { unsigned char cmd = event[1]; unsigned int parm = *(int *) &event[4]; if (seq_mode == SEQ_2) { int ret; if ((ret = tmr->event (tmr_no, event)) == TIMER_ARMED) { if ((SEQ_MAX_QUEUE - qlen) >= output_treshold) { unsigned long flags; DISABLE_INTR (flags); if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) { WAKE_UP (seq_sleeper, seq_sleep_flag); } RESTORE_INTR (flags); #if defined(__FreeBSD__) /* must issue a wakeup for anyone waiting (select) XXX */ #endif } } return ret; } switch (cmd) { case TMR_WAIT_REL: parm += prev_event_time; /* * NOTE! No break here. Execution of TMR_WAIT_REL continues in the * next case (TMR_WAIT_ABS) */ case TMR_WAIT_ABS: if (parm > 0) { long time; seq_playing = 1; time = parm; prev_event_time = time; request_sound_timer (time); if ((SEQ_MAX_QUEUE - qlen) >= output_treshold) { unsigned long flags; DISABLE_INTR (flags); if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) { WAKE_UP (seq_sleeper, seq_sleep_flag); } RESTORE_INTR (flags); #if defined(__FreeBSD__) /* must issue a wakeup for select XXX */ #endif } return TIMER_ARMED; } break; case TMR_START: seq_time = GET_TIME (); prev_input_time = 0; prev_event_time = 0; break; case TMR_STOP: break; case TMR_CONTINUE: break; case TMR_TEMPO: break; case TMR_ECHO: if (seq_mode == SEQ_2) seq_copy_to_input (event, 8); else { parm = (parm << 8 | SEQ_ECHO); seq_copy_to_input ((unsigned char *) &parm, 4); } break; default:; } return TIMER_NOT_ARMED; } static void seq_local_event (unsigned char *event) { /* unsigned char cmd = event[1]; */ printk ("seq_local_event() called. WHY????????\n"); } static int play_event (unsigned char *q) { /* * NOTE! This routine returns * 0 = normal event played. * 1 = Timer armed. Suspend playback until timer callback. * 2 = MIDI output buffer full. Restore queue and suspend until timer */ unsigned long *delay; switch (q[0]) { case SEQ_NOTEOFF: if (synth_open_mask & (1 << 0)) if (synth_devs[0]) synth_devs[0]->kill_note (0, q[1], 255, q[3]); break; case SEQ_NOTEON: if (q[4] < 128 || q[4] == 255) if (synth_open_mask & (1 << 0)) if (synth_devs[0]) synth_devs[0]->start_note (0, q[1], q[2], q[3]); break; case SEQ_WAIT: delay = (unsigned long *) q; /* * Bytes 1 to 3 are containing the * * delay in GET_TIME() */ *delay = (*delay >> 8) & 0xffffff; if (*delay > 0) { long time; seq_playing = 1; time = *delay; prev_event_time = time; request_sound_timer (time); if ((SEQ_MAX_QUEUE - qlen) >= output_treshold) { unsigned long flags; DISABLE_INTR (flags); if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) { WAKE_UP (seq_sleeper, seq_sleep_flag); } RESTORE_INTR (flags); #if defined(__FreeBSD__) /* must issue a wakeup for selects XXX */ #endif } /* * The timer is now active and will reinvoke this function * after the timer expires. Return to the caller now. */ return 1; } break; case SEQ_PGMCHANGE: if (synth_open_mask & (1 << 0)) if (synth_devs[0]) synth_devs[0]->set_instr (0, q[1], q[2]); break; case SEQ_SYNCTIMER: /* * Reset timer */ seq_time = GET_TIME (); prev_input_time = 0; prev_event_time = 0; break; case SEQ_MIDIPUTC: /* * Put a midi character */ if (midi_opened[q[2]]) { int dev; dev = q[2]; if (!midi_devs[dev]->putc (dev, q[1])) { /* * Output FIFO is full. Wait one timer cycle and try again. */ seq_playing = 1; request_sound_timer (-1); return 2; } else midi_written[dev] = 1; } break; case SEQ_ECHO: seq_copy_to_input (q, 4); /* * Echo back to the process */ break; case SEQ_PRIVATE: if ((int) q[1] < max_synthdev) synth_devs[q[1]]->hw_control (q[1], q); break; case SEQ_EXTENDED: extended_event (q); break; case EV_CHN_VOICE: seq_chn_voice_event (q); break; case EV_CHN_COMMON: seq_chn_common_event (q); break; case EV_TIMING: if (seq_timing_event (q) == TIMER_ARMED) { return 1; } break; case EV_SEQ_LOCAL: seq_local_event (q); break; default:; } return 0; } static void seq_startplay (void) { unsigned long flags; int this_one, action; while (qlen > 0) { DISABLE_INTR (flags); qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE; qlen--; RESTORE_INTR (flags); seq_playing = 1; if ((action = play_event (&queue[this_one * EV_SZ]))) { /* Suspend playback. Next timer routine invokes this routine again */ if (action == 2) { qlen++; qhead = this_one; } return; } } seq_playing = 0; if ((SEQ_MAX_QUEUE - qlen) >= output_treshold) { unsigned long flags; DISABLE_INTR (flags); if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) { WAKE_UP (seq_sleeper, seq_sleep_flag); } RESTORE_INTR (flags); #if defined(__FreeBSD__) /* must issue a wakeup for selects XXX */ #endif } } static void reset_controllers (int dev, unsigned char *controller, int update_dev) { int i; for (i = 0; i < 128; i++) controller[i] = ctrl_def_values[i]; } static void setup_mode2 (void) { int dev; max_synthdev = num_synths; for (dev = 0; dev < num_midis; dev++) if (midi_devs[dev]->converter != NULL) { synth_devs[max_synthdev++] = midi_devs[dev]->converter; } for (dev = 0; dev < max_synthdev; dev++) { int chn; for (chn = 0; chn < 16; chn++) { synth_devs[dev]->chn_info[chn].pgm_num = 0; reset_controllers (dev, synth_devs[dev]->chn_info[chn].controllers, 0); synth_devs[dev]->chn_info[chn].bender_value = (1 << 7); /* Neutral */ } } max_mididev = 0; seq_mode = SEQ_2; } int sequencer_open (int dev, struct fileinfo *file) { int retval, mode, i; int level, tmp; level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1; dev = dev >> 4; mode = file->mode & O_ACCMODE; DEB (printk ("sequencer_open(dev=%d)\n", dev)); if (!sequencer_ok) { printk ("Soundcard: Sequencer not initialized\n"); return RET_ERROR (ENXIO); } if (dev) /* * Patch manager device */ { int err; dev--; if (dev >= MAX_SYNTH_DEV) return RET_ERROR (ENXIO); if (pmgr_present[dev]) return RET_ERROR (EBUSY); if ((err = pmgr_open (dev)) < 0) return err; /* * Failed */ pmgr_present[dev] = 1; return err; } if (sequencer_busy) { printk ("Sequencer busy\n"); return RET_ERROR (EBUSY); } max_mididev = num_midis; max_synthdev = num_synths; pre_event_timeout = 0; seq_mode = SEQ_1; if (pending_timer != -1) { tmr_no = pending_timer; pending_timer = -1; } if (tmr_no == -1) /* Not selected yet */ { int i, best; best = -1; for (i = 0; i < num_sound_timers; i++) if (sound_timer_devs[i]->priority > best) { tmr_no = i; best = sound_timer_devs[i]->priority; } if (tmr_no == -1) /* Should not be */ tmr_no = 0; } tmr = sound_timer_devs[tmr_no]; if (level == 2) { if (tmr == NULL) { printk ("sequencer: No timer for level 2\n"); return RET_ERROR (ENXIO); } setup_mode2 (); } if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE)) if (!max_mididev) { printk ("Sequencer: No Midi devices. Input not possible\n"); return RET_ERROR (ENXIO); } if (!max_synthdev && !max_mididev) return RET_ERROR (ENXIO); synth_open_mask = 0; for (i = 0; i < max_mididev; i++) { midi_opened[i] = 0; midi_written[i] = 0; } /* * if (mode == OPEN_WRITE || mode == OPEN_READWRITE) */ for (i = 0; i < max_synthdev; i++) /* * Open synth devices */ if ((tmp = synth_devs[i]->open (i, mode)) < 0) { printk ("Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp); if (synth_devs[i]->midi_dev) printk ("(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev); } else { synth_open_mask |= (1 << i); if (synth_devs[i]->midi_dev) /* * Is a midi interface */ midi_opened[synth_devs[i]->midi_dev] = 1; } seq_time = GET_TIME (); prev_input_time = 0; prev_event_time = 0; if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE)) { /* * Initialize midi input devices */ for (i = 0; i < max_mididev; i++) if (!midi_opened[i]) { if ((retval = midi_devs[i]->open (i, mode, sequencer_midi_input, sequencer_midi_output)) >= 0) midi_opened[i] = 1; } } if (seq_mode == SEQ_2) { tmr->open (tmr_no, seq_mode); } sequencer_busy = 1; RESET_WAIT_QUEUE (seq_sleeper, seq_sleep_flag); RESET_WAIT_QUEUE (midi_sleeper, midi_sleep_flag); output_treshold = SEQ_MAX_QUEUE / 2; for (i = 0; i < num_synths; i++) if (pmgr_present[i]) pmgr_inform (i, PM_E_OPENED, 0, 0, 0, 0); return 0; } void seq_drain_midi_queues (void) { int i, n; /* * Give the Midi drivers time to drain their output queues */ n = 1; while (!PROCESS_ABORTING (seq_sleeper, seq_sleep_flag) && n) { n = 0; for (i = 0; i < max_mididev; i++) if (midi_opened[i] && midi_written[i]) if (midi_devs[i]->buffer_status != NULL) if (midi_devs[i]->buffer_status (i)) n++; /* * Let's have a delay */ if (n) { DO_SLEEP (seq_sleeper, seq_sleep_flag, HZ / 10); } } } void sequencer_release (int dev, struct fileinfo *file) { int i; int mode = file->mode & O_ACCMODE; dev = dev >> 4; DEB (printk ("sequencer_release(dev=%d)\n", dev)); if (dev) /* * Patch manager device */ { dev--; pmgr_release (dev); pmgr_present[dev] = 0; return; } /* * * Wait until the queue is empty (if we don't have nonblock) */ if (mode != OPEN_READ && !ISSET_FILE_FLAG (file, O_NONBLOCK)) while (!PROCESS_ABORTING (seq_sleeper, seq_sleep_flag) && qlen) { seq_sync (); } if (mode != OPEN_READ) seq_drain_midi_queues (); /* * Ensure the output queues are empty */ seq_reset (); if (mode != OPEN_READ) seq_drain_midi_queues (); /* * Flush the all notes off messages */ for (i = 0; i < max_synthdev; i++) if (synth_open_mask & (1 << i)) /* * Actually opened */ if (synth_devs[i]) { synth_devs[i]->close (i); if (synth_devs[i]->midi_dev) midi_opened[synth_devs[i]->midi_dev] = 0; } for (i = 0; i < num_synths; i++) if (pmgr_present[i]) pmgr_inform (i, PM_E_CLOSED, 0, 0, 0, 0); for (i = 0; i < max_mididev; i++) if (midi_opened[i]) midi_devs[i]->close (i); if (seq_mode == SEQ_2) tmr->close (tmr_no); sequencer_busy = 0; } static int seq_sync (void) { unsigned long flags; if (qlen && !seq_playing && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag)) seq_startplay (); DISABLE_INTR (flags); if (qlen && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) { DO_SLEEP (seq_sleeper, seq_sleep_flag, 0); } RESTORE_INTR (flags); return qlen; } static void midi_outc (int dev, unsigned char data) { /* * NOTE! Calls sleep(). Don't call this from interrupt. */ int n; unsigned long flags; /* * This routine sends one byte to the Midi channel. * If the output Fifo is full, it waits until there * is space in the queue */ n = 3 * HZ; /* Timeout */ DISABLE_INTR (flags); while (n && !midi_devs[dev]->putc (dev, data)) { DO_SLEEP (seq_sleeper, seq_sleep_flag, 4); n--; } RESTORE_INTR (flags); } static void seq_reset (void) { /* * NOTE! Calls sleep(). Don't call this from interrupt. */ int i; int chn; unsigned long flags; sound_stop_timer (); seq_time = GET_TIME (); prev_input_time = 0; prev_event_time = 0; qlen = qhead = qtail = 0; iqlen = iqhead = iqtail = 0; for (i = 0; i < max_synthdev; i++) if (synth_open_mask & (1 << i)) if (synth_devs[i]) synth_devs[i]->reset (i); if (seq_mode == SEQ_2) { for (chn = 0; chn < 16; chn++) for (i = 0; i < max_synthdev; i++) if (synth_open_mask & (1 << i)) if (synth_devs[i]) { synth_devs[i]->controller (i, chn, 123, 0); /* All notes off */ synth_devs[i]->controller (i, chn, 121, 0); /* Reset all ctl */ synth_devs[i]->bender (i, chn, 1 << 13); /* Bender off */ } } else /* seq_mode == SEQ_1 */ { for (i = 0; i < max_mididev; i++) if (midi_written[i]) /* * Midi used. Some notes may still be playing */ { /* * Sending just a ACTIVE SENSING message should be enough to stop all * playing notes. Since there are devices not recognizing the * active sensing, we have to send some all notes off messages also. */ midi_outc (i, 0xfe); for (chn = 0; chn < 16; chn++) { midi_outc (i, (unsigned char) (0xb0 + (chn & 0x0f))); /* control change */ midi_outc (i, 0x7b); /* All notes off */ midi_outc (i, 0); /* Dummy parameter */ } midi_devs[i]->close (i); midi_written[i] = 0; midi_opened[i] = 0; } } seq_playing = 0; DISABLE_INTR (flags); if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) { /* printk ("Sequencer Warning: Unexpected sleeping process - Waking up\n"); */ WAKE_UP (seq_sleeper, seq_sleep_flag); } RESTORE_INTR (flags); } static void seq_panic (void) { /* * This routine is called by the application in case the user * wants to reset the system to the default state. */ seq_reset (); /* * Since some of the devices don't recognize the active sensing and * all notes off messages, we have to shut all notes manually. * * TO BE IMPLEMENTED LATER */ /* * Also return the controllers to their default states */ } int sequencer_ioctl (int dev, struct fileinfo *file, unsigned int cmd, unsigned int arg) { int midi_dev, orig_dev; int mode = file->mode & O_ACCMODE; orig_dev = dev = dev >> 4; switch (cmd) { case SNDCTL_TMR_TIMEBASE: case SNDCTL_TMR_TEMPO: case SNDCTL_TMR_START: case SNDCTL_TMR_STOP: case SNDCTL_TMR_CONTINUE: case SNDCTL_TMR_METRONOME: case SNDCTL_TMR_SOURCE: if (dev) /* Patch manager */ return RET_ERROR (EIO); if (seq_mode != SEQ_2) return RET_ERROR (EINVAL); return tmr->ioctl (tmr_no, cmd, arg); break; case SNDCTL_TMR_SELECT: if (dev) /* Patch manager */ return RET_ERROR (EIO); if (seq_mode != SEQ_2) return RET_ERROR (EINVAL); pending_timer = IOCTL_IN (arg); if (pending_timer < 0 || pending_timer >= num_sound_timers) { pending_timer = -1; return RET_ERROR (EINVAL); } return IOCTL_OUT (arg, pending_timer); break; case SNDCTL_SEQ_PANIC: seq_panic (); break; case SNDCTL_SEQ_SYNC: if (dev) /* * Patch manager */ return RET_ERROR (EIO); if (mode == OPEN_READ) return 0; while (qlen && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag)) seq_sync (); if (qlen) return RET_ERROR (EINTR); else return 0; break; case SNDCTL_SEQ_RESET: if (dev) /* * Patch manager */ return RET_ERROR (EIO); seq_reset (); return 0; break; case SNDCTL_SEQ_TESTMIDI: if (dev) /* * Patch manager */ return RET_ERROR (EIO); midi_dev = IOCTL_IN (arg); if (midi_dev >= max_mididev) return RET_ERROR (ENXIO); if (!midi_opened[midi_dev]) { int err, mode; mode = file->mode & O_ACCMODE; if ((err = midi_devs[midi_dev]->open (midi_dev, mode, sequencer_midi_input, sequencer_midi_output)) < 0) return err; } midi_opened[midi_dev] = 1; return 0; break; case SNDCTL_SEQ_GETINCOUNT: if (dev) /* * Patch manager */ return RET_ERROR (EIO); if (mode == OPEN_WRITE) return 0; return IOCTL_OUT (arg, iqlen); break; case SNDCTL_SEQ_GETOUTCOUNT: if (mode == OPEN_READ) return 0; return IOCTL_OUT (arg, SEQ_MAX_QUEUE - qlen); break; case SNDCTL_SEQ_CTRLRATE: if (dev) /* Patch manager */ return RET_ERROR (EIO); /* * If *arg == 0, just return the current rate */ if (seq_mode == SEQ_2) return tmr->ioctl (tmr_no, cmd, arg); if (IOCTL_IN (arg) != 0) return RET_ERROR (EINVAL); return IOCTL_OUT (arg, HZ); break; case SNDCTL_SEQ_RESETSAMPLES: dev = IOCTL_IN (arg); if (dev < 0 || dev >= num_synths) return RET_ERROR (ENXIO); if (!(synth_open_mask & (1 << dev)) && !orig_dev) return RET_ERROR (EBUSY); if (!orig_dev && pmgr_present[dev]) pmgr_inform (dev, PM_E_PATCH_RESET, 0, 0, 0, 0); return synth_devs[dev]->ioctl (dev, cmd, arg); break; case SNDCTL_SEQ_NRSYNTHS: return IOCTL_OUT (arg, max_synthdev); break; case SNDCTL_SEQ_NRMIDIS: return IOCTL_OUT (arg, max_mididev); break; case SNDCTL_SYNTH_MEMAVL: { int dev = IOCTL_IN (arg); if (dev < 0 || dev >= num_synths) return RET_ERROR (ENXIO); if (!(synth_open_mask & (1 << dev)) && !orig_dev) return RET_ERROR (EBUSY); return IOCTL_OUT (arg, synth_devs[dev]->ioctl (dev, cmd, arg)); } break; case SNDCTL_FM_4OP_ENABLE: { int dev = IOCTL_IN (arg); if (dev < 0 || dev >= num_synths) return RET_ERROR (ENXIO); if (!(synth_open_mask & (1 << dev))) return RET_ERROR (ENXIO); synth_devs[dev]->ioctl (dev, cmd, arg); return 0; } break; case SNDCTL_SYNTH_INFO: { struct synth_info inf; int dev; IOCTL_FROM_USER ((char *) &inf, (char *) arg, 0, sizeof (inf)); dev = inf.device; if (dev < 0 || dev >= max_synthdev) return RET_ERROR (ENXIO); if (!(synth_open_mask & (1 << dev)) && !orig_dev) return RET_ERROR (EBUSY); return synth_devs[dev]->ioctl (dev, cmd, arg); } break; case SNDCTL_SEQ_OUTOFBAND: { struct seq_event_rec event; unsigned long flags; IOCTL_FROM_USER ((char *) &event, (char *) arg, 0, sizeof (event)); DISABLE_INTR (flags); play_event (event.arr); RESTORE_INTR (flags); return 0; } break; case SNDCTL_MIDI_INFO: { struct midi_info inf; int dev; IOCTL_FROM_USER ((char *) &inf, (char *) arg, 0, sizeof (inf)); dev = inf.device; if (dev < 0 || dev >= max_mididev) return RET_ERROR (ENXIO); IOCTL_TO_USER ((char *) arg, 0, (char *) &(midi_devs[dev]->info), sizeof (inf)); return 0; } break; case SNDCTL_PMGR_IFACE: { struct patmgr_info *inf; int dev, err; if ((inf = (struct patmgr_info *) KERNEL_MALLOC (sizeof (*inf))) == NULL) { printk ("patmgr: Can't allocate memory for a message\n"); return RET_ERROR (EIO); } IOCTL_FROM_USER ((char *) inf, (char *) arg, 0, sizeof (*inf)); dev = inf->device; if (dev < 0 || dev >= num_synths) { KERNEL_FREE (inf); return RET_ERROR (ENXIO); } if (!synth_devs[dev]->pmgr_interface) { KERNEL_FREE (inf); return RET_ERROR (ENXIO); } if ((err = synth_devs[dev]->pmgr_interface (dev, inf)) == -1) { KERNEL_FREE (inf); return err; } IOCTL_TO_USER ((char *) arg, 0, (char *) inf, sizeof (*inf)); KERNEL_FREE (inf); return 0; } break; case SNDCTL_PMGR_ACCESS: { struct patmgr_info *inf; int dev, err; if ((inf = (struct patmgr_info *) KERNEL_MALLOC (sizeof (*inf))) == NULL) { printk ("patmgr: Can't allocate memory for a message\n"); return RET_ERROR (EIO); } IOCTL_FROM_USER ((char *) inf, (char *) arg, 0, sizeof (*inf)); dev = inf->device; if (dev < 0 || dev >= num_synths) { KERNEL_FREE (inf); return RET_ERROR (ENXIO); } if (!pmgr_present[dev]) { KERNEL_FREE (inf); return RET_ERROR (ESRCH); } if ((err = pmgr_access (dev, inf)) < 0) { KERNEL_FREE (inf); return err; } IOCTL_TO_USER ((char *) arg, 0, (char *) inf, sizeof (*inf)); KERNEL_FREE (inf); return 0; } break; case SNDCTL_SEQ_TRESHOLD: { int tmp = IOCTL_IN (arg); if (dev) /* * Patch manager */ return RET_ERROR (EIO); if (tmp < 1) tmp = 1; if (tmp >= SEQ_MAX_QUEUE) tmp = SEQ_MAX_QUEUE - 1; output_treshold = tmp; return 0; } break; case SNDCTL_MIDI_PRETIME: { int val = IOCTL_IN (arg); if (val < 0) val = 0; val = (HZ * val) / 10; pre_event_timeout = val; return IOCTL_OUT (arg, val); } break; default: if (dev) /* * Patch manager */ return RET_ERROR (EIO); if (mode == OPEN_READ) return RET_ERROR (EIO); if (!synth_devs[0]) return RET_ERROR (ENXIO); if (!(synth_open_mask & (1 << 0))) return RET_ERROR (ENXIO); return synth_devs[0]->ioctl (0, cmd, arg); break; } return RET_ERROR (EINVAL); } #ifdef ALLOW_SELECT int sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait) { unsigned long flags; dev = dev >> 4; switch (sel_type) { case SEL_IN: DISABLE_INTR (flags); if (!iqlen) { #if defined(__FreeBSD__) selrecord(wait, &selinfo[dev]); #else midi_sleep_flag.mode = WK_SLEEP; select_wait (&midi_sleeper, wait); #endif RESTORE_INTR (flags); return 0; } midi_sleep_flag.mode &= ~WK_SLEEP; RESTORE_INTR (flags); return 1; break; case SEL_OUT: DISABLE_INTR (flags); if (qlen >= SEQ_MAX_QUEUE) { #if defined(__FreeBSD__) selrecord(wait, &selinfo[dev]); #else seq_sleep_flag.mode = WK_SLEEP; select_wait (&seq_sleeper, wait); #endif RESTORE_INTR (flags); return 0; } seq_sleep_flag.mode &= ~WK_SLEEP; RESTORE_INTR (flags); return 1; break; case SEL_EX: return 0; } return 0; } #endif void sequencer_timer (void) { seq_startplay (); } int note_to_freq (int note_num) { /* * This routine converts a midi note to a frequency (multiplied by 1000) */ int note, octave, note_freq; int notes[] = { 261632, 277189, 293671, 311132, 329632, 349232, 369998, 391998, 415306, 440000, 466162, 493880 }; #define BASE_OCTAVE 5 octave = note_num / 12; note = note_num % 12; note_freq = notes[note]; if (octave < BASE_OCTAVE) note_freq >>= (BASE_OCTAVE - octave); else if (octave > BASE_OCTAVE) note_freq <<= (octave - BASE_OCTAVE); /* * note_freq >>= 1; */ return note_freq; } unsigned long compute_finetune (unsigned long base_freq, int bend, int range) { unsigned long amount; int negative, semitones, cents, multiplier = 1; if (!bend) return base_freq; if (!range) return base_freq; if (!base_freq) return base_freq; if (range >= 8192) range = 8191; bend = bend * range / 8192; if (!bend) return base_freq; negative = bend < 0 ? 1 : 0; if (bend < 0) bend *= -1; if (bend > range) bend = range; /* if (bend > 2399) bend = 2399; */ while (bend > 2399) { multiplier *= 4; bend -= 2400; } semitones = bend / 100; cents = bend % 100; amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents]) / 10000; if (negative) return (base_freq * 10000) / amount; /* * Bend down */ else return (base_freq * amount) / 10000; /* * Bend up */ } long sequencer_init (long mem_start) { sequencer_ok = 1; PERMANENT_MALLOC (unsigned char *, queue, SEQ_MAX_QUEUE * EV_SZ, mem_start); PERMANENT_MALLOC (unsigned char *, iqueue, SEQ_MAX_QUEUE * IEV_SZ, mem_start); return mem_start; } #else /* * Stub version */ int sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { return RET_ERROR (EIO); } int sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { return RET_ERROR (EIO); } int sequencer_open (int dev, struct fileinfo *file) { return RET_ERROR (ENXIO); } void sequencer_release (int dev, struct fileinfo *file) { } int sequencer_ioctl (int dev, struct fileinfo *file, unsigned int cmd, unsigned int arg) { return RET_ERROR (EIO); } int sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig) { return RET_ERROR (EIO); } long sequencer_init (long mem_start) { return mem_start; } #ifdef ALLOW_SELECT int sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait) { return RET_ERROR (EIO); } #endif #endif #endif Index: head/sys/pc98/pc98/sound/sound_config.h =================================================================== --- head/sys/pc98/pc98/sound/sound_config.h (revision 18264) +++ head/sys/pc98/pc98/sound/sound_config.h (revision 18265) @@ -1,375 +1,375 @@ /* sound_config.h * * A driver for Soundcards, misc configuration parameters. * * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "local.h" -#include "os.h" -#include "soundvers.h" +#include +#include +#include #if !defined(PSS_MPU_BASE) && defined(EXCLUDE_SSCAPE) && defined(EXCLUDE_TRIX) #define EXCLUDE_MPU_EMU #endif #if defined(ISC) || defined(SCO) || defined(SVR42) #define GENERIC_SYSV #endif /* * Disable the AD1848 driver if there are no other drivers requiring it. */ #if defined(EXCLUDE_GUS16) && defined(EXCLUDE_MSS) && defined(EXCLUDE_PSS) && defined(EXCLUDE_GUSMAX) && defined(EXCLUDE_SSCAPE) && defined(EXCLUDE_TRIX) #define EXCLUDE_AD1848 #endif #ifdef PSS_MSS_BASE #undef EXCLUDE_AD1848 #endif #undef CONFIGURE_SOUNDCARD #undef DYNAMIC_BUFFER #ifdef KERNEL_SOUNDCARD #define CONFIGURE_SOUNDCARD #define DYNAMIC_BUFFER #undef LOADABLE_SOUNDCARD #endif #ifdef EXCLUDE_SEQUENCER #define EXCLUDE_MIDI #define EXCLUDE_YM3812 #define EXCLUDE_OPL3 #endif #ifndef SND_DEFAULT_ENABLE #define SND_DEFAULT_ENABLE 1 #endif #ifdef CONFIGURE_SOUNDCARD /* ****** IO-address, DMA and IRQ settings **** If your card has nonstandard I/O address or IRQ number, change defines for the following settings in your kernel Makefile */ #ifndef SBC_BASE #ifdef PC98 #define SBC_BASE 0x20d2 /* 0x20d2 is the factory default. */ #else #define SBC_BASE 0x220 /* 0x220 is the factory default. */ #endif #endif #ifndef SBC_IRQ #ifdef PC98 #define SBC_IRQ 10 /* IQR10 is not the factory default on PC9821. */ #else #define SBC_IRQ 7 /* IQR7 is the factory default. */ #endif #endif #ifndef SBC_DMA #ifdef PC98 #define SBC_DMA 3 #else #define SBC_DMA 1 #endif #endif #ifndef SB16_DMA #ifdef PC98 #define SB16_DMA 3 #else #define SB16_DMA 6 #endif #endif #ifndef SB16MIDI_BASE #ifdef PC98 #define SB16MIDI_BASE 0x80d2 #else #define SB16MIDI_BASE 0x300 #endif #endif #ifndef PAS_BASE #define PAS_BASE 0x388 #endif #ifndef PAS_IRQ #define PAS_IRQ 5 #endif #ifndef PAS_DMA #define PAS_DMA 3 #endif #ifndef GUS_BASE #define GUS_BASE 0x220 #endif #ifndef GUS_IRQ #define GUS_IRQ 15 #endif #ifndef GUS_MIDI_IRQ #define GUS_MIDI_IRQ GUS_IRQ #endif #ifndef GUS_DMA #define GUS_DMA 6 #endif #ifndef GUS_DMA_READ #define GUS_DMA_READ 3 #endif #ifndef MPU_BASE #define MPU_BASE 0x330 #endif #ifndef MPU_IRQ #define MPU_IRQ 6 #endif /* Echo Personal Sound System */ #ifndef PSS_BASE #define PSS_BASE 0x220 /* 0x240 or */ #endif #ifndef PSS_IRQ #define PSS_IRQ 7 #endif #ifndef PSS_DMA #define PSS_DMA 1 #endif #ifndef MSS_BASE #define MSS_BASE 0 #endif #ifndef MSS_DMA #define MSS_DMA 0 #endif #ifndef MSS_IRQ #define MSS_IRQ 0 #endif #ifndef GUS16_BASE #define GUS16_BASE 0 #endif #ifndef GUS16_DMA #define GUS16_DMA 0 #endif #ifndef GUS16_IRQ #define GUS16_IRQ 0 #endif #ifndef SSCAPE_BASE #define SSCAPE_BASE 0 #endif #ifndef SSCAPE_DMA #define SSCAPE_DMA 0 #endif #ifndef SSCAPE_IRQ #define SSCAPE_IRQ 0 #endif #ifndef SSCAPE_MSS_BASE #define SSCAPE_MSS_BASE 0 #endif #ifndef SSCAPE_MSS_DMA #define SSCAPE_MSS_DMA 0 #endif #ifndef SSCAPE_MSS_IRQ #define SSCAPE_MSS_IRQ 0 #endif #ifndef TRIX_BASE #define TRIX_BASE 0x530 #endif #ifndef TRIX_IRQ #define TRIX_IRQ 10 #endif #ifndef TRIX_DMA #define TRIX_DMA 1 #endif #ifndef U6850_BASE #define U6850_BASE 0x330 #endif #ifndef U6850_IRQ #define U6850_IRQ 5 #endif #ifndef U6850_DMA #define U6850_DMA 1 #endif #ifndef MAX_REALTIME_FACTOR #define MAX_REALTIME_FACTOR 4 #endif /************* PCM DMA buffer sizes *******************/ /* If you are using high playback or recording speeds, the default buffersize is too small. DSP_BUFFSIZE must be 64k or less. A rule of thumb is 64k for PAS16, 32k for PAS+, 16k for SB Pro and 4k for SB. If you change the DSP_BUFFSIZE, don't modify this file. Use the make config command instead. */ #ifndef DSP_BUFFSIZE #define DSP_BUFFSIZE (4096) #endif #ifndef DSP_BUFFCOUNT #define DSP_BUFFCOUNT 2 /* 2 is recommended. */ #endif #define DMA_AUTOINIT 0x10 #ifdef PC98 #define FM_MONO 0x28d2 /* This is the I/O address used by AdLib */ #else #define FM_MONO 0x388 /* This is the I/O address used by AdLib */ #endif /* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the driver. (There is no need to alter this) */ #define SEQ_MAX_QUEUE 1024 #define SBFM_MAXINSTR (256) /* Size of the FM Instrument bank */ /* 128 instruments for general MIDI setup and 16 unassigned */ /* * Minor numbers for the sound driver. * * Unfortunately Creative called the codec chip of SB as a DSP. For this * reason the /dev/dsp is reserved for digitized audio use. There is a * device for true DSP processors but it will be called something else. * In v3.0 it's /dev/sndproc but this could be a temporary solution. */ #define SND_NDEVS 256 /* Number of supported devices */ #define SND_DEV_CTL 0 /* Control port /dev/mixer */ #define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM synthesizer and MIDI output) */ #define SND_DEV_MIDIN 2 /* Raw midi access */ #define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ #define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ #define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ #define SND_DEV_STATUS 6 /* /dev/sndstat */ /* #7 not in use now. Was in 2.4. Free for use after v3.0. */ #define SND_DEV_SEQ2 8 /* /dev/sequecer, level 2 interface */ #define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ #define SND_DEV_PSS SND_DEV_SNDPROC #define DSP_DEFAULT_SPEED 8000 #define ON 1 #define OFF 0 #define MAX_AUDIO_DEV 5 #define MAX_MIXER_DEV 5 #define MAX_SYNTH_DEV 3 #define MAX_MIDI_DEV 6 #define MAX_TIMER_DEV 3 struct fileinfo { int mode; /* Open mode */ DECLARE_FILE(); /* Reference to file-flags. OS-dependent. */ }; struct address_info { int io_base; int irq; int dma; /* write dma channel */ int dma_read; /* read dma channel */ int always_detect; /* 1=Trust me, it's there */ }; #define SYNTH_MAX_VOICES 32 struct voice_alloc_info { int max_voice; int used_voices; int ptr; /* For device specific use */ unsigned short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */ int timestamp; int alloc_times[SYNTH_MAX_VOICES]; }; struct channel_info { int pgm_num; int bender_value; unsigned char controllers[128]; }; /* * Process wakeup reasons */ #define WK_NONE 0x00 #define WK_WAKEUP 0x01 #define WK_TIMEOUT 0x02 #define WK_SIGNAL 0x04 #define WK_SLEEP 0x08 #define OPEN_READ 1 #define OPEN_WRITE 2 #define OPEN_READWRITE 3 -#include "sound_calls.h" -#include "dev_table.h" +#include +#include #ifndef DEB #define DEB(x) #endif #ifndef AUDIO_DDB #define AUDIO_DDB(x) #endif #define TIMER_ARMED 121234 #define TIMER_NOT_ARMED 1 #endif Index: head/sys/pc98/pc98/sound/sound_switch.c =================================================================== --- head/sys/pc98/pc98/sound/sound_switch.c (revision 18264) +++ head/sys/pc98/pc98/sound/sound_switch.c (revision 18265) @@ -1,544 +1,544 @@ /* * sound/sound_switch.c * * The system call switch * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #ifdef CONFIGURE_SOUNDCARD struct sbc_device { int usecount; }; static struct sbc_device sbc_devices[SND_NDEVS] = { {0}}; static int in_use = 0; /* * * * * Total # of open device files * (excluding * * * minor 0) */ /* * /dev/sndstatus -device */ static char *status_buf = NULL; static int status_len, status_ptr; static int status_busy = 0; static int put_status (char *s) { int l; for (l = 0; l < 256, s[l]; l++); /* * l=strlen(s); */ if (status_len + l >= 4000) return 0; memcpy (&status_buf[status_len], s, l); status_len += l; return 1; } static int put_status_int (unsigned int val, int radix) { int l, v; static char hx[] = "0123456789abcdef"; char buf[11]; if (!val) return put_status ("0"); l = 0; buf[10] = 0; while (val) { v = val % radix; val = val / radix; buf[9 - l] = hx[v]; l++; } if (status_len + l >= 4000) return 0; memcpy (&status_buf[status_len], &buf[10 - l], l); status_len += l; return 1; } static void init_status (void) { /* * Write the status information to the status_buf and update status_len. * There is a limit of 4000 bytes for the data. */ int i; status_ptr = 0; put_status ("VoxWare Sound Driver:" SOUND_VERSION_STRING " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@" SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")" "\n"); if (!put_status ("Config options: ")) return; if (!put_status_int (SELECTED_SOUND_OPTIONS, 16)) return; if (!put_status ("\n\nInstalled drivers: \n")) return; for (i = 0; i < (num_sound_drivers - 1); i++) { if (!put_status ("Type ")) return; if (!put_status_int (sound_drivers[i].card_type, 10)) return; if (!put_status (": ")) return; if (!put_status (sound_drivers[i].name)) return; if (!put_status ("\n")) return; } if (!put_status ("\n\nCard config: \n")) return; for (i = 0; i < (num_sound_cards - 1); i++) { int drv; if (!snd_installed_cards[i].enabled) if (!put_status ("(")) return; /* * if (!put_status_int(snd_installed_cards[i].card_type, 10)) return; * if (!put_status (": ")) return; */ if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) != -1) if (!put_status (sound_drivers[drv].name)) return; if (!put_status (" at 0x")) return; if (!put_status_int (snd_installed_cards[i].config.io_base, 16)) return; if (!put_status (" irq ")) return; if (!put_status_int (snd_installed_cards[i].config.irq, 10)) return; #ifdef PC98 if (snd_installed_cards[i].config.dma >= 0) { #endif if (!put_status (" drq ")) return; if (!put_status_int (snd_installed_cards[i].config.dma, 10)) return; #ifdef PC98 } #endif if (!snd_installed_cards[i].enabled) if (!put_status (")")) return; if (!put_status ("\n")) return; } #ifdef EXCLUDE_AUDIO if (!put_status ("\nAudio devices: NOT ENABLED IN CONFIG\n")) return; #else if (!put_status ("\nAudio devices:\n")) return; for (i = 0; i < num_audiodevs; i++) { if (!put_status_int (i, 10)) return; if (!put_status (": ")) return; if (!put_status (audio_devs[i]->name)) return; if (!put_status ("\n")) return; } #endif #ifdef EXCLUDE_SEQUENCER if (!put_status ("\nSynth devices: NOT ENABLED IN CONFIG\n")) return; #else if (!put_status ("\nSynth devices:\n")) return; for (i = 0; i < num_synths; i++) { if (!put_status_int (i, 10)) return; if (!put_status (": ")) return; if (!put_status (synth_devs[i]->info->name)) return; if (!put_status ("\n")) return; } #endif #ifdef EXCLUDE_MIDI if (!put_status ("\nMidi devices: NOT ENABLED IN CONFIG\n")) return; #else if (!put_status ("\nMidi devices:\n")) return; for (i = 0; i < num_midis; i++) { if (!put_status_int (i, 10)) return; if (!put_status (": ")) return; if (!put_status (midi_devs[i]->info.name)) return; if (!put_status ("\n")) return; } #endif if (!put_status ("\nTimers:\n")) return; for (i = 0; i < num_sound_timers; i++) { if (!put_status_int (i, 10)) return; if (!put_status (": ")) return; if (!put_status (sound_timer_devs[i]->info.name)) return; if (!put_status ("\n")) return; } if (!put_status ("\nMixers:\n")) return; for (i = 0; i < num_mixers; i++) { if (!put_status_int (i, 10)) return; if (!put_status (": ")) return; if (!put_status (mixer_devs[i]->name)) return; if (!put_status ("\n")) return; } } static int read_status (snd_rw_buf * buf, int count) { /* * Return at most 'count' bytes from the status_buf. */ int l, c; l = count; c = status_len - status_ptr; if (l > c) l = c; if (l <= 0) return 0; COPY_TO_USER (buf, 0, &status_buf[status_ptr], l); status_ptr += l; return l; } int sound_read_sw (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { DEB (printk ("sound_read_sw(dev=%d, count=%d)\n", dev, count)); switch (dev & 0x0f) { case SND_DEV_STATUS: return read_status (buf, count); break; case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: return audio_read (dev, file, buf, count); break; case SND_DEV_SEQ: case SND_DEV_SEQ2: return sequencer_read (dev, file, buf, count); break; #ifndef EXCLUDE_MIDI case SND_DEV_MIDIN: return MIDIbuf_read (dev, file, buf, count); #endif default: printk ("Sound: Undefined minor device %d\n", dev); } return RET_ERROR (EPERM); } int sound_write_sw (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { DEB (printk ("sound_write_sw(dev=%d, count=%d)\n", dev, count)); switch (dev & 0x0f) { case SND_DEV_SEQ: case SND_DEV_SEQ2: return sequencer_write (dev, file, buf, count); break; case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: return audio_write (dev, file, buf, count); break; #ifndef EXCLUDE_MIDI case SND_DEV_MIDIN: return MIDIbuf_write (dev, file, buf, count); #endif default: return RET_ERROR (EPERM); } return count; } int sound_open_sw (int dev, struct fileinfo *file) { int retval; DEB (printk ("sound_open_sw(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount)); if ((dev >= SND_NDEVS) || (dev < 0)) { printk ("Invalid minor device %d\n", dev); return RET_ERROR (ENXIO); } switch (dev & 0x0f) { case SND_DEV_STATUS: if (status_busy) return RET_ERROR (EBUSY); status_busy = 1; if ((status_buf = (char *) KERNEL_MALLOC (4000)) == NULL) return RET_ERROR (EIO); status_len = status_ptr = 0; init_status (); break; case SND_DEV_CTL: return 0; break; case SND_DEV_SEQ: case SND_DEV_SEQ2: if ((retval = sequencer_open (dev, file)) < 0) return retval; break; #ifndef EXCLUDE_MIDI case SND_DEV_MIDIN: if ((retval = MIDIbuf_open (dev, file)) < 0) return retval; break; #endif case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: if ((retval = audio_open (dev, file)) < 0) return retval; break; default: printk ("Invalid minor device %d\n", dev); return RET_ERROR (ENXIO); } sbc_devices[dev].usecount++; in_use++; return 0; } void sound_release_sw (int dev, struct fileinfo *file) { DEB (printk ("sound_release_sw(dev=%d)\n", dev)); switch (dev & 0x0f) { case SND_DEV_STATUS: if (status_buf) KERNEL_FREE (status_buf); status_buf = NULL; status_busy = 0; break; case SND_DEV_CTL: break; case SND_DEV_SEQ: case SND_DEV_SEQ2: sequencer_release (dev, file); break; #ifndef EXCLUDE_MIDI case SND_DEV_MIDIN: MIDIbuf_release (dev, file); break; #endif case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: audio_release (dev, file); break; default: printk ("Sound error: Releasing unknown device 0x%02x\n", dev); } sbc_devices[dev].usecount--; in_use--; } int sound_ioctl_sw (int dev, struct fileinfo *file, unsigned int cmd, unsigned long arg) { DEB (printk ("sound_ioctl_sw(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); if ((cmd >> 8) & 0xff == 'M' && num_mixers > 0) /* Mixer ioctl */ if ((dev & 0x0f) != SND_DEV_CTL) { int dtype = dev & 0x0f; int mixdev; switch (dtype) { case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: mixdev = audio_devs[dev >> 4]->mixer_dev; if (mixdev < 0 || mixdev >= num_mixers) return RET_ERROR (ENXIO); return mixer_devs[mixdev]->ioctl (mixdev, cmd, arg); break; default: return mixer_devs[0]->ioctl (0, cmd, arg); } } switch (dev & 0x0f) { case SND_DEV_CTL: if (!num_mixers) return RET_ERROR (ENXIO); dev = dev >> 4; if (dev >= num_mixers) return RET_ERROR (ENXIO); return mixer_devs[dev]->ioctl (dev, cmd, arg); break; case SND_DEV_SEQ: case SND_DEV_SEQ2: return sequencer_ioctl (dev, file, cmd, arg); break; case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: return audio_ioctl (dev, file, cmd, arg); break; #ifndef EXCLUDE_MIDI case SND_DEV_MIDIN: return MIDIbuf_ioctl (dev, file, cmd, arg); break; #endif default: return RET_ERROR (EPERM); break; } return RET_ERROR (EPERM); } #endif Index: head/sys/pc98/pc98/sound/sound_timer.c =================================================================== --- head/sys/pc98/pc98/sound/sound_timer.c (revision 18264) +++ head/sys/pc98/pc98/sound/sound_timer.c (revision 18265) @@ -1,406 +1,406 @@ /* * sound/sound_timer.c * * Timer for the level 2 interface of the /dev/sequencer. Uses the * 80 and 320 usec timers of OPL-3 (PAS16 only) and GUS. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #define SEQUENCER_C -#include "sound_config.h" +#include #ifdef CONFIGURE_SOUNDCARD #if !defined(EXCLUDE_SEQUENCER) && (!defined(EXCLUDE_GUS) || (!defined(EXCLUDE_PAS) && !defined(EXCLUDE_YM3812))) static volatile int initialized = 0, opened = 0, tmr_running = 0; static volatile time_t tmr_offs, tmr_ctr; static volatile unsigned long ticks_offs; static volatile int curr_tempo, curr_timebase; static volatile unsigned long curr_ticks; static volatile unsigned long next_event_time; static unsigned long prev_event_time; static volatile int select_addr, data_addr; static volatile int curr_timer = 0; static volatile unsigned long usecs_per_tmr; /* Length of the current interval */ static void timer_command (unsigned int addr, unsigned int val) { int i; OUTB ((unsigned char) (addr & 0xff), select_addr); for (i = 0; i < 2; i++) INB (select_addr); OUTB ((unsigned char) (val & 0xff), data_addr); for (i = 0; i < 2; i++) INB (select_addr); } static void arm_timer (int timer, unsigned int interval) { curr_timer = timer; if (timer == 1) { gus_write8 (0x46, 256 - interval); /* Set counter for timer 1 */ gus_write8 (0x45, 0x04); /* Enable timer 1 IRQ */ timer_command (0x04, 0x01); /* Start timer 1 */ } else { gus_write8 (0x47, 256 - interval); /* Set counter for timer 2 */ gus_write8 (0x45, 0x08); /* Enable timer 2 IRQ */ timer_command (0x04, 0x02); /* Start timer 2 */ } } static unsigned long tmr2ticks (int tmr_value) { /* * Convert timer ticks to MIDI ticks */ unsigned long tmp; unsigned long scale; tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */ scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */ return (tmp + (scale / 2)) / scale; } static void reprogram_timer (void) { unsigned long usecs_per_tick; int timer_no, resolution; int divisor; usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase); /* * Don't kill the system by setting too high timer rate */ if (usecs_per_tick < 2000) usecs_per_tick = 2000; if (usecs_per_tick > (256 * 80)) { timer_no = 2; resolution = 320; /* usec */ } else { timer_no = 1; resolution = 80; /* usec */ } divisor = (usecs_per_tick + (resolution / 2)) / resolution; usecs_per_tmr = divisor * resolution; arm_timer (timer_no, divisor); } static void tmr_reset (void) { unsigned long flags; DISABLE_INTR (flags); tmr_offs = 0; ticks_offs = 0; tmr_ctr = 0; next_event_time = 0xffffffff; prev_event_time = 0; curr_ticks = 0; RESTORE_INTR (flags); } static int timer_open (int dev, int mode) { if (opened) return RET_ERROR (EBUSY); tmr_reset (); curr_tempo = 60; curr_timebase = HZ; opened = 1; reprogram_timer (); return 0; } static void timer_close (int dev) { opened = tmr_running = 0; gus_write8 (0x45, 0); /* Disable both timers */ } static int timer_event (int dev, unsigned char *event) { unsigned char cmd = event[1]; unsigned long parm = *(int *) &event[4]; switch (cmd) { case TMR_WAIT_REL: parm += prev_event_time; case TMR_WAIT_ABS: if (parm > 0) { long time; if (parm <= curr_ticks) /* It's the time */ return TIMER_NOT_ARMED; time = parm; next_event_time = prev_event_time = time; return TIMER_ARMED; } break; case TMR_START: tmr_reset (); tmr_running = 1; reprogram_timer (); break; case TMR_STOP: tmr_running = 0; break; case TMR_CONTINUE: tmr_running = 1; reprogram_timer (); break; case TMR_TEMPO: if (parm) { if (parm < 8) parm = 8; if (parm > 250) parm = 250; tmr_offs = tmr_ctr; ticks_offs += tmr2ticks (tmr_ctr); tmr_ctr = 0; curr_tempo = parm; reprogram_timer (); } break; case TMR_ECHO: seq_copy_to_input (event, 8); break; default:; } return TIMER_NOT_ARMED; } static unsigned long timer_get_time (int dev) { if (!opened) return 0; return curr_ticks; } static int timer_ioctl (int dev, unsigned int cmd, unsigned int arg) { switch (cmd) { case SNDCTL_TMR_SOURCE: return IOCTL_OUT (arg, TMR_INTERNAL); break; case SNDCTL_TMR_START: tmr_reset (); tmr_running = 1; return 0; break; case SNDCTL_TMR_STOP: tmr_running = 0; return 0; break; case SNDCTL_TMR_CONTINUE: tmr_running = 1; return 0; break; case SNDCTL_TMR_TIMEBASE: { int val = IOCTL_IN (arg); if (val) { if (val < 1) val = 1; if (val > 1000) val = 1000; curr_timebase = val; } return IOCTL_OUT (arg, curr_timebase); } break; case SNDCTL_TMR_TEMPO: { int val = IOCTL_IN (arg); if (val) { if (val < 8) val = 8; if (val > 250) val = 250; tmr_offs = tmr_ctr; ticks_offs += tmr2ticks (tmr_ctr); tmr_ctr = 0; curr_tempo = val; reprogram_timer (); } return IOCTL_OUT (arg, curr_tempo); } break; case SNDCTL_SEQ_CTRLRATE: if (IOCTL_IN (arg) != 0) /* Can't change */ return RET_ERROR (EINVAL); return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60); break; case SNDCTL_TMR_METRONOME: /* NOP */ break; default: } return RET_ERROR (EINVAL); } static void timer_arm (int dev, long time) { if (time < 0) time = curr_ticks + 1; else if (time <= curr_ticks) /* It's the time */ return; next_event_time = prev_event_time = time; return; } static struct sound_timer_operations sound_timer = { {"OPL-3/GUS Timer", 0}, 1, /* Priority */ 0, /* Local device link */ timer_open, timer_close, timer_event, timer_get_time, timer_ioctl, timer_arm }; void sound_timer_interrupt (void) { gus_write8 (0x45, 0); /* Ack IRQ */ timer_command (4, 0x80); /* Reset IRQ flags */ if (!opened) return; if (curr_timer == 1) gus_write8 (0x45, 0x04); /* Start timer 1 again */ else gus_write8 (0x45, 0x08); /* Start timer 2 again */ if (!tmr_running) return; tmr_ctr++; curr_ticks = ticks_offs + tmr2ticks (tmr_ctr); if (curr_ticks >= next_event_time) { next_event_time = 0xffffffff; sequencer_timer (); } } void sound_timer_init (int io_base) { int n; if (initialized) return; /* There is already a similar timer */ select_addr = io_base; data_addr = io_base + 1; initialized = 1; #if 1 if (num_sound_timers >= MAX_TIMER_DEV) n = 0; /* Overwrite the system timer */ else n = num_sound_timers++; #else n = 0; #endif sound_timer_devs[n] = &sound_timer; } #endif #endif Index: head/sys/pc98/pc98/sound/soundcard.c =================================================================== --- head/sys/pc98/pc98/sound/soundcard.c (revision 18264) +++ head/sys/pc98/pc98/sound/soundcard.c (revision 18265) @@ -1,573 +1,573 @@ /* * sound/386bsd/soundcard.c * * Soundcard driver for FreeBSD. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: soundcard.c,v 1.3 1996/09/03 10:24:21 asami Exp $ + * $Id: soundcard.c,v 1.43 1996/09/10 08:26:06 bde Exp $ */ -#include "sound_config.h" +#include #include #include #ifdef CONFIGURE_SOUNDCARD -#include "dev_table.h" +#include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #define FIX_RETURN(ret) { \ int tmp_ret = (ret); \ if (tmp_ret<0) return -tmp_ret; else return 0; \ } static int timer_running = 0; static int soundcards_installed = 0; /* Number of installed * soundcards */ static int soundcard_configured = 0; static struct fileinfo files[SND_NDEVS]; #ifdef DEVFS static void * snd_devfs_token[SND_NDEVS]; static void * sndstat_devfs_token; #endif struct selinfo selinfo[SND_NDEVS >> 4]; static int sndprobe (struct isa_device *dev); static int sndattach (struct isa_device *dev); static void sound_mem_init(void); static d_open_t sndopen; static d_close_t sndclose; static d_read_t sndread; static d_write_t sndwrite; static d_ioctl_t sndioctl; static d_select_t sndselect; #define CDEV_MAJOR 30 static struct cdevsw snd_cdevsw = { sndopen, sndclose, sndread, sndwrite, /*30*/ sndioctl, nostop, nullreset, nodevtotty,/* sound */ sndselect, nommap, NULL, "snd", NULL, -1 }; struct isa_driver opldriver = {sndprobe, sndattach, "opl"}; struct isa_driver sbdriver = {sndprobe, sndattach, "sb"}; struct isa_driver sbxvidriver = {sndprobe, sndattach, "sbxvi"}; struct isa_driver sbmididriver = {sndprobe, sndattach, "sbmidi"}; struct isa_driver pasdriver = {sndprobe, sndattach, "pas"}; struct isa_driver mpudriver = {sndprobe, sndattach, "mpu"}; struct isa_driver gusdriver = {sndprobe, sndattach, "gus"}; struct isa_driver gusxvidriver = {sndprobe, sndattach, "gusxvi"}; struct isa_driver gusmaxdriver = {sndprobe, sndattach, "gusmax"}; struct isa_driver uartdriver = {sndprobe, sndattach, "uart"}; struct isa_driver mssdriver = {sndprobe, sndattach, "mss"}; #ifdef PC98 struct isa_driver pcmdriver = {sndprobe, sndattach, "pcm"}; #endif static unsigned short ipri_to_irq (unsigned short ipri); void adintr(INT_HANDLER_PARMS(unit,dummy)) { #ifndef EXCLUDE_AD1848 static short unit_to_irq[4] = { -1, -1, -1, -1 }; struct isa_device *dev; if (unit_to_irq [unit] > 0) ad1848_interrupt(INT_HANDLER_CALL (unit_to_irq [unit])); else { dev = find_isadev (isa_devtab_null, &mssdriver, unit); if (!dev) printk ("ad1848: Couldn't determine unit\n"); else { unit_to_irq [unit] = ipri_to_irq (dev->id_irq); ad1848_interrupt(INT_HANDLER_CALL (unit_to_irq [unit])); } } #endif } unsigned long get_time(void) { struct timeval timecopy; int x; x = splclock(); timecopy = time; splx(x); return timecopy.tv_usec/(1000000/HZ) + (unsigned long)timecopy.tv_sec*HZ; } static int sndread (dev_t dev, struct uio *buf, int ioflag) { int count = buf->uio_resid; dev = minor (dev); FIX_RETURN (sound_read_sw (dev, &files[dev], buf, count)); } static int sndwrite (dev_t dev, struct uio *buf, int ioflag) { int count = buf->uio_resid; dev = minor (dev); FIX_RETURN (sound_write_sw (dev, &files[dev], buf, count)); } static int sndopen (dev_t dev, int flags, int fmt, struct proc *p) { dev = minor (dev); if (!soundcard_configured && dev) { printk ("SoundCard Error: The soundcard system has not been configured\n"); FIX_RETURN (-ENODEV); } files[dev].mode = 0; if (flags & FREAD && flags & FWRITE) files[dev].mode = OPEN_READWRITE; else if (flags & FREAD) files[dev].mode = OPEN_READ; else if (flags & FWRITE) files[dev].mode = OPEN_WRITE; selinfo[dev >> 4].si_pid = 0; selinfo[dev >> 4].si_flags = 0; FIX_RETURN(sound_open_sw (dev, &files[dev])); } static int sndclose (dev_t dev, int flags, int fmt, struct proc *p) { dev = minor (dev); sound_release_sw(dev, &files[dev]); FIX_RETURN (0); } static int sndioctl (dev_t dev, int cmd, caddr_t arg, int flags, struct proc *p) { dev = minor (dev); FIX_RETURN (sound_ioctl_sw (dev, &files[dev], cmd, (unsigned int) arg)); } static int sndselect (dev_t dev, int rw, struct proc *p) { dev = minor (dev); DEB (printk ("snd_select(dev=%d, rw=%d, pid=%d)\n", dev, rw, p->p_pid)); #ifdef ALLOW_SELECT switch (dev & 0x0f) { #ifndef EXCLUDE_SEQUENCER case SND_DEV_SEQ: case SND_DEV_SEQ2: return sequencer_select (dev, &files[dev], rw, p); break; #endif #ifndef EXCLUDE_MIDI case SND_DEV_MIDIN: return MIDIbuf_select (dev, &files[dev], rw, p); break; #endif #ifndef EXCLUDE_AUDIO case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: return audio_select (dev, &files[dev], rw, p); break; #endif default: return 0; } #endif return 0; } static unsigned short ipri_to_irq (unsigned short ipri) { /* * Converts the ipri (bitmask) to the corresponding irq number */ int irq; for (irq = 0; irq < 16; irq++) if (ipri == (1 << irq)) return irq; return -1; /* Invalid argument */ } static int driver_to_voxunit(struct isa_driver *driver) { /* converts a sound driver pointer into the equivalent VoxWare device unit number */ if(driver == &opldriver) return(SNDCARD_ADLIB); else if(driver == &sbdriver) return(SNDCARD_SB); else if(driver == &pasdriver) return(SNDCARD_PAS); else if(driver == &gusdriver) return(SNDCARD_GUS); else if(driver == &mpudriver) return(SNDCARD_MPU401); else if(driver == &sbxvidriver) return(SNDCARD_SB16); else if(driver == &sbmididriver) return(SNDCARD_SB16MIDI); else if(driver == &uartdriver) return(SNDCARD_UART6850); else if(driver == &gusdriver) return(SNDCARD_GUS16); else if(driver == &mssdriver) return(SNDCARD_MSS); #ifdef PC98 else if(driver == &pcmdriver) return(SNDCARD_PCM86); #endif else return(0); } static int sndprobe (struct isa_device *dev) { struct address_info hw_config; int unit; unit = driver_to_voxunit(dev->id_driver); hw_config.io_base = dev->id_iobase; hw_config.irq = ipri_to_irq (dev->id_irq); hw_config.dma = dev->id_drq; hw_config.dma_read = dev->id_flags; /* misuse the flags field for read dma*/ if(unit) #ifdef PC98 if(unit == SNDCARD_PCM86) { int result; result = sndtable_probe (unit, &hw_config); dev->id_iobase = hw_config.io_base; dev->id_irq = (1 << hw_config.irq); dev->id_drq = hw_config.dma; return result; } else #endif return sndtable_probe (unit, &hw_config); else return 0; } static int sndattach (struct isa_device *dev) { int unit; static int midi_initialized = 0; static int seq_initialized = 0; unsigned long mem_start = 0xefffffffUL; struct address_info hw_config; unit = driver_to_voxunit(dev->id_driver); hw_config.io_base = dev->id_iobase; hw_config.irq = ipri_to_irq (dev->id_irq); hw_config.dma = dev->id_drq; hw_config.dma_read = dev->id_flags; /* misuse the flags field for read dma*/ if(!unit) return FALSE; if (!sndtable_init_card (unit, &hw_config)) { printf (" "); return FALSE; } /* * Init the high level sound driver */ if (!(soundcards_installed = sndtable_get_cardcount ())) { printf (" "); return FALSE; /* No cards detected */ } printf("\n"); #ifndef EXCLUDE_AUDIO if (num_audiodevs) /* Audio devices present */ { mem_start = DMAbuf_init (mem_start); mem_start = audio_init (mem_start); sound_mem_init (); } soundcard_configured = 1; #endif #ifndef EXCLUDE_MIDI if (num_midis && !midi_initialized) { midi_initialized = 1; mem_start = MIDIbuf_init (mem_start); } #endif #ifndef EXCLUDE_SEQUENCER if ((num_midis + num_synths) && !seq_initialized) { seq_initialized = 1; mem_start = sequencer_init (mem_start); } #endif #ifdef DEVFS /* XXX */ /* find out where to store the tokens.. */ /* XXX */ /* should only create devices if that card has them */ #define SND_UID 0 #define SND_GID 13 snd_devfs_token[unit]= devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_CTL, DV_CHR, SND_UID, SND_GID, 0660, "mixer%d", unit); #ifndef EXCLUDE_SEQUENCER snd_devfs_token[unit]= devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_SEQ, DV_CHR, SND_UID, SND_GID, 0660, "sequencer%d", unit); snd_devfs_token[unit]= devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_SEQ2, DV_CHR, SND_UID, SND_GID, 0660, "music%d", unit); #endif #ifndef EXCLUDE_MIDI snd_devfs_token[unit]= devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_MIDIN, DV_CHR, SND_UID, SND_GID, 0660, "midi%d", unit); #endif #ifndef EXCLUDE_AUDIO snd_devfs_token[unit]= devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_DSP, DV_CHR, SND_UID, SND_GID, 0660, "dsp%d", unit); snd_devfs_token[unit]= devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_AUDIO, DV_CHR, SND_UID, SND_GID, 0660, "audio%d", unit); snd_devfs_token[unit]= devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_DSP16, DV_CHR, SND_UID, SND_GID, 0660, "dspW%d", unit); #endif snd_devfs_token[unit]= devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_SNDPROC, DV_CHR, SND_UID, SND_GID, 0660, "pss%d", unit); if ( ! sndstat_devfs_token) { sndstat_devfs_token = devfs_add_devswf(&snd_cdevsw, 6, DV_CHR, SND_UID, SND_GID, 0660, "sndstat"); } #endif /* DEVFS */ return TRUE; } void tenmicrosec (void) { int i; for (i = 0; i < 16; i++) inb (0x80); } #ifndef EXCLUDE_SEQUENCER void request_sound_timer (int count) { static int current = 0; int tmp = count; if (count < 0) timeout ((timeout_func_t)sequencer_timer, 0, -count); else { if (count < current) current = 0; /* Timer restarted */ count = count - current; current = tmp; if (!count) count = 1; timeout ((timeout_func_t)sequencer_timer, 0, count); } timer_running = 1; } void sound_stop_timer (void) { if (timer_running) untimeout ((timeout_func_t)sequencer_timer, 0); timer_running = 0; } #endif #ifndef EXCLUDE_AUDIO static void sound_mem_init (void) { int dev; unsigned long dma_pagesize; struct dma_buffparms *dmap; static unsigned long dsp_init_mask = 0; for (dev = 0; dev < num_audiodevs; dev++) /* Enumerate devices */ if (!(dsp_init_mask & (1 << dev))) /* Not already done */ #ifdef PC98 if (audio_devs[dev]->buffcount > 0 && audio_devs[dev]->dmachan >= 0) #else if (audio_devs[dev]->buffcount > 0 && audio_devs[dev]->dmachan > 0) #endif { dsp_init_mask |= (1 << dev); dmap = audio_devs[dev]->dmap; if (audio_devs[dev]->flags & DMA_AUTOMODE) audio_devs[dev]->buffcount = 1; audio_devs[dev]->buffsize &= ~0xfff; /* Truncate to n*4k */ if (audio_devs[dev]->dmachan > 3 && audio_devs[dev]->buffsize > 65536) dma_pagesize = 131072; /* 128k */ else dma_pagesize = 65536; /* More sanity checks */ if (audio_devs[dev]->buffsize > dma_pagesize) audio_devs[dev]->buffsize = dma_pagesize; if (audio_devs[dev]->buffsize < 4096) audio_devs[dev]->buffsize = 4096; /* Now allocate the buffers */ for (dmap->raw_count = 0; dmap->raw_count < audio_devs[dev]->buffcount; dmap->raw_count++) { char *tmpbuf = (char *)vm_page_alloc_contig(audio_devs[dev]->buffsize, 0ul, 0xfffffful, dma_pagesize); if (tmpbuf == NULL) { printk ("snd: Unable to allocate %ld bytes of buffer\n", audio_devs[dev]->buffsize); return; } dmap->raw_buf[dmap->raw_count] = tmpbuf; /* * Use virtual address as the physical address, since * isa_dmastart performs the phys address computation. */ dmap->raw_buf_phys[dmap->raw_count] = (unsigned long) dmap->raw_buf[dmap->raw_count]; } } /* for dev */ } #endif int snd_set_irq_handler (int interrupt_level, INT_HANDLER_PROTO(), char *name) { return 1; } void snd_release_irq(int vect) { } static snd_devsw_installed = 0; static void snd_drvinit(void *unused) { dev_t dev; if( ! snd_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&snd_cdevsw, NULL); snd_devsw_installed = 1; } } SYSINIT(snddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,snd_drvinit,NULL) #endif Index: head/sys/pc98/pc98/sound/sscape.c =================================================================== --- head/sys/pc98/pc98/sound/sscape.c (revision 18264) +++ head/sys/pc98/pc98/sound/sscape.c (revision 18265) @@ -1,1120 +1,1120 @@ /* * sound/sscape.c * * Low level driver for Ensoniq Soundscape * * Copyright by Hannu Savolainen 1994 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SSCAPE) -#include "coproc.h" +#include /* * I/O ports */ #define MIDI_DATA 0 #define MIDI_CTRL 1 #define HOST_CTRL 2 #define TX_READY 0x02 #define RX_READY 0x01 #define HOST_DATA 3 #define ODIE_ADDR 4 #define ODIE_DATA 5 /* * Indirect registers */ #define GA_INTSTAT_REG 0 #define GA_INTENA_REG 1 #define GA_DMAA_REG 2 #define GA_DMAB_REG 3 #define GA_INTCFG_REG 4 #define GA_DMACFG_REG 5 #define GA_CDCFG_REG 6 #define GA_SMCFGA_REG 7 #define GA_SMCFGB_REG 8 #define GA_HMCTL_REG 9 /* * DMA channel identifiers (A and B) */ #define SSCAPE_DMA_A 0 #define SSCAPE_DMA_B 1 #define PORT(name) (devc->base+name) /* * Host commands recognized by the OBP microcode */ #define CMD_GEN_HOST_ACK 0x80 #define CMD_GEN_MPU_ACK 0x81 #define CMD_GET_BOARD_TYPE 0x82 #define CMD_SET_CONTROL 0x88 #define CMD_GET_CONTROL 0x89 #define CMD_SET_MT32 0x96 #define CMD_GET_MT32 0x97 #define CMD_SET_EXTMIDI 0x9b #define CMD_GET_EXTMIDI 0x9c #define CMD_ACK 0x80 typedef struct sscape_info { int base, irq, dma; int ok; /* Properly detected */ int dma_allocated; int my_audiodev; int opened; } sscape_info; static struct sscape_info dev_info = {0}; static struct sscape_info *devc = &dev_info; DEFINE_WAIT_QUEUE (sscape_sleeper, sscape_sleep_flag); #ifdef REVEAL_SPEA /* Spea and Reveal have assigned interrupt bits differently than Ensoniq */ static char valid_interrupts[] = {9, 7, 5, 15}; #else static char valid_interrupts[] = {9, 5, 7, 10}; #endif static unsigned char sscape_read (struct sscape_info *devc, int reg) { unsigned long flags; unsigned char val; DISABLE_INTR (flags); OUTB (reg, PORT (ODIE_ADDR)); val = INB (PORT (ODIE_DATA)); RESTORE_INTR (flags); return val; } static void sscape_write (struct sscape_info *devc, int reg, int data) { unsigned long flags; DISABLE_INTR (flags); OUTB (reg, PORT (ODIE_ADDR)); OUTB (data, PORT (ODIE_DATA)); RESTORE_INTR (flags); } static void host_open (struct sscape_info *devc) { OUTB (0x00, PORT (HOST_CTRL)); /* Put the board to the host mode */ } static void host_close (struct sscape_info *devc) { OUTB (0x03, PORT (HOST_CTRL)); /* Put the board to the MIDI mode */ } static int host_write (struct sscape_info *devc, unsigned char *data, int count) { unsigned long flags; int i, timeout; DISABLE_INTR (flags); /* * Send the command and data bytes */ for (i = 0; i < count; i++) { for (timeout = 10000; timeout > 0; timeout--) if (INB (PORT (HOST_CTRL)) & TX_READY) break; if (timeout <= 0) { RESTORE_INTR (flags); return 0; } OUTB (data[i], PORT (HOST_DATA)); } RESTORE_INTR (flags); return 1; } static int host_read (struct sscape_info *devc) { unsigned long flags; int timeout; unsigned char data; DISABLE_INTR (flags); /* * Read a byte */ for (timeout = 10000; timeout > 0; timeout--) if (INB (PORT (HOST_CTRL)) & RX_READY) break; if (timeout <= 0) { RESTORE_INTR (flags); return -1; } data = INB (PORT (HOST_DATA)); RESTORE_INTR (flags); return data; } static int host_command1 (struct sscape_info *devc, int cmd) { unsigned char buf[10]; buf[0] = (unsigned char) (cmd & 0xff); return host_write (devc, buf, 1); } static int host_command2 (struct sscape_info *devc, int cmd, int parm1) { unsigned char buf[10]; buf[0] = (unsigned char) (cmd & 0xff); buf[1] = (unsigned char) (parm1 & 0xff); return host_write (devc, buf, 2); } static int host_command3 (struct sscape_info *devc, int cmd, int parm1, int parm2) { unsigned char buf[10]; buf[0] = (unsigned char) (cmd & 0xff); buf[1] = (unsigned char) (parm1 & 0xff); buf[2] = (unsigned char) (parm2 & 0xff); return host_write (devc, buf, 3); } static void set_mt32 (struct sscape_info *devc, int value) { host_open (devc); host_command2 (devc, CMD_SET_MT32, value ? 1 : 0); if (host_read (devc) != CMD_ACK) { printk ("SNDSCAPE: Setting MT32 mode failed\n"); } host_close (devc); } static int get_board_type (struct sscape_info *devc) { int tmp; host_open (devc); if (!host_command1 (devc, CMD_GET_BOARD_TYPE)) tmp = -1; else tmp = host_read (devc); host_close (devc); return tmp; } void sscapeintr (INT_HANDLER_PARMS (irq, dummy)) { unsigned char bits, tmp; static int debug = 0; printk ("sscapeintr(0x%02x)\n", (bits = sscape_read (devc, GA_INTSTAT_REG))); if (SOMEONE_WAITING (sscape_sleeper, sscape_sleep_flag)) { WAKE_UP (sscape_sleeper, sscape_sleep_flag); } if (bits & 0x02) /* Host interface interrupt */ { printk ("SSCAPE: Host interrupt, data=%02x\n", host_read (devc)); } #if (!defined(EXCLUDE_MPU401) || !defined(EXCLUDE_MPU_EMU)) && !defined(EXCLUDE_MIDI) if (bits & 0x01) { mpuintr (INT_HANDLER_CALL (irq)); if (debug++ > 10) /* Temporary debugging hack */ { sscape_write (devc, GA_INTENA_REG, 0x00); /* Disable all interrupts */ } } #endif /* * Acknowledge interrupts (toggle the interrupt bits) */ tmp = sscape_read (devc, GA_INTENA_REG); sscape_write (devc, GA_INTENA_REG, (~bits & 0x0e) | (tmp & 0xf1)); } static void sscape_enable_intr (struct sscape_info *devc, unsigned intr_bits) { unsigned char temp, orig; temp = orig = sscape_read (devc, GA_INTENA_REG); temp |= intr_bits; temp |= 0x80; /* Master IRQ enable */ if (temp == orig) return; /* No change */ sscape_write (devc, GA_INTENA_REG, temp); } static void sscape_disable_intr (struct sscape_info *devc, unsigned intr_bits) { unsigned char temp, orig; temp = orig = sscape_read (devc, GA_INTENA_REG); temp &= ~intr_bits; if ((temp & ~0x80) == 0x00) temp = 0x00; /* Master IRQ disable */ if (temp == orig) return; /* No change */ sscape_write (devc, GA_INTENA_REG, temp); } static void do_dma (struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode) { unsigned char temp; if (dma_chan != SSCAPE_DMA_A) { printk ("SSCAPE: Tried to use DMA channel != A. Why?\n"); return; } DMAbuf_start_dma (devc->my_audiodev, buf, blk_size, mode); temp = devc->dma << 4; /* Setup DMA channel select bits */ if (devc->dma <= 3) temp |= 0x80; /* 8 bit DMA channel */ temp |= 1; /* Trigger DMA */ sscape_write (devc, GA_DMAA_REG, temp); temp &= 0xfe; /* Clear DMA trigger */ sscape_write (devc, GA_DMAA_REG, temp); } static int verify_mpu (struct sscape_info *devc) { /* * The SoundScape board could be in three modes (MPU, 8250 and host). * If the card is not in the MPU mode, enabling the MPU driver will * cause infinite loop (the driver believes that there is always some * received data in the buffer. * * Detect this by looking if there are more than 10 received MIDI bytes * (0x00) in the buffer. */ int i; for (i = 0; i < 10; i++) { if (INB (devc->base + HOST_CTRL) & 0x80) return 1; if (INB (devc->base) != 0x00) return 1; } printk ("SoundScape: The device is not in the MPU-401 mode\n"); return 0; } static int sscape_coproc_open (void *dev_info, int sub_device) { if (sub_device == COPR_MIDI) { set_mt32 (devc, 0); if (!verify_mpu (devc)) return RET_ERROR (EIO); } return 0; } static void sscape_coproc_close (void *dev_info, int sub_device) { struct sscape_info *devc = dev_info; unsigned long flags; DISABLE_INTR (flags); if (devc->dma_allocated) { sscape_write (devc, GA_DMAA_REG, 0x20); /* DMA channel disabled */ #ifndef EXCLUDE_NATIVE_PCM DMAbuf_close_dma (devc->my_audiodev); #endif devc->dma_allocated = 0; } RESET_WAIT_QUEUE (sscape_sleeper, sscape_sleep_flag); RESTORE_INTR (flags); return; } static void sscape_coproc_reset (void *dev_info) { } static int sscape_download_boot (struct sscape_info *devc, unsigned char *block, int size, int flag) { unsigned long flags; unsigned char temp; int done, timeout; if (flag & CPF_FIRST) { /* * First block. Have to allocate DMA and to reset the board * before continuing. */ DISABLE_INTR (flags); if (devc->dma_allocated == 0) { #ifndef EXCLUDE_NATIVE_PCM if (DMAbuf_open_dma (devc->my_audiodev) < 0) { RESTORE_INTR (flags); return 0; } #endif devc->dma_allocated = 1; } RESTORE_INTR (flags); sscape_write (devc, GA_HMCTL_REG, (temp = sscape_read (devc, GA_HMCTL_REG)) & 0x3f); /*Reset */ for (timeout = 10000; timeout > 0; timeout--) sscape_read (devc, GA_HMCTL_REG); /* Delay */ /* Take board out of reset */ sscape_write (devc, GA_HMCTL_REG, (temp = sscape_read (devc, GA_HMCTL_REG)) | 0x80); } /* * Transfer one code block using DMA */ memcpy (audio_devs[devc->my_audiodev]->dmap->raw_buf[0], block, size); DISABLE_INTR (flags); /******** INTERRUPTS DISABLED NOW ********/ do_dma (devc, SSCAPE_DMA_A, audio_devs[devc->my_audiodev]->dmap->raw_buf_phys[0], size, DMA_MODE_WRITE); /* * Wait until transfer completes. */ RESET_WAIT_QUEUE (sscape_sleeper, sscape_sleep_flag); done = 0; timeout = 100; while (!done && timeout-- > 0) { int resid; DO_SLEEP (sscape_sleeper, sscape_sleep_flag, 1); clear_dma_ff (devc->dma); if ((resid = get_dma_residue (devc->dma)) == 0) done = 1; } RESTORE_INTR (flags); if (!done) return 0; if (flag & CPF_LAST) { /* * Take the board out of reset */ OUTB (0x00, PORT (HOST_CTRL)); OUTB (0x00, PORT (MIDI_CTRL)); temp = sscape_read (devc, GA_HMCTL_REG); temp |= 0x40; sscape_write (devc, GA_HMCTL_REG, temp); /* Kickstart the board */ /* * Wait until the ODB wakes up */ DISABLE_INTR (flags); done = 0; timeout = 5 * HZ; while (!done && timeout-- > 0) { DO_SLEEP (sscape_sleeper, sscape_sleep_flag, 1); if (INB (PORT (HOST_DATA)) == 0xff) /* OBP startup acknowledge */ done = 1; } RESTORE_INTR (flags); if (!done) { printk ("SoundScape: The OBP didn't respond after code download\n"); return 0; } DISABLE_INTR (flags); done = 0; timeout = 5 * HZ; while (!done && timeout-- > 0) { DO_SLEEP (sscape_sleeper, sscape_sleep_flag, 1); if (INB (PORT (HOST_DATA)) == 0xfe) /* Host startup acknowledge */ done = 1; } RESTORE_INTR (flags); if (!done) { printk ("SoundScape: OBP Initialization failed.\n"); return 0; } printk ("SoundScape board of type %d initialized OK\n", get_board_type (devc)); #ifdef SSCAPE_DEBUG3 /* * Temporary debugging aid. Print contents of the registers after * downloading the code. */ { int i; for (i = 0; i < 13; i++) printk ("I%d = %02x (new value)\n", i, sscape_read (devc, i)); } #endif } return 1; } static int download_boot_block (void *dev_info, copr_buffer * buf) { if (buf->len <= 0 || buf->len > sizeof (buf->data)) return RET_ERROR (EINVAL); if (!sscape_download_boot (devc, buf->data, buf->len, buf->flags)) { printk ("SSCAPE: Unable to load microcode block to the OBP.\n"); return RET_ERROR (EIO); } return 0; } static int sscape_coproc_ioctl (void *dev_info, unsigned int cmd, unsigned int arg, int local) { switch (cmd) { case SNDCTL_COPR_RESET: sscape_coproc_reset (dev_info); return 0; break; case SNDCTL_COPR_LOAD: { copr_buffer *buf; int err; buf = (copr_buffer *) KERNEL_MALLOC (sizeof (copr_buffer)); IOCTL_FROM_USER ((char *) buf, (char *) arg, 0, sizeof (*buf)); err = download_boot_block (dev_info, buf); KERNEL_FREE (buf); return err; } break; default: return RET_ERROR (EINVAL); } return RET_ERROR (EINVAL); } static coproc_operations sscape_coproc_operations = { "SoundScape M68K", sscape_coproc_open, sscape_coproc_close, sscape_coproc_ioctl, sscape_coproc_reset, &dev_info }; static int sscape_audio_open (int dev, int mode) { unsigned long flags; sscape_info *devc = (sscape_info *) audio_devs[dev]->devc; DISABLE_INTR (flags); if (devc->opened) { RESTORE_INTR (flags); return RET_ERROR (EBUSY); } if (devc->dma_allocated == 0) { int err; if ((err = DMAbuf_open_dma (devc->my_audiodev)) < 0) { RESTORE_INTR (flags); return err; } devc->dma_allocated = 1; } devc->opened = 1; RESTORE_INTR (flags); #ifdef SSCAPE_DEBUG4 /* * Temporary debugging aid. Print contents of the registers * when the device is opened. */ { int i; for (i = 0; i < 13; i++) printk ("I%d = %02x\n", i, sscape_read (devc, i)); } #endif return 0; } static void sscape_audio_close (int dev) { unsigned long flags; sscape_info *devc = (sscape_info *) audio_devs[dev]->devc; DEB (printk ("sscape_audio_close(void)\n")); DISABLE_INTR (flags); if (devc->dma_allocated) { sscape_write (devc, GA_DMAA_REG, 0x20); /* DMA channel disabled */ DMAbuf_close_dma (dev); devc->dma_allocated = 0; } devc->opened = 0; RESTORE_INTR (flags); } static int set_speed (sscape_info * devc, int arg) { return 8000; } static int set_channels (sscape_info * devc, int arg) { return 1; } static int set_format (sscape_info * devc, int arg) { return AFMT_U8; } static int sscape_audio_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) { sscape_info *devc = (sscape_info *) audio_devs[dev]->devc; switch (cmd) { case SOUND_PCM_WRITE_RATE: if (local) return set_speed (devc, arg); return IOCTL_OUT (arg, set_speed (devc, IOCTL_IN (arg))); case SOUND_PCM_READ_RATE: if (local) return 8000; return IOCTL_OUT (arg, 8000); case SNDCTL_DSP_STEREO: if (local) return set_channels (devc, arg + 1) - 1; return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg) + 1) - 1); case SOUND_PCM_WRITE_CHANNELS: if (local) return set_channels (devc, arg); return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg))); case SOUND_PCM_READ_CHANNELS: if (local) return 1; return IOCTL_OUT (arg, 1); case SNDCTL_DSP_SAMPLESIZE: if (local) return set_format (devc, arg); return IOCTL_OUT (arg, set_format (devc, IOCTL_IN (arg))); case SOUND_PCM_READ_BITS: if (local) return 8; return IOCTL_OUT (arg, 8); default:; } return RET_ERROR (EINVAL); } static void sscape_audio_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart) { } static void sscape_audio_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart) { } static int sscape_audio_prepare_for_input (int dev, int bsize, int bcount) { return 0; } static int sscape_audio_prepare_for_output (int dev, int bsize, int bcount) { return 0; } static void sscape_audio_halt (int dev) { } static void sscape_audio_reset (int dev) { sscape_audio_halt (dev); } static struct audio_operations sscape_audio_operations = { "Ensoniq SoundScape channel A", 0, AFMT_U8 | AFMT_S16_LE, NULL, sscape_audio_open, sscape_audio_close, sscape_audio_output_block, sscape_audio_start_input, sscape_audio_ioctl, sscape_audio_prepare_for_input, sscape_audio_prepare_for_output, sscape_audio_reset, sscape_audio_halt, NULL, NULL }; long attach_sscape (long mem_start, struct address_info *hw_config) { int my_dev; #ifndef SSCAPE_REGS /* * Config register values for Spea/V7 Media FX and Ensoniq S-2000. * These values are card * dependent. If you have another SoundScape based card, you have to * find the correct values. Do the following: * - Compile this driver with SSCAPE_DEBUG1 defined. * - Shut down and power off your machine. * - Boot with DOS so that the SSINIT.EXE program is run. * - Warm boot to {Linux|SYSV|BSD} and write down the lines displayed * when detecting the SoundScape. * - Modify the following list to use the values printed during boot. * Undefine the SSCAPE_DEBUG1 */ #define SSCAPE_REGS { \ /* I0 */ 0x00, \ 0xf0, /* Note! Ignored. Set always to 0xf0 */ \ 0x20, /* Note! Ignored. Set always to 0x20 */ \ 0x20, /* Note! Ignored. Set always to 0x20 */ \ 0xf5, /* Ignored */ \ 0x10, \ 0x00, \ 0x2e, /* I7 MEM config A. Likely to vary between models */ \ 0x00, /* I8 MEM config A. Likely to vary between models */ \ /* I9 */ 0x40 /* Ignored */ \ } #endif unsigned long flags; static unsigned char regs[10] = SSCAPE_REGS; int i, irq_bits = 0xff; if (!probe_sscape (hw_config)) return mem_start; printk (" "); for (i = 0; i < sizeof (valid_interrupts); i++) if (hw_config->irq == valid_interrupts[i]) { irq_bits = i; break; } if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff)) { printk ("Invalid IRQ%d\n", hw_config->irq); return mem_start; } DISABLE_INTR (flags); for (i = 1; i < 10; i++) switch (i) { case 1: /* Host interrupt enable */ sscape_write (devc, i, 0xf0); /* All interrupts enabled */ break; case 2: /* DMA A status/trigger register */ case 3: /* DMA B status/trigger register */ sscape_write (devc, i, 0x20); /* DMA channel disabled */ break; case 4: /* Host interrupt config reg */ sscape_write (devc, i, 0xf0 | (irq_bits << 2) | irq_bits); break; case 5: /* Don't destroy CD-ROM DMA config bits (0xc0) */ sscape_write (devc, i, (regs[i] & 0x3f) | (sscape_read (devc, i) & 0x0c)); break; case 6: /* CD-ROM config. Don't touch. */ break; case 9: /* Master control reg. Don't modify CR-ROM bits. Disable SB emul */ sscape_write (devc, i, (sscape_read (devc, i) & 0xf0) | 0x00); break; default: sscape_write (devc, i, regs[i]); } RESTORE_INTR (flags); #ifdef SSCAPE_DEBUG2 /* * Temporary debugging aid. Print contents of the registers after * changing them. */ { int i; for (i = 0; i < 13; i++) printk ("I%d = %02x (new value)\n", i, sscape_read (devc, i)); } #endif #if !defined(EXCLUDE_MIDI) && !defined(EXCLUDE_MPU_EMU) hw_config->always_detect = 1; if (probe_mpu401 (hw_config)) { int prev_devs; prev_devs = num_midis; mem_start = attach_mpu401 (mem_start, hw_config); if (num_midis == (prev_devs + 1)) /* The MPU driver installed itself */ midi_devs[prev_devs]->coproc = &sscape_coproc_operations; } #endif #ifndef EXCLUDE_NATIVE_PCM /* Not supported yet */ #ifndef EXCLUDE_AUDIO if (num_audiodevs < MAX_AUDIO_DEV) { audio_devs[my_dev = num_audiodevs++] = &sscape_audio_operations; audio_devs[my_dev]->dmachan = hw_config->dma; audio_devs[my_dev]->buffcount = 1; audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; audio_devs[my_dev]->devc = devc; devc->my_audiodev = my_dev; devc->opened = 0; audio_devs[my_dev]->coproc = &sscape_coproc_operations; if (snd_set_irq_handler (hw_config->irq, sscapeintr, "SoundScape") < 0) printk ("Error: Can't allocate IRQ for SoundScape\n"); sscape_write (devc, GA_INTENA_REG, 0x80); /* Master IRQ enable */ } else printk ("SoundScape: More than enough audio devices detected\n"); #endif #endif devc->ok = 1; return mem_start; } int probe_sscape (struct address_info *hw_config) { unsigned char save; devc->base = hw_config->io_base; devc->irq = hw_config->irq; devc->dma = hw_config->dma; /* * First check that the address register of "ODIE" is * there and that it has exactly 4 writeable bits. * First 4 bits */ if ((save = INB (PORT (ODIE_ADDR))) & 0xf0) return 0; OUTB (0x00, PORT (ODIE_ADDR)); if (INB (PORT (ODIE_ADDR)) != 0x00) return 0; OUTB (0xff, PORT (ODIE_ADDR)); if (INB (PORT (ODIE_ADDR)) != 0x0f) return 0; OUTB (save, PORT (ODIE_ADDR)); /* * Now verify that some indirect registers return zero on some bits. * This may break the driver with some future revisions of "ODIE" but... */ if (sscape_read (devc, 0) & 0x0c) return 0; if (sscape_read (devc, 1) & 0x0f) return 0; if (sscape_read (devc, 5) & 0x0f) return 0; #ifdef SSCAPE_DEBUG1 /* * Temporary debugging aid. Print contents of the registers before * changing them. */ { int i; for (i = 0; i < 13; i++) printk ("I%d = %02x (old value)\n", i, sscape_read (devc, i)); } #endif return 1; } int probe_ss_ms_sound (struct address_info *hw_config) { int i, irq_bits = 0xff; if (devc->ok == 0) { printk ("SoundScape: Invalid initialization order.\n"); return 0; } for (i = 0; i < sizeof (valid_interrupts); i++) if (hw_config->irq == valid_interrupts[i]) { irq_bits = i; break; } #ifdef REVEAL_SPEA { int tmp, status = 0; int cc; if (!((tmp = sscape_read (devc, GA_HMCTL_REG)) & 0xc0)) { sscape_write (devc, GA_HMCTL_REG, tmp | 0x80); for (cc = 0; cc < 200000; ++cc) INB (devc->base + ODIE_ADDR); } } #endif if (hw_config->irq > 15 || irq_bits == 0xff) { printk ("SoundScape: Invalid MSS IRQ%d\n", hw_config->irq); return 0; } return ad1848_detect (hw_config->io_base); } long attach_ss_ms_sound (long mem_start, struct address_info *hw_config) { /* * This routine configures the SoundScape card for use with the * Win Sound System driver. The AD1848 codec interface uses the CD-ROM * config registers of the "ODIE". */ int i, irq_bits = 0xff; #ifdef EXCLUDE_NATIVE_PCM int prev_devs = num_audiodevs; #endif /* * Setup the DMA polarity. */ sscape_write (devc, GA_DMACFG_REG, 0x50); /* * Take the gate-arry off of the DMA channel. */ sscape_write (devc, GA_DMAB_REG, 0x20); /* * Init the AD1848 (CD-ROM) config reg. */ for (i = 0; i < sizeof (valid_interrupts); i++) if (hw_config->irq == valid_interrupts[i]) { irq_bits = i; break; } sscape_write (devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) | (irq_bits << 1)); if (hw_config->irq == devc->irq) printk ("SoundScape: Warning! The WSS mode can't share IRQ with MIDI\n"); ad1848_init ("SoundScape", hw_config->io_base, hw_config->irq, hw_config->dma, hw_config->dma); #ifdef EXCLUDE_NATIVE_PCM if (num_audiodevs == (prev_devs + 1)) /* The AD1848 driver installed itself */ audio_devs[prev_devs]->coproc = &sscape_coproc_operations; #endif #ifdef SSCAPE_DEBUG5 /* * Temporary debugging aid. Print contents of the registers * after the AD1848 device has been initialized. */ { int i; for (i = 0; i < 13; i++) printk ("I%d = %02x\n", i, sscape_read (devc, i)); } #endif return mem_start; } #endif Index: head/sys/pc98/pc98/sound/sys_timer.c =================================================================== --- head/sys/pc98/pc98/sound/sys_timer.c (revision 18264) +++ head/sys/pc98/pc98/sound/sys_timer.c (revision 18265) @@ -1,304 +1,304 @@ /* * sound/sys_timer.c * * The default timer for the Level 2 sequencer interface * Uses the (100HZ) timer of kernel. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #define SEQUENCER_C -#include "sound_config.h" +#include #ifdef CONFIGURE_SOUNDCARD #ifndef EXCLUDE_SEQUENCER static volatile int opened = 0, tmr_running = 0; static volatile time_t tmr_offs, tmr_ctr; static volatile unsigned long ticks_offs; static volatile int curr_tempo, curr_timebase; static volatile unsigned long curr_ticks; static volatile unsigned long next_event_time; static unsigned long prev_event_time; static void poll_def_tmr (unsigned long dummy); DEFINE_TIMER (def_tmr, poll_def_tmr); static unsigned long tmr2ticks (int tmr_value) { /* * Convert system timer ticks (HZ) to MIDI ticks */ unsigned long tmp; unsigned long scale; tmp = (tmr_value * 1000) / HZ; /* Convert to msecs */ scale = (60 * 1000) / (curr_tempo * curr_timebase); /* msecs per MIDI tick */ return (tmp + (scale / 2)) / scale; } static void poll_def_tmr (unsigned long dummy) { if (opened) { ACTIVATE_TIMER (def_tmr, poll_def_tmr, 1); if (tmr_running) { tmr_ctr++; curr_ticks = ticks_offs + tmr2ticks (tmr_ctr); if (curr_ticks >= next_event_time) { next_event_time = 0xffffffff; sequencer_timer (); } } } } static void tmr_reset (void) { unsigned long flags; DISABLE_INTR (flags); tmr_offs = 0; ticks_offs = 0; tmr_ctr = 0; next_event_time = 0xffffffff; prev_event_time = 0; curr_ticks = 0; RESTORE_INTR (flags); } static int def_tmr_open (int dev, int mode) { if (opened) return RET_ERROR (EBUSY); tmr_reset (); curr_tempo = 60; curr_timebase = HZ; opened = 1; ACTIVATE_TIMER (def_tmr, poll_def_tmr, 1); return 0; } static void def_tmr_close (int dev) { opened = tmr_running = 0; } static int def_tmr_event (int dev, unsigned char *event) { unsigned char cmd = event[1]; unsigned long parm = *(int *) &event[4]; switch (cmd) { case TMR_WAIT_REL: parm += prev_event_time; case TMR_WAIT_ABS: if (parm > 0) { long time; if (parm <= curr_ticks) /* It's the time */ return TIMER_NOT_ARMED; time = parm; next_event_time = prev_event_time = time; return TIMER_ARMED; } break; case TMR_START: tmr_reset (); tmr_running = 1; break; case TMR_STOP: tmr_running = 0; break; case TMR_CONTINUE: tmr_running = 1; break; case TMR_TEMPO: if (parm) { if (parm < 8) parm = 8; if (parm > 250) parm = 250; tmr_offs = tmr_ctr; ticks_offs += tmr2ticks (tmr_ctr); tmr_ctr = 0; curr_tempo = parm; } break; case TMR_ECHO: seq_copy_to_input (event, 8); break; default:; } return TIMER_NOT_ARMED; } static unsigned long def_tmr_get_time (int dev) { if (!opened) return 0; return curr_ticks; } static int def_tmr_ioctl (int dev, unsigned int cmd, unsigned int arg) { switch (cmd) { case SNDCTL_TMR_SOURCE: return IOCTL_OUT (arg, TMR_INTERNAL); break; case SNDCTL_TMR_START: tmr_reset (); tmr_running = 1; return 0; break; case SNDCTL_TMR_STOP: tmr_running = 0; return 0; break; case SNDCTL_TMR_CONTINUE: tmr_running = 1; return 0; break; case SNDCTL_TMR_TIMEBASE: { int val = IOCTL_IN (arg); if (val) { if (val < 1) val = 1; if (val > 1000) val = 1000; curr_timebase = val; } return IOCTL_OUT (arg, curr_timebase); } break; case SNDCTL_TMR_TEMPO: { int val = IOCTL_IN (arg); if (val) { if (val < 8) val = 8; if (val > 250) val = 250; tmr_offs = tmr_ctr; ticks_offs += tmr2ticks (tmr_ctr); tmr_ctr = 0; curr_tempo = val; } return IOCTL_OUT (arg, curr_tempo); } break; case SNDCTL_SEQ_CTRLRATE: if (IOCTL_IN (arg) != 0) /* Can't change */ return RET_ERROR (EINVAL); return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60); break; case SNDCTL_TMR_METRONOME: /* NOP */ break; default:; } return RET_ERROR (EINVAL); } static void def_tmr_arm (int dev, long time) { if (time < 0) time = curr_ticks + 1; else if (time <= curr_ticks) /* It's the time */ return; next_event_time = prev_event_time = time; return; } struct sound_timer_operations default_sound_timer = { {"System Timer", 0}, 0, /* Priority */ 0, /* Local device link */ def_tmr_open, def_tmr_close, def_tmr_event, def_tmr_get_time, def_tmr_ioctl, def_tmr_arm }; #endif #endif Index: head/sys/pc98/pc98/sound/trix.c =================================================================== --- head/sys/pc98/pc98/sound/trix.c (revision 18264) +++ head/sys/pc98/pc98/sound/trix.c (revision 18265) @@ -1,323 +1,323 @@ /* * sound/trix.c * * Low level driver for the MediaTriX AudioTriX Pro * (MT-0002-PC Control Chip) * * Copyright by Hannu Savolainen 1995 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_TRIX) #ifdef INCLUDE_TRIX_BOOT -#include "trix_boot.h" +#include #endif static int kilroy_was_here = 0; /* Don't detect twice */ static int sb_initialized = 0; static int mpu_initialized = 0; static unsigned char trix_read (int addr) { OUTB ((unsigned char) addr, 0x390); /* MT-0002-PC ASIC address */ return INB (0x391); /* MT-0002-PC ASIC data */ } static void trix_write (int addr, int data) { OUTB ((unsigned char) addr, 0x390); /* MT-0002-PC ASIC address */ OUTB ((unsigned char) data, 0x391); /* MT-0002-PC ASIC data */ } static void download_boot (int base) { int i = 0, n = sizeof (trix_boot); trix_write (0xf8, 0x00); /* ??????? */ OUTB (0x01, base + 6); /* Clear the internal data pointer */ OUTB (0x00, base + 6); /* Restart */ /* * Write the boot code to the RAM upload/download register. * Each write increments the internal data pointer. */ OUTB (0x01, base + 6); /* Clear the internal data pointer */ OUTB (0x1A, 0x390); /* Select RAM download/upload port */ for (i = 0; i < n; i++) OUTB (trix_boot[i], 0x391); for (i = n; i < 10016; i++) /* Clear up to first 16 bytes of data RAM */ OUTB (0x00, 0x391); OUTB (0x00, base + 6); /* Reset */ OUTB (0x50, 0x390); /* ?????? */ } static int trix_set_wss_port (struct address_info *hw_config) { unsigned char addr_bits; if (kilroy_was_here) /* Already initialized */ return 0; kilroy_was_here = 1; if (trix_read (0x15) != 0x71) /* No asic signature */ return 0; /* * Disable separate wave playback and recording DMA channels since * the driver doesn't support duplex mode yet. */ trix_write (0x13, trix_read (0x13) & ~0x80); trix_write (0x14, trix_read (0x14) & ~0x80); /* * Configure the ASIC to place the codec to the proper I/O location */ switch (hw_config->io_base) { case 0x530: addr_bits = 0; break; case 0x604: addr_bits = 1; break; case 0xE80: addr_bits = 2; break; case 0xF40: addr_bits = 3; break; default: return 0; } trix_write (0x19, (trix_read (0x19) & 0x03) | addr_bits); return 1; } /* * Probe and attach routines for the Windows Sound System mode of * AudioTriX Pro */ int probe_trix_wss (struct address_info *hw_config) { /* * Check if the IO port returns valid signature. The original MS Sound * system returns 0x04 while some cards (AudioTriX Pro for example) * return 0x00. */ if (!trix_set_wss_port (hw_config)) return 0; if ((INB (hw_config->io_base + 3) & 0x3f) != 0x00) { DDB (printk ("No MSS signature detected on port 0x%x\n", hw_config->io_base)); return 0; } if (hw_config->irq > 11) { printk ("AudioTriX: Bad WSS IRQ %d\n", hw_config->irq); return 0; } if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) { printk ("AudioTriX: Bad WSS DMA %d\n", hw_config->dma); return 0; } /* * Check that DMA0 is not in use with a 8 bit board. */ if (hw_config->dma == 0 && INB (hw_config->io_base + 3) & 0x80) { printk ("AudioTriX: Can't use DMA0 with a 8 bit card\n"); return 0; } if (hw_config->irq > 7 && hw_config->irq != 9 && INB (hw_config->io_base + 3) & 0x80) { printk ("AudioTriX: Can't use IRQ%d with a 8 bit card\n", hw_config->irq); return 0; } return ad1848_detect (hw_config->io_base + 4); } long attach_trix_wss (long mem_start, struct address_info *hw_config) { static unsigned char interrupt_bits[12] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20}; char bits; static unsigned char dma_bits[4] = {1, 2, 0, 3}; int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3; if (!kilroy_was_here) return mem_start; /* * Set the IRQ and DMA addresses. */ bits = interrupt_bits[hw_config->irq]; if (bits == -1) return mem_start; OUTB (bits | 0x40, config_port); if ((INB (version_port) & 0x40) == 0) printk ("[IRQ Conflict?]"); OUTB (bits | dma_bits[hw_config->dma], config_port); /* Write IRQ+DMA setup */ ad1848_init ("AudioTriX Pro", hw_config->io_base + 4, hw_config->irq, hw_config->dma, hw_config->dma); return mem_start; } int probe_trix_sb (struct address_info *hw_config) { int tmp; unsigned char conf; static char irq_translate[] = {-1, -1, -1, 0, 1, 2, -1, 3}; #ifndef INCLUDE_TRIX_BOOT return 0; /* No boot code -> no fun */ #endif if (!kilroy_was_here) return 0; /* AudioTriX Pro has not been detected earlier */ if (sb_initialized) return 0; if (hw_config->io_base & 0xffffff8f != 0x200) return 0; tmp = hw_config->irq; if (tmp > 7) return 0; if (irq_translate[tmp] == -1) return 0; tmp = hw_config->dma; if (tmp != 1 && tmp != 3) return 0; conf = 0x84; /* DMA and IRQ enable */ conf |= hw_config->io_base & 0x70; /* I/O address bits */ conf |= irq_translate[hw_config->irq]; if (hw_config->dma == 3) conf |= 0x08; trix_write (0x1b, conf); download_boot (hw_config->io_base); sb_initialized = 1; return 1; } long attach_trix_sb (long mem_start, struct address_info *hw_config) { printk (" "); return mem_start; } long attach_trix_mpu (long mem_start, struct address_info *hw_config) { return attach_mpu401 (mem_start, hw_config); } int probe_trix_mpu (struct address_info *hw_config) { unsigned char conf; static char irq_bits[] = {-1, -1, -1, 1, 2, 3, -1, 4, -1, 5}; if (!kilroy_was_here) return 0; /* AudioTriX Pro has not been detected earlier */ if (!sb_initialized) return 0; if (mpu_initialized) return 0; if (hw_config->irq > 9) return 0; if (irq_bits[hw_config->irq] == -1) return 0; switch (hw_config->io_base) { case 0x330: conf = 0x00; break; case 0x370: conf = 0x04; break; case 0x3b0: conf = 0x08; break; case 0x3f0: conf = 0x0c; break; default: return 0; /* Invalid port */ } conf |= irq_bits[hw_config->irq] << 4; trix_write (0x19, (trix_read (0x19) & 0x83) | conf); mpu_initialized = 1; return probe_mpu401 (hw_config); } #endif Index: head/sys/pc98/pc98/sound/uart6850.c =================================================================== --- head/sys/pc98/pc98/sound/uart6850.c (revision 18264) +++ head/sys/pc98/pc98/sound/uart6850.c (revision 18265) @@ -1,327 +1,327 @@ /* * sound/uart6850.c * * Copyright by Hannu Savolainen 1993 * * Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl: * added 6850 support, used with COVOX SoundMaster II and custom cards. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include "sound_config.h" +#include #ifdef CONFIGURE_SOUNDCARD #if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI) #define DATAPORT (uart6850_base) /* * * * Midi6850 Data I/O Port on IBM * */ #define COMDPORT (uart6850_base+1) /* * * * Midi6850 Command Port on IBM */ #define STATPORT (uart6850_base+1) /* * * * Midi6850 Status Port on IBM */ #define uart6850_status() INB(STATPORT) #define input_avail() ((uart6850_status()&INPUT_AVAIL)) #define output_ready() ((uart6850_status()&OUTPUT_READY)) #define uart6850_cmd(cmd) OUTB(cmd, COMDPORT) #define uart6850_read() INB(DATAPORT) #define uart6850_write(byte) OUTB(byte, DATAPORT) #define OUTPUT_READY 0x02 /* * * * Mask for Data Read Ready Bit */ #define INPUT_AVAIL 0x01 /* * * * Mask for Data Send Ready Bit */ #define UART_RESET 0x95 /* * * * 6850 Total Reset Command */ #define UART_MODE_ON 0x03 /* * * * 6850 Send/Receive UART Mode */ static int uart6850_opened = 0; static int uart6850_base = 0x330; static int uart6850_irq; static int uart6850_detected = 0; static int my_dev; static int reset_uart6850 (void); static void (*midi_input_intr) (int dev, unsigned char data); static void uart6850_input_loop (void) { int count; count = 10; while (count) /* * Not timed out */ if (input_avail ()) { unsigned char c = uart6850_read (); count = 100; if (uart6850_opened & OPEN_READ) midi_input_intr (my_dev, c); } else while (!input_avail () && count) count--; } void m6850intr (int unit) { if (input_avail ()) uart6850_input_loop (); } /* * It looks like there is no input interrupts in the UART mode. Let's try * polling. */ static void poll_uart6850 (unsigned long dummy) { unsigned long flags; DEFINE_TIMER (uart6850_timer, poll_uart6850); if (!(uart6850_opened & OPEN_READ)) return; /* * No longer required */ DISABLE_INTR (flags); if (input_avail ()) uart6850_input_loop (); ACTIVATE_TIMER (uart6850_timer, poll_uart6850, 1); /* * Come back later */ RESTORE_INTR (flags); } static int uart6850_open (int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev) ) { if (uart6850_opened) { printk ("Midi6850: Midi busy\n"); return RET_ERROR (EBUSY); } uart6850_cmd (UART_RESET); uart6850_input_loop (); midi_input_intr = input; uart6850_opened = mode; poll_uart6850 (0); /* * Enable input polling */ return 0; } static void uart6850_close (int dev) { uart6850_cmd (UART_MODE_ON); uart6850_opened = 0; } static int uart6850_out (int dev, unsigned char midi_byte) { int timeout; unsigned long flags; /* * Test for input since pending input seems to block the output. */ DISABLE_INTR (flags); if (input_avail ()) uart6850_input_loop (); RESTORE_INTR (flags); /* * Sometimes it takes about 13000 loops before the output becomes ready * (After reset). Normally it takes just about 10 loops. */ for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /* * Wait */ if (!output_ready ()) { printk ("Midi6850: Timeout\n"); return 0; } uart6850_write (midi_byte); return 1; } static int uart6850_command (int dev, unsigned char *midi_byte) { return 1; } static int uart6850_start_read (int dev) { return 0; } static int uart6850_end_read (int dev) { return 0; } static int uart6850_ioctl (int dev, unsigned cmd, unsigned arg) { return RET_ERROR (EINVAL); } static void uart6850_kick (int dev) { } static int uart6850_buffer_status (int dev) { return 0; /* * No data in buffers */ } #define MIDI_SYNTH_NAME "6850 UART Midi" #define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" +#include static struct midi_operations uart6850_operations = { {"6850 UART", 0, 0, SNDCARD_UART6850}, &std_midi_synth, {0}, uart6850_open, uart6850_close, uart6850_ioctl, uart6850_out, uart6850_start_read, uart6850_end_read, uart6850_kick, uart6850_command, uart6850_buffer_status }; long attach_uart6850 (long mem_start, struct address_info *hw_config) { int ok, timeout; unsigned long flags; if (num_midis >= MAX_MIDI_DEV) { printk ("Sound: Too many midi devices detected\n"); return mem_start; } uart6850_base = hw_config->io_base; uart6850_irq = hw_config->irq; if (!uart6850_detected) return RET_ERROR (EIO); DISABLE_INTR (flags); for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* * Wait */ uart6850_cmd (UART_MODE_ON); ok = 1; RESTORE_INTR (flags); #if defined(__FreeBSD__) printk ("uart0: <6850 Midi Interface>"); #else printk (" <6850 Midi Interface>"); #endif std_midi_synth.midi_dev = my_dev = num_midis; midi_devs[num_midis++] = &uart6850_operations; return mem_start; } static int reset_uart6850 (void) { uart6850_read (); return 1; /* * OK */ } int probe_uart6850 (struct address_info *hw_config) { int ok = 0; uart6850_base = hw_config->io_base; uart6850_irq = hw_config->irq; if (snd_set_irq_handler (uart6850_irq, m6850intr, "MIDI6850") < 0) return 0; ok = reset_uart6850 (); uart6850_detected = ok; return ok; } #endif #endif Index: head/sys/pc98/pc98/syscons.c =================================================================== --- head/sys/pc98/pc98/syscons.c (revision 18264) +++ head/sys/pc98/pc98/syscons.c (revision 18265) @@ -1,4592 +1,4631 @@ /*- * Copyright (c) 1992-1995 Sen Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software withough specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: syscons.c,v 1.166 1996/09/06 23:35:54 pst Exp $ + * $Id: syscons.c,v 1.8 1996/09/10 09:38:39 asami Exp $ */ #include "sc.h" #include "apm.h" #include "opt_ddb.h" #include "opt_syscons.h" #if NSC > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PC98 #define KANJI #include #include #include #include #include #else #include #include #include #include #include #endif #if defined(PC98) && defined(LINE30) #include "30line.h" #endif #if !defined(MAXCONS) #define MAXCONS 16 #endif #define COLD 0 #define WARM 1 #define RUNNING 2 /* this may break on older VGA's but is usefull on real 32 bit systems */ #define bcopyw bcopy static default_attr user_default = { (FG_LIGHTGREY | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; #ifdef PC98 static default_attr kernel_default = { (FG_LIGHTGREY | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; #else static default_attr kernel_default = { (FG_WHITE | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; #endif static scr_stat main_console; static scr_stat *console[MAXCONS]; #ifdef DEVFS static void *sc_devfs_token[MAXCONS]; #endif scr_stat *cur_console; static scr_stat *new_scp, *old_scp; static term_stat kernel_console; static default_attr *current_default; static int flags = 0; static char init_done = COLD; static char switch_in_progress = FALSE; static char write_in_progress = FALSE; static char blink_in_progress = FALSE; static int blinkrate = 0; #ifndef PC98 u_int crtc_addr = MONO_BASE; #endif char crtc_vga = FALSE; static u_char shfts = 0, ctls = 0, alts = 0, agrs = 0, metas = 0; #ifdef PC98 static u_char nlkcnt = 0, slkcnt = 0, alkcnt = 0; #else static u_char nlkcnt = 0, clkcnt = 0, slkcnt = 0, alkcnt = 0; #endif char *font_8 = NULL, *font_14 = NULL, *font_16 = NULL; int fonts_loaded = 0; char *palette; static const u_int n_fkey_tab = sizeof(fkey_tab) / sizeof(*fkey_tab); static int delayed_next_scr = FALSE; static long scrn_blank_time = 0; /* screen saver timeout value */ int scrn_blanked = FALSE; /* screen saver active flag */ static long scrn_time_stamp; u_char scr_map[256]; u_char scr_rmap[256]; char *video_mode_ptr = NULL; static char *cut_buffer; static u_short mouse_and_mask[16] = { 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80, 0xfe00, 0x1e00, 0x1f00, 0x0f00, 0x0f00, 0x0000, 0x0000, 0x0000 }; static u_short mouse_or_mask[16] = { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x6800, 0x0c00, 0x0c00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000 }; static void none_saver(int blank) { } void (*current_saver) __P((int blank)) = none_saver; /* OS specific stuff */ #ifdef not_yet_done #define VIRTUAL_TTY(x) (sccons[x] = ttymalloc(sccons[x])) struct CONSOLE_TTY (sccons[MAXCONS] = ttymalloc(sccons[MAXCONS])) -static const int nsccons = MAXCONS+1; -struct tty *sccons[MAXCONS+1]; +struct MOUSE_TTY (sccons[MAXCONS+1] = ttymalloc(sccons[MAXCONS+1])) +struct tty *sccons[MAXCONS+2]; #else #define VIRTUAL_TTY(x) &sccons[x] #define CONSOLE_TTY &sccons[MAXCONS] -static struct tty sccons[MAXCONS+1]; +#define MOUSE_TTY &sccons[MAXCONS+1] +static struct tty sccons[MAXCONS+2]; #endif +#define SC_MOUSE 128 +#define SC_CONSOLE 255 #ifdef PC98 static u_char default_kanji = UJIS; u_short *Crtat; u_short *Atrat; #else #define MONO_BUF pa_to_va(0xB0000) #define CGA_BUF pa_to_va(0xB8000) u_short *Crtat; #endif +static const int nsccons = MAXCONS+2; #define WRAPHIST(scp, pointer, offset)\ ((scp->history) + ((((pointer) - (scp->history)) + (scp->history_size)\ + (offset)) % (scp->history_size))) #ifdef PC98 #define WRAPHIST_A(scp, pointer, offset)\ ((scp->his_atr) + ((((pointer) - (scp->his_atr)) + (scp->history_size)\ + (offset)) % (scp->history_size))) #endif /* prototypes */ static int scattach(struct isa_device *dev); static int scparam(struct tty *tp, struct termios *t); static int scprobe(struct isa_device *dev); static void scstart(struct tty *tp); +static void scmousestart(struct tty *tp); static void scinit(void); static u_int scgetc(int noblock); static scr_stat *get_scr_stat(dev_t dev); static scr_stat *alloc_scp(void); static void init_scp(scr_stat *scp); static int get_scr_num(void); static void scrn_timer(void); static void clear_screen(scr_stat *scp); static int switch_scr(scr_stat *scp, u_int next_scr); static void exchange_scr(void); static inline void move_crsr(scr_stat *scp, int x, int y); static void scan_esc(scr_stat *scp, u_char c); static void draw_cursor_image(scr_stat *scp); static void remove_cursor_image(scr_stat *scp); static void ansi_put(scr_stat *scp, u_char *buf, int len); static u_char *get_fstr(u_int c, u_int *len); static void update_leds(int which); static void history_to_screen(scr_stat *scp); static int history_up_line(scr_stat *scp); static int history_down_line(scr_stat *scp); static void kbd_wait(void); static void kbd_cmd(u_char command); static void set_vgaregs(char *modetable); static void set_font_mode(void); static void set_normal_mode(void); static void set_destructive_cursor(scr_stat *scp); static void set_mouse_pos(scr_stat *scp); static void mouse_cut_start(scr_stat *scp); static void mouse_cut_end(scr_stat *scp); static void mouse_paste(scr_stat *scp); static void draw_mouse_image(scr_stat *scp); static void remove_mouse_image(scr_stat *scp); static void draw_cutmarking(scr_stat *scp); static void remove_cutmarking(scr_stat *scp); static void save_palette(void); static void do_bell(scr_stat *scp, int pitch, int duration); static void blink_screen(scr_stat *scp); struct isa_driver scdriver = { scprobe, scattach, "sc", 1 }; static d_open_t scopen; static d_close_t scclose; static d_read_t scread; static d_write_t scwrite; static d_ioctl_t scioctl; static d_devtotty_t scdevtotty; static d_mmap_t scmmap; #define CDEV_MAJOR 12 static struct cdevsw scdevsw = { scopen, scclose, scread, scwrite, scioctl, nullstop, noreset, scdevtotty, ttselect, scmmap, nostrategy, "sc", NULL, -1 }; #ifdef PC98 static u_char ibmpc_to_pc98[16] = { 0x01,0x21,0x81,0xa1,0x41,0x61,0xc1,0xe1, 0x09,0x29,0x89,0xa9,0x49,0x69,0xc9,0xe9 }; static u_char ibmpc_to_pc98rev[16] = { 0x05,0x25,0x85,0xa5,0x45,0x65,0xc5,0xe5, 0x0d,0x2d,0x8d,0xad,0x4d,0x6d,0xcd,0xed }; unsigned int at2pc98(unsigned int attr) { unsigned char fg_at, bg_at; unsigned int at; fg_at = ((attr >> 8) & 0x0F); bg_at = ((attr >> 8) & 0xF0); if (bg_at) { if (bg_at & 0x80) { if (bg_at & 0x70) { /* reverse & blink */ at = ibmpc_to_pc98rev[bg_at >> 4] | 0x02; } else { /* normal & blink */ at = ibmpc_to_pc98[fg_at] | 0x02; } } else { /* reverse */ at = ibmpc_to_pc98rev[bg_at >> 4]; } } else { /* normal */ at = ibmpc_to_pc98[fg_at]; } at |= ((fg_at|bg_at) << 8); return (at); } #endif /* * Calculate hardware attributes word using logical attributes mask and * hardware colors */ static int mask2attr(struct term_stat *term) { int attr, mask = term->attr_mask; if (mask & REVERSE_ATTR) { attr = ((mask & FOREGROUND_CHANGED) ? ((term->cur_color & 0xF000) >> 4) : (term->rev_color & 0x0F00)) | ((mask & BACKGROUND_CHANGED) ? ((term->cur_color & 0x0F00) << 4) : (term->rev_color & 0xF000)); } else attr = term->cur_color; /* XXX: underline mapping for Hercules adapter can be better */ if (mask & (BOLD_ATTR | UNDERLINE_ATTR)) attr ^= 0x0800; if (mask & BLINK_ATTR) attr ^= 0x8000; return attr; } static int scprobe(struct isa_device *dev) { #ifdef PC98 return(16); #else int i, j, retries = 5; unsigned char val; /* Enable interrupts and keyboard controller */ kbd_wait(); outb(KB_STAT, KB_WRITE); kbd_wait(); outb(KB_DATA, KB_MODE); /* flush any noise in the buffer */ while (inb(KB_STAT) & KB_BUF_FULL) { DELAY(100); (void) inb(KB_DATA); } /* Reset keyboard hardware */ while (retries--) { kbd_wait(); outb(KB_DATA, KB_RESET); for (i=0; i<10000; i++) { DELAY(100); val = inb(KB_DATA); if (val == KB_ACK || val == KB_ECHO) goto gotres; if (val == KB_RESEND) break; } } gotres: if (retries < 0) { printf("scprobe: keyboard won't accept RESET command\n"); #ifdef SC_KBD_PROBE_WORKS return (0); #endif } else { i = 10; /* At most 10 retries. */ gotack: DELAY(100); j = 1000; /* Wait at most 1 s. */ while ((inb(KB_STAT) & KB_BUF_FULL) == 0 && --j > 0) DELAY(1000); DELAY(1000); val = inb(KB_DATA); if (val == KB_ACK && --i > 0) goto gotack; if (val != KB_RESET_DONE) { printf("scprobe: keyboard RESET failed (result = 0x%02x)\n", val); #ifdef SC_KBD_PROBE_WORKS return (0); #endif } } #ifdef XT_KEYBOARD kbd_wait(); outb(KB_DATA, 0xF0); kbd_wait(); outb(KB_DATA, 1); kbd_wait(); #endif /* XT_KEYBOARD */ return (IO_KBDSIZE); #endif } #if NAPM > 0 static int scresume(void *dummy) { shfts = ctls = alts = agrs = metas = 0; return 0; } #endif /* * These functions need to be before calls to them so they can be inlined. */ static inline void draw_cursor_image(scr_stat *scp) { u_short cursor_image, *ptr; #ifdef PC98 int pos = scp->cursor_pos - scp->scr_buf; while((inb(TEXT_GDC + 0) & 0x04) == 0) {} outb(TEXT_GDC + 2, 0x49); /* CSRW */ outb(TEXT_GDC + 0, pos & 0xff); /* EADl */ outb(TEXT_GDC + 0, pos >> 8); /* EADh */ #else ptr = Crtat + (scp->cursor_pos - scp->scr_buf); /* do we have a destructive cursor ? */ if (flags & CHAR_CURSOR) { cursor_image = *scp->cursor_pos; scp->cursor_saveunder = cursor_image; /* modify cursor_image */ if (!(flags & BLINK_CURSOR)||((flags & BLINK_CURSOR)&&(blinkrate & 4))){ set_destructive_cursor(scp); cursor_image &= 0xff00; cursor_image |= DEAD_CHAR; } } else { cursor_image = (*(ptr) & 0x00ff) | *(scp->cursor_pos) & 0xff00; scp->cursor_saveunder = cursor_image; if (!(flags & BLINK_CURSOR)||((flags & BLINK_CURSOR)&&(blinkrate & 4))){ if ((cursor_image & 0x7000) == 0x7000) { cursor_image &= 0x8fff; if(!(cursor_image & 0x0700)) cursor_image |= 0x0700; } else { cursor_image |= 0x7000; if ((cursor_image & 0x0700) == 0x0700) cursor_image &= 0xf0ff; } } } *ptr = cursor_image; #endif } static inline void remove_cursor_image(scr_stat *scp) { #ifndef PC98 /* u_short cursor_image, *ptr; ptr = Crtat + (scp->cursor_oldpos - scp->scr_buf); cursor_image = scp->cursor_saveunder; *ptr = cursor_image; SOS */ *(Crtat + (scp->cursor_oldpos - scp->scr_buf)) = scp->cursor_saveunder; #endif } static inline void move_crsr(scr_stat *scp, int x, int y) { if (x < 0) x = 0; if (y < 0) y = 0; if (x >= scp->xsize) x = scp->xsize-1; if (y >= scp->ysize) y = scp->ysize-1; scp->xpos = x; scp->ypos = y; scp->cursor_pos = scp->scr_buf + scp->ypos * scp->xsize + scp->xpos; #ifdef PC98 scp->cursor_atr = scp->atr_buf + scp->ypos * scp->xsize + scp->xpos; #endif } static int scattach(struct isa_device *dev) { scr_stat *scp; dev_t cdev = makedev(CDEV_MAJOR, 0); #ifdef DEVFS int vc; #endif scinit(); flags = dev->id_flags; scp = console[0]; #ifndef PC98 if (crtc_vga) { cut_buffer = (char *)malloc(scp->xsize*scp->ysize, M_DEVBUF, M_NOWAIT); font_8 = (char *)malloc(8*256, M_DEVBUF, M_NOWAIT); font_14 = (char *)malloc(14*256, M_DEVBUF, M_NOWAIT); font_16 = (char *)malloc(16*256, M_DEVBUF, M_NOWAIT); copy_font(SAVE, FONT_16, font_16); fonts_loaded = FONT_16; scp->font_size = FONT_16; palette = (char *)malloc(3*256, M_DEVBUF, M_NOWAIT); save_palette(); } #endif scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_NOWAIT); #ifdef PC98 scp->atr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_NOWAIT); #endif /* copy screen to buffer */ bcopyw(Crtat, scp->scr_buf, scp->xsize * scp->ysize * sizeof(u_short)); #ifdef PC98 bcopyw(Atrat, scp->atr_buf, scp->xsize * scp->ysize * sizeof(u_short)); #endif scp->cursor_pos = scp->cursor_oldpos = scp->scr_buf + scp->xpos + scp->ypos * scp->xsize; #ifdef PC98 scp->cursor_atr = scp->atr_buf + scp->xpos + scp->ypos * scp->xsize; #endif scp->mouse_pos = scp->scr_buf; /* initialize history buffer & pointers */ scp->history_head = scp->history_pos = scp->history = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_NOWAIT); bzero(scp->history_head, scp->history_size*sizeof(u_short)); #ifdef PC98 scp->his_atr_head = scp->his_atr_pos = scp->his_atr = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_NOWAIT); bzero(scp->his_atr_head, scp->history_size*sizeof(u_short)); #endif /* initialize cursor stuff */ draw_cursor_image(scp); if (crtc_vga && (flags & CHAR_CURSOR)) set_destructive_cursor(scp); /* get screen update going */ scrn_timer(); update_leds(scp->status); printf("sc%d: ", dev->id_unit); #ifdef PC98 printf(" "); #else if (crtc_vga) if (crtc_addr == MONO_BASE) printf("VGA mono"); else printf("VGA color"); else if (crtc_addr == MONO_BASE) printf("MDA/hercules"); else printf("CGA/EGA"); #endif printf(" <%d virtual consoles, flags=0x%x>\n", MAXCONS, flags); #if NAPM > 0 scp->r_hook.ah_fun = scresume; scp->r_hook.ah_arg = NULL; scp->r_hook.ah_name = "system keyboard"; scp->r_hook.ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_RESUME , &scp->r_hook); #endif cdevsw_add(&cdev, &scdevsw, NULL); #ifdef DEVFS for (vc = 0; vc < MAXCONS; vc++) sc_devfs_token[vc] = devfs_add_devswf(&scdevsw, vc, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyv%n", vc); #endif return 0; } struct tty *scdevtotty(dev_t dev) { int unit = minor(dev); if (init_done == COLD) return(NULL); - if (unit > MAXCONS || unit < 0) - return(NULL); - if (unit == MAXCONS) + if (unit == SC_CONSOLE) return CONSOLE_TTY; + if (unit == SC_MOUSE) + return MOUSE_TTY; + if (unit >= MAXCONS || unit < 0) + return(NULL); return VIRTUAL_TTY(unit); } static scr_stat *get_scr_stat(dev_t dev) { int unit = minor(dev); - if (unit > MAXCONS || unit < 0) - return(NULL); - if (unit == MAXCONS) + if (unit == SC_CONSOLE) return console[0]; + if (unit >= MAXCONS || unit < 0) + return(NULL); return console[unit]; } static int get_scr_num() { int i = 0; while ((i < MAXCONS) && (cur_console != console[i])) i++; return i < MAXCONS ? i : 0; } int scopen(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); - tp->t_oproc = scstart; + tp->t_oproc = (minor(dev) == SC_MOUSE) ? scmousestart : scstart; tp->t_param = scparam; tp->t_dev = dev; if (!(tp->t_state & TS_ISOPEN)) { ttychars(tp); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; scparam(tp, &tp->t_termios); ttsetwater(tp); (*linesw[tp->t_line].l_modem)(tp, 1); } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) return(EBUSY); - if (!console[minor(dev)]) + if (minor(dev) < MAXCONS && !console[minor(dev)]) console[minor(dev)] = alloc_scp(); return((*linesw[tp->t_line].l_open)(dev, tp)); } int scclose(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = scdevtotty(dev); struct scr_stat *scp; if (!tp) return(ENXIO); if (minor(dev) < MAXCONS) { scp = get_scr_stat(tp->t_dev); if (scp->status & SWITCH_WAIT_ACQ) wakeup((caddr_t)&scp->smode); #if not_yet_done if (scp == &main_console) { scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; } else { free(scp->scr_buf, M_DEVBUF); #ifdef PC98 free(scp->atr_buf, M_DEVBUF); free(scp->his_atr, M_DEVBUF); #endif free(scp->history, M_DEVBUF); free(scp, M_DEVBUF); console[minor(dev)] = NULL; } #else scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; #endif } (*linesw[tp->t_line].l_close)(tp, flag); ttyclose(tp); return(0); } int scread(dev_t dev, struct uio *uio, int flag) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); return((*linesw[tp->t_line].l_read)(tp, uio, flag)); } int scwrite(dev_t dev, struct uio *uio, int flag) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); return((*linesw[tp->t_line].l_write)(tp, uio, flag)); } void scintr(int unit) { static struct tty *cur_tty; int c, len; u_char *cp; /* make screensaver happy */ scrn_time_stamp = time.tv_sec; if (scrn_blanked) { (*current_saver)(FALSE); cur_console->start = 0; cur_console->end = cur_console->xsize * cur_console->ysize; } c = scgetc(1); cur_tty = VIRTUAL_TTY(get_scr_num()); if (!(cur_tty->t_state & TS_ISOPEN)) if (!((cur_tty = CONSOLE_TTY)->t_state & TS_ISOPEN)) return; switch (c & 0xff00) { case 0x0000: /* normal key */ (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty); break; case NOKEY: /* nothing there */ break; case FKEY: /* function key, return string */ if (cp = get_fstr((u_int)c, (u_int *)&len)) { while (len-- > 0) (*linesw[cur_tty->t_line].l_rint)(*cp++ & 0xFF, cur_tty); } break; case MKEY: /* meta is active, prepend ESC */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty); break; case BKEY: /* backtab fixed sequence (esc [ Z) */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)('[', cur_tty); (*linesw[cur_tty->t_line].l_rint)('Z', cur_tty); break; } } static int scparam(struct tty *tp, struct termios *t) { tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; return 0; } int scioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { int i, error; struct tty *tp; struct trapframe *fp; scr_stat *scp; tp = scdevtotty(dev); if (!tp) return ENXIO; scp = get_scr_stat(tp->t_dev); switch (cmd) { /* process console hardware related ioctl's */ case GIO_ATTR: /* get current attributes */ *(int*)data = (scp->term.cur_attr >> 8) & 0xFF; return 0; case GIO_COLOR: /* is this a color console ? */ #ifdef PC98 *(int*)data = 0; #else if (crtc_addr == COLOR_BASE) *(int*)data = 1; else *(int*)data = 0; #endif return 0; case CONS_CURRENT: /* get current adapter type */ #ifdef PC98 *(int*)data = KD_PC98; #else if (crtc_vga) *(int*)data = KD_VGA; else if (crtc_addr == MONO_BASE) *(int*)data = KD_MONO; else *(int*)data = KD_CGA; #endif return 0; case CONS_GET: /* get current video mode */ *(int*)data = scp->mode; return 0; case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */ scrn_blank_time = *(int*)data; return 0; case CONS_CURSORTYPE: /* set cursor type blink/noblink */ if ((*(int*)data) & 0x01) flags |= BLINK_CURSOR; else flags &= ~BLINK_CURSOR; if ((*(int*)data) & 0x02) { if (!crtc_vga) return ENXIO; flags |= CHAR_CURSOR; set_destructive_cursor(scp); } else flags &= ~CHAR_CURSOR; return 0; case CONS_BELLTYPE: /* set bell type sound/visual */ if (*data) flags |= VISUAL_BELL; else flags &= ~VISUAL_BELL; return 0; case CONS_HISTORY: /* set history size */ if (*data) { free(scp->history, M_DEVBUF); #ifdef PC98 free(scp->his_atr, M_DEVBUF); #endif scp->history_size = *(int*)data; if (scp->history_size < scp->ysize) #ifdef PC98 { #endif scp->history = NULL; #ifdef PC98 scp->his_atr = NULL; } #endif else { scp->history_size *= scp->xsize; scp->history_head = scp->history_pos = scp->history = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_WAITOK); bzero(scp->history_head, scp->history_size*sizeof(u_short)); #ifdef PC98 scp->his_atr_head = scp->his_atr_pos = scp->his_atr = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_WAITOK); bzero(scp->his_atr_head, scp->history_size*sizeof(u_short)); #endif } return 0; } else return EINVAL; case CONS_MOUSECTL: /* control mouse arrow */ { mouse_info_t *mouse = (mouse_info_t*)data; if (!crtc_vga) return ENXIO; switch (mouse->operation) { case MOUSE_MODE: if (mouse->u.mode.signal > 0 && mouse->u.mode.signal < NSIG) { scp->mouse_signal = mouse->u.mode.signal; scp->mouse_proc = p; scp->mouse_pid = p->p_pid; } else { scp->mouse_signal = 0; scp->mouse_proc = NULL; scp->mouse_pid = 0; } - return 0; + break; case MOUSE_SHOW: if (!(scp->status & MOUSE_ENABLED)) { scp->status |= MOUSE_ENABLED; scp->mouse_oldpos = scp->mouse_pos; mark_all(scp); } else return EINVAL; break; case MOUSE_HIDE: if (scp->status & MOUSE_ENABLED) { scp->status &= ~MOUSE_ENABLED; mark_all(scp); } else return EINVAL; break; case MOUSE_MOVEABS: scp->mouse_xpos = mouse->u.data.x; scp->mouse_ypos = mouse->u.data.y; set_mouse_pos(scp); break; case MOUSE_MOVEREL: scp->mouse_xpos += mouse->u.data.x; scp->mouse_ypos += mouse->u.data.y; set_mouse_pos(scp); break; case MOUSE_GETINFO: mouse->u.data.x = scp->mouse_xpos; mouse->u.data.y = scp->mouse_ypos; mouse->u.data.buttons = scp->mouse_buttons; - return 0; + break; case MOUSE_ACTION: - /* this should maybe only be settable from /dev/console SOS */ + /* this should maybe only be settable from /dev/mouse SOS */ + /* send out mouse event on /dev/mouse */ + if ((MOUSE_TTY)->t_state & TS_ISOPEN) { + u_char buf[5]; + int i; + + buf[0] = 0x80 | ((~mouse->u.data.buttons) & 0x07); + buf[1] = (mouse->u.data.x & 0x1fe >> 1); + buf[3] = (mouse->u.data.x & 0x1ff) - buf[1]; + buf[2] = -(mouse->u.data.y & 0x1fe >> 1); + buf[4] = -(mouse->u.data.y & 0x1ff) - buf[2]; + for (i=0; i<5; i++) + (*linesw[(MOUSE_TTY)->t_line].l_rint)(buf[i],MOUSE_TTY); + } cur_console->mouse_xpos += mouse->u.data.x; cur_console->mouse_ypos += mouse->u.data.y; if (cur_console->mouse_signal) { cur_console->mouse_buttons = mouse->u.data.buttons; /* has controlling process died? */ if (cur_console->mouse_proc && (cur_console->mouse_proc != pfind(cur_console->mouse_pid))){ cur_console->mouse_signal = 0; cur_console->mouse_proc = NULL; cur_console->mouse_pid = 0; } else psignal(cur_console->mouse_proc, cur_console->mouse_signal); } else { /* process button presses*/ if (cur_console->mouse_buttons != mouse->u.data.buttons) { cur_console->mouse_buttons = mouse->u.data.buttons; if (!(scp->status & UNKNOWN_MODE)) { if (cur_console->mouse_buttons & LEFT_BUTTON) mouse_cut_start(cur_console); else mouse_cut_end(cur_console); if (cur_console->mouse_buttons & RIGHT_BUTTON) mouse_paste(cur_console); } } } if (mouse->u.data.x != 0 || mouse->u.data.y != 0) set_mouse_pos(cur_console); break; default: return EINVAL; } /* make screensaver happy */ if (scp == cur_console) { scrn_time_stamp = time.tv_sec; if (scrn_blanked) { (*current_saver)(FALSE); cur_console->start = 0; cur_console->end = cur_console->xsize * cur_console->ysize; } } return 0; } case CONS_GETINFO: /* get current (virtual) console info */ { vid_info_t *ptr = (vid_info_t*)data; if (ptr->size == sizeof(struct vid_info)) { ptr->m_num = get_scr_num(); ptr->mv_col = scp->xpos; ptr->mv_row = scp->ypos; ptr->mv_csz = scp->xsize; ptr->mv_rsz = scp->ysize; ptr->mv_norm.fore = (scp->term.std_color & 0x0f00)>>8; ptr->mv_norm.back = (scp->term.std_color & 0xf000)>>12; ptr->mv_rev.fore = (scp->term.rev_color & 0x0f00)>>8; ptr->mv_rev.back = (scp->term.rev_color & 0xf000)>>12; ptr->mv_grfc.fore = 0; /* not supported */ ptr->mv_grfc.back = 0; /* not supported */ ptr->mv_ovscan = scp->border; ptr->mk_keylock = scp->status & LOCK_KEY_MASK; return 0; } return EINVAL; } case CONS_GETVERS: /* get version number */ *(int*)data = 0x200; /* version 2.0 */ return 0; #ifdef PC98 case SW_PC98_80x25: case SW_PC98_80x30: /* PC98 TEXT MODES */ if (!crtc_vga) return ENXIO; scp->xsize = 80; switch (cmd & 0xff) { case M_PC98_80x25: scp->ysize = 25; break; #ifdef LINE30 case M_PC98_80x30: scp->ysize = LINE30_ROW; break; #endif } scp->mode = cmd & 0xff; scp->status &= ~UNKNOWN_MODE; /* text mode */ free(scp->scr_buf, M_DEVBUF); scp->scr_buf = (u_short *)malloc(scp->xsize * scp->ysize * sizeof(u_short),M_DEVBUF, M_WAITOK); #ifdef PC98 free(scp->atr_buf, M_DEVBUF); scp->atr_buf = (u_short *)malloc(scp->xsize * scp->ysize * sizeof(u_short),M_DEVBUF, M_WAITOK); #endif if (scp == cur_console) set_mode(scp); clear_screen(scp); if (tp->t_winsize.ws_col != scp->xsize || tp->t_winsize.ws_row != scp->ysize) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; #else /* VGA TEXT MODES */ case SW_VGA_C40x25: case SW_VGA_C80x25: case SW_VGA_M80x25: case SW_VGA_C80x30: case SW_VGA_M80x30: case SW_VGA_C80x50: case SW_VGA_M80x50: case SW_VGA_C80x60: case SW_VGA_M80x60: case SW_B40x25: case SW_C40x25: case SW_B80x25: case SW_C80x25: case SW_ENH_B40x25: case SW_ENH_C40x25: case SW_ENH_B80x25: case SW_ENH_C80x25: case SW_ENH_B80x43: case SW_ENH_C80x43: if (!crtc_vga || video_mode_ptr == NULL) return ENXIO; switch (cmd & 0xff) { case M_VGA_C80x60: case M_VGA_M80x60: if (!(fonts_loaded & FONT_8)) return EINVAL; scp->xsize = 80; scp->ysize = 60; break; case M_VGA_C80x50: case M_VGA_M80x50: if (!(fonts_loaded & FONT_8)) return EINVAL; scp->xsize = 80; scp->ysize = 50; break; case M_ENH_B80x43: case M_ENH_C80x43: if (!(fonts_loaded & FONT_8)) return EINVAL; scp->xsize = 80; scp->ysize = 43; break; case M_VGA_C80x30: case M_VGA_M80x30: scp->xsize = 80; scp->ysize = 30; break; default: if ((cmd & 0xff) > M_VGA_CG320) return EINVAL; else scp->xsize = *(video_mode_ptr+((cmd&0xff)*64)); scp->ysize = *(video_mode_ptr+((cmd&0xff)*64)+1)+1; break; } scp->mode = cmd & 0xff; scp->status &= ~UNKNOWN_MODE; free(scp->scr_buf, M_DEVBUF); scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_WAITOK); #ifdef PC98 free(scp->scr_atr, M_DEVBUF); scp->scr_atr = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_WAITOK); #endif free(cut_buffer, M_DEVBUF); cut_buffer = (char *)malloc(scp->xsize*scp->ysize, M_DEVBUF, M_NOWAIT); cut_buffer[0] = 0x00; if (scp == cur_console) set_mode(scp); clear_screen(scp); if (tp->t_winsize.ws_col != scp->xsize || tp->t_winsize.ws_row != scp->ysize) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; /* GRAPHICS MODES */ case SW_BG320: case SW_BG640: case SW_CG320: case SW_CG320_D: case SW_CG640_E: case SW_CG640x350: case SW_ENH_CG640: case SW_BG640x480: case SW_CG640x480: case SW_VGA_CG320: if (!crtc_vga || video_mode_ptr == NULL) return ENXIO; scp->mode = cmd & 0xFF; scp->status |= UNKNOWN_MODE; /* graphics mode */ scp->xsize = (*(video_mode_ptr + (scp->mode*64))) * 8; scp->ysize = (*(video_mode_ptr + (scp->mode*64) + 1) + 1) * (*(video_mode_ptr + (scp->mode*64) + 2)); set_mode(scp); /* clear_graphics();*/ if (tp->t_winsize.ws_xpixel != scp->xsize || tp->t_winsize.ws_ypixel != scp->ysize) { tp->t_winsize.ws_xpixel = scp->xsize; tp->t_winsize.ws_ypixel = scp->ysize; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; #endif /* PC98 */ case VT_SETMODE: /* set screen switcher mode */ bcopy(data, &scp->smode, sizeof(struct vt_mode)); if (scp->smode.mode == VT_PROCESS) { scp->proc = p; scp->pid = scp->proc->p_pid; } return 0; case VT_GETMODE: /* get screen switcher mode */ bcopy(&scp->smode, data, sizeof(struct vt_mode)); return 0; case VT_RELDISP: /* screen switcher ioctl */ switch(*data) { case VT_FALSE: /* user refuses to release screen, abort */ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { old_scp->status &= ~SWITCH_WAIT_REL; switch_in_progress = FALSE; return 0; } return EINVAL; case VT_TRUE: /* user has released screen, go on */ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { scp->status &= ~SWITCH_WAIT_REL; exchange_scr(); if (new_scp->smode.mode == VT_PROCESS) { new_scp->status |= SWITCH_WAIT_ACQ; psignal(new_scp->proc, new_scp->smode.acqsig); } else switch_in_progress = FALSE; return 0; } return EINVAL; case VT_ACKACQ: /* acquire acknowledged, switch completed */ if (scp == new_scp && (scp->status & SWITCH_WAIT_ACQ)) { scp->status &= ~SWITCH_WAIT_ACQ; switch_in_progress = FALSE; return 0; } return EINVAL; default: return EINVAL; } /* NOT REACHED */ case VT_OPENQRY: /* return free virtual console */ for (i = 0; i < MAXCONS; i++) { tp = VIRTUAL_TTY(i); if (!(tp->t_state & TS_ISOPEN)) { *data = i + 1; return 0; } } return EINVAL; case VT_ACTIVATE: /* switch to screen *data */ return switch_scr(scp, (*data) - 1); case VT_WAITACTIVE: /* wait for switch to occur */ if (*data > MAXCONS || *data < 0) return EINVAL; if (minor(dev) == (*data) - 1) return 0; if (*data == 0) { if (scp == cur_console) return 0; } else scp = console[(*data) - 1]; while ((error=tsleep((caddr_t)&scp->smode, PZERO|PCATCH, "waitvt", 0)) == ERESTART) ; return error; case VT_GETACTIVE: *data = get_scr_num()+1; return 0; case KDENABIO: /* allow io operations */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return error; fp = (struct trapframe *)p->p_md.md_regs; fp->tf_eflags |= PSL_IOPL; return 0; case KDDISABIO: /* disallow io operations (default) */ fp = (struct trapframe *)p->p_md.md_regs; fp->tf_eflags &= ~PSL_IOPL; return 0; case KDSETMODE: /* set current mode of this (virtual) console */ switch (*data) { case KD_TEXT: /* switch to TEXT (known) mode */ /* restore fonts & palette ! */ if (crtc_vga) { if (fonts_loaded & FONT_8) copy_font(LOAD, FONT_8, font_8); if (fonts_loaded & FONT_14) copy_font(LOAD, FONT_14, font_14); if (fonts_loaded & FONT_16) copy_font(LOAD, FONT_16, font_16); if (flags & CHAR_CURSOR) set_destructive_cursor(scp); load_palette(); } /* FALL THROUGH */ case KD_TEXT1: /* switch to TEXT (known) mode */ /* no restore fonts & palette */ scp->status &= ~UNKNOWN_MODE; #ifndef PC98 if (crtc_vga && video_mode_ptr) #endif set_mode(scp); clear_screen(scp); return 0; case KD_GRAPHICS: /* switch to GRAPHICS (unknown) mode */ scp->status |= UNKNOWN_MODE; #ifdef PC98 set_mode(scp); #endif return 0; default: return EINVAL; } /* NOT REACHED */ case KDGETMODE: /* get current mode of this (virtual) console */ *data = (scp->status & UNKNOWN_MODE) ? KD_GRAPHICS : KD_TEXT; return 0; case KDSBORDER: /* set border color of this (virtual) console */ if (!crtc_vga) return ENXIO; scp->border = *data; if (scp == cur_console) set_border(scp->border); return 0; case KDSKBSTATE: /* set keyboard state (locks) */ if (*data >= 0 && *data <= LOCK_KEY_MASK) { scp->status &= ~LOCK_KEY_MASK; scp->status |= *data; if (scp == cur_console) update_leds(scp->status); return 0; } return EINVAL; case KDGKBSTATE: /* get keyboard state (locks) */ *data = scp->status & LOCK_KEY_MASK; return 0; case KDSETRAD: /* set keyboard repeat & delay rates */ #ifndef PC98 if (*data & 0x80) return EINVAL; i = spltty(); kbd_cmd(KB_SETRAD); kbd_cmd(*data); splx(i); #endif return 0; case KDSKBMODE: /* set keyboard mode */ switch (*data) { case K_RAW: /* switch to RAW scancode mode */ scp->status |= KBD_RAW_MODE; return 0; case K_XLATE: /* switch to XLT ascii mode */ if (scp == cur_console && scp->status == KBD_RAW_MODE) shfts = ctls = alts = agrs = metas = 0; scp->status &= ~KBD_RAW_MODE; return 0; default: return EINVAL; } /* NOT REACHED */ case KDGKBMODE: /* get keyboard mode */ *data = (scp->status & KBD_RAW_MODE) ? K_RAW : K_XLATE; return 0; case KDMKTONE: /* sound the bell */ if (*(int*)data) do_bell(scp, (*(int*)data)&0xffff, (((*(int*)data)>>16)&0xffff)*hz/1000); else do_bell(scp, scp->bell_pitch, scp->bell_duration); return 0; case KIOCSOUND: /* make tone (*data) hz */ if (scp == cur_console) { if (*(int*)data) { int pitch = TIMER_FREQ/(*(int*)data); #ifdef PC98 /* enable counter 1 */ outb(0x35, inb(0x35) & 0xf7); /* set command for counter 1, 2 byte write */ if (acquire_timer1(TIMER_16BIT|TIMER_SQWAVE)) { return EBUSY; } /* set pitch */ outb(TIMER_CNTR1, pitch); outb(TIMER_CNTR1, (pitch>>8)); #else /* set command for counter 2, 2 byte write */ if (acquire_timer2(TIMER_16BIT|TIMER_SQWAVE)) return EBUSY; /* set pitch */ outb(TIMER_CNTR2, pitch); outb(TIMER_CNTR2, (pitch>>8)); /* enable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) | 3); #endif } else { #ifdef PC98 /* disable counter 1 */ outb(0x35, inb(0x35) | 0x08); release_timer1(); #else /* disable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) & 0xFC); release_timer2(); #endif } } return 0; case KDGKBTYPE: /* get keyboard type */ *data = 0; /* type not known (yet) */ return 0; case KDSETLED: /* set keyboard LED status */ if (*data >= 0 && *data <= LED_MASK) { scp->status &= ~LED_MASK; scp->status |= *data; if (scp == cur_console) update_leds(scp->status); return 0; } return EINVAL; case KDGETLED: /* get keyboard LED status */ *data = scp->status & LED_MASK; return 0; case GETFKEY: /* get functionkey string */ if (*(u_short*)data < n_fkey_tab) { fkeyarg_t *ptr = (fkeyarg_t*)data; bcopy(&fkey_tab[ptr->keynum].str, ptr->keydef, fkey_tab[ptr->keynum].len); ptr->flen = fkey_tab[ptr->keynum].len; return 0; } else return EINVAL; case SETFKEY: /* set functionkey string */ if (*(u_short*)data < n_fkey_tab) { fkeyarg_t *ptr = (fkeyarg_t*)data; bcopy(ptr->keydef, &fkey_tab[ptr->keynum].str, min(ptr->flen, MAXFK)); fkey_tab[ptr->keynum].len = min(ptr->flen, MAXFK); return 0; } else return EINVAL; case GIO_SCRNMAP: /* get output translation table */ bcopy(&scr_map, data, sizeof(scr_map)); return 0; case PIO_SCRNMAP: /* set output translation table */ bcopy(data, &scr_map, sizeof(scr_map)); for (i=0; it_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return(error); error = ttioctl(tp, cmd, data, flag); if (error >= 0) return(error); return(ENOTTY); } static void scstart(struct tty *tp) { struct clist *rbp; int s, len; u_char buf[PCBURST]; scr_stat *scp = get_scr_stat(tp->t_dev); - /* XXX who repeats the call when the above flags are cleared? */ if (scp->status & SLKED || blink_in_progress) - return; + return; /* XXX who repeats the call when the above flags are cleared? */ s = spltty(); if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { tp->t_state |= TS_BUSY; rbp = &tp->t_outq; while (rbp->c_cc) { len = q_to_b(rbp, buf, PCBURST); splx(s); ansi_put(scp, buf, len); s = spltty(); } tp->t_state &= ~TS_BUSY; ttwwakeup(tp); } splx(s); } +static void +scmousestart(struct tty *tp) +{ + struct clist *rbp; + int s; + u_char buf[PCBURST]; + + s = spltty(); + if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { + tp->t_state |= TS_BUSY; + rbp = &tp->t_outq; + while (rbp->c_cc) { + q_to_b(rbp, buf, PCBURST); + } + tp->t_state &= ~TS_BUSY; + ttwwakeup(tp); + } + splx(s); +} + void sccnprobe(struct consdev *cp) { struct isa_device *dvp; /* * Take control if we are the highest priority enabled display device. */ dvp = find_display(); if (dvp != NULL && dvp->id_driver != &scdriver) { cp->cn_pri = CN_DEAD; return; } /* initialize required fields */ - cp->cn_dev = makedev(CDEV_MAJOR, MAXCONS); + cp->cn_dev = makedev(CDEV_MAJOR, SC_CONSOLE); cp->cn_pri = CN_INTERNAL; } void sccninit(struct consdev *cp) { scinit(); } void sccnputc(dev_t dev, int c) { u_char buf[1]; int s; scr_stat *scp = console[0]; term_stat save = scp->term; scp->term = kernel_console; current_default = &kernel_default; if (scp->scr_buf == Crtat) { remove_cursor_image(scp); } buf[0] = c; ansi_put(scp, buf, 1); kernel_console = scp->term; current_default = &user_default; scp->term = save; s = splclock(); if (/* timers_not_running && */ scp == cur_console) { if (scp->scr_buf != Crtat && (scp->start <= scp->end)) { bcopyw(scp->scr_buf + scp->start, Crtat + scp->start, (1 + scp->end - scp->start) * sizeof(u_short)); #ifdef PC98 bcopyw(scp->atr_buf + scp->start, Atrat + scp->start, (1 + scp->end - scp->start) * sizeof(u_short)); #endif scp->start = scp->xsize * scp->ysize; scp->end = 0; } scp->cursor_oldpos = scp->cursor_pos; draw_cursor_image(scp); } splx(s); } int sccngetc(dev_t dev) { int s = spltty(); /* block scintr while we poll */ int c = scgetc(0); splx(s); return(c); } int sccncheckc(dev_t dev) { return (scgetc(1) & 0xff); } static void scrn_timer() { scr_stat *scp = cur_console; /* should we just return ? */ if ((scp->status&UNKNOWN_MODE) || blink_in_progress || switch_in_progress) { timeout((timeout_func_t)scrn_timer, 0, hz/10); return; } if (!scrn_blanked) { /* update screen image */ if (scp->start <= scp->end) { bcopyw(scp->scr_buf + scp->start, Crtat + scp->start, (1 + scp->end - scp->start) * sizeof(u_short)); #ifdef PC98 bcopyw(scp->atr_buf + scp->start, Atrat + scp->start, (1 + scp->end - scp->start) * sizeof(u_short)); #endif } /* update "pseudo" mouse pointer image */ if ((scp->status & MOUSE_ENABLED) && crtc_vga) { /* did mouse move since last time ? */ if (scp->status & MOUSE_MOVED) { /* do we need to remove old mouse pointer image ? */ if (scp->mouse_cut_start != NULL || (scp->mouse_pos-scp->scr_buf) <= scp->start || (scp->mouse_pos+scp->xsize+1-scp->scr_buf) >= scp->end) { remove_mouse_image(scp); } scp->status &= ~MOUSE_MOVED; draw_mouse_image(scp); } else { /* mouse didn't move, has it been overwritten ? */ if ((scp->mouse_pos+scp->xsize+1-scp->scr_buf) >= scp->start && (scp->mouse_pos - scp->scr_buf) <= scp->end) { draw_mouse_image(scp); } } } /* update cursor image */ if (scp->status & CURSOR_ENABLED) { /* did cursor move since last time ? */ if (scp->cursor_pos != scp->cursor_oldpos) { /* do we need to remove old cursor image ? */ if ((scp->cursor_oldpos - scp->scr_buf) < scp->start || ((scp->cursor_oldpos - scp->scr_buf) > scp->end)) { remove_cursor_image(scp); } scp->cursor_oldpos = scp->cursor_pos; draw_cursor_image(scp); } else { /* cursor didn't move, has it been overwritten ? */ if (scp->cursor_pos - scp->scr_buf >= scp->start && scp->cursor_pos - scp->scr_buf <= scp->end) { draw_cursor_image(scp); } else { /* if its a blinking cursor, we may have to update it */ if (flags & BLINK_CURSOR) draw_cursor_image(scp); } } blinkrate++; } if (scp->mouse_cut_start != NULL) draw_cutmarking(scp); scp->end = 0; scp->start = scp->xsize*scp->ysize; } if (scrn_blank_time && (time.tv_sec>scrn_time_stamp+scrn_blank_time)) (*current_saver)(TRUE); timeout((timeout_func_t)scrn_timer, 0, hz/25); } static void clear_screen(scr_stat *scp) { move_crsr(scp, 0, 0); scp->cursor_oldpos = scp->cursor_pos; #ifdef PC98 fillw(scr_map[0x20], scp->scr_buf, scp->xsize * scp->ysize); fillw(at2pc98(scp->term.cur_color), scp->atr_buf, scp->xsize * scp->ysize); #else fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize * scp->ysize); #endif mark_all(scp); remove_cutmarking(scp); } static int switch_scr(scr_stat *scp, u_int next_scr) { if (switch_in_progress && (cur_console->proc != pfind(cur_console->pid))) switch_in_progress = FALSE; if (next_scr >= MAXCONS || switch_in_progress || (cur_console->smode.mode == VT_AUTO && cur_console->status & UNKNOWN_MODE)) { do_bell(scp, BELL_PITCH, BELL_DURATION); return EINVAL; } /* is the wanted virtual console open ? */ if (next_scr) { struct tty *tp = VIRTUAL_TTY(next_scr); if (!(tp->t_state & TS_ISOPEN)) { do_bell(scp, BELL_PITCH, BELL_DURATION); return EINVAL; } } /* delay switch if actively updating screen */ if (write_in_progress || blink_in_progress) { delayed_next_scr = next_scr+1; return 0; } switch_in_progress = TRUE; old_scp = cur_console; new_scp = console[next_scr]; wakeup((caddr_t)&new_scp->smode); if (new_scp == old_scp) { switch_in_progress = FALSE; delayed_next_scr = FALSE; return 0; } /* has controlling process died? */ if (old_scp->proc && (old_scp->proc != pfind(old_scp->pid))) old_scp->smode.mode = VT_AUTO; if (new_scp->proc && (new_scp->proc != pfind(new_scp->pid))) new_scp->smode.mode = VT_AUTO; /* check the modes and switch approbiatly */ if (old_scp->smode.mode == VT_PROCESS) { old_scp->status |= SWITCH_WAIT_REL; psignal(old_scp->proc, old_scp->smode.relsig); } else { exchange_scr(); if (new_scp->smode.mode == VT_PROCESS) { new_scp->status |= SWITCH_WAIT_ACQ; psignal(new_scp->proc, new_scp->smode.acqsig); } else switch_in_progress = FALSE; } return 0; } static void exchange_scr(void) { move_crsr(old_scp, old_scp->xpos, old_scp->ypos); cur_console = new_scp; #ifdef PC98 if (old_scp->mode != new_scp->mode || (old_scp->status & UNKNOWN_MODE) || (new_scp->status & UNKNOWN_MODE)){ #else if (old_scp->mode != new_scp->mode || (old_scp->status & UNKNOWN_MODE)){ if (crtc_vga && video_mode_ptr) #endif set_mode(new_scp); } move_crsr(new_scp, new_scp->xpos, new_scp->ypos); #ifndef PC98 if ((old_scp->status & UNKNOWN_MODE) && crtc_vga) { if (fonts_loaded & FONT_8) copy_font(LOAD, FONT_8, font_8); if (fonts_loaded & FONT_14) copy_font(LOAD, FONT_14, font_14); if (fonts_loaded & FONT_16) copy_font(LOAD, FONT_16, font_16); if (flags & CHAR_CURSOR) set_destructive_cursor(new_scp); load_palette(); } #endif if (old_scp->status & KBD_RAW_MODE || new_scp->status & KBD_RAW_MODE) shfts = ctls = alts = agrs = metas = 0; update_leds(new_scp->status); delayed_next_scr = FALSE; mark_all(new_scp); } static void scan_esc(scr_stat *scp, u_char c) { static u_char ansi_col[16] = {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15}; int i, n; u_short *src, *dst, count; #ifdef PC98 u_short *src_attr, *dst_attr; #endif if (scp->term.esc == 1) { #ifdef KANJI switch (scp->kanji_type) { case 0x80: switch (c) { case 'B': case '@': scp->kanji_type = 0x20; scp->term.esc = 0; scp->kanji_1st_char = 0; return; default: scp->kanji_type = 0; scp->term.esc = 0; break; } break; case 0x40: switch (c) { case 'J': case 'B': case 'H': scp->kanji_type = 0; scp->term.esc = 0; scp->kanji_1st_char = 0; return; case 'I': scp->kanji_type = 0x10; scp->term.esc = 0; scp->kanji_1st_char = 0; return; default: scp->kanji_type = 0; scp->term.esc = 0; break; } break; default: break; } #endif switch (c) { case '[': /* Start ESC [ sequence */ scp->term.esc = 2; scp->term.last_param = -1; for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) scp->term.param[i] = 1; scp->term.num_param = 0; return; #ifdef KANJI case '$': /* Kanji IN sequence */ scp->kanji_type = 0x80; return; case '(': /* Kanji OUT sequence */ scp->kanji_type = 0x40; return; #endif case 'M': /* Move cursor up 1 line, scroll if at top */ if (scp->ypos > 0) move_crsr(scp, scp->xpos, scp->ypos - 1); else { #ifdef PC98 bcopyw(scp->scr_buf, scp->scr_buf + scp->xsize, (scp->ysize - 1) * scp->xsize * sizeof(u_short)); bcopyw(scp->atr_buf, scp->atr_buf + scp->xsize, (scp->ysize - 1) * scp->xsize * sizeof(u_short)); fillw(scr_map[0x20], scp->scr_buf, scp->xsize); fillw(at2pc98(scp->term.cur_color), scp->atr_buf, scp->xsize); #else bcopyw(scp->scr_buf, scp->scr_buf + scp->xsize, (scp->ysize - 1) * scp->xsize * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize); #endif mark_all(scp); } break; #if notyet case 'Q': scp->term.esc = 4; break; #endif case 'c': /* Clear screen & home */ clear_screen(scp); break; } } else if (scp->term.esc == 2) { if (c >= '0' && c <= '9') { if (scp->term.num_param < MAX_ESC_PAR) { if (scp->term.last_param != scp->term.num_param) { scp->term.last_param = scp->term.num_param; scp->term.param[scp->term.num_param] = 0; } else scp->term.param[scp->term.num_param] *= 10; scp->term.param[scp->term.num_param] += c - '0'; return; } } scp->term.num_param = scp->term.last_param + 1; switch (c) { case ';': if (scp->term.num_param < MAX_ESC_PAR) return; break; case '=': scp->term.esc = 3; scp->term.last_param = -1; for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) scp->term.param[i] = 1; scp->term.num_param = 0; return; case 'A': /* up n rows */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos - n); break; case 'B': /* down n rows */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos + n); break; case 'C': /* right n columns */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos + n, scp->ypos); break; case 'D': /* left n columns */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos - n, scp->ypos); break; case 'E': /* cursor to start of line n lines down */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, 0, scp->ypos + n); break; case 'F': /* cursor to start of line n lines up */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, 0, scp->ypos - n); break; case 'f': /* Cursor move */ case 'H': if (scp->term.num_param == 0) move_crsr(scp, 0, 0); else if (scp->term.num_param == 2) move_crsr(scp, scp->term.param[1] - 1, scp->term.param[0] - 1); break; case 'J': /* Clear all or part of display */ if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* clear form cursor to end of display */ #ifdef PC98 fillw(scr_map[0x20], scp->cursor_pos, scp->scr_buf + scp->xsize * scp->ysize - scp->cursor_pos); fillw(at2pc98(scp->term.cur_color), scp->cursor_atr, scp->atr_buf + scp->xsize * scp->ysize - scp->cursor_atr); #else fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos, scp->scr_buf + scp->xsize * scp->ysize - scp->cursor_pos); #endif mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif mark_for_update(scp, scp->xsize * scp->ysize); break; case 1: /* clear from beginning of display to cursor */ #ifdef PC98 fillw(scr_map[0x20], scp->scr_buf, scp->cursor_pos - scp->scr_buf); fillw(at2pc98(scp->term.cur_color), scp->atr_buf, scp->cursor_atr - scp->atr_buf); #else fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->cursor_pos - scp->scr_buf); #endif mark_for_update(scp, 0); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif break; case 2: /* clear entire display */ clear_screen(scp); break; } break; case 'K': /* Clear all or part of line */ if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* clear form cursor to end of line */ #ifdef PC98 fillw(scr_map[0x20], scp->cursor_pos, scp->xsize - scp->xpos); fillw(at2pc98(scp->term.cur_color), scp->cursor_atr, scp->xsize - scp->xpos); #else fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos, scp->xsize - scp->xpos); #endif mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif mark_for_update(scp, scp->cursor_pos - scp->scr_buf + scp->xsize - scp->xpos); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf + scp->xsize - scp->xpos); #endif break; case 1: /* clear from beginning of line to cursor */ #ifdef PC98 fillw(scr_map[0x20], scp->cursor_pos - scp->xpos, scp->xpos + 1); fillw(at2pc98(scp->term.cur_color), scp->cursor_atr - scp->xpos, scp->xpos + 1); #else fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos - scp->xpos, scp->xpos + 1); #endif mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif break; case 2: /* clear entire line */ #ifdef PC98 fillw(scr_map[0x20], scp->cursor_pos - scp->xpos, scp->xsize); fillw(at2pc98(scp->term.cur_color), scp->cursor_atr - scp->xpos, scp->xsize); #else fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos - scp->xpos, scp->xsize); #endif mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, (scp->ypos + 1) * scp->xsize); break; } break; case 'L': /* Insert n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize - scp->ypos) n = scp->ysize - scp->ypos; src = scp->scr_buf + scp->ypos * scp->xsize; dst = src + n * scp->xsize; count = scp->ysize - (scp->ypos + n); bcopyw(src, dst, count * scp->xsize * sizeof(u_short)); #ifdef PC98 src_attr = scp->atr_buf + scp->ypos * scp->xsize; dst_attr = src_attr + n * scp->xsize; bcopyw(src_attr, dst_attr, count * scp->xsize * sizeof(u_short)); fillw(scr_map[0x20], src, n * scp->xsize); fillw(at2pc98(scp->term.cur_color), src_attr, n * scp->xsize); #else fillw(scp->term.cur_color | scr_map[0x20], src, n * scp->xsize); #endif mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, scp->xsize * scp->ysize); break; case 'M': /* Delete n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize - scp->ypos) n = scp->ysize - scp->ypos; dst = scp->scr_buf + scp->ypos * scp->xsize; src = dst + n * scp->xsize; count = scp->ysize - (scp->ypos + n); bcopyw(src, dst, count * scp->xsize * sizeof(u_short)); src = dst + count * scp->xsize; #ifdef PC98 dst_attr = scp->atr_buf + scp->ypos * scp->xsize; src_attr = dst_attr + n * scp->xsize; bcopyw(src_attr, dst_attr, count * scp->xsize * sizeof(u_short)); src_attr = dst_attr + count * scp->xsize; fillw(scr_map[0x20], src, n * scp->xsize); fillw(at2pc98(scp->term.cur_color), src_attr, n * scp->xsize); #else fillw(scp->term.cur_color | scr_map[0x20], src, n * scp->xsize); #endif mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, scp->xsize * scp->ysize); break; case 'P': /* Delete n chars */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; dst = scp->cursor_pos; src = dst + n; count = scp->xsize - (scp->xpos + n); bcopyw(src, dst, count * sizeof(u_short)); src = dst + count; #ifdef PC98 dst_attr = scp->cursor_atr; src_attr = dst_attr + n; bcopyw(src_attr, dst_attr, count * sizeof(u_short)); src_attr = dst_attr + count; fillw(scr_map[0x20], src, n); fillw(at2pc98(scp->term.cur_color), src_attr, n); #else fillw(scp->term.cur_color | scr_map[0x20], src, n); #endif mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n + count); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf + n + count); #endif break; case '@': /* Insert n chars */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; src = scp->cursor_pos; dst = src + n; count = scp->xsize - (scp->xpos + n); bcopyw(src, dst, count * sizeof(u_short)); #ifdef PC98 src_attr = scp->cursor_atr; dst_attr = src_attr + n; bcopyw(src_attr, dst_attr, count * sizeof(u_short)); fillw(scr_map[0x20], src, n); fillw(at2pc98(scp->term.cur_color), src_attr, n); #else fillw(scp->term.cur_color | scr_map[0x20], src, n); #endif mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n + count); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf + n + count); #endif break; case 'S': /* scroll up n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize) n = scp->ysize; bcopyw(scp->scr_buf + (scp->xsize * n), scp->scr_buf, scp->xsize * (scp->ysize - n) * sizeof(u_short)); #ifdef PC98 bcopyw(scp->atr_buf + (scp->xsize * n), scp->atr_buf, scp->xsize * (scp->ysize - n) * sizeof(u_short)); fillw(scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - n), scp->xsize * n); fillw(at2pc98(scp->term.cur_color), scp->atr_buf + scp->xsize * (scp->ysize - n), scp->xsize * n); #else fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - n), scp->xsize * n); #endif mark_all(scp); break; case 'T': /* scroll down n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize) n = scp->ysize; bcopyw(scp->scr_buf, scp->scr_buf + (scp->xsize * n), scp->xsize * (scp->ysize - n) * sizeof(u_short)); #ifdef PC98 bcopyw(scp->atr_buf, scp->atr_buf + (scp->xsize * n), scp->xsize * (scp->ysize - n) * sizeof(u_short)); fillw(scr_map[0x20], scp->scr_buf, scp->xsize * n); fillw(at2pc98(scp->term.cur_color), scp->atr_buf, scp->xsize * n); #else fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize * n); #endif mark_all(scp); break; case 'X': /* erase n characters in line */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; #ifdef PC98 fillw(scr_map[0x20], scp->cursor_pos, n); fillw(at2pc98(scp->term.cur_color), scp->cursor_atr, n); #else fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos, n); #endif mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf + n); #endif break; case 'Z': /* move n tabs backwards */ n = scp->term.param[0]; if (n < 1) n = 1; if ((i = scp->xpos & 0xf8) == scp->xpos) i -= 8*n; else i -= 8*(n-1); if (i < 0) i = 0; move_crsr(scp, i, scp->ypos); break; case '`': /* move cursor to column n */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, n - 1, scp->ypos); break; case 'a': /* move cursor n columns to the right */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos + n, scp->ypos); break; case 'd': /* move cursor to row n */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, n - 1); break; case 'e': /* move cursor n rows down */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos + n); break; case 'm': /* change attribute */ if (scp->term.num_param == 0) { scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color; break; } for (i = 0; i < scp->term.num_param; i++) { switch (n = scp->term.param[i]) { case 0: /* back to normal */ scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color; break; case 1: /* bold */ scp->term.attr_mask |= BOLD_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 4: /* underline */ scp->term.attr_mask |= UNDERLINE_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 5: /* blink */ scp->term.attr_mask |= BLINK_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 7: /* reverse video */ scp->term.attr_mask |= REVERSE_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 30: case 31: /* set fg color */ case 32: case 33: case 34: case 35: case 36: case 37: scp->term.attr_mask |= FOREGROUND_CHANGED; scp->term.cur_color = (scp->term.cur_color&0xF000) | (ansi_col[(n-30)&7]<<8); scp->term.cur_attr = mask2attr(&scp->term); break; case 40: case 41: /* set bg color */ case 42: case 43: case 44: case 45: case 46: case 47: scp->term.attr_mask |= BACKGROUND_CHANGED; scp->term.cur_color = (scp->term.cur_color&0x0F00) | (ansi_col[(n-40)&7]<<12); scp->term.cur_attr = mask2attr(&scp->term); break; } } break; case 'x': if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* reset attributes */ scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color = current_default->std_color; scp->term.rev_color = current_default->rev_color; break; case 1: /* set ansi background */ scp->term.attr_mask &= ~BACKGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0x0F00) | (ansi_col[(scp->term.param[1])&0x0F]<<12); scp->term.cur_attr = mask2attr(&scp->term); break; case 2: /* set ansi foreground */ scp->term.attr_mask &= ~FOREGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0xF000) | (ansi_col[(scp->term.param[1])&0x0F]<<8); scp->term.cur_attr = mask2attr(&scp->term); break; case 3: /* set ansi attribute directly */ scp->term.attr_mask &= ~(FOREGROUND_CHANGED|BACKGROUND_CHANGED); scp->term.cur_color = scp->term.std_color = (scp->term.param[1]&0xFF)<<8; scp->term.cur_attr = mask2attr(&scp->term); break; case 5: /* set ansi reverse video background */ scp->term.rev_color = (scp->term.rev_color & 0x0F00) | (ansi_col[(scp->term.param[1])&0x0F]<<12); scp->term.cur_attr = mask2attr(&scp->term); break; case 6: /* set ansi reverse video foreground */ scp->term.rev_color = (scp->term.rev_color & 0xF000) | (ansi_col[(scp->term.param[1])&0x0F]<<8); scp->term.cur_attr = mask2attr(&scp->term); break; case 7: /* set ansi reverse video directly */ scp->term.rev_color = (scp->term.param[1]&0xFF)<<8; scp->term.cur_attr = mask2attr(&scp->term); break; } break; case 'z': /* switch to (virtual) console n */ if (scp->term.num_param == 1) switch_scr(scp, scp->term.param[0]); break; } } else if (scp->term.esc == 3) { if (c >= '0' && c <= '9') { if (scp->term.num_param < MAX_ESC_PAR) { if (scp->term.last_param != scp->term.num_param) { scp->term.last_param = scp->term.num_param; scp->term.param[scp->term.num_param] = 0; } else scp->term.param[scp->term.num_param] *= 10; scp->term.param[scp->term.num_param] += c - '0'; return; } } scp->term.num_param = scp->term.last_param + 1; switch (c) { case ';': if (scp->term.num_param < MAX_ESC_PAR) return; break; case 'A': /* set display border color */ if (scp->term.num_param == 1) scp->border=scp->term.param[0] & 0xff; if (scp == cur_console) set_border(scp->border); break; case 'B': /* set bell pitch and duration */ if (scp->term.num_param == 2) { scp->bell_pitch = scp->term.param[0]; scp->bell_duration = scp->term.param[1]*10; } break; case 'C': /* set cursor type & shape */ if (scp->term.num_param == 1) { if (scp->term.param[0] & 0x01) flags |= BLINK_CURSOR; else flags &= ~BLINK_CURSOR; if (scp->term.param[0] & 0x02) { flags |= CHAR_CURSOR; set_destructive_cursor(scp); } else flags &= ~CHAR_CURSOR; } else if (scp->term.num_param == 2) { scp->cursor_start = scp->term.param[0] & 0x1F; scp->cursor_end = scp->term.param[1] & 0x1F; if (flags & CHAR_CURSOR) set_destructive_cursor(scp); } break; case 'F': /* set ansi foreground */ if (scp->term.num_param == 1) { scp->term.attr_mask &= ~FOREGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0xF000) | ((scp->term.param[0] & 0x0F) << 8); scp->term.cur_attr = mask2attr(&scp->term); } break; case 'G': /* set ansi background */ if (scp->term.num_param == 1) { scp->term.attr_mask &= ~BACKGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0x0F00) | ((scp->term.param[0] & 0x0F) << 12); scp->term.cur_attr = mask2attr(&scp->term); } break; case 'H': /* set ansi reverse video foreground */ if (scp->term.num_param == 1) { scp->term.rev_color = (scp->term.rev_color & 0xF000) | ((scp->term.param[0] & 0x0F) << 8); scp->term.cur_attr = mask2attr(&scp->term); } break; case 'I': /* set ansi reverse video background */ if (scp->term.num_param == 1) { scp->term.rev_color = (scp->term.rev_color & 0x0F00) | ((scp->term.param[0] & 0x0F) << 12); scp->term.cur_attr = mask2attr(&scp->term); } break; } } scp->term.esc = 0; } #ifdef KANJI static u_char iskanji1(u_char mode, u_char c) { if ((mode == 0x20) && (c >= 0x21) && (c <= 0x7e)) { /* JIS */ return 0x20; } if ((mode == 0x10) && (c >= 0x21) && (c <= 0x5f)) { /* JIS HANKAKU */ return 0x10; } if ((c >= 0x81) && (c <= 0x9f) && (c != 0x8e)) { /* SJIS */ default_kanji = SJIS; return 2; } if ((c >= 0xa1) && (c <= 0xdf) && (default_kanji == SJIS)) { /* Sjis HANKAKU */ return 1; } if ((c >= 0xa1) && (c <= 0xdf) && (default_kanji == UJIS)) { /* UJIS */ return 4; } if ((c >= 0xf0) && (c <= 0xfe)) { /* UJIS */ default_kanji = UJIS; return 4; } if ((c >= 0xe0) && (c <= 0xef)) { /* SJIS or UJIS */ return 6; } if (c == 0x8e) { /* SJIS or UJIS HANKAKU */ return 3; } return 0; } static u_char iskanji2(u_char mode, u_char c) { switch (mode) { case 0x20: if ((c >= 0x21) && (c <= 0x7e)) { /* JIS */ return 0x20; } break; case 2: if ((c >= 0x40) && (c <= 0xfc) && (c != 0x7f)) { /* SJIS */ return 2; } break; case 4: if ((c >= 0xa1) && (c <= 0xfe)) { /* UJIS */ return 4; } break; case 3: if ((c >= 0xa1) && (c <= 0xdf) && (default_kanji == UJIS)) { /* UJIS HANKAKU */ return 1; } if ((c >= 0x40) && (c <= 0xfc) && (c != 0x7f)) { /* SJIS */ default_kanji = SJIS; return 2; } break; case 6: if ((c >= 0x40) && (c <= 0xa0) && (c != 0x7f)) { /* SJIS */ default_kanji = SJIS; return 2; } if ((c == 0xfd) || (c == 0xfe)) { /* UJIS */ default_kanji = UJIS; return 4; } if ((c >= 0xa1) && (c <= 0xfc)) { if (default_kanji == SJIS) return 2; if (default_kanji == UJIS) return 4; } break; } return 0; } /* * JIS X0208-83 keisen conversion table */ static u_short keiConv[32] = { 0x240c, 0x260c, 0x300c, 0x340c, 0x3c0c, 0x380c, 0x400c, 0x500c, 0x480c, 0x580c, 0x600c, 0x250c, 0x270c, 0x330c, 0x370c, 0x3f0c, 0x3b0c, 0x470c, 0x570c, 0x4f0c, 0x5f0c, 0x6f0c, 0x440c, 0x530c, 0x4c0c, 0x5b0c, 0x630c, 0x410c, 0x540c, 0x490c, 0x5c0c, 0x660c }; static u_short kanji_convert(u_char mode, u_char h, u_char l) { u_short tmp, high, low, c; high = (u_short) h; low = (u_short) l; switch (mode) { case 2: /* SHIFT JIS */ if (low >= 0xe0) { low -= 0x40; } low = (low - 0x81) * 2 + 0x21; if (high > 0x7f) { high--; } if (high >0x9d) { low++; high -= 0x9e - 0x21; } else { high -= 0x40 - 0x21; } high &= 0x7F; low &= 0x7F; tmp = ((high << 8) | low) - 0x20; break; case 0x20: /* JIS */ case 4: /* HANKAKU? */ high &= 0x7F; low &= 0x7F; tmp = ((high << 8) | low) - 0x20; break; default: tmp = 0; break; } /* keisen */ c = ((tmp & 0xff) << 8) | (tmp >> 8); if (0x0821 <= c && c <= 0x0840) tmp = keiConv[c - 0x0821]; return (tmp); } #endif static void ansi_put(scr_stat *scp, u_char *buf, int len) { u_char *ptr = buf; #ifdef KANJI u_short i, kanji_code; #endif if (scp->status & UNKNOWN_MODE) return; /* make screensaver happy */ if (scp == cur_console) { scrn_time_stamp = time.tv_sec; if (scrn_blanked) { (*current_saver)(FALSE); cur_console->start = 0; cur_console->end = cur_console->xsize * cur_console->ysize; } } write_in_progress++; outloop: if (scp->term.esc) { scan_esc(scp, *ptr++); len--; } else if (PRINTABLE(*ptr)) { /* Print only printables */ int cnt = len <= (scp->xsize-scp->xpos) ? len : (scp->xsize-scp->xpos); u_short cur_attr = scp->term.cur_attr; u_short *cursor_pos = scp->cursor_pos; #ifdef PC98 u_char c = *ptr; u_short *cursor_atr = scp->cursor_atr; #ifdef KANJI if (scp->kanji_1st_char == 0) { scp->kanji_type = iskanji1(scp->kanji_type, c); if (scp->kanji_type & 0xee) { /* not Ascii & not HANKAKU */ scp->kanji_1st_char = c; ptr++; len--; goto kanji_end; } else { scp->kanji_1st_char = 0; } } else { if ((scp->kanji_type = iskanji2(scp->kanji_type, c)) & 0xee) { /* print kanji on TEXT VRAM */ kanji_code = kanji_convert(scp->kanji_type, c, scp->kanji_1st_char); for (i=0; i<2; i++){ *cursor_pos = (kanji_code | (i*0x80)); *cursor_atr = (at2pc98(cur_attr)); cursor_pos++; cursor_atr++; if (++scp->xpos >= scp->xsize) { scp->xpos = 0; scp->ypos++; } } scp->kanji_type &= 0xF0; scp->kanji_1st_char = 0; ptr++; len--; goto kanji_end; } else { scp->kanji_1st_char = 0; } } if ((scp->kanji_type & 0x11)) c |= 0x80; scp->kanji_type &= 0xf0; #endif /* KANJI */ *cursor_pos++ = (scr_map[c]); *cursor_atr++ = at2pc98(cur_attr); ptr++; #else do { /* * gcc-2.6.3 generates poor (un)sign extension code. Casting the * pointers in the following to volatile should have no effect, * but in fact speeds up this inner loop from 26 to 18 cycles * (+ cache misses) on i486's. */ #define UCVP(ucp) ((u_char volatile *)(ucp)) *cursor_pos++ = UCVP(scr_map)[*UCVP(ptr)] | cur_attr; ptr++; cnt--; } while (cnt && PRINTABLE(*ptr)); #endif /* PC98 */ len -= (cursor_pos - scp->cursor_pos); scp->xpos += (cursor_pos - scp->cursor_pos); #ifdef KANJI kanji_end: #endif mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif mark_for_update(scp, cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, cursor_atr - scp->atr_buf); #endif scp->cursor_pos = cursor_pos; #ifdef PC98 scp->cursor_atr = cursor_atr; #endif if (scp->xpos >= scp->xsize) { scp->xpos = 0; scp->ypos++; } } else { switch(*ptr) { case 0x07: do_bell(scp, scp->bell_pitch, scp->bell_duration); break; case 0x08: /* non-destructive backspace */ if (scp->cursor_pos > scp->scr_buf) { mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif scp->cursor_pos--; #ifdef PC98 scp->cursor_atr--; #endif mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif if (scp->xpos > 0) scp->xpos--; else { scp->xpos += scp->xsize - 1; scp->ypos--; } } break; case 0x09: /* non-destructive tab */ mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif scp->cursor_pos += (8 - scp->xpos % 8u); #ifdef PC98 scp->cursor_atr += (8 - scp->xpos % 8u); #endif mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif if ((scp->xpos += (8 - scp->xpos % 8u)) >= scp->xsize) { scp->xpos = 0; scp->ypos++; } break; case 0x0a: /* newline, same pos */ mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif scp->cursor_pos += scp->xsize; #ifdef PC98 scp->cursor_atr += scp->xsize; #endif mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif scp->ypos++; break; case 0x0c: /* form feed, clears screen */ clear_screen(scp); break; case 0x0d: /* return, return to pos 0 */ mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif scp->cursor_pos -= scp->xpos; #ifdef PC98 scp->cursor_atr -= scp->xpos; #endif mark_for_update(scp, scp->cursor_pos - scp->scr_buf); #ifdef PC98 mark_for_update(scp, scp->cursor_atr - scp->atr_buf); #endif scp->xpos = 0; break; case 0x1b: /* start escape sequence */ scp->term.esc = 1; scp->term.num_param = 0; break; } ptr++; len--; } /* do we have to scroll ?? */ if (scp->cursor_pos >= scp->scr_buf + scp->ysize * scp->xsize) { remove_cutmarking(scp); if (scp->history) { bcopyw(scp->scr_buf, scp->history_head, scp->xsize * sizeof(u_short)); scp->history_head += scp->xsize; #ifdef PC98 bcopyw(scp->atr_buf, scp->his_atr_head, scp->xsize * sizeof(u_short)); scp->his_atr_head += scp->xsize; #endif if (scp->history_head + scp->xsize > scp->history + scp->history_size) #ifdef PC98 { #endif scp->history_head = scp->history; #ifdef PC98 scp->his_atr_head = scp->his_atr; } #endif } bcopyw(scp->scr_buf + scp->xsize, scp->scr_buf, scp->xsize * (scp->ysize - 1) * sizeof(u_short)); #ifdef PC98 bcopyw(scp->atr_buf + scp->xsize, scp->atr_buf, scp->xsize * (scp->ysize - 1) * sizeof(u_short)); fillw(scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - 1), scp->xsize); fillw(at2pc98(scp->term.cur_color), scp->atr_buf + scp->xsize * (scp->ysize - 1), scp->xsize); #else fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - 1), scp->xsize); #endif scp->cursor_pos -= scp->xsize; #ifdef PC98 scp->cursor_atr -= scp->xsize; #endif scp->ypos--; mark_all(scp); } if (len) goto outloop; write_in_progress--; if (delayed_next_scr) switch_scr(scp, delayed_next_scr - 1); } static void scinit(void) { #ifndef PC98 u_short volatile *cp; u_short was; #endif unsigned hw_cursor; int i; if (init_done != COLD) return; init_done = WARM; /* * Finish defaulting crtc variables for a mono screen. Crtat is a * bogus common variable so that it can be shared with pcvt, so it * can't be statically initialized. XXX. */ #ifdef PC98 Crtat = (u_short *)TEXT_VRAM; Atrat = (u_short *)TEXT_VRAM + ATTR_OFFSET; #else Crtat = (u_short *)MONO_BUF; /* * If CGA memory seems to work, switch to color. */ cp = (u_short *)CGA_BUF; was = *cp; *cp = (u_short) 0xA55A; if (*cp == 0xA55A) { - Crtat = (u_short *)cp; + Crtat = (u_short *)CGA_BUF; crtc_addr = COLOR_BASE; } *cp = was; #endif #ifdef PC98 #ifdef AUTO_CLOCK if (pc98_machine_type & M_8M) { BELL_PITCH = 1339; TIMER_FREQ = 1996800L; } else { BELL_PITCH = 1678; TIMER_FREQ = 2457600L; } #endif /* AUTO_CLOCK */ outb(0x62, 0xd); outb(0xA2, 0xd); /* Extract cursor location */ while((inb(TEXT_GDC + 0) & 0x04) == 0) {} /* GDC wait */ outb(TEXT_GDC + 2, 0xe0); /* CSRR */ while((inb(TEXT_GDC + 0) & 0x1) == 0) {} /* GDC wait */ hw_cursor = inb(TEXT_GDC + 2); /* EADl */ hw_cursor |= (inb(TEXT_GDC + 2) << 8); /* EADh */ inb(TEXT_GDC + 2); /* dummy */ inb(TEXT_GDC + 2); /* dummy */ inb(TEXT_GDC + 2); /* dummy */ if (hw_cursor >= ROW*COL) { hw_cursor = 0; } crtc_vga = 1; #else /* IBM-PC */ /* * Ensure a zero start address. This is mainly to recover after * switching from pcvt using userconfig(). The registers are w/o * for old hardware so it's too hard to relocate the active screen * memory. */ outb(crtc_addr, 12); outb(crtc_addr + 1, 0); outb(crtc_addr, 13); outb(crtc_addr + 1, 0); /* extract cursor location */ outb(crtc_addr, 14); hw_cursor = inb(crtc_addr + 1) << 8; outb(crtc_addr, 15); hw_cursor |= inb(crtc_addr + 1); /* move hardware cursor out of the way */ outb(crtc_addr, 14); outb(crtc_addr + 1, 0xff); outb(crtc_addr, 15); outb(crtc_addr + 1, 0xff); /* is this a VGA or higher ? */ outb(crtc_addr, 7); if (inb(crtc_addr) == 7) { u_long pa; u_long segoff; crtc_vga = TRUE; /* * Get the BIOS video mode pointer. */ segoff = *(u_long *)pa_to_va(0x4a8); pa = (((segoff & 0xffff0000) >> 12) + (segoff & 0xffff)); if (ISMAPPED(pa, sizeof(u_long))) { segoff = *(u_long *)pa_to_va(pa); pa = (((segoff & 0xffff0000) >> 12) + (segoff & 0xffff)); if (ISMAPPED(pa, 64)) video_mode_ptr = (char *)pa_to_va(pa); } } #endif /* IBM */ current_default = &user_default; console[0] = &main_console; init_scp(console[0]); console[0]->scr_buf = console[0]->mouse_pos = Crtat; console[0]->cursor_pos = console[0]->cursor_oldpos = Crtat + hw_cursor; #ifdef PC98 console[0]->atr_buf = Atrat; console[0]->cursor_atr = Atrat + hw_cursor; #endif console[0]->xpos = hw_cursor % COL; console[0]->ypos = hw_cursor / COL; cur_console = console[0]; for (i=1; iscr_buf = scp->cursor_pos = scp->cursor_oldpos = scp->mouse_pos = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_WAITOK); scp->history_head = scp->history_pos = scp->history = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_WAITOK); bzero(scp->history_head, scp->history_size*sizeof(u_short)); #ifdef PC98 scp->atr_buf = scp->cursor_atr = scp->atr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_WAITOK); scp->his_atr_head = scp->his_atr_pos = scp->his_atr = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_WAITOK); bzero(scp->his_atr_head, scp->history_size*sizeof(u_short)); #endif #ifndef PC98 if (crtc_vga && video_mode_ptr) #endif set_mode(scp); clear_screen(scp); return scp; } static void init_scp(scr_stat *scp) { #ifndef PC98 scp->mode = M_VGA_C80x25; #else scp->mode = M_PC98_80x25; #endif scp->font_size = FONT_16; scp->xsize = COL; scp->ysize = ROW; scp->start = COL * ROW; scp->end = 0; scp->term.esc = 0; scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color = current_default->std_color; scp->term.rev_color = current_default->rev_color; scp->border = BG_BLACK; #ifdef PC98 scp->cursor_start = 0; scp->cursor_end = 0; #else scp->cursor_start = *(char *)pa_to_va(0x461); scp->cursor_end = *(char *)pa_to_va(0x460); #endif scp->mouse_xpos = scp->mouse_ypos = 0; scp->mouse_cut_start = scp->mouse_cut_end = NULL; scp->mouse_signal = 0; scp->mouse_pid = 0; scp->mouse_proc = NULL; scp->bell_pitch = BELL_PITCH; scp->bell_duration = BELL_DURATION; #ifdef PC98 scp->status = 0; scp->status |= CURSOR_ENABLED; #else scp->status = (*(char *)pa_to_va(0x417) & 0x20) ? NLKED : 0; scp->status |= CURSOR_ENABLED; #endif scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; scp->history_head = scp->history_pos = scp->history = NULL; #ifdef PC98 scp->his_atr_head = scp->his_atr_pos = scp->his_atr = NULL; #endif scp->history_size = HISTORY_SIZE; #ifdef KANJI scp->kanji_1st_char = 0; scp->kanji_type = 0; #endif } static u_char *get_fstr(u_int c, u_int *len) { u_int i; if (!(c & FKEY)) return(NULL); i = (c & 0xFF) - F_FN; if (i > n_fkey_tab) return(NULL); *len = fkey_tab[i].len; return(fkey_tab[i].str); } static void update_leds(int which) { #ifndef PC98 int s; static u_char xlate_leds[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* replace CAPS led with ALTGR led for ALTGR keyboards */ if (key_map.n_keys > ALTGR_OFFSET) { if (which & ALKED) which |= CLKED; else which &= ~CLKED; } s = spltty(); kbd_cmd(KB_SETLEDS); kbd_cmd(xlate_leds[which & LED_MASK]); splx(s); #endif } static void history_to_screen(scr_stat *scp) { int i; for (i=0; iysize; i++) #ifdef PC98 { #endif bcopyw(scp->history + (((scp->history_pos - scp->history) + scp->history_size-((i+1)*scp->xsize))%scp->history_size), scp->scr_buf + (scp->xsize * (scp->ysize-1 - i)), scp->xsize * sizeof(u_short)); #ifdef PC98 bcopyw(scp->his_atr + (((scp->his_atr_pos - scp->his_atr) + scp->history_size-((i+1)*scp->xsize))%scp->history_size), scp->atr_buf + (scp->xsize * (scp->ysize-1 - i)), scp->xsize * sizeof(u_short)); } #endif mark_all(scp); } static int history_up_line(scr_stat *scp) { if (WRAPHIST(scp, scp->history_pos, -(scp->xsize*scp->ysize)) != scp->history_head) { scp->history_pos = WRAPHIST(scp, scp->history_pos, -scp->xsize); #ifdef PC98 scp->his_atr_pos = WRAPHIST_A(scp, scp->his_atr_pos, -scp->xsize); #endif history_to_screen(scp); return 0; } else return -1; } static int history_down_line(scr_stat *scp) { if (scp->history_pos != scp->history_head) { scp->history_pos = WRAPHIST(scp, scp->history_pos, scp->xsize); #ifdef PC98 scp->his_atr_pos = WRAPHIST_A(scp, scp->his_atr_pos, scp->xsize); #endif history_to_screen(scp); return 0; } else return -1; } /* * scgetc(noblock) - get character from keyboard. * If noblock = 0 wait until a key is pressed. * Else return NOKEY. */ u_int scgetc(int noblock) { u_char scancode, keycode; u_int state, action; struct key_t *key; static u_char esc_flag = 0, compose = 0; static u_int chr = 0; next_code: kbd_wait(); /* First see if there is something in the keyboard port */ if (inb(KB_STAT) & KB_BUF_FULL) #ifdef PC98 { kbd_wait(); scancode = inb(KB_DATA); } else if (noblock) #else scancode = inb(KB_DATA); else if (noblock) #endif return(NOKEY); else goto next_code; add_keyboard_randomness(scancode); if (cur_console->status & KBD_RAW_MODE) return scancode; keycode = scancode & 0x7F; switch (esc_flag) { case 0x00: /* normal scancode */ switch(scancode) { case 0xB8: /* left alt (compose key) */ if (compose) { compose = 0; if (chr > 255) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); chr = 0; } } break; case 0x38: if (!compose) { compose = 1; chr = 0; } break; case 0xE0: case 0xE1: esc_flag = scancode; goto next_code; } break; case 0xE0: /* 0xE0 prefix */ esc_flag = 0; switch (keycode) { case 0x1C: /* right enter key */ keycode = 0x59; break; case 0x1D: /* right ctrl key */ keycode = 0x5A; break; case 0x35: /* keypad divide key */ keycode = 0x5B; break; case 0x37: /* print scrn key */ keycode = 0x5C; break; case 0x38: /* right alt key (alt gr) */ keycode = 0x5D; break; case 0x47: /* grey home key */ keycode = 0x5E; break; case 0x48: /* grey up arrow key */ keycode = 0x5F; break; case 0x49: /* grey page up key */ keycode = 0x60; break; case 0x4B: /* grey left arrow key */ keycode = 0x61; break; case 0x4D: /* grey right arrow key */ keycode = 0x62; break; case 0x4F: /* grey end key */ keycode = 0x63; break; case 0x50: /* grey down arrow key */ keycode = 0x64; break; case 0x51: /* grey page down key */ keycode = 0x65; break; case 0x52: /* grey insert key */ keycode = 0x66; break; case 0x53: /* grey delete key */ keycode = 0x67; break; /* the following 3 are only used on the MS "Natural" keyboard */ case 0x5b: /* left Window key */ keycode = 0x69; break; case 0x5c: /* right Window key */ keycode = 0x6a; break; case 0x5d: /* menu key */ keycode = 0x6b; break; default: /* ignore everything else */ goto next_code; } break; case 0xE1: /* 0xE1 prefix */ esc_flag = 0; if (keycode == 0x1D) esc_flag = 0x1D; goto next_code; /* NOT REACHED */ case 0x1D: /* pause / break */ esc_flag = 0; if (keycode != 0x45) goto next_code; keycode = 0x68; break; } /* if scroll-lock pressed allow history browsing */ if (cur_console->history && cur_console->status & SLKED) { int i; cur_console->status &= ~CURSOR_ENABLED; if (!(cur_console->status & BUFFER_SAVED)) { cur_console->status |= BUFFER_SAVED; cur_console->history_save = cur_console->history_head; #ifdef PC98 cur_console->his_atr_save = cur_console->his_atr_head; #endif /* copy screen into top of history buffer */ for (i=0; iysize; i++) { bcopyw(cur_console->scr_buf + (cur_console->xsize * i), cur_console->history_head, cur_console->xsize * sizeof(u_short)); cur_console->history_head += cur_console->xsize; #ifdef PC98 bcopyw(cur_console->atr_buf + (cur_console->xsize * i), cur_console->his_atr_head, cur_console->xsize * sizeof(u_short)); cur_console->his_atr_head += cur_console->xsize; #endif if (cur_console->history_head + cur_console->xsize > cur_console->history + cur_console->history_size) #ifdef PC98 { #endif cur_console->history_head=cur_console->history; #ifdef PC98 cur_console->his_atr_head=cur_console->his_atr; } #endif } cur_console->history_pos = cur_console->history_head; #ifdef PC98 cur_console->his_atr_pos = cur_console->his_atr_head; #endif history_to_screen(cur_console); } switch (scancode) { #ifdef PC98 case 0x3E: /* home key */ #else case 0x47: /* home key */ #endif cur_console->history_pos = cur_console->history_head; #ifdef PC98 cur_console->his_atr_pos = cur_console->his_atr_head; #endif history_to_screen(cur_console); goto next_code; #ifdef PC98 case 0x3F: /* help key */ #else case 0x4F: /* end key */ #endif cur_console->history_pos = WRAPHIST(cur_console, cur_console->history_head, cur_console->xsize*cur_console->ysize); #ifdef PC98 cur_console->his_atr_pos = WRAPHIST_A(cur_console, cur_console->his_atr_head, cur_console->xsize*cur_console->ysize); #endif history_to_screen(cur_console); goto next_code; #ifdef PC98 case 0x3A: /* up arrow key */ #else case 0x48: /* up arrow key */ #endif if (history_up_line(cur_console)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; #ifdef PC98 case 0x3D: /* down arrow key */ #else case 0x50: /* down arrow key */ #endif if (history_down_line(cur_console)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; #ifdef PC98 case 0x36: /* roll up key */ #else case 0x49: /* page up key */ #endif for (i=0; iysize; i++) if (history_up_line(cur_console)) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); break; } goto next_code; #ifdef PC98 case 0x37: /* roll down key */ #else case 0x51: /* page down key */ #endif for (i=0; iysize; i++) if (history_down_line(cur_console)) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); break; } goto next_code; } } if (compose) { switch (scancode) { /* key pressed process it */ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ chr = (scancode - 0x40) + chr*10; goto next_code; case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ chr = (scancode - 0x47) + chr*10; goto next_code; case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ chr = (scancode - 0x4E) + chr*10; goto next_code; case 0x52: /* keypad 0 */ chr *= 10; goto next_code; /* key release, no interest here */ case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */ case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */ case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */ case 0xD2: /* keypad 0 */ goto next_code; case 0x38: /* left alt key */ break; default: if (chr) { compose = chr = 0; do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; } break; } } state = (shfts ? 1 : 0 ) | (2 * (ctls ? 1 : 0)) | (4 * (alts ? 1 : 0)); if ((!agrs && (cur_console->status & ALKED)) || (agrs && !(cur_console->status & ALKED))) keycode += ALTGR_OFFSET; key = &key_map.key[keycode]; if ( ((key->flgs & FLAG_LOCK_C) && (cur_console->status & CLKED)) || ((key->flgs & FLAG_LOCK_N) && (cur_console->status & NLKED)) ) state ^= 1; /* Check for make/break */ action = key->map[state]; if (scancode & 0x80) { /* key released */ if (key->spcl & 0x80) { switch (action) { case LSH: shfts &= ~1; break; case RSH: shfts &= ~2; break; case LCTR: ctls &= ~1; break; case RCTR: ctls &= ~2; break; case LALT: alts &= ~1; break; case RALT: alts &= ~2; break; case NLK: nlkcnt = 0; break; case CLK: #ifdef PC98 cur_console->status &= ~CLKED; update_leds(cur_console->status); #else clkcnt = 0; #endif break; case SLK: slkcnt = 0; break; case ASH: agrs = 0; break; case ALK: alkcnt = 0; break; case META: metas = 0; break; } } if (chr && !compose) { action = chr; chr = 0; return(action); } } else { /* key pressed */ if (key->spcl & (0x80>>state)) { switch (action) { /* LOCKING KEYS */ case NLK: if (!nlkcnt) { nlkcnt++; if (cur_console->status & NLKED) cur_console->status &= ~NLKED; else cur_console->status |= NLKED; update_leds(cur_console->status); } break; case CLK: #ifdef PC98 cur_console->status |= CLKED; update_leds(cur_console->status); #else if (!clkcnt) { clkcnt++; if (cur_console->status & CLKED) cur_console->status &= ~CLKED; else cur_console->status |= CLKED; update_leds(cur_console->status); } #endif break; case SLK: if (!slkcnt) { slkcnt++; if (cur_console->status & SLKED) { cur_console->status &= ~SLKED; if (cur_console->status & BUFFER_SAVED){ int i; u_short *ptr = cur_console->history_save; #ifdef PC98 u_short *ptr_a = cur_console->his_atr_save; #endif for (i=0; iysize; i++) { bcopyw(ptr, cur_console->scr_buf + (cur_console->xsize*i), cur_console->xsize * sizeof(u_short)); ptr += cur_console->xsize; #ifdef PC98 bcopyw(ptr_a, cur_console->atr_buf + (cur_console->xsize*i), cur_console->xsize * sizeof(u_short)); ptr_a += cur_console->xsize; #endif if (ptr + cur_console->xsize > cur_console->history + cur_console->history_size) #ifdef PC98 { #endif ptr = cur_console->history; #ifdef PC98 ptr_a = cur_console->his_atr; } #endif } cur_console->status &= ~BUFFER_SAVED; cur_console->history_head=cur_console->history_save; #ifdef PC98 cur_console->his_atr_head=cur_console->his_atr_save; #endif cur_console->status |= CURSOR_ENABLED; mark_all(cur_console); } scstart(VIRTUAL_TTY(get_scr_num())); } else cur_console->status |= SLKED; update_leds(cur_console->status); } break; case ALK: if (!alkcnt) { alkcnt++; if (cur_console->status & ALKED) cur_console->status &= ~ALKED; else cur_console->status |= ALKED; update_leds(cur_console->status); } break; /* NON-LOCKING KEYS */ case NOP: break; case RBT: shutdown_nice(); break; case SUSP: #if NAPM > 0 apm_suspend(); #endif break; case DBG: #ifdef DDB /* try to switch to console 0 */ if (cur_console->smode.mode == VT_AUTO && console[0]->smode.mode == VT_AUTO) switch_scr(cur_console, 0); Debugger("manual escape to debugger"); return(NOKEY); #else printf("No debugger in kernel\n"); #endif break; case LSH: shfts |= 1; break; case RSH: shfts |= 2; break; case LCTR: ctls |= 1; break; case RCTR: ctls |= 2; break; case LALT: alts |= 1; break; case RALT: alts |= 2; break; case ASH: agrs = 1; break; case META: metas = 1; break; case NEXT: switch_scr(cur_console, (get_scr_num() + 1) % MAXCONS); break; case BTAB: return(BKEY); default: if (action >= F_SCR && action <= L_SCR) { switch_scr(cur_console, action - F_SCR); break; } if (action >= F_FN && action <= L_FN) action |= FKEY; return(action); } } else { if (metas) action |= MKEY; return(action); } } goto next_code; } int scmmap(dev_t dev, int offset, int nprot) { #ifdef PC98 if (offset > 0x48000 - PAGE_SIZE) #else if (offset > 0x20000 - PAGE_SIZE) #endif return -1; return i386_btop((VIDEOMEM + offset)); } static void kbd_wait(void) { #ifdef PC98 DELAY(30); #else int i = 500; while (i--) { if ((inb(KB_STAT) & KB_READY) == 0) break; DELAY (25); } #endif } #ifndef PC98 static void kbd_cmd(u_char command) { int i, retry = 5; do { kbd_wait(); outb(KB_DATA, command); i = 50000; while (i--) { if (inb(KB_STAT) & KB_BUF_FULL) { int val; DELAY(25); val = inb(KB_DATA); if (val == KB_ACK) return; if (val == KB_RESEND) break; } } } while (retry--); } #endif void set_mode(scr_stat *scp) { char *modetable; char special_modetable[64]; if (scp != cur_console) return; /* setup video hardware for the given mode */ #ifdef PC98 #ifdef LINE30 switch (scp->mode) { case M_PC98_80x25: /* VGA TEXT MODES */ initialize_gdc(T25_G400); break; case M_PC98_80x30: initialize_gdc(T30_G400); break; default: break; } #endif if (scp->status & UNKNOWN_MODE) { while (!(inb(0x60) & 0x20)) {} /* V-SYNC wait */ outb(0x62, 0xc); /* text off */ outb(0xA2, 0xd); /* graphics on */ } else { while (!(inb(0x60) & 0x20)) {} /* V-SYNC wait */ outb(0x62, 0xd); /* text on */ outb(0xA2, 0xc); /* graphics off */ } #else switch (scp->mode) { case M_VGA_M80x60: bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64); goto special_80x60; case M_VGA_C80x60: bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64); special_80x60: special_modetable[2] = 0x08; special_modetable[19] = 0x47; goto special_480l; case M_VGA_M80x30: bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64); goto special_80x30; case M_VGA_C80x30: bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64); special_80x30: special_modetable[19] = 0x4f; special_480l: special_modetable[9] |= 0xc0; special_modetable[16] = 0x08; special_modetable[17] = 0x3e; special_modetable[26] = 0xea; special_modetable[28] = 0xdf; special_modetable[31] = 0xe7; special_modetable[32] = 0x04; modetable = special_modetable; goto setup_mode; case M_ENH_B80x43: bcopyw(video_mode_ptr+(64*M_ENH_B80x25),&special_modetable, 64); goto special_80x43; case M_ENH_C80x43: bcopyw(video_mode_ptr+(64*M_ENH_C80x25),&special_modetable, 64); special_80x43: special_modetable[28] = 87; goto special_80x50; case M_VGA_M80x50: bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64); goto special_80x50; case M_VGA_C80x50: bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64); special_80x50: special_modetable[2] = 8; special_modetable[19] = 7; modetable = special_modetable; goto setup_mode; case M_VGA_C40x25: case M_VGA_C80x25: case M_VGA_M80x25: case M_B40x25: case M_C40x25: case M_B80x25: case M_C80x25: case M_ENH_B40x25: case M_ENH_C40x25: case M_ENH_B80x25: case M_ENH_C80x25: modetable = video_mode_ptr + (scp->mode * 64); setup_mode: set_vgaregs(modetable); scp->font_size = *(modetable + 2); /* set font type (size) */ if (scp->font_size < FONT_14) { outb(TSIDX, 0x03); outb(TSREG, 0x0A); /* font 2 */ } else if (scp->font_size >= FONT_16) { outb(TSIDX, 0x03); outb(TSREG, 0x00); /* font 0 */ } else { outb(TSIDX, 0x03); outb(TSREG, 0x05); /* font 1 */ } if (flags & CHAR_CURSOR) set_destructive_cursor(scp); break; case M_BG320: case M_CG320: case M_BG640: case M_CG320_D: case M_CG640_E: case M_CG640x350: case M_ENH_CG640: case M_BG640x480: case M_CG640x480: case M_VGA_CG320: set_vgaregs(video_mode_ptr + (scp->mode * 64)); scp->font_size = FONT_NONE; break; default: /* call user defined function XXX */ break; } #endif /* set border color for this (virtual) console */ set_border(scp->border); return; } void set_border(u_char color) { #ifdef PC98 outb(0x6c, color << 4); #else inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x11); outb(ATC, color); inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x20); /* enable Palette */ #endif } #ifndef PC98 static void set_vgaregs(char *modetable) { int i, s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */ outb(TSIDX, 0x07); outb(TSREG, 0x00); /* unlock registers */ for (i=0; i<4; i++) { /* program sequencer */ outb(TSIDX, i+1); outb(TSREG, modetable[i+5]); } outb(MISC, modetable[9]); /* set dot-clock */ outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */ outb(crtc_addr, 0x11); outb(crtc_addr+1, inb(crtc_addr+1) & 0x7F); for (i=0; i<25; i++) { /* program crtc */ outb(crtc_addr, i); if (i == 14 || i == 15) /* no hardware cursor */ outb(crtc_addr+1, 0xff); else outb(crtc_addr+1, modetable[i+10]); } inb(crtc_addr+6); /* reset flip-flop */ for (i=0; i<20; i++) { /* program attribute ctrl */ outb(ATC, i); outb(ATC, modetable[i+35]); } for (i=0; i<9; i++) { /* program graph data ctrl */ outb(GDCIDX, i); outb(GDCREG, modetable[i+55]); } inb(crtc_addr+6); /* reset flip-flop */ outb(ATC ,0x20); /* enable palette */ splx(s); } static void set_font_mode() { /* setup vga for loading fonts (graphics plane mode) */ inb(crtc_addr+6); outb(ATC, 0x30); outb(ATC, 0x01); #if SLOW_VGA outb(TSIDX, 0x02); outb(TSREG, 0x04); outb(TSIDX, 0x04); outb(TSREG, 0x06); outb(GDCIDX, 0x04); outb(GDCREG, 0x02); outb(GDCIDX, 0x05); outb(GDCREG, 0x00); outb(GDCIDX, 0x06); outb(GDCREG, 0x05); #else outw(TSIDX, 0x0402); outw(TSIDX, 0x0604); outw(GDCIDX, 0x0204); outw(GDCIDX, 0x0005); outw(GDCIDX, 0x0506); /* addr = a0000, 64kb */ #endif } static void set_normal_mode() { int s = splhigh(); /* setup vga for normal operation mode again */ inb(crtc_addr+6); outb(ATC, 0x30); outb(ATC, 0x0C); #if SLOW_VGA outb(TSIDX, 0x02); outb(TSREG, 0x03); outb(TSIDX, 0x04); outb(TSREG, 0x02); outb(GDCIDX, 0x04); outb(GDCREG, 0x00); outb(GDCIDX, 0x05); outb(GDCREG, 0x10); if (crtc_addr == MONO_BASE) { outb(GDCIDX, 0x06); outb(GDCREG, 0x0A); /* addr = b0000, 32kb */ } else { outb(GDCIDX, 0x06); outb(GDCREG, 0x0E); /* addr = b8000, 32kb */ } #else outw(TSIDX, 0x0302); outw(TSIDX, 0x0204); outw(GDCIDX, 0x0004); outw(GDCIDX, 0x1005); if (crtc_addr == MONO_BASE) outw(GDCIDX, 0x0A06); /* addr = b0000, 32kb */ else outw(GDCIDX, 0x0E06); /* addr = b8000, 32kb */ #endif splx(s); } #endif void copy_font(int operation, int font_type, char* font_image) { #ifndef PC98 int ch, line, segment, fontsize; u_char val; switch (font_type) { default: case FONT_8: segment = 0x8000; fontsize = 8; break; case FONT_14: segment = 0x4000; fontsize = 14; break; case FONT_16: segment = 0x0000; fontsize = 16; break; } outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */ outb(TSIDX, 0x01); outb(TSREG, val | 0x20); set_font_mode(); for (ch=0; ch < 256; ch++) for (line=0; line < fontsize; line++) if (operation) *(char *)pa_to_va(VIDEOMEM+(segment)+(ch*32)+line) = font_image[(ch*fontsize)+line]; else font_image[(ch*fontsize)+line] = *(char *)pa_to_va(VIDEOMEM+(segment)+(ch*32)+line); set_normal_mode(); outb(TSIDX, 0x01); outb(TSREG, val & 0xDF); /* enable screen */ #endif } static void set_destructive_cursor(scr_stat *scp) { #ifndef PC98 u_char cursor[32]; caddr_t address; int i; char *font_buffer; if (scp->font_size < FONT_14) { font_buffer = font_8; address = (caddr_t)VIDEOMEM + 0x8000; } else if (scp->font_size >= FONT_16) { font_buffer = font_16; address = (caddr_t)VIDEOMEM; } else { font_buffer = font_14; address = (caddr_t)VIDEOMEM + 0x4000; } if (scp->status & MOUSE_ENABLED) { if ((scp->cursor_saveunder & 0xff) == 0xd0) bcopyw(&scp->mouse_cursor[0], cursor, scp->font_size); else if ((scp->cursor_saveunder & 0xff) == 0xd1) bcopyw(&scp->mouse_cursor[32], cursor, scp->font_size); else if ((scp->cursor_saveunder & 0xff) == 0xd2) bcopyw(&scp->mouse_cursor[64], cursor, scp->font_size); else if ((scp->cursor_saveunder & 0xff) == 0xd3) bcopyw(&scp->mouse_cursor[96], cursor, scp->font_size); else bcopyw(font_buffer+((scp->cursor_saveunder & 0xff)*scp->font_size), cursor, scp->font_size); } else bcopyw(font_buffer + ((scp->cursor_saveunder & 0xff) * scp->font_size), cursor, scp->font_size); for (i=0; i<32; i++) if ((i >= scp->cursor_start && i <= scp->cursor_end) || (scp->cursor_start >= scp->font_size && i == scp->font_size - 1)) cursor[i] |= 0xff; while (!(inb(crtc_addr+6) & 0x08)) /* wait for vertical retrace */ ; set_font_mode(); bcopy(cursor, (char *)pa_to_va(address) + DEAD_CHAR * 32, 32); set_normal_mode(); #endif } static void set_mouse_pos(scr_stat *scp) { #ifndef PC98 static int last_xpos = -1, last_ypos = -1; /* * the margins imposed here are not ideal, we loose * a couble of pixels on the borders.. */ if (scp->mouse_xpos < 0) scp->mouse_xpos = 0; if (scp->mouse_ypos < 0) scp->mouse_ypos = 0; if (scp->mouse_xpos > (scp->xsize*8)-2) scp->mouse_xpos = (scp->xsize*8)-2; if (scp->mouse_ypos > (scp->ysize*scp->font_size)-2) scp->mouse_ypos = (scp->ysize*scp->font_size)-2; if (scp->status & UNKNOWN_MODE) return; if (scp->mouse_xpos != last_xpos || scp->mouse_ypos != last_ypos) { scp->status |= MOUSE_MOVED; scp->mouse_pos = scp->scr_buf + ((scp->mouse_ypos/scp->font_size)*scp->xsize + scp->mouse_xpos/8); if ((scp->status & MOUSE_ENABLED) && (scp->status & MOUSE_CUTTING)) { u_short *ptr; int i = 0; mark_for_update(scp, scp->mouse_cut_start - scp->scr_buf); mark_for_update(scp, scp->mouse_cut_end - scp->scr_buf); scp->mouse_cut_end = scp->mouse_pos; for (ptr = (scp->mouse_cut_start > scp->mouse_cut_end ? scp->mouse_cut_end : scp->mouse_cut_start); ptr <= (scp->mouse_cut_start > scp->mouse_cut_end ? scp->mouse_cut_start : scp->mouse_cut_end); ptr++) { cut_buffer[i++] = *ptr & 0xff; if (((ptr - scp->scr_buf) % scp->xsize) == (scp->xsize - 1)) { cut_buffer[i++] = '\n'; } } cut_buffer[i] = 0x00; } } #endif } static void mouse_cut_start(scr_stat *scp) { #ifndef PC98 int i; if (scp->status & MOUSE_ENABLED) { if (scp->mouse_pos == scp->mouse_cut_start && scp->mouse_cut_start == scp->mouse_cut_end) { cut_buffer[0] = 0x00; remove_cutmarking(scp); } else { scp->mouse_cut_start = scp->mouse_cut_end = scp->mouse_pos; cut_buffer[0] = *scp->mouse_cut_start & 0xff; cut_buffer[1] = 0x00; scp->status |= MOUSE_CUTTING; } mark_all(scp); /* delete all other screens cut markings */ for (i=0; istatus & MOUSE_ENABLED) { scp->status &= ~MOUSE_CUTTING; } #endif } static void mouse_paste(scr_stat *scp) { #ifndef PC98 if (scp->status & MOUSE_ENABLED) { struct tty *tp; u_char *ptr = cut_buffer; tp = VIRTUAL_TTY(get_scr_num()); while (*ptr) (*linesw[tp->t_line].l_rint)(scr_rmap[*ptr++], tp); } #endif } static void draw_mouse_image(scr_stat *scp) { #ifndef PC98 caddr_t address; int i; char *font_buffer; u_short buffer[32]; u_short xoffset, yoffset; u_short *crt_pos = Crtat + (scp->mouse_pos - scp->scr_buf); int font_size = scp->font_size; if (font_size < FONT_14) { font_buffer = font_8; address = (caddr_t)VIDEOMEM + 0x8000; } else if (font_size >= FONT_16) { font_buffer = font_16; address = (caddr_t)VIDEOMEM; } else { font_buffer = font_14; address = (caddr_t)VIDEOMEM + 0x4000; } xoffset = scp->mouse_xpos % 8; yoffset = scp->mouse_ypos % font_size; /* prepare mousepointer char's bitmaps */ bcopyw(font_buffer + ((*(scp->mouse_pos) & 0xff) * font_size), &scp->mouse_cursor[0], font_size); bcopyw(font_buffer + ((*(scp->mouse_pos+1) & 0xff) * font_size), &scp->mouse_cursor[32], font_size); bcopyw(font_buffer + ((*(scp->mouse_pos+scp->xsize) & 0xff) * font_size), &scp->mouse_cursor[64], font_size); bcopyw(font_buffer + ((*(scp->mouse_pos+scp->xsize+1) & 0xff) * font_size), &scp->mouse_cursor[96], font_size); for (i=0; imouse_cursor[i]<<8 | scp->mouse_cursor[i+32]; buffer[i+font_size]=scp->mouse_cursor[i+64]<<8|scp->mouse_cursor[i+96]; } /* now and-or in the mousepointer image */ for (i=0; i<16; i++) { buffer[i+yoffset] = ( buffer[i+yoffset] & ~(mouse_and_mask[i] >> xoffset)) | (mouse_or_mask[i] >> xoffset); } for (i=0; imouse_cursor[i] = (buffer[i] & 0xff00) >> 8; scp->mouse_cursor[i+32] = buffer[i] & 0xff; scp->mouse_cursor[i+64] = (buffer[i+font_size] & 0xff00) >> 8; scp->mouse_cursor[i+96] = buffer[i+font_size] & 0xff; } scp->mouse_oldpos = scp->mouse_pos; /* wait for vertical retrace to avoid jitter on some videocards */ while (!(inb(crtc_addr+6) & 0x08)) /* idle */ ; set_font_mode(); bcopy(scp->mouse_cursor, (char *)pa_to_va(address) + 0xd0 * 32, 128); set_normal_mode(); *(crt_pos) = (*(scp->mouse_pos)&0xff00)|0xd0; *(crt_pos+scp->xsize) = (*(scp->mouse_pos+scp->xsize)&0xff00)|0xd2; if (scp->mouse_xpos < (scp->xsize-1)*8) { *(crt_pos+1) = (*(scp->mouse_pos+1)&0xff00)|0xd1; *(crt_pos+scp->xsize+1) = (*(scp->mouse_pos+scp->xsize+1)&0xff00)|0xd3; } mark_for_update(scp, scp->mouse_oldpos - scp->scr_buf); mark_for_update(scp, scp->mouse_oldpos + scp->xsize + 1 - scp->scr_buf); #endif } static void remove_mouse_image(scr_stat *scp) { #ifndef PC98 u_short *crt_pos = Crtat + (scp->mouse_oldpos - scp->scr_buf); *(crt_pos) = *(scp->mouse_oldpos); *(crt_pos+1) = *(scp->mouse_oldpos+1); *(crt_pos+scp->xsize) = *(scp->mouse_oldpos+scp->xsize); *(crt_pos+scp->xsize+1) = *(scp->mouse_oldpos+scp->xsize+1); mark_for_update(scp, scp->mouse_oldpos - scp->scr_buf); mark_for_update(scp, scp->mouse_oldpos + scp->xsize + 1 - scp->scr_buf); #endif } static void draw_cutmarking(scr_stat *scp) { #ifndef PC98 u_short *ptr; u_short och, nch; for (ptr=scp->scr_buf; ptr<=(scp->scr_buf+(scp->xsize*scp->ysize)); ptr++) { nch = och = *(Crtat + (ptr - scp->scr_buf)); /* are we outside the selected area ? */ if ( ptr < (scp->mouse_cut_start > scp->mouse_cut_end ? scp->mouse_cut_end : scp->mouse_cut_start) || ptr > (scp->mouse_cut_start > scp->mouse_cut_end ? scp->mouse_cut_start : scp->mouse_cut_end)) { if (ptr != scp->cursor_pos) nch = (och & 0xff) | (*ptr & 0xff00); } else { /* are we clear of the cursor image ? */ if (ptr != scp->cursor_pos) nch = (och & 0x88ff) | (*ptr & 0x7000)>>4 | (*ptr & 0x0700)<<4; else { if (flags & CHAR_CURSOR) nch = (och & 0x88ff)|(*ptr & 0x7000)>>4|(*ptr & 0x0700)<<4; else if (!(flags & BLINK_CURSOR)) nch = (och & 0xff) | (*ptr & 0xff00); } } if (nch != och) *(Crtat + (ptr - scp->scr_buf)) = nch; } #endif } static void remove_cutmarking(scr_stat *scp) { #ifndef PC98 scp->mouse_cut_start = scp->mouse_cut_end = NULL; scp->status &= ~MOUSE_CUTTING; mark_all(scp); #endif } static void save_palette(void) { #ifndef PC98 int i; outb(PALRADR, 0x00); for (i=0x00; i<0x300; i++) palette[i] = inb(PALDATA); inb(crtc_addr+6); /* reset flip/flop */ #endif } void load_palette(void) { #ifndef PC98 int i; outb(PIXMASK, 0xFF); /* no pixelmask */ outb(PALWADR, 0x00); for (i=0x00; i<0x300; i++) outb(PALDATA, palette[i]); inb(crtc_addr+6); /* reset flip/flop */ outb(ATC, 0x20); /* enable palette */ #endif } static void do_bell(scr_stat *scp, int pitch, int duration) { if (flags & VISUAL_BELL) { if (blink_in_progress) return; blink_in_progress = 4; if (scp != cur_console) blink_in_progress += 2; blink_screen(cur_console); timeout((timeout_func_t)blink_screen, cur_console, hz/10); } else { if (scp != cur_console) pitch *= 2; sysbeep(pitch, duration); } } static void blink_screen(scr_stat *scp) { if (blink_in_progress > 1) { #ifdef PC98 if (blink_in_progress & 1){ fillw(scr_map[0x20], Crtat, scp->xsize * scp->ysize); fillw(at2pc98(kernel_default.std_color), Atrat, scp->xsize * scp->ysize); } else { fillw(scr_map[0x20], Crtat, scp->xsize * scp->ysize); fillw(at2pc98(kernel_default.rev_color), Atrat, scp->xsize * scp->ysize); } #else if (blink_in_progress & 1) fillw(kernel_default.std_color | scr_map[0x20], Crtat, scp->xsize * scp->ysize); else fillw(kernel_default.rev_color | scr_map[0x20], Crtat, scp->xsize * scp->ysize); #endif blink_in_progress--; timeout((timeout_func_t)blink_screen, scp, hz/10); } else { blink_in_progress = FALSE; mark_all(scp); if (delayed_next_scr) switch_scr(scp, delayed_next_scr - 1); } } #if defined(PC98) && defined(LINE30) /* 30line */ static void master_gdc_cmd(unsigned int cmd) { while ( (inb(0x60) & 2) != 0); outb(0x62, cmd); } static void master_gdc_prm(unsigned int pmtr) { while ( (inb(0x60) & 2) != 0); outb(0x60, pmtr); } static void master_gdc_word_prm(unsigned int wpmtr) { master_gdc_prm(wpmtr & 0x00ff); master_gdc_prm((wpmtr >> 8) & 0x00ff); } static void master_gdc_fifo_empty(void) { while ( (inb(0x60) & 4) == 0); } static void master_gdc_wait_vsync(void) { while ( (inb(0x60) & 0x20) != 0); while ( (inb(0x60) & 0x20) == 0); } static void gdc_cmd(unsigned int cmd) { while ( (inb(0xa0) & 2) != 0); outb( 0xa2, cmd); } static void gdc_prm(unsigned int pmtr) { while ( (inb(0xa0) & 2) != 0); outb( 0xa0, pmtr); } static void gdc_word_prm(unsigned int wpmtr) { gdc_prm(wpmtr & 0x00ff); gdc_prm((wpmtr >> 8) & 0x00ff); } static void gdc_fifo_empty(void) { while ( (inb(0xa0) & 0x04) == 0); } static void gdc_wait_vsync(void) { while ( (inb(0xa0) & 0x20) != 0); while ( (inb(0xa0) & 0x20) == 0); } static int check_gdc_clock(void) { if ((inb(0x31) & 0x80) == 0){ return _5MHZ; } else { return _2_5MHZ; } } static void initialize_gdc(unsigned int mode) { /* start 30line initialize */ int m_mode,s_mode,gdc_clock; gdc_clock = check_gdc_clock(); if (mode == T25_G400){ m_mode = _25L; }else{ m_mode = _30L; } s_mode = 2*mode+gdc_clock; gdc_INFO = m_mode; master_gdc_cmd(_GDC_RESET); master_gdc_cmd(_GDC_MASTER); gdc_cmd(_GDC_RESET); gdc_cmd(_GDC_SLAVE); /* GDC Master */ master_gdc_cmd(_GDC_SYNC); master_gdc_prm(0x00); /* flush less */ /* text & graph */ master_gdc_prm(master_param[m_mode][GDC_CR]); master_gdc_word_prm(((master_param[m_mode][GDC_HFP] << 10) + (master_param[m_mode][GDC_VS] << 5) + master_param[m_mode][GDC_HS])); master_gdc_prm(master_param[m_mode][GDC_HBP]); master_gdc_prm(master_param[m_mode][GDC_VFP]); master_gdc_word_prm(((master_param[m_mode][GDC_VBP] << 10) + (master_param[m_mode][GDC_LF]))); master_gdc_fifo_empty(); master_gdc_cmd(_GDC_PITCH); master_gdc_prm(MasterPCH); master_gdc_fifo_empty(); /* GDC slave */ gdc_cmd(_GDC_SYNC); gdc_prm(0x06); gdc_prm(slave_param[s_mode][GDC_CR]); gdc_word_prm((slave_param[s_mode][GDC_HFP] << 10) + (slave_param[s_mode][GDC_VS] << 5) + (slave_param[s_mode][GDC_HS])); gdc_prm(slave_param[s_mode][GDC_HBP]); gdc_prm(slave_param[s_mode][GDC_VFP]); gdc_word_prm((slave_param[s_mode][GDC_VBP] << 10) + (slave_param[s_mode][GDC_LF])); gdc_fifo_empty(); gdc_cmd(_GDC_PITCH); gdc_prm(SlavePCH[gdc_clock]); gdc_fifo_empty(); /* set Master GDC scroll param */ master_gdc_wait_vsync(); master_gdc_wait_vsync(); master_gdc_wait_vsync(); master_gdc_cmd(_GDC_SCROLL); master_gdc_word_prm(0); master_gdc_word_prm((master_param[m_mode][GDC_LF] << 4) | 0x0000); master_gdc_fifo_empty(); /* set Slave GDC scroll param */ gdc_wait_vsync(); gdc_cmd(_GDC_SCROLL); gdc_word_prm(0); if (gdc_clock == _5MHZ){ gdc_word_prm((SlaveScrlLF[mode] << 4) | 0x4000); }else{ gdc_word_prm(SlaveScrlLF[mode] << 4); } gdc_fifo_empty(); gdc_word_prm(0); if (gdc_clock == _5MHZ){ gdc_word_prm((SlaveScrlLF[mode] << 4) | 0x4000); }else{ gdc_word_prm(SlaveScrlLF[mode] << 4); } gdc_fifo_empty(); /* sync start */ gdc_cmd(_GDC_STOP); gdc_wait_vsync(); gdc_wait_vsync(); gdc_wait_vsync(); master_gdc_cmd(_GDC_START); } #endif /* 30 line */ #endif /* NSC */ Index: head/sys/pc98/pc98/wcd.c =================================================================== --- head/sys/pc98/pc98/wcd.c (revision 18264) +++ head/sys/pc98/pc98/wcd.c (revision 18265) @@ -1,1217 +1,1217 @@ /* * IDE CD-ROM driver for FreeBSD. * Supports ATAPI-compatible drives. * * Copyright (C) 1995 Cronyx Ltd. * Author Serge Vakulenko, * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Authors grant any other persons or organisations permission to use * or modify this software as long as this message is kept with the software, * all derivative works or modified versions. * * Version 1.9, Mon Oct 9 20:27:42 MSK 1995 */ #include "wdc.h" #include "wcd.h" #include "opt_atapi.h" #if NWCD > 0 && NWDC > 0 && defined (ATAPI) #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #ifdef PC98 #include #else #include #endif static d_open_t wcdropen; static d_open_t wcdbopen; static d_close_t wcdrclose; static d_close_t wcdbclose; static d_ioctl_t wcdioctl; static d_strategy_t wcdstrategy; #define CDEV_MAJOR 69 #define BDEV_MAJOR 19 extern struct cdevsw wcd_cdevsw; static struct bdevsw wcd_bdevsw = { wcdbopen, wcdbclose, wcdstrategy, wcdioctl, /*19*/ nodump, nopsize, 0, "wcd", &wcd_cdevsw, -1 }; static struct cdevsw wcd_cdevsw = { wcdropen, wcdrclose, rawread, nowrite, /*69*/ wcdioctl, nostop, nullreset, nodevtotty,/* atapi */ seltrue, nommap, wcdstrategy, "wcd", &wcd_bdevsw, -1 }; #ifndef ATAPI_STATIC static #endif int wcdattach(struct atapi*, int, struct atapi_params*, int); #define NUNIT (NWDC*2) /* Max. number of devices */ #define UNIT(d) ((minor(d) >> 3) & 3) /* Unit part of minor device number */ #define SECSIZE 2048 /* CD-ROM sector size in bytes */ #define F_BOPEN 0x0001 /* The block device is opened */ #define F_MEDIA_CHANGED 0x0002 /* The media have changed since open */ #define F_DEBUG 0x0004 /* Print debug info */ /* * Disc table of contents. */ #define MAXTRK 99 struct toc { struct ioc_toc_header hdr; struct cd_toc_entry tab[MAXTRK+1]; /* One extra for the leadout */ }; /* * Volume size info. */ struct volinfo { u_long volsize; /* Volume size in blocks */ u_long blksize; /* Block size in bytes */ } info; /* * Current subchannel status. */ struct subchan { u_char void0; u_char audio_status; u_short data_length; u_char data_format; u_char control; u_char track; u_char indx; u_long abslba; u_long rellba; }; /* * Audio Control Parameters Page */ struct audiopage { /* Mode data header */ u_short data_length; u_char medium_type; u_char reserved1[5]; /* Audio control page */ u_char page_code; #define AUDIO_PAGE 0x0e #define AUDIO_PAGE_MASK 0x4e /* changeable values */ u_char param_len; u_char flags; #define CD_PA_SOTC 0x02 /* mandatory */ #define CD_PA_IMMED 0x04 /* always 1 */ u_char reserved3[3]; u_short lb_per_sec; struct port_control { u_char channels : 4; #define CHANNEL_0 1 /* mandatory */ #define CHANNEL_1 2 /* mandatory */ #define CHANNEL_2 4 /* optional */ #define CHANNEL_3 8 /* optional */ u_char volume; } port[4]; }; /* * CD-ROM Capabilities and Mechanical Status Page */ struct cappage { /* Mode data header */ u_short data_length; u_char medium_type; #define MDT_UNKNOWN 0x00 #define MDT_DATA_120 0x01 #define MDT_AUDIO_120 0x02 #define MDT_COMB_120 0x03 #define MDT_PHOTO_120 0x04 #define MDT_DATA_80 0x05 #define MDT_AUDIO_80 0x06 #define MDT_COMB_80 0x07 #define MDT_PHOTO_80 0x08 #define MDT_NO_DISC 0x70 #define MDT_DOOR_OPEN 0x71 #define MDT_FMT_ERROR 0x72 u_char reserved1[5]; /* Capabilities page */ u_char page_code; #define CAP_PAGE 0x2a u_char param_len; u_char reserved2[2]; u_char audio_play : 1; /* audio play supported */ u_char composite : 1; /* composite audio/video supported */ u_char dport1 : 1; /* digital audio on port 1 */ u_char dport2 : 1; /* digital audio on port 2 */ u_char mode2_form1 : 1; /* mode 2 form 1 (XA) read */ u_char mode2_form2 : 1; /* mode 2 form 2 format */ u_char multisession : 1; /* multi-session photo-CD */ u_char : 1; u_char cd_da : 1; /* audio-CD read supported */ u_char cd_da_stream : 1; /* CD-DA streaming */ u_char rw : 1; /* combined R-W subchannels */ u_char rw_corr : 1; /* R-W subchannel data corrected */ u_char c2 : 1; /* C2 error pointers supported */ u_char isrc : 1; /* can return the ISRC info */ u_char upc : 1; /* can return the catalog number UPC */ u_char : 1; u_char lock : 1; /* could be locked */ u_char locked : 1; /* current lock state */ u_char prevent : 1; /* prevent jumper installed */ u_char eject : 1; /* can eject */ u_char : 1; u_char mech : 3; /* loading mechanism type */ #define MECH_CADDY 0 #define MECH_TRAY 1 #define MECH_POPUP 2 #define MECH_CHANGER 4 #define MECH_CARTRIDGE 5 u_char sep_vol : 1; /* independent volume of channels */ u_char sep_mute : 1; /* independent mute of channels */ u_char : 6; u_short max_speed; /* max raw data rate in bytes/1000 */ u_short max_vol_levels; /* number of discrete volume levels */ u_short buf_size; /* internal buffer size in bytes/1024 */ u_short cur_speed; /* current data rate in bytes/1000 */ /* Digital drive output format description (optional?) */ u_char reserved3; u_char bckf : 1; /* data valid on failing edge of BCK */ u_char rch : 1; /* high LRCK indicates left channel */ u_char lsbf : 1; /* set if LSB first */ u_char dlen: 2; #define DLEN_32 0 /* 32 BCKs */ #define DLEN_16 1 /* 16 BCKs */ #define DLEN_24 2 /* 24 BCKs */ #define DLEN_24_I2S 3 /* 24 BCKs (I2S) */ u_char : 3; u_char reserved4[2]; }; struct wcd { struct atapi *ata; /* Controller structure */ int unit; /* IDE bus drive unit */ int lun; /* Logical device unit */ int flags; /* Device state flags */ int refcnt; /* The number of raw opens */ struct buf_queue_head buf_queue; /* Queue of i/o requests */ struct atapi_params *param; /* Drive parameters table */ struct toc toc; /* Table of disc contents */ struct volinfo info; /* Volume size info */ struct audiopage au; /* Audio page info */ struct cappage cap; /* Capabilities page info */ struct audiopage aumask; /* Audio page mask */ struct subchan subchan; /* Subchannel info */ char description[80]; /* Device description */ #ifdef DEVFS void *ra_devfs_token; void *rc_devfs_token; void *a_devfs_token; void *c_devfs_token; #endif }; struct wcd *wcdtab[NUNIT]; /* Drive info by unit number */ static int wcdnlun = 0; /* Number of configured drives */ static void wcd_start (struct wcd *t); static void wcd_done (struct wcd *t, struct buf *bp, int resid, struct atapires result); static void wcd_error (struct wcd *t, struct atapires result); static int wcd_read_toc (struct wcd *t); static int wcd_request_wait (struct wcd *t, u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4, u_char a5, u_char a6, u_char a7, u_char a8, u_char a9, char *addr, int count); static void wcd_describe (struct wcd *t); static int wcd_open(dev_t dev, int rawflag); static int wcd_setchan (struct wcd *t, u_char c0, u_char c1, u_char c2, u_char c3); static int wcd_eject (struct wcd *t, int closeit); /* * Dump the array in hexadecimal format for debugging purposes. */ static void wcd_dump (int lun, char *label, void *data, int len) { u_char *p = data; printf ("wcd%d: %s %x", lun, label, *p++); while (--len > 0) printf ("-%x", *p++); printf ("\n"); } #ifndef ATAPI_STATIC static #endif int wcdattach (struct atapi *ata, int unit, struct atapi_params *ap, int debug) { struct wcd *t; struct atapires result; int lun; if (wcdnlun >= NUNIT) { printf ("wcd: too many units\n"); return (0); } if (!atapi_request_immediate) { printf("wcd: configuration error, ATAPI core code not present!\n"); printf("wcd: check `options ATAPI_STATIC' in your kernel config file!\n"); return (0); } t = malloc (sizeof (struct wcd), M_TEMP, M_NOWAIT); if (! t) { printf ("wcd: out of memory\n"); return (0); } wcdtab[wcdnlun] = t; bzero (t, sizeof (struct wcd)); t->ata = ata; t->unit = unit; lun = t->lun = wcdnlun++; t->param = ap; t->flags = F_MEDIA_CHANGED; t->refcnt = 0; if (debug) { t->flags |= F_DEBUG; /* Print params. */ wcd_dump (t->lun, "info", ap, sizeof *ap); } /* Get drive capabilities. */ result = atapi_request_immediate (ata, unit, ATAPI_MODE_SENSE, 0, CAP_PAGE, 0, 0, 0, 0, sizeof (t->cap) >> 8, sizeof (t->cap), 0, 0, 0, 0, 0, 0, 0, (char*) &t->cap, sizeof (t->cap)); /* Do it twice to avoid the stale media changed state. */ if (result.code == RES_ERR && (result.error & AER_SKEY) == AER_SK_UNIT_ATTENTION) result = atapi_request_immediate (ata, unit, ATAPI_MODE_SENSE, 0, CAP_PAGE, 0, 0, 0, 0, sizeof (t->cap) >> 8, sizeof (t->cap), 0, 0, 0, 0, 0, 0, 0, (char*) &t->cap, sizeof (t->cap)); /* Some drives have shorter capabilities page. */ if (result.code == RES_UNDERRUN) result.code = 0; if (result.code == 0) { wcd_describe (t); if (t->flags & F_DEBUG) wcd_dump (t->lun, "cap", &t->cap, sizeof t->cap); } #ifdef DEVFS t->ra_devfs_token = devfs_add_devswf(&wcd_cdevsw, dkmakeminor(lun, 0, 0), DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rwcd%da", lun); t->rc_devfs_token = devfs_add_devswf(&wcd_cdevsw, dkmakeminor(lun, 0, RAW_PART), DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rwcd%dc", lun); t->a_devfs_token = devfs_add_devswf(&wcd_bdevsw, dkmakeminor(lun, 0, 0), DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "wcd%da", lun); t->c_devfs_token = devfs_add_devswf(&wcd_bdevsw, dkmakeminor(lun, 0, RAW_PART), DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "wcd%dc", lun); #endif return (1); } void wcd_describe (struct wcd *t) { char *m; t->cap.max_speed = ntohs (t->cap.max_speed); t->cap.max_vol_levels = ntohs (t->cap.max_vol_levels); t->cap.buf_size = ntohs (t->cap.buf_size); t->cap.cur_speed = ntohs (t->cap.cur_speed); printf ("wcd%d: ", t->lun); if (t->cap.cur_speed != t->cap.max_speed) printf ("%d/", t->cap.cur_speed * 1000 / 1024); printf ("%dKb/sec", t->cap.max_speed * 1000 / 1024); if (t->cap.buf_size) printf (", %dKb cache", t->cap.buf_size); if (t->cap.audio_play) printf (", audio play"); if (t->cap.max_vol_levels) printf (", %d volume levels", t->cap.max_vol_levels); switch (t->cap.mech) { default: m = 0; break; case MECH_CADDY: m = "caddy"; break; case MECH_TRAY: m = "tray"; break; case MECH_POPUP: m = "popup"; break; case MECH_CHANGER: m = "changer"; break; case MECH_CARTRIDGE: m = "cartridge"; break; } if (m) printf (", %s%s", t->cap.eject ? "ejectable " : "", m); else if (t->cap.eject) printf (", eject"); printf ("\n"); printf ("wcd%d: ", t->lun); switch (t->cap.medium_type) { case MDT_UNKNOWN: printf ("medium type unknown"); break; case MDT_DATA_120: printf ("120mm data disc loaded"); break; case MDT_AUDIO_120: printf ("120mm audio disc loaded"); break; case MDT_COMB_120: printf ("120mm data/audio disc loaded"); break; case MDT_PHOTO_120: printf ("120mm photo disc loaded"); break; case MDT_DATA_80: printf ("80mm data disc loaded"); break; case MDT_AUDIO_80: printf ("80mm audio disc loaded"); break; case MDT_COMB_80: printf ("80mm data/audio disc loaded"); break; case MDT_PHOTO_80: printf ("80mm photo disc loaded"); break; case MDT_NO_DISC: printf ("no disc inside"); break; case MDT_DOOR_OPEN: printf ("door open"); break; case MDT_FMT_ERROR: printf ("medium format error"); break; default: printf ("medium type=0x%x", t->cap.medium_type); break; } if (t->cap.lock) printf (t->cap.locked ? ", locked" : ", unlocked"); if (t->cap.prevent) printf (", lock protected"); printf ("\n"); } static int wcd_open (dev_t dev, int rawflag) { int lun = UNIT(dev); struct wcd *t; /* Check that the device number is legal * and the ATAPI driver is loaded. */ if (lun >= wcdnlun || ! atapi_request_immediate) return (ENXIO); t = wcdtab[lun]; /* On the first open, read the table of contents. */ if (! (t->flags & F_BOPEN) && ! t->refcnt) { /* Read table of contents. */ if (wcd_read_toc (t) < 0) return (EIO); /* Lock the media. */ wcd_request_wait (t, ATAPI_PREVENT_ALLOW, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0); } if (rawflag) ++t->refcnt; else t->flags |= F_BOPEN; return (0); } int wcdbopen (dev_t dev, int flags, int fmt, struct proc *p) { return wcd_open (dev, 0); } int wcdropen (dev_t dev, int flags, int fmt, struct proc *p) { return wcd_open (dev, 1); } /* * Close the device. Only called if we are the LAST * occurence of an open device. */ int wcdbclose (dev_t dev, int flags, int fmt, struct proc *p) { int lun = UNIT(dev); struct wcd *t = wcdtab[lun]; /* If we were the last open of the entire device, release it. */ if (! t->refcnt) wcd_request_wait (t, ATAPI_PREVENT_ALLOW, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); t->flags &= ~F_BOPEN; return (0); } int wcdrclose (dev_t dev, int flags, int fmt, struct proc *p) { int lun = UNIT(dev); struct wcd *t = wcdtab[lun]; /* If we were the last open of the entire device, release it. */ if (! (t->flags & F_BOPEN) && t->refcnt == 1) wcd_request_wait (t, ATAPI_PREVENT_ALLOW, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); --t->refcnt; return (0); } /* * Actually translate the requested transfer into one the physical driver can * understand. The transfer is described by a buf and will include only one * physical transfer. */ void wcdstrategy (struct buf *bp) { int lun = UNIT(bp->b_dev); struct wcd *t = wcdtab[lun]; int x; /* Can't ever write to a CD. */ if (! (bp->b_flags & B_READ)) { bp->b_error = EROFS; bp->b_flags |= B_ERROR; biodone (bp); return; } /* If it's a null transfer, return immediatly. */ if (bp->b_bcount == 0) { bp->b_resid = 0; biodone (bp); return; } /* Process transfer request. */ bp->b_pblkno = bp->b_blkno; bp->b_resid = bp->b_bcount; x = splbio(); /* Place it in the queue of disk activities for this disk. */ tqdisksort (&t->buf_queue, bp); /* Tell the device to get going on the transfer if it's * not doing anything, otherwise just wait for completion. */ wcd_start (t); splx(x); } /* * Look to see if there is a buf waiting for the device * and that the device is not already busy. If both are true, * It dequeues the buf and creates an ATAPI command to perform the * transfer in the buf. * The bufs are queued by the strategy routine (wcdstrategy). * Must be called at the correct (splbio) level. */ static void wcd_start (struct wcd *t) { struct buf *bp = TAILQ_FIRST(&t->buf_queue); u_long blkno, nblk; /* See if there is a buf to do and we are not already doing one. */ if (! bp) return; /* Unqueue the request. */ TAILQ_REMOVE(&t->buf_queue, bp, b_act); /* Should reject all queued entries if media have changed. */ if (t->flags & F_MEDIA_CHANGED) { bp->b_error = EIO; bp->b_flags |= B_ERROR; biodone (bp); return; } /* We have a buf, now we should make a command * First, translate the block to absolute and put it in terms of the * logical blocksize of the device. * What if something asks for 512 bytes not on a 2k boundary? */ blkno = bp->b_blkno / (SECSIZE / 512); nblk = (bp->b_bcount + (SECSIZE - 1)) / SECSIZE; atapi_request_callback (t->ata, t->unit, ATAPI_READ_BIG, 0, blkno>>24, blkno>>16, blkno>>8, blkno, 0, nblk>>8, nblk, 0, 0, 0, 0, 0, 0, 0, (u_char*) bp->b_un.b_addr, bp->b_bcount, wcd_done, t, bp); } static void wcd_done (struct wcd *t, struct buf *bp, int resid, struct atapires result) { if (result.code) { wcd_error (t, result); bp->b_error = EIO; bp->b_flags |= B_ERROR; } else bp->b_resid = resid; biodone (bp); wcd_start (t); } static void wcd_error (struct wcd *t, struct atapires result) { if (result.code != RES_ERR) return; switch (result.error & AER_SKEY) { case AER_SK_NOT_READY: if (result.error & ~AER_SKEY) { /* Audio disc. */ printf ("wcd%d: cannot read audio disc\n", t->lun); return; } /* Tray open. */ if (! (t->flags & F_MEDIA_CHANGED)) printf ("wcd%d: tray open\n", t->lun); t->flags |= F_MEDIA_CHANGED; return; case AER_SK_UNIT_ATTENTION: /* Media changed. */ if (! (t->flags & F_MEDIA_CHANGED)) printf ("wcd%d: media changed\n", t->lun); t->flags |= F_MEDIA_CHANGED; return; case AER_SK_ILLEGAL_REQUEST: /* Unknown command or invalid command arguments. */ if (t->flags & F_DEBUG) printf ("wcd%d: invalid command\n", t->lun); return; } printf ("wcd%d: i/o error, status=%b, error=%b\n", t->lun, result.status, ARS_BITS, result.error, AER_BITS); } static int wcd_request_wait (struct wcd *t, u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4, u_char a5, u_char a6, u_char a7, u_char a8, u_char a9, char *addr, int count) { struct atapires result; result = atapi_request_wait (t->ata, t->unit, cmd, a1, a2, a3, a4, a5, a6, a7, a8, a9, 0, 0, 0, 0, 0, 0, addr, count); if (result.code) { wcd_error (t, result); return (EIO); } return (0); } static inline void lba2msf (int lba, u_char *m, u_char *s, u_char *f) { lba += 150; /* offset of first logical frame */ lba &= 0xffffff; /* negative lbas use only 24 bits */ *m = lba / (60 * 75); lba %= (60 * 75); *s = lba / 75; *f = lba % 75; } /* * Perform special action on behalf of the user. * Knows about the internals of this device */ int wcdioctl (dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p) { int lun = UNIT(dev); struct wcd *t = wcdtab[lun]; int error = 0; if (t->flags & F_MEDIA_CHANGED) switch (cmd) { case CDIOCSETDEBUG: case CDIOCCLRDEBUG: case CDIOCRESET: /* These ops are media change transparent. */ break; default: /* Read table of contents. */ wcd_read_toc (t); /* Lock the media. */ wcd_request_wait (t, ATAPI_PREVENT_ALLOW, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0); break; } switch (cmd) { default: return (ENOTTY); case CDIOCSETDEBUG: if (p->p_cred->pc_ucred->cr_uid) return (EPERM); t->flags |= F_DEBUG; atapi_debug (t->ata, 1); return 0; case CDIOCCLRDEBUG: if (p->p_cred->pc_ucred->cr_uid) return (EPERM); t->flags &= ~F_DEBUG; atapi_debug (t->ata, 0); return 0; case CDIOCRESUME: return wcd_request_wait (t, ATAPI_PAUSE, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0); case CDIOCPAUSE: return wcd_request_wait (t, ATAPI_PAUSE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); case CDIOCSTART: return wcd_request_wait (t, ATAPI_START_STOP, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0); case CDIOCSTOP: return wcd_request_wait (t, ATAPI_START_STOP, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); case CDIOCALLOW: return wcd_request_wait (t, ATAPI_PREVENT_ALLOW, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); case CDIOCPREVENT: return wcd_request_wait (t, ATAPI_PREVENT_ALLOW, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0); case CDIOCRESET: if (p->p_cred->pc_ucred->cr_uid) return (EPERM); return wcd_request_wait (t, ATAPI_TEST_UNIT_READY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); case CDIOCEJECT: /* Don't allow eject if the device is opened * by somebody (not us) in block mode. */ if ((t->flags & F_BOPEN) && t->refcnt) return (EBUSY); return wcd_eject (t, 0); case CDIOCCLOSE: if ((t->flags & F_BOPEN) && t->refcnt) return (0); return wcd_eject (t, 1); case CDIOREADTOCHEADER: if (! t->toc.hdr.ending_track) return (EIO); bcopy (&t->toc.hdr, addr, sizeof t->toc.hdr); break; case CDIOREADTOCENTRYS: { struct ioc_read_toc_entry *te = (struct ioc_read_toc_entry*) addr; struct toc *toc = &t->toc; struct toc buf; u_long len; u_char starting_track = te->starting_track; if (! t->toc.hdr.ending_track) return (EIO); if ( te->data_len < sizeof(toc->tab[0]) || (te->data_len % sizeof(toc->tab[0])) != 0 || te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT ) return EINVAL; if (starting_track == 0) starting_track = toc->hdr.starting_track; else if (starting_track == 170) /* Handle leadout request */ starting_track = toc->hdr.ending_track + 1; else if (starting_track < toc->hdr.starting_track || starting_track > toc->hdr.ending_track + 1) return (EINVAL); len = ((toc->hdr.ending_track + 1 - starting_track) + 1) * sizeof(toc->tab[0]); if (te->data_len < len) len = te->data_len; if (len > sizeof(toc->tab)) return EINVAL; /* Convert to MSF format, if needed. */ if (te->address_format == CD_MSF_FORMAT) { struct cd_toc_entry *e; buf = t->toc; toc = &buf; e = toc->tab + (toc->hdr.ending_track + 1 - toc->hdr.starting_track) + 1; while (--e >= toc->tab) lba2msf (ntohl(e->addr.lba), &e->addr.msf.minute, &e->addr.msf.second, &e->addr.msf.frame); } return copyout (toc->tab + starting_track - toc->hdr.starting_track, te->data, len); } case CDIOCREADSUBCHANNEL: { struct ioc_read_subchannel *args = (struct ioc_read_subchannel*) addr; struct cd_sub_channel_info data; u_long len = args->data_len; int abslba, rellba; if (len > sizeof(data) || len < sizeof(struct cd_sub_channel_header)) return (EINVAL); if (wcd_request_wait (t, ATAPI_READ_SUBCHANNEL, 0, 0x40, 1, 0, 0, 0, sizeof (t->subchan) >> 8, sizeof (t->subchan), 0, (char*)&t->subchan, sizeof (t->subchan)) != 0) return (EIO); if (t->flags & F_DEBUG) wcd_dump (t->lun, "subchan", &t->subchan, sizeof t->subchan); abslba = t->subchan.abslba; rellba = t->subchan.rellba; if (args->address_format == CD_MSF_FORMAT) { lba2msf (ntohl(abslba), &data.what.position.absaddr.msf.minute, &data.what.position.absaddr.msf.second, &data.what.position.absaddr.msf.frame); lba2msf (ntohl(rellba), &data.what.position.reladdr.msf.minute, &data.what.position.reladdr.msf.second, &data.what.position.reladdr.msf.frame); } else { data.what.position.absaddr.lba = abslba; data.what.position.reladdr.lba = rellba; } data.header.audio_status = t->subchan.audio_status; data.what.position.control = t->subchan.control & 0xf; data.what.position.addr_type = t->subchan.control >> 4; data.what.position.track_number = t->subchan.track; data.what.position.index_number = t->subchan.indx; return copyout (&data, args->data, len); } case CDIOCPLAYMSF: { struct ioc_play_msf *args = (struct ioc_play_msf*) addr; return wcd_request_wait (t, ATAPI_PLAY_MSF, 0, 0, args->start_m, args->start_s, args->start_f, args->end_m, args->end_s, args->end_f, 0, 0, 0); } case CDIOCPLAYBLOCKS: { struct ioc_play_blocks *args = (struct ioc_play_blocks*) addr; return wcd_request_wait (t, ATAPI_PLAY_BIG, 0, args->blk >> 24 & 0xff, args->blk >> 16 & 0xff, args->blk >> 8 & 0xff, args->blk & 0xff, args->len >> 24 & 0xff, args->len >> 16 & 0xff, args->len >> 8 & 0xff, args->len & 0xff, 0, 0); } case CDIOCPLAYTRACKS: { struct ioc_play_track *args = (struct ioc_play_track*) addr; u_long start, len; int t1, t2; if (! t->toc.hdr.ending_track) return (EIO); /* Ignore index fields, * play from start_track to end_track inclusive. */ if (args->end_track < t->toc.hdr.ending_track+1) ++args->end_track; if (args->end_track > t->toc.hdr.ending_track+1) args->end_track = t->toc.hdr.ending_track+1; t1 = args->start_track - t->toc.hdr.starting_track; t2 = args->end_track - t->toc.hdr.starting_track; if (t1 < 0 || t2 < 0) return (EINVAL); start = ntohl(t->toc.tab[t1].addr.lba); len = ntohl(t->toc.tab[t2].addr.lba) - start; return wcd_request_wait (t, ATAPI_PLAY_BIG, 0, start >> 24 & 0xff, start >> 16 & 0xff, start >> 8 & 0xff, start & 0xff, len >> 24 & 0xff, len >> 16 & 0xff, len >> 8 & 0xff, len & 0xff, 0, 0); } case CDIOCGETVOL: { struct ioc_vol *arg = (struct ioc_vol*) addr; error = wcd_request_wait (t, ATAPI_MODE_SENSE, 0, AUDIO_PAGE, 0, 0, 0, 0, sizeof (t->au) >> 8, sizeof (t->au), 0, (char*) &t->au, sizeof (t->au)); if (error) return (error); if (t->flags & F_DEBUG) wcd_dump (t->lun, "au", &t->au, sizeof t->au); if (t->au.page_code != AUDIO_PAGE) return (EIO); arg->vol[0] = t->au.port[0].volume; arg->vol[1] = t->au.port[1].volume; arg->vol[2] = t->au.port[2].volume; arg->vol[3] = t->au.port[3].volume; break; } case CDIOCSETVOL: { struct ioc_vol *arg = (struct ioc_vol*) addr; error = wcd_request_wait (t, ATAPI_MODE_SENSE, 0, AUDIO_PAGE, 0, 0, 0, 0, sizeof (t->au) >> 8, sizeof (t->au), 0, (char*) &t->au, sizeof (t->au)); if (error) return (error); if (t->flags & F_DEBUG) wcd_dump (t->lun, "au", &t->au, sizeof t->au); if (t->au.page_code != AUDIO_PAGE) return (EIO); error = wcd_request_wait (t, ATAPI_MODE_SENSE, 0, AUDIO_PAGE_MASK, 0, 0, 0, 0, sizeof (t->aumask) >> 8, sizeof (t->aumask), 0, (char*) &t->aumask, sizeof (t->aumask)); if (error) return (error); if (t->flags & F_DEBUG) wcd_dump (t->lun, "mask", &t->aumask, sizeof t->aumask); /* Sony-55E requires the data length field to be zeroed. */ t->au.data_length = 0; t->au.port[0].channels = CHANNEL_0; t->au.port[1].channels = CHANNEL_1; t->au.port[0].volume = arg->vol[0] & t->aumask.port[0].volume; t->au.port[1].volume = arg->vol[1] & t->aumask.port[1].volume; t->au.port[2].volume = arg->vol[2] & t->aumask.port[2].volume; t->au.port[3].volume = arg->vol[3] & t->aumask.port[3].volume; return wcd_request_wait (t, ATAPI_MODE_SELECT_BIG, 0x10, 0, 0, 0, 0, 0, sizeof (t->au) >> 8, sizeof (t->au), 0, (char*) &t->au, - sizeof (t->au)); } case CDIOCSETPATCH: { struct ioc_patch *arg = (struct ioc_patch*) addr; return wcd_setchan (t, arg->patch[0], arg->patch[1], arg->patch[2], arg->patch[3]); } case CDIOCSETMONO: return wcd_setchan (t, CHANNEL_0 | CHANNEL_1, CHANNEL_0 | CHANNEL_1, 0, 0); case CDIOCSETSTERIO: return wcd_setchan (t, CHANNEL_0, CHANNEL_1, 0, 0); case CDIOCSETMUTE: return wcd_setchan (t, 0, 0, 0, 0); case CDIOCSETLEFT: return wcd_setchan (t, CHANNEL_0, CHANNEL_0, 0, 0); case CDIOCSETRIGHT: return wcd_setchan (t, CHANNEL_1, CHANNEL_1, 0, 0); } return (error); } /* * Read the entire TOC for the disc into our internal buffer. */ static int wcd_read_toc (struct wcd *t) { int ntracks, len; struct atapires result; bzero (&t->toc, sizeof (t->toc)); bzero (&t->info, sizeof (t->info)); /* Check for the media. * Do it twice to avoid the stale media changed state. */ result = atapi_request_wait (t->ata, t->unit, ATAPI_TEST_UNIT_READY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); if (result.code == RES_ERR && (result.error & AER_SKEY) == AER_SK_UNIT_ATTENTION) { t->flags |= F_MEDIA_CHANGED; result = atapi_request_wait (t->ata, t->unit, ATAPI_TEST_UNIT_READY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } if (result.code) { wcd_error (t, result); return (EIO); } t->flags &= ~F_MEDIA_CHANGED; /* First read just the header, so we know how long the TOC is. */ len = sizeof(struct ioc_toc_header) + sizeof(struct cd_toc_entry); if (wcd_request_wait (t, ATAPI_READ_TOC, 0, 0, 0, 0, 0, 0, len >> 8, len & 0xff, 0, (char*)&t->toc, len) != 0) { err: bzero (&t->toc, sizeof (t->toc)); return (0); } ntracks = t->toc.hdr.ending_track - t->toc.hdr.starting_track + 1; if (ntracks <= 0 || ntracks > MAXTRK) goto err; /* Now read the whole schmeer. */ len = sizeof(struct ioc_toc_header) + ntracks * sizeof(struct cd_toc_entry); if (wcd_request_wait (t, ATAPI_READ_TOC, 0, 0, 0, 0, 0, 0, len >> 8, len & 0xff, 0, (char*)&t->toc, len) & 0xff) goto err; NTOHS(t->toc.hdr.len); /* Read disc capacity. */ if (wcd_request_wait (t, ATAPI_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, sizeof(t->info), 0, (char*)&t->info, sizeof(t->info)) != 0) bzero (&t->info, sizeof (t->info)); /* make fake leadout entry */ t->toc.tab[ntracks].control = t->toc.tab[ntracks-1].control; t->toc.tab[ntracks].addr_type = t->toc.tab[ntracks-1].addr_type; t->toc.tab[ntracks].track = 170; /* magic */ t->toc.tab[ntracks].addr.lba = t->info.volsize; NTOHL(t->info.volsize); NTOHL(t->info.blksize); /* Print the disc description string on every disc change. * It would help to track the history of disc changes. */ if (t->info.volsize && t->toc.hdr.ending_track && (t->flags & F_MEDIA_CHANGED) && (t->flags & F_DEBUG)) { printf ("wcd%d: ", t->lun); if (t->toc.tab[0].control & 4) printf ("%ldMB ", t->info.volsize / 512); else printf ("%ld:%ld audio ", t->info.volsize/75/60, t->info.volsize/75%60); printf ("(%ld sectors), %d tracks\n", t->info.volsize, t->toc.hdr.ending_track - t->toc.hdr.starting_track + 1); } return (0); } /* * Set up the audio channel masks. */ static int wcd_setchan (struct wcd *t, u_char c0, u_char c1, u_char c2, u_char c3) { int error; error = wcd_request_wait (t, ATAPI_MODE_SENSE, 0, AUDIO_PAGE, 0, 0, 0, 0, sizeof (t->au) >> 8, sizeof (t->au), 0, (char*) &t->au, sizeof (t->au)); if (error) return (error); if (t->flags & F_DEBUG) wcd_dump (t->lun, "au", &t->au, sizeof t->au); if (t->au.page_code != AUDIO_PAGE) return (EIO); /* Sony-55E requires the data length field to be zeroed. */ t->au.data_length = 0; t->au.port[0].channels = c0; t->au.port[1].channels = c1; t->au.port[2].channels = c2; t->au.port[3].channels = c3; return wcd_request_wait (t, ATAPI_MODE_SELECT_BIG, 0x10, 0, 0, 0, 0, 0, sizeof (t->au) >> 8, sizeof (t->au), 0, (char*) &t->au, - sizeof (t->au)); } static int wcd_eject (struct wcd *t, int closeit) { struct atapires result; /* Try to stop the disc. */ result = atapi_request_wait (t->ata, t->unit, ATAPI_START_STOP, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); if (result.code == RES_ERR && ((result.error & AER_SKEY) == AER_SK_NOT_READY || (result.error & AER_SKEY) == AER_SK_UNIT_ATTENTION)) { int err; if (!closeit) return (0); /* * The disc was unloaded. * Load it (close tray). * Read the table of contents. */ err = wcd_request_wait (t, ATAPI_START_STOP, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0); if (err) return (err); /* Read table of contents. */ wcd_read_toc (t); /* Lock the media. */ wcd_request_wait (t, ATAPI_PREVENT_ALLOW, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0); return (0); } if (result.code) { wcd_error (t, result); return (EIO); } if (closeit) return (0); /* Give it some time to stop spinning. */ tsleep ((caddr_t)&lbolt, PRIBIO, "wcdej1", 0); tsleep ((caddr_t)&lbolt, PRIBIO, "wcdej2", 0); /* Unlock. */ wcd_request_wait (t, ATAPI_PREVENT_ALLOW, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); /* Eject. */ t->flags |= F_MEDIA_CHANGED; return wcd_request_wait (t, ATAPI_START_STOP, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0); } #ifdef WCD_MODULE /* * Loadable ATAPI CD-ROM driver stubs. */ #include #include #include /* * Construct lkm_dev structures (see lkm.h). * Our bdevsw/cdevsw slot numbers are 19/69. */ MOD_DEV(wcd, LM_DT_BLOCK, BDEV_MAJOR, &wcd_bdevsw); MOD_DEV(rwcd, LM_DT_CHAR, CDEV_MAJOR, &wcd_cdevsw); /* * Function called when loading the driver. */ int wcd_load (struct lkm_table *lkmtp, int cmd) { struct atapi *ata; int n, u; if (! atapi_start) /* No ATAPI driver available. */ return EPROTONOSUPPORT; n = 0; for (ata=atapi_tab; ataport) for (u=0; u<2; ++u) /* Probing controller ata->ctrlr, unit u. */ if (ata->params[u] && ! ata->attached[u] && wcdattach (ata, u, ata->params[u], - ata->debug, ata->parent) >= 0) + ata->debug) >= 0) { /* Drive found. */ ata->attached[u] = 1; ++n; } if (! n) /* No IDE CD-ROMs found. */ return ENXIO; return 0; } /* * Function called when unloading the driver. */ int wcd_unload (struct lkm_table *lkmtp, int cmd) { struct wcd **t; for (t=wcdtab; tflags & F_BOPEN) || (*t)->refcnt) /* The device is opened, cannot unload the driver. */ return EBUSY; for (t=wcdtab; tata->attached[(*t)->unit] = 0; free (*t, M_TEMP); } wcdnlun = 0; bzero (wcdtab, sizeof(wcdtab)); return 0; } /* * Dispatcher function for the module (load/unload/stat). */ int wcd_mod (struct lkm_table *lkmtp, int cmd, int ver) { int err = 0; if (ver != LKM_VERSION) return EINVAL; if (cmd == LKM_E_LOAD) err = wcd_load (lkmtp, cmd); else if (cmd == LKM_E_UNLOAD) err = wcd_unload (lkmtp, cmd); if (err) return err; /* Register the cdevsw entry. */ lkmtp->private.lkm_dev = &rwcd_module; err = lkmdispatch (lkmtp, cmd); if (err) return err; /* Register the bdevsw entry. */ lkmtp->private.lkm_dev = &wcd_module; return lkmdispatch (lkmtp, cmd); } #endif /* WCD_MODULE */ static wcd_devsw_installed = 0; static void wcd_drvinit(void *unused) { dev_t dev; if( ! wcd_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&wcd_cdevsw, NULL); dev = makedev(BDEV_MAJOR, 0); bdevsw_add(&dev,&wcd_bdevsw, NULL); wcd_devsw_installed = 1; } } SYSINIT(wcddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,wcd_drvinit,NULL) #endif /* NWCD && NWDC && ATAPI */