diff options
Diffstat (limited to 'sys/i386')
181 files changed, 32225 insertions, 12340 deletions
diff --git a/sys/i386/boot/Makefile b/sys/i386/boot/Makefile index 3f402426ee49..3c7418f7ddda 100644 --- a/sys/i386/boot/Makefile +++ b/sys/i386/boot/Makefile @@ -20,7 +20,7 @@ # the rights to redistribute these changes. # # from: Mach, Revision 2.2 92/04/04 11:33:46 rpd -# $Id: Makefile,v 1.5 1993/12/11 20:35:15 ats Exp $ +# $Id: Makefile,v 1.10 1994/06/20 04:32:40 jkh Exp $ # wd0: @@ -32,7 +32,10 @@ wd0: NOPROG= noprog NOMAN= noman -CFLAGS = -O -DDO_BAD144 -I${.CURDIR} +# tunable loopcount parameter, waiting for keypress +BOOTWAIT?= 2400 + +CFLAGS = -O2 -DDO_BAD144 -DBOOTWAIT=${BOOTWAIT} -I${.CURDIR} LIBS= -lc INC= -I${.CURDIR}/../.. @@ -61,31 +64,31 @@ biosboot: boot bootbios: boot dd if=boot of=bootbios skip=1 -/usr/mdec/bootsd: bootbios - cp bootbios /usr/mdec/bootsd +${DESTDIR}/usr/mdec/bootsd: bootbios + cp bootbios ${DESTDIR}/usr/mdec/bootsd -/usr/mdec/sdboot: biosboot - cp biosboot /usr/mdec/sdboot +${DESTDIR}/usr/mdec/sdboot: biosboot + cp biosboot ${DESTDIR}/usr/mdec/sdboot -/usr/mdec/bootwd: /usr/mdec/bootsd - rm -f /usr/mdec/bootwd - ln /usr/mdec/bootsd /usr/mdec/bootwd +${DESTDIR}/usr/mdec/bootwd: ${DESTDIR}/usr/mdec/bootsd + rm -f ${DESTDIR}/usr/mdec/bootwd + ln ${DESTDIR}/usr/mdec/bootsd ${DESTDIR}/usr/mdec/bootwd -/usr/mdec/wdboot: /usr/mdec/sdboot - rm -f /usr/mdec/wdboot - ln /usr/mdec/sdboot /usr/mdec/wdboot +${DESTDIR}/usr/mdec/wdboot: ${DESTDIR}/usr/mdec/sdboot + rm -f ${DESTDIR}/usr/mdec/wdboot + ln ${DESTDIR}/usr/mdec/sdboot ${DESTDIR}/usr/mdec/wdboot -/usr/mdec/bootfd: /usr/mdec/bootsd - rm -f /usr/mdec/bootfd - ln /usr/mdec/bootsd /usr/mdec/bootfd +${DESTDIR}/usr/mdec/bootfd: ${DESTDIR}/usr/mdec/bootsd + rm -f ${DESTDIR}/usr/mdec/bootfd + ln ${DESTDIR}/usr/mdec/bootsd ${DESTDIR}/usr/mdec/bootfd -/usr/mdec/fdboot: /usr/mdec/sdboot - rm -f /usr/mdec/fdboot - ln /usr/mdec/sdboot /usr/mdec/fdboot +${DESTDIR}/usr/mdec/fdboot: ${DESTDIR}/usr/mdec/sdboot + rm -f ${DESTDIR}/usr/mdec/fdboot + ln ${DESTDIR}/usr/mdec/sdboot ${DESTDIR}/usr/mdec/fdboot -sd: /usr/mdec/bootsd /usr/mdec/sdboot -wd: /usr/mdec/bootwd /usr/mdec/wdboot -fd: /usr/mdec/bootfd /usr/mdec/fdboot +sd: ${DESTDIR}/usr/mdec/bootsd ${DESTDIR}/usr/mdec/sdboot +wd: ${DESTDIR}/usr/mdec/bootwd ${DESTDIR}/usr/mdec/wdboot +fd: ${DESTDIR}/usr/mdec/bootfd ${DESTDIR}/usr/mdec/fdboot all: biosboot bootbios diff --git a/sys/i386/boot/biosboot b/sys/i386/boot/biosboot Binary files differnew file mode 100644 index 000000000000..9c572c5d2422 --- /dev/null +++ b/sys/i386/boot/biosboot diff --git a/sys/i386/boot/boot b/sys/i386/boot/boot Binary files differnew file mode 100644 index 000000000000..bba109001d81 --- /dev/null +++ b/sys/i386/boot/boot diff --git a/sys/i386/boot/boot.c b/sys/i386/boot/boot.c index 19de113b0c51..ad3c7b2ee258 100644 --- a/sys/i386/boot/boot.c +++ b/sys/i386/boot/boot.c @@ -24,7 +24,7 @@ * the rights to redistribute these changes. * * from: Mach, [92/04/03 16:51:14 rvb] - * $Id: boot.c,v 1.9.2.1 1994/05/01 05:14:49 rgrimes Exp $ + * $Id: boot.c,v 1.14 1994/06/16 03:53:27 adam Exp $ */ @@ -60,8 +60,7 @@ struct exec head; int argv[10], esym; char *name; char *names[] = { - "/386bsd", "/o386bsd", "/386bsd.old", - "/vmunix", "/ovmunix", "/vmunix.old" + "/386bsd", "/o386bsd", "/386bsd.old" }; #define NUMNAMES (sizeof(names)/sizeof(char *)) @@ -76,7 +75,7 @@ int drive; ouraddr, argv[7] = memsize(0), argv[8] = memsize(1), - "$Revision: 1.9.2.1 $"); + "$Revision: 1.14 $"); printf("use hd(1,a)/386bsd to boot sd0 when wd0 is also installed\n"); gateA20(); loadstart: @@ -138,13 +137,12 @@ loadprog(howto) { if((addr + head.a_text + head.a_data) > ouraddr) { - printf("kernel will not fit below loader\n"); + printf("kernel overlaps loader\n"); return; } if((addr + head.a_text + head.a_data + head.a_bss) > 0xa0000) { - printf("kernel too big, won't fit in 640K with bss\n"); - printf("Only hope is to link the kernel for > 1MB\n"); + printf("bss exceeds 640k limit\n"); return; } } diff --git a/sys/i386/boot/boot.sym b/sys/i386/boot/boot.sym Binary files differnew file mode 100755 index 000000000000..8249abc812dc --- /dev/null +++ b/sys/i386/boot/boot.sym diff --git a/sys/i386/boot/boot2.S b/sys/i386/boot/boot2.S index 9270d4de0f3c..c49bde8d377e 100644 --- a/sys/i386/boot/boot2.S +++ b/sys/i386/boot/boot2.S @@ -24,7 +24,7 @@ * the rights to redistribute these changes. * * from: Mach, Revision 2.2 92/04/04 11:35:26 rpd - * $Id: boot2.S,v 1.3 1993/11/13 04:43:25 rgrimes Exp $ + * $Id: boot2.S,v 1.4 1994/06/22 05:52:25 jkh Exp $ */ #include "asm.h" @@ -128,14 +128,22 @@ ENTRY(boot2) mov %ax, %es /* fix up IDT entries for bdb */ - subl $2, %ebx + data32 + subl $2, %ebx /* calculate EA to check it */ + jb 1f /* give up if it would trap */ + addr32 movl %es: (%ebx), %eax /* actually movw to %ax */ addr32 movl %eax, EXT(Idt)+8*DEBUG_VECTOR /* actually movw %ax */ +1: + data32 subl $2, %ecx + jb 1f + addr32 movl %es: (%ecx), %eax /* actually movw to %ax */ addr32 movl %eax, EXT(Idt)+8*BREAKPOINT_VECTOR /* actually movw %ax */ +1: /* finished with groping in real mode segments */ pop %es diff --git a/sys/i386/boot/bootbios b/sys/i386/boot/bootbios Binary files differnew file mode 100644 index 000000000000..a0ed09db1e0c --- /dev/null +++ b/sys/i386/boot/bootbios diff --git a/sys/i386/boot/disk.c b/sys/i386/boot/disk.c index 1c7712f10517..8db41bbb6f4b 100644 --- a/sys/i386/boot/disk.c +++ b/sys/i386/boot/disk.c @@ -24,7 +24,17 @@ * the rights to redistribute these changes. * * from: Mach, Revision 2.2 92/04/04 11:35:49 rpd - * $Id: disk.c,v 1.4 1994/02/22 22:59:40 rgrimes Exp $ + * $Id: disk.c,v 1.5 1994/05/16 03:06:00 ache Exp $ + */ + +/* + * 93/10/08 bde + * If there is no 386BSD partition, initialize the label sector with + * LABELSECTOR instead of with garbage. + * + * 93/08/22 bde + * Fixed reading of bad sector table. It is at the end of the 'c' + * partition, which is not always at the end of the disk. */ #include "boot.h" @@ -80,10 +90,12 @@ devopen() #else EMBEDDED_DISKLABEL Bread(dosdev, 0); dptr = (struct dos_partition *)(((char *)0)+DOSPARTOFF); + sector = LABELSECTOR; for (i = 0; i < NDOSPART; i++, dptr++) - if (dptr->dp_typ == DOSPTYP_386BSD) + if (dptr->dp_typ == DOSPTYP_386BSD) { + sector = dptr->dp_start + LABELSECTOR; break; - sector = dptr->dp_start + LABELSECTOR; + } Bread(dosdev, sector++); dl=((struct disklabel *)0); disklabel = *dl; /* structure copy (maybe useful later)*/ @@ -113,10 +125,20 @@ devopen() int dkbbnum; struct dkbad *dkbptr; - /* find the first readable bad144 sector */ - /* some of this code is copied from ufs/disk_subr.c */ + /* find the first readable bad sector table */ + /* some of this code is copied from ufs/ufs_disksubr.c */ + /* including the bugs :-( */ /* read a bad sector table */ - dkbbnum = dl->d_secperunit - dl->d_nsectors; + +#define BAD144_PART 2 /* XXX scattered magic numbers */ +#define BSD_PART 0 /* XXX should be 2 but bad144.c uses 0 */ + if (dl->d_partitions[BSD_PART].p_offset != 0) + dkbbnum = dl->d_partitions[BAD144_PART].p_offset + + dl->d_partitions[BAD144_PART].p_size; + else + dkbbnum = dl->d_secperunit; + dkbbnum -= dl->d_nsectors; + if (dl->d_secsize > DEV_BSIZE) dkbbnum *= dl->d_secsize / DEV_BSIZE; else @@ -138,9 +160,9 @@ devopen() i += 2; } while (i < 10 && i < dl->d_nsectors); if (!do_bad144) - printf("Bad badsect table\n"); + printf("Bad bad sector table\n"); else - printf("Using bad144 bad sector at %d\n", dkbbnum+i); + printf("Using bad sector table at %d\n", dkbbnum+i); } #endif DO_BAD144 } @@ -245,7 +267,12 @@ badsect(dosdev, sector) goto no_remap; } /* otherwise find replacement sector */ - newsec = dl->d_secperunit - dl->d_nsectors - i -1; + if (dl->d_partitions[BSD_PART].p_offset != 0) + newsec = dl->d_partitions[BAD144_PART].p_offset + + dl->d_partitions[BAD144_PART].p_size; + else + newsec = dl->d_secperunit; + newsec -= dl->d_nsectors + i + 1; return newsec; } #endif DO_BAD144 diff --git a/sys/i386/boot/io.c b/sys/i386/boot/io.c index 7cb6d02ba93a..29fd128c9ce7 100644 --- a/sys/i386/boot/io.c +++ b/sys/i386/boot/io.c @@ -1,3 +1,4 @@ + /* * Mach Operating System * Copyright (c) 1992, 1991 Carnegie Mellon University @@ -24,7 +25,7 @@ * the rights to redistribute these changes. * * from: Mach, Revision 2.2 92/04/04 11:35:57 rpd - * $Id: io.c,v 1.3 1993/10/16 19:11:36 rgrimes Exp $ + * $Id: io.c,v 1.6 1994/06/16 03:53:29 adam Exp $ */ #include <i386/include/pio.h> @@ -38,10 +39,10 @@ #define KC_CMD_WIN 0xd0 /* read output port */ #define KC_CMD_WOUT 0xd1 /* write output port */ -#define KB_A20 0x9f /* enable A20, +#define KB_A20 0xdf /* enable A20, enable output buffer full interrupt enable data line - disable clock line */ + enable clock line */ /* * Gate A20 for high memory @@ -137,13 +138,24 @@ getchar() return(c); } +#if BOOTWAIT +spinwait(i) +int i; +{ + while (--i >= 0) + (void)inb(0x84); +} +#endif + gets(buf) char *buf; { int i; char *ptr=buf; - for (i = 240000; i>0; i--) +#if BOOTWAIT + for (i = BOOTWAIT; i>0; spinwait(10000),i--) +#endif if (ischar()) for (;;) switch(*ptr = getchar() & 0xff) { diff --git a/sys/i386/boot/start.S b/sys/i386/boot/start.S index bdf387667780..44de86eb89f4 100644 --- a/sys/i386/boot/start.S +++ b/sys/i386/boot/start.S @@ -24,7 +24,7 @@ * the rights to redistribute these changes. * * from: Mach, Revision 2.2 92/04/04 11:36:29 rpd - * $Id: start.S,v 1.2 1993/10/16 19:11:38 rgrimes Exp $ + * $Id: start.S,v 1.3 1994/06/13 19:27:52 jkh Exp $ */ /* @@ -99,6 +99,7 @@ start: jae hd fd: + mov $0x0, %dl # reset the disk system #ifdef DEBUG data32 diff --git a/sys/i386/conf/ECLIPSE b/sys/i386/conf/ECLIPSE new file mode 100644 index 000000000000..31eede546035 --- /dev/null +++ b/sys/i386/conf/ECLIPSE @@ -0,0 +1,66 @@ +# +# GENERICAH -- Generic machine with WD/AHx family disks +# +# $Id: GENERICAH,v 1.36 1994/06/17 06:56:59 sean Exp $ +# + +machine "i386" +cpu "I386_CPU" +cpu "I486_CPU" +cpu "I586_CPU" +ident ECLIPSE +timezone 8 dst +maxusers 10 +maxfdescs 2048 #Max file descriptors per process +options INET #InterNETworking +options ISOFS #ISO File System +options NFS #Network File System +options PCFS #MSDOS File System +options "COMPAT_43" #Compatible with BSD 4.3 +options "TCP_COMPAT_42" #TCP/IP compatible with 4.2 +options XSERVER #Xserver +options UCONSOLE #X Console support +options "FAT_CURSOR" #block cursor in syscons or pccons +options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device +options "NCONS=4" #4 virtual consoles + +config "386bsd" root on sd0 swap on sd0 dumps on sd0 + +controller isa0 + +device pci0 at isa? bio irq 9 vector pciintr +device ncr0 + +controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr +disk fd0 at fdc0 drive 0 + +controller scbus0 + +device sd0 +device sd1 + +device st0 + +device cd0 #Only need one of these, the code dynamically grows + +device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr +device npx0 at isa? port "IO_NPX" irq 13 vector npxintr + +device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr +device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr + +device lpt0 at isa? port? tty irq 7 vector lptintr + +device ix0 at isa? port 0x320 net irq 10 iomem 0xd0000 iosiz 32768 vector ixintr + +pseudo-device loop +pseudo-device ether +pseudo-device log +pseudo-device sl 1 +pseudo-device ppp 1 +pseudo-device pty 32 +pseudo-device speaker + +pseudo-device swappager +pseudo-device vnodepager +pseudo-device devpager diff --git a/sys/i386/conf/GENERICAH b/sys/i386/conf/GENERICAH index 4bba699be512..2c10cd45daa1 100644 --- a/sys/i386/conf/GENERICAH +++ b/sys/i386/conf/GENERICAH @@ -1,12 +1,13 @@ # # GENERICAH -- Generic machine with WD/AHx family disks # -# $Id: GENERICAH,v 1.25.2.2 1994/04/12 16:37:30 csgr Exp $ +# $Id: GENERICAH,v 1.36 1994/06/17 06:56:59 sean Exp $ # machine "i386" cpu "I386_CPU" cpu "I486_CPU" +cpu "I586_CPU" ident GENERICAH timezone 8 dst maxusers 10 @@ -22,14 +23,17 @@ options XSERVER #Xserver options UCONSOLE #X Console support options "FAT_CURSOR" #block cursor in syscons or pccons #options GATEWAY #Host is a Gateway (forwards packets) +options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device +options "NCONS=4" #4 virtual consoles -config "386bsd" root on wd0 swap on wd0 and sd0 dumps on wd0 +config "386bsd" root on wd0 swap on wd0 and wd1 and sd0 and sd1 dumps on wd0 controller isa0 controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr disk fd0 at fdc0 drive 0 disk fd1 at fdc0 drive 1 +#tape ft0 at fdc0 drive 2 controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr disk wd0 at wdc0 drive 0 @@ -41,6 +45,7 @@ disk wd3 at wdc1 drive 1 controller ahb0 at isa? bio irq 11 vector ahbintr controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr +controller sea0 at isa? bio irq 5 iomem 0xc8000 iosiz 0x2000 vector seaintr controller scbus0 device sd0 @@ -57,7 +62,7 @@ device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr device mcd1 at isa? port 0x340 bio irq 11 vector mcdintr -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint +device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr device npx0 at isa? port "IO_NPX" irq 13 vector npxintr device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr @@ -78,7 +83,7 @@ pseudo-device loop pseudo-device ether pseudo-device log pseudo-device sl 2 -pseudo-device pty 12 +pseudo-device pty 16 pseudo-device speaker pseudo-device swappager diff --git a/sys/i386/conf/GENERICBT b/sys/i386/conf/GENERICBT index 5311d76f7388..fae078940fa7 100644 --- a/sys/i386/conf/GENERICBT +++ b/sys/i386/conf/GENERICBT @@ -1,12 +1,13 @@ # # GENERICBT -- Generic machine with WD/BTx family disks # -# $Id: GENERICBT,v 1.25.2.2 1994/04/12 16:37:32 csgr Exp $ +# $Id: GENERICBT,v 1.35 1994/06/08 00:30:32 phk Exp $ # machine "i386" cpu "I386_CPU" cpu "I486_CPU" +cpu "I586_CPU" ident GENERICBT timezone 8 dst maxusers 10 @@ -22,14 +23,17 @@ options XSERVER #Xserver options UCONSOLE #X Console support options "FAT_CURSOR" #block cursor in syscons or pccons #options GATEWAY #Host is a Gateway (forwards packets) +options "NCONS=4" #4 virtual consoles +options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device -config "386bsd" root on wd0 swap on wd0 and sd0 dumps on wd0 +config "386bsd" root on wd0 swap on wd0 and wd1 and sd0 and sd1 dumps on wd0 controller isa0 controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr disk fd0 at fdc0 drive 0 disk fd1 at fdc0 drive 1 +#tape ft0 at fdc0 drive 2 controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr disk wd0 at wdc0 drive 0 @@ -57,7 +61,7 @@ device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr device mcd1 at isa? port 0x340 bio irq 11 vector mcdintr -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint +device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr device npx0 at isa? port "IO_NPX" irq 13 vector npxintr device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr @@ -78,7 +82,7 @@ pseudo-device loop pseudo-device ether pseudo-device log pseudo-device sl 2 -pseudo-device pty 12 +pseudo-device pty 16 pseudo-device speaker pseudo-device swappager diff --git a/sys/i386/conf/HAMSTER b/sys/i386/conf/HAMSTER new file mode 100644 index 000000000000..a3da520cd05e --- /dev/null +++ b/sys/i386/conf/HAMSTER @@ -0,0 +1,67 @@ +# +# GENERICAH -- Generic machine with WD/AHx family disks +# +# $Id: GENERICAH,v 1.36 1994/06/17 06:56:59 sean Exp $ +# + +machine "i386" +cpu "I386_CPU" +cpu "I486_CPU" +cpu "I586_CPU" +ident GENERICAH +timezone 8 dst +maxusers 10 +maxfdescs 2048 #Max file descriptors per process +options INET #InterNETworking +options ISOFS #ISO File System +options NFS #Network File System +options PCFS #MSDOS File System +options "COMPAT_43" #Compatible with BSD 4.3 +options "TCP_COMPAT_42" #TCP/IP compatible with 4.2 +options XSERVER #Xserver +options UCONSOLE #X Console support +options "FAT_CURSOR" #block cursor in syscons or pccons +#options GATEWAY #Host is a Gateway (forwards packets) +options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device +options "NCONS=8" #4 virtual consoles + +config "386bsd" root on sd0 swap on sd0 dumps on sd0 + +controller isa0 + +controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr +disk fd0 at fdc0 drive 0 + +controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr +controller scbus0 + +device sd0 +device sd1 +device sd2 +device sd3 + +device st0 +device st1 + +device cd0 #Only need one of these, the code dynamically grows + +device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr +device npx0 at isa? port "IO_NPX" irq 13 vector npxintr + +device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr +device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr + +device lpt0 at isa? port? tty irq 7 vector lptintr + +device ix0 at isa? port 0x300 net irq 10 iomem 0xd0000 iosiz 32768 vector ixintr + +pseudo-device loop +pseudo-device ether +pseudo-device log +pseudo-device sl 2 +pseudo-device pty 32 +pseudo-device speaker + +pseudo-device swappager +pseudo-device vnodepager +pseudo-device devpager diff --git a/sys/i386/conf/LAPTOP b/sys/i386/conf/LAPTOP new file mode 100644 index 000000000000..4c7ce3b2fbd7 --- /dev/null +++ b/sys/i386/conf/LAPTOP @@ -0,0 +1,68 @@ +# +# LAPTOP -- Minimal machine with IDE drives. +# +# $Id: LAPTOP,v 1.3 1994/06/22 05:58:53 jkh Exp $ +# + +machine "i386" +cpu "I386_CPU" +cpu "I486_CPU" +cpu "I586_CPU" +ident LAPTOP +timezone 8 dst +maxusers 8 +maxfdescs 256 #Max file descriptors per process +options INET #InterNETworking +options NFS #Network File System +options PCFS #MSDOS File System +options "COMPAT_43" #Compatible with BSD 4.3 +options "TCP_COMPAT_42" #TCP/IP compatible with 4.2 +options XSERVER #Xserver +options UCONSOLE #X Console support +options "FAT_CURSOR" #block cursor in syscons or pccons +options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device +options "NCONS=8" #8 virtual consoles +options LAPTOP + +# Do not use in binary distributions; put here because most laptops lack 387 +# support, and LAPTOP is not one of the kernels we build by default. +options GPL_MATH_EMULATE #Support for x87 emualtion via GPL'd emu + +# Most laptops have PS/2 style trackball mice. +options ALLOW_CONFLICT_IOADDR #no IO addr conflict checks (PS/2 mice) + +config "386bsd" root on wd0 swap on wd0 and wd1 and sd0 and sd1 dumps on wd0 + +controller isa0 + +controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr +disk fd0 at fdc0 drive 0 + +controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr +disk wd0 at wdc0 drive 0 + +device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr +device npx0 at isa? port "IO_NPX" irq 13 vector npxintr + +device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr +device lpt0 at isa? port? tty irq 7 vector lptintr +device psm0 at isa? port "IO_KBD" tty irq 12 vector psmintr + +# IBM/National PCMCIA ethernet cards +device ze0 at isa? port 0x300 net irq 5 iomem 0xd8000 vector zeintr + +# The digital speaker driver (/dev/pcaudio). Might as well since we almost +# certainly won't have a sound card! +device pca0 at isa? tty + +pseudo-device loop +pseudo-device ether +pseudo-device log +pseudo-device sl 1 +pseudo-device ppp 1 +pseudo-device pty 16 +pseudo-device speaker + +pseudo-device swappager +pseudo-device vnodepager +pseudo-device devpager diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT index 129d2c5d6ea0..9235212a9920 100644 --- a/sys/i386/conf/LINT +++ b/sys/i386/conf/LINT @@ -4,38 +4,40 @@ # # This kernel is NOT MEANT to be runnable! # -# $Id: LINT,v 1.53 1994/02/09 05:35:57 nate Exp $ +# $Id: LINT,v 1.79 1994/06/16 05:31:16 jkh Exp $ # machine "i386" cpu "I386_CPU" cpu "I486_CPU" +cpu "I586_CPU" ident LINT timezone 8 dst maxusers 10 maxfdescs 2048 #Max file descriptors per process options MATH_EMULATE #Support for x87 emulation +# Do not use in binary distributions +#options GPL_MATH_EMULATE #Support for x87 emualtion via + #new math emulator + config "386bsd" root on wd0 swap on wd0 and sd0 dumps on wd0 # # options that appear as inline #ifdef's # -options "COM_BIDIR" #Bidirectional support in sys/isa/sio.c options "COM_MULTIPORT" #Multiport support in sys/isa/sio.c -options "FIFO_TRIGGER=FIFO_TRIGGER_1" #Use this fifo value in sio.c options "COMPAT_43" #compatible with BSD 4.3 -options "SYMTAB_SPACE=104705" #This kernel needs LOTS of symtable +options "SYMTAB_SPACE=119000" #This kernel needs LOTS of symtable options GATEWAY #internetwork gateway options KTRACE #kernel tracing options "NCONS=8" #number of syscons virtual consoles options "FAT_CURSOR" #block cursor in syscons or pccons -options "STAR_SAVER" #syscons "stars" screen saver -options "FADE_SAVER" #syscons "fade" screen saver -options "SNAKE_SAVER" #syscons "snake" screen saver -options "BLANK_SAVER" #syscons "blank" screen saver + +#options ALLOW_CONFLICT_IOADDR #no IO addr conflict checks (PS/2 mice) +#options ALLOW_CONFLICT_IRQ #no IRQ conflict checks (mport serial) options "TCP_COMPAT_42" #tcp/ip compatible with 4.2 # ^^^ NOT RECOMMENDED FOR NORMAL USE @@ -53,13 +55,14 @@ options MACHVMCOMPAT #support for Mach-style vm calls options IPBROADCASTECHO=1 #send reply to broadcast pings options IPMASKAGENT=1 #send reply to icmp mask requests options TPCONS #support X.25 network-layer service +options USER_LDT #allow user-level control of i386 ldt -options EXCLUDE_CHIP_MIDI # \ sound driver options -options "EXCLUDE_MPU401" # \ exclude specified -options EXCLUDE_GUS # / device or chip -options EXCLUDE_SBPRO # / from driver +# See /sys/i386/doc/sound.doc for information about EXCLUDE options for +# the sound drivers. -options USER_LDT #allow user-level control of i386 ldt +# Multicast support. +options MULTICAST # Multicast code +options MROUTING # Multicast routing # # options that are in sys/conf/files @@ -120,13 +123,15 @@ controller ahb0 at isa? bio irq 11 vector ahbintr controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr # driver for the Seagate ST01/ST02 card, not yet finished. #controller sg0 at isa? bio irq 5 iomem 0xc8000 iosiz 0x2000 vector sgintr -#dcfclk device-driver +# driver for the Seagate ST01/ST02 or Future Domain 950 card, works +controller sea0 at isa? bio irq 5 iomem 0xc8000 iosiz 0x2000 vector seaintr controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr disk fd0 at fdc0 drive 0 disk fd1 at fdc0 drive 1 tape ft0 at fdc0 drive 2 + # driver for the Western Digital and SMCC WD80xx cards, for the Novell -# NE1000/200 card and the 3COM 3C503 card. +# NE1000/2000 card and the 3COM 3C503 card. device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr # driver for the AT&T Starlan card. device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr @@ -135,27 +140,38 @@ device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr #device ix0 at isa? port 0x320 net irq 10 iomem 0xd0000 iosiz 32768 vector ixintr # driver for the Etherlink III ( 3C509 ) card, beta version. device ep0 at isa? port 0x300 net irq 10 vector epintr +#driver for the 3c501 +device el0 at isa? port 0x300 net irq 9 vector elintr +# IBM/National PCMCIA ethernet cards +device ze0 at isa? port 0x300 net irq 5 iomem 0xd8000 vector zeintr + #special cased above: #controller isa0 -# interruptless parallel printer port driver -device lpa0 at isa? port "IO_LPT1" tty -device lpa1 at isa? port "IO_LPT2" tty + +# interruptless parallel printer port driver. NOW OBSOLETE, DON'T USE. +#device lpa0 at isa? port "IO_LPT1" tty +#device lpa1 at isa? port "IO_LPT2" tty # interrupt driven parallel printer port driver device lpt0 at isa? port "IO_LPT3" tty irq 7 vector lptintr -# Driver for Mutsumi CD-ROM players -device mcd0 at isa? port 0x300 bio irq 10 vector mcdint +# Driver for Mitsumi CD-ROM players +device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr # Driver for Logitech and ATI inport bus mice device mse0 at isa? port 0x23c tty irq 5 vector mseintr device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint #only one of pc0 or sc0 allowed -#device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr +#device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint +device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr +#PS/2 mouse driver (must follow pc0 or sc0 if enabled). Also enable +#ALLOW_CONFLICT_IOADDR option (see above) if you want to use this. +#device psm0 at isa? port "IO_KBD" tty irq 12 vector psmintr + pseudo-device speaker -#tw device-driver +device tw0 at isa? port 0x278 tty irq 5 vector twintr + controller uha0 at isa? port "IO_UHA0" bio irq 14 drq 5 vector uhaintr controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr disk wd0 at wdc0 drive 0 @@ -166,14 +182,17 @@ disk wd3 at wdc1 drive 1 device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr # Various sound card drivers. -# See /sys/i386/doc/sound.doc for more information. -device snd5 at isa? port 0x330 irq 6 drq 0 vector mpuintr +# See /sys/doc/sound.doc for more information. +device snd5 at isa? port 0x330 irq 6 vector mpuintr device snd4 at isa? port 0x220 irq 15 drq 6 vector gusintr -device snd3 at isa? port 0x388 irq 12 drq 3 vector pasintr +device snd3 at isa? port 0x388 irq 10 drq 6 vector pasintr device snd2 at isa? port 0x220 irq 7 drq 1 vector sbintr -device snd1 at isa? port 0x388 irq 0 drq 0 vector sbintr -# -# +device snd6 at isa? port 0x220 irq 7 drq 5 vector sbintr +device snd7 at isa? port 0x300 +device snd1 at isa? port 0x388 + +# The digital speaker driver (/dev/pcaudio). +device pca0 at isa? tty + # options that have not been resolved yet -# pseudo-device log diff --git a/sys/i386/conf/Makefile.i386 b/sys/i386/conf/Makefile.i386 index df5fcf18c66c..0ecf4c5e14e5 100644 --- a/sys/i386/conf/Makefile.i386 +++ b/sys/i386/conf/Makefile.i386 @@ -1,6 +1,6 @@ # Copyright 1990 W. Jolitz # from: @(#)Makefile.i386 7.1 5/10/91 -# $Id: Makefile.i386,v 1.22 1994/02/17 06:51:15 rgrimes Exp $ +# $Id: Makefile.i386,v 1.27 1994/06/16 00:45:02 adam Exp $ # # Makefile for FreeBSD # @@ -24,7 +24,15 @@ TOUCH= touch -f -c LD= /usr/bin/ld CC= cc CPP= cpp +.if defined(DEBUG) +.if defined(NOSTRIP) +STRIP= echo '(skipping) strip' +.else +STRIP= cp $@ $@.sym; strip +.endif +.else STRIP= strip +.endif DBSYM= /usr/sbin/dbsym S= ../.. @@ -51,11 +59,11 @@ NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $< NORMAL_S= ${CPP} -I. -DLOCORE ${COPTS} $< | ${AS} ${ASFLAGS} -o $*.o DRIVER_C= ${CC} -c ${CFLAGS} ${PROF} $< DRIVER_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $< -SYSTEM_OBJS=locore.o exception.o swtch.o support.o ${OBJS} param.o \ +SYSTEM_OBJS=locore.o config.o exception.o swtch.o support.o ${OBJS} param.o \ ioconf.o conf.o machdep.o SYSTEM_DEP=Makefile symbols.sort ${SYSTEM_OBJS} SYSTEM_LD_HEAD= @echo loading $@; rm -f $@ -SYSTEM_LD= @${LD} -Bstatic -Z -T ${LOAD_ADDRESS} -o $@ -X vers.o ${SYSTEM_OBJS} +SYSTEM_LD= @${LD} -Bstatic -Z -T ${LOAD_ADDRESS} -o $@ -X ${SYSTEM_OBJS} vers.o SYSTEM_LD_TAIL= @echo rearranging symbols; symorder symbols.sort $@; \ ${DBSYM} -fT ${LOAD_ADDRESS} $@; ${STRIP} -x $@; size $@; chmod 755 $@ @@ -90,7 +98,7 @@ symbols.sort: ${I386}/i386/symbols.raw locore.o: assym.s ${I386}/i386/locore.s machine/trap.h machine/psl.h \ machine/pte.h ${I386}/isa/vector.s ${I386}/isa/icu.s \ - $S/sys/errno.h machine/specialreg.h ${I386}/isa/debug.h \ + $S/sys/errno.h machine/specialreg.h \ ${I386}/isa/icu.h ${I386}/isa/isa.h vector.h $S/net/netisr.h \ machine/asmacros.h ${CPP} -I. -DLOCORE ${COPTS} ${I386}/i386/locore.s | \ @@ -104,7 +112,7 @@ exception.o: assym.s ${I386}/i386/exception.s machine/trap.h \ ${AS} ${ASFLAGS} -o exception.o swtch.o: assym.s ${I386}/i386/swtch.s \ - $S/sys/errno.h ${I386}/isa/debug.h machine/asmacros.h + $S/sys/errno.h machine/asmacros.h ${CPP} -I. ${COPTS} ${I386}/i386/swtch.s | \ ${AS} ${ASFLAGS} -o swtch.o diff --git a/sys/i386/conf/SYSCONS b/sys/i386/conf/SYSCONS deleted file mode 100644 index 2143ec61c60d..000000000000 --- a/sys/i386/conf/SYSCONS +++ /dev/null @@ -1,86 +0,0 @@ -# -# SYSCONS -- Generic machine with WD/AHx family disks and syscons -# -# $Id: SYSCONS,v 1.18 1994/02/07 10:42:04 rgrimes Exp $ -# - -machine "i386" -cpu "I386_CPU" -cpu "I486_CPU" -ident SYSCONS -timezone 8 dst -maxusers 10 -maxfdescs 2048 #Max file descriptors per process -options MATH_EMULATE #Support for x87 emulation -options INET #InterNETworking -options ISOFS #ISO File System -options NFS #Network File System -options PCFS #MSDOS File System -options "COMPAT_43" #Compatible with BSD 4.3 -options "TCP_COMPAT_42" #TCP/IP compatible with 4.2 -options XSERVER #Xserver -options UCONSOLE #X Console support -options "NCONS=8" #8 virtual consoles -options "FAT_CURSOR" #block cursor in syscons -options "STAR_SAVER" #syscons "stars" screen saver -#options GATEWAY #Host is a Gateway (forwards packets) - -config "386bsd" root on wd0 swap on wd0 and sd0 dumps on wd0 - -controller isa0 - -controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk fd0 at fdc0 drive 0 -disk fd1 at fdc0 drive 1 - -controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr -disk wd0 at wdc0 drive 0 -disk wd1 at wdc0 drive 1 - -controller wdc1 at isa? port "IO_WD2" bio irq 15 vector wdintr -disk wd2 at wdc1 drive 0 -disk wd3 at wdc1 drive 1 - -controller ahb0 at isa? bio irq 11 vector ahbintr -controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr -controller scbus0 - -device sd0 -device sd1 -device sd2 -device sd3 - -device st0 -device st1 - -device cd0 #Only need one of these, the code dynamically grows - -device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr - -device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr -device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr -device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr -device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr - -device lpt0 at isa? port "IO_LPT3" tty irq 7 vector lptintr -device lpa0 at isa? port "IO_LPT1" tty -device lpa1 at isa? port "IO_LPT2" tty - -device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr -device ed1 at isa? port 0x300 net irq 5 iomem 0xd8000 vector edintr -device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr -device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr - -device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr - -pseudo-device loop -pseudo-device ether -pseudo-device log -pseudo-device sl 2 -pseudo-device pty 4 -pseudo-device speaker - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/TIME b/sys/i386/conf/TIME new file mode 100644 index 000000000000..bc9c0d8e8752 --- /dev/null +++ b/sys/i386/conf/TIME @@ -0,0 +1,64 @@ +# +# GENERICBT -- Generic machine with WD/BTx family disks +# +# $Id: GENERICBT,v 1.35 1994/06/08 00:30:32 phk Exp $ +# + +machine "i386" +cpu "I386_CPU" +cpu "I486_CPU" +cpu "I586_CPU" +ident TIME +timezone 8 dst +maxusers 10 +maxfdescs 1024 #Max file descriptors per process +options INET #InterNETworking +options ISOFS #ISO File System +options NFS #Network File System +options PCFS #MSDOS File System +options "COMPAT_43" #Compatible with BSD 4.3 +options "TCP_COMPAT_42" #TCP/IP compatible with 4.2 +options XSERVER #Xserver +options UCONSOLE #X Console support +options "FAT_CURSOR" #block cursor in syscons or pccons +options "NCONS=4" #4 virtual consoles +options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device + +config "386bsd" root on sd0 swap on sd0 dumps on sd0 + +controller isa0 + +controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr +disk fd0 at fdc0 drive 0 + +controller bt0 at isa? port "IO_BT0" bio irq 11 vector btintr +controller scbus0 + +device sd0 +device sd1 + +device st0 + +device cd0 #Only need one of these, the code dynamically grows + +device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr +device npx0 at isa? port "IO_NPX" irq 13 vector npxintr + +device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr +device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr + +device lpt0 at isa? port? tty irq 7 vector lptintr + +device ed0 at isa? port 0x360 net irq 10 iomem 0xd8000 iosiz 16384 vector edintr +#device ix0 at isa? port 0x320 net irq 10 iomem 0xd8000 iosiz 32768 vector ixintr + +pseudo-device loop +pseudo-device ether +pseudo-device log +pseudo-device sl 2 +pseudo-device pty 32 +pseudo-device speaker + +pseudo-device swappager +pseudo-device vnodepager +pseudo-device devpager diff --git a/sys/i386/conf/WCARCH b/sys/i386/conf/WCARCH new file mode 100644 index 000000000000..f228ec5752d8 --- /dev/null +++ b/sys/i386/conf/WCARCH @@ -0,0 +1,67 @@ +# +# GENERICBT -- Generic machine with WD/BTx family disks +# +# $Id: GENERICBT,v 1.35 1994/06/08 00:30:32 phk Exp $ +# + +machine "i386" +cpu "I386_CPU" +cpu "I486_CPU" +cpu "I586_CPU" +ident TIME +timezone 8 dst +maxusers 10 +maxfdescs 1024 #Max file descriptors per process +options INET #InterNETworking +options ISOFS #ISO File System +options NFS #Network File System +options PCFS #MSDOS File System +options "COMPAT_43" #Compatible with BSD 4.3 +options "TCP_COMPAT_42" #TCP/IP compatible with 4.2 +options XSERVER #Xserver +options UCONSOLE #X Console support +options "FAT_CURSOR" #block cursor in syscons or pccons +options "NCONS=4" #4 virtual consoles +options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device + +config "386bsd" root on sd0 swap on sd0 dumps on sd0 + +controller isa0 + +controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr +disk fd0 at fdc0 drive 0 + +# driver for the Adaptec 154x SCSI cards. +controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr + +#controller bt0 at isa? port "IO_BT0" bio irq 11 vector btintr +#controller scbus0 + +device sd0 +device sd1 + +device st0 + +device cd0 #Only need one of these, the code dynamically grows + +device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr +device npx0 at isa? port "IO_NPX" irq 13 vector npxintr + +device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr +device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr + +device lpt0 at isa? port? tty irq 7 vector lptintr + +device ed0 at isa? port 0x360 net irq 10 iomem 0xd8000 iosiz 16384 vector edintr +#device ix0 at isa? port 0x320 net irq 10 iomem 0xd8000 iosiz 32768 vector ixintr + +pseudo-device loop +pseudo-device ether +pseudo-device log +pseudo-device sl 2 +pseudo-device pty 32 +pseudo-device speaker + +pseudo-device swappager +pseudo-device vnodepager +pseudo-device devpager diff --git a/sys/i386/conf/WCARCHIVE b/sys/i386/conf/WCARCHIVE new file mode 100644 index 000000000000..0ce90784f3fd --- /dev/null +++ b/sys/i386/conf/WCARCHIVE @@ -0,0 +1,73 @@ +# +# GENERICAH -- Generic machine with WD/AHx family disks +# +# $Id: GENERICAH,v 1.36 1994/06/17 06:56:59 sean Exp $ +# + +machine "i386" +cpu "I386_CPU" +cpu "I486_CPU" +cpu "I586_CPU" +ident WCARCHIVE +timezone 8 dst +maxusers 40 +maxfdescs 1024 #Max file descriptors per process +options INET #InterNETworking +options ISOFS #ISO File System +options NFS #Network File System +options PCFS #MSDOS File System +options "COMPAT_43" #Compatible with BSD 4.3 +options "TCP_COMPAT_42" #TCP/IP compatible with 4.2 +options XSERVER #Xserver +options UCONSOLE #X Console support +options "FAT_CURSOR" #block cursor in syscons or pccons +options GATEWAY #Host is a Gateway (forwards packets) +options "SCSI_DELAY=10" #Be pessimistic about Joe SCSI device +options "NCONS=4" #4 virtual consoles +options "NMBCLUSTERS=1024" #Needed space for many ftp connections + +config "386bsd" root on sd0 swap on sd0 and sd1 and sd2 dumps on sd0 + +controller isa0 + +controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr +disk fd0 at fdc0 drive 0 + +controller ahb0 at isa? bio irq 11 vector ahbintr +controller ahb1 at isa? bio irq 12 vector ahbintr + +#controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr +#controller aha1 at isa? port 0x230 bio irq 12 drq 6 vector ahaintr + +controller scbus0 +controller scbus1 + +device sd0 +device sd1 +device sd2 +device sd3 +device sd4 +device sd5 +device sd6 +device sd7 + +device cd0 #Only need one of these, the code dynamically grows + +device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr +device npx0 at isa? port "IO_NPX" irq 13 vector npxintr + +device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr + +device lpt0 at isa? port? tty irq 7 vector lptintr + +device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 flags 0x4 vector edintr + +pseudo-device loop +pseudo-device ether +pseudo-device log +pseudo-device pty 32 +pseudo-device speaker + +pseudo-device swappager +pseudo-device vnodepager +pseudo-device devpager diff --git a/sys/i386/conf/devices.i386 b/sys/i386/conf/devices.i386 index c388159afee3..3abc0605aac1 100644 --- a/sys/i386/conf/devices.i386 +++ b/sys/i386/conf/devices.i386 @@ -1,6 +1,6 @@ # This file tells what major numbers the various possible swap devices have. # -# $Id: devices.i386,v 1.5 1994/01/04 20:09:28 nate Exp $ +# $Id: devices.i386,v 1.6 1994/03/21 20:48:49 ats Exp $ # wd 0 dk 1 @@ -10,3 +10,4 @@ sd 4 st 5 cd 6 mcd 7 +scd 8 diff --git a/sys/i386/conf/files.i386 b/sys/i386/conf/files.i386 index 1b84847c7713..92a79684af05 100644 --- a/sys/i386/conf/files.i386 +++ b/sys/i386/conf/files.i386 @@ -1,7 +1,7 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # -# $Id: files.i386,v 1.25 1994/02/07 04:27:59 alm Exp $ +# $Id: files.i386,v 1.39 1994/06/16 05:31:20 jkh Exp $ # i386/i386/autoconf.c standard device-driver i386/i386/cons.c standard @@ -17,53 +17,61 @@ i386/i386/pmap.c standard i386/i386/sys_machdep.c standard i386/i386/trap.c standard i386/i386/vm_machdep.c standard +i386/i386/random.s optional multicast i386/isa/aha1542.c optional aha device-driver i386/isa/aha1742.c optional ahb device-driver i386/isa/bt742a.c optional bt device-driver i386/isa/clock.c standard i386/isa/com.c optional com device-driver -i386/isa/dcfclk.c optional dcfclk device-driver i386/isa/fd.c optional fd device-driver i386/isa/ft.c optional ft device-driver i386/isa/if_ed.c optional ed device-driver +i386/isa/if_el.c optional el device-driver i386/isa/if_ep.c optional ep device-driver i386/isa/if_ie.c optional ie device-driver i386/isa/if_is.c optional is device-driver i386/isa/if_ix.c optional ix device-driver i386/isa/isa.c optional isa device-driver -i386/isa/lpa.c optional lpa device-driver i386/isa/lpt.c optional lpt device-driver i386/isa/mcd.c optional mcd device-driver i386/isa/mse.c optional mse device-driver i386/isa/npx.c optional npx device-driver i386/isa/syscons.c optional sc device-driver i386/isa/pccons.c optional pc device-driver +i386/isa/pcaudio.c optional pca device-driver i386/isa/psm.c optional psm device-driver i386/isa/sb.c optional sb device-driver +i386/isa/scd.c optional scd device-driver +i386/isa/seagate.c optional sea device-driver i386/isa/sg.c optional sg device-driver i386/isa/sio.c optional sio device-driver -i386/isa/sound/adlib_card.c optional snd device-driver -i386/isa/sound/audio.c optional snd device-driver -i386/isa/sound/dev_table.c optional snd device-driver -i386/isa/sound/dmabuf.c optional snd device-driver -i386/isa/sound/dsp.c optional snd device-driver -i386/isa/sound/gus_card.c optional snd device-driver -i386/isa/sound/gus_midi.c optional snd device-driver -i386/isa/sound/gus_vol.c optional snd device-driver -i386/isa/sound/gus_wave.c optional snd device-driver -i386/isa/sound/midi.c optional snd device-driver -i386/isa/sound/midibuf.c optional snd device-driver -i386/isa/sound/mpu401.c optional snd device-driver -i386/isa/sound/opl3.c optional snd device-driver -i386/isa/sound/pas2_card.c optional snd device-driver -i386/isa/sound/pas2_midi.c optional snd device-driver -i386/isa/sound/pas2_mixer.c optional snd device-driver -i386/isa/sound/pas2_pcm.c optional snd device-driver -i386/isa/sound/patmgr.c optional snd device-driver -i386/isa/sound/pro_midi.c optional snd device-driver -i386/isa/sound/sb_card.c optional snd device-driver -i386/isa/sound/sb_dsp.c optional snd device-driver -i386/isa/sound/sequencer.c optional snd device-driver +i386/isa/sound/adlib_card.c optional snd device-driver +i386/isa/sound/audio.c optional snd device-driver +i386/isa/sound/dev_table.c optional snd device-driver +i386/isa/sound/dmabuf.c optional snd device-driver +i386/isa/sound/gus_card.c optional snd device-driver +i386/isa/sound/gus_midi.c optional snd device-driver +i386/isa/sound/gus_vol.c optional snd device-driver +i386/isa/sound/gus_wave.c optional snd device-driver +i386/isa/sound/ics2101.c optional snd device-driver +i386/isa/sound/midi.c optional snd device-driver +i386/isa/sound/midibuf.c optional snd device-driver +i386/isa/sound/mpu401.c optional snd device-driver +i386/isa/sound/opl3.c optional snd device-driver +i386/isa/sound/pas2_card.c optional snd device-driver +i386/isa/sound/pas2_midi.c optional snd device-driver +i386/isa/sound/pas2_mixer.c optional snd device-driver +i386/isa/sound/pas2_pcm.c optional snd device-driver +i386/isa/sound/patmgr.c optional snd device-driver +i386/isa/sound/pro_midi.c optional snd device-driver +i386/isa/sound/sb16_dsp.c optional snd device-driver +i386/isa/sound/sb16_midi.c optional snd device-driver +i386/isa/sound/sb_card.c optional snd device-driver +i386/isa/sound/sb_dsp.c optional snd device-driver +i386/isa/sound/sb_midi.c optional snd device-driver +i386/isa/sound/sb_mixer.c optional snd device-driver +i386/isa/sound/sequencer.c optional snd device-driver +i386/isa/sound/sound_switch.c optional snd device-driver i386/isa/sound/soundcard.c optional snd device-driver i386/isa/spkr.c optional speaker i386/isa/tw.c optional tw device-driver @@ -76,3 +84,39 @@ i386/isa/pcvt/pcvt_out.c optional vt device-driver i386/isa/pcvt/pcvt_kbd.c optional vt device-driver i386/isa/pcvt/pcvt_vtf.c optional vt device-driver i386/isa/pcvt/pcvt_ext.c optional vt device-driver +i386/isa/if_ze.c optional ze device-driver +gnu/fpemul/div_small.s optional gpl_math_emulate +gnu/fpemul/errors.c optional gpl_math_emulate +gnu/fpemul/fpu_arith.c optional gpl_math_emulate +gnu/fpemul/fpu_aux.c optional gpl_math_emulate +gnu/fpemul/fpu_entry.c optional gpl_math_emulate +gnu/fpemul/fpu_etc.c optional gpl_math_emulate +gnu/fpemul/fpu_trig.c optional gpl_math_emulate +gnu/fpemul/get_address.c optional gpl_math_emulate +gnu/fpemul/load_store.c optional gpl_math_emulate +gnu/fpemul/poly_2xm1.c optional gpl_math_emulate +gnu/fpemul/poly_atan.c optional gpl_math_emulate +gnu/fpemul/poly_div.s optional gpl_math_emulate +gnu/fpemul/poly_l2.c optional gpl_math_emulate +gnu/fpemul/poly_mul64.s optional gpl_math_emulate +gnu/fpemul/poly_sin.c optional gpl_math_emulate +gnu/fpemul/poly_tan.c optional gpl_math_emulate +gnu/fpemul/polynomial.s optional gpl_math_emulate +gnu/fpemul/reg_add_sub.c optional gpl_math_emulate +gnu/fpemul/reg_compare.c optional gpl_math_emulate +gnu/fpemul/reg_constant.c optional gpl_math_emulate +gnu/fpemul/reg_div.s optional gpl_math_emulate +gnu/fpemul/reg_ld_str.c optional gpl_math_emulate +gnu/fpemul/reg_mul.c optional gpl_math_emulate +gnu/fpemul/reg_norm.s optional gpl_math_emulate +gnu/fpemul/reg_round.s optional gpl_math_emulate +gnu/fpemul/reg_u_add.s optional gpl_math_emulate +gnu/fpemul/reg_u_div.s optional gpl_math_emulate +gnu/fpemul/reg_u_mul.s optional gpl_math_emulate +gnu/fpemul/reg_u_sub.s optional gpl_math_emulate +gnu/fpemul/wm_shrx.s optional gpl_math_emulate +gnu/fpemul/wm_sqrt.s optional gpl_math_emulate +i386/pci/ncr.c optional ncr device-driver +i386/pci/pci.c optional pci device-driver +i386/pci/pcibios.c optional pci device-driver +i386/pci/pci_config.c optional pci device-driver diff --git a/sys/i386/doc/Changes b/sys/i386/doc/Changes deleted file mode 100644 index 590f087f85bf..000000000000 --- a/sys/i386/doc/Changes +++ /dev/null @@ -1,209 +0,0 @@ -Hello, Emacs, this is an -*- Indented-Text -*- file! - -$Id: Changes,v 1.15 1994/02/21 23:03:09 rgrimes Exp $ - -This file is intended to keep track of important kernel and user -changes in FreeBSD between releases. Entries are in reverse -chronological order; userids can be decoded with the chart at the end -of this file. - -Since 1.1 BETA: - -Between 1.1 BETA and 1.0.2: -- Improved lpt driver, should no longer lock up when lprm is done on - an active job. Fixed up the probe routine so that it works on most - if not all printers now. (csgr/rgrimes) - -- Substantial changes to system configuration; you MUST re-build - `config' before attempting to build a 1.1 kernel. (nate/martin) - -- Improved the quality of the information given to the user when - a fatal trap occurs. (davidg) - -- Added support in the if_ed driver for the WD8013W, WD8003W, and - WD8003EB. (davidg) - -- Change to generic console code to eliminate console hangs with all - `pc' consoles. (davidg) - -- Upgrade to new version of syscons which handles the `hanging console' - problem and adds some new features and code cleanup. (nate) - -- Various TCP bugs fixed - don't forward loopback packets; Nagel - congestion avoidence - immediately ack small packets. (davidg) - -- TCP debugging code is now truly optional, thus reducing kernel size - when it is disabled (the default). (davidg) - -- Because the `sio' FIFOs are now configurable, the `com' driver is no - longer supported. (team sighs with relief) - -- Performance and stylistic improvements to the `sio' serial driver. - Probe code now works somewhat better for oddball devices. The 16550 - FIFO length is now configurable using `flags' in the config - declaration. (ache/bde) - -- Performance improvements and complete implementation of POSIX VMIN - and VTIME for the generic TTY code. (ache/bde) - -- Crash dumps on SCSI disks now work and are standard. (rgrimes/davidg) - -- QMAGIC is now the official default executable format. (davidg) - -- Network booting is now supported, as is booting from DOS. (martin) - -- Local LDTs are now supported for WINE (based on work by John Brezak). - (hsu/davidg) - -- DDB will now print symbolic arguments and line numbers in - backtraces (from John Brezak). (davidg) - -- Added four pattern memory test to eliminate problems with buggy - chipsets that incorrectly map memory, and to find problems with - defective memory. The memory sizing code has been improved to - further eliminate problems with buggy chipsets/BIOSs. (davidg) - -- USE_486_WRITE_PROTECT is now gone; the system will automatically - detect 486 CPUs and behave accordingly. (davidg) - -- Added SysV IPC, messaging, and semaphore code by Danny Boulet. - (hsu/davidg) - -- Because of the VM system changes, Paul Kranenburg's process - filesystem is now MANDATORY in order for `ps' and friends to be able - to dig up process information which has been paged out. (davidg) - -- Substantial VM system improvements: (dyson/davidg) - o FreeBSD once again works on 4-MB machines. - o Maximum and default size limits set to reasonable values. - o The user area is now in the process address space, and can - now be paged out along with the rest of the process. - o Process page tables can now be paged out. - o The physical map (pmap) module has been mostly rewritten for - efficiency, and is considerably faster than it used to be. - o The pageout system now actually implements modified LRU. - o Process RSS soft limits implemented. Hooks are in place for - RSS hard limits. - o Pagers can now do multiple-page operations ("page fault - clustering"), and page fault read behind and read ahead - have been implemented to take advantage of this. - o The vnode pager no longer drags pages through the buffer - cache, eliminating an expensive memory copy and flushing - of cached data. - o When the system runs out of swap space, the faulting process - is killed off (with a message to syslog); the old code would - just deadlock. - o Swap space allocation is much more efficient, and swap - striping actually works now. It's now possible for every - last block of swap space to be used before the systems runs - out. - o The pagedaemon's algorithms are considerably improved, thus - reducing the amount of CPU time used by the pagedaemon. - o All kernels MUST now load at virtual address 0xf010000, and - the lower 640k is reclaimed for system use. This removes - the 640K kernel size limit. - o VM object cache size is now dynamic and a function of the - kernel 'maxusers' parameter. - o Memory in the I/O hole is explicitly marked non-cacheable. - -- Added 3C509 driver written by Herb Peyerl. (ats/nate) - -- Added a new 'wd' driver which does a much better job of probing, handles - stray interrupts better, and supports multiple controllers. In addition - this driver supports DOS partitions much better and conforms to ATA specs - much better. NB: configuration lines for this driver are different - than those for pervious versions; see a GENERIC for details. - (nate/bde/guido) - -- Added support in the if_ed driver for the Toshiba ethernet cards.The - support must be enabled with an "options TOSH_ETHER" in the config - file. Done it this way, because i don't know how widespread the cards - are. (ats) - -- Added support in the if_ed driver for the SMC Ultra via patches from - Glen Lowe. (davidg) - -- Updated Mitsumi CD driver to work with FX models. (jkh/Gary - Clark II) - -- Add extended formats set to floppy driver, improve autoconfiguration, - add "fdformat" utility for floppy formatting. - The format of floppy disk minor numbers has changed, thus - necessitating a new `MAKEDEV fd'. (ache/joerg/Serge Vakulenko) - -- Add XNTPD to contrib section, and (un-compilable) kernel support for - same to /sys/kern. (wollman) - -- Use linker-constructed sets to initialize certain system tables - rather than manually enumerating all the options in the source files. - This makes certain pseudo-devices and all image activators drop-in at - link time, if desired. (wollman) - -- Added YP code from Theo Deraadt. (paul/nate) - -- Make all mandatory options ``standard''. (wollman) - -- Update `wt' driver to support more devices and controllers. The - driver will also auto-detect tape density on models which support - it. The structure of `wt' device minor numbers has changed; - `MAKEDEV wt' must be run to create the new device nodes. - -- Re-design execve() system call to allow for multiple ``image activators'' - which recognize and load various file formats. Currently only - a.out and interpreted formats are recognized. (davidg) - -- Provide the address of the faulting reference to signal handlers, to - make life easier for smart garbage-collection algorithms. (hsu) - -- New, improved process tracing code from Sean Eric Fagan. (davidg) - -- Re-organize locore into several different source files according to - function. (davidg) - -- On panic, don't reboot right away but give the user some time to - abort the reboot or at least write down the panic message. (davidg) - -- Kernel timezone handling is now delegated to an external program, - `adjkerntz'. No more bogus summer time jumps. (ache) - -- Separate all IP-related variables that users might want to modify into - netinet/in_var.c. (wollman) - -- New, redesigned SCSI system; should run faster and have fewer bugs. - (julian) - -- Make it possible to mmap(2) /dev/mem. (julian) - -Between 1.0.2 and 1.0: - -Between 1.0 EPSILON and 1.0 GAMMA: - -Between 1.0 GAMMA and 1.0 BETA: - -Between 1.0 BETA and 1.0 ALPHA: - -Between 1.0 ALPHA and Patchkit 0.2.4: - -Userids map as follows: -ache Andrew Chernov -alm Andrew Moore -ats Andreas Schulz -chmr Christoph Robitscho -csgr Geoff Rehmet -davidg David Greenman -dyson John Dyson -guido Guido van Rooij -hsu Jeffrey Hsu -jkh Jordan Hubbard -joerg Joerg Wunsch -jtc J.T. Conklin -ljo L. Jonas Olson -martin Martin Renters -nate Nate Williams -paul Paul Richards -proven Chris Provenzano -rich Rich Murphey -rls Rob Shady -smace Scott Mace -swallace Steven Wallace -wollman Garrett Wollman diff --git a/sys/i386/doc/Makefile b/sys/i386/doc/Makefile deleted file mode 100644 index 67fb36b0ceac..000000000000 --- a/sys/i386/doc/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# $Id: Makefile,v 1.1 1993/11/06 00:07:42 wollman Exp $ -# -# Makefile for /sys/i386/doc -# This creates options.info and options.doc from options.texi, if the -# GNU makeinfo program is present, and fails miserably otherwise. -# - -all: options.info options.doc - -options.info: options.texi - makeinfo options.texi - -options.doc: options.texi - makeinfo -o options.doc+ --no-headers options.texi - sed '/^General Index/,$$d' < options.doc+ > options.doc - rm -f options.doc+ - -clean: - rm -f options.info options.doc options.doc+ diff --git a/sys/i386/doc/ata/ata-1 b/sys/i386/doc/ata/ata-1 deleted file mode 100644 index 5486217b026b..000000000000 --- a/sys/i386/doc/ata/ata-1 +++ /dev/null @@ -1,45 +0,0 @@ - Information Processing Systems -- - - Common Access Method -- - - AT Attachment - - -1. Scope - -This standard defines the CAM (Common Access Method) AT Attachment. - -The CAM Committee was formed in October, 1988 and the first working document -of the AT Attachment was introduced in March, 1989. - -1.1 Description of Clauses - -Clause 1 contains the Scope and Purpose. - -Clause 2 contains Referenced and Related International Standards. - -Clause 3 contains the General Description. - -Clause 4 contains the Glossary. - -Clause 5 contains the electrical and mechanical characteristics; covering -the interface cabling requirements of the DC, data cables and connectors. - -Clause 6 contains the signal descriptions of the AT Attachment interface. - -Clause 7 contains descriptions of the registers of the AT Attachment -interface. - -Clause 8 describes the programming requirements of the AT Attachment -interface. - -Clause 9 contains descriptions of the commands of the AT Attachment interface. - -Clause 10 contains an overview of the protocol of the AT Attachment interface. - -Clause 11 contains the interface timing diagrams. - -Annex A is informative. - -Annex B is informative. - diff --git a/sys/i386/doc/ata/ata-10 b/sys/i386/doc/ata/ata-10 deleted file mode 100644 index f4c0336944d4..000000000000 --- a/sys/i386/doc/ata/ata-10 +++ /dev/null @@ -1,213 +0,0 @@ -10. Protocol Overview - -Commands can be grouped into different classes according to the protocols -followed for command execution. The command classes with their associated -protocols are defined below. - -For all commands, the host first checks if BSY=1, and should proceed no -further unless and until BSY=0. For most commands, the host will also wait for -DRDY=1 before proceeding. Those commands shown with DRDY=x can be executed -when DRDY=0. - -Data transfers may be accomplished in more ways than are described below, but -these sequences should work with all known implementations of ATA drives. - -10.1 PIO Data In Commands - -This class includes: - - - Identify Drive - - Read Buffer - - Read Long - - Read Sector(s) - -Execution includes the transfer of one or more 512 byte (>512 bytes on Read -Long) sectors of data from the drive to the host. - - a) The host writes any required parameters to the Features, Sector Count, - Sector Number, Cylinder and Drive/Head registers. - b) The host writes the command code to the Command Register. - c) The drive sets BSY and prepares for data transfer. - d) When a sector of data is available, the drive sets DRQ and clears BSY - prior to asserting INTRQ. - e) After detecting INTRQ, the host reads the Status Register, then reads one - sector of data via the Data Register. In response to the Status Register - being read, the drive negates INTRQ. - f) The drive clears DRQ. If transfer of another sector is required, the drive - also sets BSY and the above sequence is repeated from d). - -10.1.1 PIO Read Command - - +- a) -+-- b) -+ +- e) -+--------+ +- e) -+--------+ - |Setup | Issue | | Read |Transfer| | Read |Transfer| - | |Command| |Status| Data |:::::::|Status| Data | - +------+-------+ +------+--------+ +------+--------+ - |BSY=0 | |BSY=1 |BSY=0 | |BSY=1 |BSY=0 | |BSY=1 - |DRDY=1 | | | | | | - |DRQ=1 | |DRQ=0 |DRQ=1 | |DRQ=0 - |Assert|Negate | |Assert|Negate - INTRQ INTRQ INTRQ INTRQ - -If Error Status is presented, the drive is prepared to transfer data, and it -is at the host's discretion that the data is transferred. - -10.1.2 PIO Read Aborted Command - - +- a) -+-- b) -+ +- e) -+ - |Setup | Issue | | Read | - | |Command| |Status| - +------+-------+ +------+ - |BSY=0 | |BSY=1 |BSY=0 | - |DRDY=1 | | - |DRQ=1 |DRQ=0 - |Assert|Negate - INTRQ INTRQ - -Although DRQ=1, there is no data to be transferred under this condition. - -10.2 PIO Data Out Commands - -This class includes: - - - Format - - Write Buffer - - Write Long - - Write Sector(s) - -Execution includes the transfer of one or more 512 byte (>512 bytes on Write -Long) sectors of data from the drive to the host. - - a) The host writes any required parameters to the Features, Sector Count, - Sector Number, Cylinder and Drive/Head registers. - b) The host writes the command code to the Command Register. - c) The drive sets DRQ when it is ready to accept the first sector of data. - d) The host writes one sector of data via the Data Register. - e) The drive clears DRQ and sets BSY. - f) When the drive has completed processing of the sector, it clears BSY and - asserts INTRQ. If transfer of another sector is required, the drive also - sets DRQ. - g) After detecting INTRQ, the host reads the Status Register. - h) The drive clears the interrupt. - i) If transfer of another sector is required, the above sequence is repeated - from d). - -10.2.1 PIO Write Command - - +- a) -+-- b) -+ +--------+ +- e) -+--------+ +- e) -+ - |Setup | Issue | |Transfer| | Read |Transfer| | Read | - | |Command| | Data | |Status| Data |:::::::|Status| - +------+-------+ +--------+ +------+--------+ +------+ - |BSY=0 | |BSY=1 |BSY=0 |BSY=1 |BSY=0 | |BSY=1 |BSY=0 | - |DRDY=1 | | | | | | | - |DRQ=1 |DRQ=0 |DRQ=1 | |DRQ=0 | | - | | |Assert|Negate | |Assert|Negate - INTRQ INTRQ INTRQ INTRQ - -10.2.2 PIO Write Aborted Command - - +- a) -+-- b) -+ +- e) -+ - |Setup | Issue | | Read | - | |Command| |Status| - +------+-------+ +------+ - |BSY=0 | |BSY=1 |BSY=0 | - |DRDY=1 | | - | |Assert|Negate - INTRQ INTRQ - -10.3 Non-Data Commands - -This class includes: - - - Execute Drive Diagnostic (DRDY=x) - - Idle - - Initialize Drive Parameters (DRDY=x) - - Read Power Mode - - Read Verify Sector(s) - - Recalibrate - - Seek - - Set Features - - Set Multiple Mode - - Standby - -Execution of these commands involves no data transfer. - - a) The host writes any required parameters to the Features, Sector Count, - Sector Number, Cylinder and Drive/Head registers. - b) The host writes the command code to the Command Register. - c) The drive sets BSY. - d) When the drive has completed processing, it clears BSY and asserts INTRQ. - g) The host reads the Status Register. - h) The drive negates INTRQ. - -10.4 Miscellaneous Commands - -This class includes: - - - Read Multiple - - Sleep - - Write Multiple - - Write Same - -The protocol for these commands is contained in the individual command -descriptions. - -10.5 DMA Data Transfer Commands (Optional) - -This class comprises: - - - Read DMA - - Write DMA - -Data transfers using DMA commands differ in two ways from PIO transfers: - - - data transfers are performed using the slave-DMA channel - - no intermediate sector interrupts are issued on multi-sector commands - -Initiation of the DMA transfer commands is identical to the Read Sector or -Write Sector commands except that the host initializes the slave-DMA channel -prior to issuing the command. - -The interrupt handler for DMA transfers is different in that: - - - no intermediate sector interrupts are issued on multi-sector commands - - the host resets the DMA channel prior to reading status from the drive. - -The DMA protocol allows high performance multi-tasking operating systems to -eliminate processor overhead associated with PIO transfers. - - a) Command Phase - 1) Host initializes the slave-DMA channel - 2) Host updates the Command Block Registers - 3) Host writes command code to the Command Register - b) Data Phase - the register contents are not valid during a DMA Data Phase. - 1) The slave-DMA channel qualifies data transfers to and from the drive - with DMARQ - c) Status Phase - 1) Drive generates the interrupt to the host - 2) Host resets the slave-DMA channel - 3) Host reads the Status Register and Error Register - -10.5.1 Normal DMA Transfer - - +--------------+-------+ +---------------------+ +---------+------+ - |Initialize DMA|Command| | DMA Data Transfer | |Reset DMA|Status| - +--------------+-------+ +---------------------+ +---------+------+ - |BSY=0 |BSY=1 |BSY=x |BSY=1 |BSY=0 - |DRQ=x |nIEN=0 - -10.5.2 Aborted DMA Transfer - - +--------------+-------+ +-------------+ +---------+------+ - |Initialize DMA|Command| | DMA Data | |Reset DMA|Status| - +--------------+-------+ +-------------+ +---------+------+ - |BSY=0 |BSY=1 |BSY=x |BSY=1 |BSY=0 - |DRQ=1 |nIEN=0 - -10.5.3 Aborted DMA Command - - +--------------+-------+ +---------+------+ - |Initialize DMA|Command| |Reset DMA|Status| - +--------------+-------+ +---------+------+ - |BSY=0 |BSY=1 |BSY=1 |BSY=0 - |nIEN=0 - diff --git a/sys/i386/doc/ata/ata-11 b/sys/i386/doc/ata/ata-11 deleted file mode 100644 index bba6a1073cc8..000000000000 --- a/sys/i386/doc/ata/ata-11 +++ /dev/null @@ -1,201 +0,0 @@ -11. Timing - -11.1 Deskewing - -The host shall provide cable deskewing for all signals originating from the -controller. The drive shall provide cable deskewing for all signals -originating at the host. - -11.2 Symbols - -Certain symbols are used in the timing diagrams. These symbols and their -respective definitions are listed below. - - / or \ - signal transition (asserted or negated) * - < or > - data transition (asserted or negated) - XXXXXX - undefined but not necessarily released - . . . - the "other" condition if a signal is shown with no change - #n - used to number the sequence in which events occur e.g. #a, #b - _ _ __ -__/_ _/ - a degree of uncertainty as to when a signal may be asserted - -__ _ _ - \_ _\__ - a degree of uncertainty as to when a signal may be negated - -.. T - Nominal Clock Period -.. - * All signals are shown with the Asserted condition facing to the top of - the page. The negated condition is shown towards the bottom of the page - relative to the asserted condition. - -..Within each figure the timing terms i.e. tA, tB etc are repeated. There is -..no continuity of definition of tA from one figure to another. -.. -11.3 Terms - -The interface uses a mixture of negative and positive signals for control and -data. The terms asserted and negated are used for consistency and are -independent of electrical characteristics. - -In all timing diagrams, the lower line indicates negated, and the upper line -indicates asserted e.g. the following illustrates the representation of a -signal named TEST going from negated to asserted and back to negated, based on -the polarity of the signal. - - Assert Negate - | | - Bit Setting=1 |__________| - Bit Setting=0 TEST _____/ \_______ - - Assert Negate - | | - Bit Setting=0 |__________| - Bit Setting=1 TEST- _____/ \_______ - -.. Processor I/O Write, 16 Bit: -11.4 Data Transfers - -Figure 11-1 defines the relationships between the interface signals for both -16-bit and 8-bit data transfers. - - |<------------ t0 -------------------->| - __________________________________________ | - Address Valid *1 ...../ \________ - |<-t1->| ->| t9 |<- - ->|t7|<- |<----------- t2 ------------->| ->|t8|<- - | | |______________________________| | |_____ - DIOR-/DIOW- ____________/ \_______/ - | | |_ _ _ _ _ _ _ _ _ _ _____________ | - Write Data Valid *2__________/_ _ _ _ _ _ _ _ _ _/ \__________ - | | | |<--t3---->| | - | | | ->|t4|<- | - | | |_ _ _ _ _ _ _ _ _ _ _ ___________ | - Read Data Valid *2__________/_ _ _ _ _ _ _ _ _ _ _/ | \__________ - | | | |<--t5-->| | | - | | | ->|t6|<- | - | | | | | | - | |__________________________________________| - IOCS16- ________/ \_____ - - *1 Drive Address consists of signals CS1FX-, CS3FX- and DA2-0 - *2 Data consists of DD0-15 (16-bit) or DD0-7 (8-bit) - - +------------------------------------------+-------+-------+-------+ - | PIO | Mode 0| Mode 1| Mode 2| - | Timing Parameters | nsec | nsec | nsec | - +----+------------------------------------------+-------+-------+-------+ - | t0 | Cycle Time (Min) | 600 | 383 | 240 | - | t1 | Address Valid to DIOR-/DIOW- Setup (Min) | 70 | 50 | 30 | - | t2 | DIOR-/DIOW- 16-bit (Min) | 165 | 125 | 100 | - | | Pulse Width 8-bit (Min) | 290 | 290 | 290 | - | t3 | DIOW- Data Setup (Min) | 60 | 45 | 30 | - | t4 | DIOW- Data Hold (Min) | 30 | 20 | 15 | - | t5 | DIOR- Data Setup (Min) | 50 | 35 | 20 | - | t6 | DIOR- Data Hold (Min) | 5 | 5 | 5 | - | t7 | Addr Valid to IOCS16- Assertion (Max) | 90 | 50 | 40 | - | t8 | Addr Valid to IOCS16- Negation (Max) | 60 | 45 | 30 | - | t9 | DIOR-/DIOW- to Address Valid Hold (Min) | 20 | 15 | 10 | - +----+------------------------------------------+-------+-------+-------+ -..rm102 -.. NOTE: These are minimum acceptable interface timing requirements. - - FIGURE 11-1: PIO DATA TRANSFER TO/FROM DRIVE - - ___________________________________ - DIOR-/DIOW- __________/ \______________ - | - |<- tA ->|<--- tB ---->| - ___________________| |_____________________ - IORDY \___________________/ - - Label Description Min Max Units - - tA IORDY Setup time - 35 nsecs - tB IORDY Pulse Width - 1,250 nsecs - - WARNING: The use of IORDY for data transfers is a system integration issue - which requires control of both ends of the cable. - - FIGURE 11-2: IORDY TIMING REQUIRMENTS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |<----------------------- t0 ---------------------->| - ____________ _______ - DMARQ ___/ \______________________________________/ | - |<- tC ->| | - |_____________________________________________ |___ - DMACK- _______/ \_____/ - |<--- tI --->|________________|<----- tJ -----| | - DIOR-/DIOW- ____________________/ \_________________________ - | | | | - | |<------ tD ---->| | - Read | ______________ | - DD0-15 -------------------------------<______________>---------------- - | |<-- tE -->| |<- tF ->| | - Write | _________________________ | - DD0-15 --------------------------<_________________________>----------- - | | | | | - | |<-- tG -->|<-- tH -->| | - - +----------------------------------+-------+-------+-------+ - | DMA | Mode 0| Mode 1| Mode 2| - | Timing Parameters | nsec | nsec | nsec | - +----+----------------------------------+-------+-------+-------+ - | t0 | Cycle Time (Min) | 960 | 480 | 240 | - | tC | DMACK to DMREQ Delay (Max) | 200 | 100 | 80 | - | tD | DIOR-/DIOW- 16-bit (Min) | 480 | 240 | 120 | - | tE | DIOR- Data Setup (Min) | 250 | 150 | 50 | - | tF | DIOR- Data Hold (Min) | 5 | 5 | 5 | - | tG | DIOW- Data Setup (Min) | 250 | 100 | 35 | - | tH | DIOW- Data Hold (Min) | 50 | 30 | 20 | - | tI | DMACK to DIOR-/DIOW- Setup (Min) | 0 | 0 | 0 | - | tJ | DIOR-/DIOW- to DMACK Hold (Min) | 0 | 0 | 0 | - +----+----------------------------------+-------+-------+-------+ - - FIGURE 11-3: DMA DATA TRANSFER - -11.5 Power On and Hard Reset - - ______ - RESET- _____/ \_____________________________________________________ - |<-tM->| - | | Drive 0 - _ _ _ _ _ _ _ _______ _ _ _ _ _ _ _ _ _ _ _ _ _| - BSY _ _ _ _ _ _ _/ \_ _ _ _ _ *1 _ _ _ _ _ _\________________ - ->|tN|<- - _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - DASP- _ _ _ _ _ _ _ _\_______/_ _ _ _ *2 _ _ _ _ _ _ _ _ _ _\=== *3 == - ->| tP |<- | |_ _ _ __________ - Control Registers_______________________________________/_ _ _ / - | | | - | | | Drive 1 - _ _ _ _ _ _ _ _ _________________________________| - BSY _ _ _ _ _ _ _ _/ \________________ - _ _ _ _ _ _ _ _ _ _ ______ _ _ _ _ _ - PDIAG- _ _ _ _ _ _ _ _ _ _\____________________________/ \_ _ _ _ _ - | | | |<----------- tQ -------->| - _ _ _ _ _ _ _ _ _ _________________________ _ _ _ - DASP- _ _ _ _ _ _ _ _ _\_____/ \ _ _ _\=== *3 == - |<- tR ->|<------------ tS -------------->| - _ _ _ __________ - Control Registers_______________________________________/_ _ _ / - - *1 Drive 0 can set BSY=0 if Drive 1 not present - *2 Drive 0 can use DASP- to indicate it is active if Drive 1 is not - present - *3 DASP- can be asserted to indicate that the drive is active - +-------------------+------------+ - | Label | Units | - +-------------------+------------+ - | tM (Min) | 25 usec | - | tN (Max) | 400 nsec | - | tP (Max) | 1 msec | - | tQ (Max) | 30 secs | - | tR Drive 0 (Max) | 450 msec | - | tR Drive 1 (Max) | 400 msec | - | tS (Max) | 30.5 secs | - +-------------------+------------+ - - FIGURE 11-4 RESET SEQUENCE - diff --git a/sys/i386/doc/ata/ata-2 b/sys/i386/doc/ata/ata-2 deleted file mode 100644 index bad2d9eea869..000000000000 --- a/sys/i386/doc/ata/ata-2 +++ /dev/null @@ -1,4 +0,0 @@ -2. References - -None. - diff --git a/sys/i386/doc/ata/ata-3 b/sys/i386/doc/ata/ata-3 deleted file mode 100644 index 54645e040b7f..000000000000 --- a/sys/i386/doc/ata/ata-3 +++ /dev/null @@ -1,27 +0,0 @@ -3. General Description - -The application environment for the AT Attachment is any computer which uses -an AT Bus or 40-pin ATA interface. - -The AT Bus is a widely used and implemented interface for which a variety of -peripherals have been manufactured. As a means of reducing size and cost, a -class of products has emerged which embed the controller functionality in the -drive. These new products utilize the AT Bus fixed disk interface protocol, -and a subset of the AT bus. Because of their compatibility with existing AT -hardware and software this interface quickly became a de facto industry -standard. - -The purpose of the ATA standard is to define the de facto implementations. - -Software in the Operating System dispatches I/O (Input/Output) requests via -the AT Bus to peripherals which respond to direct commands. - -3.1 Structure - -This standard relies upon specifications of the mechanical and electrical -characteristics of the AT Bus and a subset of the AT Bus specifically -developed for the direct attachment of peripherals. - -Also defined are the methods by which commands are directed to peripherals, -the contents of registers and the method of data transfers. - diff --git a/sys/i386/doc/ata/ata-4 b/sys/i386/doc/ata/ata-4 deleted file mode 100644 index 0c2fb638ce8d..000000000000 --- a/sys/i386/doc/ata/ata-4 +++ /dev/null @@ -1,59 +0,0 @@ -4. Definitions and Conventions - -4.1 Definitions - -For the purpose of this standard the following definitions apply: - -4.1.1 ATA (AT Attachment): ATA defines a compatible register set and a 40-pin -connector and its associated signals. - -4.1.2 Data block: This term describes a data transfer, and is typically a -single sector, except when declared otherwise by use of the Set Multiple -command. - -4.1.3 DMA (Direct Memory Access): A means of data transfer between -peripheral and host memory without processor intervention. - -4.1.4 Optional: This term describes features which are not required by the -standard. However, if any feature defined by the standard is implemented, it -shall be done in the same way as defined by the standard. Describing a feature -as optional in the text is done to assist the reader. If there is a conflict -between text and tables on a feature described as optional, the table shall be -accepted as being correct. - -4.1.5 PIO (Programmed Input/Output): A means of data transfer that requires -the use of the host processor. - -4.1.6 Reserved: Where this term is used for bits, bytes, fields and code -values; the bits, bytes, fields and code values are set aside for future -standardization, and shall be zero. - -..4.1.10 VU (Vendor Unique): This term defines those features that can be -..defined by the vendor in a specific implementation. Caution should be -..exercised in defining and using such features since they may or may not -..vary between vendors. -.. -4.1.7 VU (Vendor Unique): This term is used to describe bits, bytes, -fields, code values and features which are not described in this standard, -and may be used in a way that varies between vendors. - -4.2 Conventions - -Certain terms used herein are the proper names of signals. These are printed -in uppercase to avoid possible confusion with other uses of the same words; -e.g., ATTENTION. Any lowercase uses of these words have the normal American- -English meaning. - -A number of conditions, commands, sequence parameters, events, English text, -states or similar terms are printed with the first letter of each word in -uppercase and the rest lowercase; e.g., In, Out, Request Status. Any lowercase -uses of these words have the normal American-English meaning. - -The American convention of numbering is used i.e., the thousands and higher -multiples are separated by a comma and a period is used as the decimal point. -This is equivalent to the ISO convention of a space and comma. - - American: 0.6 ISO: 0,6 - 1,000 1 000 - 1,323,462.9 1 323 462,9 - diff --git a/sys/i386/doc/ata/ata-5 b/sys/i386/doc/ata/ata-5 deleted file mode 100644 index c120a39b3736..000000000000 --- a/sys/i386/doc/ata/ata-5 +++ /dev/null @@ -1,189 +0,0 @@ -5. Interface Cabling Requirements - -5.1 Configuration - -This standard provides the capability of operating on the AT Bus in a daisy -chained configuration with a second drive that operates in accordance with -these standards. One drive (selected as Drive 0) has been referred to as the -master in industry terms and the second (selected as Drive 1) has been -referred to as the slave (see Figure 5-3). - -The designation as Drive 0 or Drive 1 is made by a jumper plug or switch on -the drive. - -Data is transferred in parallel (8 or 16 bits) either to or from host memory -to the drive's buffer under the direction of commands previously transferred -from the host. The drive performs all of the operations necessary to properly -write data to, or read data from, the disk media. Data read from the media is -stored in the drive's buffer pending transfer to the host memory and data is -transferred from the host memory to the drive's buffer to be written to the -media. - - +-----------------------------------------------------+ - | | - | HOST | - | | - +---------------^-------------------------------------+ - | ATA Interface - | _____________________ - |/ | - +------v--+ +------v--+ - | DRIVE 0 | | DRIVE 1 | - +---------+ +---------+ - - FIGURE 5-1: ATA INTERFACE TO EMBEDDED BUS PERIPHERALS - - +-----------------------------------------------------+ - | | - | HOST | - | | - +------+====== AT Bus ======+-------------------------+ - | | - | ADAPTER | - | | - +--------^-----------+ - | ATA Interface - | _____________________ - |/ | - +------v--+ +------v--+ - | DRIVE 0 | | DRIVE 1 | - +---------+ +---------+ - - FIGURE 5-2: HOST BUS ADAPTER AND PERIPHERAL DEVICES - - +-----------------------------------------------------+ - | | - | HOST | - | | - +---------------^-------------------------------------+ - | ATA Interface - +------v-----+ - | | - | CONTROLLER | - | | - +-^----^---^-+ - | | |__________________ Device Interface - | ___|_________________ | e.g. ESDI, SCSI - |/ | | | - +-v----v--+ +-v----v--+ - | DRIVE | | DRIVE | - +---------+ +---------+ - - FIGURE 5-3: ATA INTERFACE TO CONTROLLER AND PERIPHERAL DEVICES - -5.2 Addressing Considerations - -In traditional controller operation, only the selected controller receives -commands from the host following selection. In this standard, the register -contents go to both drives (and their embedded controllers). The host -discriminates between the two by using the DRV bit in the Drive/Head Register. - -5.3 DC Cable and Connector - -The drive receives DC power through a 4-pin or a low-power application 3-pin -connector. - -5.3.1 4-Pin Power - -The pin assignments are shown in Table 5-1. Recommended part numbers for the -mating connector to 18AWG cable are shown below, but equivalent parts may be -used. - - Connector (4 Pin) AMP 1-480424-0 or equivalent. - Contacts (Loose Piece) AMP 60619-4 or equivalent. - Contacts (Strip) AMP 61117-4 or equivalent. - - TABLE 5-1: DC INTERFACE - +------------------------+------------+ - | POWER LINE DESIGNATION | PIN NUMBER | - +------------------------+------------+ - | +12 V | 1-01 | - | +12 V RETURN | 1-02 | - | +5 V RETURN | 1-03 | - | +5 V | 1-04 | - +------------------------+------------+ - -5.3.2 3-Pin Power - -The pin assignments are shown in Table 5-2. Recommended part numbers for the -mating connector to 18AWG cable are shown below, but equivalent parts may be -used. - - Connector (3 Pin) Molex 5484 39-27-0032 or equivalent. - - TABLE 5-2: DC INTERFACE - +------------------------+------------+ - | POWER LINE DESIGNATION | PIN NUMBER | - +------------------------+------------+ - | +12 V | 1-01 | - | Ground | 1-02 | - | +5 V | 1-03 | - +------------------------+------------+ - -5.3.3 Device Grounding - -System ground may be connected to a "quick-connect" terminal equivalent to: - - Drive Connector Terminal AMP 61664-1 or equivalent. - Cable Connector Terminal AMP 62137-2 or equivalent. - -Provision for tying the DC Logic ground and the chassis ground together or for -separating these two ground planes is vendor specific. - -5.4 I/O Connector - -The I/O connector is a 40-pin connector as shown in Figure 5-4, with pin -assignments as shown in Table 6-1. - -The connector should be keyed to prevent the possibility of installing it -upside down. A key is provided by the removal of Pin 20. The corresponding pin -on the cable connector should be plugged. - -The pin locations are governed by the cable plug, not the receptacle. The way -in which the receptacle is mounted on the Printed Circuit Board affects the -pin positions, and pin 1 should remain in the same relative position. This -means the pin numbers of the receptacle may not reflect the conductor number -of the plug. The header receptacle is not polarized, and all the signals are -relative to Pin 20, which is keyed. - -By using the plug positions as primary, a straight cable can connect drives. -As shown in Figure 5-4, conductor 1 on pin 1 of the plug has to be in the -same relative position no matter what the receptacle numbering looks like. -If receptacle numbering was followed, the cable would have to twist 180 -degrees between a drive with top-mounted receptacles, and a drive with -bottom-mounted receptacles. - - +-----------------------+ - | 1| - |40 20 2| - ==+==== Circuit Board ====+== ==+==== Circuit Board ====+== - | 1| - |40 20 2| - +-----------------------+ -.. * Location of pin number stamped as 1 when receptacle mounted upside down - FIGURE 5-4: 40-PIN CONNECTOR MOUNTING - -Recommended part numbers for the mating connector are shown below, but -equivalent parts may be used. - - Connector (40 Pin) 3M 3417-7000 or equivalent. - Strain relief 3M 3448-2040 or equivalent. - Flat Cable (Stranded 28 AWG) 3M 3365-40 or equivalent. - Flat Cable (Stranded 28 AWG) 3M 3517-40 (Shielded) or equivalent. - -5.5 I/O Cable - -The cable specifications affect system integrity and the maximum length that -can be supported in any application. - - TABLE 5-3: CABLE PARAMETERS - +-------------------------------------+------+--------+ - | Cable length of 0.46m (18 inches) * | Min | Max | - +-------------------------------------+------+--------+ - | Driver IoL Sink Current | 12mA | | - | Driver IoH Source Current | | -400uA | - | Cable Capacitive Loading | | 200pF | - +-------------------------------------+------+--------+ - * This distance may be exceeded in circumstances where the - characteristics of both ends of the cable can be controlled. - diff --git a/sys/i386/doc/ata/ata-6 b/sys/i386/doc/ata/ata-6 deleted file mode 100644 index 1bce4866f5b5..000000000000 --- a/sys/i386/doc/ata/ata-6 +++ /dev/null @@ -1,337 +0,0 @@ -6. Physical Interface - -6.1 Signal Conventions - -Signal names are shown in all upper case letters. Signals can be asserted -(active, true) in either a high (more positive voltage) or low (less positive -voltage) state. A dash character (-) at the beginning or end of a signal name -indicates it is asserted at the low level (active low). No dash or a plus -character (+) at the beginning or end of a signal name indicates it is -asserted high (active high). An asserted signal may be driven high or low by -an active circuit, or it may be allowed to be pulled to the correct state by -the bias circuitry. - -Control signals that are asserted for one function when high and asserted for -another function when low are named with the asserted high function name -followed by a slash character (/), and the asserted low function name followed -with a dash (-) e.g. BITENA/BITCLR- enables a bit when high and clears a bit -when low. All signals are TTL compatible unless otherwise noted. Negated means -that the signal is driven by an active circuit to the state opposite to the -asserted state (inactive, or false) or may be simply released (in which case -the bias circuitry pulls it inactive, or false), at the option of the -implementor. - -6.2 Signal Summary - -The physical interface consists of single ended TTL compatible receivers and -drivers communicating through a 40-conductor flat ribbon nonshielded cable -using an asynchronous interface protocol. The pin numbers and signal names -are shown in Table 6-1. Reserved signals shall be left unconnected. - - TABLE 6-1: INTERFACE SIGNALS - +----------------------------------+ +-----------+ - | HOST I/O | | DRIVE I/O | - | CONNECTOR | | CONNECTOR | - | | | | - | HOST RESET 1 | ----- RESET- -------->| 1 | - | 2 | ----- Ground -------- | 2 | - | HOST DATA BUS BIT 7 3 |<----- DD7 ----------->| 3 | - | HOST DATA BUS BIT 8 4 |<----- DD8 ----------->| 4 | - | HOST DATA BUS BIT 6 5 |<----- DD6 ----------->| 5 | - | HOST DATA BUS BIT 9 6 |<----- DD9 ----------->| 6 | - | HOST DATA BUS BIT 5 7 |<----- DD5 ----------->| 7 | - | HOST DATA BUS BIT 10 8 |<----- DD10 ---------->| 8 | - | HOST DATA BUS BIT 4 9 |<----- DD4 ----------->| 9 | - | HOST DATA BUS BIT 11 10 |<----- DD11 ---------->| 10 | - | HOST DATA BUS BIT 3 11 |<----- DD3 ----------->| 11 | - | HOST DATA BUS BIT 12 12 |<----- DD12 ---------->| 12 | - | HOST DATA BUS BIT 2 13 |<----- DD2 ----------->| 13 | - | HOST DATA BUS BIT 13 14 |<----- DD13 ---------->| 14 | - | HOST DATA BUS BIT 1 15 |<----- DD1 ----------->| 15 | - | HOST DATA BUS BIT 14 16 |<----- DD14 ---------->| 16 | - | HOST DATA BUS BIT 0 17 |<----- DD0 ----------->| 17 | - | HOST DATA BUS BIT 15 18 |<----- DD15 ---------->| 18 | - | 19 | ----- Ground -------- | 19 | - | 20 | ----- (keypin) ------ | 20 | - | DMA REQUEST 21 |<----- DMARQ --------- | 21 | - | 22 | ----- Ground -------- | 22 | - | HOST I/O WRITE 23 | ----- DIOW- --------->| 23 | - | 24 | ----- Ground -------- | 24 | - | HOST I/O READ 25 | ----- DIOR- --------->| 25 | - | 26 | ----- Ground -------- | 26 | - | I/O CHANNEL READY 27 |<----- IORDY --------- | 27 | - | SPINDLE SYNC 28 |*----- SPSYNC --------*| 28 | - | DMA ACKNOWLEDGE 29 | ----- DMACK- -------->| 29 | - | 30 | ----- Ground -------- | 30 | - | HOST INTERRUPT REQUEST 31 |<----- INTRQ --------- | 31 | - | HOST 16 BIT I/O 32 |<----- IOCS16- ------- | 32 | - | HOST ADDRESS BUS BIT 1 33 | ----- DA1 ----------->| 33 | - | PASSED DIAGNOSTICS 34 |*----- PDIAG- --------*| 34 | - | HOST ADDRESS BUS BIT 0 35 | ----- DAO ----------->| 35 | - | HOST ADDRESS BUS BIT 2 36 | ----- DA2 ----------->| 36 | - | HOST CHIP SELECT 0 37 | ----- CS1FX- -------->| 37 | - | HOST CHIP SELECT 1 38 | ----- CS3FX- -------->| 38 | - | DRIVE ACTIVE/DRIVE 1 PRESENT 39 |<----- DASP- ---------*| 39 | - | 40 | ----- Ground -------- | 40 | - +----------------------------------+ +-----------+ - - * Drive Intercommunication Signals - - +---HOST---+ +-Drive 0-+ +-Drive 1-+ - | 28 | ----->| 28 28 |<----- SPSYNC ---->| 28 | - | 34 | ----- | 34 34 |<----- PDIAG- ---- | 34 | - | 39 |<----- | 39 39 |<----- DASP- ----- | 39 | - +----------+ +---------+ +---------+ - -6.3 Signal Descriptions - -The interface signals and pins are described in more detail than shown in -Table 6-1. The signals are listed according to function, rather than in -numerical connector pin order. Table 6-2 lists signal name mnemonic, connector -pin number, whether input to (I) or output from (O) the drive, and full signal -name. - - TABLE 6-2: INTERFACE SIGNALS DESCRIPTION - +--------+----+-----+ - | Signal | Pin| I/O | - +--------+----+-----+-----------------------------------------------------+ - | CS1FX- | 37 | I | Drive chip Select 0 | - | CS3FX- | 38 | I | Drive chip Select 1 | - | DA0 | 35 | I | Drive Address Bus - Bit 0 | - | DA1 | 33 | I | - Bit 1 | - | DA2 | 36 | I | - Bit 2 | - | DASP- | 39 | I/O | Drive Active/Drive 1 Present | - | DD0 | 17 | I/O | Drive Data Bus - Bit 0 | - | DD1 | 15 | I/O | - Bit 1 | - | DD2 | 13 | I/O | - Bit 2 | - | DD3 | 11 | I/O | - Bit 3 | - | DD4 | 9 | I/O | - Bit 4 | - | DD5 | 7 | I/O | - Bit 5 | - | DD6 | 5 | I/O | - Bit 6 | - | DD7 | 3 | I/O | - Bit 7 | - | DD8 | 4 | I/O | - Bit 8 | - | DD9 | 6 | I/O | - Bit 9 | - | DD10 | 8 | I/O | - Bit 10 | - | DD11 | 10 | I/O | - Bit 11 | - | DD12 | 12 | I/O | - Bit 12 | - | DD13 | 14 | I/O | - Bit 13 | - | DD14 | 16 | I/O | - Bit 14 | - | DD15 | 18 | I/O | - Bit 15 | - | DIOR- | 25 | I | Drive I/O Read | - | DIOW- | 23 | I | Drive I/O Write | - | DMACK- | 29 | I | DMA Acknowledge | - | DMARQ | 21 | O | DMA Request | - | INTRQ | 31 | O | Drive Interrupt | - | IOCS16-| 32 | O | Drive 16-bit I/O | - | IORDY | 27 | O | I/O Channel Ready | - | PDIAG- | 34 | I/O | Passed Diagnostics | - | RESET- | 1 | I | Drive Reset | - | SPSYNC | 28 | - | Spindle Sync | - | keypin | 20 | - | Pin used for keying the interface connector. | - +--------+----+-----+-----------------------------------------------------+ - -6.3.1 CS1FX- (Drive chip Select 0) - -This is the chip select signal decoded from the host address bus used to -select the Command Block Registers. - -6.3.2 CS3FX- (Drive chip Select 1) - -This is the chip select signal decoded from the host address bus used to -select the Control Block Registers. - -6.3.3 DA0-2 (Drive Address Bus) - -This is the 3-bit binary coded address asserted by the host to access a -register or data port in the drive. - -6.3.4 DASP- (Drive Active/Drive 1 Present) - -This is a time-multiplexed signal which indicates that a drive is active, or -that Drive 1 is present. This signal shall be an open collector output and -each drive shall have a 10K pull-up resistor. - -During power on initialization or after RESET- is negated, DASP- shall be -asserted by Drive 1 within 400 msec to indicate that Drive 1 is present. - -Drive 0 shall allow up to 450 msec for Drive 1 to assert DASP-. If Drive 1 is -not present, Drive 0 may assert DASP- to drive an activity LED. - -DASP- shall be negated following acceptance of the first valid command by -Drive 1 or after 31 seconds, whichever comes first. - -Any time after negation of DASP-, either drive may assert DASP- to indicate -that a drive is active. - -..rm102 - NOTE: Prior to the development of this standard, products were introduced - which did not time multiplex DASP-. Some used two jumpers to indicate - to Drive 0 whether Drive 1 was present. If such a drive is jumpered to - indicate Drive 1 is present it should work successfully with a Drive 1 - which complies with this standard. If installed as Drive 1, such a - drive may not work successfully because it may not assert DASP- for a - long enough period to be recognized. However, it would assert DASP- - to indicate that the drive is active. - -6.3.5 DD0-DD15 (Drive Data Bus) - -This is an 8- or 16-bit bidirectional data bus between the host and the drive. -The lower 8 bits are used for 8-bit transfers e.g. registers, ECC bytes. - -6.3.6 DIOR- (Drive I/O Read) - -This is the Read strobe signal. The falling edge of DIOR- enables data from a -register or the data port of the drive onto the host data bus, DD0-DD7 or DD0- -DD15. The rising edge of DIOR- latches data at the host. - -6.3.7 DIOW- (Drive I/O Write) - -This is the Write strobe signal. The rising edge of DIOW- clocks data from the -host data bus, DD0-DD7 or DD0-DD15, into a register or the data port of the -drive. - -6.3.8 DMACK- (DMA Acknowledge) (Optional) - -This signal shall be used by the host in response to DMARQ to either -acknowledge that data has been accepted, or that data is available. - -6.3.9 DMARQ (DMA Request) (Optional) - -This signal, used for DMA data transfers between host and drive, shall be -asserted by the drive when it is ready to transfer data to or from the host. -The direction of data transfer is controlled by DIOR- and DIOW-. This signal -is used in a handshake manner with DMACK- i.e. the drive shall wait until the -host asserts DMACK- before negating DMARQ, and re-asserting DMARQ if there is -more data to transfer. - -When a DMA operation is enabled, IOCS16-, CS1FX- and CS3FX- shall not be -asserted and transfers shall be 16-bits wide. - -..rm102 - NOTE: ATA products with DMA capability require a pull-down resistor on this - signal to prevent spurious data transfers. This resistor may affect - driver requirements for drives sharing this signal in systems with - unbuffered ATA signals. - -6.3.10 INTRQ (Drive Interrupt) - -This signal is used to interrupt the host system. INTRQ is asserted only when -the drive has a pending interrupt, the drive is selected, and the host has -cleared nIEN in the Device Control Register. If nIEN=1, or the drive is not -selected, this output is in a high impedance state, regardless of the presence -or absence of a pending interrupt. - -INTRQ shall be negated by: - - - assertion of RESET- or - - the setting of SRST of the Device Control Register, or - - the host writing the Command Register or - - the host reading the Status Register - -..rm102 - NOTE: Some drives may negate INTRQ on a PIO data transfer completion, except - on a single sector read or on the last sector of a multi-sector read. - -On PIO transfers, INTRQ is asserted at the beginning of each data block to be -transferred. A data block is typically a single sector, except when -declared otherwise by use of the Set Multiple command. An exception occurs on -Format Track, Write Sector(s), Write Buffer and Write Long commands - INTRQ -shall not be asserted at the beginning of the first data block to be -transferred. - -On DMA transfers, INTRQ is asserted only once, after the command has -completed. - -6.3.11 IOCS16- (Drive 16-bit I/O) - -Except for DMA transfers, IOCS16- indicates to the host system that the 16-bit -data port has been addressed and that the drive is prepared to send or receive -a 16-bit data word. This shall be an open collector output. - - - When transferring in PIO mode, If IOCS16- is not asserted, transfers shall - be 8-bit using DD0-7. - - When transferring in PIO mode, if IOCS16- is asserted, transfers shall be - 16-bit using DD0-15. - for 16-bit data transfers. - - When transferring in DMA mode, the host shall use a 16-bit DMA channel and - IOCS16- shall not be asserted. - -6.3.12 IORDY (I/O Channel Ready) (Optional) - -This signal is negated to extend the host transfer cycle of any host register -access (Read or Write) when the drive is not ready to respond to a data -transfer request. When IORDY is not negated, IORDY shall be in a high -impedance state. - -6.3.13 PDIAG- (Passed Diagnostics) - -This signal shall be asserted by Drive 1 to indicate to Drive 0 that it has -completed diagnostics. A 10K pull-up resistor shall be used on this signal by -each drive. - -Following a power on reset, software reset or RESET-, Drive 1 shall negate -PDIAG- within 1 msec (to indicate to Drive 0 that it is busy). Drive 1 shall -then assert PDIAG- within 30 seconds to indicate that it is no longer busy, -and is able to provide status. After the assertion of PDIAG-, Drive 1 may be -unable to accept commands until it has finished its reset procedure and is -Ready (DRDY=1). - -Following the receipt of a valid Execute Drive Diagnostics command, Drive -1 shall negate PDIAG- within 1 msec to indicate to Drive 0 that it is busy -and has not yet passed its drive diagnostics. If Drive 1 is present then -Drive 0 shall wait for up to 5 seconds from the receipt of a valid Execute -Drive Diagnostics command for Drive 1 to assert PDIAG-. Drive 1 should -clear BSY before asserting PDIAG-, as PDIAG- is used to indicate that -Drive 1 has passed its diagnostics and is ready to post status. - -.. -If DASP- was not asserted by Drive 1 during reset initialization, Drive 0 -shall post its own status immediately after it completes diagnostics, and -clear the Drive 1 Status Register to 00h. Drive 0 may be unable to accept -commands until it has finished its reset procedure and is Ready (DRDY=1). - -6.3.14 RESET- (Drive Reset) - -This signal from the host system shall be asserted for at least 25 usec after -voltage levels have stabilized during power on and negated thereafter unless -some event requires that the drive(s) be reset following power on. - -6.3.15 SPSYNC (Spindle Synchronization) (Optional) - -This signal may be either input or output to the drive depending on a vendor- -defined switch. If a drive is set to Master the signal is output, and if a -drive is set to slave the signal is input. - -There is no requirement that each drive implementation be plug-compatible to -the extent that a multiple vendor drive subsystem be operable. Mix and match -of different manufacturers drives is unlikely because rpm, sync fields, sync -bytes etc need to be virtually identical. However, if drives are designed to -match the following recommendation, controllers can operate drives with a -single implementation. - -There can only be one master drive at a time in a configuration. The host or -the drive designated as master can generate SPSYNC at least once per rotation, -but may be at a higher frequency. - -SPSYNC received by a drive is used as the synchronization signal to lock the -spindles in step. The time to achieve synchronization varies, and is indicated -by the drive setting DRDY i.e. if the drive does not achieve synchronization -following power on or a reset, it shall not set DRDY. - -A master drive or the host generates SPSYNC and transmits it. - -A slave drive does not generate SPSYNC and is responsible to synchronize its -index to SPSYNC. - -If a drive does not support synchronization, it shall ignore SPSYNC. - -In the event that a drive previously synchronized loses synchronization, but -is otherwise operational, it does not clear DRDY. - -Prior to the introduction of this standard, this signal was defined as DALE -(Drive Address Latch Enable), and used for an address valid indication from -the host system. If used, the host address and chip selects, DAO through DA2, -CS1FX-, and CS3FX- were valid at the negation of this signal and remained -valid while DALE was negated, therefore, the drive did not need to latch these -signals with DALE. - diff --git a/sys/i386/doc/ata/ata-7 b/sys/i386/doc/ata/ata-7 deleted file mode 100644 index 240706912a85..000000000000 --- a/sys/i386/doc/ata/ata-7 +++ /dev/null @@ -1,321 +0,0 @@ -7. Logical Interface - -7.1 General - -7.1.1 Bit Conventions - -Bit names are shown in all upper case letters except where a lower case n -precedes a bit name. This indicates that when nBIT=0 (bit is zero) the action -is true and when nBIT=1 (bit is one) the action is false. If there is no -preceding n, then when BIT=1 it is true, and when BIT=0 it is false. - -A bit can be set to one or cleared to zero and polarity influences whether it -is to be interpreted as true or false: - - True BIT=1 nBIT=0 - False BIT=0 nBIT=1 - -7.1.2 Environment - -The drives using this interface shall be programmed by the host computer to -perform commands and return status to the host at command completion. When two -drives are daisy chained on the interface, commands are written in parallel to -both drives, and for all except the Execute Diagnostics command, only the -selected drive executes the command. On an Execute Diagnostics command -addressed to Drive 0, both drives shall execute the command, and Drive 1 shall -post its status to Drive 0 via PDIAG-. - -Drives are selected by the DRV bit in the Drive/Head Register (see 7.2.8), and -by a jumper or switch on the drive designating it as either a Drive 0 or as -Drive 1. When DRV=0, Drive 0 is selected. When DRV=1, Drive 1 is selected. -When drives are daisy chained, one shall be set as Drive 0 and the other as -Drive 1. When a single drive is attached to the interface it shall be set as -Drive 0. - -Prior to the adoption of this standard, some drives may have provided jumpers -to indicate Drive 0 with no Drive 1 present, or Drive 0 with Drive 1 present. - -Throughout this document, drive selection always refers to the state of the -DRV bit, and the position of the Drive 0/Drive 1 jumper or switch. - -7.2 I/O Register Descriptions - -Communication to or from the drive is through an I/O Register that routes the -input or output data to or from registers (selected) by a code on signals from -the host (CS1FX-, CS3FX-, DA2, DA1, DA0, DIOR- and DIOW-). - -The Command Block Registers are used for sending commands to the drive or -posting status from the drive. - -The Control Block Registers are used for drive control and to post alternate -status. - -Table 7-1 lists these registers and the addresses that select them. - -Logic conventions are: A = signal asserted - N = signal negated - x = does not matter which it is - - TABLE 7-1: I/O PORT FUNCTIONS/SELECTION ADDRESSES - +-------------------------------+-----------------------------------------+ - | Addresses | Functions | - |CS1FX-|CS3FX-| DA2 | DA1 | DA0 | READ (DIOR-) | WRITE (DIOW-) | - +------+------+-----+-----+-----+---------------------+-------------------+ - | Control Block Registers | - +------+------+-----+-----+-----+---------------------+-------------------+ - | N | N | x | x | x | Data Bus High Imped | Not used | - | N | A | 0 | x | X | Data Bus High Imped | Not used | - | N | A | 1 | 0 | x | Data Bus High Imped | Not used | - | N | A | 1 | 1 | 0 | Alternate Status | Device Control | - | N | A | 1 | 1 | 1 | Drive Address | Not used | - +------+------+-----+-----+-----+---------------------+-------------------+ - | Command Block Registers | - +------+------+-----+-----+-----+---------------------+-------------------+ - | A | N | 0 | 0 | 0 | Data | Data | - | A | N | 0 | 0 | 1 | Error Register | Features | - | A | N | 0 | 1 | 0 | Sector Count | Sector Count | - | A | N | 0 | 1 | 1 | Sector Number | Sector Number | - | A | N | 1 | 0 | 0 | Cylinder Low | Cylinder Low | - | A | N | 1 | 0 | 1 | Cylinder High | Cylinder High | - | A | N | 1 | 1 | 0 | Drive/Head | Drive/Head | - | A | N | 1 | 1 | 1 | Status | Command | - | A | A | x | x | x | Invalid Address | Invalid Address | - +------+------+-----+-----+-----+---------------------+-------------------+ - -7.2.1 Alternate Status Register - -This register contains the same information as the Status Register in the -command block. The only difference being that reading this register does not -imply interrupt acknowledge or clear a pending interrupt. - - 7 6 5 4 3 2 1 0 - +-------+-------+-------+-------+-------+-------+-------+-------+ - | BSY | DRDY | DWF | DSC | DRQ | CORR | IDX | ERR | - +-------+-------+-------+-------+-------+-------+-------+-------+ - -See 7.2.13 for definitions of the bits in this register. - -7.2.2 Command Register - -This register contains the command code being sent to the drive. Command -execution begins immediately after this register is written. The executable -commands, the command codes, and the necessary parameters for each command are -listed in Table 9-1. - -7.2.3 Cylinder High Register - -This register contains the high order bits of the starting cylinder address -for any disk access. At the end of the command, this register is updated to -reflect the current cylinder number. The most significant bits of the cylinder -address shall be loaded into the cylinder high Register. - -..rm102 - NOTE: Prior to the introduction of this standard, only the lower 2 bits of - this register were valid, limiting cylinder address to 10 bits i.e. - 1,024 cylinders. - -7.2.4 Cylinder Low Register - -This register contains the low order 8 bits of the starting cylinder address -for any disk access. At the end of the command, this register is updated to -reflect the current cylinder number. - -7.2.5 Data Register - -This 16-bit register is used to transfer data blocks between the device data -buffer and the host. It is also the register through which sector information -is transferred on a Format command. Data transfers may be either PIO or DMA. - -7.2.6 Device Control Register - -The bits in this register are as follows: - - 7 6 5 4 3 2 1 0 - +-------+-------+-------+-------+-------+-------+-------+-------+ - | x | x | x | x | 1 | SRST | nIEN | 0 | - +-------+-------+-------+-------+-------+-------+-------+-------+ - - - SRST is the host software reset bit. The drive is held reset when this bit - is set. If two disk drives are daisy chained on the interface, this bit - resets both simultaneously. Drive 1 is not required to execute the DASP- - handshake procedure. - - nIEN is the enable bit for the drive interrupt to the host. When nIEN=0, - and the drive is selected, INTRQ shall be enabled through a tri-state - buffer. When nIEN=1, or the drive is not selected, the INTRQ signal shall - be in a high impedance state. - -7.2.7 Drive Address Register - -This register contains the inverted drive select and head select addresses of -the currently selected drive. The bits in this register are as follows: - - 7 6 5 4 3 2 1 0 - +-------+-------+-------+-------+-------+-------+-------+-------+ - | HiZ | nWTG | nHS3 | nHS2 | nHS1 | nHS0 | nDS1 | nDS0 | - +-------+-------+-------+-------+-------+-------+-------+-------+ - - - HiZ shall always be in a high impedance state. - - nWTG is the Write Gate bit. When writing to the disk drive is in progress, - nWTG=0. - - nHS3 through nHS0 are the one's complement of the binary coded address of - the currently selected head. For example, if nHS3 through nHS0 are 1100b, - respectively, head 3 is selected. nHS3 is the most significant bit. - - nDS1 is the drive select bit for drive 1. When drive 1 is selected and - active, nDS1=0. - - nDS0 is the drive select bit for drive 0. When drive 0 is selected and - active, nDS0=0. - -..rm102 - NOTE: Care should be used when interpreting these bits, as they do not - always represent the expected status of drive operations at the - instant the status was put into this register. This is because of the - use of cacheing, translate mode and the Drive 0/Drive 1 concept with - each drive having its own embedded controller. - -7.2.8 Drive/Head Register - -This register contains the drive and head numbers. The contents of this -register define the number of heads minus 1, when executing an Initialize -Drive Parameters command. - - 7 6 5 4 3 2 1 0 - +-------+-------+-------+-------+-------+-------+-------+-------+ - | 1 | 0 | 1 | DRV | HS3 | HS2 | HS1 | HS0 | - +-------+-------+-------+-------+-------+-------+-------+-------+ - - - DRV is the binary encoded drive select number. When DRV=0, Drive 0 is - selected. When DRV=1, Drive 1 is selected. - - HS3 through HS0 contain the binary coded address of the head to be selected - e.g. if HS3 through HS0 are 0011b, respectively, head 3 will be selected. - HS3 is the most significant bit. At command completion, this register is - updated to reflect the currently selected head. - -7.2.9 Error Register - -This register contains status from the last command executed by the drive or a -Diagnostic Code. - -At the completion of any command except Execute Drive Diagnostic, the contents -of this register are valid when ERR=1 in the Status Register. - -Following a power on, a reset, or completion of an Execute Drive Diagnostic -command, this register contains a Diagnostic Code (see Table 9-2). - - 7 6 5 4 3 2 1 0 - +-------+-------+-------+-------+-------+-------+-------+-------+ - | BBK | UNC | 0 | IDNF | 0 | ABRT | TK0NF | AMNF | - +-------+-------+-------+-------+-------+-------+-------+-------+ - - - BBK (Bad Block Detected) indicates a bad block mark was detected in the - requested sector's ID field. - - UNC (Uncorrectable Data Error) indicates an uncorrectable data error has - been encountered. - - IDNF (ID Not Found) indicates the requested sector's ID field could not be - found. - - ABRT (Aborted Command) indicates the requested command has been aborted due - to a drive status error (Not Ready, Write Fault, etc.) or because the - command code is invalid. - - TK0NF (Track 0 Not Found) indicates track 0 has not been found during a - Recalibrate command. - - AMNF (Address Mark Not Found) indicates the data address mark has not been - found after finding the correct ID field. - - Unused bits are cleared to zero. - -7.2.10 Features Register - -This register is command specific and may be used to enable and disable -features of the interface e.g. by the Set Features Command to enable and -disable cacheing. - -This register may be ignored by some drives. - -Some hosts, based on definitions prior to the completion of this standard, set -values in this register to designate a recommended Write Precompensation -Cylinder value. - -7.2.11 Sector Count Register - -This register contains the number of sectors of data requested to be -transferred on a read or write operation between the host and the drive. If -the value in this register is zero, a count of 256 sectors is specified. - -If this register is zero at command completion, the command was successful. If -not successfully completed, the register contains the number of sectors which -need to be transferred in order to complete the request. - -The contents of this register may be defined otherwise on some commands e.g. -Initialize Drive Parameters, Format Track or Write Same commands. - -7.2.12 Sector Number Register - -This register contains the starting sector number for any disk data access for -the subsequent command. The sector number may be from 1 to the maximum number -of sectors per track. - -See the command descriptions for contents of the register at command -completion (whether successful or unsuccessful). - -..Typically, when a read or write command completes successfully, the value -..in the register is the last sector number accessed modulo the number of -..sectors per track. Typically, if a command does not complete successfully, -..the register contains the sector number at which an error occurred on a -..multiple sector transfer. -.. -7.2.13 Status Register - -This register contains the drive status. The contents of this register are -updated at the completion of each command. When BSY is cleared, the other bits -in this register shall be valid within 400 nsec. If BSY=1, no other bits in -this register are valid. If the host reads this register when an interrupt is -pending, it is considered to be the interrupt acknowledge. Any pending -interrupt is cleared whenever this register is read. - -..rm102 - NOTE: If Drive 1 is not detected as being present, Drive 0 clears the Drive - 1 Status Register to 00h (indicating that the drive is Not Ready). - - 7 6 5 4 3 2 1 0 - +-------+-------+-------+-------+-------+-------+-------+-------+ - | BSY | DRDY | DWF | DSC | DRQ | CORR | IDX | ERR | - +-------+-------+-------+-------+-------+-------+-------+-------+ - - - BSY (Busy) is set whenever the drive has access to the Command Block - Registers. The host should not access the Command Block Register when - BSY=1. When BSY=1, a read of any Command Block Register shall return the - contents of the Status Register. This bit is set by the drive (which may be - able to respond at times when the media cannot be accessed) under the - following circumstances: - a) within 400 nsec after the negation of RESET- or after SRST has been set - in the Device Control Register. Following acceptance of a reset it is - recommended that BSY be set for no longer than 30 seconds by Drive 1 and - no longer than 31 seconds by Drive 0. - b) within 400 nsec of a host write of the Command Register with a Read, - Read Long, Read Buffer, Seek, Recalibrate, Initialize Drive Parameters, - Read Verify, Identify Drive, or Execute Drive Diagnostic command. - c) within 5 usecs following transfer of 512 bytes of data during execution - of a Write, Format Track, or Write Buffer command, or 512 bytes of data - and the appropriate number of ECC bytes during the execution of a Write - Long command. - - DRDY (Drive Ready) indicates that the drive is capable of responding to a - command. When there is an error, this bit is not changed until the Status - Register is read by the host, at which time the bit again indicates the - current readiness of the drive. This bit shall be cleared at power on and - remain cleared until the drive is ready to accept a command. - - DWF (Drive Write Fault) indicates the current write fault status. When an - error occurs, this bit shall not be changed until the Status Register is - read by the host, at which time the bit again indicates the current write - fault status. - - DSC (Drive Seek Complete) indicates that the drive heads are settled over a - track. When an error occurs, this bit shall not be changed until the Status - Register is read by the host, at which time the bit again indicates the - current Seek Complete status. - - DRQ (Data Request) indicates that the drive is ready to transfer a word or - byte of data between the host and the drive. - - CORR (Corrected Data) indicates that a correctable data error was - encountered and the data has been corrected. This condition does not - terminate a data transfer. - - IDX (Index) is set once per disk revolution. - - ERR (Error) indicates that an error occurred during execution of the - previous command. The bits in the Error Register have additional - information regarding the cause of the error. - diff --git a/sys/i386/doc/ata/ata-8 b/sys/i386/doc/ata/ata-8 deleted file mode 100644 index f651b6b49326..000000000000 --- a/sys/i386/doc/ata/ata-8 +++ /dev/null @@ -1,143 +0,0 @@ -8. Programming Requirements - -8.1 Reset Response - -A reset is accepted within 400 nsec after the negation of RESET- or within 400 -nsec after SRST has been set in the Device Control Register. - -When the drive is reset by RESET-, Drive 1 shall indicate it is present by -asserting DASP- within 400 msec, and DASP- shall remain asserted for 30 -seconds or until Drive 1 accepts the first command. See also 6.3.4 and 6.3.13. - -When the drive is reset by SRST, the drive shall set BSY=1. - -See also 7.2.6. - -When a reset is accepted, and with BSY set: - - a) Both drives perform any necessary hardware initialization - b) Both drives clear any previously programmed drive parameters - c) Both drives may revert to the default condition - d) Both drives load the Command Block Registers with their default values - e) If a hardware reset, Drive 0 waits for DASP- to be asserted by Drive 1 - f) If operational, Drive 1 asserts DASP- - g) Drive 0 waits for PDIAG- to be asserted if Drive 1 asserts DASP- - h) If operational, Drive 1 clears BSY - i) If operational, Drive 1 asserts PDIAG- - j) Drive 0 clears BSY - -No interrupt is generated when initialization is complete. - -The default values for the Command Block Registers if no self-tests are -performed or if no errors occurred are: - - Error = 01h Cylinder Low = 00h - Sector Count = 01h Cylinder High = 00h - Sector Number = 01h Drive/Head = 00h - -The Error Register shall contain a Diagnostic Code (see Table 9.2) if a self- -test is performed. - -Following any reset, the host should issue an Initialize Drive Parameters -command to ensure the drive is initialized as desired. - -There are three types of reset in ATA. The following is a suggested method of -classifying reset actions: - - - Power On Reset: the drive executes a series of electrical circuitry - diagnostics, spins up the HDA, tests speed and other mechanical - parametrics, and sets default values. - - Hardware Reset: the drive executes a series of electrical circuitry - diagnostics, and resets to default values. - - Software Reset: the drive resets the interface circuitry to default values. - -8.2 Translate Mode - -The cylinder, head and sector geometry of the drive as presented to the host -may differ from the actual physical geometry. Translate mode is an optional -and device specific means of mapping between the two. - -8.3 Power Conditions - -Optional power commands permit the host to modify the behavior of the drive in -a manner which reduces the power required to operate. - - TABLE 8-1: POWER CONDITIONS - +----------+----+----+----+------------------+-----+ - | Mode |SRST| BSY|DRDY| Interface Active |Media| - +----------+----+----+----+------------------+-----+ - | Sleep | 1 | x | x | No | 0 | - | | | | | | | - | Standby | x | 0 | 1 | Yes | 0 | - | | | | | | | - | Idle | x | 0 | 1 | Yes | 1 | - | | | | | | | - | Active | x | x | x | Yes | 1 | - +----------+----+----+----+------------------+-----+ - | 1 = Active 0 = Inactive | - +--------------------------------------------------+ - -The lowest power consumption occurs in Sleep mode. When in Sleep mode, the -drive needs a Software Reset to be activated (see 9.18). The time to respond -could be as long as 30 seconds or more. - -In Standby mode the drive interface is capable of accepting commands, but as -the media is not immediately accessible, it could take the drive as long as 30 -seconds or more to respond. - -In Idle mode the drive is capable of responding immediately to media access -requests. A drive in Idle mode may take longer to complete the execution of a -command because it may have to activate some circuitry. - -In Active mode the drive is capable of responding immediately to media access -requests, and commands complete execution in the shortest possible time. - -Ready is not a power condition. A drive may post ready at the interface even -though the media may not be accessible. - -See specific power-related commands. - -8.4 Error Posting - -The errors that are valid for each command are defined in Table 8-1. It is not -a requirement that all valid conditions be implemented. See 7.2.9 and 7.2.13 -for the definition of the Error Register and Status Register bits. - - TABLE 8-2: REGISTER CONTENTS - +----------------------------+---------------------+ - | Error Register | Status Register | - |BBK|UNC|IDNF|ABRT|TK0NF|AMNF|DRDY|DWF|DSC|CORR|ERR| - +------------------------+---+---+----+----+-----+----+----+---+---+----+---+ - | Check Power Mode | | | | V | | | V | V | V | | V | - | Execute Drive Diags | See 9.2 | | | | | V | - | Format Track | | | V | V | | | V | V | V | | V | - | Identify Drive | | | | V | | | V | V | V | | V | - | Idle | | | | V | | | V | V | V | | V | - | Idle Immediate | | | | V | | | V | V | V | | V | - | Initialize Drive Parms | | | | | | | V | V | V | | | - | Recalibrate | | | | V | V | | V | V | V | | V | - | Read Buffer | | | | V | | | V | V | V | | V | - | Read DMA | V | V | V | V | | V | V | V | V | V | V | - | Read Long | V | V | V | V | | V | V | V | V | V | V | - | Read Multiple | V | V | V | V | | V | V | V | V | V | V | - | Read Sector(s) | V | V | V | V | | V | V | V | V | V | V | - | Read Verify Sector(s) | V | V | V | V | | V | V | V | V | V | V | - | Seek | | | V | V | | | V | V | V | | V | - | Set Features | | | | V | | | V | V | V | | V | - | Set Multiple Mode | | | | V | | | V | V | V | | V | - | Sleep | | | | V | | | V | V | V | | V | - | Standby | | | | V | | | V | V | V | | V | - | Standby Immediate | | | | V | | | V | V | V | | V | - | Write Buffer | | | | V | | | V | V | V | | V | - | Write DMA | V | | V | V | | | V | V | V | | V | - | Write Long | V | | V | V | | | V | V | V | | V | - | Write Multiple | V | | V | V | | | V | V | V | | V | - | Write Same | V | | V | V | | | V | V | V | | V | - | Write Sector(s) | V | | V | V | | | V | V | V | | V | - | Write Verify | V | V | V | V | | V | V | V | V | V | V | - +------------------------+---+---+----+----+-----+----+----+---+---+----+---+ - | Invalid Command Code | | | | V | | | V | V | V | | V | - +------------------------+---+---+----+----+-----+----+----+---+---+----+---+ - | V = valid on this command | - +---------------------------------------------------------------------------+ - diff --git a/sys/i386/doc/ata/ata-9 b/sys/i386/doc/ata/ata-9 deleted file mode 100644 index 4cd3f6684989..000000000000 --- a/sys/i386/doc/ata/ata-9 +++ /dev/null @@ -1,721 +0,0 @@ -9. Command Descriptions - -Commands are issued to the drive by loading the pertinent registers in the -command block with the needed parameters, and then writing the command code to -the Command Register. - -The manner in which a command is accepted varies. There are three classes -(see Table 9-1) of command acceptance, all predicated on the fact that to -receive a command, BSY=0: - - - Upon receipt of a Class 1 command, the drive sets BSY within 400 nsec. - - Upon receipt of a Class 2 command, the drive sets BSY within 400 nsec, sets - up the sector buffer for a write operation, sets DRQ within 700 usec, and - clears BSY within 400 nsec of setting DRQ. - - Upon receipt of a Class 3 command, the drive sets BSY within 400 nsec, sets - up the sector buffer for a write operation, sets DRQ within 20 msec, and - clears BSY within 400 nsec of setting DRQ. - -..rm102 - NOTE: DRQ may be set so quickly on Class 2 and Class 3 that the BSY - transition is too short for BSY=1 to be recognized. - -The drive shall implement all mandatory commands as identified by an M, and -may implement the optional commands identified by an O, in Table 9-1. V -indicates a Vendor Specific command code. - - TABLE 9-1: COMMAND CODES AND PARAMETERS - +-------+-------------------+ - +-----+ |Command| Parameters Used | - |Class| | Code |FR SC SN CY DH | - +-----+----------------------------------+---+-------+---+---+---+---+---+ - | 1 | Check Power Mode | O |98h E5h| | y | | | D | - | 1 | Execute Drive Diagnostic | M | 90h | | | | | D*| - | 2 | Format Track | M | 50h | * | y | | y | y | - | 1 | Identify Drive | O | ECh | | | | | D | - | 1 | Idle | O |97h E3h| | y | | | D | - | 1 | Idle Immediate | O |95h E1h| | | | | D | - | 1 | Initialize Drive Parameters | M | 91h | | y | | | y | - | 1 | Recalibrate | M | 1xh | | | | | D | - | 1 | Read Buffer | O | E4h | | | | | D | - | 1 | Read DMA (w/retry) | O | C8h | | y | y | y | y | - | 1 | Read DMA (w/o retry) | O | C9h | | y | y | y | y | - | 1 | Read Multiple | O | C4h | | y | y | y | y | - | 1 | Read Sector(s) (w/retry) | M | 20 | | y | y | y | y | - | 1 | Read Sector(s) (w/o retry) | M | 21 | | y | y | y | y | - | 1 | Read Long (w/retry) See 9.13 | M | 22 | | y | y | y | y | - | 1 | Read Long (w/o retry) See 9.13 | M | 23 | | y | y | y | y | - | 1 | Read Verify Sector(s) (w/retry) | M | 40 | | y | y | y | y | - | 1 | Read Verify Sector(s) (w/o retry)| M | 41 | | y | y | y | y | - | 1 | Seek | M | 7xh | | | y | y | y | - | 1 | Set Features | O | EFh | y | | | | D | - | 1 | Set Multiple Mode | O | C6h | | y | | | D | - | 1 | Set Sleep Mode | O |99h E6h| | | | | D | - | 1 | Standby | O |96h E2h| | y | | | D | - | 1 | Standby Immediate | O |94h E0h| | | | | D | - | 2 | Write Buffer | O | E8h | | | | | D | - | 3 | Write DMA (w/retry) | O | CAh | | y | y | y | y | - | 3 | Write DMA (w/o retry) | O | CBh | | y | y | y | y | - | 3 | Write Multiple | O | C5h | * | y | y | y | y | - | 3 | Write Same | O | E9h | y | y | y | y | y | - | 2 | Write Sector(s) (w/retry) | M | 30 | * | y | y | y | y | - | 2 | Write Sector(s) (w/o retry) | M | 31 | * | y | y | y | y | - | 2 | Write Sector(s) (w/retry) | M | 32 | * | y | y | y | y | - | 2 | Write Sector(s) (w/o retry) | M | 33 | * | y | y | y | y | - | 3 | Write Verify | O | 3Ch | * | y | y | y | y | - | | Vendor Unique | V | 9Ah | | | | | | - | | Vendor Unique | V | C0-C3h| | | | | | - | | Vendor Unique | V | 8xh | | | | | | - | | Vendor Unique | V |F5h-FFh| | | | | | - | | EATA Standard (CAM/89-004) | O |F0h-F4h| | | | | | - | | Reserved: All remaining codes | | | | | | | | - +-----+----------------------------------+---+-------+---+---+---+---+---+ - | | CY = Cylinder Registers SC = Sector Count Register | - | | DH = Drive/Head Register SN = Sector Number Register | - | | FR = Features Register (see command descriptions for use) | -.. | | | -.. | | M = Mandatory O = Optional V = Vendor Specific | - | | y - the register contains a valid parameter for this command. | - | | For the Drive/Head Register, y means both the drive and | - | | head parameters are used. | - | | D - only the drive parameter is valid and not the head parameter.| - | | D* - Addressed to Drive 0 but both drives execute it. | - | | * - Maintained for compatibility (see 7.2.9) | - +-----+------------------------------------------------------------------+ - -9.1 Check Power Mode - -This command checks the power mode. - -If the drive is in, going to, or recovering from the Standby Mode the drive -shall set BSY, set the Sector Count Register to 00h, clear BSY, and generate -an interrupt. - -If the drive is in the Idle Mode, the drive shall set BSY, set the Sector -Count Register to FFh, clear BSY, and generate an interrupt. - -9.2 Execute Drive Diagnostic - -This command shall perform the internal diagnostic tests implemented by the -drive. See also 6.3.4 and 6.3.13. The DRV bit is ignored. Both drives, if -present, shall execute this command. - -If Drive 1 is present: - - - Drive 0 waits up to 5 seconds for Drive 1 to assert PDIAG-. - - If Drive 1 has not asserted PDIAG-, indicating a failure, Drive 0 shall - append 80h to its own diagnostic status. - - Both drives shall execute diagnostics. - - If Drive 1 diagnostic failure is detected when Drive 0 status is read, - Drive 1 status is obtained by setting the DRV bit, and reading status. - -If there is no Drive 1 present: - - - Drive 0 posts only its own diagnostic results. - - Drive 0 clears BSY, and generates an interrupt. - -The Diagnostic Code written to the Error Register is a unique 8-bit code as -shown in Table 9-2, and not as the single bit flags defined in 7.2.9. - -..Table 9-2 details the codes and the corresponding explanations. -If Drive 1 fails diagnostics, Drive 0 "ORs" 80h with its own status and loads -that code into the Error Register. If Drive 1 passes diagnostics or there is -no Drive 1 connected, Drive 0 "ORs" 00h with its own status and loads that -code into the Error Register. - - TABLE 9-2: DIAGNOSTIC CODES - +-------+ - | Code | - +-------+----------------------------------+ - | 01h | No error detected | - | 02h | Formatter device error | - | 03h | Sector buffer error | - | 04h | ECC circuitry error | - | 05h | Controlling microprocessor error | - | 8xh | Drive 1 failed | - +-------+----------------------------------+ - -9.3 Format Track - -The implementation of the Format Track command is vendor specific. The actions -may be a physical reformatting of a track, initializing the data field -contents to some value, or doing nothing. - -The Sector Count Register contains the number of sectors per track. - -The track address is specified in the Cylinder High and Cylinder Low -Registers, and the number of sectors is specified in the Sector Count -Register. When the command is accepted, the drive sets the DRQ bit and waits -for the host to fill the sector buffer. When the sector buffer is full, the -drive clears DRQ, sets BSY and begins command execution. - -The contents of the sector buffer shall not be written to the media, and may -be either ignored or interpreted as follows: - - DD15 ---- DD0 DD15 ---- DD0 - +------+------+ +------+------+--------------------------+ - | First|Desc- | | Last |Desc- | Remainder of buffer | - |Sector|riptor| : : : : : : |Sector|riptor| filled with zeros | - +------+------+ +------+------+--------------------------+ - -One 16-bit word represents each sector, the words being contiguous from the -start of a sector. Any words remaining in the buffer after the representation -of the last sector are filled with zeros. DD15-8 contain the sector number. If -an interleave is specified, the words appear in the same sequence as they -appear on the track. DD7-0 contain a descriptor value defined as follows: - - 00h - Format sector as good - 20h - Unassign the alternate location for this sector - 40h - Assign this sector to an alternate location - 80h - Format sector as bad - -..rm102 - NOTE: Some users of the ATA drive expect the operating system partition - table to be erased on a Format command. It is recommended that a drive - which does not perform a physical format of the track, write a data - pattern of all zeros to the sectors which have been specified by the - Format Track command. - -..rm102 - NOTE: It is recommended that implementors resassign data blocks which show - repeated errors. - -9.4 Identify Drive - -The Identify Drive command enables the host to receive parameter information -from the drive. When the command is issued, the drive sets BSY, stores the -required parameter information in the sector buffer, sets DRQ, and generates -an interrupt. The host then reads the information out of the sector buffer. -The parameter words in the buffer have the arrangement and meanings defined in -Table 9-3. All reserved bits or words shall be zero. - - +-------+ TABLE 9-3: IDENTIFY DRIVE INFORMATION - | Word | - +-------+------------------------------------------------------------------+ - | 0 | General configuration bit-significant information: | - | | 15 0 reserved for non-magnetic drives | - | | 14 1=format speed tolerance gap required | - | | 13 1=track offset option available | - | | 12 1=data strobe offset option available | - | | 11 1=rotational speed tolerance is > 0.5% | - | | 10 1=disk transfer rate > 10 Mbs | - | | 9 1=disk transfer rate > 5Mbs but <= 10Mbs | - | | 8 1=disk transfer rate <= 5Mbs | - | | 7 0 reserved for removable cartridge drive | - | | 6 1=fixed drive | - | | 5 1=spindle motor control option implemented | - | | 4 1=head switch time > 15 usec | - | | 3 1=not MFM encoded | - | | 2 1=soft sectored | - | | 1 1=hard sectored | - | | 0 0=reserved | - | 1 | Number of fixed cylinders | - | 2 | reserved | - | 3 | Number of heads | - | 4 | Number of unformatted bytes per track | - | 5 | Number of unformatted bytes per sector | - | 6 | Number of sectors per track | - | 7-9 | Vendor Unique | - | 10-19 | Serial number (20 ASCII characters, 0000h=not specified) | - | 20 | Buffer type | - | 21 | Buffer size in 512 byte increments (0000h=not specified) | - | 22 | # of ECC bytes passed on Read/Write Long cmds (0000h=not spec'd) | - | 23-26 | Firmware revision (8 ASCII characters, 0000h=not specified) | - | 27-46 | Model number (40 ASCII characters, 0000h=not specified) | - | 47 | 0000h = Read/Write Multiple commands not implemented | - | | x = number of sectors that can be transferred per interrupt | - | | on Read and Write Multiple commands | - | 48 | 0000h = cannot perform doubleword I/O | - | | 0001h = can perform doubleword I/O | - | 49 | Capabilities | - | | 15-9 0=reserved | - | | 8 1=DMA Supported | - | | 7-0 Vendor Unique | - | 50 | reserved | - | 51 | PIO data transfer cycle timing mode | - | 52 | DMA data transfer cycle timing mode | - | 53-127| reserved | - |128-159| Vendor Unique | - |160-255| reserved | - +-------+------------------------------------------------------------------+ - -The fields described in 9.4.1 through 9.4.5 are not affected by the Initialize -Drive Parameters command. - -9.4.1 Number of fixed cylinders - -The number of translated cylinders in the default translation mode. - -9.4.2 Number of heads - -The number of translated heads in the default translation mode. - -9.4.3 Number of unformatted bytes per track - -The number of unformatted bytes per translated track in the default -translation mode. - -9.4.4 Number of unformatted bytes per sector - -The number of unformatted bytes per sector in the default translation mode. - -9.4.5 Number of sectors per track - -The number of sectors per track in the default translation mode. - -9.4.6 Serial Number - -The contents of this field are right justified and padded with spaces (20h). - -9.4.7 Buffer Type - -The contents of the field are determined by the manufacturer. - - 0000h = not specified. - 0001h = a single ported single sector buffer which is not capable of - simultaneous data transfers to or from the host and the disk. - 0002h = a dual ported multi-sector buffer capable of simultaneous data - transfers to or from the host and the disk. - 0003h = a dual ported multi-sector buffer capable of simultaneous transfers - with a read cacheing capability. - 0004-FFFFh = reserved - -These codes are typically not used by the operating system, however, they are -useful for diagnostic programs which perform initialization routines e.g. a -different interleave may be desirable for 0001h vs 0002h or 0003h. - -9.4.8 Firmware Revision - -The contents of this field are left justified and padded with spaces (20h). - -9.4.9 Model Number - -The contents of this field are left justified and padded with spaces (20h). - -9.4.10 PIO data transfer cycle timing mode - -The PIO transfer timing for each ATA device falls into categories which have -unique parametric timing specifications. To determine the proper device timing -category, compare the Cycle Time specified in Figure 11-1 with the contents of -this field. The value returned should fall into one of the categories -specified in Figure 11-1, and if it does not, then Mode 0 shall be used to -serve as the default timing. - -9.4.11 DMA data transfer cycle timing mode - -The DMA transfer timing for each ATA device falls into categories which have -unique parametric timing specifications. To determine the proper device timing -category, compare the Cycle Time specified in Figure 11-3 with the contents of -this field. The value returned should fall into one of the categories -specified in Figure 11-3, and if it does not, then Mode 0 shall be used to -serve as the default timing. - -9.5 Idle - -This command causes the drive to set BSY, enter the Idle Mode, clear BSY, and -generate an interrupt. The interrupt is generated even though the drive may -not have fully transitioned to Idle Mode. - -If the drive is already spinning, the spinup sequence is not executed. - -If the Sector Count Register is non-zero then the automatic power down -sequence shall be enabled and the timer begins counting down immediately. If -the Sector Count Register is zero then the automatic power down sequence shall -be disabled. - -9.6 Idle Immediate - -This command causes the drive to set BSY, enter the Idle Mode, clear BSY, and -generate an interrupt. The interrupt is generated even though the drive may -not have fully transitioned to Idle Mode. - -9.7 Initialize Drive Parameters - -..This command enables the host to control certain drive parameters. Some -..parameters are standard while others are vendor specific. -This command enables the host to set the number of sectors per track and the -number of heads minus 1, per cylinder. Upon receipt of the command, the drive -sets BSY, saves the parameters, clears BSY, and generates an interrupt. - -The only two register values used by this command are the Sector Count -Register which specifies the number of sectors per track, and the Drive/Head -Register which specifies the number of heads minus 1. The DRV bit designates -these values to Drive 0 or Drive 1, as appropriate. - -The sector count and head values are not checked for validity by this command. -If they are invalid, no error will be posted until an illegal access is made -by some other command. - -9.8 Recalibrate - -This command moves the read/write heads from anywhere on the disk to cylinder -0. Upon receipt of the command, the drive sets BSY and issues a seek to -cylinder zero. The drive then waits for the seek to complete before updating -status, clearing BSY and generating an interrupt. - -If the drive cannot reach cylinder 0, a Track Not Found error is posted. - -9.9 Read Buffer - -The Read Buffer command enables the host to read the current contents of the -drive's sector buffer. When this command is issued, the drive sets BSY, sets -up the sector buffer for a read operation, sets DRQ, clears BSY, and generates -an interrupt. The host then reads up to 512 bytes of data from the buffer. - -The Read Buffer and Write Buffer commands shall be synchronized such that -sequential Write Buffer and Read Buffer commands access the same 512 bytes -within the buffer. - -9.10 Read DMA - -This command executes in a similar manner to the Read Sectors command except -for the following: - - - the host initializes a slave-DMA channel prior to issuing the command - - data transfers are qualified by DMARQ and are performed by the slave-DMA - channel - - the drive issues only one interrupt per command to indicate that data - transfer has terminated and status is available. - -Any error encountered during execution of a Read DMA command results in the -termination of data transfer at the sector where the error was detected. The -sector in error is not transferred. The drive generates an interrupt to -indicate that data transfer has terminated and status is available. The error -posting is the same as that of the Read Sectors command. - -9.11 Read Long - -The Read Long command performs similarly to the Read Sectors command except -that it returns the data and the ECC bytes contained in the data field of the -desired sector. During a Read Long command, the drive does not check the ECC -bytes to determine if there has been a data error. Only single sector read -long operations are supported. - -The transfer of the ECC bytes shall be 8-bits wide. - -9.12 Read Multiple Command - -The Read Multiple command performs similarly to the Read Sectors command. -Interrupts are not generated on every sector, but on the transfer of a block -which contains the number of sectors defined by a Set Multiple command. - -Command execution is identical to the Read Sectors operation except that the -number of sectors defined by a Set Multiple command are transferred without -intervening interrupts. DRQ qualification of the transfer is required only at -the start of the data block, not on each sector. - -The block count of sectors to be transferred without intervening interrupts is -programmed by the Set Multiple Mode command, which shall be executed prior to -the Read Multiple command. - -When the Read Multiple command is issued, the Sector Count Register contains -the number of sectors (not the number of blocks or the block count) requested. - -If the number of requested sectors is not evenly divisible by the block count, -as many full blocks as possible are transferred, followed by a final, partial -block transfer. The partial block transfer shall be for n sectors, where - - n = (sector count) - modulo (block count) - -If the Read Multiple command is attempted before the Set Multiple Mode command -has been executed or when Read Multiple commands are disabled, the Read -Multiple operation shall be rejected with an Aborted Command error. - -Disk errors encountered during Read Multiple commands are posted at the -beginning of the block or partial block transfer, but DRQ is still set and the -data transfer shall take place as it normally would, including transfer of -corrupted data, if any. - -The contents of the Command Block Registers following the transfer of a data -block which had a sector in error are undefined. The host should retry the -transfer as individual requests to obtain valid error information. - -Subsequent blocks or partial blocks are transferred only if the error was a -correctable data error. All other errors cause the command to stop after -transfer of the block which contained the error. Interrupts are generated when -DRQ is set at the beginning of each block or partial block. - -..The error reporting is the same as that on a Read Sectors command. -.. -9.13 Read Sector(s) - -This command reads from 1 to 256 sectors as specified in the Sector Count -register. A sector count of 0 requests 256 sectors. The transfer begins at the -sector specified in the Sector Number Register. See 10.1 for the DRQ, IRQ and -BSY protocol on data transfers. - -If the drive is not already on the desired track, an implied seek is -performed. Once at the desired track, the drive searches for the appropriate -ID field. - -If retries are disabled and two index pulses have occurred without error free -reading of the requested ID, an ID Not Found error is posted. - -If retries are enabled, up to a vendor specific number of attempts may be made -to read the requested ID before posting an error. - -If the ID is read correctly, the data address mark shall be recognized within -a specified number of bytes, or the Address Mark Not Found error is posted. - -..Once the data address mark is found, the data field is read into the sector -..buffer, error bits are set if an error was encountered, DRQ is set, and an -..interrupt is generated. -.. -DRQ is always set regardless of the presence or absence of an error condition -at the end of the sector. - -At command completion, the Command Block Registers contain the cylinder, head, -and sector number of the last sector read. - -If an error occurs, the read terminates at the sector where the error -occurred. The Command Block Registers contain the cylinder, head, and sector -number of the sector where the error occurred. - -The flawed data is pending in the sector buffer. - -9.14 Read Verify Sector(s) - -This command is identical to the Read Sectors command, except that DRQ is -never set, and no data is transferred to the host. See 10.3 for protocol. -..When the command is accepted, the drive sets BSY. - -When the requested sectors have been verified, the drive clears BSY and -generates an interrupt. Upon command completion, the Command Block Registers -contain the cylinder, head, and sector number of the last sector verified. - -If an error occurs, the verify terminates at the sector where the error -occurs. The Command Block Registers contain the cylinder, head, and sector -number of the sector where the error occurred. The Sector Count Register shall -contain the number of sectors not yet verified. - -9.15 Seek - -This command initiates a seek to the track and selects the head specified in -the command block. The drive need not be formatted for a seek to execute -properly. See 10.3 for protocol. The drive shall not set DSC=1 until the -action of seeking has completed. The drive may return the interrupt before the -seek is completed. - -If another command is issued to the drive while a seek is being executed, the -drive sets BSY=1, waits for the seek to complete, and then begins execution of -the command. - -..The Sector Number Register is used to translate between perceived and real -..geometry. -.. -9.16 Set Features - -This command is used by the host to establish the following parameters which -affect the execution of certain drive features: - - 44h Vendor unique length of ECC on Read Long/Write Long commands - 55h Disable read look-ahead feature - AAh Enable read look-ahead feature - BBh 4 bytes of ECC apply on Read Long/Write Long commands - -See 10.3 for protocol. If the value in the register is not supported or is -invalid, the drive posts an Aborted Command error. - -At power on, or after a software or hardware reset, the default mode is read -look-ahead enabled and 4 bytes of ECC. - -9.17 Set Multiple Mode - -This command enables the drive to perform Read and Write Multiple operations -and establishes the block count for these commands. See 10.3 for protocol. - -The Sector Count Register is loaded with the number of sectors per block. -Drives shall support block sizes of 2, 4, 8, and 16 sectors, if their buffer -size is at least 8,192 bytes, and may also support other block sizes. Upon -receipt of the command, the drive sets BSY=1 and checks the Sector Count -Register. - -If the Sector Count Register contains a valid value and the block count is -supported, the value is loaded for all subsequent Read Multiple and Write -Multiple commands and execution of those commands is enabled. If a block count -is not supported, an Aborted Command error is posted, and Read Multiple and -Write Multiple commands are disabled. - -If the Sector Count Register contains 0 when the command is issued, Read and -Write Multiple commands are disabled. - -At power on, or after a hardware or software reset, the default mode is Read -and Write Multiple disabled. - -9.18 Sleep - -This command is the only way to cause the drive to enter Sleep Mode. The drive -is spun down, and when it is stopped, BSY is cleared, an interrupt is -generated, and the interface becomes inactive. - -The only way to recover from Sleep mode without a reset or power on, is for -the host to issue a software reset. - -A drive shall not power on in Sleep Mode nor remain in Sleep Mode following a -reset sequence. If the drive is already spun down, the spin down sequence is -not executed. - -9.19 Standby - -This command causes the drive to enter the Standby Mode. See 10.3 for -protocol. The drive may return the interrupt before the transition to Standby -Mode is completed. - -If the drive is already spun down, the spin down sequence is not executed. - -If the Sector Count Register is non-zero then the automatic power down -sequence shall be enabled and the timer will begin counting down when the -drive returns to Idle mode. If the Sector Count Register is zero then the -automatic power down sequence shall be disabled. - -9.20 Standby Immediate - -This command causes the drive to enter the Standby Mode. See 10.3 for -protocol. The drive may return the interrupt before the transition to Standby -Mode is completed. - -If the drive is already spun down, the spin down sequence is not executed. - -9.21 Write Buffer - -This command enables the host to overwrite the contents of the drive's sector -buffer with any data pattern desired. See 10.2 for protocol. - -The Read Buffer and Write Buffer commands shall be synchronized within the -drive such that sequential Write Buffer and Read Buffer commands access the -same 512 bytes within the buffer. - -9.22 Write DMA - -This command executes in a similar manner to Write Sectors except for the -following: - - - the host initializes a slave-DMA channel prior to issuing the command - - data transfers are qualified by DMARQ and are performed by the slave-DMA - channel - - the drive issues only one interrupt per command to indicate that data - transfer has terminated and status is available. - -Any error encountered during Write DMA execution results in the termination of -data transfer. The drive issues an interrupt to indicate that data transfer -has terminated and status is available in the Error Register. The error -posting is the same as that of the Write Sectors command. - -9.23 Write Multiple Command - -This command is similar to the Write Sectors command. The drive sets BSY -within 400 nsec of accepting the command, and interrupts are not presented on -each sector but on the transfer of a block which contains the number of -sectors defined by Set Multiple. - -Command execution is identical to the Write Sectors operation except that the -number of sectors defined by the Set Multiple command are transferred without -intervening interrupts. DRQ qualification of the transfer is required only at -the start of the data block, not on each sector. - -The block count of sectors to be transferred without intervening interrupts is -programmed by the Set Multiple Mode command, which shall be executed prior to -the Read Multiple command. - -When the Write Multiple command is issued, the Sector Count Register contains -the number of sectors (not the number of blocks or the block count) requested. - -If the number of requested sectors is not evenly divisible by the block count, -as many full blocks as possible are transferred, followed by a final, partial -block transfer. The partial block transfer is for n sectors, where - - n = (sector count) - modulo (block count) - -If the Write Multiple command is attempted before the Set Multiple Mode -command has been executed or when Write Multiple commands are disabled, the -Write Multiple operation shall be rejected with an aborted command error. - -Disk errors encountered during Write Multiple commands are posted after the -attempted disk write of the block or partial block transferred. The Write -command ends with the sector in error, even if it was in the middle of a -block. Subsequent blocks are not transferred in the event of an error. -Interrupts are generated when DRQ is set at the beginning of each block or -partial block. - -..The Command Block Registers contain the cylinder, head, and sector number of -..the sector where the error occurred and the Sector Count Register contains the -..residual of sectors that need to be transferred for successful completion of -..the command e.g. each block has 4 sectors, a request for 8 sectors is issued, -..and an error occurs on the third sector. The Sector Count Register contains 6 -..and the address is that of the third sector. -.. -The contents of the Command Block Registers following the transfer of a data -block which had a sector in error are undefined. The host should retry the -transfer as individual requests to obtain valid error information. - -9.24 Write Same - -This command executes in a similar manner to Write Sectors except that only -one sector of data is transferred. The contents of the sector are written to -the medium one or more times. - -NOTE: The Write Same command allows for initialization of part or all of the -medium to the specified data with a single command. - -If the Features Register is 22h, the drive shall write that part of the medium -specified by the sector count, sector number, cylinder and drive/head -registers. If the Features Register contains DDh, the drive shall initialize -all the user accessible medium. If the register contains a value other than -22h or DDh, the command shall be rejected with an aborted command error. - -The drive issues an interrupt to indicate that the command is complete. Any -error encountered during execution results in the termination of the write -operation. Status is available in the Error Register if an error occurs. The -error posting is the same as that of the Write Sectors command. - -9.25 Write Long - -This command is similar to the Write Sectors command except that it writes the -data and the ECC bytes directly from the sector buffer; the drive does not -generate the ECC bytes itself. Only single sector Write Long operations are -supported. - -The transfer of the ECC bytes shall be 8-bits wide. - -9.26 Write Sector(s) - -This command writes from 1 to 256 sectors as specified in the Sector Count -Register (a sector count of zero requests 256 sectors), beginning at the -specified sector. See 10.1 for the DRQ, IRQ and BSY protocol on data -transfers. - -..When this command is accepted, the drive sets DRQ and waits -..for the host to fill the sector buffer with the data to be written. No -..interrupt is generated to start the first buffer fill operation. Once the -..buffer is full, the drive clears DRQ, sets BSY and begins command execution. -.. -If the drive is not already on the desired track, an implied seek is -performed. Once at the desired track, the drive searches for the appropriate -ID field. - -If retries are disabled and two index pulses have occurred without error free -reading of the requested ID, an ID Not Found error is posted. - -If retries are enabled, up to a vendor specific number of attempts may be made -to read the requested ID before posting an error. - -If the ID is read correctly, the data loaded in the buffer is written to the -data field of the sector, followed by the ECC bytes. Upon command completion, -the Command Block Registers contain the cylinder, head, and sector number of -the last sector written. - -If an error occurs during a write of more than one sector, writing terminates -at the sector where the error occurs. The Command Block Registers contain the -cylinder, head, and sector number of the sector where the error occurred. The -host may then read the command block to determine what error has occurred, and -on which sector. - -9.27 Write Verify - -This command is similar to the Write Sectors command, except that each sector -is verified immediately after being written. The verify operation is a read -without transfer and a check for data errors. Any errors encountered during -the verify operation are posted. Multiple sector write verify commands write -all the requested sectors and then verify all the requested sectors before -generating the final interrupt. - diff --git a/sys/i386/doc/ata/ata-apx b/sys/i386/doc/ata/ata-apx deleted file mode 100644 index cd8981c58eab..000000000000 --- a/sys/i386/doc/ata/ata-apx +++ /dev/null @@ -1,426 +0,0 @@ -Annex A: Diagnostic and Reset Considerations - (informative). - -This annex describes the following timing relationships during: - - a) Power On and Hardware Resets - - One drive - - Two drives - b) Software Reset - - One drive - - Two drives - c) Diagnostic Command Execution - - One drive - - Two drives - - Two drives - Drive1 failed - -The timing assumes the following: - - o DASP- is asserted by Drive1 and received by Drive0 at power-on or - hardware reset to indicate the presence of Drive1. At all other times it - is asserted by Drive0 and Drive1 to indicate when a drive is active. - o PDIAG- is asserted by Drive1 and detected by Drive0. It is used by - Drive1 to indicate to Drive0 that it has completed diagnostics and is - ready to accept commands from the Host (BSY bit is cleared). This does not - indicate that the drive is ready, only that it can accept commands. This - line may remain asserted until the next reset occurs or an Execute - Diagnostic command is received. - o Unless indicated otherwise, all times are relative to the event that - triggers the operation (RESET-, SRST=1, Execute Diagnostic Command). - -A.1 Power On and Hardware Resets - -A.1.1 Power On and Hardware Resets - One Drive - - - Host asserts RESET- for a minimum of 25 usec. - - Drive0 sets BSY within 400 nsecs after RESET- is negated. - - Drive0 negates DASP- within 1 msec after RESET- negated. - - Drive0 performs hardware initialization - - Drive0 may revert to its default condition - - Drive0 waits 1 msec then samples for at least 450 msec for DASP- to be - asserted from Drive1. - - Drive0 clears BSY when ready to accept commands (within 31 seconds). - -A.1.2 Power On and Hardware Resets - Two Drives - - - Host asserts RESET- for a minimum of 25 usec. - - Drive0 and Drive1 set BSY within 400 nsec after RESET- negated. - - DASP- is negated within 1 msec after RESET- is negated. - -A.1.2.1 Drive1 - - - Drive1 negates PDIAG- before asserting DASP-. - - Drive1 asserts DASP- within 400 msecs after RESET- (to show presence). - - Drive1 performs hardware initialization and executes its internal - diagnostics. - - Drive1 may revert to its default condition - - Drive1 posts diagnostic results to the Error Register - - Drive1 clears BSY when ready to accept commands. - - Drive1 asserts PDIAG- to indicate that it is ready to accept commands - (within 30 seconds from RESET-). - - Drive1 negates DASP- after the first command is received or negates DASP- - if no command is received within 30 seconds after RESET-. - -A.1.2.2 Drive0 - - - Drive0 performs hardware initialization and executes its internal - diagnostics. - - Drive0 may revert to its default condition - - Drive0 posts diagnostic results to the Error Register - - After 1 msec, Drive0 waits at least 450 msec for DASP- to be asserted - (from Drive1). If DASP- is not asserted, no Drive1 is present (see POWER- - ON RESET - One Driveoperation). - - Drive0 waits up to 31 seconds for Drive1 to assert PDIAG-. If PDIAG- is - not asserted, Drive0 sets Bit7=1 in the Error Register. - - Drive0 clears BSY when ready to accept commands (within 31 seconds). - -A.2 Software Reset - -A.2.1 Software Reset - One Drive - - - Host sets SRST=1 in the Device Control Register. -.. - Drive0 waits for SRST to be cleared by Host. -.. - Drive0 sets BSY within 400 nsec after SRST=0. - - Drive0 sets BSY within 400 nsec after detecting that SRST=1. - - Drive0 performs hardware initialization and executes its internal - diagnostics. - - Drive0 may revert to its default condition. - - Drive0 posts diagnostic results to the Error Register. - - Drive0 clears BSY when ready to accept commands (within 31 seconds). - -A.2.2 Software Reset - Two Drives - - - Host sets SRST=1 in the Device Control Register. -.. - Drives wait for host to clear SRST. -.. - Drive0 and Drive1 set BSY within 400 nsec after SRST=0. - - Drive0 and Drive1 set BSY within 400 nsec after detecting that SRST=1. - - Drive0 and Drive1 perform hardware initialization. - - Drive0 and Drive1 may revert to their default condition. - -A.2.2.1 Drive1 - - - Drive1 negates PDIAG- within 1 msec. - - Drive1 clears BSY when ready to accept commands. - - Drive1 asserts PDIAG- to indicate that it is ready to accept commands - (within 30 seconds). - -A.2.2.2 Drive0 - - - Drive0 waits up to 31 seconds for Drive1 to assert PDIAG-. - - Drive0 clears BSY when ready to accept commands (within 31 seconds). - -A.3 Diagnostic Command Execution - -A.3.1 Diagnostic Command Execution - One Drive(Passed) - - - Drive0 sets BSY within 400 nsec after the Execute Diagnostic command was - received. - - Drive0 performs hardware initialization and internal diagnostics. - - Drive0 resets Command Block registers to default condition. - - Drive0 posts diagnostic results to the Error Register - - Drive0 clears BSY when ready to accept commands (within 6 seconds). - -A.3.2 Diagnostic Command - Two Drives (Passed) - - - Drive0 and Drive1 set BSY within 400 nsec after the Execute Diagnostic - command was received. - -A.3.2.1 Drive1 - - - Drive1 negates PDIAG- within 1 msec after command received. - - Drive1 performs hardware initialization and internal diagnostics. - - Drive1 resets the Command Block registers to their default condition. - - Drive1 posts diagnostic results to the Error Register - - Drive1 clears BSY when ready to accept commands. - - Drive1 asserts PDIAG- to indicate that it is ready to accept commands - (within 5 seconds). - -A.3.2.2 Drive0 - - - Drive0 performs hardware initialization and internal diagnostics. - - Drive0 resets the Command Block registers to their default condition. - - Drive0 waits up to <5 seconds for Drive1 to assert PDIAG-. - - Drive0 posts diagnostic results to the Error Register - - Drive0 clears BSY when ready to accept commands (within 6 seconds). - -A.3.3 Diagnostic Command Execution - One Drive(Failed) - - - Drive0 sets BSY within 400 nsec after Diagnostic command received. - - Drive0 performs hardware initialization and internal diagnostics. - - Drive0 resets Command Block registers to default condition. - - Drive0 posts a Diagnostic Code to the Error Register indicating a failure. - - Drive0 clears BSY when ready to accept commands (within 6 seconds) - -A.3.4 Diagnostic Command Execution - Two Drives (Drive1 Failed) - - - Drive0 and Drive1 set BSY within 400 nsec after Diagnostic command - received. - -A.3.4.1 Drive1 - - - Drive1 negates PDIAG- within 1 msec after command received. - - Drive1 performs hardware initialization and internal diagnostics. - - Drive1 resets the Command Block registers to their default condition. - - Drive1 posts a Diagnostic Code to the Error Register indicating failure. - - Drive1 clears BSY. - - Drive1 does not assert PDIAG-, indicating that it failed diagnostics. - -A.3.4.2 Drive0 - - - Drive0 performs hardware initialization and internal diagnostics. - - Drive0 resets the Command Block registers to their default condition. - - Drive0 waits 6 seconds for Drive1 to assert PDIAG- but PDIAG- is not - asserted by Drive1. - - Drive0 posts a Diagnostic Code to the Error Register setting Bit7=1 to - indicate that Drive1 failed diagnostics. - - Drive0 clears BSY when ready to accept commands (within 6 seconds). - -..rm102 - NOTE: The 6 seconds referenced above is a host-oriented value. - -Annex B: Diagnostic and Reset Considerations - (informative). - -B.1 Power on and hardware reset (RESET-) - -DASP- is read by Drive0 to determine if Drive1 is present. If Drive1 is -present Drive0 will read PDIAG- to determine when it is valid to clear BSY -and whether Drive1 has powered on or reset without error, otherwise Drive0 -clears BSY whenever it is ready to accept commands. Drive 0 may assert DASP- -to indicate drive activity. - -B.2 Software reset - -If Drive1 is present Drive0 will read PDIAG- to determine when it is valid -to clear BSY and whether Drive1 has reset without any errors, otherwise -Drive 0 will simply reset and clear BSY. DASP- is asserted by Drive0 (and -Drive1 if it is present) in order to indicate drive active. - -B.3 DriveDiagnostic Command - -If Drive1 is present, Drive0 will read PDIAG- to determine when it is valid -to clear BSY and if Drive1 passed or failed the Execute DriveDiagnostic -command, otherwise Drive0 will simply execute its diagnostics and then clear -BSY. DASP- is asserted by Drive0 (and Drive1 if it is present) in order to -indicate the drive is active. - -B.4 Truth Table - -In all the above cases: Power on, RESET-, software reset, and the Execute -DriveDiagnostics command the Drive0 Error Register is calculated as follows: - - Drive1 PDIAG- Drive0 Error - Present? Asserted? Passed Register - - Yes Yes Yes 01h - Yes Yes No 0xh - Yes No Yes 81h - Yes No No 8xh - No (not read) Yes 01h - No (not read) No 0xh - -Where x indicates the appropriate Diagnostic Code for the Power on, RESET-, -software reset, or drivediagnostics error. - -B.5 Power On or Hardware Reset Algorithm - - 1) Power on or hardware reset - 2) The hardware should automatically do the following: - a) Set up the hardware to post both Drive0 and Drive1 status - b) Set the Drive0 Status Register to 80h (set BSY and clear - all the other status bits) - c) Set the Drive1 Status Register to 80h (set BSY and clear all - the other status bits) - 3) Read the single Drive0/Drive1 jumper and note its state - 4) Perform any remaining time critical hardware initialization including - starting the spin up of the disk if needed - 5) If Drive1 - a) Negate the PDIAG- signal - b) Set up PDIAG- as an output - c) Assert the DASP- output - d) Set up DASP- as an output if necessary - e) Set up the hardware so it posts Drive1 status only and - continue to post 80h for Drive1 status - NOTE: all this must happen within 400 msec after power on or RESET- - If Drive0 - a) Set up PDIAG- as an input - b) Release DASP- and set up DASP- as an input - c) Test DASP- for 450 msec or until DASP- is asserted by Drive1 - d) If DASP- is asserted within 450 msec - i) Note that Drive1 is present - ii) Set up the hardware so it posts Drive0 status only - and continue to post 80h for the Drive0 status - If DASP- is not asserted within 450 msec - i) note that Drive1 is not present - e) Assert DASP- to indicate drive activity - 6) Complete all the hardware initialization needed to get the drive ready, - including: - a) Set the Sector Count Register to 01h - b) Set the Sector Number Register to 01h - c) Set the Cylinder Low Register to 00h - d) Set the Cylinder High Register to 00h - e) Set the Drive/Head Register to 00h - 7) If Drive1 and power on, or RESET- is valid - a) Set the Error Register to Diagnostic Code 01h - b) Set the Drive1 Status Register to 00h - c) Assert PDIAG- - NOTE: All this must happen within 5 seconds of power on or the - negation of RESET- - If Drive1 and power on or RESET- bad - a) Set the Error Register to the appropriate Diagnostic Code - b) Set the Drive1 Status Register to 00h - NOTE: All this must happen within 5 seconds of power on or the - negation of RESET- - If Drive0, power on or RESET- valid, and a Drive1 is present - a) Test PDIAG- for 6 seconds or until PDIAG- is asserted by Drive1 - b) If PDIAG- is asserted within 6 seconds - i) Set the Error Register to Diagnostic Code 01h - c) If PDIAG- is not asserted within 6 seconds - i) Set the Error Register to 81h - d) Set the Drive0 Status Register to 00h - If Drive0, power on or RESET- bad, and a Drive1 is present - a) Test PDIAG- for 6 seconds or until PDIAG- is asserted by - Drive1 - b) If PDIAG- is asserted within 6 seconds - i) Set the Error Register to the appropriate Diagnostic Code - c) If PDIAG- is not asserted within 6 seconds - i) Set the Error Register to 80h + the appropriate code - d) Set the Drive0 Status Register to 00h - If Drive0, power on or RESET- valid, and no Drive1 is present - a) Set the Error Register to Diagnostic Code 01h - b) Set the Drive1 Status Register to 00h - c) Set the Drive0 Status Register to 00h - If Drive0, power on or RESET- bad, and no Drive1 is present - a) Set the Error Register to the appropriate Diagnostic Code - b) Set the Drive1 Status Register to 00h - c) Set the Drive0 Status Register to 00h - 8) Finish spin up if needed - 9) If Drive1 - a) Set the Drive1 Status Register to 50h - b) Negate DASP- if a command is not received within 30 seconds - If Drive0 and a Drive1 is present - a) Set the Drive0 Status Register to 50h - b) Negate DASP- - If Drive0 and no Drive1 is present - a) Leave the Drive1 Status Register 00h - b) Set the Drive0 Status Register to 50h - c) Negate DASP- - -B.6 Software Reset Algorithm - - 1) The software reset bit is set - 2) If Drive1 - a) The hardware should set BUSY in the Drive1 Status Register - b) Negate the PDIAG- signal - NOTE: this must happen within 1 msec of the software reset - If Drive0 and Drive1 is present - a) The hardware should set BUSY in the Drive0 Status Register - If Drive0 and there is no Drive1 the hardware should: - a) Set BUSY in the Drive0 Status Register - b) Set the Drive1 Status Register to 80h - 3) Assert DASP- - 4) Finish all the hardware initialization needed to place the drive in reset - 5) Wait for the software reset bit to clear - 6) Finish all hardware initialization needed to get the drive ready - to receive any type of command from the host including: - a) Set the Sector Count Register to 01h - b) Set the Sector Number Register to 01h - c) Set the Cylinder Low Register to 00h - d) Set the Cylinder High Register to 00h - e) Set the Drive/Head Register to 00h - 7) If Drive1 and reset valid - a) Set the Error Register to Diagnostic Code 01h - b) Set the Drive1 Status Register to 50h - c) Assert PDIAG- - NOTE: All this must happen within 5 seconds of the clearing of - the software reset bit - If Drive1 and reset bad - a) Set the Error Register to the appropriate Diagnostic Code - b) Set the Drive1 Status Register to 50h - NOTE: All this must happen within 5 seconds of the clearing of - the software reset bit - If Drive0, reset valid, and a Drive1 is present - a) Test PDIAG- for 6 seconds or until PDIAG- is asserted by - Drive1 - b) If PDIAG- is asserted within 6 seconds - i) Set the Error Register to Diagnostic Code 01h - c) If PDIAG- is not asserted within 6 seconds - i) Set the Error Register to 81h - d) Set the Drive0 Status Register to 50h - If Drive0, reset bad, and a Drive1 is present - a) Test PDIAG- for 31 seconds or until PDIAG- is asserted by - Drive1 - b) If PDIAG- is asserted within 31 seconds - i) Set the Error Register to the appropriate Diagnostic Code - c) If PDIAG- is not asserted within 31 seconds - i) Set the Error Register to 80h + the appropriate code - d) Set the Drive0 Status Register to 50h - If Drive0, reset valid, and no Drive1 is present - a) Set the Error Register to Diagnostic Code 01h - b) Set the Drive1 Status Register to 00h - c) Set the Drive0 Status Register to 50h - If Drive0, reset bad, and no Drive1 is present - a) Set the Error Register to the appropriate Diagnostic Code - b) Set the Drive1 Status Register to 00h - c) Set the Drive0 Status Register to 50h - -B.7 Diagnostic Command Algorithm - - 1) The diagnostics command is received - 2) If Drive1 - a) The hardware should set BUSY in the Drive1 Status Register - b) Negate the PDIAG- signal - NOTE: this must happen within 1 msec after command acceptance - If Drive0 and Drive1 is present - a) The hardware should set BUSY in the Drive0 Status Register - If Drive0 and there is no Drive1 the hardware should - a) Set BUSY in the Drive0 Status Register - b) Set BUSY in the Drive1 Status Register - 3) Assert DASP- - 4) Perform all the drive diagnostics and note their results - 5) Finish all the hardware initialization needed to get the drive ready - to receive any type of command from the host including: - a) Set the Sector Count Register to 01h - b) Set the Sector Number Register to 01h - c) Set the Cylinder Low Register to 00h - d) Set the Cylinder High Register to 00h - e) Set the Drive/Head Register to 00h - 6) If Drive1 and passed - a) Set the Error Register to Diagnostic Code 01h - b) Set the Drive1 status to 50h - c) Assert PDIAG- - NOTE: All this must happen within 5 seconds of the acceptance - of the diagnostic command - If Drive1 and did not pass - a) Set the Error Register to the appropriate Diagnostic Code - b) Set the Drive1 status to 50h - NOTE: All this must happen within 5 seconds of the acceptance - of the diagnostic command - If Drive0, passed, and a Drive1 is present - a) Test PDIAG- for 6 seconds or until PDIAG- is asserted by - Drive1 - b) If PDIAG- is asserted within 6 seconds - i) Set the Error Register to Diagnostic Code 01h - c) If PDIAG- is not asserted within 6 seconds - i) Set the Error Register to 81h - d) Set the Drive0 status to 50h - e) Issue interrupt to the host - If Drive0, did not pass, and a Drive1 is present - a) Test PDIAG- for 6 seconds or until PDIAG- is asserted by Drive1 - b) If PDIAG- is asserted within 6 seconds - i) Set the Error Register to the appropriate Diagnostic Code - c) If PDIAG- is not asserted within seconds - i) Set the Error Register to 80h + the appropriate code - d) Set the Drive0 Status Register to 50h - e) Issue interrupt to the host - If Drive0, passed, and no Drive1 is present - a) Set the Error Register to Diagnostic Code 01h - b) Set the Drive1 Status Register to 00h - c) Set the Drive0 Status Register to 50h - d) Issue interrupt to the host - If Drive0, did not pass, and no Drive1 is present - a) Set the Error Register to the appropriate Diagnostic Code - b) Set the Drive1 Status Register to 00h - c) Set the Drive0 Status Register to 50h - d) Issue interrupt to the host - diff --git a/sys/i386/doc/ata/ata-prt b/sys/i386/doc/ata/ata-prt deleted file mode 100644 index 546510bcf6a0..000000000000 --- a/sys/i386/doc/ata/ata-prt +++ /dev/null @@ -1,174 +0,0 @@ -Copies of this proposal may be purchased from: CAM/89-002 -CAM Committee 408-867-6630 -14426 Black Walnut Ct, Saratoga CA 95070 - - working document of the - CAM (Common Access Method Committee) - - draft proposal - - ATA (AT Attachment) - - Rev 2.1 June 11, 1990 - -ABSTRACT: This standard defines mechanical, electrical, and functional -requirements for attaching small computers employing an AT Bus with -intelligent peripheral devices. The resulting interface provides a common -interface specification for systems manufacturers, system integrators, -controller manufacturers, and suppliers of intelligent disk drives. - - -CAUTION: This is a preliminary draft of the proposed standard. It is subject -to change without notice, at the sole discretion of the CAM Committee. It is -likely that some sections may change significantly, and most sections will -change to some extent in the course of further development of this draft -standard. This draft is distributed solely for the purpose of review and -comment, and it should not be used as a design document without assuming the -risk of an early implementation. - -COPYRIGHT NOTICE: This draft standard is based upon product specifications of -contributing members, these documents may be copyrighted by the individual -companies. This draft standard may be reproduced without further permission, -for the purpose of review and comment or for the purpose of including in an -appropriate product specification provided the risk of early implementation is -assumed. All other rights are reserved. - - -POINTS OF CONTACT: - - I. Dal Allan (CAM Committee Chairman) - ENDL - 14426 Black Walnut Court - Saratoga, CA 95070 - (408) 867-6630 - - - -If changes from the prior revision of this document are included, they may -be identified by: - - - change bars at the side of each paragraph or - - printed in bold or - - printed in italics or - - underscored in the text. - -The specific technique used will depend on the software used to detect the -differences and the printer used to print the document. - -In the event that deleted text is identified by an overstrike, the Table of -Contents will not be accurate. - -An electronic copy of this document is available from the CAM Committee -(408-867-6630). - -This document has been prepared according to the style guide of the ISO -(International Organization of Standards). - - -If this document was printed in a 2-up form directly from the printer, NOTEs -had to be adjusted to fit into a half-page, which may have resulted in an -imperfect representation of the format within the NOTE. This is most likely -to occur if a series of NOTEs are mixed in without any line separation. - - - - - - - - -.. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -.. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -.. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -.. -.. Please look this revision over carefully. Larry and I have reviewed -.. this document for editorial consistency. -.. This is close to the final step in having a standards document. -.. Please pay special attention to references, because any renumbering -.. may mean a few boo-boos crept in. -.. -.. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -.. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -.. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -.. -.. EXPOSITORY REMARKS -.. -.. -.. -.. -.. -.. -.. -.. -..I S O -.. -..INTERNATIONAL ORGANIZATION FOR STANDARDIZATION -.. -.. -.. -.. -.. -.. -.. Information Processing Systems -- -.. -.. Common Access Method AT Attachment -.. -.. -.. -.. -.. -.. -.. -.. +-----------------------------------------+ -.. | +-------------------------------------+ | -.. | | | | -.. | | ISO 10***:198x | | -.. | | | | -.. | | | | -.. | | | | -.. | +-------------------------------------+ | -.. +-----------------------------------------+ -.. -.. -.. -.. -.. -.. -.. -.. -.. Technical Editor: I. Dal Allan -.. ENDL -.. 14426 Black Walnut Court -.. Saratoga -.. CA 95070 -.. -.. Ph: 408-867-6630 -.. Fx: 408-867-2115 -.. Tx: 650-250-1752 -.. -.. -..foCommon Access Method AT Attachment -..dm Merging ata-1 .dif -..fi ata-1 .dif -..dm Merging ata-2 .dif -..fi ata-2 .dif -..dm Merging ata-3 .dif -..fi ata-3 .dif -..dm Merging ata-4 .dif -..fi ata-4 .dif -..dm Merging ata-5 .dif -..fi ata-5 .dif -..dm Merging ata-6 .dif -..fi ata-6 .dif -..dm Merging ata-7 .dif -..fi ata-7 .dif -..dm Merging ata-8 .dif -..fi ata-8 .dif -..dm Merging ata-9 .dif -..fi ata-9 .dif -..dm Merging ata-10 .dif -..fi ata-10 .dif -..dm Merging ata-11 .dif -..fi ata-11 .dif -..dm Merging ata-apx .dif -..fi ata-apx .dif diff --git a/sys/i386/doc/ata/ata-toc b/sys/i386/doc/ata/ata-toc deleted file mode 100644 index 1866f7677596..000000000000 --- a/sys/i386/doc/ata/ata-toc +++ /dev/null @@ -1,159 +0,0 @@ - TABLE OF CONTENTS - - 1. Scope 1 - 1.1 Description of Clauses 1 - - 2. References 1 - 3. General Description 1 - 3.1 Structure 2 - - 4. Definitions and Conventions 2 - 4.1 Definitions 2 - 4.2 Conventions 2 - - 5. Interface Cabling Requirements 3 - 5.1 Configuration 3 - 5.2 Addressing Considerations 4 - 5.3 DC Cable and Connector 4 - 5.3.1 4-Pin Power 4 - 5.3.2 3-Pin Power 5 - 5.3.3 Device Grounding 5 - - 5.4 I/O Connector 5 - 5.5 I/O Cable 6 - - 6. Physical Interface 6 - 6.1 Signal Conventions 6 - 6.2 Signal Summary 7 - 6.3 Signal Descriptions 9 - 6.3.1 CS1FX- (Drive chip Select 0) 9 - 6.3.2 CS3FX- (Drive chip Select 1) 9 - 6.3.3 DA0-2 (Drive Address Bus) 10 - 6.3.4 DASP- (Drive Active/Drive 1 Present) 10 - 6.3.5 DD0-DD15 (Drive Data Bus) 10 - 6.3.6 DIOR- (Drive I/O Read) 10 - 6.3.7 DIOW- (Drive I/O Write) 10 - 6.3.8 DMACK- (DMA Acknowledge) (Optional) 10 - 6.3.9 DMARQ (DMA Request) (Optional) 11 - 6.3.10 INTRQ (Drive Interrupt) 11 - 6.3.11 IOCS16- (Drive 16-bit I/O) 11 - 6.3.12 IORDY (I/O Channel Ready) (Optional) 12 - 6.3.13 PDIAG- (Passed Diagnostics) 12 - 6.3.14 RESET- (Drive Reset) 12 - 6.3.15 SPSYNC (Spindle Synchronization) (Optional) 12 - - 7. Logical Interface 13 - 7.1 General 13 - 7.1.1 Bit Conventions 13 - 7.1.2 Environment 13 - - 7.2 I/O Register Descriptions 14 - 7.2.1 Alternate Status Register 14 - 7.2.2 Command Register 15 - 7.2.3 Cylinder High Register 15 - 7.2.4 Cylinder Low Register 15 - 7.2.5 Data Register 15 - 7.2.6 Device Control Register 15 - 7.2.7 Drive Address Register 16 - 7.2.8 Drive/Head Register 16 - 7.2.9 Error Register 16 - 7.2.10 Features Register 17 - 7.2.11 Sector Count Register 17 - 7.2.12 Sector Number Register 17 - 7.2.13 Status Register 18 - - 8. Programming Requirements 19 - 8.1 Reset Response 19 - 8.2 Translate Mode 19 - 8.3 Power Conditions 20 - 8.4 Error Posting 20 - - 9. Command Descriptions 21 - 9.1 Check Power Mode 24 - 9.2 Execute Drive Diagnostic 24 - 9.3 Format Track 24 - 9.4 Identify Drive 25 - 9.4.1 Number of fixed cylinders 26 - 9.4.2 Number of heads 27 - 9.4.3 Number of unformatted bytes per track 27 - 9.4.4 Number of unformatted bytes per sector 27 - 9.4.5 Number of sectors per track 27 - 9.4.6 Serial Number 27 - 9.4.7 Buffer Type 27 - 9.4.8 Firmware Revision 27 - 9.4.9 Model Number 27 - 9.4.10 PIO data transfer cycle timing mode 27 - 9.4.11 DMA data transfer cycle timing mode 28 - - 9.5 Idle 28 - 9.6 Idle Immediate 28 - 9.7 Initialize Drive Parameters 28 - 9.8 Recalibrate 28 - 9.9 Read Buffer 28 - 9.10 Read DMA 29 - 9.11 Read Long 29 - 9.12 Read Multiple Command 29 - 9.13 Read Sector(s) 30 - 9.14 Read Verify Sector(s) 30 - 9.15 Seek 31 - 9.16 Set Features 31 - 9.17 Set Multiple Mode 31 - 9.18 Sleep 32 - 9.19 Standby 32 - 9.20 Standby Immediate 32 - 9.21 Write Buffer 32 - 9.22 Write DMA 32 - 9.23 Write Multiple Command 33 - 9.24 Write Same 33 - 9.25 Write Long 34 - 9.26 Write Sector(s) 34 - 9.27 Write Verify 34 - - 10. Protocol Overview 35 - 10.1 PIO Data In Commands 35 - 10.1.1 PIO Read Command 35 - 10.1.2 PIO Read Aborted Command 36 - - 10.2 PIO Data Out Commands 36 - 10.2.1 PIO Write Command 36 - 10.2.2 PIO Write Aborted Command 37 - - 10.3 Non-Data Commands 37 - 10.4 Miscellaneous Commands 37 - 10.5 DMA Data Transfer Commands (Optional) 37 - 10.5.1 Normal DMA Transfer 38 - 10.5.2 Aborted DMA Transfer 38 - 10.5.3 Aborted DMA Command 38 - - 11. Timing 39 - 11.1 Deskewing 39 - 11.2 Symbols 39 - 11.3 Terms 39 - 11.4 Data Transfers 40 - 11.5 Power On and Hard Reset 42 - - FIGURES - - FIGURE 5-1: ATA INTERFACE TO EMBEDDED BUS PERIPHERALS 3 - FIGURE 5-2: HOST BUS ADAPTER AND PERIPHERAL DEVICES 4 - FIGURE 5-3: ATA INTERFACE TO CONTROLLER AND PERIPHERAL DEVICES 4 - FIGURE 5-4: 40-PIN CONNECTOR MOUNTING 6 - FIGURE 11-1: PIO DATA TRANSFER TO/FROM DRIVE 40 - FIGURE 11-2: IORDY TIMING REQUIRMENTS 41 - FIGURE 11-3: DMA DATA TRANSFER 41 - FIGURE 11-4 RESET SEQUENCE 42 - - TABLES - - TABLE 5-1: DC INTERFACE 5 - TABLE 5-2: DC INTERFACE 5 - TABLE 5-3: CABLE PARAMETERS 6 - TABLE 6-1: INTERFACE SIGNALS 8 - TABLE 6-2: INTERFACE SIGNALS DESCRIPTION 9 - TABLE 7-1: I/O PORT FUNCTIONS/SELECTION ADDRESSES 14 - TABLE 8-1: POWER CONDITIONS 20 - TABLE 8-2: REGISTER CONTENTS 21 - TABLE 9-1: COMMAND CODES AND PARAMETERS 23 - TABLE 9-2: DIAGNOSTIC CODES 24 - - diff --git a/sys/i386/doc/ed.relnotes b/sys/i386/doc/ed.relnotes deleted file mode 100644 index d01e21d4c427..000000000000 --- a/sys/i386/doc/ed.relnotes +++ /dev/null @@ -1,174 +0,0 @@ - - Release Notes for 'ed' Device Driver - David Greenman, 24-May-1993 - ------------------------------------ - -Last updated: 22-November-1993 - -INTRODUCTION ------------- - The 'ed' device driver is a new, high performance device driver supporting -the Western Digital/SMC 80x3 series (including 'EtherCard PLUS' 'Elite16' and -'Ultra'), the 3Com 3c503 (both 8 and 16 bit versions), and the Novell NE1000/ -NE2000. All of the ethernet controllers use the DS8390, 83C690, or 83C790 -Network Interface Controller (NIC). The differences between the boards are in -their memory capacity, bus width (8/16 bits), special logic (asic) used to -configure the board, and the host access method to the NIC memory (shared or -programmed I/O). Every effort has been made to conform to the manufactures' -specifications for the NIC and asic. This includes both normal operation and -error recovery. - -PERFORMANCE ------------ - -transmit --------- - The 8390 doesn't provide a mechanism for chained write buffers, so it is -very important for maximum performance to queue the next packet for -transmission as soon as the current one has completed. On boards with 16k or -more of memory, the NIC memory is divided in a way that allows enough space -for two full size packets to be buffered for transmission. When sufficient -data is available for transmission, a packet is copied into the NIC memory, -the transmission is started, and then an additional packet is copied into the -NIC memory (to a different memory area). As soon as the first packet has -completed, transmission of a second packet can then be started immediately. -This results in nearly the highest performance possible from ethernet. - -Packets go out on the 'wire' with the following format: - -preamble dest-addr src-addr type data FCS intr-frame -64bits 48bits 48bits 16bits 1500bytes 32bits 96bits - - With 10Mbits/sec, each bit is 100ns in duration. All of the above fields, -except for data are of fixed length. With full sized packets (1500 bytes), the -maximum unidirectional data rate can be calculated as: 6.4us + 4.8us + 4.8us + -1.6us + 1200us + 3.2us + 9.6us = 1230.4us/packet = 812.74382 packets/second = -1219115.7 (1190k) bytes/second. With TCP, there is a 40 byte overhead for the -IP and TCP headers, so there is only 1460 bytes of data per packet. This -reduces the maximum data rate to 1186606 bytes/second. With TCP, there will -also be periodic acknowledgments which will reduce this figure somewhat both -because of the additional traffic in the reverse direction and because of the -occasional collisions that this will cause. Despite this, the data rate has -still been consistantly measured at 1125000 (~1100k) bytes/second through a TCP -socket. In these tests, the TCP window was set to 16384 bytes. With UDP, there -is less overhead for the headers, and with 1472 bytes of data per packet, a -data rate of 1196358.9 (1168k) bytes/sec is possible. UDP performance hasn't -been precisely measured with this device driver, but less precise tests show -this to be true (measured at around 1135k/second). - -receive -------- - The 8390 implements an NIC memory ring-buffer to store incoming packets. -The 8bit boards (8003, 3c503, and NE1000) usually have only 8k bytes of shared -memory. This is only enough room for about 4 full size (1500 byte) packets. -This can sometimes be a problem, especially on the original WD8003E and 3c503. -This is because these boards' NIC memory access speed is also quite slow -compared to newer 16bit boards - typically less than 1MB/second. The additional -overhead of this slow memory access, and the fact that there is only room for 4 -full-sized packets means that the ring-buffer will occassionally overflow. When -this happens, the board must be reset to avoid a lockup problem in early -revision 8390's. Resetting the board will cause all of the data in the ring- -buffer to be lost - requiring it to be re-transmitted/received...slowing things -even further. Because of these problems, maximum throughput on boards of this -type is only about 400-600k per second. The 16bit boards, however, have 16k of -memory as well as much faster memory access speed. Typical memory access speed -on these boards is about 1.7-4MB/second (with the Novell NE2000 being the -slowest and the SMC 8013 being the fastest). These boards generally have no -problems keeping up with full ethernet speed. The only problem I've seen with -these boards is related to the (slow) performance of the BSD Net/2 malloc code -when additional mbufs must be added to the pool. This can sometimes increase -the total time to remove a packet enough for a ring-buffer overflow to occur. -This tends to be highly transient, and quite rare on fast machines. I've only -seen this problem when doing tests with large amounts of UDP traffic without -any acknowledgments (uni-directional). Again, this has been very rare. - - All of the above tests were done using a 486DX2/66, 486DX/33, 386DX/40, -8-9Mhz ISA bus, using FreeBSD 1.0. TCP tests were done with the 'ttcp' -performance test utility, and also with FTP client/server. UDP tests were done -with a modified version of ttcp (to work around a bug in the BSD Net/2 UDP -code related to queue depth), and also with NFS. - -KERNEL INSTALLATION -------------------- - (Note that this driver comes standard in FreeBSD 1.0, NetBSD 0.9, and 386BSD -0.2, and therefore doesn't require installation) - To 'install' this driver, the files if_ed.c and if_edreg.h must be copied -into the i386/isa kernel source directory and the following line must be -added into the file i386/conf/files.i386: - -i386/isa/if_ed.c optional ed device-driver - - To build a kernel that includes this driver, first comment out any 'we', -'ec', or 'ne' devices in your kernel config file. Then, add a line similar to -the following (modify to match your cards configuration): - -device ed0 at isa? port 0x300 net irq 10 iomem 0xcc000 vector edintr - - Note that the 'iosiz' option is not needed because the driver automatically -figures this out. However, if you have problems with this, it can be specified -to force the use of the size you specify. - The iomem 0xcc000 option is not need for NE1000/NE2000 boards because they -use programmed I/O rather than shared memory to access the NIC's memory. - On 3Com boards, the tranceiver must be enabled in software (there is no -hardware default). In this driver, this is controlled using the "LLC0" link- -level control flag. The default for this flag can be set in your kernel config -file by specifying 'flags 0x01' in the 'ed' device specification line (this -is necessary for diskless support). Otherwise, the tranceiver is easily enabled -and disabled with a command like "ifconfig ed0 -llc0" to enable the tranceiver -or "ifconfig ed0 llc0" to disable it; this assumes that you have the modified -ifconfig(8) that originally appeared in the 386BSD patchkit. To specify the -'flags' option, use a line similar to: - -device ed0 at isa? port 0x300 net irq 10 flags 0x01 iomem 0xcc000 vector edintr - - Flags can be similarly specified to force 8 or 16bit mode, and disabling -the use of transmitter double buffering. The various supported flag values -are: - - Disable tranceiver 0x01 - Force 8 bit mode 0x02 - Force 16 bit mode 0x04 - Disable multi TX buffering 0x08 - - To use multiple flags, simply add the values together. Note that these -numbers are in hexadecimal. If the 'Force 8 bit' and 'Force 16 bit' flags are -both specified, the 8 bit flag has precedence. - The use of the above flags should only be necessary under exceptional -conditions where the probe routine incorrectly determines the board type (such -is the case with Compex boards, which require the 16bit flag and an iosiz -16384), or where the high performance of the transmitter causes problems with -other vendors hardware. - - -KNOWN PROBLEMS --------------- - -1) Early revision DS8390B chips have problems. They lock-up whenever the - receive ring-buffer overflows. They occassionally switch the byte order - of the length field in the packet ring header (several different causes - of this related to an off-by-one byte alignment) - resulting in "NIC - memory corrupt - invalid length NNNNN" messages. The board is reset - whenever these problems occur, but otherwise there is no problem with - recovering from these conditions. -2) 16bit boards that use shared memory can conflict with 8bit BIOSes, BIOS - extensions (like the VGA), and 8bit devices with shared memory (again - like the VGA, or perhaps a second ethernet board). There is a work- - around for this in the driver, however. The problem is that the - ethernet board stays in 16bit mode, asserting its '16bit' signal on - the ISA bus. This signal is shared by other devices/ROMs in the same - 128K memory segment as the ethernet card - causing the CPU to read the - 8bit ROMs as if they were 16bit wide. The work-around involves setting - the host access to the shared memory to 16bits only when the memory is - actually accessed, and setting it back to 8bit mode all other times. - Without this work-around, the machine will hang whenever a reboot is - attempted. This work-around also allows the board to co-exist with - other 8bit devices that have shared memory. This has only been - implemented for SMC/WD boards, but I haven't seen this problem with - 3Com boards (i.e. if you have a 3Com board, you might experiance the - above problem - I haven't specifically tested for it). -3) The NIC memory access to 3Com and Novell boards is much slower than it is on - SMC boards; it's less than 1MB on 8bit boards and less than 2MB/second - on the 16bit boards. This can lead to ring-buffer overruns resulting in - additional lost data during heavy network traffic. - -$Id: ed.relnotes,v 1.4 1993/11/22 11:15:16 davidg Exp $ diff --git a/sys/i386/doc/options.texi b/sys/i386/doc/options.texi deleted file mode 100644 index 4313cf009459..000000000000 --- a/sys/i386/doc/options.texi +++ /dev/null @@ -1,974 +0,0 @@ -\input texinfo @c -*- texinfo -*- -@c %**start of header -@setfilename options.info -@settitle Configuration Options for FreeBSD -@c @setchapternewpage odd -@c function index is option/pseudo/device names; concept is everything -@c else -@syncodeindex fn cp -@finalout -@c %**end of header - -@ifinfo -$Id: options.texi,v 1.4.2.1 1994/03/07 01:51:59 rgrimes Exp $ - -This file documents the configuration options available in the FreeBSD -operating system. - -@display -Copyright @copyright{} 1993, 1994, Garrett A. Wollman. All rights reserved. - -Redistribution and use in source and printed forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice and this list of conditions. -2. Redistributions in printed form must reproduce the above copyright - notice, this list of conditions and the following notice of - authorship. - -Trademarks are property of their respective owners. -@end display -@end ifinfo - -@titlepage -@title Configuration Options in FreeBSD -@subtitle for FreeBSD 1.1 -@author Garrett A. Wollman -@author FreeBSD Project - -@page -@vskip 0pt plus 1filll - -i386, i387, and i486 are trademarks of Intel Corporation. SunOS is a -registered trademark and NFS is a trademark of Sun Microsystems, Inc. -Ultrix is a registered trademark of Digital Equipment Corporation. -Xerox is a registered trademark and XNS is a trademark of Xerox -Corporation. VESA is a trademark of the Video Electronics Standards -Association. System V is a registered trademark of Novell Corporation. -All other trademarks are property of their respective owners. FreeBSD -is nobody's trademark, and damn proud of it. - -Copyright @copyright{} 1993, 1994, Garrett A. Wollman. All rights reserved. - -Redistribution and use in source and printed forms, with or without -modification, are permitted provided that the following conditions -are met: - -@enumerate -@item -Redistributions of source code must retain the above copyright -notice and this list of conditions. - -@item -Redistributions in printed form must reproduce the above copyright -notice, this list of conditions and the preceding notice of -authorship. -@end enumerate -@end titlepage - -@node Top, Subsystems, (dir), (dir) -@top FreeBSD Configuration Options - -This document describes kernel configuration options relevant to the -FreeBSD operating system version 1.1. It is intended for readers who -already have a general understanding of the process of configuring a BSD -kernel and wish to get a general overview of the meaning of various -configuration options. This document covers configurable options and -pseudo-devices; it is intended that devices may be added at a later -date. - -@menu -* Subsystems:: Controlling subsystems. -* Performance:: Performance enhancement. -* Devices:: Device-control options. -* Internals:: Non-user-serviceable parts. - -* Index:: General Index. -@end menu - -@node Subsystems, Performance, Top, Top -@c node,next,prev,up - -@chapter Options for Subsystems - -This chapter discusses options controlling the inclusion of various -subsystems in FreeBSD. These include things like filesystems, -networking modules, and whatnot. Remember that options containing -underscores must be quoted. - -@table @code -@item pseudo-device bpfilter @var{number} -@findex bpfilter -@cindex Berkeley packet filter -@cindex Network interfaces -The @samp{bpfilter} pseudo-device is the Berkeley Packet Filter, -developed by Lawrence Berkeley Labs and based on an earlier packet -filter from Stanford. See the @samp{bpf} manual page for more details. -The @var{number} given is the maximum number of simultaneous users -permitted. (NB: in previous version of BPF, the @var{number} had to be -greater than the number of interfaces; this space is now dynamically -allocated so this requirement is no longer present.) - -@item options CCITT -@findex CCITT -@cindex X.25 -@cindex Networking domains -The @samp{CCITT} option enables support for the ITU-T X.25(1980) -network-layer protocol. Nobody we know has a direct X.25 connection to -anything, so this code has never been tested. - -@item options "COMPAT_42" -@findex COMPAT_42 -@cindex UDP Checksums -@cindex Checksums, UDP -@cindex 4.2 Compatibility -@cindex Compatibility options -This option is used to disable UDP checksumming. Under ordinary -circumstances it should never ever ever be defined; however, if you are -stuck trying to communicate with an old 4.2BSD machine, or one running -something derived from 4.2 like SunOS 3.5 or Ultrix 2.0, this may be -necessary in order to successfully receive UDP packets. - -@item options "COMPAT_43" -@findex COMPAT_43 -@cindex 4.3 Compatibility -@cindex Compatibility options -This option controls a whole host of features, mostly relating to -system-call compatibilty with 4.3BSD. At the present time, it should -not be turned off, as many utilities and library routines still depend -on these obsolescent system calls being present. At some future date, -this will probably be split up into two separate options, one for binary -compatibility and one for the old but useful system calls. - -@item options "DIRECTED_BROADCAST" -@findex DIRECTED_BROADCAST -@cindex IP -@cindex UDP -If this option is enabled, the kernel will support sending IP broadcast -packets to subnets other than the one that the machine is on, and when -forwarding will accept such packets. That is to say, if your host lives -on subnets @samp{132.198.3} and @samp{132.198.4}, and the -@samp{132.198.3} side receives a packet addressed to -@samp{132.198.4.255}, it will forward the packet as a broadcast on that -subnet. - -@item pseudo-device ether -@findex ether -@cindex Ethernet -@cindex Network interfaces -This pseudo-device provides link-layer support for Ethernet device -drivers. It is mandatory for all systems which include Ethernet or -Ethernet-like devices, such as @samp{ed}, @samp{ie}, and @samp{is}. -This code is due for a redesign. - -@item options EON -@itemx pseudo-device eon -@findex EON -@cindex ISO 8473 CLNP -@cindex Network interfaces -The @samp{eon} network interface supports the ISO 8473 -Connectionless-Mode Network Protocol, tunnelled through IP version 4. -@samp{eon} interfaces are created automatically once initially -configured by adding ISO routes with IP destinations. At present, both -the pseudo-device and option declaration are necessary. - -@item options FIFO -@findex FIFO -@cindex Named pipes -This option enables support for System V-- and POSIX-style named pipes -or fifos. - -@item options GATEWAY -@itemx options IPFORWARDING=@var{value} -@itemx options IPSENDREDIRECTS=@var{value} -@findex GATEWAY -@findex IPFORWARDING -@findex IPSENDREDIRECTS -@cindex ICMP -@cindex IP -@cindex Network parameters -These three options control whether FreeBSD's IP forwarding functions -are enabled. Technically speaking, because FreeBSD does not meet the -standards set out in the ``Router Requirements'' document (RFC 1009), -these should not be enabled, but sometimes it is necessary to enable -this function. The @samp{GATEWAY} option turns on @samp{IPFORWARDING}, -and also controls the sizing of certain system tables. The -@samp{IPFORWARDING} option controls the initial value of the -@samp{ipforwarding} kernel variable (default 1 if @samp{GATEWAY} -defined, 0 otherwise), which controls whether packets are acutally -forwarded or not; @var{value} should be either @samp{0} or @samp{1}. -@samp{IPSENDREDIRECTS} controls the initial value of the -@samp{ipsendredirects} variable (default is one, but should be changed -to zero); its @var{value} should also be either @samp{0} or @samp{1}. - -@item options INET -@findex INET -@cindex IP -@cindex TCP -@cindex UDP -@cindex ICMP -@cindex Networking domains -This option controls the inclusion of the Internet protocol suite, -including IP version 4, TCP, UDP, and ICMP. Support for IP multicast, -IP next generation, and IGMP will be provided at a future date. It is -not recommended to even attempt to generate a system with this option -turned off, as many parts of the system depend on Internet networking in -important and subtle ways. - -@item options ISO -@itemx options TPIP -@findex ISO -@findex TPIP -@cindex ISO 8473 CLNP -@cindex ISO TP4 -@cindex ISO TP0 -@cindex ISO 9542 ESIS -@cindex IEEE 802.2 LLC -@cindex Networking domains -These options control the inclusion of ISO OSI networking protocols. -The TPIP option includes just enough support to run ISO Transport -Protocol class 4 over IP, supporing the @samp{SOCK_SEQPACKET} -abstraction. The ISO option includes support for CLNP, TP class 0, -ISO 9542 ESIS, and IEEE 802.2 logical link control class 0 (for CLNP only). - -@item options ISOFS -@findex ISOFS -@cindex ISO 9660 filesystem -@cindex CD-ROM -@cindex Rock Ridge filesystem -@cindex Filesystems -The @samp{ISOFS} option enables kernel support for the ISO 9660 CD-ROM -filesystem, including RockRidge extensions. - -@item options "ISO_X25ESIS" -@findex ISO_X25ESIS -@cindex ISO 9542 ESIS -@cindex X.25 -This option controls whether ISO 9542 ESIS is run over ITU-T X.25 link -layers. This requires the @samp{CCITT} option to be enabled as well. - -@item options KTRACE -@findex KTRACE -@cindex Kernel tracing -This option enables the tracing of several classes of internal kernel -events. See the @samp{ktrace} command for more details. - -@item pseudo-device log -@findex log -@cindex Kernel message logging -The @samp{log} pseudo-device provides kernel support to send kernel -messages to @samp{syslog}. It is mandatory. - -@item pseudo-device loop -@findex loop -@cindex Network interfaces -The @samp{loop} pseudo-device provides the trivial network interface. -It is required when any networking options are enabled. - -@item options MACHVMCOMPAT -@findex MACHVMCOMPAT -@cindex Mach virtual memory -This option enables a Mach-compatible interface to the virtual memory -subsystem, supporting system calls @samp{vm_allocate}, -@samp{vm_deallocate}, @samp{vm_inherit}, and @samp{vm_protect}. -(Given the nature of the VM system, it is impossible to support a -Mach-style @samp{vm_region} call, and in every case the `map' argument -is ignored and replaced with the calling process's own map.) - -@item options MFS -@findex MFS -@cindex Memory filesystem -@cindex Filesystems -This option enables support for the memory filesystem, an in-core -filesystem which lives in the swap area. Using MFS as a @file{/tmp} -filesystem can dramatically increase the speed of -temporary-space-intensive operations such as compilations. See the -@samp{mount_mfs} manual page for more details. - -@item options NFS -@findex NFS -@cindex Network File System -@cindex Filesystems -The @samp{NFS} option enables support for Sun's Network File System. -(Also called ``Nightmare'' or ``Not a''@dots{}.) This presently includes -both client-- and server-side kernelized NFS support; it may in the -future be broken into separate options. This NFS implmentation comes to -BSD courtesy of Rick Macklem of the University of Guelph, and is not -derived from Sun licensed source code. As a result, there are sometimes -interoperability problems where the published specification is vague, -and this option supports several new and useful features compared to -Sun's. See the @samp{mount} manual page for more details. - -@item options NS -@itemx options NSIP -@findex NS -@findex NSIP -@cindex Xerox Network System -@cindex XNS IDP -@cindex XNS SPP -@cindex Network interfaces -@samp{NS} controls the inclusion of support for the Xerox Network -Service protocol family. At the present time, it is not known whether -this code even works; testers are welcome. The @samp{NSIP} option -enables encapsulation of XNS IDP over IP. - -@item options PANICDELAY -@itemx options PANICWAIT -@findex PANICDELAY -@findex PANICWAIT -@cindex Kernel panics -These options control whether the system waits after a panic. This is -necessary on some systems which do not support crash dumps, so that the -actual panic message can be read. The @samp{PANICDELAY} option inserts -a delay of twenty seconds before self-destructing; the @samp{PANICWAIT} -option instead waits for a key to be pressed on the console. - -@item options PCFS -@findex PCFS -@cindex MS-DOS filesystem -@cindex Filesystems -This option controls support for mounting MS-DOS disks and disk -partitions under FreeBSD. The @samp{pcfs} manual page is presently very -bogus. - -@item pseudo-device ppp @var{number} -@findex ppp -@cindex Point-To-Point Protocol -@cindex Network interfaces -The @samp{ppp} pseudo-device provides support for the Internet -Point-to-Point protocol (RFC 1351 @i{et seq}), implemented as a line -discipline over standard serial links. @var{number} should be the -number of simultaneous PPP interfaces which will be configured. - -@item pseudo-device pty @var{number} -@findex pty -@cindex Pseudo-terminals -This pseudo-device provides support for pseudo-ttys, which are required -for @samp{rlogin}, @samp{telnet}, and @samp{xterm} to operate correctly; -@var{number} should be set to the total number of these programs you -expect to have running at any given time. Because pty's are not as yet -dynamically allocated, and the underlying structures are large, it is -best to keep this value as small as feasible, until this deficiency is -remedied. - -@item options QUOTA -@findex QUOTA -@cindex Disk quotas -@cindex Filesystems -The @samp{QUOTA} option enables support for disk quotas. Note that NFS -quota support is not available. - -@item pseudo-device sl @var{number} -@findex sl -@cindex Serial Line Internet Protocol -@cindex SLIP -@cindex CSLIP -@cindex IP -@cindex Network interfaces -This pseudo-device provides support for the Serial Line Internet -Protocol (RFC 1055), implemented as a line discipline over standard -serial links. It includes support for Van Jacobson header compression. -@var{number} should be the number of simultaneous SLIP interfaces which -will be configured. See also the @samp{slattach} manual page. - -@item options SYSVSHM -@item options SYSVSEM -@item options SYSVMSG -@itemx options SHMMAXPGS=@var{value} -@findex SYSVSHM -@findex SYSVSEM -@findex SYSVMSG -@findex SHMMAXPGS -@cindex System V shared memory -@cindex System V semaphores -@cindex System V message queues -@cindex System V IPC -@cindex Shared memory, System V -@cindex Semaphores, System V -@cindex Message queues, System V -@cindex IPC, System V -The @samp{SYSVSHM} option enables kernel-side emulation of System -V-compatible shared memory. The @samp{SHMMAXPGS} option (default 64 -pages or 256K) determines the maximum amount of shared memory available -under this mechanism. The @samp{SYSVSEM} option provides emulation of -System V-compatible semaphores, and likewise @samp{SYSVMSG} for message -queues. - -@item options RMP -@findex RMP -@cindex Remote maintenance protocol -@cindex Networking domains -This option should control the inclusion of support for HP's remote -maintenance protocol, but the source code is not included in FreeBSD at -present, so enabling it will not result in any good. - -@item pseudo-device tb -@findex tb -@cindex Tablet line discipline -The @samp{tb} pseudo-device provides support for the `tablet' line -discipline. Nobody on the FreeBSD team actually has one of the tablets -in question, so we have no idea if this actually works or not. It may -not even compile. - -@item options "TCP_COMPAT_42" -@findex TCP_COMPAT_42 -@cindex 4.2 Compatibility -@cindex Compatibility options -@cindex TCP -This option controls the perpetuation of several bugs inherited from the -4.2BSD implementation of TCP. It should only be defined in the -circumstances outlined for @samp{COMPAT_42}, above. - -@item pseudo-device tun -@findex tun -@cindex Network interfaces -The @samp{tun} driver provides a network interface which is attached to -a character device. In this way, a user-mode program can grab packets -out of the networking system, fiddle with them or move them around, and -pass stuff packets back up into the kernel. It is not known if this -device either compiles or operates correctly, although it was believed -to do both at some time in the past. - -@item options UCONSOLE -@findex UCONSOLE -@cindex Console redirection -This option allows any old user to grab kernel output away from the -console and send it to the tty of their choice. It presents an -incredile security hole for some systems, but is necessary in order to -allow programs like @samp{xconsole} to operate. (The alternative, -making @samp{xconsole} set-uid root, opens the exact same security -hole.) - -@item options XE -@findex XE -@cindex X.25 -@cindex IEEE 802.2 LLC -@cindex Network interfaces -@cindex Ethernet -This option should control the inclusion of support for running X.25 -over IEEE 802.2 LLC class 2, but that code was not included in the -Networking/2 release, so enabling it will disable kernel compilation. -Requires @samp{CCITT}. - -@item options XSERVER -@findex XSERVER -@cindex X Window System -This obsolescent option enables support in the @samp{pc} console -driver for certain operations required by the XFree86 server. -@end table - -@node Performance, Devices, Subsystems, Top -@c node,next,prev,up - -@chapter Performace and Debugging Options - -The following options are provided for system performace optimization. -Note that kernel profiling is supported via the @samp{-p} option to the -@samp{config} command; for more information see the @samp{config} manual -page. - -@table @code -@item psuedo-device ddb -@findex ddb -@cindex Kernel debugger -@cindex Debugger, kernel -This option enables the @samp{ddb} debugger, taken from Mach. See the -@samp{ddb} and @samp{dbsym} manual pages for more information on the use -of this debugger. - -@item options DIAGNOSTIC -@itemx options NAMEI_DIAGNOSTIC -@itemx options PARANOID -@findex DIAGNOSTIC -@findex NAMEI_DIAGNOSTIC -@findex PARANOID -@cindex Debugging options -These debugging options reduce performace. They are intended to enable -certain internal consistency checks which are not supposed to fail -during correct operation, and so are normally disabled for performace -reasons. - -@item options FASTLINKS -@findex FASTLINKS -@cindex Symbolic links -@cindex Filesystems -The @samp{FASTLINKS} option enables the creation of symbolic links whose -target names reside entirely within the i-node of the link, when -possible. This results in faster access for those links which are short -enough (in practice, most of them). All kernels can read such links, -but only @samp{FASTLINKS} kernels will create them, for compatibility -with older kernels lacking such support. - -@item options ICMPPRINTFS -@findex ICMPPRINTFS -@cindex Debugging options -@cindex ICMP -This option is defined to allow debugging of ICMP (@dfn{Internet Control -Message Protocol}) packets in the kernel. When defined and the -@samp{icmpprintfs} kernel variable (default false) is set to true, ICMP -packets will be printed out to the console when received. Note that it -is probably better to use @samp{tcpdump} for this kind of debugging. - -@item options KGDB -@findex KGDB -@cindex Kernel debugger -@cindex Debugger, kernel -@cindex Remote debugging -The @samp{KGDB} option enables certain bits of kernel code which will -eventually be able to talk to a remote copy of the @samp{gdb} debugger -over a serial connection. The present code does not work, but users are -invited to hack on it and contribute the changes back to the FreeBSD -team. - -@item options MCLSHIFT=@var{value} -@findex MCLSHIFT -@cindex Network parameters -This option controls the number of bytes in an mbuf cluster, which is -one of the basic units through which network data is managed. It is -equal to the log base two of @samp{MCLBYTES}, the size of an mbuf -cluster, and defaults to eleven (for an @samp{MCLBYTES} of 2048). It -will likely eventually be bumped up to twelve (4096), so that the -network code can take advantage of page flipping to reduce the numer of -copies necessary. - -@item options SUBNETSARELOCAL -@findex SUBNETSARELOCAL -@cindex TCP -@cindex Network parameters -This option controls whether the TCP system believes that machines on -other subnets of your network are considered to be ``local'' to your -host. For most systems, this option should be on (the default); if you -are directly connected to a class A network, however, then it may need -to be turned off. (This is true of networks like the MILNET.) - -@item options "SYMTAB_SPACE=@var{value}" -@findex SYMTAB_SPACE -@cindex Debugger, kernel -@cindex Kernel debugger -This obsolescent option controls the amount of space that will be -statically allocated in the debugger source code to hold the kernel -symbol table that @samp{dbsym} sticks there. Eventually this will be -dynamically allocated at load time. The default @var{value} is 63000 -bytes. -@end table - -@node Devices, Internals, Performance, Top - -@chapter Device Options - -There are different device selections available depending on the type of -bus present in your computer. We will cover generic FreeBSD devices, -ISA-bus devices, and EISA-bus devices. A separate section describes the -devices available in the SCSI subsystem. - -@menu -* Generic:: Devices available in all FreeBSD systems. -* ISA:: Devices specific to the ISA bus. -* EISA:: Devices specific to the EISA bus. -* MCA:: No support for Micro Channel, yet. -* PCI:: No support for PCI, yet. -* SCSI:: The SCSI subsystem. -@end menu - -@node Generic, ISA,, Devices -@section Generic Devices and Options - -The following devices and options are available in all FreeBSD -configurations. In addition to these devices, a selection of ISA -devices (@pxref{ISA}) is required in order to generate a workable -system. - -@table @code -@item machine "i386" -@findex i386 -This mandatory declaration informs the @samp{config} program that you -are using an i386 or compatible CPU, and enables the selection of all -the other devices listed here. - -@item cpu "I386_CPU" -@itemx cpu "I486_CPU" -@findex I386_CPU -@findex I486_CPU -These two options control which specific CPUs will be supported by the -generated kernel. If the kernel detects that it is not running on a CPU -for which support was enabled, it will panic quickly upon startup. If -you do not expect to need to run your kernel on an i386 or similar CPU, -leaving out that support can increase virtual memory system performance. - -@item options "MATH_EMULATE" -@findex MATH_EMULATE -@cindex Floating-point emulator -@cindex i387 -When this option is defined, the math coprocessor emulator is compiled -into the kernel. When it is not defined and the coprocessor is absent, -programs which use floating-point operations are automatically killed. - -@item device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -@findex npx -@cindex i386 -The @samp{npx} device provides support for the i387 numeric coprocessor -and the floating-point portions of the i486 CPU. This will eventually -be fixed to not require ISA to be configured. - -@item pseudo-device speaker -@findex speaker -The @samp{speaker} pseudo-device provides support for rudimentary access -to the PC's speaker via @file{/dev/spkr}. It provides a -character-device interface which interprets @samp{PLAY} strings similar -to IBM PC Advanced BASIC, as well as an @samp{ioctl} interface with more -fine-grained control. See the @samp{spkr} manual page for more -information. -@end table - -@node ISA, EISA, Generic, Devices -@section ISA-bus Devices and Options - -The following options are specific to ISA-bus devices and systems. -Since the EISA bus is backwards-compatible with the ISA bus, all these -options also apply to EISA systems. The same goes for VESA Local Bus -(VL-Bus) systems. - -@table @code -@item controller isa0 -@findex isa -This @strong{mandatory} declaration must precede any other devices -listed in this section. It provides the basic support for the ISA-bus -glue logic, including DMA and autoconfiguration. - -@item controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr -@findex aha -@cindex Adaptec 154x -@cindex SCSI host adaptors -The @samp{aha} device supports the Adaptec 154x series of SCSI -controllers, and attempts to support other vendors' controllers which -claim compatibility with the Adaptec 1542, such as the BusLogic 545. -This device is included in the @samp{GENERICAH} distribution kernel. -The @samp{scbus} device (@pxref{SCSI}) is a prerequisite for this -device. - -@item controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr -@findex bt -@cindex Bustek 742 -@cindex SCSI host adaptors -This device supports the Bustek 742 SCSI controller. It is included in -the @samp{GENERICBT} distribution kernel; the @samp{scbus} device -(@pxref{SCSI}) is a prerequisite. - -@item options COM_BIDIR -@findex COM_BIDIR -@cindex Bi-directional serial ports -@cindex Serial ports -This option enables bi-directional support in the @samp{sio} serial -driver. This option is slated for removal, at which time bi-directional -support will always be enabled. See the @samp{comcontrol} manual page -for more information. - -@item options COM_MULTIPORT -@findex COM_MULTIPORT -@cindex Multi-port serial boards -@cindex Serial ports -This option enables support in the @samp{sio} serial driver for certain -multi-port serial boards. - -@item device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr -@itemx device ed1 at isa? port 0x300 net irq 5 iomem 0xd8000 vector edintr -@findex ed -@cindex Western Digital 80x3 -@cindex 3Com 3C503 -@cindex Novel NE1000/NE2000 -@cindex Network interfaces -@cindex Ethernet -The @samp{ed} network interface driver provides support for the Western -Digital/SMC 80x3 series, the 3Com 3c503, and Novell NE1000 and NE2000 -series of Ethernet controllers. It automatically detects differences -among the various versions of these controllers and adapts -appropriately. The @samp{ed1} line shown is for the Novell boards; the -@samp{ed0} line is appropriate for all other supported controllers. -(The Novell controllers cannot be configured to use port 0x280.) - -@item controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -@itemx disk fd0 at fdc0 drive 0 -@itemx disk fd1 at fdc0 drive 1 -@itemx tape ft0 at fdc0 drive 2 -@findex fd -@findex ft -@cindex Floppy disk -@cindex Floppy tape -@cindex QIC-80 -@cindex Tape drives -@cindex Cartridge tape drives -@cindex Quarter-Inch-Cartridge (QIC) tape drives -@cindex Disk drives -The @samp{fdc} driver provides support for the standard PC floppy-disk -controller. The @samp{fd} sub-driver supports 3.5-- and 5.25-inch -floppy disks in the standard 360KB, 720KB, 1200KB, 1440KB, and 2880KB -formats, as well as a number of other formats not supported by DOS. The -@samp{ft} driver is available for QIC-80 ``floppy tape'' support. -The drivers support formatting of both tapes and disks. This driver is -substantially improved from that shipped in previous releases of -FreeBSD. - -@item device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr -@findex ie -@cindex AT&T EN100 -@cindex AT&T StarLAN 10 -@cindex Network interfaces -@cindex Ethernet -@cindex StarLAN -This network interface driver provides support for the AT&T StarLAN 10 -and EN100 family of controllers. Note that the configuration specified -here is not the default configuration, but one which attempts to deal -with the conflicts that arise in more modern systems. (It is expected -that this driver will be expanded in the future to support other similar -cards in the manner of @samp{ed}.) - -@item device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr -@findex is -@cindex Isolan 4141-0 -@cindex Isolink 4110 -@cindex Ethernet -@cindex Network interfaces -The @samp{is} network interface driver supports the Isolan 4141-0 and -Isolink 4110 Ethernet controllers. - -@c @item device ix0 at isa? port 0x320 net irq 10 iomem 0xd0000 iosiz 32768 \ -@c @itemx vector ixintr -@c @findex ix -@c @cindex Intel EtherEXPRESS -@c @cindex Ethernet -@c @cindex Network interfaces -@c This device is known to exist, but is not presently in the FreeBSD -@c source tree. When it is made available, this information will be -@c updated. - -@item device lpa0 at isa? port "IO_LPT1" tty -@itemx device lpt0 at isa? port "IO_LPT1" tty irq 7 vector lptintr -@findex lpa -@findex lpt -@cindex Parallel printers -The @samp{lpa} device provides support for the parallel printer driver -accessed as @file{/dev/lp}. The @samp{lpt} driver provides the same -functionality, but only works with those printer controllers which -support interrupt-driven operations. If you receive @samp{ISA strayintr -7} messages correlated with the use of the @samp{lpa} driver, chances -are that your controller supports interrupt-driven operation, and you -should switch to the @samp{lpt} driver. - -The @samp{lpa} driver is obsolete, and will be removed in release 1.2, -to be replaced by special flags to the @samp{lpt} driver. - -@item device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr -@findex mcd -@cindex Mitsumi CD-ROM -@cindex CD-ROM -This device provides support for the Mitsumi non-SCSI CD-ROM drive. -Performance is known to be quite slow. - -@c mse, anyone? - -@item device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -@itemx device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr -@itemx options NCONS=@var{value} -@itemx options COMCONSOLE -@findex pc -@findex sc -@findex NCONS -@findex COMCONSOLE -@cindex Console devices -@cindex pccons -@cindex Syscons -@cindex Virtual consoles -@cindex X Window System -The @samp{pc} and @samp{sc} devices provide support for the system -display and keyboard, which is the default console. There might -actually be documentation somewhere for both of these. The @samp{sc} -device requires the @samp{NCONS} option to be defined to some value; it -represents the number of virtual consoles to be provided by the driver; -a reasonable value is 8. One of @samp{pc} or @samp{sc} is presently -required unless @samp{COMSONSOLE} is enabled, in which case a serial -port is made into the console. - -@c sb, anyone? - -@item device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr -@itemx device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr -@itemx device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr -@itemx device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr -@findex sio -@cindex Serial ports -@cindex National 8250/16450/16550 -@cindex Bi-directional serial ports -@cindex Multi-port serial boards -The @samp{sio} driver provides support for high-speed serial -communications using the standard 8250, 16450, and 16550 UART chips. It -provides a standard tty interface for these devices as -@file{/dev/tty@var{unit}}, and, when enabled with the @samp{comcontrol} -program, a call-out capability as @file{/dev/cua@var{unit}} (@var{unit} -is two digits, zero-padded in both cases). Certain multi-port systems -are also supported. - -@item device uha0 at isa? port "IO_UHA0" bio irq 14 drq 5 vector uhaintr -@findex uha -@cindex Ultrastor 14F -@cindex Ultrastor 34F -@cindex SCSI host adaptors -This device supports the Ultrastor 14F and related SCSI controllers. It -is included in the @samp{GENERICBT} distribution kernel, and requires -@samp{scbus} (@pxref{SCSI}) as a prerequisite. The Ultrastor 24F is not -supported. - -@item controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr -@itemx disk wd0 at wdc0 drive 0 -@itemx disk wd1 at wdc0 drive 1 -@findex wd -@cindex Western Digital WD100x -@cindex ST-506 hard disks -@cindex RLL hard disks -@cindex ESDI hard disks -@cindex IDE hard disks -@cindex Disk drives -The @samp{wd} device supports standard ST-506, RLL, ESDI, and IDE hard -disks, as controlled by the Western Digital WD100x series of controllers -(and compatible hardware). This version is substantially improved from -that provided in FreeBSD 1.0. - -@item device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr -@findex wt -@cindex Archive QIC-02 -@cindex Wangtek QIC-02 -@cindex Cartridge tape drives -@cindex QIC-02 -@cindex QIC-36 -@cindex Tape drives -@cindex Quarter-Inch-Cartridge (QIC) tape drives -This driver supports Archive QIC-02 and Wangtek QIC-02 and QIC-36 -cartridge tape controllers. -@end table - -@node EISA, MCA, ISA, Devices -@section EISA-bus Devices and Options - -There is presently only one EISA-specific device driver. - -@table @code -@item controller ahb0 at isa? bio irq 11 vector ahbintr -@findex ahb -@cindex Adaptec 174x -@cindex SCSI host adaptors -The @samp{ahb} driver provides support for the Adaptec AHA-174x series -of SCSI controllers. This controller is included in the -@samp{GENERICAH} distribution kernel, and requires the @samp{scbus} -driver (@pxref{SCSI}) as a prerequisite. -@end table - -@node MCA, PCI, EISA, Devices -@section Micro Channel Devices and Options - -@cindex Micro Channel Architecture -We don't support Micro Channel right now. Anyone interested in working -on Micro Channel support should send mail to -@samp{FreeBSD-Questions@@freefall.cdrom.com} for information on how to -help. - -@node PCI, SCSI, MCA, Devices -@section PCI Devices and Options - -@cindex PCI -We don't support PCI, either. Anyone interested in working on PCI -support should send mail to @samp{FreeBSD-Questions@@freefall.cdrom.com} -for information on how to help. - -@node SCSI,, PCI, Devices -@section The SCSI Subsystem - -The SCSI subsystem consists of a set of adaptor-specific driver -routines, which were described in the previous sections, and the generic -SCSI device drivers, which handle the standardized interactions with -devices on the SCSI bus. - -@c devices: cd, ch, scbus, sd, sg, st - -@table @code -@item device cd0 -@findex cd -@cindex CD-ROM -@cindex SCSI devices -The @samp{cd} device provides support for CD-ROM drives. Only one -@samp{cd} device need be configured, as the driver automatically -allocates units for each CD-ROM drive found. Playing of audio CDs is -also supported, on drives which support it, through @code{ioctl} calls. -Support for retrieval of CD audio over the SCSI bus is not presently -available. - -@item device ch0 -@findex ch -@cindex Media changers -@cindex SCSI devices -The @samp{ch} driver supports SCSI media changers; this may include -tape, removable disk, and CD changers. One @samp{ch} device should be -configured for each changer you expect to support. - -@item device scbus0 -@findex scbus -@cindex SCSI bus management -This driver forms the core of the SCSI subsystem. It provides the -device-independent routines that manage SCSI transactions, keep track of -attached devices, and act as glue between SCSI-device-specific drivers -and system-specific host adaptors. This device is @emph{mandatory} for -all SCSI systems. - -@item device sd0 -@findex sd -@cindex SCSI devices -@cindex Disk drives -The @samp{sd} driver provides access to non-removable SCSI disks. One -@samp{sd} device should be defined for each disk you expect to have -simultaneously connected to the system. - -@c @item device sg0 -@c @findex sg -@c @cindex SCSI devices -@c @cindex Generic SCSI -@c @cindex Unsupported SCSI devices -@c This driver provides support for development of user-mode drivers and -@c other programs which access the SCSI bus directly. One @samp{sg} device -@c should be defined for each @emph{host adaptor} you have installed in -@c your system. - -@item device st0 -@findex st -@cindex SCSI devices -@cindex Tape drives -@cindex Quarter-Inch-Cartridge (QIC) tape drives -The @samp{st} driver supports generic SCSI tape drives. One @samp{st} -device should be defined for each tape drive you wish to access. See -the @samp{st} manual page for information about how to manipulate the -parameters of this device. - -@item device uk0 -@findex uk -@cindex SCSI devices -@cindex Unknown SCSI devices -The @samp{uk} driver provides an attachment point for all otherwise -unrecognized SCSI devices. You can't actually do anything with such a -device, except perhaps send it an inquiry command using the @samp{scsi} -program (q.v.). -@end table - -@node Internals, Index, Devices, Top - -@chapter Internal Use Only - -Eventually, this chapter will document some of the kernel manifest -constants which are not defines, but which can be tweaked in various -header files. - -@node Index,, Internals, Top -@appendix General Index - -Items in @code{typewriter} font are option or device names. - -@printindex cp - -@bye diff --git a/sys/i386/doc/sound.doc b/sys/i386/doc/sound.doc deleted file mode 100644 index 7f5b9da64e6d..000000000000 --- a/sys/i386/doc/sound.doc +++ /dev/null @@ -1,38 +0,0 @@ -To enable sound card support, you need to add one or more of the following -lines to your kernel configuration file: - -device snd5 at isa? port 0x330 irq 6 drq 0 vector mpuintr -device snd4 at isa? port 0x220 irq 15 drq 6 vector gusintr -device snd3 at isa? port 0x388 irq 12 drq 3 vector pasintr -device snd2 at isa? port 0x220 irq 7 drq 1 vector sbintr -device snd1 at isa? port 0x388 irq 0 drq 0 vector sbintr - - Unit numbers are: - 1 for Yamaha FM synth - 2 for SB/SB Pro DSP - 3 for PAS PCM and Midi - 4 for GUS - 5 for MPU-401 - - If you have ProAudioSpectrum, uncomment units 3 and 2. - If you have SoundBlaster, uncomment 2 and 1. - If you have GravisUltrasound, uncomment 4 - If you have MPU-401, uncomment 5 - -NOTE: The MPU-401 driver may or may not work, and is unfortunately -unverifiable since no one I know has one. If you can test this, -please let me know! Also note that you will have to change these -settings if your soundcard is set for a non-standard address or IRQ. -Please check your documentation (or verify with any provided DOS utilities -that may have come with your card) and set the IRQ or address fields -accordingly. - - -Also: Some systems with the OPTI chipset will require you to #define -BROKEN_BUS_CLOCK in /sys/i386/sound/pas2_card.c. Symptoms are that -you will hear a lot of clicking and popping sounds, like a geiger counter, -coming out of the PAS even when is not playing anything. - - - - - Jordan Hubbard (jkh@freefall.cdrom.com) diff --git a/sys/i386/doc/vm_layout.doc b/sys/i386/doc/vm_layout.doc deleted file mode 100644 index 3734fd8ab088..000000000000 --- a/sys/i386/doc/vm_layout.doc +++ /dev/null @@ -1,32 +0,0 @@ -Physical Memory Layout: - -NOT YET DONE - - - -Virtual Memory Layout: - -Page Table Directories, and how they relate to the vm address space -Note: PTDI stands for Page Table Directory Index. - -PTDI Address pmap.h/param.h Calculation to locate it in vm space --------------------------------------------------------------------------------- - FFFFF000 APTD APTmap + (APTDPTDI * NBPG) - FFC00000 APTmap APTDPTDI << PDRSHIFT -3FF FFC00000 APTDPTDI #define (NPTEPG-1) - FFBFFFFF KERNEND ((KPTDI+NKPDE) << PDRSHIFT) - 1 -3FD FF400000 . -3FC FF000000 . -3FB FEC00000 . -3FA FE800000 . -3F9 FE400000 . - FE000000 KERNBASE KPTDI << PDRSHIFT -3F8 FE000000 KPTDI #define (APTDPTDI-NKPDE) - FDFF8000 Sysmap PTmap + (KPTDI * NBPG) - FDFF7FF8 APTpde PTD + (APTDPTDI * sizeof(pde)) - FDFF7FDC PTDpde PTD + (PTDPTDI * sizeof(pde)) - FDFF7000 PTD PTmap + (PTDPTDI * NBPG) - FDC00000 PTmap PTDPTDI << PDRSHIFT -3F7 FDC00000 PTDPTDI #define (KPTDI-1) - -$Id: vm_layout.doc,v 1.6 1993/10/16 19:25:07 rgrimes Exp $ diff --git a/sys/i386/doc/wt.doc b/sys/i386/doc/wt.doc deleted file mode 100644 index 1e9f1bafec8a..000000000000 --- a/sys/i386/doc/wt.doc +++ /dev/null @@ -1,77 +0,0 @@ -This is the streamer tape driver for 386bsd and FreeBSD, which -supports Wangtek and Archive compatible QIC-02/QIC-36 controllers. -It was developed as a replacement of the old Wangtek tape driver from CMU. - -In comparison with the CMU driver, this version has the following enhancements: -1) Support for Archive SC402 and SC499 tape controllers added. -2) Support for up to three tape controllers on the same machine. -3) Support for BSD-style ioctls MTIOCGET, MTIOCTOP. - Mt command now works adequately with this driver. -2) Asynchronous REWIND and FSF operations, close() will not wait - until they finish. The next open() will wait for it instead. -4) Use of WTQICMD ioctl is limited to ERASE and RETENS operations. - This prevents the user from locking the tape driver by strange - tape operations. -5) Tape density switching added. -6) The status of the process, blocked on the tape operation, - is displayed at the WCHAN column of the `ps' command as: - - wtread reading data from the tape - wtwrite writing data to the tape - wtrfm reading the tape marker - wtwfm writing the tape marker - wtrew rewinding the tape - wterase doing WTQICMD ERASE operation - wtretens doing WTQICMD RETENS operation - wtorew doing MTIOCTOP REW/OFFL operation - wtorfm doing MTIOCTOP FSF operation - wtowfm doing MTIOCTOP WEOF operation - -7) It's possible to use the tape with "default density", - useful for devices which don't support density switching - or do automatic format determination. -8) Some controllers support only 1024 block length. - Setting WT_BSIZE bit in device minor number turns on this mode. - -Minor number structure: - 0bfffuuu -Fields: - uuu - Unit number. It's possible to install - up to three tape controllers on the same machine, - using DRQs 1..3. Hence, unit number can lie - in range 0..2. - fff - Tape format number: - 0 - /dev/rwt0 - default density (auto select) - 1 - /dev/rwt0a - QIC 11 (obsolete) - 2 - /dev/rwt0b - QIC 24 (60 megabytes) - 3 - /dev/rwt0c - QIC 120 (120 megabytes) - 4 - /dev/rwt0d - QIC 150 (150 megabytes) - 5 - /dev/rwt0e - QIC 300 (300 megabytes?) - 6 - /dev/rwt0f - QIC 600 (600 megabytes?) - b - Long block size flag. With this bit set, - the driver will perform all i/o operations - with the controller using 1024-byte - blocks, instead of 512 ones. - Some controllers need it (CMS for example). - If you Wangtek controller does not stream well, - you can try to use /dev/rWt0 device instead - of /dev/rwt0 (uncomment needed lines in /dev/MAKEDEV - to create it). - -Block interface (writing blocks less than 2048 bytes) is not functioning -pwoperly. Use raw interface instead. - -Thanks to all who helped to test it on the following hardware: - -Controller Drive Volume Interface Thanks to ---------------------------------------------------------------------------- -Archive SC-499 Archive 2150L 150 Meg QIC-02 KIAE -CMS? ? 150 Meg QIC-02 KIAE -Everex EV 831/833 ? ? QIC-36 Joergen Haegg -Wangtek ASSY Wangtek 60 Meg QIC-02 Ken Whedbee -Tecmar QT150i? Wangtek 5150EQ ? QIC-02? Marko Teiste -? Wangtek 5099EK 60 Meg QIC-36 Robert Shien -Archive SC400S ? 60 Meg ? Warren Toomey - -Sergey Ryzhkov, Serge Vakulenko -E-mail: <sir@kiae.su>, <vak@zebub.msk.su> diff --git a/sys/i386/i386/autoconf.c b/sys/i386/i386/autoconf.c index 7584c02b353e..5b571a04432c 100644 --- a/sys/i386/i386/autoconf.c +++ b/sys/i386/i386/autoconf.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)autoconf.c 7.1 (Berkeley) 5/9/91 - * $Id: autoconf.c,v 1.8.2.1 1994/03/23 23:45:08 rgrimes Exp $ + * $Id: autoconf.c,v 1.11 1994/03/21 15:02:47 ache Exp $ */ /* @@ -79,6 +79,11 @@ configure() isa_configure(); #endif +#include "pci.h" +#if NPCI > 0 + pci_configure(); +#endif + #if GENERICxxx && !defined(DISKLESS) if ((boothowto & RB_ASKNAME) == 0) setroot(); diff --git a/sys/i386/i386/conf.c b/sys/i386/i386/conf.c index b1ed3063a3b1..8e0bf2673c3a 100644 --- a/sys/i386/i386/conf.c +++ b/sys/i386/i386/conf.c @@ -41,7 +41,7 @@ * SUCH DAMAGE. * * from: @(#)conf.c 5.8 (Berkeley) 5/12/91 - * $Id: conf.c,v 1.20.2.1 1994/05/04 07:45:12 rgrimes Exp $ + * $Id: conf.c,v 1.28 1994/05/30 03:35:53 ache Exp $ */ #include "param.h" @@ -213,6 +213,7 @@ struct bdevsw bdevsw[] = cddump, cdsize, 0 }, { mcdopen, mcdclose, mcdstrategy, mcdioctl, /*7*/ mcddump, mcdsize, 0 }, + { 0, } /* block major 8 is reserved for local use */ /* * If you need a bdev major number, please contact the FreeBSD team * by sending mail to "FreeBSD-hackers@freefall.cdrom.com". @@ -236,7 +237,7 @@ d_close_t pcclose; d_rdwr_t pcread, pcwrite; d_ioctl_t pcioctl; d_mmap_t pcmmap; -extern struct tty pccons; +extern struct tty *pccons; /* controlling TTY */ d_open_t cttyopen; @@ -262,7 +263,7 @@ d_close_t ptcclose; d_rdwr_t ptcread, ptcwrite; d_select_t ptcselect; d_ioctl_t ptyioctl; -extern struct tty pt_tty[]; +extern struct tty *pt_tty[]; #else #define ptsopen (d_open_t *)enxio #define ptsclose (d_close_t *)enxio @@ -285,9 +286,8 @@ d_close_t comclose; d_rdwr_t comread; d_rdwr_t comwrite; d_ioctl_t comioctl; -d_select_t comselect; #define comreset (d_reset_t *)enxio -extern struct tty com_tty[]; +extern struct tty *com_tty[]; #else #define comopen (d_open_t *)enxio #define comclose (d_close_t *)enxio @@ -295,7 +295,6 @@ extern struct tty com_tty[]; #define comwrite (d_rdwr_t *)enxio #define comioctl (d_ioctl_t *)enxio #define comreset (d_reset_t *)enxio -#define comselect (d_select_t *)enxio #define com_tty NULL #endif @@ -401,33 +400,15 @@ d_ioctl_t bpfioctl; #define bpfioctl (d_ioctl_t *)enxio #endif -#include "dcfclk.h" -#if NDCFCLK > 0 -d_open_t dcfclkopen; -d_close_t dcfclkclose; -d_rdwr_t dcfclkread; -d_ioctl_t dcfclkioctl; -d_select_t dcfclkselect; -#else -#define dcfclkopen (d_open_t *)enxio -#define dcfclkclose (d_close_t *)enxio -#define dcfclkread (d_rdwr_t *)enxio -#define dcfclkioctl (d_ioctl_t *)enxio -#define dcfclkselect (d_select_t *)enxio -#endif - -#include "lpa.h" -#if NLPA > 0 -d_open_t lpaopen; -d_close_t lpaclose; -d_rdwr_t lpawrite; -d_ioctl_t lpaioctl; -#else +/* + * lpa has been removed from the tree, + * but the major dev no for it still remains. + * (lpt now provides all the functionality which lpa used to.) + */ #define lpaopen (d_open_t *)enxio #define lpaclose (d_close_t *)enxio #define lpawrite (d_rdwr_t *)enxio #define lpaioctl (d_ioctl_t *)enxio -#endif #include "speaker.h" #if NSPEAKER > 0 @@ -442,6 +423,21 @@ d_ioctl_t spkrioctl; #define spkrioctl (d_ioctl_t *)enxio #endif +#include "pca.h" +#if NPCA > 0 +d_open_t pcaopen; +d_close_t pcaclose; +d_rdwr_t pcawrite; +d_ioctl_t pcaioctl; +d_select_t pcaselect; +#else +#define pcaopen (d_open_t *)enxio +#define pcaclose (d_close_t *)enxio +#define pcawrite (d_rdwr_t *)enxio +#define pcaioctl (d_ioctl_t *)enxio +#define pcaselect (d_select_t *)enxio +#endif + #include "mse.h" #if NMSE > 0 d_open_t mseopen; @@ -464,7 +460,7 @@ d_ioctl_t sioioctl; d_select_t sioselect; d_stop_t siostop; #define sioreset (d_reset_t *)enxio -extern struct tty sio_tty[]; +extern struct tty *sio_tty[]; #else #define sioopen (d_open_t *)enxio #define sioclose (d_close_t *)enxio @@ -544,7 +540,7 @@ struct cdevsw cdevsw[] = logselect, nommap, NULL }, { comopen, comclose, comread, comwrite, /*8*/ comioctl, nostop, comreset, com_tty, /* com */ - comselect, nommap, NULL }, + ttselect, nommap, NULL }, { Fdopen, fdclose, rawread, rawwrite, /*9*/ fdioctl, nostop, nullreset, NULL, /* Fd (!=fd) */ seltrue, nommap, fdstrategy }, @@ -590,9 +586,9 @@ struct cdevsw cdevsw[] = { bpfopen, bpfclose, bpfread, bpfwrite, /*23*/ bpfioctl, nostop, nullreset, NULL, /* bpf */ bpfselect, nommap, NULL }, - { dcfclkopen, dcfclkclose, dcfclkread, nowrite, /*24*/ - dcfclkioctl, nostop, nullreset, NULL, /* dcfclk */ - dcfclkselect, nommap, NULL }, + { pcaopen, pcaclose, noread, pcawrite, /*24*/ + pcaioctl, nostop, nullreset, NULL, /* pcaudio */ + pcaselect, nommap, NULL }, { lpaopen, lpaclose, noread, lpawrite, /*25*/ lpaioctl, nullstop, nullreset, NULL, /* lpa */ seltrue, nommap, NULL }, @@ -614,6 +610,7 @@ struct cdevsw cdevsw[] = { ukopen, ukclose, noread, nowrite, /*31*/ ukioctl, nostop, nullreset, NULL, /* unknown */ seltrue, nommap, NULL }, /* scsi */ + { 0, } /* character device 32 is reserved for local use */ /* * If you need a cdev major number, please contact the FreeBSD team * by sending mail to `freebsd-hackers@freefall.cdrom.com'. diff --git a/sys/i386/i386/db_interface.c b/sys/i386/i386/db_interface.c index 7e68844e4ade..aeeedbecdac1 100644 --- a/sys/i386/i386/db_interface.c +++ b/sys/i386/i386/db_interface.c @@ -23,7 +23,7 @@ * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. * - * $Id: db_interface.c,v 1.5 1993/12/19 00:50:00 wollman Exp $ + * $Id: db_interface.c,v 1.7 1994/06/22 05:52:28 jkh Exp $ */ /* @@ -178,8 +178,6 @@ db_read_bytes(addr, size, data) db_nofault = 0; } -struct pte *pmap_pte(pmap_t, vm_offset_t); - /* * Write bytes to kernel address space for debugger. */ @@ -234,9 +232,24 @@ db_write_bytes(addr, size, data) } } +/* + * XXX move this to machdep.c and allow it to be called iff any debugger is + * installed. + * XXX msg is not printed. + */ void Debugger (msg) const char *msg; { - asm ("int $3"); + static volatile u_char in_Debugger; + + if (!in_Debugger) { + in_Debugger = 1; +#ifdef __GNUC__ + asm("int $3"); +#else + int3(); +#endif + in_Debugger = 0; + } } diff --git a/sys/i386/i386/exception.s b/sys/i386/i386/exception.s index aa2e3d940fd1..f247c0a233af 100644 --- a/sys/i386/i386/exception.s +++ b/sys/i386/i386/exception.s @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: exception.s,v 1.2 1994/01/03 07:55:20 davidg Exp $ + * $Id: exception.s,v 1.3 1994/04/02 07:00:23 davidg Exp $ */ #include "npx.h" /* NNPX */ @@ -39,7 +39,9 @@ #include "errno.h" /* error return codes */ -#include "i386/isa/debug.h" /* BDE debugging macros */ +#include "machine/spl.h" /* SWI_AST_MASK ... */ + +#include "machine/psl.h" /* PSL_I */ #include "machine/trap.h" /* trap codes */ #include "syscall.h" /* syscall numbers */ @@ -57,31 +59,49 @@ /*****************************************************************************/ /* * Trap and fault vector routines - * + */ +#define IDTVEC(name) ALIGN_TEXT ; .globl _X/**/name ; _X/**/name: +#define TRAP(a) pushl $(a) ; jmp _alltraps + +/* * XXX - debugger traps are now interrupt gates so at least bdb doesn't lose * control. The sti's give the standard losing behaviour for ddb and kgdb. */ -#define IDTVEC(name) ALIGN_TEXT; .globl _X/**/name; _X/**/name: -#define TRAP(a) pushl $(a) ; jmp alltraps +#ifdef BDE_DEBUGGER +#define BDBTRAP(name) \ + ss ; \ + cmpb $0,_bdb_exists ; \ + je 1f ; \ + testb $SEL_RPL_MASK,4(%esp) ; \ + jne 1f ; \ + ss ; \ + .globl bdb_/**/name/**/_ljmp ; \ +bdb_/**/name/**/_ljmp: ; \ + ljmp $0,$0 ; \ +1: +#else +#define BDBTRAP(name) +#endif + #ifdef KGDB -# define BPTTRAP(a) sti; pushl $(a) ; jmp bpttraps +# define BPTTRAP(a) testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; \ + pushl $(a) ; jmp _bpttraps #else -# define BPTTRAP(a) sti; TRAP(a) +# define BPTTRAP(a) testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; TRAP(a) #endif +MCOUNT_LABEL(user) +MCOUNT_LABEL(btrap) + IDTVEC(div) pushl $0; TRAP(T_DIVIDE) IDTVEC(dbg) -#if defined(BDE_DEBUGGER) && defined(BDBTRAP) BDBTRAP(dbg) -#endif pushl $0; BPTTRAP(T_TRCTRAP) IDTVEC(nmi) pushl $0; TRAP(T_NMI) IDTVEC(bpt) -#if defined(BDE_DEBUGGER) && defined(BDBTRAP) BDBTRAP(bpt) -#endif pushl $0; BPTTRAP(T_BPTFLT) IDTVEC(ofl) pushl $0; TRAP(T_OFLOW) @@ -114,22 +134,24 @@ IDTVEC(fpu) * error. It would be better to handle npx interrupts as traps but * this is difficult for nested interrupts. */ - pushl $0 /* dummy error code */ - pushl $T_ASTFLT + pushl $0 /* dumby error code */ + pushl $0 /* dumby trap type */ pushal - nop /* silly, the bug is for popal and it only - * bites when the next instruction has a - * complicated address mode */ pushl %ds pushl %es /* now the stack frame is a trap frame */ movl $KDSEL,%eax movl %ax,%ds movl %ax,%es - pushl _cpl + FAKE_MCOUNT(12*4(%esp)) + movl _cpl,%eax + pushl %eax pushl $0 /* dummy unit to finish building intr frame */ incl _cnt+V_TRAP + orl $SWI_AST_MASK,%eax + movl %eax,_cpl call _npxintr - jmp doreti + MEXITCOUNT + jmp _doreti #else /* NNPX > 0 */ pushl $0; TRAP(T_ARITHTRAP) #endif /* NNPX > 0 */ @@ -166,25 +188,37 @@ IDTVEC(rsvd14) pushl $0; TRAP(31) SUPERALIGN_TEXT -alltraps: +_alltraps: pushal - nop pushl %ds pushl %es movl $KDSEL,%eax movl %ax,%ds movl %ax,%es + FAKE_MCOUNT(12*4(%esp)) calltrap: + FAKE_MCOUNT(_btrap) /* init "from" _btrap -> calltrap */ incl _cnt+V_TRAP + orl $SWI_AST_MASK,_cpl call _trap /* - * Return through doreti to handle ASTs. Have to change trap frame + * There was no place to save the cpl so we have to recover it + * indirectly. For traps from user mode it was 0, and for traps + * from kernel mode Oring SWI_AST_MASK into it didn't change it. + */ + subl %eax,%eax + testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp) + jne 1f + movl _cpl,%eax +1: + /* + * Return via _doreti to handle ASTs. Have to change trap frame * to interrupt frame. */ - movl $T_ASTFLT,TF_TRAPNO(%esp) /* new trap type (err code not used) */ - pushl _cpl - pushl $0 /* dummy unit */ - jmp doreti + pushl %eax + subl $4,%esp + MEXITCOUNT + jmp _doreti #ifdef KGDB /* @@ -192,17 +226,18 @@ calltrap: * to the regular trap code. */ SUPERALIGN_TEXT -bpttraps: +_bpttraps: pushal - nop pushl %ds pushl %es movl $KDSEL,%eax movl %ax,%ds movl %ax,%es + FAKE_MCOUNT(12*4(%esp)) testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp) /* non-kernel mode? */ jne calltrap /* yes */ call _kgdb_trap_glue + MEXITCOUNT jmp calltrap #endif @@ -214,7 +249,6 @@ IDTVEC(syscall) pushfl /* Room for tf_err */ pushfl /* Room for tf_trapno */ pushal - nop pushl %ds pushl %es movl $KDSEL,%eax /* switch to kernel segments */ @@ -222,51 +256,17 @@ IDTVEC(syscall) movl %ax,%es movl TF_ERR(%esp),%eax /* copy eflags from tf_err to fs_eflags */ movl %eax,TF_EFLAGS(%esp) - movl $0,TF_ERR(%esp) /* zero tf_err */ + FAKE_MCOUNT(12*4(%esp)) incl _cnt+V_SYSCALL + movl $SWI_AST_MASK,_cpl call _syscall /* - * Return through doreti to handle ASTs. + * Return via _doreti to handle ASTs. */ - movl $T_ASTFLT,TF_TRAPNO(%esp) /* new trap type (err code not used) */ - pushl _cpl - pushl $0 - jmp doreti - -#ifdef SHOW_A_LOT -/* - * 'show_bits' was too big when defined as a macro. The line length for some - * enclosing macro was too big for gas. Perhaps the code would have blown - * the cache anyway. - */ - ALIGN_TEXT -show_bits: - pushl %eax - SHOW_BIT(0) - SHOW_BIT(1) - SHOW_BIT(2) - SHOW_BIT(3) - SHOW_BIT(4) - SHOW_BIT(5) - SHOW_BIT(6) - SHOW_BIT(7) - SHOW_BIT(8) - SHOW_BIT(9) - SHOW_BIT(10) - SHOW_BIT(11) - SHOW_BIT(12) - SHOW_BIT(13) - SHOW_BIT(14) - SHOW_BIT(15) - popl %eax - ret - - .data -bit_colors: - .byte GREEN,RED,0,0 - .text - -#endif /* SHOW_A_LOT */ + pushl $0 /* cpl to restore */ + subl $4,%esp + MEXITCOUNT + jmp _doreti /* * include generated interrupt vectors and ISA intr code diff --git a/sys/i386/i386/in_cksum.c b/sys/i386/i386/in_cksum.c index 0dec1d67056d..0b3d3e5d9637 100644 --- a/sys/i386/i386/in_cksum.c +++ b/sys/i386/i386/in_cksum.c @@ -32,7 +32,7 @@ * * from tahoe: in_cksum.c 1.2 86/01/05 * from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91 - * $Id: in_cksum.c,v 1.4 1993/12/19 00:50:02 wollman Exp $ + * $Id: in_cksum.c,v 1.5 1994/03/07 11:47:30 davidg Exp $ */ #include "param.h" @@ -56,9 +56,10 @@ * Thanks to gcc we don't have to guess * which registers contain sum & w. */ -#define CLC asm("clc") -#define ADD(n) asm("adcl " #n "(%2), %0": "=r"(sum): "0"(sum), "r"(w)) -#define MOP asm("adcl $0, %0": "=r"(sum): "0"(sum)) +#define ADD(n) asm("addl " #n "(%2), %0" : "=r" (sum) : "0" (sum), "r" (w)) +#define ADDC(n) asm("adcl " #n "(%2), %0" : "=r" (sum) : "0" (sum), "r" (w)) +#define LOAD(n) asm volatile("movb " #n "(%1), %0" : "=r" (junk) : "r" (w)) +#define MOP asm("adcl $0, %0" : "=r" (sum) : "0" (sum)) int in_cksum(m, len) @@ -114,28 +115,90 @@ in_cksum(m, len) } } /* + * Advance to a 486 cache line boundary. + */ + if (4 & (int) w && mlen >= 4) { + ADD(0); + MOP; + w += 2; + mlen -= 4; + } + if (8 & (int) w && mlen >= 8) { + ADD(0); + ADDC(4); + MOP; + w += 4; + mlen -= 8; + } + /* * Do as much of the checksum as possible 32 bits at at time. * In fact, this loop is unrolled to make overhead from * branches &c small. */ + mlen -= 1; while ((mlen -= 32) >= 0) { + u_char junk; /* - * Clear the carry flag, add with carry 16 words - * and fold-in last carry by adding a 0 with carry. + * Add with carry 16 words and fold in the last + * carry by adding a 0 with carry. + * + * The early ADD(16) and the LOAD(32) are to load + * the next 2 cache lines in advance on 486's. The + * 486 has a penalty of 2 clock cycles for loading + * a cache line, plus whatever time the external + * memory takes to load the first word(s) addressed. + * These penalties are unavoidable. Subsequent + * accesses to a cache line being loaded (and to + * other external memory?) are delayed until the + * whole load finishes. These penalties are mostly + * avoided by not accessing external memory for + * 8 cycles after the ADD(16) and 12 cycles after + * the LOAD(32). The loop terminates when mlen + * is initially 33 (not 32) to guaranteed that + * the LOAD(32) is within bounds. */ - CLC; - ADD(0); ADD(4); ADD(8); ADD(12); - ADD(16); ADD(20); ADD(24); ADD(28); - MOP; w += 16; + ADD(16); + ADDC(0); + ADDC(4); + ADDC(8); + ADDC(12); + LOAD(32); + ADDC(20); + ADDC(24); + ADDC(28); + MOP; + w += 16; + } + mlen += 32 + 1; + if (mlen >= 32) { + ADD(16); + ADDC(0); + ADDC(4); + ADDC(8); + ADDC(12); + ADDC(20); + ADDC(24); + ADDC(28); + MOP; + w += 16; + mlen -= 32; } - mlen += 32; - while ((mlen -= 8) >= 0) { - CLC; - ADD(0); ADD(4); + if (mlen >= 16) { + ADD(0); + ADDC(4); + ADDC(8); + ADDC(12); + MOP; + w += 8; + mlen -= 16; + } + if (mlen >= 8) { + ADD(0); + ADDC(4); MOP; w += 4; + mlen -= 8; } - mlen += 8; if (mlen == 0 && byte_swapped == 0) continue; /* worth 1% maybe ?? */ REDUCE; @@ -172,4 +235,3 @@ in_cksum(m, len) REDUCE; return (~sum & 0xffff); } - diff --git a/sys/i386/i386/locore.s b/sys/i386/i386/locore.s index 57ee6a93832d..d0317103d43e 100644 --- a/sys/i386/i386/locore.s +++ b/sys/i386/i386/locore.s @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)locore.s 7.3 (Berkeley) 5/13/91 - * $Id: locore.s,v 1.15 1994/02/01 04:08:54 davidg Exp $ + * $Id: locore.s,v 1.20 1994/06/14 17:49:42 davidg Exp $ */ /* @@ -51,7 +51,6 @@ #include "machine/pte.h" /* page table entry definitions */ #include "errno.h" /* error return codes */ #include "machine/specialreg.h" /* x86 special registers */ -#include "i386/isa/debug.h" /* BDE debugging macros */ #include "machine/cputypes.h" /* x86 cpu type definitions */ #include "syscall.h" /* system call numbers */ #include "machine/asmacros.h" /* miscellaneous asm macros */ @@ -102,8 +101,10 @@ _esym: .long 0 /* ptr to end of syms */ .globl _boothowto,_bootdev,_curpcb - .globl _cpu,_cold,_atdevbase + .globl _cpu,_cold,_atdevbase,_cpu_vendor,_cpu_id _cpu: .long 0 /* are we 386, 386sx, or 486 */ +_cpu_id: .long 0 +_cpu_vendor: .space 17 _cold: .long 1 /* cold till we are not */ _atdevbase: .long 0 /* location of start of iomem in virtual */ _atdevphys: .long 0 /* location of device mapping ptes (phys) */ @@ -123,7 +124,7 @@ _proc0paddr: .long 0 /* address of proc 0 address space */ #ifdef BDE_DEBUGGER .globl _bdb_exists /* flag to indicate BDE debugger is available */ -_bde_exists: .long 0 +_bdb_exists: .long 0 #endif .globl tmpstk @@ -140,10 +141,10 @@ tmpstk: * btext: beginning of text section. * Also the entry point (jumped to directly from the boot blocks). */ -ENTRY(btext) +NON_GPROF_ENTRY(btext) movw $0x1234,0x472 /* warm boot */ jmp 1f - .space 0x500 /* skip over warm boot shit */ + .org 0x500 /* space for BIOS variables */ /* * pass parameters on stack (howto, bootdev, unit, cyloffset, esym) @@ -151,7 +152,8 @@ ENTRY(btext) * ( if we want to hold onto /boot, it's physical %esp up to _end) */ - 1: movl 4(%esp),%eax + 1: + movl 4(%esp),%eax movl %eax,_boothowto-KERNBASE movl 8(%esp),%eax movl %eax,_bootdev-KERNBASE @@ -164,30 +166,76 @@ ENTRY(btext) movl _nfs_diskless_size-KERNBASE,%ecx movl 20(%esp),%esi movl $(_nfs_diskless-KERNBASE),%edi + cld rep movsb #endif - /* find out our CPU type. */ - pushfl - popl %eax - movl %eax,%ecx - xorl $0x40000,%eax - pushl %eax - popfl - pushfl - popl %eax - xorl %ecx,%eax - shrl $18,%eax - andl $1,%eax - push %ecx - popfl - - cmpl $0,%eax - jne 1f - movl $CPU_386,_cpu-KERNBASE + /* don't trust what the BIOS gives for eflags */ + pushl $PSL_MBO + popfl + + /* Find out our CPU type. */ + + /* Try to toggle alignment check flag; does not exist on 386. */ + pushfl + popl %eax + movl %eax,%ecx + orl $PSL_AC,%eax + pushl %eax + popfl + pushfl + popl %eax + xorl %ecx,%eax + andl $PSL_AC,%eax + pushl %ecx + popfl + + testl %eax,%eax + jnz 1f + movl $CPU_386,_cpu-KERNBASE + jmp 2f + +1: /* Try to toggle identification flag; does not exist on early 486s. */ + pushfl + popl %eax + movl %eax,%ecx + xorl $PSL_ID,%eax + pushl %eax + popfl + pushfl + popl %eax + xorl %ecx,%eax + andl $PSL_ID,%eax + pushl %ecx + popfl + + testl %eax,%eax + jnz 1f + movl $CPU_486,_cpu-KERNBASE + jmp 2f + +1: /* Use the `cpuid' instruction. */ + xorl %eax,%eax + .byte 0x0f,0xa2 # cpuid 0 + movl %ebx,_cpu_vendor-KERNBASE # store vendor string + movl %edx,_cpu_vendor+4-KERNBASE + movl %ecx,_cpu_vendor+8-KERNBASE + movb $0,_cpu_vendor+12-KERNBASE + + movl $1,%eax + .byte 0x0f,0xa2 # cpuid 1 + movl %eax,_cpu_id-KERNBASE # store cpu_id + rorl $8,%eax # extract family type + andl $15,%eax + cmpl $5,%eax + jae 1f + + /* less than Pentium; must be 486 */ + movl $CPU_486,_cpu-KERNBASE jmp 2f -1: movl $CPU_486,_cpu-KERNBASE + +1: movl $CPU_586,_cpu-KERNBASE 2: /* @@ -217,7 +265,7 @@ ENTRY(btext) movl $_end-KERNBASE,%ecx addl $NBPG-1,%ecx /* page align up */ andl $~(NBPG-1),%ecx - movl %ecx,%esi /* esi=start of tables */ + movl %ecx,%esi /* esi = start of free memory */ movl %ecx,_KERNend-KERNBASE /* save end of kernel */ /* clear bss */ @@ -296,7 +344,7 @@ ENTRY(btext) shrl $PGSHIFT,%ecx orl $PG_V|PG_KW,%eax /* valid, kernel read/write */ fillkpt -#endif +#endif /* KGDB || BDE_DEBUGGER */ /* now initialize the page dir, upages, p0stack PT, and page tables */ @@ -309,7 +357,7 @@ ENTRY(btext) addl %esi,%ebx /* address of page directory */ addl $((1+UPAGES+1)*NBPG),%ebx /* offset to kernel page tables */ fillkpt - + /* map I/O memory map */ movl _KPTphys-KERNBASE,%ebx /* base of kernel page tables */ @@ -368,6 +416,7 @@ ENTRY(btext) movl $_gdt-KERNBASE,%edi movl %edi,2(%esp) movl $8*18/4,%ecx + cld rep /* copy gdt */ movsl movl $_gdt-KERNBASE,-8+2(%edi) /* adjust gdt self-ptr */ @@ -389,6 +438,7 @@ ENTRY(btext) movl $_idt-KERNBASE,%edi movl %edi,6+2(%esp) movl $8*4/4,%ecx + cld rep /* copy idt */ movsl @@ -397,7 +447,7 @@ ENTRY(btext) addl $2*6,%esp popal -#endif +#endif /* BDE_DEBUGGER */ /* load base of page directory and enable mapping */ movl %esi,%eax /* phys address of ptd in proc 0 */ @@ -436,7 +486,7 @@ begin: /* now running relocated at KERNBASE where the system is linked to run */ movl $_gdt+8*9,%eax /* adjust slots 9-17 */ movl $9,%ecx reloc_gdt: - movb $0xfe,7(%eax) /* top byte of base addresses, was 0, */ + movb $KERNBASE>>24,7(%eax) /* top byte of base addresses, was 0, */ addl $8,%eax /* now KERNBASE>>24 */ loop reloc_gdt @@ -444,7 +494,7 @@ reloc_gdt: je 1f int $3 1: -#endif +#endif /* BDE_DEBUGGER */ /* * Skip over the page tables and the kernel stack @@ -494,7 +544,7 @@ lretmsg1: .asciz "lret: toinit\n" -#define LCALL(x,y) .byte 0x9a ; .long y; .word x +#define LCALL(x,y) .byte 0x9a ; .long y ; .word x /* * Icode is copied out to process 1 and executed in user mode: * execve("/sbin/init", argv, envp); exit(0); @@ -551,4 +601,3 @@ NON_GPROF_ENTRY(sigcode) .globl _szsigcode _szsigcode: .long _szsigcode-_sigcode - diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index 3e0a368b4b44..05546058fbd0 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -35,13 +35,12 @@ * SUCH DAMAGE. * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 - * $Id: machdep.c,v 1.36.2.5 1994/04/18 04:56:56 rgrimes Exp $ + * $Id: machdep.c,v 1.47 1994/06/17 13:32:07 davidg Exp $ */ #include "npx.h" #include "isa.h" -#include <stddef.h> #include "param.h" #include "systm.h" #include "signalvar.h" @@ -58,7 +57,6 @@ #include "malloc.h" #include "mbuf.h" #include "msgbuf.h" -#include "net/netisr.h" #ifdef SYSVSHM #include "sys/shm.h" @@ -96,6 +94,8 @@ static void initcpu(void); static int test_page(int *, int); extern int grow(struct proc *,int); +const char machine[] = "PC-Class"; +const char *cpu_model; #ifndef PANIC_REBOOT_WAIT_TIME #define PANIC_REBOOT_WAIT_TIME 15 /* default to 15 seconds */ @@ -115,22 +115,22 @@ int bufpages = BUFPAGES; #else int bufpages = 0; #endif +#ifdef BOUNCEPAGES +int bouncepages = BOUNCEPAGES; +#else +int bouncepages = 0; +#endif extern int freebufspace; +extern char *bouncememory; int _udatasel, _ucodesel; /* * Machine-dependent startup code */ -int boothowto = 0, Maxmem = 0, maxmem = 0, badpages = 0, physmem = 0; +int boothowto = 0, Maxmem = 0, badpages = 0, physmem = 0; long dumplo; extern int bootdev; -#ifdef SMALL -extern int forcemaxmem; -#endif -#if defined(GENERICAH) || defined(GENERICBT) -int generic_hack = 1; -#endif int biosmem; vm_offset_t phys_avail[6]; @@ -141,6 +141,8 @@ int cpu_class; void dumpsys __P((void)); +#define offsetof(type, member) ((size_t)(&((type *)0)->member)) + void cpu_startup() { @@ -255,6 +257,7 @@ again: panic("startup: no room for tables"); goto again; } + /* * End of second pass, addresses have been assigned */ @@ -295,6 +298,22 @@ again: printf("using %d buffers containing %d bytes of memory\n", nbuf, bufpages * CLBYTES); +#ifndef NOBOUNCE + /* + * If there is more than 16MB of memory, allocate some bounce buffers + */ + if (Maxmem > 4096) { + if (bouncepages == 0) + bouncepages = 96; /* largest physio size + extra */ + bouncememory = (char *)kmem_alloc(kernel_map, bouncepages * PAGE_SIZE); + } + + /* + * init bounce buffers + */ + vm_bounce_init(); +#endif + /* * Set up CPU-specific registers, cache, etc. */ @@ -324,10 +343,13 @@ struct cpu_nameclass i386_cpus[] = { static void identifycpu() { + extern unsigned long cpu_id; + extern char cpu_vendor[]; printf("CPU: "); if (cpu >= 0 && cpu < (sizeof i386_cpus/sizeof(struct cpu_nameclass))) { printf("%s", i386_cpus[cpu].cpu_name); cpu_class = i386_cpus[cpu].cpu_class; + cpu_model = i386_cpus[cpu].cpu_name; } else { printf("unknown cpu type %d\n", cpu); panic("startup: bad cpu id"); @@ -350,6 +372,10 @@ identifycpu() printf("unknown"); /* will panic below... */ } printf("-class CPU)"); + if(cpu_id) + printf(" Id = 0x%x",cpu_id); + if(*cpu_vendor) + printf(" Origin = \"%s\"",cpu_vendor); printf("\n"); /* cpu speed would be nice, but how? */ /* @@ -503,7 +529,7 @@ sendsig(catcher, sig, mask, code) * Return to previous pc and psl as specified by * context left by sendsig. Check carefully to * make sure that the user has not modified the - * psl to gain improper priviledges or to cause + * psl to gain improper privileges or to cause * a machine fault. */ struct sigreturn_args { @@ -558,11 +584,11 @@ sigreturn(p, uap, retval) #define null_sel(sel) \ (!ISLDT(sel) && IDXSEL(sel) == 0) - if ((scp->sc_cs&0xffff != _ucodesel && !valid_ldt_sel(scp->sc_cs)) || - (scp->sc_ss&0xffff != _udatasel && !valid_ldt_sel(scp->sc_ss)) || - (scp->sc_ds&0xffff != _udatasel && !valid_ldt_sel(scp->sc_ds) && + if (((scp->sc_cs&0xffff) != _ucodesel && !valid_ldt_sel(scp->sc_cs)) || + ((scp->sc_ss&0xffff) != _udatasel && !valid_ldt_sel(scp->sc_ss)) || + ((scp->sc_ds&0xffff) != _udatasel && !valid_ldt_sel(scp->sc_ds) && !null_sel(scp->sc_ds)) || - (scp->sc_es&0xffff != _udatasel && !valid_ldt_sel(scp->sc_es) && + ((scp->sc_es&0xffff) != _udatasel && !valid_ldt_sel(scp->sc_es) && !null_sel(scp->sc_es))) { #ifdef DEBUG printf("sigreturn: cs=0x%x ss=0x%x ds=0x%x es=0x%x\n", @@ -709,10 +735,10 @@ boot(arghowto) #endif die: printf("Rebooting...\n"); - DELAY (100000); /* wait 100ms for printf's to complete */ + DELAY(1000000); /* wait 1 sec for printf's to complete and be read */ cpu_reset(); for(;;) ; - /*NOTREACHED*/ + /* NOTREACHED */ } unsigned long dumpmag = 0x8fca0101UL; /* magic number for savecore */ @@ -782,36 +808,6 @@ microtime(tvp) } #endif /* HZ */ -void -physstratdone(bp) - struct buf *bp; -{ - wakeup((caddr_t) bp); - bp->b_flags &= ~B_CALL; -} - -void -physstrat(bp, strat, prio) - struct buf *bp; - int (*strat)(), prio; -{ - register int s; - caddr_t baddr; - - bp->b_flags |= B_CALL; - bp->b_iodone = physstratdone; - vmapbuf(bp); - (*strat)(bp); - /* pageout daemon doesn't wait for pushed pages */ - if (bp->b_flags & B_DIRTY) - return; - s = splbio(); - while ((bp->b_flags & B_DONE) == 0) - tsleep((caddr_t)bp, prio, "physstr", 0); - splx(s); - vunmapbuf(bp); -} - static void initcpu() { @@ -1001,7 +997,7 @@ setidt(idx, func, typ, dpl) ip->gd_hioffset = ((int)func)>>16 ; } -#define IDTVEC(name) __CONCAT(X, name) +#define IDTVEC(name) __CONCAT(X,name) typedef void idtvec_t(); extern idtvec_t @@ -1012,7 +1008,7 @@ extern idtvec_t IDTVEC(rsvd1), IDTVEC(rsvd2), IDTVEC(rsvd3), IDTVEC(rsvd4), IDTVEC(rsvd5), IDTVEC(rsvd6), IDTVEC(rsvd7), IDTVEC(rsvd8), IDTVEC(rsvd9), IDTVEC(rsvd10), IDTVEC(rsvd11), IDTVEC(rsvd12), - IDTVEC(rsvd13), IDTVEC(rsvd14), IDTVEC(rsvd14), IDTVEC(syscall); + IDTVEC(rsvd13), IDTVEC(rsvd14), IDTVEC(syscall); int _gsel_tss; @@ -1044,8 +1040,9 @@ init386(first) * the address space */ gdt_segs[GCODE_SEL].ssd_limit = i386_btop(i386_round_page(&etext)) - 1; - gdt_segs[GDATA_SEL].ssd_limit = 0xffffffffUL; /* XXX constant? */ + gdt_segs[GDATA_SEL].ssd_limit = i386_btop(0) - 1; for (x=0; x < NGDT; x++) ssdtosd(gdt_segs+x, gdt+x); + /* make ldt memory segments */ /* * The data segment limit must not cover the user area because we @@ -1146,7 +1143,7 @@ init386(first) #ifndef LARGEMEM if (biosextmem > 65536) { panic("extended memory beyond limit of 64MB"); - /* NOT REACHED */ + /* NOTREACHED */ } #endif @@ -1169,25 +1166,6 @@ init386(first) if ((pagesinext > 3840) && (pagesinext < 4096)) pagesinext = 3840; -#if defined(GENERICAH) || defined(GENERICBT) - /* XXX This is an ugle hack so that machines with >16MB of memory - * can be booted using the GENERIC* kernels and not have to worry - * about bus mastered DMA on the ISA bus. It is ONLY compiled into - * the GENERIC* kernels and can be disabled by tweaking the global - * generic_hack to be zero using gdb. - */ - if (generic_hack) { - if (pagesinext > 3840) { - printf("WARNING WARNING WARNING WARNING WARNING WARNING\n"); - printf("GENERIC* kernels only USE the first 16MB of your "); - printf("%dMB.\n", (pagesinext + 256) / 256); - printf("Read the RELNOTES.FreeBSD file for the reason.\n"); - printf("WARNING WARNING WARNING WARNING WARNING WARNING\n"); - pagesinext = 3840; - } - } -#endif /* defined (GENERICAH) || defiend(GENERICBT) */ - /* * Maxmem isn't the "maximum memory", it's the highest page of * of the physical address space. It should be "Maxphyspage". @@ -1266,9 +1244,9 @@ init386(first) } } printf("done.\n"); - - maxmem = Maxmem - 1; /* highest page of usable memory */ - avail_end = (maxmem << PAGE_SHIFT) - i386_round_page(sizeof(struct msgbuf)); + + avail_end = (Maxmem << PAGE_SHIFT) + - i386_round_page(sizeof(struct msgbuf)); /* * Initialize pointers to the two chunks of memory; for use @@ -1334,15 +1312,6 @@ test_page(address, pattern) return(0); } -/*aston() { - schednetisr(NETISR_AST); -}*/ - -void -setsoftclock() { - schednetisr(NETISR_SCLK); -} - /* * insert an element into a queue */ diff --git a/sys/i386/i386/math_emulate.c b/sys/i386/i386/math_emulate.c index 256f8fb1033d..c6df09e9dddb 100644 --- a/sys/i386/i386/math_emulate.c +++ b/sys/i386/i386/math_emulate.c @@ -6,7 +6,7 @@ * [expediant "port" of linux 8087 emulator to 386BSD, with apologies -wfj] * * from: 386BSD 0.1 - * $Id: math_emulate.c,v 1.7 1994/01/29 22:07:16 nate Exp $ + * $Id: math_emulate.c,v 1.9 1994/06/05 19:31:45 ats Exp $ */ /* @@ -107,243 +107,255 @@ math_emulate(struct trapframe * info) switch (code) { case 0x1d0: /* fnop */ return(0); - case 0x1d1: case 0x1d2: case 0x1d3: + case 0x1d1: case 0x1d2: case 0x1d3: /* fst to 32-bit mem */ case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7: math_abort(info,SIGILL); - case 0x1e0: + case 0x1e0: /* fchs */ ST(0).exponent ^= 0x8000; return(0); - case 0x1e1: + case 0x1e1: /* fabs */ ST(0).exponent &= 0x7fff; return(0); case 0x1e2: case 0x1e3: math_abort(info,SIGILL); - case 0x1e4: + case 0x1e4: /* ftst */ ftst(PST(0)); return(0); - case 0x1e5: + case 0x1e5: /* fxam */ printf("fxam not implemented\n\r"); math_abort(info,SIGILL); - case 0x1e6: case 0x1e7: + case 0x1e6: case 0x1e7: /* fldenv */ math_abort(info,SIGILL); - case 0x1e8: + case 0x1e8: /* fld1 */ fpush(); ST(0) = CONST1; return(0); - case 0x1e9: + case 0x1e9: /* fld2t */ fpush(); ST(0) = CONSTL2T; return(0); - case 0x1ea: + case 0x1ea: /* fld2e */ fpush(); ST(0) = CONSTL2E; return(0); - case 0x1eb: + case 0x1eb: /* fldpi */ fpush(); ST(0) = CONSTPI; return(0); - case 0x1ec: + case 0x1ec: /* fldlg2 */ fpush(); ST(0) = CONSTLG2; return(0); - case 0x1ed: + case 0x1ed: /* fldln2 */ fpush(); ST(0) = CONSTLN2; return(0); - case 0x1ee: + case 0x1ee: /* fldz */ fpush(); ST(0) = CONSTZ; return(0); case 0x1ef: math_abort(info,SIGILL); - case 0x1f0: case 0x1f1: case 0x1f2: case 0x1f3: - case 0x1f4: case 0x1f5: case 0x1f6: case 0x1f7: - case 0x1f8: case 0x1f9: case 0x1fa: case 0x1fb: - case 0x1fe: case 0x1ff: + case 0x1f0: /* f2xm1 */ + case 0x1f1: /* fyl2x */ + case 0x1f2: /* fptan */ + case 0x1f3: /* fpatan */ + case 0x1f4: /* fxtract */ + case 0x1f5: /* fprem1 */ + case 0x1f6: /* fdecstp */ + case 0x1f7: /* fincstp */ + case 0x1f8: /* fprem */ + case 0x1f9: /* fyl2xp1 */ + case 0x1fa: /* fsqrt */ + case 0x1fb: /* fsincos */ + case 0x1fe: /* fsin */ + case 0x1ff: /* fcos */ uprintf( "math_emulate: instruction %04x not implemented\n", code + 0xd800); math_abort(info,SIGILL); - case 0x1fd: + case 0x1fc: /* frndint */ + frndint(PST(0),&tmp); + real_to_real(&tmp,&ST(0)); + return(0); + case 0x1fd: /* fscale */ /* incomplete and totally inadequate -wfj */ Fscale(PST(0), PST(1), &tmp); real_to_real(&tmp,&ST(0)); return(0); /* 19 Sep 92*/ - case 0x1fc: - frndint(PST(0),&tmp); - real_to_real(&tmp,&ST(0)); - return(0); - case 0x2e9: + case 0x2e9: /* ????? */ +/* if this should be a fucomp ST(0),ST(1) , it must be a 0x3e9 ATS */ fucom(PST(1),PST(0)); fpop(); fpop(); return(0); - case 0x3d0: case 0x3d1: + case 0x3d0: case 0x3d1: /* fist ?? */ return(0); - case 0x3e2: + case 0x3e2: /* fclex */ I387.swd &= 0x7f00; return(0); - case 0x3e3: + case 0x3e3: /* fninit */ I387.cwd = 0x037f; I387.swd = 0x0000; I387.twd = 0x0000; return(0); case 0x3e4: return(0); - case 0x6d9: + case 0x6d9: /* fcompp */ fcom(PST(1),PST(0)); fpop(); fpop(); return(0); - case 0x7e0: + case 0x7e0: /* fstsw ax */ *(short *) &info->tf_eax = I387.swd; return(0); } switch (code >> 3) { - case 0x18: + case 0x18: /* fadd */ fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return(0); - case 0x19: + case 0x19: /* fmul */ fmul(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return(0); - case 0x1a: + case 0x1a: /* fcom */ fcom(PST(code & 7),PST(0)); return(0); - case 0x1b: + case 0x1b: /* fcomp */ fcom(PST(code & 7),PST(0)); fpop(); return(0); - case 0x1c: + case 0x1c: /* fsubr */ real_to_real(&ST(code & 7),&tmp); tmp.exponent ^= 0x8000; fadd(PST(0),&tmp,&tmp); real_to_real(&tmp,&ST(0)); return(0); - case 0x1d: + case 0x1d: /* fsub */ ST(0).exponent ^= 0x8000; fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return(0); - case 0x1e: + case 0x1e: /* fdivr */ fdiv(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return(0); - case 0x1f: + case 0x1f: /* fdiv */ fdiv(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(0)); return(0); - case 0x38: + case 0x38: /* fld */ fpush(); - ST(0) = ST((code & 7)+1); + ST(0) = ST((code & 7)+1); /* why plus 1 ????? ATS */ return(0); - case 0x39: + case 0x39: /* fxch */ fxchg(&ST(0),&ST(code & 7)); return(0); - case 0x3b: + case 0x3b: /* ??? ??? wrong ???? ATS */ ST(code & 7) = ST(0); fpop(); return(0); - case 0x98: + case 0x98: /* fadd */ fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return(0); - case 0x99: + case 0x99: /* fmul */ fmul(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return(0); - case 0x9a: + case 0x9a: /* ???? , my manual don't list a direction bit +for fcom , ??? ATS */ fcom(PST(code & 7),PST(0)); return(0); - case 0x9b: + case 0x9b: /* same as above , ATS */ fcom(PST(code & 7),PST(0)); fpop(); return(0); - case 0x9c: + case 0x9c: /* fsubr */ ST(code & 7).exponent ^= 0x8000; fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return(0); - case 0x9d: + case 0x9d: /* fsub */ real_to_real(&ST(0),&tmp); tmp.exponent ^= 0x8000; fadd(PST(code & 7),&tmp,&tmp); real_to_real(&tmp,&ST(code & 7)); return(0); - case 0x9e: + case 0x9e: /* fdivr */ fdiv(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return(0); - case 0x9f: + case 0x9f: /* fdiv */ fdiv(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); return(0); - case 0xb8: + case 0xb8: /* ffree */ printf("ffree not implemented\n\r"); math_abort(info,SIGILL); - case 0xb9: + case 0xb9: /* fstp ???? where is the pop ? ATS */ fxchg(&ST(0),&ST(code & 7)); return(0); - case 0xba: + case 0xba: /* fst */ ST(code & 7) = ST(0); return(0); - case 0xbb: + case 0xbb: /* ????? encoding of fstp to mem ? ATS */ ST(code & 7) = ST(0); fpop(); return(0); - case 0xbc: + case 0xbc: /* fucom */ fucom(PST(code & 7),PST(0)); return(0); - case 0xbd: + case 0xbd: /* fucomp */ fucom(PST(code & 7),PST(0)); fpop(); return(0); - case 0xd8: + case 0xd8: /* faddp */ fadd(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return(0); - case 0xd9: + case 0xd9: /* fmulp */ fmul(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return(0); - case 0xda: + case 0xda: /* ??? encoding of ficom with 16 bit mem ? ATS */ fcom(PST(code & 7),PST(0)); fpop(); return(0); - case 0xdc: + case 0xdc: /* fsubrp */ ST(code & 7).exponent ^= 0x8000; fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return(0); - case 0xdd: + case 0xdd: /* fsubp */ real_to_real(&ST(0),&tmp); tmp.exponent ^= 0x8000; fadd(PST(code & 7),&tmp,&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return(0); - case 0xde: + case 0xde: /* fdivrp */ fdiv(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return(0); - case 0xdf: + case 0xdf: /* fdivp */ fdiv(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return(0); - case 0xf8: + case 0xf8: /* fild 16-bit mem ???? ATS */ printf("ffree not implemented\n\r"); math_abort(info,SIGILL); fpop(); return(0); - case 0xf9: + case 0xf9: /* ????? ATS */ fxchg(&ST(0),&ST(code & 7)); return(0); - case 0xfa: - case 0xfb: + case 0xfa: /* fist 16-bit mem ? ATS */ + case 0xfb: /* fistp 16-bit mem ? ATS */ ST(code & 7) = ST(0); fpop(); return(0); diff --git a/sys/i386/i386/microtime.s b/sys/i386/i386/microtime.s index d167b6ee556a..4c77fd08117d 100644 --- a/sys/i386/i386/microtime.s +++ b/sys/i386/i386/microtime.s @@ -31,10 +31,10 @@ * SUCH DAMAGE. * * from: Steve McCanne's microtime code - * $Id: microtime.s,v 1.2 1993/10/16 14:15:08 rgrimes Exp $ + * $Id: microtime.s,v 1.4 1994/05/02 09:44:20 sos Exp $ */ -#include "asm.h" +#include "machine/asmacros.h" #include "../isa/isa.h" #include "../isa/timerreg.h" @@ -44,31 +44,25 @@ */ #ifndef HZ ENTRY(microtime) - pushl %edi + pushl %edi # save registers pushl %esi pushl %ebx - movl $_time,%ebx + movl $_time, %ebx # get timeval ptr + movl (%ebx), %edi # sec = time.tv_sec + movl 4(%ebx), %esi # usec = time.tv_usec cli # disable interrupts - movl (%ebx),%edi # sec = time.tv_sec - movl 4(%ebx),%esi # usec = time.tv_usec + movl $(TIMER_SEL0|TIMER_LATCH), %eax + outb %al, $TIMER_MODE # latch timer 0's counter - movl $(TIMER_SEL0|TIMER_LATCH),%eax - outb %al,$TIMER_MODE # latch timer 0's counter + xorl %ebx, %ebx # clear ebx + inb $TIMER_CNTR0, %al # Read counter value, LSB first + movb %al, %bl + inb $TIMER_CNTR0, %al + movb %al, %bh - # - # Read counter value into ebx, LSB first - # - inb $TIMER_CNTR0,%al - movzbl %al,%ebx - inb $TIMER_CNTR0,%al - movzbl %al,%eax - sall $8,%eax - orl %eax,%ebx - - # # Now check for counter overflow. This is tricky because the # timer chip doesn't let us atomically read the current counter # value and the output state (i.e., overflow state). We have @@ -96,38 +90,45 @@ ENTRY(microtime) # we're called from an ipl less than the clock. Otherwise, # it might not work. Currently, only gettimeofday and bpf # call microtime so it's not a problem. - # - cmpl $11890,%ebx - jle 2f - movl $0x0a,%eax # tell ICU we want IRR - outb %al,$IO_ICU1 - inb $IO_ICU1,%al # read IRR in ICU - testb $1,%al # is a timer interrupt pending? + movl _timer0_prescale, %eax # adjust value if timer is + addl _timer0_divisor, %eax # reprogrammed + addl $-11932, %eax + subl %eax, %ebx + + cmpl $11890, %ebx # do we have a possible overflow condition + jle 1f + + inb $IO_ICU1, %al # read IRR in ICU + testb $1, %al # is a timer interrupt pending? je 1f - addl $-11932,%ebx # yes, subtract one clock period + addl $-11932, %ebx # yes, subtract one clock period 1: - movl $0x0b,%eax # tell ICU we want ISR - outb %al,$IO_ICU1 # (rest of kernel expects this) -2: sti # enable interrupts - movl $11932,%eax # subtract counter value from 11932 since - subl %ebx,%eax # it is a count-down value - imull $1000,%eax,%eax - movl $0,%edx # zero extend eax for div - movl $1193,%ecx + movl $11932, %eax # subtract counter value from 11932 since + subl %ebx, %eax # it is a count-down value + + movl %eax, %ebx # this really is a "imull $1000, %eax, %eax" + sall $10, %eax # instruction, but this saves us + sall $3, %ebx # 33/23 clocks on a 486/386 CPU + subl %ebx, %eax # + sall $1, %ebx # /sos + subl %ebx, %eax # + + movl $0, %edx # zero extend eax into edx for div + movl $1193, %ecx idivl %ecx # convert to usecs: mult by 1000/1193 - addl %eax,%esi # add counter usecs to time.tv_usec - cmpl $1000000,%esi # carry in timeval? - jl 3f - subl $1000000,%esi # adjust usec + addl %eax, %esi # add counter usecs to time.tv_usec + cmpl $1000000, %esi # carry in timeval? + jl 2f + subl $1000000, %esi # adjust usec incl %edi # bump sec -3: - movl 16(%esp),%ecx # load timeval pointer arg - movl %edi,(%ecx) # tvp->tv_sec = sec - movl %esi,4(%ecx) # tvp->tv_usec = usec +2: + movl 16(%esp), %ecx # load timeval pointer arg + movl %edi, (%ecx) # tvp->tv_sec = sec + movl %esi, 4(%ecx) # tvp->tv_usec = usec popl %ebx # restore regs popl %esi diff --git a/sys/i386/i386/pmap.c b/sys/i386/i386/pmap.c index da74b5f6455b..a1c04662cb8b 100644 --- a/sys/i386/i386/pmap.c +++ b/sys/i386/i386/pmap.c @@ -1,6 +1,10 @@ /* * Copyright (c) 1991 Regents of the University of California. * All rights reserved. + * Copyright (c) 1994 John S. Dyson + * All rights reserved. + * Copyright (c) 1994 David Greenman + * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer @@ -35,7 +39,7 @@ * SUCH DAMAGE. * * from: @(#)pmap.c 7.7 (Berkeley) 5/12/91 - * $Id: pmap.c,v 1.19.2.1 1994/04/18 04:56:59 rgrimes Exp $ + * $Id: pmap.c,v 1.26 1994/06/11 17:09:39 paul Exp $ */ /* @@ -51,7 +55,8 @@ * Major modifications by John S. Dyson primarily to support * pageable page tables, eliminating pmap_attributes, * discontiguous memory pages, and using more efficient string - * instructions. Jan 13, 1994. + * instructions. Jan 13, 1994. Further modifications on Mar 2, 1994, + * general clean-up and efficiency mods. */ /* @@ -86,6 +91,7 @@ #include "malloc.h" #include "user.h" #include "i386/include/cpufunc.h" +#include "i386/include/cputypes.h" #include "vm/vm.h" #include "vm/vm_kern.h" @@ -99,55 +105,6 @@ */ #define BSDVM_COMPAT 1 -#ifdef DEBUG -struct { - int kernel; /* entering kernel mapping */ - int user; /* entering user mapping */ - int ptpneeded; /* needed to allocate a PT page */ - int pwchange; /* no mapping change, just wiring or protection */ - int wchange; /* no mapping change, just wiring */ - int mchange; /* was mapped but mapping to different page */ - int managed; /* a managed page */ - int firstpv; /* first mapping for this PA */ - int secondpv; /* second mapping for this PA */ - int ci; /* cache inhibited */ - int unmanaged; /* not a managed page */ - int flushes; /* cache flushes */ -} enter_stats; -struct { - int calls; - int removes; - int pvfirst; - int pvsearch; - int ptinvalid; - int uflushes; - int sflushes; -} remove_stats; - -int debugmap = 0; -int pmapdebug = 0 /* 0xffff */; -#define PDB_FOLLOW 0x0001 -#define PDB_INIT 0x0002 -#define PDB_ENTER 0x0004 -#define PDB_REMOVE 0x0008 -#define PDB_CREATE 0x0010 -#define PDB_PTPAGE 0x0020 -#define PDB_CACHE 0x0040 -#define PDB_BITS 0x0080 -#define PDB_COLLECT 0x0100 -#define PDB_PROTECT 0x0200 -#define PDB_PDRTAB 0x0400 -#define PDB_PARANOIA 0x2000 -#define PDB_WIRING 0x4000 -#define PDB_PVDUMP 0x8000 - -int pmapvacflush = 0; -#define PVF_ENTER 0x01 -#define PVF_REMOVE 0x02 -#define PVF_PROTECT 0x04 -#define PVF_TOTAL 0x80 -#endif - /* * Get PDEs and PTEs for user/kernel address space */ @@ -156,14 +113,14 @@ int pmapvacflush = 0; #define pmap_pte_pa(pte) (*(int *)(pte) & PG_FRAME) -#define pmap_pde_v(pte) ((pte)->pd_v) -#define pmap_pte_w(pte) ((pte)->pg_w) -/* #define pmap_pte_ci(pte) ((pte)->pg_ci) */ -#define pmap_pte_m(pte) ((pte)->pg_m) -#define pmap_pte_u(pte) ((pte)->pg_u) -#define pmap_pte_v(pte) ((pte)->pg_v) -#define pmap_pte_set_w(pte, v) ((pte)->pg_w = (v)) -#define pmap_pte_set_prot(pte, v) ((pte)->pg_prot = (v)) +#define pmap_pde_v(pte) ((*(int *)pte & PG_V) != 0) +#define pmap_pte_w(pte) ((*(int *)pte & PG_W) != 0) +#define pmap_pte_m(pte) ((*(int *)pte & PG_M) != 0) +#define pmap_pte_u(pte) ((*(int *)pte & PG_U) != 0) +#define pmap_pte_v(pte) ((*(int *)pte & PG_V) != 0) + +#define pmap_pte_set_w(pte, v) ((v)?(*(int *)pte |= PG_W):(*(int *)pte &= ~PG_W)) +#define pmap_pte_set_prot(pte, v) ((*(int *)pte &= ~PG_PROT), (*(int *)pte |= (v))) /* * Given a map and a machine independent protection code, @@ -184,19 +141,21 @@ vm_offset_t virtual_end; /* VA of last avail page (end of kernel AS) */ int i386pagesperpage; /* PAGE_SIZE / I386_PAGE_SIZE */ boolean_t pmap_initialized = FALSE; /* Has pmap_init completed? */ vm_offset_t vm_first_phys, vm_last_phys; -vm_offset_t pager_sva, pager_eva; - -static inline boolean_t pmap_testbit(); -static inline void pmap_changebit(); -static inline int pmap_is_managed(); -static inline void *vm_get_pmap(); -static inline void vm_put_pmap(); -static inline void pmap_use_pt(); -inline struct pte *pmap_pte(); -static inline pv_entry_t get_pv_entry(); -void pmap_alloc_pv_entry(); -void pmap_clear_modify(); -void i386_protection_init(); + +static inline boolean_t pmap_testbit(); +static inline void pmap_changebit(); +static inline int pmap_is_managed(); +static inline void *vm_get_pmap(); +static inline void vm_put_pmap(); +inline void pmap_use_pt(); +inline void pmap_unuse_pt(); +inline pt_entry_t * const pmap_pte(); +static inline pv_entry_t get_pv_entry(); +void pmap_alloc_pv_entry(); +void pmap_clear_modify(); +void i386_protection_init(); +extern vm_offset_t pager_sva, pager_eva; +extern int cpu_class; #if BSDVM_COMPAT #include "msgbuf.h" @@ -204,13 +163,12 @@ void i386_protection_init(); /* * All those kernel PT submaps that BSD is so fond of */ -struct pte *CMAP1, *CMAP2, *mmap; +pt_entry_t *CMAP1, *CMAP2, *mmap; caddr_t CADDR1, CADDR2, vmmap; -struct pte *msgbufmap; +pt_entry_t *msgbufmap; struct msgbuf *msgbufp; #endif -struct vm_map * pmap_fmap(pmap_t pmap) ; void init_pv_entries(int) ; /* @@ -221,26 +179,24 @@ void init_pv_entries(int) ; * [ what about induced faults -wfj] */ -inline struct pte * -pmap_pte(pmap, va) +inline pt_entry_t * +const pmap_pte(pmap, va) register pmap_t pmap; vm_offset_t va; { - if (pmap && pmap_pde_v(pmap_pde(pmap, va))) { + if (pmap && *pmap_pde(pmap, va)) { + vm_offset_t frame = (int) pmap->pm_pdir[PTDPTDI] & PG_FRAME; /* are we current address space or kernel? */ - if (pmap->pm_pdir[PTDPTDI].pd_pfnum == PTDpde.pd_pfnum - || pmap == kernel_pmap) - return ((struct pte *) vtopte(va)); - + if ( (pmap == kernel_pmap) || (frame == ((int) PTDpde & PG_FRAME))) + return ((pt_entry_t *) vtopte(va)); /* otherwise, we are alternate address space */ else { - if (pmap->pm_pdir[PTDPTDI].pd_pfnum - != APTDpde.pd_pfnum) { + if ( frame != ((int) APTDpde & PG_FRAME) ) { APTDpde = pmap->pm_pdir[PTDPTDI]; tlbflush(); } - return((struct pte *) avtopte(va)); + return((pt_entry_t *) avtopte(va)); } } return(0); @@ -258,40 +214,34 @@ pmap_extract(pmap, va) register pmap_t pmap; vm_offset_t va; { - struct pde save; + pd_entry_t save; vm_offset_t pa; int s; - s = splhigh(); - if (pmap && pmap_pde_v(pmap_pde(pmap, va))) { + if (pmap && *pmap_pde(pmap, va)) { + vm_offset_t frame = (int) pmap->pm_pdir[PTDPTDI] & PG_FRAME; /* are we current address space or kernel? */ - if (pmap->pm_pdir[PTDPTDI].pd_pfnum == PTDpde.pd_pfnum - || pmap == kernel_pmap) { + if ( (pmap == kernel_pmap) + || (frame == ((int) PTDpde & PG_FRAME)) ) { pa = *(int *) vtopte(va); /* otherwise, we are alternate address space */ } else { - if (pmap->pm_pdir[PTDPTDI].pd_pfnum - != APTDpde.pd_pfnum) { - save = APTDpde; + if ( frame != ((int) APTDpde & PG_FRAME)) { APTDpde = pmap->pm_pdir[PTDPTDI]; tlbflush(); - pa = *(int *) avtopte(va); - APTDpde = save; - tlbflush(); - } else { - tlbflush(); - pa = *(int *) avtopte(va); } + pa = *(int *) avtopte(va); } pa = (pa & PG_FRAME) | (va & ~PG_FRAME); - splx(s); return pa; } - splx(s); return 0; } +/* + * determine if a page is managed (memory vs. device) + */ static inline int pmap_is_managed(pa) vm_offset_t pa; @@ -309,36 +259,51 @@ pmap_is_managed(pa) } /* - * increment/decrement pmap wiring count + * find the vm_page_t of a pte (only) given va of pte and pmap */ -static inline void -pmap_use_pt(pmap, va, use) +inline vm_page_t +pmap_pte_vm_page(pmap, pt) + pmap_t pmap; + vm_offset_t pt; +{ + pt = i386_trunc_page( pt); + pt = (pt - UPT_MIN_ADDRESS) / NBPG; + pt = ((vm_offset_t) pmap->pm_pdir[pt]) & PG_FRAME; + return PHYS_TO_VM_PAGE(pt); +} + +/* + * Wire a page table page + */ +inline void +pmap_use_pt(pmap, va) pmap_t pmap; vm_offset_t va; - int use; { - vm_offset_t pt, pa; - pv_entry_t pv; - vm_page_t m; + vm_offset_t pt; - if (va >= VM_MAX_ADDRESS) + if (va >= VM_MAX_ADDRESS || !pmap_initialized) return; - pt = i386_trunc_page(vtopte(va)); - pa = pmap_extract(pmap, pt); - if (pa == 0) { - printf("Warning pmap_use_pt pte paging failure\n"); - } - if (!pa || !pmap_is_managed(pa)) - return; - pv = pa_to_pvh(pa); - - m = PHYS_TO_VM_PAGE(pa); - if (use) { - vm_page_wire(m); - } else { - vm_page_unwire(m); - } + pt = (vm_offset_t) vtopte(va); + vm_page_hold( pmap_pte_vm_page(pmap, pt)); +} + +/* + * Unwire a page table page + */ +inline void +pmap_unuse_pt(pmap, va) + pmap_t pmap; + vm_offset_t va; +{ + vm_offset_t pt; + + if (va >= VM_MAX_ADDRESS || !pmap_initialized) + return; + + pt = (vm_offset_t) vtopte(va); + vm_page_unhold( pmap_pte_vm_page(pmap, pt)); } /* [ macro again?, should I force kstack into user map here? -wfj ] */ @@ -370,7 +335,7 @@ pmap_bootstrap(firstaddr, loadaddr) { #if BSDVM_COMPAT vm_offset_t va; - struct pte *pte; + pt_entry_t *pte; #endif extern int IdlePTD; @@ -436,6 +401,8 @@ pmap_bootstrap(firstaddr, loadaddr) * Initialize the pmap module. * Called by vm_init, to initialize any structures that the pmap * system needs to map virtual memory. + * pmap_init has been enhanced to support in a fairly consistant + * way, discontiguous physical memory. */ void pmap_init(phys_start, phys_end) @@ -538,12 +505,7 @@ pmap_create(size) if (size) return(NULL); - /* XXX: is it ok to wait here? */ pmap = (pmap_t) malloc(sizeof *pmap, M_VMPMAP, M_WAITOK); -#ifdef notifwewait - if (pmap == NULL) - panic("pmap_create: cannot allocate a pmap"); -#endif bzero(pmap, sizeof(*pmap)); pmap_pinit(pmap); return (pmap); @@ -590,7 +552,7 @@ pmap_pinit(pmap) /* install self-referential address mapping entry */ *(int *)(pmap->pm_pdir+PTDPTDI) = - ((int)pmap_extract(kernel_pmap, (vm_offset_t)pmap->pm_pdir)) | PG_V | PG_KW; + ((int)pmap_kextract((vm_offset_t)pmap->pm_pdir)) | PG_V | PG_KW; pmap->pm_count = 1; simple_lock_init(&pmap->pm_lock); @@ -698,6 +660,11 @@ get_pv_entry() return tmp; } +/* + * this *strange* allocation routine *statistically* eliminates the + * *possibility* of a malloc failure (*FATAL*) for a pv_entry_t data structure. + * also -- this code is MUCH MUCH faster than the malloc equiv... + */ void pmap_alloc_pv_entry() { @@ -755,7 +722,7 @@ pmap_alloc_pv_entry() /* * init the pv_entry allocation system */ -#define PVSPERPAGE 16 +#define PVSPERPAGE 64 void init_pv_entries(npg) int npg; @@ -778,15 +745,13 @@ get_pt_entry(pmap) pmap_t pmap; { pt_entry_t *ptp; + vm_offset_t frame = (int) pmap->pm_pdir[PTDPTDI] & PG_FRAME; /* are we current address space or kernel? */ - if (pmap->pm_pdir[PTDPTDI].pd_pfnum == PTDpde.pd_pfnum - || pmap == kernel_pmap) + if (pmap == kernel_pmap || frame == ((int) PTDpde & PG_FRAME)) { ptp=PTmap; - /* otherwise, we are alternate address space */ - else { - if (pmap->pm_pdir[PTDPTDI].pd_pfnum - != APTDpde.pd_pfnum) { + } else { + if ( frame != ((int) APTDpde & PG_FRAME)) { APTDpde = pmap->pm_pdir[PTDPTDI]; tlbflush(); } @@ -808,9 +773,9 @@ pmap_remove_entry(pmap, pv, va) vm_offset_t va; { pv_entry_t npv; - int s; int wired; - s = splhigh(); + int s; + s = splimp(); if (pmap == pv->pv_pmap && va == pv->pv_va) { npv = pv->pv_next; if (npv) { @@ -849,121 +814,144 @@ pmap_remove(pmap, sva, eva) register pt_entry_t *ptp,*ptq; vm_offset_t pa; register pv_entry_t pv; - vm_offset_t asva, va; + vm_offset_t va; vm_page_t m; - int oldpte; + pt_entry_t oldpte; if (pmap == NULL) return; ptp = get_pt_entry(pmap); +/* + * special handling of removing one page. a very + * common operation and easy to short circuit some + * code. + */ + if( (sva + NBPG) == eva) { + + if( *pmap_pde( pmap, sva) == 0) + return; + + ptq = ptp + i386_btop(sva); + + if( !*ptq) + return; + /* + * Update statistics + */ + if (pmap_pte_w(ptq)) + pmap->pm_stats.wired_count--; + pmap->pm_stats.resident_count--; + + pa = pmap_pte_pa(ptq); + oldpte = *ptq; + *ptq = 0; + + if (pmap_is_managed(pa)) { + if ((((int) oldpte & PG_M) && (sva < USRSTACK || sva > UPT_MAX_ADDRESS)) + || (sva >= USRSTACK && sva < USRSTACK+(UPAGES*NBPG))) { + if (sva < pager_sva || sva >= pager_eva) { + m = PHYS_TO_VM_PAGE(pa); + m->flags &= ~PG_CLEAN; + } + } + + pv = pa_to_pvh(pa); + pmap_remove_entry(pmap, pv, sva); + pmap_unuse_pt(pmap, sva); + } + tlbflush(); + return; + } - /* this is essential since we must check the PDE(sva) for precense */ - while (sva <= eva && !pmap_pde_v(pmap_pde(pmap, sva))) - sva = (sva & PD_MASK) + (1<<PD_SHIFT); sva = i386_btop(sva); eva = i386_btop(eva); - for (; sva < eva; sva++) { + while (sva < eva) { /* * Weed out invalid mappings. * Note: we assume that the page directory table is * always allocated, and in kernel virtual. */ - if (!pmap_pde_v(pmap_pde(pmap, i386_ptob(sva)))) - { + if ( *pmap_pde(pmap, i386_ptob(sva)) == 0 ) { /* We can race ahead here, straight to next pde.. */ - sva = sva & ~((NBPG/PTESIZE) - 1); - sva = sva + NBPG/PTESIZE - 1; + nextpde: + sva = ((sva + NPTEPG) & ~(NPTEPG - 1)); continue; - } + } - ptq=ptp+sva; - + ptq = ptp + sva; /* - * search for page table entries + * search for page table entries, use string operations + * that are much faster than + * explicitly scanning when page tables are not fully + * populated. */ - if (!pmap_pte_v(ptq)) { - vm_offset_t nscan = ((sva + (NBPG/PTESIZE)) & ~((NBPG/PTESIZE) - 1)) - sva; + if ( *ptq == 0) { + vm_offset_t pdnxt = ((sva + NPTEPG) & ~(NPTEPG - 1)); + vm_offset_t nscan = pdnxt - sva; + int found = 0; + if ((nscan + sva) > eva) nscan = eva - sva; - if (nscan) { - int found; - - asm("xorl %%eax,%%eax;cld;repe;scasl;jz 1f;incl %%eax;1:;" - :"=D"(ptq),"=a"(found) - :"c"(nscan),"0"(ptq) - :"cx"); - if (found) - ptq -= 1; + asm("xorl %%eax,%%eax;cld;repe;scasl;jz 1f;incl %%eax;1:;" + :"=D"(ptq),"=a"(found) + :"c"(nscan),"0"(ptq) + :"cx"); - sva = ptq - ptp; + if( !found) { + sva = pdnxt; + continue; } - if (sva >= eva) - goto endofloop; - } - - - if (!(sva & 0x3ff)) /* Only check once in a while */ - { - if (!pmap_pde_v(pmap_pde(pmap, i386_ptob(sva)))) { - /* We can race ahead here, straight to next pde.. */ - sva = sva & ~((NBPG/PTESIZE) - 1); - sva = sva + NBPG/PTESIZE - 1; - continue; - } - } + ptq -= 1; - if (!pmap_pte_v(ptq)) - continue; + sva = ptq - ptp; + } /* * Update statistics */ - if (pmap_pte_w(ptq)) + oldpte = *ptq; + if (((int)oldpte) & PG_W) pmap->pm_stats.wired_count--; pmap->pm_stats.resident_count--; - pa = pmap_pte_pa(ptq); - oldpte = *(int *) ptq; - /* * Invalidate the PTEs. * XXX: should cluster them up and invalidate as many * as possible at once. */ - *(int *)ptq = 0; + *ptq = 0; + + va = i386_ptob(sva); /* * Remove from the PV table (raise IPL since we * may be called at interrupt time). */ - if (!pmap_is_managed(pa)) + pa = ((int)oldpte) & PG_FRAME; + if (!pmap_is_managed(pa)) { + ++sva; continue; + } - va = i386_ptob(sva); - - if (((oldpte & PG_M) && (va < USRSTACK || va > UPT_MAX_ADDRESS)) + if ((((int) oldpte & PG_M) && (va < USRSTACK || va > UPT_MAX_ADDRESS)) || (va >= USRSTACK && va < USRSTACK+(UPAGES*NBPG))) { - /* - * don't update the dirty bit for pager mappings - */ - if( va < pager_sva || va >= pager_eva) { + if (va < pager_sva || va >= pager_eva) { m = PHYS_TO_VM_PAGE(pa); m->flags &= ~PG_CLEAN; } } pv = pa_to_pvh(pa); - asva = i386_ptob(sva); - pmap_remove_entry(pmap, pv, asva); - pmap_use_pt(pmap, asva, 0); + pmap_remove_entry(pmap, pv, va); + pmap_unuse_pt(pmap, va); + ++sva; } -endofloop: tlbflush(); } @@ -973,6 +961,11 @@ endofloop: * Removes this physical page from * all physical maps in which it resides. * Reflects back modify bits to the pager. + * + * Notes: + * Original versions of this routine were very + * inefficient because they iteratively called + * pmap_remove (slow...) */ void pmap_remove_all(pa) @@ -1004,21 +997,22 @@ pmap_remove_all(pa) pte = ptp + va; if (pmap_pte_w(pte)) pmap->pm_stats.wired_count--; - if (pmap_pte_v(pte)) + if ( *pte) pmap->pm_stats.resident_count--; - if (((*(int *)pte & PG_M) && (pv->pv_va < USRSTACK || pv->pv_va > UPT_MAX_ADDRESS)) + /* + * update the vm_page_t clean bit + */ + if ( (m->flags & PG_CLEAN) && + ((((int) *pte) & PG_M) && (pv->pv_va < USRSTACK || pv->pv_va > UPT_MAX_ADDRESS)) || (pv->pv_va >= USRSTACK && pv->pv_va < USRSTACK+(UPAGES*NBPG))) { - /* - * don't update the dirty bit for pager mappings - */ - if( pv->pv_va < pager_sva || pv->pv_va >= pager_eva) { + if (pv->pv_va < pager_sva || pv->pv_va >= pager_eva) { m->flags &= ~PG_CLEAN; } } - *(int *)pte = 0; - pmap_use_pt(pmap, pv->pv_va, 0); + *pte = 0; + pmap_unuse_pt(pmap, pv->pv_va); npv = pv->pv_next; if (npv) { @@ -1027,10 +1021,8 @@ pmap_remove_all(pa) } else { pv->pv_pmap = NULL; } - } splx(s); - tlbflush(); } @@ -1049,7 +1041,6 @@ pmap_protect(pmap, sva, eva, prot) register vm_offset_t va; int i386prot; register pt_entry_t *ptp; - int reqactivate = 0; int evap = i386_btop(eva); int s; @@ -1065,70 +1056,62 @@ pmap_protect(pmap, sva, eva, prot) ptp = get_pt_entry(pmap); - for (va = sva; va < eva; va += PAGE_SIZE) { + va = sva; + while (va < eva) { + int found=0; + int svap; + vm_offset_t nscan; /* * Page table page is not allocated. * Skip it, we don't want to force allocation * of unnecessary PTE pages just to set the protection. */ - if (!pmap_pde_v(pmap_pde(pmap, va))) { + if (! *pmap_pde(pmap, va)) { /* XXX: avoid address wrap around */ +nextpde: if (va >= i386_trunc_pdr((vm_offset_t)-1)) break; - va = i386_round_pdr(va + PAGE_SIZE) - PAGE_SIZE; + va = i386_round_pdr(va + PAGE_SIZE); continue; } pte = ptp + i386_btop(va); + if( *pte == 0) { /* * scan for a non-empty pte */ - { - int found=0; - int svap = pte - ptp; - vm_offset_t nscan = - ((svap + (NBPG/PTESIZE)) & ~((NBPG/PTESIZE) - 1)) - svap; + svap = pte - ptp; + nscan = ((svap + NPTEPG) & ~(NPTEPG - 1)) - svap; + if (nscan + svap > evap) nscan = evap - svap; - if (nscan) { + + found = 0; + if (nscan) asm("xorl %%eax,%%eax;cld;repe;scasl;jz 1f;incl %%eax;1:;" :"=D"(pte),"=a"(found) :"c"(nscan),"0"(pte):"cx"); - pte -= 1; - svap = pte - ptp; + if( !found) + goto nextpde; + + pte -= 1; + svap = pte - ptp; - } - if (svap >= evap) - goto endofloop; va = i386_ptob(svap); - if (!found) - continue; } - /* - * Page not valid. Again, skip it. - * Should we do this? Or set protection anyway? - */ - if (!pmap_pte_v(pte)) - continue; - i386prot = pte_prot(pmap, prot); - - if (va < UPT_MIN_ADDRESS) + if (va < UPT_MAX_ADDRESS) { i386prot |= PG_u; - else if (va < UPT_MAX_ADDRESS) - i386prot |= PG_u | PG_RW; - - if (i386prot != pte->pg_prot) { - reqactivate = 1; - pmap_pte_set_prot(pte, i386prot); + if( va >= UPT_MIN_ADDRESS) + i386prot |= PG_RW; } + pmap_pte_set_prot(pte, i386prot); + va += PAGE_SIZE; } -endofloop: - if (reqactivate) - tlbflush(); + tlbflush(); } /* @@ -1152,10 +1135,9 @@ pmap_enter(pmap, va, pa, prot, wired) boolean_t wired; { register pt_entry_t *pte; - register int npte; + register pt_entry_t npte; vm_offset_t opa; - boolean_t cacheable = TRUE; - boolean_t checkpv = TRUE; + int cacheable=1; if (pmap == NULL) return; @@ -1167,7 +1149,7 @@ pmap_enter(pmap, va, pa, prot, wired) /* * Page Directory table entry not valid, we need a new PT page */ - if (!pmap_pde_v(pmap_pde(pmap, va))) { + if ( *pmap_pde(pmap, va) == 0) { pg("ptdi %x, va %x", pmap->pm_pdir[PTDPTDI], va); } @@ -1211,7 +1193,7 @@ pmap_enter(pmap, va, pa, prot, wired) int s; pv = pa_to_pvh(pa); - s = splhigh(); + s = splimp(); /* * No entries yet, use header as the first entry */ @@ -1232,11 +1214,12 @@ pmap_enter(pmap, va, pa, prot, wired) pv->pv_next = npv; } splx(s); + cacheable = 1; } else { - cacheable = FALSE; + cacheable = 0; } - pmap_use_pt(pmap, va, 1); + pmap_use_pt(pmap, va); /* * Increment counters @@ -1249,11 +1232,12 @@ validate: /* * Now validate mapping with desired protection/wiring. */ - npte = (pa & PG_FRAME) | pte_prot(pmap, prot) | PG_V; - - if (!cacheable) { - npte |= PG_N; - } + npte = (pt_entry_t) ( (int) (pa | pte_prot(pmap, prot) | PG_V)); + /* + * for correctness: + */ + if( !cacheable) + (int) npte |= PG_N; /* * When forking (copy-on-write, etc): @@ -1269,43 +1253,244 @@ validate: * used (referenced) bits. */ if (pa == opa) - npte |= *(int *)pte & (PG_M|PG_U); + (int) npte |= (int) *pte & (PG_M|PG_U); + if (wired) - npte |= PG_W; + (int) npte |= PG_W; if (va < UPT_MIN_ADDRESS) - npte |= PG_u; + (int) npte |= PG_u; else if (va < UPT_MAX_ADDRESS) - npte |= PG_u | PG_RW; + (int) npte |= PG_u | PG_RW; - if (npte != *(int *)pte) { - *(int *)pte = npte; + if( *pte != npte) { + *pte = npte; tlbflush(); } } /* - * pmap_page_protect: - * - * Lower the permission for all mappings to a given page. + * add a wired page to the kva */ void -pmap_page_protect(phys, prot) - vm_offset_t phys; - vm_prot_t prot; +pmap_kenter(va, pa) + vm_offset_t va; + register vm_offset_t pa; { - void pmap_copy_on_write(); - switch (prot) { - case VM_PROT_READ: - case VM_PROT_READ|VM_PROT_EXECUTE: - pmap_copy_on_write(phys); - break; - case VM_PROT_ALL: - break; - default: - pmap_remove_all(phys); - break; - } + register pt_entry_t *pte; + register pv_entry_t pv, npv; + vm_offset_t opa; + int s; + + /* + * Enter on the PV list if part of our managed memory + * Note that we raise IPL while manipulating pv_table + * since pmap_enter can be called at interrupt time. + */ + + pte = vtopte(va); + + opa = pmap_pte_pa(pte); + /* + * Mapping has not changed, must be protection or wiring change. + */ + if (opa == pa) { + /* + * Wiring change, just update stats. + * We don't worry about wiring PT pages as they remain + * resident as long as there are valid mappings in them. + * Hence, if a user page is wired, the PT page will be also. + */ + if (!pmap_pte_w(pte)) { + kernel_pmap->pm_stats.wired_count++; + } + goto validate; + } + + if (opa) { + pmap_remove(kernel_pmap, va, va + PAGE_SIZE); + } + + pv = pa_to_pvh(pa); + s = splimp(); + /* + * No entries yet, use header as the first entry + */ + if (pv->pv_pmap == NULL) { + pv->pv_va = va; + pv->pv_pmap = kernel_pmap; + pv->pv_next = NULL; + } + /* + * There is at least one other VA mapping this page. + * Place this entry after the header. + */ + else { + npv = get_pv_entry(); + npv->pv_va = va; + npv->pv_pmap = kernel_pmap; + npv->pv_next = pv->pv_next; + pv->pv_next = npv; + } + splx(s); + + /* + * Increment counters + */ + kernel_pmap->pm_stats.resident_count++; + +validate: + + /* + * Now validate mapping with desired protection/wiring. + */ + *pte = (pt_entry_t) ( (int) (pa | PG_RW | PG_V | PG_W)); +} + +/* + * this code makes some *MAJOR* assumptions: + * 1. Current pmap & pmap exists. + * 2. Not wired. + * 3. Read access. + * 4. No page table pages. + * 5. Tlbflush is deferred to calling procedure. + * 6. Page IS managed. + * but is *MUCH* faster than pmap_enter... + */ + +static inline void +pmap_enter_quick(pmap, va, pa) + register pmap_t pmap; + vm_offset_t va; + register vm_offset_t pa; +{ + register pt_entry_t *pte; + register pv_entry_t pv, npv; + int s; + + /* + * Enter on the PV list if part of our managed memory + * Note that we raise IPL while manipulating pv_table + * since pmap_enter can be called at interrupt time. + */ + + pte = vtopte(va); + if (pmap_pte_pa(pte)) { + pmap_remove(pmap, va, va + PAGE_SIZE); + } + + pv = pa_to_pvh(pa); + s = splimp(); + /* + * No entries yet, use header as the first entry + */ + if (pv->pv_pmap == NULL) { + pv->pv_va = va; + pv->pv_pmap = pmap; + pv->pv_next = NULL; + } + /* + * There is at least one other VA mapping this page. + * Place this entry after the header. + */ + else { + npv = get_pv_entry(); + npv->pv_va = va; + npv->pv_pmap = pmap; + npv->pv_next = pv->pv_next; + pv->pv_next = npv; + } + splx(s); + + pmap_use_pt(pmap, va); + + /* + * Increment counters + */ + pmap->pm_stats.resident_count++; + +validate: + + /* + * Now validate mapping with desired protection/wiring. + */ + *pte = (pt_entry_t) ( (int) (pa | PG_RO | PG_V | PG_u)); +} + +/* + * pmap_object_init_pt preloads the ptes for a given object + * into the specified pmap. This eliminates the blast of soft + * faults on process startup and immediately after an mmap. + */ +void +pmap_object_init_pt(pmap, addr, object, offset, size) + pmap_t pmap; + vm_offset_t addr; + vm_object_t object; + vm_offset_t offset; + vm_offset_t size; +{ + + vm_offset_t tmpoff; + vm_page_t p; + int s; + vm_offset_t v, lastv=0; + pt_entry_t pte; + extern vm_map_t kernel_map; + vm_offset_t objbytes; + + if (!pmap) + return; + + /* + * if we are processing a major portion of the object, then + * scan the entire thing. + */ + if( size > object->size / 2) { + objbytes = size; + p = (vm_page_t) queue_first(&object->memq); + while (!queue_end(&object->memq, (queue_entry_t) p) && objbytes != 0) { + tmpoff = p->offset; + if( tmpoff < offset) { + p = (vm_page_t) queue_next(&p->listq); + continue; + } + tmpoff -= offset; + if( tmpoff >= size) { + p = (vm_page_t) queue_next(&p->listq); + continue; + } + + if ((p->flags & (PG_BUSY|PG_FICTITIOUS)) == 0 ) { + vm_page_hold(p); + v = i386_trunc_page(((vm_offset_t)vtopte( addr+tmpoff))); + /* a fault might occur here */ + *(volatile char *)v += 0; + vm_page_unhold(p); + pmap_enter_quick(pmap, addr+tmpoff, VM_PAGE_TO_PHYS(p)); + } + p = (vm_page_t) queue_next(&p->listq); + objbytes -= NBPG; + } + } else { + /* + * else lookup the pages one-by-one. + */ + for(tmpoff = 0; tmpoff < size; tmpoff += NBPG) { + if( p = vm_page_lookup(object, tmpoff + offset)) { + if( (p->flags & (PG_BUSY|PG_FICTITIOUS)) == 0) { + vm_page_hold(p); + v = i386_trunc_page(((vm_offset_t)vtopte( addr+tmpoff))); + /* a fault might occur here */ + *(volatile char *)v += 0; + vm_page_unhold(p); + pmap_enter_quick(pmap, addr+tmpoff, VM_PAGE_TO_PHYS(p)); + } + } + } + } + + tlbflush(); } /* @@ -1343,7 +1528,7 @@ pmap_change_wiring(pmap, va, wired) * been changed by the kernel */ if (!wired) - pmap_pte_m(pte) = 1; + (int) *pte |= PG_M; } @@ -1357,14 +1542,12 @@ pmap_change_wiring(pmap, va, wired) */ void pmap_copy(dst_pmap, src_pmap, dst_addr, len, src_addr) - pmap_t dst_pmap; - pmap_t src_pmap; + pmap_t dst_pmap, src_pmap; vm_offset_t dst_addr; vm_size_t len; vm_offset_t src_addr; { } - /* * Require that all active physical maps contain no * incorrect entries NOW. [This update includes @@ -1450,20 +1633,23 @@ pmap_pageable(pmap, sva, eva, pageable) { } +/* + * this routine returns true if a physical page resides + * in the given pmap. + */ boolean_t pmap_page_exists(pmap, pa) pmap_t pmap; vm_offset_t pa; { register pv_entry_t pv; - register int *pte; int s; if (!pmap_is_managed(pa)) return FALSE; pv = pa_to_pvh(pa); - s = splhigh(); + s = splimp(); /* * Not found, check current mappings returning @@ -1481,20 +1667,25 @@ pmap_page_exists(pmap, pa) return(FALSE); } +/* + * pmap_testbit tests bits in pte's + * note that the testbit/changebit routines are inline, + * and a lot of things compile-time evaluate. + */ static inline boolean_t pmap_testbit(pa, bit) register vm_offset_t pa; int bit; { register pv_entry_t pv; - register int *pte; + pt_entry_t *pte; int s; if (!pmap_is_managed(pa)) return FALSE; pv = pa_to_pvh(pa); - s = splhigh(); + s = splimp(); /* * Not found, check current mappings returning @@ -1502,8 +1693,21 @@ pmap_testbit(pa, bit) */ if (pv->pv_pmap != NULL) { for (; pv; pv = pv->pv_next) { + /* + * if the bit being tested is the modified bit, + * then mark UPAGES as always modified, and + * ptes as never modified. + */ + if (bit & PG_U ) { + if ((pv->pv_va >= pager_sva) && (pv->pv_va < pager_eva)) { + continue; + } + } if (bit & PG_M ) { if (pv->pv_va >= USRSTACK) { + if (pv->pv_va >= pager_sva && pv->pv_va < pager_eva) { + continue; + } if (pv->pv_va < USRSTACK+(UPAGES*NBPG)) { splx(s); return TRUE; @@ -1514,8 +1718,8 @@ pmap_testbit(pa, bit) } } } - pte = (int *) pmap_pte(pv->pv_pmap, pv->pv_va); - if (*pte & bit) { + pte = pmap_pte(pv->pv_pmap, pv->pv_va); + if ((int) *pte & bit) { splx(s); return TRUE; } @@ -1525,6 +1729,9 @@ pmap_testbit(pa, bit) return(FALSE); } +/* + * this routine is used to modify bits in ptes + */ static inline void pmap_changebit(pa, bit, setem) vm_offset_t pa; @@ -1532,16 +1739,15 @@ pmap_changebit(pa, bit, setem) boolean_t setem; { register pv_entry_t pv; - register int *pte, npte; + register pt_entry_t *pte, npte; vm_offset_t va; int s; - int reqactivate = 0; if (!pmap_is_managed(pa)) return; pv = pa_to_pvh(pa); - s = splhigh(); + s = splimp(); /* * Loop over all current mappings setting/clearing as appropos @@ -1551,34 +1757,47 @@ pmap_changebit(pa, bit, setem) for (; pv; pv = pv->pv_next) { va = pv->pv_va; - /* - * XXX don't write protect pager mappings - */ - if (!setem && (bit == PG_RW)) { - extern vm_offset_t pager_sva, pager_eva; - - if (va >= pager_sva && va < pager_eva) - continue; - } + /* + * don't write protect pager mappings + */ + if (!setem && (bit == PG_RW)) { + if (va >= pager_sva && va < pager_eva) + continue; + } - pte = (int *) pmap_pte(pv->pv_pmap, va); + pte = pmap_pte(pv->pv_pmap, va); if (setem) - npte = *pte | bit; + (int) npte = (int) *pte | bit; else - npte = *pte & ~bit; - if (*pte != npte) { - *pte = npte; - tlbflush(); - } + (int) npte = (int) *pte & ~bit; + *pte = npte; } } splx(s); + tlbflush(); } /* - * Clear the modify bits on the specified physical page. + * pmap_page_protect: + * + * Lower the permission for all mappings to a given page. */ +void +pmap_page_protect(phys, prot) + vm_offset_t phys; + vm_prot_t prot; +{ + if ((prot & VM_PROT_WRITE) == 0) { + if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) + pmap_changebit(phys, PG_RW, FALSE); + else + pmap_remove_all(phys); + } +} +/* + * Clear the modify bits on the specified physical page. + */ void pmap_clear_modify(pa) vm_offset_t pa; @@ -1591,7 +1810,6 @@ pmap_clear_modify(pa) * * Clear the reference bit on the specified physical page. */ - void pmap_clear_reference(pa) vm_offset_t pa; @@ -1624,7 +1842,6 @@ boolean_t pmap_is_modified(pa) vm_offset_t pa; { - return(pmap_testbit(pa, PG_M)); } @@ -1680,32 +1897,17 @@ i386_protection_init() } #ifdef DEBUG -void -pmap_pvdump(pa) - vm_offset_t pa; -{ - register pv_entry_t pv; - - printf("pa %x", pa); - for (pv = pa_to_pvh(pa); pv; pv = pv->pv_next) { - printf(" -> pmap %x, va %x, flags %x", - pv->pv_pmap, pv->pv_va, pv->pv_flags); - pads(pv->pv_pmap); - } - printf(" "); -} - /* print address space of pmap*/ void pads(pm) pmap_t pm; { unsigned va, i, j; - struct pte *ptep; + pt_entry_t *ptep; if (pm == kernel_pmap) return; for (i = 0; i < 1024; i++) - if (pm->pm_pdir[i].pd_v) + if (pm->pm_pdir[i]) for (j = 0; j < 1024 ; j++) { va = (i<<PD_SHIFT)+(j<<PG_SHIFT); if (pm == kernel_pmap && va < KERNBASE) @@ -1718,4 +1920,23 @@ pads(pm) } ; } + +void +pmap_pvdump(pa) + vm_offset_t pa; +{ + register pv_entry_t pv; + + printf("pa %x", pa); + for (pv = pa_to_pvh(pa); pv; pv = pv->pv_next) { +#ifdef used_to_be + printf(" -> pmap %x, va %x, flags %x", + pv->pv_pmap, pv->pv_va, pv->pv_flags); +#endif + printf(" -> pmap %x, va %x", + pv->pv_pmap, pv->pv_va); + pads(pv->pv_pmap); + } + printf(" "); +} #endif diff --git a/sys/i386/i386/random.s b/sys/i386/i386/random.s new file mode 100644 index 000000000000..eab2f74475f5 --- /dev/null +++ b/sys/i386/i386/random.s @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1990,1993 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Here is a very good random number generator. This implementation is + * based on ``Two Fast Implementations of the "Minimal Standard" Random + * Number Generator'', David G. Carta, Communications of the ACM, Jan 1990, + * Vol 33 No 1. Do NOT modify this code unless you have a very thorough + * understanding of the algorithm. It's trickier than you think. If + * you do change it, make sure that its 10,000'th invocation returns + * 1043618065. + * + * Here is easier-to-decipher pseudocode: + * + * p = (16807*seed)<30:0> # e.g., the low 31 bits of the product + * q = (16807*seed)<62:31> # e.g., the high 31 bits starting at bit 32 + * if (p + q < 2^31) + * seed = p + q + * else + * seed = ((p + q) & (2^31 - 1)) + 1 + * return (seed); + * + * The result is in (0,2^31), e.g., it's always positive. + */ +#include "asm.h" + + .data +randseed: + .word 1 + .text +ENTRY(random) + movl $16807,%eax + imull randseed + shll $1,%edx + movl %eax,%ecx + and $0x7fffffff,%eax + shrl $31,%ecx + orl %ecx,%edx + addl %edx,%eax + jge 1f + incl %eax +1: + movl %eax,randseed + ret diff --git a/sys/i386/i386/support.s b/sys/i386/i386/support.s index 14808109213f..2f3aa9a10410 100644 --- a/sys/i386/i386/support.s +++ b/sys/i386/i386/support.s @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: support.s,v 1.4 1994/02/01 04:09:07 davidg Exp $ + * $Id: support.s,v 1.11 1994/06/11 02:30:05 davidg Exp $ */ #include "assym.s" /* system definitions */ @@ -75,7 +75,7 @@ ENTRY(inw) /* val = inw(port) */ ENTRY(insb) /* insb(port, addr, cnt) */ pushl %edi - movw 8(%esp),%dx + movl 8(%esp),%edx movl 12(%esp),%edi movl 16(%esp),%ecx cld @@ -88,7 +88,7 @@ ENTRY(insb) /* insb(port, addr, cnt) */ ENTRY(insw) /* insw(port, addr, cnt) */ pushl %edi - movw 8(%esp),%dx + movl 8(%esp),%edx movl 12(%esp),%edi movl 16(%esp),%ecx cld @@ -99,6 +99,19 @@ ENTRY(insw) /* insw(port, addr, cnt) */ popl %edi ret +ENTRY(insl) /* insl(port, addr, cnt) */ + pushl %edi + movl 8(%esp),%edx + movl 12(%esp),%edi + movl 16(%esp),%ecx + cld + rep + insl + NOP + movl %edi,%eax + popl %edi + ret + ENTRY(rtcin) /* rtcin(val) */ movl 4(%esp),%eax outb %al,$0x70 @@ -124,7 +137,7 @@ ENTRY(outw) /* outw(port, val) */ ENTRY(outsb) /* outsb(port, addr, cnt) */ pushl %esi - movw 8(%esp),%dx + movl 8(%esp),%edx movl 12(%esp),%esi movl 16(%esp),%ecx cld @@ -137,7 +150,7 @@ ENTRY(outsb) /* outsb(port, addr, cnt) */ ENTRY(outsw) /* outsw(port, addr, cnt) */ pushl %esi - movw 8(%esp),%dx + movl 8(%esp),%edx movl 12(%esp),%esi movl 16(%esp),%ecx cld @@ -148,11 +161,36 @@ ENTRY(outsw) /* outsw(port, addr, cnt) */ popl %esi ret +ENTRY(outsl) /* outsl(port, addr, cnt) */ + pushl %esi + movl 8(%esp),%edx + movl 12(%esp),%esi + movl 16(%esp),%ecx + cld + rep + outsl + NOP + movl %esi,%eax + popl %esi + ret + /* * bcopy family */ -/* void bzero(void *base, u_int cnt) */ + +/* + * void bzero(void *base, u_int cnt) + * Special code for I486 because stosl uses lots + * of clocks. Makes little or no difference on DX2 type + * machines, but stosl is about 1/2 as fast as + * memory moves on a standard DX !!!!! + */ ENTRY(bzero) +#if defined(I486_CPU) + cmpl $CPUCLASS_486,_cpu_class + jz 1f +#endif + pushl %edi movl 8(%esp),%edi movl 12(%esp),%ecx @@ -168,6 +206,107 @@ ENTRY(bzero) popl %edi ret +#if defined(I486_CPU) + SUPERALIGN_TEXT +1: + movl 4(%esp),%edx + movl 8(%esp),%ecx + xorl %eax,%eax +/ +/ do 64 byte chunks first +/ +/ XXX this is probably over-unrolled at least for DX2's +/ +2: + cmpl $64,%ecx + jb 3f + movl %eax,(%edx) + movl %eax,4(%edx) + movl %eax,8(%edx) + movl %eax,12(%edx) + movl %eax,16(%edx) + movl %eax,20(%edx) + movl %eax,24(%edx) + movl %eax,28(%edx) + movl %eax,32(%edx) + movl %eax,36(%edx) + movl %eax,40(%edx) + movl %eax,44(%edx) + movl %eax,48(%edx) + movl %eax,52(%edx) + movl %eax,56(%edx) + movl %eax,60(%edx) + addl $64,%edx + subl $64,%ecx + jnz 2b + ret + +/ +/ do 16 byte chunks +/ + SUPERALIGN_TEXT +3: + cmpl $16,%ecx + jb 4f + movl %eax,(%edx) + movl %eax,4(%edx) + movl %eax,8(%edx) + movl %eax,12(%edx) + addl $16,%edx + subl $16,%ecx + jnz 3b + ret + +/ +/ do 4 byte chunks +/ + SUPERALIGN_TEXT +4: + cmpl $4,%ecx + jb 5f + movl %eax,(%edx) + addl $4,%edx + subl $4,%ecx + jnz 4b + ret + +/ +/ do 1 byte chunks +/ a jump table seems to be faster than a loop or more range reductions +/ +/ XXX need a const section for non-text +/ + SUPERALIGN_TEXT +jtab: + .long do0 + .long do1 + .long do2 + .long do3 + + SUPERALIGN_TEXT +5: + jmp jtab(,%ecx,4) + + SUPERALIGN_TEXT +do3: + movw %ax,(%edx) + movb %al,2(%edx) + ret + + SUPERALIGN_TEXT +do2: + movw %ax,(%edx) + ret + + SUPERALIGN_TEXT +do1: + movb %al,(%edx) + + SUPERALIGN_TEXT +do0: + ret +#endif /* I486_CPU */ + /* fillw(pat, base, cnt) */ ENTRY(fillw) pushl %edi @@ -182,7 +321,6 @@ ENTRY(fillw) /* filli(pat, base, cnt) */ ENTRY(filli) -filli: pushl %edi movl 8(%esp),%eax movl 12(%esp),%edi @@ -232,8 +370,8 @@ bcopyw: movl 20(%esp),%ecx cmpl %esi,%edi /* potentially overlapping? */ jnb 1f - cld /* nope, copy forwards */ shrl $1,%ecx /* copy by 16-bit words */ + cld /* nope, copy forwards */ rep movsw adc %ecx,%ecx /* any bytes left? */ @@ -247,10 +385,10 @@ bcopyw: 1: addl %ecx,%edi /* copy backwards */ addl %ecx,%esi - std andl $1,%ecx /* any fractional bytes? */ decl %edi decl %esi + std rep movsb movl 20(%esp),%ecx /* copy remainder by 16-bit words */ @@ -269,7 +407,7 @@ ENTRY(bcopyx) cmpl $2,%eax je bcopyw /* not _bcopyw, to avoid multiple mcounts */ cmpl $4,%eax - je bcopy + je bcopy /* XXX the shared ret's break mexitcount */ jmp bcopyb /* @@ -286,8 +424,8 @@ bcopy: movl 20(%esp),%ecx cmpl %esi,%edi /* potentially overlapping? */ jnb 1f - cld /* nope, copy forwards */ shrl $2,%ecx /* copy by 32-bit words */ + cld /* nope, copy forwards */ rep movsl movl 20(%esp),%ecx @@ -302,10 +440,10 @@ bcopy: 1: addl %ecx,%edi /* copy backwards */ addl %ecx,%esi - std andl $3,%ecx /* any fractional bytes? */ decl %edi decl %esi + std rep movsb movl 20(%esp),%ecx /* copy remainder by 32-bit words */ @@ -395,6 +533,12 @@ ENTRY(copyout) /* copyout(from_kernel, to_user, len) */ movl %edi,%eax addl %ebx,%eax jc copyout_fault +/* + * XXX STOP USING VM_MAXUSER_ADDRESS. + * It is an end address, not a max, so every time it is used correctly it + * looks like there is an off by one error, and of course it caused an off + * by one error in several places. + */ cmpl $VM_MAXUSER_ADDRESS,%eax ja copyout_fault @@ -449,13 +593,13 @@ ENTRY(copyout) /* copyout(from_kernel, to_user, len) */ /* bcopy(%esi, %edi, %ebx) */ 3: - cld movl %ebx,%ecx shrl $2,%ecx + cld rep movsl movb %bl,%cl - andb $3,%cl /* XXX can we trust the rest of %ecx on clones? */ + andb $3,%cl rep movsb @@ -488,15 +632,22 @@ ENTRY(copyin) movl 16(%esp),%edi /* caddr_t to */ movl 20(%esp),%ecx /* size_t len */ + /* + * make sure address is valid + */ + movl %esi,%edx + addl %ecx,%edx + jc copyin_fault + cmpl $VM_MAXUSER_ADDRESS,%edx + ja copyin_fault + movb %cl,%al shrl $2,%ecx /* copy longword-wise */ cld - gs rep movsl movb %al,%cl andb $3,%cl /* copy remaining bytes */ - gs rep movsb @@ -517,39 +668,42 @@ copyin_fault: ret /* - * fu{byte,sword,word} : fetch a byte(sword, word) from user memory + * fu{byte,sword,word} : fetch a byte (sword, word) from user memory */ ALTENTRY(fuiword) ENTRY(fuword) - movl __udatasel,%ax - movl %ax,%gs movl _curpcb,%ecx movl $fusufault,PCB_ONFAULT(%ecx) - movl 4(%esp),%edx - gs + movl 4(%esp),%edx /* from */ + + cmpl $VM_MAXUSER_ADDRESS-4,%edx /* verify address is valid */ + ja fusufault + movl (%edx),%eax movl $0,PCB_ONFAULT(%ecx) ret ENTRY(fusword) - movl __udatasel,%ax - movl %ax,%gs movl _curpcb,%ecx movl $fusufault,PCB_ONFAULT(%ecx) movl 4(%esp),%edx - gs + + cmpl $VM_MAXUSER_ADDRESS-2,%edx + ja fusufault + movzwl (%edx),%eax movl $0,PCB_ONFAULT(%ecx) ret ALTENTRY(fuibyte) ENTRY(fubyte) - movl __udatasel,%ax - movl %ax,%gs movl _curpcb,%ecx movl $fusufault,PCB_ONFAULT(%ecx) movl 4(%esp),%edx - gs + + cmpl $VM_MAXUSER_ADDRESS-1,%edx + ja fusufault + movzbl (%edx),%eax movl $0,PCB_ONFAULT(%ecx) ret @@ -563,15 +717,10 @@ fusufault: ret /* - * su{byte,sword,word}: write a byte(word, longword) to user memory - */ -/* - * we only have to set the right segment selector. + * su{byte,sword,word}: write a byte (word, longword) to user memory */ ALTENTRY(suiword) ENTRY(suword) - movl __udatasel,%ax - movl %ax,%gs movl _curpcb,%ecx movl $fusufault,PCB_ONFAULT(%ecx) movl 4(%esp),%edx @@ -580,9 +729,10 @@ ENTRY(suword) #if defined(I486_CPU) || defined(I586_CPU) cmpl $CPUCLASS_386,_cpu_class - jne 2f + jne 2f /* we only have to set the right segment selector */ #endif /* I486_CPU || I586_CPU */ + /* XXX - page boundary crossing is still not handled */ movl %edx,%eax shrl $IDXSHIFT,%edx andb $0xfc,%dl @@ -603,16 +753,16 @@ ENTRY(suword) #endif 2: + cmpl $VM_MAXUSER_ADDRESS-4,%edx /* verify address validity */ + ja fusufault + movl 8(%esp),%eax - gs movl %eax,(%edx) xorl %eax,%eax movl %eax,PCB_ONFAULT(%ecx) ret ENTRY(susword) - movl __udatasel,%eax - movl %ax,%gs movl _curpcb,%ecx movl $fusufault,PCB_ONFAULT(%ecx) movl 4(%esp),%edx @@ -624,6 +774,7 @@ ENTRY(susword) jne 2f #endif /* I486_CPU || I586_CPU */ + /* XXX - page boundary crossing is still not handled */ movl %edx,%eax shrl $IDXSHIFT,%edx andb $0xfc,%dl @@ -644,8 +795,10 @@ ENTRY(susword) #endif 2: + cmpl $VM_MAXUSER_ADDRESS-2,%edx /* verify address validity */ + ja fusufault + movw 8(%esp),%ax - gs movw %ax,(%edx) xorl %eax,%eax movl %eax,PCB_ONFAULT(%ecx) @@ -684,8 +837,10 @@ ENTRY(subyte) #endif 2: + cmpl $VM_MAXUSER_ADDRESS-1,%edx /* verify address validity */ + ja fusufault + movb 8(%esp),%al - gs movb %al,(%edx) xorl %eax,%eax movl %eax,PCB_ONFAULT(%ecx) @@ -702,11 +857,12 @@ ENTRY(copyoutstr) pushl %esi pushl %edi movl _curpcb,%ecx - movl $cpystrflt,PCB_ONFAULT(%ecx) + movl $cpystrflt,PCB_ONFAULT(%ecx) /* XXX rename copyoutstr_fault */ movl 12(%esp),%esi /* %esi = from */ movl 16(%esp),%edi /* %edi = to */ movl 20(%esp),%edx /* %edx = maxlen */ + cld #if defined(I386_CPU) @@ -721,8 +877,8 @@ ENTRY(copyoutstr) * we look at a page at a time and the end address is on a page * boundary. */ - cmpl $VM_MAXUSER_ADDRESS,%edi - jae cpystrflt + cmpl $VM_MAXUSER_ADDRESS-1,%edi + ja cpystrflt movl %edi,%eax shrl $IDXSHIFT,%eax @@ -736,6 +892,7 @@ ENTRY(copyoutstr) pushl %edx pushl %edi call _trapwrite + cld popl %edi popl %edx orl %eax,%eax @@ -747,7 +904,7 @@ ENTRY(copyoutstr) movl $NBPG,%ecx subl %eax,%ecx /* ecx = NBPG - (src % NBPG) */ cmpl %ecx,%edx - jge 3f + jae 3f movl %edx,%ecx /* ecx = min(ecx, edx) */ 3: orl %ecx,%ecx @@ -780,13 +937,11 @@ ENTRY(copyoutstr) decl %edx jz 2f /* - * gs override doesn't work for stosb. Use the same explicit check - * as in copyout(). It's much slower now because it is per-char. - * XXX - however, it would be faster to rewrite this function to use + * XXX - would be faster to rewrite this function to use * strlen() and copyout(). */ - cmpl $VM_MAXUSER_ADDRESS,%edi - jae cpystrflt + cmpl $VM_MAXUSER_ADDRESS-1,%edi + ja cpystrflt lodsb stosb @@ -805,6 +960,28 @@ ENTRY(copyoutstr) #endif /* I486_CPU || I586_CPU */ /* + * This was split from copyinstr_fault mainly because pushing gs changes the + * stack offsets. It's better to have it separate for mcounting too. + */ +cpystrflt: + movl $EFAULT,%eax +cpystrflt_x: + /* set *lencopied and return %eax */ + movl _curpcb,%ecx + movl $0,PCB_ONFAULT(%ecx) + movl 20(%esp),%ecx + subl %edx,%ecx + movl 24(%esp),%edx + orl %edx,%edx + jz 1f + movl %ecx,(%edx) +1: + popl %edi + popl %esi + ret + + +/* * copyinstr(from, to, maxlen, int *lencopied) * copy a string from from to to, stop when a 0 character is reached. * return ENAMETOOLONG if string is longer than maxlen, and @@ -815,18 +992,24 @@ ENTRY(copyinstr) pushl %esi pushl %edi movl _curpcb,%ecx - movl $cpystrflt,PCB_ONFAULT(%ecx) + movl $copyinstr_fault,PCB_ONFAULT(%ecx) movl 12(%esp),%esi /* %esi = from */ movl 16(%esp),%edi /* %edi = to */ movl 20(%esp),%edx /* %edx = maxlen */ + /* + * XXX should avoid touching gs. Either copy the string in and + * check the bounds later or get its length and check the bounds + * and then use copyin(). + */ + pushl %gs movl __udatasel,%eax movl %ax,%gs incl %edx - + cld 1: decl %edx - jz 4f + jz 2f gs lodsb stosb @@ -836,26 +1019,27 @@ ENTRY(copyinstr) /* Success -- 0 byte reached */ decl %edx xorl %eax,%eax - jmp 6f -4: + jmp 3f +2: /* edx is zero -- return ENAMETOOLONG */ movl $ENAMETOOLONG,%eax - jmp 6f + jmp 3f -cpystrflt: + ALIGN_TEXT +copyinstr_fault: movl $EFAULT,%eax -cpystrflt_x: -6: +3: /* set *lencopied and return %eax */ movl _curpcb,%ecx movl $0,PCB_ONFAULT(%ecx) - movl 20(%esp),%ecx + movl 24(%esp),%ecx subl %edx,%ecx - movl 24(%esp),%edx + movl 28(%esp),%edx orl %edx,%edx - jz 7f + jz 4f movl %ecx,(%edx) -7: +4: + popl %gs popl %edi popl %esi ret @@ -872,7 +1056,7 @@ ENTRY(copystr) movl 16(%esp),%edi /* %edi = to */ movl 20(%esp),%edx /* %edx = maxlen */ incl %edx - + cld 1: decl %edx jz 4f @@ -971,15 +1155,6 @@ ENTRY(ssdtosd) popl %ebx ret -#if 0 -/* tlbflush() */ -ENTRY(tlbflush) - movl %cr3,%eax - orl $I386_CR3PAT,%eax - movl %eax,%cr3 - ret -#endif - /* load_cr0(cr0) */ ENTRY(load_cr0) movl 4(%esp),%eax @@ -991,11 +1166,6 @@ ENTRY(rcr0) movl %cr0,%eax ret -/* rcr2() */ -ENTRY(rcr2) - movl %cr2,%eax - ret - /* rcr3() */ ENTRY(rcr3) movl %cr3,%eax @@ -1037,4 +1207,3 @@ ENTRY(longjmp) xorl %eax,%eax /* return(1); */ incl %eax ret - diff --git a/sys/i386/i386/swtch.s b/sys/i386/i386/swtch.s index 0851bae3a001..f9d4f91c082c 100644 --- a/sys/i386/i386/swtch.s +++ b/sys/i386/i386/swtch.s @@ -33,15 +33,17 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: swtch.s,v 1.4 1994/01/31 10:26:59 davidg Exp $ + * $Id: swtch.s,v 1.6 1994/04/20 07:06:18 davidg Exp $ */ #include "npx.h" /* for NNPX */ #include "assym.s" /* for preprocessor defines */ #include "errno.h" /* for error codes */ -#include "i386/isa/debug.h" /* for SHOW macros */ #include "machine/asmacros.h" /* for miscellaneous assembly macros */ +#define LOCORE /* XXX inhibit C declarations */ +#include "machine/spl.h" /* for SWI_AST_MASK ... */ + /*****************************************************************************/ /* Scheduling */ @@ -132,23 +134,31 @@ rem3: .asciz "remrq" sw0: .asciz "swtch" /* - * When no processes are on the runq, Swtch branches to idle + * When no processes are on the runq, swtch() branches to _idle * to wait for something to come ready. */ ALIGN_TEXT -Idle: +_idle: + MCOUNT movl _IdlePTD,%ecx movl %ecx,%cr3 movl $tmpstk-4,%esp sti - SHOW_STI + + /* + * XXX callers of swtch() do a bogus splclock(). Locking should + * be left to swtch(). + */ + movl $SWI_AST_MASK,_cpl + testl $~SWI_AST_MASK,_ipending + je idle_loop + call _splz ALIGN_TEXT idle_loop: - call _spl0 cli cmpl $0,_whichqs - jne sw1 + jne sw1a sti hlt /* wait for interrupt */ jmp idle_loop @@ -161,9 +171,7 @@ badsw: /* * Swtch() */ - SUPERALIGN_TEXT /* so profiling doesn't lump Idle with swtch().. */ ENTRY(swtch) - incl _cnt+V_SWTCH /* switch to new process. first, save context as needed */ @@ -208,14 +216,14 @@ ENTRY(swtch) /* save is done, now choose a new process or idle */ sw1: cli - SHOW_CLI +sw1a: movl _whichqs,%edi 2: /* XXX - bsf is sloow */ bsfl %edi,%eax /* find a full q */ - je Idle /* if none, idle */ + je _idle /* if none, idle */ + /* XX update whichqs? */ -swfnd: btrl %eax,%edi /* clear q full status */ jnb 2b /* if it was clear, look for another */ movl %eax,%ebx /* save which one we are using */ @@ -296,7 +304,6 @@ swfnd: */ pushl PCB_IML(%edx) sti - SHOW_STI #if 0 call _splx #endif @@ -312,7 +319,7 @@ ENTRY(mvesp) movl %esp,%eax ret /* - * struct proc *swtch_to_inactive(p) ; struct proc *p; + * struct proc *swtch_to_inactive(struct proc *p); * * At exit of a process, move off the address space of the * process and onto a "safe" one. Then, on a temporary stack @@ -327,6 +334,7 @@ ENTRY(swtch_to_inactive) movl %ecx,%cr3 /* good bye address space */ #write buffer? movl $tmpstk-4,%esp /* temporary stack, compensated for call */ + MEXITCOUNT jmp %edx /* return, execute remainder of cleanup */ /* @@ -418,7 +426,7 @@ ENTRY(addupc) movl 8(%ebp),%eax /* pc */ subl PR_OFF(%edx),%eax /* pc -= up->pr_off */ - jl L1 /* if (pc < 0) return */ + jb L1 /* if (pc was < off) return */ shrl $1,%eax /* praddr = pc >> 1 */ imull PR_SCALE(%edx),%eax /* praddr *= up->pr_scale */ @@ -448,8 +456,3 @@ proffault: movl $0,PR_SCALE(%ecx) /* up->pr_scale = 0 */ leave ret - -/* To be done: */ -ENTRY(astoff) - ret - diff --git a/sys/i386/i386/trap.c b/sys/i386/i386/trap.c index 34a2993ba1e7..c21693c85990 100644 --- a/sys/i386/i386/trap.c +++ b/sys/i386/i386/trap.c @@ -1,4 +1,5 @@ /*- + * Copyright (C) 1994, David Greenman * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * @@ -34,11 +35,11 @@ * SUCH DAMAGE. * * from: @(#)trap.c 7.4 (Berkeley) 5/13/91 - * $Id: trap.c,v 1.17 1994/02/08 09:26:01 davidg Exp $ + * $Id: trap.c,v 1.27 1994/06/22 05:52:30 jkh Exp $ */ /* - * 386 Trap and System call handleing + * 386 Trap and System call handling */ #include "isa.h" @@ -68,28 +69,13 @@ #include "machine/trap.h" -#ifdef __GNUC__ - -/* - * The "r" contraint could be "rm" except for fatal bugs in gas. As usual, - * we omit the size from the mov instruction to avoid nonfatal bugs in gas. - */ -#define read_gs() ({ u_short gs; __asm("mov %%gs,%0" : "=r" (gs)); gs; }) -#define write_gs(newgs) __asm("mov %0,%%gs" : : "r" ((u_short) newgs)) - -#else /* not __GNUC__ */ - -u_short read_gs __P((void)); -void write_gs __P((/* promoted u_short */ int gs)); - -#endif /* __GNUC__ */ +int trap_pfault __P((struct trapframe *, int)); +void trap_fatal __P((struct trapframe *)); extern int grow(struct proc *,int); struct sysent sysent[]; int nsysent; -extern short cpl; -extern short netmask, ttymask, biomask; #define MAX_TRAP_MSG 27 char *trap_msg[] = { @@ -102,7 +88,7 @@ char *trap_msg[] = { "arithmetic trap", /* 6 T_ARITHTRAP */ "system forced exception", /* 7 T_ASTFLT */ "segmentation (limit) fault", /* 8 T_SEGFLT */ - "protection fault", /* 9 T_PROTFLT */ + "general protection fault", /* 9 T_PROTFLT */ "trace trap", /* 10 T_TRCTRAP */ "", /* 11 unused */ "page fault", /* 12 T_PAGEFLT */ @@ -123,15 +109,59 @@ char *trap_msg[] = { "stack fault", /* 27 T_STKFLT */ }; -#define pde_v(v) (PTD[((v)>>PD_SHIFT)&1023].pd_v) +static inline void +userret(p, frame, osyst) + struct proc *p; + struct trapframe *frame; + struct timeval *osyst; +{ + int sig, s; + + while (sig = CURSIG(p)) + psig(sig); + p->p_pri = p->p_usrpri; + if (want_resched) { + /* + * Since we are curproc, clock will normally just change + * our priority without moving us from one queue to another + * (since the running process is not on a queue.) + * If that happend after we put ourselves on the run queue + * but before swtch()'ed, we might not be on the queue + * indicated by our priority. + */ + s = splclock(); + setrq(p); + p->p_stats->p_ru.ru_nivcsw++; + swtch(); + splx(s); + while (sig = CURSIG(p)) + psig(sig); + } + if (p->p_stats->p_prof.pr_scale) { + int ticks; + struct timeval *tv = &p->p_stime; + + ticks = ((tv->tv_sec - osyst->tv_sec) * 1000 + + (tv->tv_usec - osyst->tv_usec) / 1000) / (tick / 1000); + if (ticks) { +#ifdef PROFTIMER + extern int profscale; + addupc(frame->tf_eip, &p->p_stats->p_prof, + ticks * profscale); +#else + addupc(frame->tf_eip, &p->p_stats->p_prof, ticks); +#endif + } + } + curpri = p->p_pri; +} /* * trap(frame): - * Exception, fault, and trap interface to BSD kernel. This - * common code is called from assembly language IDT gate entry + * Exception, fault, and trap interface to the FreeBSD kernel. + * This common code is called from assembly language IDT gate entry * routines that prepare a suitable stack frame, and restore this - * frame after the exception has been processed. Note that the - * effect is as if the arguments were passed call by reference. + * frame after the exception has been processed. */ /*ARGSUSED*/ @@ -139,361 +169,369 @@ void trap(frame) struct trapframe frame; { - register int i; - register struct proc *p = curproc; + struct proc *p = curproc; struct timeval syst; - int ucode, type, code, eva, fault_type; + int i = 0, ucode = 0, type, code, eva, fault_type; frame.tf_eflags &= ~PSL_NT; /* clear nested trap XXX */ type = frame.tf_trapno; + code = frame.tf_err; + + if (ISPL(frame.tf_cs) == SEL_UPL) { + /* user trap */ + + syst = p->p_stime; + p->p_regs = (int *)&frame; + + switch (type) { + case T_RESADFLT: /* reserved addressing fault */ + case T_PRIVINFLT: /* privileged instruction fault */ + case T_RESOPFLT: /* reserved operand fault */ + ucode = type; + i = SIGILL; + break; + + case T_BPTFLT: /* bpt instruction fault */ + case T_TRCTRAP: /* trace trap */ + frame.tf_eflags &= ~PSL_T; + i = SIGTRAP; + break; + + case T_ARITHTRAP: /* arithmetic trap */ + ucode = code; + i = SIGFPE; + break; + + case T_ASTFLT: /* Allow process switch */ + astoff(); + cnt.v_soft++; + if ((p->p_flag & SOWEUPC) && p->p_stats->p_prof.pr_scale) { + addupc(frame.tf_eip, &p->p_stats->p_prof, 1); + p->p_flag &= ~SOWEUPC; + } + goto out; + + case T_PROTFLT: /* general protection fault */ + case T_SEGNPFLT: /* segment not present fault */ + case T_STKFLT: /* stack fault */ + ucode = code + BUS_SEGM_FAULT ; + i = SIGBUS; + break; + + case T_PAGEFLT: /* page fault */ + i = trap_pfault(&frame, TRUE); + + if (i == 0) + goto out; + + ucode = T_PAGEFLT; + break; + + case T_DIVIDE: /* integer divide fault */ + ucode = FPE_INTDIV_TRAP; + i = SIGFPE; + break; + +#if NISA > 0 + case T_NMI: #if NDDB > 0 - if (curpcb && curpcb->pcb_onfault) { - if (frame.tf_trapno == T_BPTFLT - || frame.tf_trapno == T_TRCTRAP) + /* NMI can be hooked up to a pushbutton for debugging */ + printf ("NMI ... going to debugger\n"); if (kdb_trap (type, 0, &frame)) return; - } #endif - - if (curpcb == 0 || curproc == 0) - goto skiptoswitch; - if (curpcb->pcb_onfault && frame.tf_trapno != T_PAGEFLT) { - extern int _udatasel; - - if (read_gs() != (u_short) _udatasel) - /* - * Some user has corrupted %gs but we depend on it in - * copyout() etc. Fix it up and retry. - * - * (We don't preserve %fs or %gs, so users can change - * them to either _ucodesel, _udatasel or a not-present - * selector, possibly ORed with 0 to 3, making them - * volatile for other users. Not preserving them saves - * time and doesn't lose functionality or open security - * holes.) - */ - write_gs(_udatasel); - else -copyfault: - frame.tf_eip = (int)curpcb->pcb_onfault; - return; - } + /* machine/parity/power fail/"kitchen sink" faults */ + if (isa_nmi(code) == 0) return; + /* FALL THROUGH */ +#endif - syst = p->p_stime; - if (ISPL(frame.tf_cs) == SEL_UPL) { - type |= T_USER; - p->p_regs = (int *)&frame; - } + case T_OFLOW: /* integer overflow fault */ + ucode = FPE_INTOVF_TRAP; + i = SIGFPE; + break; -skiptoswitch: - ucode=0; - eva = rcr2(); - code = frame.tf_err; + case T_BOUND: /* bounds check fault */ + ucode = FPE_SUBRNG_TRAP; + i = SIGFPE; + break; - if ((type & ~T_USER) == T_PAGEFLT) - goto pfault; + case T_DNA: +#if NNPX > 0 + /* if a transparent fault (due to context switch "late") */ + if (npxdna()) + return; +#endif /* NNPX > 0 */ - switch (type) { - case T_SEGNPFLT|T_USER: - case T_STKFLT|T_USER: - case T_PROTFLT|T_USER: /* protection fault */ - ucode = code + BUS_SEGM_FAULT ; - i = SIGBUS; - break; +#if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE) + i = math_emulate(&frame); + if (i == 0) { + if (!(frame.tf_eflags & PSL_T)) + return; + frame.tf_eflags &= ~PSL_T; + i = SIGTRAP; + } + /* else ucode = emulator_only_knows() XXX */ +#else /* MATH_EMULATE || GPL_MATH_EMULATE */ + i = SIGFPE; + ucode = FPE_FPU_NP_TRAP; +#endif /* MATH_EMULATE || GPL_MATH_EMULATE */ + break; + + case T_FPOPFLT: /* FPU operand fetch fault */ + ucode = T_FPOPFLT; + i = SIGILL; + break; + + default: + trap_fatal(&frame); + } + } else { + /* kernel trap */ - case T_PRIVINFLT|T_USER: /* privileged instruction fault */ - case T_RESADFLT|T_USER: /* reserved addressing fault */ - case T_RESOPFLT|T_USER: /* reserved operand fault */ - case T_FPOPFLT|T_USER: /* coprocessor operand fault */ - ucode = type &~ T_USER; - i = SIGILL; - break; + switch (type) { + case T_PAGEFLT: /* page fault */ + (void) trap_pfault(&frame, FALSE); + return; - case T_ASTFLT|T_USER: /* Allow process switch */ - astoff(); - cnt.v_soft++; - if ((p->p_flag & SOWEUPC) && p->p_stats->p_prof.pr_scale) { - addupc(frame.tf_eip, &p->p_stats->p_prof, 1); - p->p_flag &= ~SOWEUPC; + case T_PROTFLT: /* general protection fault */ + case T_SEGNPFLT: /* segment not present fault */ + if (curpcb && curpcb->pcb_onfault) { + frame.tf_eip = (int)curpcb->pcb_onfault; + return; + } + break; + +#if NDDB > 0 + case T_BPTFLT: + case T_TRCTRAP: + if (kdb_trap (type, 0, &frame)) + return; + break; +#else + case T_TRCTRAP: /* trace trap -- someone single stepping lcall's */ + /* Q: how do we turn it on again? */ + frame.tf_eflags &= ~PSL_T; + return; +#endif + +#if NISA > 0 + case T_NMI: +#if NDDB > 0 + /* NMI can be hooked up to a pushbutton for debugging */ + printf ("NMI ... going to debugger\n"); + if (kdb_trap (type, 0, &frame)) + return; +#endif + /* machine/parity/power fail/"kitchen sink" faults */ + if (isa_nmi(code) == 0) return; + /* FALL THROUGH */ +#endif } - goto out; - case T_DNA|T_USER: -#if NNPX > 0 - /* if a transparent fault (due to context switch "late") */ - if (npxdna()) return; -#endif /* NNPX > 0 */ -#ifdef MATH_EMULATE - i = math_emulate(&frame); - if (i == 0) return; -#else /* MATH_EMULTATE */ - panic("trap: math emulation necessary!"); -#endif /* MATH_EMULTATE */ - ucode = FPE_FPU_NP_TRAP; - break; + trap_fatal(&frame); + } - case T_BOUND|T_USER: - ucode = FPE_SUBRNG_TRAP; - i = SIGFPE; - break; + trapsignal(p, i, ucode); - case T_OFLOW|T_USER: - ucode = FPE_INTOVF_TRAP; - i = SIGFPE; - break; +#ifdef DIAGNOSTIC + eva = rcr2(); + if (type <= MAX_TRAP_MSG) { + uprintf("fatal process exception: %s", + trap_msg[type]); + if ((type == T_PAGEFLT) || (type == T_PROTFLT)) + uprintf(", fault VA = 0x%x", eva); + uprintf("\n"); + } +#endif - case T_DIVIDE|T_USER: - ucode = FPE_INTDIV_TRAP; - i = SIGFPE; - break; +out: + userret(p, &frame, &syst); +} - case T_ARITHTRAP|T_USER: - ucode = code; - i = SIGFPE; - break; +int +trap_pfault(frame, usermode) + struct trapframe *frame; + int usermode; +{ + vm_offset_t va; + struct vmspace *vm; + vm_map_t map = 0; + int rv = 0, oldflags; + vm_prot_t ftype; + extern vm_map_t kernel_map; + int eva; + struct proc *p = curproc; + + eva = rcr2(); + va = trunc_page((vm_offset_t)eva); - pfault: - case T_PAGEFLT: /* allow page faults in kernel mode */ - case T_PAGEFLT|T_USER: /* page fault */ - { - vm_offset_t va; - struct vmspace *vm; - vm_map_t map = 0; - int rv = 0, oldflags; - vm_prot_t ftype; - unsigned nss, v; - extern vm_map_t kernel_map; + /* + * Don't allow user-mode faults in kernel address space + */ + if (usermode && (va >= KERNBASE)) { + goto nogo; + } + + if ((p == 0) || (va >= KERNBASE)) { + vm = 0; + map = kernel_map; + } else { + vm = p->p_vmspace; + map = &vm->vm_map; + } + + if (frame->tf_err & PGEX_W) + ftype = VM_PROT_READ | VM_PROT_WRITE; + else + ftype = VM_PROT_READ; - va = trunc_page((vm_offset_t)eva); + oldflags = p->p_flag; + if (map != kernel_map) { + vm_offset_t pa; + vm_offset_t v = (vm_offset_t) vtopte(va); + vm_page_t ptepg; /* - * Don't allow user-mode faults in kernel address space + * Keep swapout from messing with us during this + * critical time. */ - if ((type == (T_PAGEFLT|T_USER)) && (va >= KERNBASE)) { - goto nogo; - } + p->p_flag |= SLOCK; - if ((p == 0) || (type == T_PAGEFLT && va >= KERNBASE)) { - vm = 0; - map = kernel_map; - } else { - vm = p->p_vmspace; - map = &vm->vm_map; + /* + * Grow the stack if necessary + */ + if ((caddr_t)va > vm->vm_maxsaddr + && (caddr_t)va < (caddr_t)USRSTACK) { + if (!grow(p, va)) { + rv = KERN_FAILURE; + p->p_flag &= ~SLOCK; + p->p_flag |= (oldflags & SLOCK); + goto nogo; + } } - if (code & PGEX_W) - ftype = VM_PROT_READ | VM_PROT_WRITE; - else - ftype = VM_PROT_READ; - - oldflags = p->p_flag; - if (map != kernel_map) { - vm_offset_t pa; - vm_offset_t v = (vm_offset_t) vtopte(va); - - /* - * Keep swapout from messing with us during this - * critical time. - */ - p->p_flag |= SLOCK; - - /* - * Grow the stack if necessary - */ - if ((caddr_t)va > vm->vm_maxsaddr - && (caddr_t)va < (caddr_t)USRSTACK) { - if (!grow(p, va)) { - rv = KERN_FAILURE; - p->p_flag &= ~SLOCK; - p->p_flag |= (oldflags & SLOCK); - goto nogo; - } - } + /* + * Check if page table is mapped, if not, + * fault it first + */ - /* - * Check if page table is mapped, if not, - * fault it first - */ + /* Fault the pte only if needed: */ + *(volatile char *)v += 0; - /* Fault the pte only if needed: */ - *(volatile char *)v += 0; + ptepg = (vm_page_t) pmap_pte_vm_page(vm_map_pmap(map), v); + if( ptepg->hold_count == 0) + ptepg->act_count += 3; + vm_page_hold(ptepg); - /* Get the physical address: */ - pa = pmap_extract(vm_map_pmap(map), v); + /* Fault in the user page: */ + rv = vm_fault(map, va, ftype, FALSE); - /* And wire the pte page at system vm level: */ - vm_page_wire(PHYS_TO_VM_PAGE(pa)); + vm_page_unhold(ptepg); - /* Fault in the user page: */ - rv = vm_fault(map, va, ftype, FALSE); + /* + * page table pages don't need to be kept if they + * are not held + */ + if( ptepg->hold_count == 0 && ptepg->wire_count == 0) { + pmap_page_protect( VM_PAGE_TO_PHYS(ptepg), + VM_PROT_NONE); + vm_page_free(ptepg); + } - /* Unwire the pte page: */ - vm_page_unwire(PHYS_TO_VM_PAGE(pa)); - p->p_flag &= ~SLOCK; - p->p_flag |= (oldflags & SLOCK); - } else { - /* - * Since we know that kernel virtual address addresses - * always have pte pages mapped, we just have to fault - * the page. - */ - rv = vm_fault(map, va, ftype, FALSE); - } + p->p_flag &= ~SLOCK; + p->p_flag |= (oldflags & SLOCK); + } else { + /* + * Since we know that kernel virtual address addresses + * always have pte pages mapped, we just have to fault + * the page. + */ + rv = vm_fault(map, va, ftype, FALSE); + } - if (rv == KERN_SUCCESS) { - if (type == T_PAGEFLT) - return; - goto out; - } + if (rv == KERN_SUCCESS) + return (0); nogo: - if (type == T_PAGEFLT) { - if (curpcb->pcb_onfault) - goto copyfault; - - goto we_re_toast; + if (!usermode) { + if (curpcb->pcb_onfault) { + frame->tf_eip = (int)curpcb->pcb_onfault; + return (0); } - i = (rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV; + trap_fatal(frame); + } - /* kludge to pass faulting virtual address to sendsig */ - ucode = type &~ T_USER; - frame.tf_err = eva; + /* kludge to pass faulting virtual address to sendsig */ + frame->tf_err = eva; - break; - } + return((rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV); +} -#if NDDB == 0 - case T_TRCTRAP: /* trace trap -- someone single stepping lcall's */ - frame.tf_eflags &= ~PSL_T; +void +trap_fatal(frame) + struct trapframe *frame; +{ + int code, type, eva; - /* Q: how do we turn it on again? */ - return; -#endif - - case T_BPTFLT|T_USER: /* bpt instruction fault */ - case T_TRCTRAP|T_USER: /* trace trap */ - frame.tf_eflags &= ~PSL_T; - i = SIGTRAP; - break; + code = frame->tf_err; + type = frame->tf_trapno; + eva = rcr2(); -#if NISA > 0 - case T_NMI: - case T_NMI|T_USER: -#if NDDB > 0 - /* NMI can be hooked up to a pushbutton for debugging */ - printf ("NMI ... going to debugger\n"); - if (kdb_trap (type, 0, &frame)) - return; -#endif - /* machine/parity/power fail/"kitchen sink" faults */ - if (isa_nmi(code) == 0) return; - /* FALL THROUGH */ -#endif - default: - we_re_toast: - - fault_type = type & ~T_USER; - if (fault_type <= MAX_TRAP_MSG) - printf("\n\nFatal trap %d: %s while in %s mode\n", - fault_type, trap_msg[fault_type], - ISPL(frame.tf_cs) == SEL_UPL ? "user" : "kernel"); - if (fault_type == T_PAGEFLT) { - printf("fault virtual address = 0x%x\n", eva); - printf("fault code = %s %s, %s\n", - code & PGEX_U ? "user" : "supervisor", - code & PGEX_W ? "write" : "read", - code & PGEX_P ? "protection violation" : "page not present"); - } - printf("instruction pointer = 0x%x\n", frame.tf_eip); - printf("processor eflags = "); - if (frame.tf_eflags & EFL_TF) - printf("trace/trap, "); - if (frame.tf_eflags & EFL_IF) - printf("interrupt enabled, "); - if (frame.tf_eflags & EFL_NT) - printf("nested task, "); - if (frame.tf_eflags & EFL_RF) - printf("resume, "); - if (frame.tf_eflags & EFL_VM) - printf("vm86, "); - printf("IOPL = %d\n", (frame.tf_eflags & EFL_IOPL) >> 12); - printf("current process = "); - if (curproc) { - printf("%d (%s)\n", - curproc->p_pid, curproc->p_comm ? - curproc->p_comm : ""); - } else { - printf("Idle\n"); - } - printf("interrupt mask = "); - if ((cpl & netmask) == netmask) - printf("net "); - if ((cpl & ttymask) == ttymask) - printf("tty "); - if ((cpl & biomask) == biomask) - printf("bio "); - if (cpl == 0) - printf("none"); - printf("\n"); + if (type <= MAX_TRAP_MSG) + printf("\n\nFatal trap %d: %s while in %s mode\n", + type, trap_msg[type], + ISPL(frame->tf_cs) == SEL_UPL ? "user" : "kernel"); + if (type == T_PAGEFLT) { + printf("fault virtual address = 0x%x\n", eva); + printf("fault code = %s %s, %s\n", + code & PGEX_U ? "user" : "supervisor", + code & PGEX_W ? "write" : "read", + code & PGEX_P ? "protection violation" : "page not present"); + } + printf("instruction pointer = 0x%x\n", frame->tf_eip); + printf("processor eflags = "); + if (frame->tf_eflags & EFL_TF) + printf("trace/trap, "); + if (frame->tf_eflags & EFL_IF) + printf("interrupt enabled, "); + if (frame->tf_eflags & EFL_NT) + printf("nested task, "); + if (frame->tf_eflags & EFL_RF) + printf("resume, "); + if (frame->tf_eflags & EFL_VM) + printf("vm86, "); + printf("IOPL = %d\n", (frame->tf_eflags & EFL_IOPL) >> 12); + printf("current process = "); + if (curproc) { + printf("%d (%s)\n", + curproc->p_pid, curproc->p_comm ? + curproc->p_comm : ""); + } else { + printf("Idle\n"); + } + printf("interrupt mask = "); + if ((cpl & net_imask) == net_imask) + printf("net "); + if ((cpl & tty_imask) == tty_imask) + printf("tty "); + if ((cpl & bio_imask) == bio_imask) + printf("bio "); + if (cpl == 0) + printf("none"); + printf("\n"); #ifdef KDB - if (kdb_trap(&psl)) - return; + if (kdb_trap(&psl)) + return; #endif #if NDDB > 0 - if (kdb_trap (type, 0, &frame)) - return; -#endif - if (fault_type <= MAX_TRAP_MSG) - panic(trap_msg[fault_type]); - else - panic("unknown/reserved trap"); - - /* NOT REACHED */ - } - - trapsignal(p, i, ucode); - if ((type & T_USER) == 0) + if (kdb_trap (type, 0, frame)) return; -out: - while (i = CURSIG(p)) - psig(i); - p->p_pri = p->p_usrpri; - if (want_resched) { - int s; - /* - * Since we are curproc, clock will normally just change - * our priority without moving us from one queue to another - * (since the running process is not on a queue.) - * If that happened after we setrq ourselves but before we - * swtch()'ed, we might not be on the queue indicated by - * our priority. - */ - s = splclock(); - setrq(p); - p->p_stats->p_ru.ru_nivcsw++; - swtch(); - splx(s); - while (i = CURSIG(p)) - psig(i); - } - if (p->p_stats->p_prof.pr_scale) { - int ticks; - struct timeval *tv = &p->p_stime; - - ticks = ((tv->tv_sec - syst.tv_sec) * 1000 + - (tv->tv_usec - syst.tv_usec) / 1000) / (tick / 1000); - if (ticks) { -#ifdef PROFTIMER - extern int profscale; - addupc(frame.tf_eip, &p->p_stats->p_prof, - ticks * profscale); -#else - addupc(frame.tf_eip, &p->p_stats->p_prof, ticks); #endif - } - } - curpri = p->p_pri; + if (type <= MAX_TRAP_MSG) + panic(trap_msg[type]); + else + panic("unknown/reserved trap"); } /* @@ -505,7 +543,6 @@ out: int trapwrite(addr) unsigned addr; { - unsigned nss; struct proc *p; vm_offset_t va, v; struct vmspace *vm; @@ -572,21 +609,17 @@ int trapwrite(addr) /*ARGSUSED*/ void syscall(frame) - volatile struct trapframe frame; + struct trapframe frame; { - register int *locr0 = ((int *)&frame); - register caddr_t params; - register int i; - register struct sysent *callp; - register struct proc *p = curproc; + caddr_t params; + int i; + struct sysent *callp; + struct proc *p = curproc; struct timeval syst; int error, opc; int args[8], rval[2]; int code; -#ifdef lint - r0 = 0; r0 = r0; r1 = 0; r1 = r1; -#endif syst = p->p_stime; if (ISPL(frame.tf_cs) != SEL_UPL) panic("syscall"); @@ -610,13 +643,11 @@ syscall(frame) if ((i = callp->sy_narg * sizeof (int)) && (error = copyin(params, (caddr_t)args, (u_int)i))) { - frame.tf_eax = error; - frame.tf_eflags |= PSL_C; /* carry bit */ #ifdef KTRACE if (KTRPOINT(p, KTR_SYSCALL)) ktrsyscall(p->p_tracep, code, callp->sy_narg, args); #endif - goto done; + goto bad; } #ifdef KTRACE if (KTRPOINT(p, KTR_SYSCALL)) @@ -624,81 +655,40 @@ syscall(frame) #endif rval[0] = 0; rval[1] = frame.tf_edx; -/*pg("%d. s %d\n", p->p_pid, code);*/ + error = (*callp->sy_call)(p, args, rval); - if (error == ERESTART) - frame.tf_eip = opc; - else if (error != EJUSTRETURN) { - if (error) { -/*pg("error %d", error);*/ - frame.tf_eax = error; - frame.tf_eflags |= PSL_C; /* carry bit */ - } else { - frame.tf_eax = rval[0]; - frame.tf_edx = rval[1]; - frame.tf_eflags &= ~PSL_C; /* carry bit */ - } - } - /* else if (error == EJUSTRETURN) */ - /* nothing to do */ -done: - /* - * Reinitialize proc pointer `p' as it may be different - * if this is a child returning from fork syscall. - */ - p = curproc; - while (i = CURSIG(p)) - psig(i); - p->p_pri = p->p_usrpri; - if (want_resched) { - int s; + + switch (error) { + + case 0: /* - * Since we are curproc, clock will normally just change - * our priority without moving us from one queue to another - * (since the running process is not on a queue.) - * If that happened after we setrq ourselves but before we - * swtch()'ed, we might not be on the queue indicated by - * our priority. + * Reinitialize proc pointer `p' as it may be different + * if this is a child returning from fork syscall. */ - s = splclock(); - setrq(p); - p->p_stats->p_ru.ru_nivcsw++; - swtch(); - splx(s); - while (i = CURSIG(p)) - psig(i); - } - if (p->p_stats->p_prof.pr_scale) { - int ticks; - struct timeval *tv = &p->p_stime; + p = curproc; + frame.tf_eax = rval[0]; + frame.tf_edx = rval[1]; + frame.tf_eflags &= ~PSL_C; /* carry bit */ + break; - ticks = ((tv->tv_sec - syst.tv_sec) * 1000 + - (tv->tv_usec - syst.tv_usec) / 1000) / (tick / 1000); - if (ticks) { -#ifdef PROFTIMER - extern int profscale; - addupc(frame.tf_eip, &p->p_stats->p_prof, - ticks * profscale); -#else - addupc(frame.tf_eip, &p->p_stats->p_prof, ticks); -#endif - } + case ERESTART: + frame.tf_eip = opc; + break; + + case EJUSTRETURN: + break; + + default: + bad: + frame.tf_eax = error; + frame.tf_eflags |= PSL_C; /* carry bit */ + break; } - curpri = p->p_pri; + + userret(p, &frame, &syst); + #ifdef KTRACE if (KTRPOINT(p, KTR_SYSRET)) ktrsysret(p->p_tracep, code, error, rval[0]); #endif -#ifdef DIAGNOSTICx -{ extern int _udatasel, _ucodesel; - if (frame.tf_ss != _udatasel) - printf("ss %x call %d\n", frame.tf_ss, code); - if ((frame.tf_cs&0xffff) != _ucodesel) - printf("cs %x call %d\n", frame.tf_cs, code); - if (frame.tf_eip > VM_MAXUSER_ADDRESS) { - printf("eip %x call %d\n", frame.tf_eip, code); - frame.tf_eip = 0; - } -} -#endif } diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c index ac7174eb8b53..8b7e625cf41e 100644 --- a/sys/i386/i386/vm_machdep.c +++ b/sys/i386/i386/vm_machdep.c @@ -1,6 +1,7 @@ /*- * Copyright (c) 1982, 1986 The Regents of the University of California. * Copyright (c) 1989, 1990 William Jolitz + * Copyright (c) 1994 John Dyson * All rights reserved. * * This code is derived from software contributed to Berkeley by @@ -37,7 +38,7 @@ * * from: @(#)vm_machdep.c 7.3 (Berkeley) 5/13/91 * Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$ - * $Id: vm_machdep.c,v 1.11.2.1 1994/03/24 08:56:56 rgrimes Exp $ + * $Id: vm_machdep.c,v 1.28 1994/06/06 12:08:16 davidg Exp $ */ #include "npx.h" @@ -53,6 +54,837 @@ #include "vm/vm.h" #include "vm/vm_kern.h" +#define b_cylin b_resid + +#define MAXCLSTATS 256 +int clstats[MAXCLSTATS]; +int rqstats[MAXCLSTATS]; + + +#ifndef NOBOUNCE +vm_map_t io_map; +volatile int kvasfreecnt; + + +caddr_t bouncememory; +int bouncepages, bpwait; +vm_offset_t *bouncepa; +int bmwait, bmfreeing; + +#define BITS_IN_UNSIGNED (8*sizeof(unsigned)) +int bounceallocarraysize; +unsigned *bounceallocarray; +int bouncefree; + +#define SIXTEENMEG (4096*4096) +#define MAXBKVA 1024 + +/* special list that can be used at interrupt time for eventual kva free */ +struct kvasfree { + vm_offset_t addr; + vm_offset_t size; +} kvaf[MAXBKVA]; + + +vm_offset_t vm_bounce_kva(); +/* + * get bounce buffer pages (count physically contiguous) + * (only 1 inplemented now) + */ +vm_offset_t +vm_bounce_page_find(count) + int count; +{ + int bit; + int s,i; + + if (count != 1) + panic("vm_bounce_page_find -- no support for > 1 page yet!!!"); + + s = splbio(); +retry: + for (i = 0; i < bounceallocarraysize; i++) { + if (bounceallocarray[i] != 0xffffffff) { + if (bit = ffs(~bounceallocarray[i])) { + bounceallocarray[i] |= 1 << (bit - 1) ; + bouncefree -= count; + splx(s); + return bouncepa[(i * BITS_IN_UNSIGNED + (bit - 1))]; + } + } + } + bpwait = 1; + tsleep((caddr_t) &bounceallocarray, PRIBIO, "bncwai", 0); + goto retry; +} + +void +vm_bounce_kva_free(addr, size, now) + vm_offset_t addr; + vm_offset_t size; + int now; +{ + int s = splbio(); + kvaf[kvasfreecnt].addr = addr; + kvaf[kvasfreecnt].size = size; + ++kvasfreecnt; + if( now) { + /* + * this will do wakeups + */ + vm_bounce_kva(0,0); + } else { + if (bmwait) { + /* + * if anyone is waiting on the bounce-map, then wakeup + */ + wakeup((caddr_t) io_map); + bmwait = 0; + } + } + splx(s); +} + +/* + * free count bounce buffer pages + */ +void +vm_bounce_page_free(pa, count) + vm_offset_t pa; + int count; +{ + int allocindex; + int index; + int bit; + + if (count != 1) + panic("vm_bounce_page_free -- no support for > 1 page yet!!!\n"); + + for(index=0;index<bouncepages;index++) { + if( pa == bouncepa[index]) + break; + } + + if( index == bouncepages) + panic("vm_bounce_page_free: invalid bounce buffer"); + + allocindex = index / BITS_IN_UNSIGNED; + bit = index % BITS_IN_UNSIGNED; + + bounceallocarray[allocindex] &= ~(1 << bit); + + bouncefree += count; + if (bpwait) { + bpwait = 0; + wakeup((caddr_t) &bounceallocarray); + } +} + +/* + * allocate count bounce buffer kva pages + */ +vm_offset_t +vm_bounce_kva(size, waitok) + int size; + int waitok; +{ + int i; + int startfree; + vm_offset_t kva = 0; + int s = splbio(); +more: + if (!bmfreeing && kvasfreecnt) { + bmfreeing = 1; + for (i = 0; i < kvasfreecnt; i++) { + pmap_remove(kernel_pmap, + kvaf[i].addr, kvaf[i].addr + kvaf[i].size); + kmem_free_wakeup(io_map, kvaf[i].addr, + kvaf[i].size); + } + kvasfreecnt = 0; + bmfreeing = 0; + if( bmwait) { + bmwait = 0; + wakeup( (caddr_t) io_map); + } + } + + if( size == 0) { + splx(s); + return NULL; + } + + if ((kva = kmem_alloc_pageable(io_map, size)) == 0) { + if( !waitok) { + splx(s); + return NULL; + } + bmwait = 1; + tsleep((caddr_t) io_map, PRIBIO, "bmwait", 0); + goto more; + } + splx(s); + return kva; +} + +/* + * same as vm_bounce_kva -- but really allocate (but takes pages as arg) + */ +vm_offset_t +vm_bounce_kva_alloc(count) +int count; +{ + int i; + vm_offset_t kva; + vm_offset_t pa; + if( bouncepages == 0) { + kva = (vm_offset_t) malloc(count*NBPG, M_TEMP, M_WAITOK); + return kva; + } + kva = vm_bounce_kva(count*NBPG, 1); + for(i=0;i<count;i++) { + pa = vm_bounce_page_find(1); + pmap_kenter(kva + i * NBPG, pa); + } + pmap_update(); + return kva; +} + +/* + * same as vm_bounce_kva_free -- but really free + */ +void +vm_bounce_kva_alloc_free(kva, count) + vm_offset_t kva; + int count; +{ + int i; + vm_offset_t pa; + if( bouncepages == 0) { + free((caddr_t) kva, M_TEMP); + return; + } + for(i = 0; i < count; i++) { + pa = pmap_kextract(kva + i * NBPG); + vm_bounce_page_free(pa, 1); + } + vm_bounce_kva_free(kva, count*NBPG, 0); +} + +/* + * do the things necessary to the struct buf to implement + * bounce buffers... inserted before the disk sort + */ +void +vm_bounce_alloc(bp) + struct buf *bp; +{ + int countvmpg; + vm_offset_t vastart, vaend; + vm_offset_t vapstart, vapend; + vm_offset_t va, kva; + vm_offset_t pa; + int dobounceflag = 0; + int bounceindex; + int i; + int s; + + if (bouncepages == 0) + return; + + if (bp->b_flags & B_BOUNCE) { + printf("vm_bounce_alloc: called recursively???\n"); + return; + } + + if (bp->b_bufsize < bp->b_bcount) { + printf("vm_bounce_alloc: b_bufsize(0x%x) < b_bcount(0x%x) !!!!\n", + bp->b_bufsize, bp->b_bcount); + panic("vm_bounce_alloc"); + } + +/* + * This is not really necessary + * if( bp->b_bufsize != bp->b_bcount) { + * printf("size: %d, count: %d\n", bp->b_bufsize, bp->b_bcount); + * } + */ + + + vastart = (vm_offset_t) bp->b_un.b_addr; + vaend = (vm_offset_t) bp->b_un.b_addr + bp->b_bufsize; + + vapstart = i386_trunc_page(vastart); + vapend = i386_round_page(vaend); + countvmpg = (vapend - vapstart) / NBPG; + +/* + * if any page is above 16MB, then go into bounce-buffer mode + */ + va = vapstart; + for (i = 0; i < countvmpg; i++) { + pa = pmap_kextract(va); + if (pa >= SIXTEENMEG) + ++dobounceflag; + va += NBPG; + } + if (dobounceflag == 0) + return; + + if (bouncepages < dobounceflag) + panic("Not enough bounce buffers!!!"); + +/* + * allocate a replacement kva for b_addr + */ + kva = vm_bounce_kva(countvmpg*NBPG, 1); + va = vapstart; + for (i = 0; i < countvmpg; i++) { + pa = pmap_kextract(va); + if (pa >= SIXTEENMEG) { + /* + * allocate a replacement page + */ + vm_offset_t bpa = vm_bounce_page_find(1); + pmap_kenter(kva + (NBPG * i), bpa); + /* + * if we are writing, the copy the data into the page + */ + if ((bp->b_flags & B_READ) == 0) { + pmap_update(); + bcopy((caddr_t) va, (caddr_t) kva + (NBPG * i), NBPG); + } + } else { + /* + * use original page + */ + pmap_kenter(kva + (NBPG * i), pa); + } + va += NBPG; + } + pmap_update(); + +/* + * flag the buffer as being bounced + */ + bp->b_flags |= B_BOUNCE; +/* + * save the original buffer kva + */ + bp->b_savekva = bp->b_un.b_addr; +/* + * put our new kva into the buffer (offset by original offset) + */ + bp->b_un.b_addr = (caddr_t) (((vm_offset_t) kva) | + ((vm_offset_t) bp->b_savekva & (NBPG - 1))); + return; +} + +/* + * hook into biodone to free bounce buffer + */ +void +vm_bounce_free(bp) + struct buf *bp; +{ + int i; + vm_offset_t origkva, bouncekva, bouncekvaend; + int countvmpg; + int s; + +/* + * if this isn't a bounced buffer, then just return + */ + if ((bp->b_flags & B_BOUNCE) == 0) + return; + +/* + * This check is not necessary + * if (bp->b_bufsize != bp->b_bcount) { + * printf("vm_bounce_free: b_bufsize=%d, b_bcount=%d\n", + * bp->b_bufsize, bp->b_bcount); + * } + */ + + origkva = (vm_offset_t) bp->b_savekva; + bouncekva = (vm_offset_t) bp->b_un.b_addr; + +/* + * check every page in the kva space for b_addr + */ + for (i = 0; i < bp->b_bufsize; ) { + vm_offset_t mybouncepa; + vm_offset_t copycount; + + copycount = i386_round_page(bouncekva + 1) - bouncekva; + mybouncepa = pmap_kextract(i386_trunc_page(bouncekva)); + +/* + * if this is a bounced pa, then process as one + */ + if ( mybouncepa != pmap_kextract( i386_trunc_page( origkva))) { + vm_offset_t tocopy = copycount; + if (i + tocopy > bp->b_bufsize) + tocopy = bp->b_bufsize - i; +/* + * if this is a read, then copy from bounce buffer into original buffer + */ + if (bp->b_flags & B_READ) + bcopy((caddr_t) bouncekva, (caddr_t) origkva, tocopy); +/* + * free the bounce allocation + */ + vm_bounce_page_free(mybouncepa, 1); + } + + origkva += copycount; + bouncekva += copycount; + i += copycount; + } + +/* + * add the old kva into the "to free" list + */ + + bouncekva= i386_trunc_page((vm_offset_t) bp->b_un.b_addr); + bouncekvaend= i386_round_page((vm_offset_t)bp->b_un.b_addr + bp->b_bufsize); + + vm_bounce_kva_free( bouncekva, (bouncekvaend - bouncekva), 0); + bp->b_un.b_addr = bp->b_savekva; + bp->b_savekva = 0; + bp->b_flags &= ~B_BOUNCE; + + return; +} + + +/* + * init the bounce buffer system + */ +void +vm_bounce_init() +{ + vm_offset_t minaddr, maxaddr; + int i; + + io_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, MAXBKVA * NBPG, FALSE); + kvasfreecnt = 0; + + if (bouncepages == 0) + return; + + bounceallocarraysize = (bouncepages + BITS_IN_UNSIGNED - 1) / BITS_IN_UNSIGNED; + bounceallocarray = malloc(bounceallocarraysize * sizeof(unsigned), M_TEMP, M_NOWAIT); + + if (!bounceallocarray) + panic("Cannot allocate bounce resource array\n"); + + bzero(bounceallocarray, bounceallocarraysize * sizeof(unsigned)); + bouncepa = malloc(bouncepages * sizeof(vm_offset_t), M_TEMP, M_NOWAIT); + if (!bouncepa) + panic("Cannot allocate physical memory array\n"); + + for(i=0;i<bouncepages;i++) { + vm_offset_t pa; + if( (pa = pmap_kextract((vm_offset_t) bouncememory + i * NBPG)) >= SIXTEENMEG) + panic("bounce memory out of range"); + if( pa == 0) + panic("bounce memory not resident"); + bouncepa[i] = pa; + } + bouncefree = bouncepages; + +} +#endif /* NOBOUNCE */ + + +static void +cldiskvamerge( kvanew, orig1, orig1cnt, orig2, orig2cnt) + vm_offset_t kvanew; + vm_offset_t orig1, orig1cnt; + vm_offset_t orig2, orig2cnt; +{ + int i; + vm_offset_t pa; +/* + * enter the transfer physical addresses into the new kva + */ + for(i=0;i<orig1cnt;i++) { + vm_offset_t pa; + pa = pmap_kextract((caddr_t) orig1 + i * PAGE_SIZE); + pmap_kenter(kvanew + i * PAGE_SIZE, pa); + } + + for(i=0;i<orig2cnt;i++) { + vm_offset_t pa; + pa = pmap_kextract((caddr_t) orig2 + i * PAGE_SIZE); + pmap_kenter(kvanew + (i + orig1cnt) * PAGE_SIZE, pa); + } + pmap_update(); +} + +void +cldisksort(struct buf *dp, struct buf *bp, vm_offset_t maxio) +{ + register struct buf *ap, *newbp; + int i, trycount=0; + vm_offset_t orig1pages, orig2pages; + vm_offset_t orig1begin, orig2begin; + vm_offset_t kvanew, kvaorig; + + if( bp->b_bcount < MAXCLSTATS*PAGE_SIZE) + ++rqstats[bp->b_bcount/PAGE_SIZE]; + /* + * If nothing on the activity queue, then + * we become the only thing. + */ + ap = dp->b_actf; + if(ap == NULL) { + dp->b_actf = bp; + dp->b_actl = bp; + bp->av_forw = NULL; + return; + } + + + if (bp->b_flags & B_READ) { + while( ap->av_forw && (ap->av_forw->b_flags & B_READ)) + ap = ap->av_forw; + goto insert; + } + + /* + * If we lie after the first (currently active) + * request, then we must locate the second request list + * and add ourselves to it. + */ + + if (bp->b_pblkno < ap->b_pblkno) { + while (ap->av_forw) { + /* + * Check for an ``inversion'' in the + * normally ascending block numbers, + * indicating the start of the second request list. + */ + if (ap->av_forw->b_pblkno < ap->b_pblkno) { + /* + * Search the second request list + * for the first request at a larger + * block number. We go before that; + * if there is no such request, we go at end. + */ + do { + if (bp->b_pblkno < ap->av_forw->b_pblkno) + goto insert; + ap = ap->av_forw; + } while (ap->av_forw); + goto insert; /* after last */ + } + ap = ap->av_forw; + } + /* + * No inversions... we will go after the last, and + * be the first request in the second request list. + */ + goto insert; + } + /* + * Request is at/after the current request... + * sort in the first request list. + */ + while (ap->av_forw) { + /* + * We want to go after the current request + * if there is an inversion after it (i.e. it is + * the end of the first request list), or if + * the next request is a larger block than our request. + */ + if (ap->av_forw->b_pblkno < ap->b_pblkno || + bp->b_pblkno < ap->av_forw->b_pblkno ) + goto insert; + ap = ap->av_forw; + } + +insert: + +#ifndef NOBOUNCE + /* + * read clustering with new read-ahead disk drives hurts mostly, so + * we don't bother... + */ + if( bp->b_flags & (B_READ|B_SYNC)) + goto nocluster; + if( bp->b_bcount != bp->b_bufsize) { + goto nocluster; + } + /* + * we currently only cluster I/O transfers that are at page-aligned + * kvas and transfers that are multiples of page lengths. + */ + if ((bp->b_flags & B_BAD) == 0 && + ((bp->b_bcount & PAGE_MASK) == 0) && + (((vm_offset_t) bp->b_un.b_addr & PAGE_MASK) == 0)) { + if( maxio > MAXCLSTATS*PAGE_SIZE) + maxio = MAXCLSTATS*PAGE_SIZE; + /* + * merge with previous? + * conditions: + * 1) We reside physically immediately after the previous block. + * 2) The previous block is not first on the device queue because + * such a block might be active. + * 3) The mode of the two I/Os is identical. + * 4) The previous kva is page aligned and the previous transfer + * is a multiple of a page in length. + * 5) And the total I/O size would be below the maximum. + */ + if( (ap->b_pblkno + (ap->b_bcount / DEV_BSIZE) == bp->b_pblkno) && + (dp->b_actf != ap) && + ((ap->b_flags & ~(B_CLUSTER|B_BOUNCE)) == (bp->b_flags & ~B_BOUNCE)) && + ((ap->b_flags & B_BAD) == 0) && + ((ap->b_bcount & PAGE_MASK) == 0) && + (((vm_offset_t) ap->b_un.b_addr & PAGE_MASK) == 0) && + (ap->b_bcount + bp->b_bcount < maxio)) { + + /* + * something is majorly broken in the upper level + * fs code... blocks can overlap!!! this detects + * the overlap and does the right thing. + */ + if( ap->av_forw && + bp->b_pblkno + ((bp->b_bcount / DEV_BSIZE) > ap->av_forw->b_pblkno)) { + goto nocluster; + } + + orig1begin = (vm_offset_t) ap->b_un.b_addr; + orig1pages = ap->b_bcount / PAGE_SIZE; + + orig2begin = (vm_offset_t) bp->b_un.b_addr; + orig2pages = bp->b_bcount / PAGE_SIZE; + + /* + * see if we can allocate a kva, if we cannot, the don't + * cluster. + */ + kvanew = vm_bounce_kva( PAGE_SIZE * (orig1pages + orig2pages), 0); + if( !kvanew) { + goto nocluster; + } + + if( (ap->b_flags & B_CLUSTER) == 0) { + + /* + * get a physical buf pointer + */ + newbp = (struct buf *)trypbuf(); + if( !newbp) { + vm_bounce_kva_free( kvanew, PAGE_SIZE * (orig1pages + orig2pages), 1); + goto nocluster; + } + + cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages); + + /* + * build the new bp to be handed off to the device + */ + + --clstats[ap->b_bcount/PAGE_SIZE]; + *newbp = *ap; + newbp->b_flags |= B_CLUSTER; + newbp->b_un.b_addr = (caddr_t) kvanew; + newbp->b_bcount += bp->b_bcount; + newbp->b_bufsize = newbp->b_bcount; + newbp->b_clusterf = ap; + newbp->b_clusterl = bp; + ++clstats[newbp->b_bcount/PAGE_SIZE]; + + /* + * enter the new bp onto the device queue + */ + if( ap->av_forw) + ap->av_forw->av_back = newbp; + else + dp->b_actl = newbp; + + if( dp->b_actf != ap ) + ap->av_back->av_forw = newbp; + else + dp->b_actf = newbp; + + /* + * enter the previous bps onto the cluster queue + */ + ap->av_forw = bp; + bp->av_back = ap; + + ap->av_back = NULL; + bp->av_forw = NULL; + + } else { + vm_offset_t addr; + + cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages); + /* + * free the old kva + */ + vm_bounce_kva_free( orig1begin, ap->b_bufsize, 0); + --clstats[ap->b_bcount/PAGE_SIZE]; + + ap->b_un.b_addr = (caddr_t) kvanew; + + ap->b_clusterl->av_forw = bp; + bp->av_forw = NULL; + bp->av_back = ap->b_clusterl; + ap->b_clusterl = bp; + + ap->b_bcount += bp->b_bcount; + ap->b_bufsize = ap->b_bcount; + ++clstats[ap->b_bcount/PAGE_SIZE]; + } + return; + /* + * merge with next? + * conditions: + * 1) We reside physically before the next block. + * 3) The mode of the two I/Os is identical. + * 4) The next kva is page aligned and the next transfer + * is a multiple of a page in length. + * 5) And the total I/O size would be below the maximum. + */ + } else if( ap->av_forw && + (bp->b_pblkno + (bp->b_bcount / DEV_BSIZE) == ap->av_forw->b_pblkno) && + ((bp->b_flags & ~B_BOUNCE) == (ap->av_forw->b_flags & ~(B_CLUSTER|B_BOUNCE))) && + ((ap->av_forw->b_flags & B_BAD) == 0) && + ((ap->av_forw->b_bcount & PAGE_MASK) == 0) && + (((vm_offset_t) ap->av_forw->b_un.b_addr & PAGE_MASK) == 0) && + (ap->av_forw->b_bcount + bp->b_bcount < maxio)) { + + /* + * something is majorly broken in the upper level + * fs code... blocks can overlap!!! this detects + * the overlap and does the right thing. + */ + if( (ap->b_pblkno + (ap->b_bcount / DEV_BSIZE)) > bp->b_pblkno) { + goto nocluster; + } + + orig1begin = (vm_offset_t) bp->b_un.b_addr; + orig1pages = bp->b_bcount / PAGE_SIZE; + + orig2begin = (vm_offset_t) ap->av_forw->b_un.b_addr; + orig2pages = ap->av_forw->b_bcount / PAGE_SIZE; + + /* + * see if we can allocate a kva, if we cannot, the don't + * cluster. + */ + kvanew = vm_bounce_kva( PAGE_SIZE * (orig1pages + orig2pages), 0); + if( !kvanew) { + goto nocluster; + } + + /* + * if next isn't a cluster we need to create one + */ + if( (ap->av_forw->b_flags & B_CLUSTER) == 0) { + + /* + * get a physical buf pointer + */ + newbp = (struct buf *)trypbuf(); + if( !newbp) { + vm_bounce_kva_free( kvanew, PAGE_SIZE * (orig1pages + orig2pages), 1); + goto nocluster; + } + + cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages); + ap = ap->av_forw; + --clstats[ap->b_bcount/PAGE_SIZE]; + *newbp = *ap; + newbp->b_flags |= B_CLUSTER; + newbp->b_un.b_addr = (caddr_t) kvanew; + newbp->b_blkno = bp->b_blkno; + newbp->b_pblkno = bp->b_pblkno; + newbp->b_bcount += bp->b_bcount; + newbp->b_bufsize = newbp->b_bcount; + newbp->b_clusterf = bp; + newbp->b_clusterl = ap; + ++clstats[newbp->b_bcount/PAGE_SIZE]; + + if( ap->av_forw) + ap->av_forw->av_back = newbp; + else + dp->b_actl = newbp; + + if( dp->b_actf != ap ) + ap->av_back->av_forw = newbp; + else + dp->b_actf = newbp; + + bp->av_forw = ap; + ap->av_back = bp; + + bp->av_back = NULL; + ap->av_forw = NULL; + } else { + vm_offset_t addr; + + cldiskvamerge( kvanew, orig1begin, orig1pages, orig2begin, orig2pages); + ap = ap->av_forw; + vm_bounce_kva_free( orig2begin, ap->b_bufsize, 0); + + ap->b_un.b_addr = (caddr_t) kvanew; + bp->av_forw = ap->b_clusterf; + ap->b_clusterf->av_back = bp; + ap->b_clusterf = bp; + bp->av_back = NULL; + --clstats[ap->b_bcount/PAGE_SIZE]; + + ap->b_blkno = bp->b_blkno; + ap->b_pblkno = bp->b_pblkno; + ap->b_bcount += bp->b_bcount; + ap->b_bufsize = ap->b_bcount; + ++clstats[ap->b_bcount/PAGE_SIZE]; + + } + return; + } + } +#endif + /* + * don't merge + */ +nocluster: + ++clstats[bp->b_bcount/PAGE_SIZE]; + bp->av_forw = ap->av_forw; + if( bp->av_forw) + bp->av_forw->av_back = bp; + else + dp->b_actl = bp; + + ap->av_forw = bp; + bp->av_back = ap; +} + +/* + * quick version of vm_fault + */ + +void +vm_fault_quick( v, prot) + vm_offset_t v; + int prot; +{ + if( (cpu_class == CPUCLASS_386) && + (prot & VM_PROT_WRITE)) + vm_fault(&curproc->p_vmspace->vm_map, v, + VM_PROT_READ|VM_PROT_WRITE, FALSE); + else if( prot & VM_PROT_WRITE) + *(volatile char *)v += 0; + else + *(volatile char *)v; +} + + /* * Finish a fork operation, with process p2 nearly set up. * Copy and update the kernel stack and pcb, making the child @@ -213,32 +1045,6 @@ setredzone(pte, vaddr) } /* - * Move pages from one kernel virtual address to another. - * Both addresses are assumed to reside in the Sysmap, - * and size must be a multiple of CLSIZE. - */ -void -pagemove(from, to, size) - register caddr_t from, to; - int size; -{ - register struct pte *fpte, *tpte; - - if (size % CLBYTES) - panic("pagemove"); - fpte = kvtopte(from); - tpte = kvtopte(to); - while (size > 0) { - *tpte++ = *fpte; - *(int *)fpte++ = 0; - from += NBPG; - to += NBPG; - size -= NBPG; - } - tlbflush(); -} - -/* * Convert kernel VA to physical address */ u_long @@ -246,7 +1052,7 @@ kvtop(void *addr) { vm_offset_t va; - va = pmap_extract(kernel_pmap, (vm_offset_t)addr); + va = pmap_kextract((vm_offset_t)addr); if (va == 0) panic("kvtop: zero page frame"); return((int)va); @@ -255,22 +1061,11 @@ kvtop(void *addr) extern vm_map_t phys_map; /* - * Map an IO request into kernel virtual address space. Requests fall into - * one of five catagories: - * - * B_PHYS|B_UAREA: User u-area swap. - * Address is relative to start of u-area (p_addr). - * B_PHYS|B_PAGET: User page table swap. - * Address is a kernel VA in usrpt (Usrptmap). - * B_PHYS|B_DIRTY: Dirty page push. - * Address is a VA in proc2's address space. - * B_PHYS|B_PGIN: Kernel pagein of user pages. - * Address is VA in user's address space. - * B_PHYS: User "raw" IO request. - * Address is VA in user's address space. + * Map an IO request into kernel virtual address space. * - * All requests are (re)mapped into kernel VA space via the useriomap - * (a name with only slightly more meaning than "kernelmap") + * All requests are (re)mapped into kernel VA space. + * Notice that we use b_bufsize for the size of the buffer + * to be mapped. b_bcount might be modified by the driver. */ void vmapbuf(bp) @@ -289,18 +1084,18 @@ vmapbuf(bp) addr = bp->b_saveaddr = bp->b_un.b_addr; off = (int)addr & PGOFSET; p = bp->b_proc; - npf = btoc(round_page(bp->b_bcount + off)); + npf = btoc(round_page(bp->b_bufsize + off)); kva = kmem_alloc_wait(phys_map, ctob(npf)); bp->b_un.b_addr = (caddr_t) (kva + off); while (npf--) { pa = pmap_extract(&p->p_vmspace->vm_pmap, (vm_offset_t)addr); if (pa == 0) panic("vmapbuf: null page frame"); - pmap_enter(vm_map_pmap(phys_map), kva, trunc_page(pa), - VM_PROT_READ|VM_PROT_WRITE, TRUE); + pmap_kenter(kva, trunc_page(pa)); addr += PAGE_SIZE; kva += PAGE_SIZE; } + pmap_update(); } /* @@ -317,7 +1112,7 @@ vunmapbuf(bp) if ((bp->b_flags & B_PHYS) == 0) panic("vunmapbuf"); - npf = btoc(round_page(bp->b_bcount + ((int)addr & PGOFSET))); + npf = btoc(round_page(bp->b_bufsize + ((int)addr & PGOFSET))); kva = (vm_offset_t)((int)addr & ~PGOFSET); kmem_free_wakeup(phys_map, kva, ctob(npf)); bp->b_un.b_addr = bp->b_saveaddr; diff --git a/sys/i386/include/ansi.h b/sys/i386/include/ansi.h index 3a54968b1595..c932ba7a598b 100644 --- a/sys/i386/include/ansi.h +++ b/sys/i386/include/ansi.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)ansi.h 7.1 (Berkeley) 3/9/91 - * $Id: ansi.h,v 1.2 1993/10/16 14:39:05 rgrimes Exp $ + * $Id: ansi.h,v 1.3 1994/04/04 21:11:11 wollman Exp $ */ #ifndef _ANSI_H_ @@ -53,6 +53,23 @@ #define _SIZE_T_ unsigned int /* sizeof() */ #define _TIME_T_ long /* time() */ #define _VA_LIST_ char * /* va_list */ -#define _WCHAR_T_ unsigned short /* wchar_t */ + +/* + * Runes (wchar_t) is declared to be an ``int'' instead of the more natural + * ``unsigned long'' or ``long''. Two things are happening here. It is not + * unsigned so that EOF (-1) can be naturally assigned to it and used. Also, + * it looks like 10646 will be a 31 bit standard. This means that if your + * ints cannot hold 32 bits, you will be in trouble. The reason an int was + * chosen over a long is that the is*() and to*() routines take ints (says + * ANSI C), but they use _RUNE_T_ instead of int. By changing it here, you + * lose a bit of ANSI conformance, but your programs will still work. + * + * Note that _WCHAR_T_ and _RUNE_T_ must be of the same type. When wchar_t + * and rune_t are typedef'd, _WCHAR_T_ will be undef'd, but _RUNE_T remains + * defined for ctype.h. + */ +#define _BSD_WCHAR_T_ int /* wchar_t */ +#define _BSD_RUNE_T_ int /* rune_t */ + #endif /* _ANSI_H_ */ diff --git a/sys/i386/include/asmacros.h b/sys/i386/include/asmacros.h index f0f2c014178c..4af0b97a8abf 100644 --- a/sys/i386/include/asmacros.h +++ b/sys/i386/include/asmacros.h @@ -5,6 +5,11 @@ #define GEN_ENTRY(name) ALIGN_TEXT; .globl name; name: #define NON_GPROF_ENTRY(name) GEN_ENTRY(_/**/name) +/* These three are place holders for future changes to the profiling code */ +#define MCOUNT_LABEL(name) +#define MEXITCOUNT +#define FAKE_MCOUNT(caller) + #ifdef GPROF /* * ALTENTRY() must be before a corresponding ENTRY() so that it can jump @@ -30,6 +35,7 @@ */ #define ALTENTRY(name) GEN_ENTRY(_/**/name) #define ENTRY(name) GEN_ENTRY(_/**/name) +#define MCOUNT #endif diff --git a/sys/i386/include/console.h b/sys/i386/include/console.h index 011889032ecc..c52a3943c764 100644 --- a/sys/i386/include/console.h +++ b/sys/i386/include/console.h @@ -14,7 +14,7 @@ * DK9210 Aalborg SO Phone: +45 9814 8076 * * from:@(#)console.h 1.1 940105 - * $Id: console.h,v 1.7 1994/02/04 10:35:29 chmr Exp $ + * $Id: console.h,v 1.9 1994/05/20 12:21:49 sos Exp $ */ #ifndef _CONSOLE_H_ @@ -55,7 +55,7 @@ #define GIO_FONT8x14 _IOR('c', 67, fnt14_t) #define PIO_FONT8x16 _IOW('c', 68, fnt16_t) #define GIO_FONT8x16 _IOR('c', 69, fnt16_t) -#define CONS_GETINFO _IOR('c', 73, vid_info_t) +#define CONS_GETINFO _IOWR('c', 73, vid_info_t) #define CONS_GETVERS _IOR('c', 74, long) #define CONS_80x25TEXT _IO('c', 102) #define CONS_80x50TEXT _IO('c', 103) @@ -124,16 +124,19 @@ struct vt_mode { #define NUM_STATES 8 /* states per key */ #define ALTGR_OFFSET 128 /* offset for altlock keys */ +struct key_t { + u_char map[NUM_STATES]; + u_char spcl; + u_char flgs; +}; + struct keymap { u_short n_keys; - struct key_t { - u_char map[NUM_STATES]; - u_char spcl; - u_char flgs; - } key[NUM_KEYS]; + struct key_t key[NUM_KEYS]; }; #define MAXFK 16 +#define NUM_FKEYS 60 struct fkeytab { u_char str[MAXFK]; @@ -190,6 +193,7 @@ typedef struct ssaver ssaver_t; #define NLK 0x05 /* num lock key */ #define SLK 0x06 /* scroll lock key */ #define LALT 0x07 /* left alt key */ +#define BTAB 0x08 /* backwards tab */ #define LCTR 0x09 /* left control key */ #define NEXT 0x0a /* switch to next screen */ #define F_SCR 0x0b /* switch to first screen */ @@ -214,11 +218,14 @@ typedef struct ssaver ssaver_t; #define KB_STAT 0x64 /* kbd status port */ #define KB_BUF_FULL 0x01 /* kbd has char pending */ #define KB_READY 0x02 /* kbd ready for command */ +#define KB_MODE 0x4D /* kbd mode (trans, ints enable)*/ #define KB_WRITE 0x60 /* kbd write command */ -#define KB_SETLEDS 0xed /* kbd set leds */ -#define KB_SETRAD 0xf3 /* kbd set repeat&delay command */ -#define KB_ACK 0xfa /* kbd acknowledge answer */ -#define KB_RESET_CPU 0xfe /* kbd reset main cpu command */ -#define KB_RESET 0xff /* kbd reset */ +#define KB_RESET_DONE 0xAA /* kbd reset command completed */ +#define KB_SETLEDS 0xED /* kbd set leds */ +#define KB_ECHO 0xEE /* kbd set leds */ +#define KB_SETRAD 0xF3 /* kbd set repeat&delay command */ +#define KB_ACK 0xFA /* kbd acknowledge answer */ +#define KB_RESEND 0xFE /* kbd resend cmd answer */ +#define KB_RESET 0xFF /* kbd reset */ #endif diff --git a/sys/i386/include/cpu.h b/sys/i386/include/cpu.h index 184f5b86151d..82ab4c3734dc 100644 --- a/sys/i386/include/cpu.h +++ b/sys/i386/include/cpu.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)cpu.h 5.4 (Berkeley) 5/9/91 - * $Id: cpu.h,v 1.4 1993/11/07 17:42:46 wollman Exp $ + * $Id: cpu.h,v 1.5 1994/04/02 07:00:35 davidg Exp $ */ #ifndef _MACHINE_CPU_H_ @@ -58,18 +58,21 @@ * Arguments to hardclock, softclock and gatherstats * encapsulate the previous machine state in an opaque * clockframe; for now, use generic intrframe. + * XXX softclock() has been fixed. It never needed a + * whole frame, only a usermode flag, at least on this + * machine. Fix the rest. */ typedef struct intrframe clockframe; #define CLKF_USERMODE(framep) (ISPL((framep)->if_cs) == SEL_UPL) -#define CLKF_BASEPRI(framep) ((framep)->if_ppl == 0) +#define CLKF_BASEPRI(framep) (((framep)->if_ppl & ~SWI_AST_MASK) == 0) #define CLKF_PC(framep) ((framep)->if_eip) /* * Preempt the current process if in interrupt from user mode, * or after the current trap/syscall if in system mode. */ -#define need_resched() { want_resched++; aston(); } +#define need_resched() { want_resched = 1; aston(); } /* * Give a profiling tick to the current process from the softclock @@ -84,7 +87,8 @@ typedef struct intrframe clockframe; */ #define signotify(p) aston() -#define aston() (astpending++) +#define aston() setsoftast() +#define astoff() /* * pull in #defines for kinds of processors @@ -97,7 +101,6 @@ struct cpu_nameclass { }; #ifdef KERNEL -extern int astpending; /* want a trap before returning to user mode */ extern int want_resched; /* resched was called */ extern int cpu; diff --git a/sys/i386/include/cpufunc.h b/sys/i386/include/cpufunc.h index 65cc855793f0..16bb63b86a17 100644 --- a/sys/i386/include/cpufunc.h +++ b/sys/i386/include/cpufunc.h @@ -2,7 +2,7 @@ * Functions to provide access to special i386 instructions. * XXX - bezillions more are defined in locore.s but are not declared anywhere. * - * $Id: cpufunc.h,v 1.9 1994/01/31 23:48:23 davidg Exp $ + * $Id: cpufunc.h,v 1.11 1994/06/01 03:09:51 davidg Exp $ */ #ifndef _MACHINE_CPUFUNC_H_ @@ -11,6 +11,8 @@ #include <sys/cdefs.h> #include <sys/types.h> +#include "machine/spl.h" + #ifdef __GNUC__ static inline int bdb(void) @@ -69,6 +71,14 @@ tlbflush() __asm __volatile("movl %%cr3, %%eax; movl %%eax, %%cr3" : : : "ax"); } +static inline u_long +rcr2() +{ + u_long data; + __asm __volatile("movl %%cr2,%%eax" : "=a" (data)); + return data; +} + static inline int imin(a, b) @@ -224,7 +234,6 @@ void load_cr0 __P((u_int cr0)); u_int rcr0 __P((void)); void load_cr3(u_long); u_long rcr3(void); -u_long rcr2(void); void setidt __P((int, void (*)(), int, int)); extern u_long kvtop(void *); diff --git a/sys/i386/include/ioctl_fd.h b/sys/i386/include/ioctl_fd.h index 2e3ac3104726..a8c17f69ac4a 100644 --- a/sys/i386/include/ioctl_fd.h +++ b/sys/i386/include/ioctl_fd.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1992-1993 by Joerg Wunsch, Dresden + * Copyright (C) 1992-1994 by Joerg Wunsch, Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,17 +11,18 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. */ #ifndef _IOCTL_FD_H @@ -94,5 +95,11 @@ struct fd_type { #define FD_FORM _IOW('F', 61, struct fd_formb) /* format a track */ #define FD_GTYPE _IOR('F', 62, struct fd_type) /* get drive type */ +#define FD_STYPE _IOW('F', 63, struct fd_type) /* set drive type */ + +#define FD_GOPTS _IOR('F', 64, int) /* drive options, see below */ +#define FD_SOPTS _IOW('F', 65, int) + +#define FDOPT_NORETRY 0x0001 /* no retries on failure (cleared on close) */ #endif /* !def _IOCTL_FD_H */ diff --git a/sys/i386/include/limits.h b/sys/i386/include/limits.h index 568ad40848a3..586d6e2f5a69 100644 --- a/sys/i386/include/limits.h +++ b/sys/i386/include/limits.h @@ -31,15 +31,14 @@ * SUCH DAMAGE. * * from: @(#)limits.h 7.2 (Berkeley) 6/28/90 - * $Id: limits.h,v 1.4 1993/12/19 05:14:46 alm Exp $ + * $Id: limits.h,v 1.6 1994/04/04 21:11:12 wollman Exp $ */ #ifndef _MACHINE_LIMITS_H_ #define _MACHINE_LIMITS_H_ 1 #define CHAR_BIT 8 /* number of bits in a char */ -#define CLK_TCK 60 /* ticks per second */ -#define MB_LEN_MAX 1 /* no multibyte characters */ +#define MB_LEN_MAX 6 /* allow 21-bit UTF2 */ #define SCHAR_MIN (-0x7f-1) /* max value for a signed char */ #define SCHAR_MAX 0x7f /* min value for a signed char */ @@ -61,6 +60,7 @@ #define LONG_MIN (-0x7fffffff-1) /* min value for a long */ #if !defined(_ANSI_SOURCE) && !defined(_POSIX_SOURCE) +#define CLK_TCK 128 /* ticks per second */ #define UQUAD_MAX 0xffffffffffffffffLL /* max unsigned quad */ #define QUAD_MAX 0x7fffffffffffffffLL /* max signed quad */ #define QUAD_MIN (-0x7fffffffffffffffLL-1) /* min signed quad */ diff --git a/sys/i386/include/lpt.h b/sys/i386/include/lpt.h new file mode 100644 index 000000000000..87af5bcc5875 --- /dev/null +++ b/sys/i386/include/lpt.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 1994 Geoffrey M. Rehmet + * + * This program is free software; you may redistribute it and/or + * modify it, provided that it retain the above copyright notice + * and the following disclaimer. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Geoff Rehmet, Rhodes University, South Africa <csgr@cs.ru.ac.za> + * + */ + +#ifndef _LPT_PRINTER_H_ +#define _LPT_PRINTER_H_ + +#include <sys/types.h> +#include <sys/ioctl.h> + +#define LPT_IRQ _IOW('p', 1, long) /* set interrupt status */ + +#endif diff --git a/sys/i386/include/mouse.h b/sys/i386/include/mouse.h new file mode 100644 index 000000000000..95a66e474c6d --- /dev/null +++ b/sys/i386/include/mouse.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 1992, 1993 Erik Forsberg. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: mouse.h,v 1.1 1994/05/17 14:05:31 jkh Exp $ + */ + +struct mouseinfo { + unsigned char status; + char xmotion, ymotion; +}; + +#define BUTSTATMASK 0x07 /* Any mouse button down if any bit set */ +#define BUTCHNGMASK 0x38 /* Any mouse button changed if any bit set */ + +#define BUT3STAT 0x01 /* Button 3 down if set */ +#define BUT2STAT 0x02 /* Button 2 down if set */ +#define BUT1STAT 0x04 /* Button 1 down if set */ +#define BUT3CHNG 0x08 /* Button 3 changed if set */ +#define BUT2CHNG 0x10 /* Button 2 changed if set */ +#define BUT1CHNG 0x20 /* Button 1 changed if set */ +#define MOVEMENT 0x40 /* Mouse movement detected */ + +/* Ioctl definitions */ + +#define MOUSEIOC ('M'<<8) +#define MOUSEIOCREAD (MOUSEIOC|60) diff --git a/sys/i386/include/npx.h b/sys/i386/include/npx.h index ec29bf4fa024..168ef19fadd2 100644 --- a/sys/i386/include/npx.h +++ b/sys/i386/include/npx.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)npx.h 5.3 (Berkeley) 1/18/91 - * $Id: npx.h,v 1.2 1993/10/16 14:39:22 rgrimes Exp $ + * $Id: npx.h,v 1.3 1994/04/29 21:44:23 gclarkii Exp $ */ /* @@ -73,11 +73,13 @@ struct fpacc87 { struct save87 { struct env87 sv_env; /* floating point control/status */ struct fpacc87 sv_ac[8]; /* accumulator contents, 0-7 */ -#ifndef dontdef u_long sv_ex_sw; /* status word for last exception (was pad) */ u_long sv_ex_tw; /* tag word for last exception (was pad) */ - u_char sv_pad[8 * 2 - 2 * 4]; /* bogus historical padding */ -#endif +#ifdef GPL_MATH_EMULATE + u_char sv_pad[60]; +#else + u_char sv_pad[8 * 2 - 2 * 4]; /* bogus historical padding */ +#endif /* GPL_MATH_EMULATE */ }; /* Cyrix EMC memory - mapped coprocessor context switch information */ diff --git a/sys/i386/include/param.h b/sys/i386/include/param.h index fe97ad809b25..953ebb9d36c4 100644 --- a/sys/i386/include/param.h +++ b/sys/i386/include/param.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)param.h 5.8 (Berkeley) 6/28/91 - * $Id: param.h,v 1.13 1994/01/31 04:18:54 davidg Exp $ + * $Id: param.h,v 1.14 1994/03/07 11:38:47 davidg Exp $ */ #ifndef _MACHINE_PARAM_H_ @@ -62,7 +62,7 @@ #define PAGE_SIZE (1 << PAGE_SHIFT) #define PAGE_MASK (PAGE_SIZE-1) #define PGOFSET (NBPG-1) /* byte offset into page */ -#define NPTEPG (NBPG/(sizeof (struct pte))) +#define NPTEPG (NBPG/(sizeof (pt_entry_t))) /* XXX PDRSHIFT and PD_SHIFT are two names for the same thing */ #define PDRSHIFT 22 /* LOG2(NBPDR) */ @@ -103,7 +103,7 @@ #endif /* MSIZE */ #ifndef MCLSHIFT -#define MCLSHIFT 11 /* convert bytes to m_buf clusters */ +#define MCLSHIFT 12 /* convert bytes to m_buf clusters */ #endif /* MCLSHIFT */ #define MCLBYTES (1 << MCLSHIFT) /* size of an m_buf cluster */ #define MCLOFSET (MCLBYTES - 1) /* offset within an m_buf cluster */ diff --git a/sys/i386/include/pcaudioio.h b/sys/i386/include/pcaudioio.h new file mode 100644 index 000000000000..71c7ebb4c226 --- /dev/null +++ b/sys/i386/include/pcaudioio.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 1994 Søren Schmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: pcaudioio.h,v 1.2 1994/05/20 12:22:40 sos Exp $ + */ + +#ifndef _PCAUDIOIO_H_ +#define _PCAUDIOIO_H_ + +typedef struct audio_prinfo { + unsigned sample_rate; /* samples per second */ + unsigned channels; /* # of channels (interleaved) */ + unsigned precision; /* sample size in bits */ + unsigned encoding; /* encoding method used */ + + unsigned gain; /* volume level: 0 - 255 */ + unsigned port; /* input/output device */ + unsigned _fill1[4]; + + unsigned samples; /* samples played */ + unsigned eof; /* ?!? */ + unsigned char pause; /* !=0 pause, ==0 continue */ + unsigned char error; /* !=0 if overflow/underflow */ + unsigned char waiting; /* !=0 if others wants access */ + unsigned char _fill2[3]; + + unsigned char open; /* is device open */ + unsigned char active; /* !=0 if sound hardware is active */ +} audio_prinfo_t; + +typedef struct audio_info { + audio_prinfo_t play; + audio_prinfo_t record; + unsigned monitor_gain; + unsigned _fill[4]; +} audio_info_t; + +#define AUDIO_ENCODING_ULAW (1) /* u-law encoding */ +#define AUDIO_ENCODING_ALAW (2) /* A-law encoding */ +#define AUDIO_ENCODING_RAW (3) /* linear encoding */ + +#define AUDIO_MIN_GAIN (0) /* minimum volume value */ +#define AUDIO_MAX_GAIN (255) /* maximum volume value */ + +#define AUDIO_INITINFO(i) memset((void*)i, 0xff, sizeof(audio_info_t)) + +#define AUDIO_GETINFO _IOR('A', 1, audio_info_t) +#define AUDIO_SETINFO _IOWR('A', 2, audio_info_t) +#define AUDIO_DRAIN _IO('A', 3) +#define AUDIO_FLUSH _IO('A', 4) + +#endif /*!_PCAUDIOIO_H*/ diff --git a/sys/i386/include/pmap.h b/sys/i386/include/pmap.h index ba27f793735d..507baf94b356 100644 --- a/sys/i386/include/pmap.h +++ b/sys/i386/include/pmap.h @@ -42,7 +42,7 @@ * * from: hp300: @(#)pmap.h 7.2 (Berkeley) 12/16/90 * from: @(#)pmap.h 7.4 (Berkeley) 5/12/91 - * $Id: pmap.h,v 1.10 1994/01/31 04:19:00 davidg Exp $ + * $Id: pmap.h,v 1.16 1994/06/07 17:48:46 davidg Exp $ */ #ifndef _PMAP_MACHINE_ @@ -58,7 +58,8 @@ struct pde unsigned int pd_v:1, /* valid bit */ pd_prot:2, /* access control */ - pd_mbz1:2, /* reserved, must be zero */ + pd_ncpwt:1, /* page cache write through */ + pd_ncpcd:1, /* page cache disable */ pd_u:1, /* hardware maintained 'used' bit */ :1, /* not used */ pd_mbz2:2, /* reserved, must be zero */ @@ -76,7 +77,8 @@ struct pte unsigned int pg_v:1, /* valid bit */ pg_prot:2, /* access control */ - pg_mbz1:2, /* reserved, must be zero */ + pg_ncpwt:1, /* page cache write through */ + pg_ncpcd:1, /* page cache disable */ pg_u:1, /* hardware maintained 'used' bit */ pg_m:1, /* hardware maintained modified bit */ pg_mbz2:2, /* reserved, must be zero */ @@ -91,10 +93,12 @@ unsigned int #define PG_RW 0x00000002 #define PG_u 0x00000004 #define PG_PROT 0x00000006 /* all protection bits . */ -#define PG_W 0x00000200 -#define PG_N 0x00000800 /* Non-cacheable */ -#define PG_M 0x00000040 +#define PG_NC_PWT 0x00000008 /* page cache write through */ +#define PG_NC_PCD 0x00000010 /* page cache disable */ +#define PG_N 0x00000018 /* Non-cacheable */ #define PG_U 0x00000020 +#define PG_M 0x00000040 +#define PG_W 0x00000200 #define PG_FRAME 0xfffff000UL #define PG_NOACC 0 @@ -115,8 +119,10 @@ unsigned int #define PGEX_W 0x02 /* during a Write cycle */ #define PGEX_U 0x04 /* access from User mode (UPL) */ -typedef struct pde pd_entry_t; /* page directory entry */ -typedef struct pte pt_entry_t; /* Mach page table entry */ +/* typedef struct pde pd_entry_t; */ /* page directory entry */ +/* typedef struct pte pt_entry_t; */ /* Mach page table entry */ +typedef unsigned int *pd_entry_t; +typedef unsigned int *pt_entry_t; /* * NKPDE controls the virtual space of the kernel, what ever is left, minus @@ -145,18 +151,18 @@ typedef struct pte pt_entry_t; /* Mach page table entry */ #define KPTDI (APTDPTDI-NKPDE)/* start of kernel virtual pde's */ #define PTDPTDI (KPTDI-1) /* ptd entry that points to ptd! */ #define KSTKPTDI (PTDPTDI-1) /* ptd entry for u./kernel&user stack */ -#define KSTKPTEOFF (NBPG/sizeof(struct pde)-UPAGES) /* pte entry for kernel stack */ +#define KSTKPTEOFF (NBPG/sizeof(pd_entry_t)-UPAGES) /* pte entry for kernel stack */ -#define PDESIZE sizeof(struct pde) /* for assembly files */ -#define PTESIZE sizeof(struct pte) /* for assembly files */ +#define PDESIZE sizeof(pd_entry_t) /* for assembly files */ +#define PTESIZE sizeof(pt_entry_t) /* for assembly files */ /* * Address of current and alternate address space page table maps * and directories. */ #ifdef KERNEL -extern struct pte PTmap[], APTmap[], Upte; -extern struct pde PTD[], APTD[], PTDpde, APTDpde, Upde; +extern pt_entry_t PTmap[], APTmap[], Upte; +extern pd_entry_t PTD[], APTD[], PTDpde, APTDpde, Upde; extern pt_entry_t *Sysmap; extern int IdlePTD; /* physical address of "Idle" state directory */ @@ -171,12 +177,29 @@ extern int IdlePTD; /* physical address of "Idle" state directory */ #define vtopte(va) (PTmap + i386_btop(va)) #define kvtopte(va) vtopte(va) #define ptetov(pt) (i386_ptob(pt - PTmap)) -#define vtophys(va) (i386_ptob(vtopte(va)->pg_pfnum) | ((int)(va) & PGOFSET)) +#define vtophys(va) (((int) (*vtopte(va))&PG_FRAME) | ((int)(va) & PGOFSET)) #define ispt(va) ((va) >= UPT_MIN_ADDRESS && (va) <= KPT_MAX_ADDRESS) #define avtopte(va) (APTmap + i386_btop(va)) #define ptetoav(pt) (i386_ptob(pt - APTmap)) -#define avtophys(va) (i386_ptob(avtopte(va)->pg_pfnum) | ((int)(va) & PGOFSET)) +#define avtophys(va) (((int) (*avtopte(va))&PG_FRAME) | ((int)(va) & PGOFSET)) + +#ifdef KERNEL +/* + * Routine: pmap_kextract + * Function: + * Extract the physical page address associated + * kernel virtual address. + */ +static inline vm_offset_t +pmap_kextract(va) + vm_offset_t va; +{ + vm_offset_t pa = *(int *)vtopte(va); + pa = (pa & PG_FRAME) | (va & ~PG_FRAME); + return pa; +} +#endif /* * macros to generate page directory/table indicies @@ -252,7 +275,7 @@ extern void pmap_remove(struct pmap *, vm_offset_t, vm_offset_t); extern void pmap_protect(struct pmap *, vm_offset_t, vm_offset_t, vm_prot_t); extern void pmap_enter(pmap_t, vm_offset_t, vm_offset_t, vm_prot_t, boolean_t); extern void pmap_change_wiring(pmap_t, vm_offset_t, boolean_t); -extern inline struct pte *pmap_pte(pmap_t, vm_offset_t); +extern inline pt_entry_t * const pmap_pte(pmap_t, vm_offset_t); extern vm_offset_t pmap_extract(pmap_t, vm_offset_t); extern void pmap_copy(pmap_t, pmap_t, vm_offset_t, vm_size_t, vm_offset_t); extern void pmap_collect(pmap_t); diff --git a/sys/i386/include/psl.h b/sys/i386/include/psl.h index eb15d181b464..c877648ee796 100644 --- a/sys/i386/include/psl.h +++ b/sys/i386/include/psl.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)psl.h 5.2 (Berkeley) 1/18/91 - * $Id: psl.h,v 1.3.2.1 1994/03/07 01:23:56 rgrimes Exp $ + * $Id: psl.h,v 1.5 1994/06/07 23:53:52 phk Exp $ */ #ifndef _MACHINE_PSL_H_ @@ -56,6 +56,10 @@ #define PSL_NT 0x00004000 /* nested task bit */ #define PSL_RF 0x00010000 /* restart flag bit */ #define PSL_VM 0x00020000 /* virtual 8086 mode bit */ +#define PSL_AC 0x00040000 /* alignment checking */ +#define PSL_VIF 0x00080000 /* virtual interrupt enable */ +#define PSL_VIP 0x00100000 /* virtual interrupt pending */ +#define PSL_ID 0x00200000 /* identification bit */ #define PSL_MBZ 0xffc08028 /* must be zero bits */ #define PSL_MBO 0x00000002 /* must be one bits */ diff --git a/sys/i386/include/pte.h b/sys/i386/include/pte.h index ac0cb541de95..4b1f71091e2b 100644 --- a/sys/i386/include/pte.h +++ b/sys/i386/include/pte.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)pte.h 5.5 (Berkeley) 5/9/91 - * $Id: pte.h,v 1.4 1994/01/31 06:52:41 davidg Exp $ + * $Id: pte.h,v 1.5 1994/03/07 11:38:49 davidg Exp $ */ #ifndef _MACHINE_PTE_H_ @@ -114,7 +114,7 @@ unsigned int /* * Pte related macros */ -#define dirty(pte) ((pte)->pg_m) +#define dirty(pte) ((pte) & PG_M) #ifndef LOCORE #ifdef KERNEL diff --git a/sys/i386/include/soundcard.h b/sys/i386/include/soundcard.h index 23f20313d98a..ce28a144242d 100644 --- a/sys/i386/include/soundcard.h +++ b/sys/i386/include/soundcard.h @@ -1,5 +1,5 @@ #ifndef _SOUNDCARD_H_ -#define _SOUNDCARD_H_ 1 +#define _SOUNDCARD_H_ /* * Copyright by Hannu Savolainen 1993 * @@ -30,14 +30,15 @@ /* * If you make modifications to this file, please contact me before * distributing the modified version. There is already enough - * diversity in the world. + * divercity in the world. * * Regards, * Hannu Savolainen - * hsavolai@cs.helsinki.fi + * hannu@voxware.pp.fi, Hannu.Savolainen@helsinki.fi */ -#define SOUND_VERSION 200 +#define SOUND_VERSION 205 +#define VOXWARE #include <sys/ioctl.h> @@ -50,6 +51,8 @@ #define SNDCARD_PAS 3 #define SNDCARD_GUS 4 #define SNDCARD_MPU401 5 +#define SNDCARD_SB16 6 +#define SNDCARD_SB16MIDI 7 /*********************************** * IOCTL Commands for /dev/sequencer @@ -66,9 +69,9 @@ */ /* #define IOCTYPE (0xff<<8) */ #define IOCPARM_MASK 0x7f /* parameters must be < 128 bytes */ -#define IOC_VOID 0x20000000 /* no parameters */ -#define IOC_OUT 0x40000000 /* copy out parameters */ -#define IOC_IN 0x80000000 /* copy in parameters */ +#define IOC_VOID 0x00000000 /* no parameters */ +#define IOC_OUT 0x20000000 /* copy out parameters */ +#define IOC_IN 0x40000000 /* copy in parameters */ #define IOC_INOUT (IOC_IN|IOC_OUT) /* the 0x20000000 is so we can distinguish new ioctl's from old */ #define _IO(x,y) ((int)(IOC_VOID|(x<<8)|y)) @@ -180,7 +183,7 @@ struct patch_info { int volume; int spare[4]; - char data[0]; /* The waveform data starts here */ + char data[1]; /* The waveform data starts here */ }; @@ -307,6 +310,14 @@ struct patmgr_info { /* Note! size must be < 4k since kmalloc() is used */ #define CTRL_EXPRESSION 253 #define CTRL_MAIN_VOLUME 252 #define SEQ_BALANCE 11 +#define SEQ_VOLMODE 12 + +/* + * Volume mode decides how volumes are used + */ + +#define VOL_METHOD_ADAGIO 1 +#define VOL_METHOD_LINEAR 2 /* * Note! SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO are used also as @@ -402,7 +413,8 @@ struct midi_info { char name[30]; int device; /* 0-N. INITIALIZE BEFORE CALLING */ unsigned long capabilities; /* To be defined later */ - int dummies[19]; /* Reserve space */ + int dev_type; + int dummies[18]; /* Reserve space */ }; /******************************************** @@ -418,6 +430,7 @@ struct midi_info { #define SOUND_PCM_WRITE_CHANNELS _IOWR('P', 6, int) #define SOUND_PCM_WRITE_FILTER _IOWR('P', 7, int) #define SNDCTL_DSP_POST _IO ('P', 8) +#define SNDCTL_DSP_SUBDIVIDE _IOWR('P', 9, int) #define SOUND_PCM_READ_RATE _IOR ('P', 2, int) #define SOUND_PCM_READ_CHANNELS _IOR ('P', 6, int) @@ -430,6 +443,7 @@ struct midi_info { #define SOUND_PCM_POST SNDCTL_DSP_POST #define SOUND_PCM_RESET SNDCTL_DSP_RESET #define SOUND_PCM_SYNC SNDCTL_DSP_SYNC +#define SOUND_PCM_SUBDIVIDE SNDCTL_DSP_SUBDIVIDE /********************************************* * IOCTL commands for /dev/mixer @@ -624,7 +638,8 @@ void seqbuf_dump(void); /* This function must be provided by programs */ * } */ -#define SEQ_DEFINEBUF(len) unsigned char _seqbuf[len]; int _seqbuflen = len, _seqbufptr = 0 +#define SEQ_DEFINEBUF(len) unsigned char _seqbuf[len]; int _seqbuflen = len; int _seqbufptr = 0 +#define SEQ_DECLAREBUF() extern unsigned char _seqbuf[]; extern int _seqbuflen;extern int _seqbufptr #define SEQ_PM_DEFINES struct patmgr_info _pm_info #define _SEQ_NEEDBUF(len) if ((_seqbufptr+(len)) > _seqbuflen) seqbuf_dump() #define _SEQ_ADVBUF(len) _seqbufptr += len @@ -638,6 +653,17 @@ void seqbuf_dump(void); /* This function must be provided by programs */ _pm_info.parm1 = bank, _pm_info.parm2 = 128, \ ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info)) +#define SEQ_VOLUME_MODE(dev, mode) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ + _seqbuf[_seqbufptr+1] = SEQ_VOLMODE;\ + _seqbuf[_seqbufptr+2] = (dev);\ + _seqbuf[_seqbufptr+3] = (mode);\ + _seqbuf[_seqbufptr+4] = 0;\ + _seqbuf[_seqbufptr+5] = 0;\ + _seqbuf[_seqbufptr+6] = 0;\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + #define SEQ_START_NOTE(dev, voice, note, vol) {_SEQ_NEEDBUF(8);\ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ _seqbuf[_seqbufptr+1] = SEQ_NOTEON;\ @@ -728,8 +754,8 @@ void seqbuf_dump(void); /* This function must be provided by programs */ _seqbuf[_seqbufptr+2] = (device);\ _seqbuf[_seqbufptr+3] = 0;\ _SEQ_ADVBUF(4);} -#define SEQ_WRPATCH(patch, len) {if (_seqbufptr) seqbuf_dump();\ - if (write(seqfd, (char*)(patch), len)==-1) \ +#define SEQ_WRPATCH(patchx, len) {if (_seqbufptr) seqbuf_dump();\ + if (write(seqfd, (char*)(patchx), len)==-1) \ perror("Write patch: /dev/sequencer");} #endif diff --git a/sys/i386/include/spl.h b/sys/i386/include/spl.h new file mode 100644 index 000000000000..0be93644a463 --- /dev/null +++ b/sys/i386/include/spl.h @@ -0,0 +1,104 @@ +#ifndef _MACHINE_IPL_H_ +#define _MACHINE_IPL_H_ + +#include "machine/../isa/ipl.h" /* XXX "machine" means cpu for i386 */ + +/* + * Software interrupt bit numbers in priority order. The priority only + * determines which swi will be dispatched next; a higher priority swi + * may be dispatched when a nested h/w interrupt handler returns. + */ +#define SWI_TTY (NHWI + 0) +#define SWI_NET (NHWI + 1) +#define SWI_CLOCK 30 +#define SWI_AST 31 + +/* + * Corresponding interrupt-pending bits for ipending. + */ +#define SWI_TTY_PENDING (1 << SWI_TTY) +#define SWI_NET_PENDING (1 << SWI_NET) +#define SWI_CLOCK_PENDING (1 << SWI_CLOCK) +#define SWI_AST_PENDING (1 << SWI_AST) + +/* + * Corresponding interrupt-disable masks for cpl. The ordering is now by + * inclusion (where each mask is considered as a set of bits). Everything + * except SWI_AST_MASK includes SWI_CLOCK_MASK so that softclock() doesn't + * run while other swi handlers are running and timeout routines can call + * swi handlers. Everything includes SWI_AST_MASK so that AST's are masked + * until just before return to user mode. + */ +#define SWI_TTY_MASK (SWI_TTY_PENDING | SWI_CLOCK_MASK) +#define SWI_NET_MASK (SWI_NET_PENDING | SWI_CLOCK_MASK) +#define SWI_CLOCK_MASK (SWI_CLOCK_PENDING | SWI_AST_MASK) +#define SWI_AST_MASK SWI_AST_PENDING +#define SWI_MASK (~HWI_MASK) + +#ifndef LOCORE + +extern unsigned bio_imask; /* group of interrupts masked with splbio() */ +extern unsigned cpl; /* current priority level mask */ +extern unsigned high_imask; /* group of interrupts masked with splhigh() */ +extern unsigned net_imask; /* group of interrupts masked with splimp() */ +extern volatile unsigned ipending; /* active interrupts masked by cpl */ +extern volatile unsigned netisr; +extern unsigned tty_imask; /* group of interrupts masked with spltty() */ + +/* + * ipending has to be volatile so that it is read every time it is accessed + * in splx() and spl0(), but we don't want it to be read nonatomically when + * it is changed. Pretending that ipending is a plain int happens to give + * suitable atomic code for "ipending |= constant;". + */ +#define setsoftast() (*(unsigned *)&ipending |= SWI_AST_PENDING) +#define setsoftclock() (*(unsigned *)&ipending |= SWI_CLOCK_PENDING) +#define setsoftnet() (*(unsigned *)&ipending |= SWI_NET_PENDING) +#define setsofttty() (*(unsigned *)&ipending |= SWI_TTY_PENDING) + +void unpend_V __P((void)); + +#ifdef __GNUC__ + +void splz __P((void)); + +#define GENSPL(name, set_cpl) \ +static __inline int name(void) \ +{ \ + unsigned x; \ + \ + x = cpl; \ + set_cpl; \ + return (x); \ +} + +GENSPL(splbio, cpl |= bio_imask) +GENSPL(splclock, cpl = HWI_MASK | SWI_MASK) +GENSPL(splhigh, cpl = HWI_MASK | SWI_MASK) +GENSPL(splimp, cpl |= net_imask) +GENSPL(splnet, cpl |= SWI_NET_MASK) +GENSPL(splsoftclock, cpl = SWI_CLOCK_MASK) +GENSPL(splsofttty, cpl |= SWI_TTY_MASK) +GENSPL(spltty, cpl |= tty_imask) + +static __inline void +spl0(void) +{ + cpl = SWI_AST_MASK; + if (ipending & ~SWI_AST_MASK) + splz(); +} + +static __inline void +splx(int ipl) +{ + cpl = ipl; + if (ipending & ~ipl) + splz(); +} + +#endif /* __GNUC__ */ + +#endif /* LOCORE */ + +#endif /* _MACHINE_IPL_H_ */ diff --git a/sys/i386/include/ultrasound.h b/sys/i386/include/ultrasound.h index 9b6e78fbadce..40e2443e6f65 100644 --- a/sys/i386/include/ultrasound.h +++ b/sys/i386/include/ultrasound.h @@ -32,12 +32,6 @@ * and not portable. */ -#ifdef linux -#include <linux/soundcard.h> -#else -#include <machine/soundcard.h> -#endif - /* * Private events for Gravis Ultrasound (GUS) * @@ -90,6 +84,7 @@ #define _GUS_VOICEFADE 0x0d #define _GUS_VOLUME_SCALE 0x0e #define _GUS_VOICEVOL2 0x0f +#define _GUS_VOICE_POS 0x10 /* * GUS API macros @@ -120,5 +115,7 @@ #define GUS_RAMPON(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_RAMPON, (p1), 0) #define GUS_RAMPOFF(chn, voice) _GUS_CMD(chn, voice, _GUS_RAMPOFF, 0, 0) #define GUS_VOLUME_SCALE(chn, voice, p1, p2) _GUS_CMD(chn, voice, _GUS_VOLUME_SCALE, (p1), (p2)) +#define GUS_VOICE_POS(chn, voice, p) _GUS_CMD(chn, voice, _GUS_VOICE_POS, \ + (p) & 0xffff, ((p) >> 16) & 0xffff) #endif diff --git a/sys/i386/include/vmparam.h b/sys/i386/include/vmparam.h index a5b657cd82c2..c5ed04dde346 100644 --- a/sys/i386/include/vmparam.h +++ b/sys/i386/include/vmparam.h @@ -36,7 +36,7 @@ * SUCH DAMAGE. * * from: @(#)vmparam.h 5.9 (Berkeley) 5/12/91 - * $Id: vmparam.h,v 1.11.2.1 1994/03/24 08:57:03 rgrimes Exp $ + * $Id: vmparam.h,v 1.12 1994/03/21 09:35:24 davidg Exp $ */ diff --git a/sys/i386/isa/aha1542.c b/sys/i386/isa/aha1542.c index efeee385ba51..df1e3cada643 100644 --- a/sys/i386/isa/aha1542.c +++ b/sys/i386/isa/aha1542.c @@ -12,7 +12,7 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - * $Id: aha1542.c,v 1.20.2.2 1994/05/03 05:16:50 rgrimes Exp $ + * $Id: aha1542.c,v 1.27 1994/06/05 19:18:10 ats Exp $ */ /* @@ -299,8 +299,8 @@ struct aha_data { struct aha_ccb *aha_ccb_free; /* the next free ccb */ struct aha_ccb aha_ccb[AHA_MBX_SIZE]; /* all the CCBs */ int aha_int; /* our irq level */ - int aha_dma; /* out DMA req channel */ - int aha_scsi_dev; /* ourscsi bus address */ + int aha_dma; /* our DMA req channel */ + int aha_scsi_dev; /* our scsi bus address */ struct scsi_link sc_link; /* prototype for subdevs */ } *ahadata[NAHA]; @@ -589,6 +589,7 @@ ahaattach(dev) aha->sc_link.adapter_targ = aha->aha_scsi_dev; aha->sc_link.adapter = &aha_switch; aha->sc_link.device = &aha_dev; + aha->sc_link.flags = SDEV_BOUNCE; /* * ask the adapter what subunits are present @@ -741,7 +742,7 @@ aha_get_ccb(unit, flags) * to come free */ while ((!(rc = aha->aha_ccb_free)) && (!(flags & SCSI_NOSLEEP))) { - sleep(&aha->aha_ccb_free, PRIBIO); + tsleep((caddr_t)&aha->aha_ccb_free, PRIBIO, "ahaccb", 0); } if (rc) { aha->aha_ccb_free = aha->aha_ccb_free->next; @@ -916,7 +917,7 @@ aha_init(unit) #ifdef AHADEBUG printf("aha%d: extended bios flags %x\n", unit, extbios.flags); #endif /* AHADEBUG */ - printf("aha%d: 1542C/CF detected, unlocking mailbox\n"); + printf("aha%d: 1542C/CF detected, unlocking mailbox\n", unit); aha_cmd(unit, 2, 0, 0, 0, AHA_MBX_ENABLE, 0, extbios.mailboxlock); } @@ -1006,7 +1007,7 @@ aha_init(unit) return (EIO); } #else - printf ("\n"); + printf (" (bus speed defaulted)\n"); #endif /*TUNE_1542*/ /* * Initialize mail box diff --git a/sys/i386/isa/bt742a.c b/sys/i386/isa/bt742a.c index e160e9167cb1..990344f28ea6 100644 --- a/sys/i386/isa/bt742a.c +++ b/sys/i386/isa/bt742a.c @@ -12,7 +12,7 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - * $Id: bt742a.c,v 1.12 1993/12/19 00:50:29 wollman Exp $ + * $Id: bt742a.c,v 1.22 1994/06/15 04:19:23 jkh Exp $ */ /* @@ -126,7 +126,7 @@ struct bt_cmd_buf { * these could be bigger but we need the bt_data to fit on a single page.. */ -#define BT_MBX_SIZE 16 /* mail box size (MAX 255 MBxs) */ +#define BT_MBX_SIZE 32 /* mail box size (MAX 255 MBxs) */ /* don't need that many really */ #define BT_CCB_MAX 32 /* store up to 32CCBs at any one time */ /* in bt742a H/W ( Not MAX ? ) */ @@ -299,6 +299,21 @@ struct bt_config { u_char :5; }; +/* + * Determin 32bit address/Data firmware functionality from Bus type + * Note: bt742a/747[s|d]/757/946/445s will return 'E' + * bt542b/545s/545d will be return 'A' + * 94/05/18 amurai@spec.co.jp + */ +#define BT_BUS_TYPE_24bit 'A' /* PC/AT 24 bit address bus type */ +#define BT_BUS_TYPE_32bit 'E' /* EISA/VLB/PCI 32 bit address bus type */ +#define BT_BUS_TYPE_MCA 'M' /* Micro chanel is ? forget it right now */ +struct bt_ext_info { + u_char bus_type; /* Host adapter bus type */ + u_char bios_addr; /* Bios Address-Not use*/ + u_short max_seg; /* Max segment List */ +}; + #define INT9 0x01 #define INT10 0x02 #define INT11 0x04 @@ -568,7 +583,8 @@ btprobe(dev) } /* * If it's there, put in it's interrupt vectors - */ dev->id_unit = unit; + */ + dev->id_unit = unit; dev->id_irq = (1 << bt->bt_int); dev->id_drq = bt->bt_dma; @@ -593,6 +609,7 @@ btattach(dev) bt->sc_link.adapter_targ = bt->bt_scsi_dev; bt->sc_link.adapter = &bt_switch; bt->sc_link.device = &bt_dev; + bt->sc_link.flags = SDEV_BOUNCE; /* * ask the adapter what subunits are present @@ -818,7 +835,8 @@ bt_get_ccb(unit, flags) goto gottit; } else { if (!(flags & SCSI_NOSLEEP)) { - sleep(&bt->bt_ccb_free, PRIBIO); + tsleep((caddr_t)&bt->bt_ccb_free, PRIBIO, + "btccb", 0); } } } @@ -987,6 +1005,7 @@ bt_init(unit) unsigned char ad[4]; volatile int i, sts; struct bt_config conf; + struct bt_ext_info info; /* * reset board, If it doesn't respond, assume @@ -1008,6 +1027,32 @@ bt_init(unit) return (ENXIO); } /* + * Make sure board has a capability of 32bit addressing. + * and Firmware also need a capability of 32bit addressing pointer + * in Extended mailbox and ccb structure. + * 94/05/18 amurai@spec.co.jp + */ + bt_cmd(unit, 1, sizeof(info),0,&info, BT_INQUIRE_EXTENDED,sizeof(info)); + switch (info.bus_type) { + case BT_BUS_TYPE_24bit: /* PC/AT 24 bit address bus */ + printf("bt%d: bt54x-ISA(24bit) bus detected\n", unit); + break; + case BT_BUS_TYPE_32bit: /* EISA/VLB/PCI 32 bit bus */ + printf("bt%d: PCI/EISA/VLB(32bit) bus detected\n",unit); + break; + case BT_BUS_TYPE_MCA: /* forget it right now */ + printf("bt%d: MCA bus architecture detected..", unit); + printf("[giving up]\n"); + return (ENXIO); + break; + default: + printf("bt%d: Unknown state detected...", unit); + printf("[giving up]\n"); + return (ENXIO); + break; + } + + /* * Assume we have a board at this stage * setup dma channel from jumpers and save int * level @@ -1107,8 +1152,6 @@ bt_init(unit) bt->bt_mbx.tmbi = &bt->bt_mbx.mbi[0]; bt_inquire_setup_information(unit); - /* Enable round-robin scheme - appeared at firmware rev. 3.31 */ - bt_cmd(unit, 1, 0, 0, 0, BT_ROUND_ROBIN, BT_ENABLE); /* * Note that we are going and return (to probe) @@ -1122,9 +1165,14 @@ bt_inquire_setup_information(unit) { struct bt_data *bt = btdata[unit]; struct bt_setup setup; + char dummy[8]; struct bt_boardID bID; int i; + /* Inquire Installed Devices */ + bzero( &dummy[0], sizeof(dummy) ); + bt_cmd(unit, 0, sizeof(dummy), 10, &dummy[0], BT_DEV_GET); + /* Inquire Board ID to Bt742 for firmware version */ bt_cmd(unit, 0, sizeof(bID), 0, &bID, BT_INQUIRE); printf("bt%d: version %c.%c, ", @@ -1143,19 +1191,37 @@ bt_inquire_setup_information(unit) } else { printf("no parity, "); } - printf("%d mbxs, %d ccbs\n", setup.num_mbx, bt->numccbs); + printf("%d mbxs, %d ccbs\n", setup.num_mbx, BT_CCB_MAX); + + /* + * Displaying SCSI negotiation value by each target. + * How can I determin FAST scsi value? XXX amurai@spec.co.jp + */ for (i = 0; i < 8; i++) { - if (!setup.sync[i].offset && - !setup.sync[i].period && - !setup.sync[i].valid) + if (!setup.sync[i].valid) continue; + if (!setup.sync[i].offset && !setup.sync[i].period ) + printf("bt%d: targ %d async\n", unit, i); + else + printf("bt%d: targ %d offset=%02d, period=%dnsec\n", + unit, i, + setup.sync[i].offset, + 200 + setup.sync[i].period * 50 ); + } - printf("bt%d: dev%02d Offset=%d,Transfer period=%d, Synchronous? %s", - unit, i, - setup.sync[i].offset, setup.sync[i].period, - setup.sync[i].valid ? "Yes" : "No"); + /* + * Enable round-robin scheme - appeared at firmware rev. 3.31 + * Below rev 2.XX firmware has a problem for issuing + * BT_ROUND_ROBIN command amurai@spec.co.jp + */ + if ( bID.firm_revision != '2' ) { + printf("bt%d: Enabling Round robin scheme\n", unit); + bt_cmd(unit, 1, 0, 0, 0, BT_ROUND_ROBIN, BT_ENABLE); + } else { + printf("bt%d: Not Enabling Round robin scheme\n", unit); } + } #ifndef min @@ -1434,13 +1500,15 @@ bt_timeout(caddr_t arg1, int arg2) struct bt_data *bt; int s = splbio(); + /* + * A timeout routine in kernel DONOT unlink + * Entry chains when time outed....So infinity Loop.. + * 94/04/20 amurai@spec.co.jp + */ + untimeout(bt_timeout, (caddr_t)ccb); + unit = ccb->xfer->sc_link->adapter_unit; bt = btdata[unit]; - printf("bt%d:%d:%d (%s%d) timed out ", unit - ,ccb->xfer->sc_link->target - ,ccb->xfer->sc_link->lun - ,ccb->xfer->sc_link->device->name - ,ccb->xfer->sc_link->dev_unit); #ifdef UTEST bt_print_active_ccbs(unit); @@ -1467,13 +1535,14 @@ bt_timeout(caddr_t arg1, int arg2) ccb->xfer->retries = 0; /* I MEAN IT ! */ ccb->host_stat = BT_ABORTED; bt_done(unit, ccb); - } else { /* abort the operation that has timed out */ + } else { + /* abort the operation that has timed out */ printf("bt%d: Try to abort\n", unit); bt_send_mbo(unit, ~SCSI_NOMASK, BT_MBO_ABORT, ccb); /* 2 secs for the abort */ - timeout(bt_timeout, (caddr_t)ccb, 2 * hz); ccb->flags = CCB_ABORTED; + timeout(bt_timeout, (caddr_t)ccb, 2 * hz); } splx(s); } diff --git a/sys/i386/isa/bt742a_32.c b/sys/i386/isa/bt742a_32.c new file mode 100644 index 000000000000..f8eabf4de61f --- /dev/null +++ b/sys/i386/isa/bt742a_32.c @@ -0,0 +1,1694 @@ +/* + * Written by Julian Elischer (julian@tfs.com) + * for TRW Financial Systems for use under the MACH(2.5) operating system. + * + * TRW Financial Systems, in accordance with their agreement with Carnegie + * Mellon University, makes this software available to CMU to distribute + * or use in any manner that they see fit as long as this message is kept with + * the software. For this reason TFS also grants any other persons or + * organisations permission to use or modify this software. + * + * TFS supplies this software to be publicly redistributed + * on the understanding that TFS is not responsible for the correct + * functioning of this software in any circumstances. + * + * $Id: bt742a_32.c,v 1.1 1994/06/17 19:05:57 jkh Exp $ + */ + +/* + * Bulogic/Bustek 32 bit Addressing Mode SCSI driver (Was bt742a.c) + * + * THIS DRIVER IS EXPERIMENTAL AND NOT ENABLED BY DEFAULT. It is + * provided here merely for reference purposes. + * + * NOTE: 1. Some bt5xx card can NOT handling 32 bit addressing mode. + * 2. OLD bt445s Revision A,B,C,D(nowired) + any firmware version + * has broken busmaster for handling 32 bit addressing on H/W bus side. + * 3. Extend probing still need to confirm by user base due to + * several H/W and firmware dependency. If you have a problem with + * extend probing, please contact to 'amurai@spec.co.jp' + * + * amurai@spec.co.jp 94/6/16 + */ + +#include <sys/types.h> + +#ifdef KERNEL /* don't laugh.. it compiles to a program too.. look */ +#include <bt.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/user.h> +#endif /* KERNEL */ + +#include <i386/isa/isa_device.h> +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#ifdef KERNEL +#include "ddb.h" +#include "kernel.h" +#else /*KERNEL */ +#define NBT 1 +#endif /*KERNEL */ + +typedef unsigned long int physaddr; + +/* + * I/O Port Interface + */ + +#define BT_BASE bt->bt_base +#define BT_CTRL_STAT_PORT (BT_BASE + 0x0) /* control & status */ +#define BT_CMD_DATA_PORT (BT_BASE + 0x1) /* cmds and datas */ +#define BT_INTR_PORT (BT_BASE + 0x2) /* Intr. stat */ + +/* + * BT_CTRL_STAT bits (write) + */ + +#define BT_HRST 0x80 /* Hardware reset */ +#define BT_SRST 0x40 /* Software reset */ +#define BT_IRST 0x20 /* Interrupt reset */ +#define BT_SCRST 0x10 /* SCSI bus reset */ + +/* + * BT_CTRL_STAT bits (read) + */ + +#define BT_STST 0x80 /* Self test in Progress */ +#define BT_DIAGF 0x40 /* Diagnostic Failure */ +#define BT_INIT 0x20 /* Mbx Init required */ +#define BT_IDLE 0x10 /* Host Adapter Idle */ +#define BT_CDF 0x08 /* cmd/data out port full */ +#define BT_DF 0x04 /* Data in port full */ +#define BT_INVDCMD 0x01 /* Invalid command */ + +/* + * BT_CMD_DATA bits (write) + */ + +#define BT_NOP 0x00 /* No operation */ +#define BT_MBX_INIT 0x01 /* Mbx initialization */ +#define BT_START_SCSI 0x02 /* start scsi command */ +#define BT_START_BIOS 0x03 /* start bios command */ +#define BT_INQUIRE 0x04 /* Adapter Inquiry */ +#define BT_MBO_INTR_EN 0x05 /* Enable MBO available interrupt */ +#define BT_SEL_TIMEOUT_SET 0x06 /* set selection time-out */ +#define BT_BUS_ON_TIME_SET 0x07 /* set bus-on time */ +#define BT_BUS_OFF_TIME_SET 0x08 /* set bus-off time */ +#define BT_SPEED_SET 0x09 /* set transfer speed */ +#define BT_DEV_GET 0x0a /* return installed devices */ +#define BT_CONF_GET 0x0b /* return configuration data */ +#define BT_TARGET_EN 0x0c /* enable target mode */ +#define BT_SETUP_GET 0x0d /* return setup data */ +#define BT_WRITE_CH2 0x1a /* write channel 2 buffer */ +#define BT_READ_CH2 0x1b /* read channel 2 buffer */ +#define BT_WRITE_FIFO 0x1c /* write fifo buffer */ +#define BT_READ_FIFO 0x1d /* read fifo buffer */ +#define BT_ECHO 0x1e /* Echo command data */ +#define BT_MBX_INIT_EXTENDED 0x81 /* Mbx initialization */ +#define BT_INQUIRE_EXTENDED 0x8D /* Adapter Setup Inquiry */ + +/* The following command appeared at FirmWare 3.31 */ +#define BT_ROUND_ROBIN 0x8f /* Enable/Disable(default) round robin */ +#define BT_DISABLE 0x00 /* Parameter value for Disable */ +#define BT_ENABLE 0x01 /* Parameter value for Enable */ + +struct bt_cmd_buf { + u_char byte[16]; +}; + +/* + * BT_INTR_PORT bits (read) + */ + +#define BT_ANY_INTR 0x80 /* Any interrupt */ +#define BT_SCRD 0x08 /* SCSI reset detected */ +#define BT_HACC 0x04 /* Command complete */ +#define BT_MBOA 0x02 /* MBX out empty */ +#define BT_MBIF 0x01 /* MBX in full */ + +/* + * Mail box defs etc. + * these could be bigger but we need the bt_data to fit on a single page.. + */ + +#define BT_MBX_SIZE 32 /* mail box size (MAX 255 MBxs) */ + /* don't need that many really */ +#define BT_CCB_MAX 32 /* store up to 32CCBs at any one time */ + /* in bt742a H/W ( Not MAX ? ) */ +#define CCB_HASH_SIZE 32 /* when we have a physical addr. for */ + /* a ccb and need to find the ccb in */ + /* space, look it up in the hash table */ +#define CCB_HASH_SHIFT 9 /* only hash on multiples of 512 */ +#define CCB_HASH(x) ((((long int)(x))>>CCB_HASH_SHIFT) % CCB_HASH_SIZE) + +#define bt_nextmbx( wmb, mbx, mbio ) \ + if ( (wmb) == &((mbx)->mbio[BT_MBX_SIZE - 1 ]) ) \ + (wmb) = &((mbx)->mbio[0]); \ + else \ + (wmb)++; + +typedef struct bt_mbx_out { + physaddr ccb_addr; + unsigned char dummy[3]; + unsigned char cmd; +} BT_MBO; + +typedef struct bt_mbx_in { + physaddr ccb_addr; + unsigned char btstat; + unsigned char sdstat; + unsigned char dummy; + unsigned char stat; +} BT_MBI; + +struct bt_mbx { + BT_MBO mbo[BT_MBX_SIZE]; + BT_MBI mbi[BT_MBX_SIZE]; + BT_MBO *tmbo; /* Target Mail Box out */ + BT_MBI *tmbi; /* Target Mail Box in */ +}; + +/* + * mbo.cmd values + */ + +#define BT_MBO_FREE 0x0 /* MBO entry is free */ +#define BT_MBO_START 0x1 /* MBO activate entry */ +#define BT_MBO_ABORT 0x2 /* MBO abort entry */ + +/* + * mbi.stat values + */ + +#define BT_MBI_FREE 0x0 /* MBI entry is free */ +#define BT_MBI_OK 0x1 /* completed without error */ +#define BT_MBI_ABORT 0x2 /* aborted ccb */ +#define BT_MBI_UNKNOWN 0x3 /* Tried to abort invalid CCB */ +#define BT_MBI_ERROR 0x4 /* Completed with error */ + +#if defined(BIG_DMA) +WARNING...THIS WON'T WORK(won't fit on 1 page) +/* #define BT_NSEG 2048*/ /* Number of scatter gather segments - to much vm */ +#define BT_NSEG 128 +#else +#define BT_NSEG 33 +#endif /* BIG_DMA */ + +struct bt_scat_gath { + unsigned long seg_len; + physaddr seg_addr; +}; + +struct bt_ccb { + unsigned char opcode; + unsigned char:3, data_in:1, data_out:1,:3; + unsigned char scsi_cmd_length; + unsigned char req_sense_length; + /*------------------------------------longword boundary */ + unsigned long data_length; + /*------------------------------------longword boundary */ + physaddr data_addr; + /*------------------------------------longword boundary */ + unsigned char dummy[2]; + unsigned char host_stat; + unsigned char target_stat; + /*------------------------------------longword boundary */ + unsigned char target; + unsigned char lun; + unsigned char scsi_cmd[12]; /* 12 bytes (bytes only) */ + unsigned char dummy2[1]; + unsigned char link_id; + /*------------------------------------4 longword boundary */ + physaddr link_addr; + /*------------------------------------longword boundary */ + physaddr sense_ptr; +/*-----end of HW fields-------------------------------longword boundary */ + struct scsi_sense_data scsi_sense; + /*------------------------------------longword boundary */ + struct bt_scat_gath scat_gath[BT_NSEG]; + /*------------------------------------longword boundary */ + struct bt_ccb *next; + /*------------------------------------longword boundary */ + struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */ + /*------------------------------------longword boundary */ + struct bt_mbx_out *mbx; /* pointer to mail box */ + /*------------------------------------longword boundary */ + int flags; +#define CCB_FREE 0 +#define CCB_ACTIVE 1 +#define CCB_ABORTED 2 + /*------------------------------------longword boundary */ + struct bt_ccb *nexthash; /* if two hash the same */ + /*------------------------------------longword boundary */ + physaddr hashkey; /*physaddr of this ccb */ + /*------------------------------------longword boundary */ +}; + +/* + * opcode fields + */ + +#define BT_INITIATOR_CCB 0x00 /* SCSI Initiator CCB */ +#define BT_TARGET_CCB 0x01 /* SCSI Target CCB */ +#define BT_INIT_SCAT_GATH_CCB 0x02 /* SCSI Initiator with scattter gather */ +#define BT_RESET_CCB 0x81 /* SCSI Bus reset */ + +/* + * bt_ccb.host_stat values + */ + +#define BT_OK 0x00 /* cmd ok */ +#define BT_LINK_OK 0x0a /* Link cmd ok */ +#define BT_LINK_IT 0x0b /* Link cmd ok + int */ +#define BT_SEL_TIMEOUT 0x11 /* Selection time out */ +#define BT_OVER_UNDER 0x12 /* Data over/under run */ +#define BT_BUS_FREE 0x13 /* Bus dropped at unexpected time */ +#define BT_INV_BUS 0x14 /* Invalid bus phase/sequence */ +#define BT_BAD_MBO 0x15 /* Incorrect MBO cmd */ +#define BT_BAD_CCB 0x16 /* Incorrect ccb opcode */ +#define BT_BAD_LINK 0x17 /* Not same values of LUN for links */ +#define BT_INV_TARGET 0x18 /* Invalid target direction */ +#define BT_CCB_DUP 0x19 /* Duplicate CCB received */ +#define BT_INV_CCB 0x1a /* Invalid CCB or segment list */ +#define BT_ABORTED 42 /* pseudo value from driver */ + +struct bt_boardID { + u_char board_type; + u_char custom_feture; + char firm_revision; + u_char firm_version; +}; + +struct bt_setup { + u_char sync_neg:1; + u_char parity:1; + u_char :6; + u_char speed; + u_char bus_on; + u_char bus_off; + u_char num_mbx; + u_char mbx[3]; /* make a sense with back-word compatibility*/ + struct { + u_char offset:4; + u_char period:3; + u_char valid:1; + } sync[8]; + u_char disc_sts; +}; + +struct bt_config { + u_char chan; + u_char intr; + u_char scsi_dev:3; + u_char :5; +}; + +#define BT_INQUIRE_REV_THIRD 0x84 /* Get Adapter FirmWare version #3 */ +#define BT_INQUIRE_REV_FOURTH 0x85 /* Get Adapter FirmWare version #4 */ + +/* + * Determine 32bit address/Data firmware functionality from the bus type + * Note: bt742a/747[s|d]/757/946/445s will return 'E' + * bt542b/545s/545d will return 'A' + * 94/05/18 amurai@spec.co.jp + */ +#define BT_BUS_TYPE_24bit 'A' /* PC/AT 24 bit address bus type */ +#define BT_BUS_TYPE_32bit 'E' /* EISA/VLB/PCI 32 bit address bus type */ +#define BT_BUS_TYPE_MCA 'M' /* Micro chanel is ? forget it right now */ +struct bt_ext_info { + u_char bus_type; /* Host adapter bus type */ + u_char bios_addr; /* Bios Address-Not used */ + u_short max_seg; /* Max segment List */ + u_char num_mbx; /* Number of mailbox */ + int32 mbx_base; /* mailbox base address */ + struct { + u_char resv1:2; /* ??? */ + u_char maxsync:1; /* ON: 10MB/s , OFF: 5MB/s */ + u_char resv2:2; /* ??? */ + u_char sync:1; /* ON: Sync, OFF: async ONLY!! */ + u_char resv3:2; /* ??? */ + } s; + u_char firmid[3]; /* Firmware ver. & rev. w/o last char */ +}; + +#define BT_GET_BOARD_INFO 0x8b /* Get H/W ID and Revision */ +struct bt_board_info { + u_char id[4]; /* i.e bt742a -> '7','4','2','A' */ + u_char ver[2]; /* i.e Board Revision 'H' -> 'H', 0x00 */ +}; + +#define BT_GET_SYNC_VALUE 0x8c /* Get Synchronous Value */ +struct bt_sync_value { + u_char value[8]; /* Synchrnous value (value * 10 nsec) */ +}; + +#define INT9 0x01 +#define INT10 0x02 +#define INT11 0x04 +#define INT12 0x08 +#define INT14 0x20 +#define INT15 0x40 + +#define EISADMA 0x00 +#define CHAN0 0x01 +#define CHAN5 0x20 +#define CHAN6 0x40 +#define CHAN7 0x80 + +#define KVTOPHYS(x) vtophys(x) +#define PAGESIZ 4096 +#define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); } + +u_char bt_scratch_buf[256]; + +struct bt_data { + short bt_base; /* base port for each board */ + struct bt_mbx bt_mbx; /* all our mailboxes */ + struct bt_ccb *bt_ccb_free; /* list of free CCBs */ + struct bt_ccb *ccbhash[CCB_HASH_SIZE]; /* phys to kv hash */ + int bt_int; /* int. read off board */ + int bt_dma; /* DMA channel read of board */ + int bt_scsi_dev; /* adapters scsi id */ + int numccbs; /* how many we have malloc'd */ + struct scsi_link sc_link; /* prototype for devs */ +} *btdata[NBT]; + +/***********debug values *************/ +#define BT_SHOWCCBS 0x01 +#define BT_SHOWINTS 0x02 +#define BT_SHOWCMDS 0x04 +#define BT_SHOWMISC 0x08 +int bt_debug = 0; + +#ifdef KERNEL +int btprobe(); +int btattach(); +int btintr(); +int32 bt_scsi_cmd(); +void bt_timeout(caddr_t, int); +void bt_inquire_setup_information(); +void bt_done(); +void btminphys(); +u_int32 bt_adapter_info(); +struct bt_ccb *bt_get_ccb(); +struct bt_ccb *bt_ccb_phys_kv(); + +static int btunit = 0; + +struct isa_driver btdriver = +{ + btprobe, + btattach, + "bt" +}; + +struct scsi_adapter bt_switch = +{ + bt_scsi_cmd, + btminphys, + 0, + 0, + bt_adapter_info, + "bt", + 0, 0 +}; + +/* the below structure is so we have a default dev struct for out link struct */ +struct scsi_device bt_dev = +{ + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ + "bt", + 0, + 0, 0 +}; + +#endif /*KERNEL */ + +#define BT_RESET_TIMEOUT 1000 +#ifndef KERNEL +main() +{ + printf("bt_data is %d bytes\n", sizeof(struct bt_data)); + printf("bt_ccb is %d bytes\n", sizeof(struct bt_ccb)); + printf("bt_mbx is %d bytes\n", sizeof(struct bt_mbx)); +} + +#else /*KERNEL */ + +/* + * bt_cmd(unit,icnt, ocnt,wait, retval, opcode, args) + * + * Activate Adapter command + * icnt: number of args (outbound bytes written after opcode) + * ocnt: number of expected returned bytes + * wait: number of seconds to wait for response + * retval: buffer where to place returned bytes + * opcode: opcode BT_NOP, BT_MBX_INIT, BT_START_SCSI ... + * args: parameters + * + * Performs an adapter command through the ports. Not to be confused with a + * scsi command, which is read in via the dma; one of the adapter commands + * tells it to read in a scsi command. + */ +int +bt_cmd(unit, icnt, ocnt, wait, retval, opcode, args) + int unit; + int icnt; + int ocnt; + int wait; + u_char *retval; + unsigned opcode; + u_char args; +{ + struct bt_data *bt = btdata[unit]; + unsigned *ic = &opcode; + u_char oc; + register i; + int sts; + + /* + * multiply the wait argument by a big constant + * zero defaults to 1 + */ + if (wait) + wait *= 100000; + else + wait = 100000; + /* + * Wait for the adapter to go idle, unless it's one of + * the commands which don't need this + */ + if (opcode != BT_MBX_INIT && opcode != BT_START_SCSI) { + i = 100000; /* 1 sec? */ + while (--i) { + sts = inb(BT_CTRL_STAT_PORT); + if (sts & BT_IDLE) { + break; + } + DELAY(10); + } + if (i == 0) { + printf("bt%d: bt_cmd, host not idle(0x%x)\n", unit, sts); + return (ENXIO); + } + } + /* + * Now that it is idle, if we expect output, preflush the + * queue feeding to us. + */ + if (ocnt) { + while ((inb(BT_CTRL_STAT_PORT)) & BT_DF) + inb(BT_CMD_DATA_PORT); + } + /* + * Output the command and the number of arguments given + * for each byte, first check the port is empty. + */ + icnt++; + /* include the command */ + while (icnt--) { + sts = inb(BT_CTRL_STAT_PORT); + for (i = wait; i; i--) { + sts = inb(BT_CTRL_STAT_PORT); + if (!(sts & BT_CDF)) + break; + DELAY(10); + } + if (i == 0) { + printf("bt%d: bt_cmd, cmd/data port full\n", unit); + outb(BT_CTRL_STAT_PORT, BT_SRST); + return (ENXIO); + } + outb(BT_CMD_DATA_PORT, (u_char) (*ic++)); + } + /* + * If we expect input, loop that many times, each time, + * looking for the data register to have valid data + */ + while (ocnt--) { + sts = inb(BT_CTRL_STAT_PORT); + for (i = wait; i; i--) { + sts = inb(BT_CTRL_STAT_PORT); + if (sts & BT_DF) + break; + DELAY(10); + } + if (i == 0) { + printf("bt%d: bt_cmd, cmd/data port empty %d\n", + unit, ocnt); + return (ENXIO); + } + oc = inb(BT_CMD_DATA_PORT); + if (retval) + *retval++ = oc; + } + /* + * Wait for the board to report a finised instruction + */ + i = 100000; /* 1 sec? */ + while (--i) { + sts = inb(BT_INTR_PORT); + if (sts & BT_HACC) { + break; + } + DELAY(10); + } + if (i == 0) { + printf("bt%d: bt_cmd, host not finished(0x%x)\n", unit, sts); + return (ENXIO); + } + outb(BT_CTRL_STAT_PORT, BT_IRST); + return (0); +} + +/* + * Check if the device can be found at the port given + * and if so, set it up ready for further work + * as an argument, takes the isa_device structure from + * autoconf.c + */ +int +btprobe(dev) + struct isa_device *dev; +{ + /* + * find unit and check we have that many defined + */ + int unit = btunit; + struct bt_data *bt; + + if (unit >= NBT) { + printf("bt%d: unit number too high\n", unit); + return 0; + } + /* + * Allocate a storage area for us + */ + if (btdata[unit]) { + printf("bt%d: memory already allocated\n", unit); + return 0; + } + bt = malloc(sizeof(struct bt_data), M_TEMP, M_NOWAIT); + if (!bt) { + printf("bt%d: cannot malloc!\n", unit); + return 0; + } + bzero(bt, sizeof(struct bt_data)); + btdata[unit] = bt; + bt->bt_base = dev->id_iobase; + + /* + * Try initialise a unit at this location + * sets up dma and bus speed, loads bt->bt_int + */ + if (bt_init(unit) != 0) { + btdata[unit] = NULL; + free(bt, M_TEMP); + return 0; + } + /* + * If it's there, put in it's interrupt vectors + */ + dev->id_unit = unit; + dev->id_irq = (1 << bt->bt_int); + dev->id_drq = bt->bt_dma; + + btunit++; + return 1; +} + +/* + * Attach all the sub-devices we can find + */ +int +btattach(dev) + struct isa_device *dev; +{ + int unit = dev->id_unit; + struct bt_data *bt = btdata[unit]; + + /* + * fill in the prototype scsi_link. + */ + bt->sc_link.adapter_unit = unit; + bt->sc_link.adapter_targ = bt->bt_scsi_dev; + bt->sc_link.adapter = &bt_switch; + bt->sc_link.device = &bt_dev; + + /* + * Forcely Bounce buffer mechanizum is ON for some broken busmaster + * chip with over 16Mbytes boundary for while... + * amurai@spec.co.jp 94/06/16 + */ + bt->sc_link.flags = SDEV_BOUNCE; /*XXX*/ + + /* + * ask the adapter what subunits are present + */ + scsi_attachdevs(&(bt->sc_link)); + return 1; +} + +/* + * Return some information to the caller about the adapter and its + * capabilities. + */ +u_int32 +bt_adapter_info(unit) + int unit; +{ + return (2); /* 2 outstanding requests at a time per device */ +} + +/* + * Catch an interrupt from the adaptor + */ +int +btintr(unit) + int unit; +{ + struct bt_data *bt = btdata[unit]; + BT_MBI *wmbi; + struct bt_mbx *wmbx; + struct bt_ccb *ccb; + unsigned char stat; + int i, wait; + int found = 0; + +#ifdef UTEST + printf("btintr "); +#endif + /* + * First acknowlege the interrupt, Then if it's + * not telling about a completed operation + * just return. + */ + stat = inb(BT_INTR_PORT); + + /* Mail Box out empty ? */ + if (stat & BT_MBOA) { + printf("bt%d: Available Free mbo post\n", unit); + /* Disable MBO available interrupt */ + outb(BT_CMD_DATA_PORT, BT_MBO_INTR_EN); + wait = 100000; /* 1 sec enough? */ + for (i = wait; i; i--) { + if (!(inb(BT_CTRL_STAT_PORT) & BT_CDF)) + break; + DELAY(10); + } + if (i == 0) { + printf("bt%d: bt_intr, cmd/data port full\n", unit); + outb(BT_CTRL_STAT_PORT, BT_SRST); + return 1; + } + outb(BT_CMD_DATA_PORT, 0x00); /* Disable */ + wakeup((caddr_t)&bt->bt_mbx); + outb(BT_CTRL_STAT_PORT, BT_IRST); + return 1; + } + if (!(stat & BT_MBIF)) { + outb(BT_CTRL_STAT_PORT, BT_IRST); + return 1; + } + /* + * If it IS then process the competed operation + */ + wmbx = &bt->bt_mbx; + wmbi = wmbx->tmbi; + AGAIN: + while (wmbi->stat != BT_MBI_FREE) { + ccb = bt_ccb_phys_kv(bt, (wmbi->ccb_addr)); + if (!ccb) { + wmbi->stat = BT_MBI_FREE; + printf("bt: BAD CCB ADDR!\n"); + continue; + } + found++; + if ((stat = wmbi->stat) != BT_MBI_OK) { + switch (stat) { + case BT_MBI_ABORT: +#ifdef UTEST + if (bt_debug & BT_SHOWMISC) + printf("abort "); +#endif + ccb->host_stat = BT_ABORTED; + break; + + case BT_MBI_UNKNOWN: + ccb = (struct bt_ccb *) 0; +#ifdef UTEST + if (bt_debug & BT_SHOWMISC) + printf("unknown ccb for abort"); +#endif + break; + + case BT_MBI_ERROR: + break; + + default: + panic("Impossible mbxi status"); + + } +#ifdef UTEST + if ((bt_debug & BT_SHOWCMDS) && ccb) { + u_char *cp; + cp = ccb->scsi_cmd; + printf("op=%x %x %x %x %x %x\n", + cp[0], cp[1], cp[2], + cp[3], cp[4], cp[5]); + printf("stat %x for mbi addr = 0x%08x\n" + ,wmbi->stat, wmbi); + printf("addr = 0x%x\n", ccb); + } +#endif + } + wmbi->stat = BT_MBI_FREE; + if (ccb) { + untimeout(bt_timeout, (caddr_t)ccb); + bt_done(unit, ccb); + } + /* Set the IN mail Box pointer for next */ bt_nextmbx(wmbi, wmbx, mbi); + } + if (!found) { + for (i = 0; i < BT_MBX_SIZE; i++) { + if (wmbi->stat != BT_MBI_FREE) { + found++; + break; + } + bt_nextmbx(wmbi, wmbx, mbi); + } + if (!found) { + printf("bt%d: mbi at 0x%08x should be found, stat=%02x..resync\n", + unit, wmbi, stat); + } else { + found = 0; + goto AGAIN; + } + } + wmbx->tmbi = wmbi; + outb(BT_CTRL_STAT_PORT, BT_IRST); + return 1; +} + +/* + * A ccb is put onto the free list. + */ +void +bt_free_ccb(unit, ccb, flags) + int unit; + struct bt_ccb *ccb; + int flags; +{ + struct bt_data *bt = btdata[unit]; + unsigned int opri = 0; + + if (!(flags & SCSI_NOMASK)) + opri = splbio(); + + ccb->next = bt->bt_ccb_free; + bt->bt_ccb_free = ccb; + ccb->flags = CCB_FREE; + /* + * If there were none, wake anybody waiting for one to come free, + * starting with queued entries. + */ + if (!ccb->next) { + wakeup((caddr_t)&bt->bt_ccb_free); + } + + if (!(flags & SCSI_NOMASK)) + splx(opri); +} + +/* + * Get a free ccb + * + * If there are none, see if we can allocate a new one. If so, put it in + * the hash table too otherwise either return an error or sleep. + */ +struct bt_ccb * +bt_get_ccb(unit, flags) + int unit; + int flags; +{ + struct bt_data *bt = btdata[unit]; + unsigned opri = 0; + struct bt_ccb *ccbp; + struct bt_mbx *wmbx; /* Mail Box pointer specified unit */ + BT_MBO *wmbo; /* Out Mail Box pointer */ + int hashnum; + + if (!(flags & SCSI_NOMASK)) + opri = splbio(); + /* + * If we can and have to, sleep waiting for one to come free + * but only if we can't allocate a new one. + */ + while (!(ccbp = bt->bt_ccb_free)) { + if (bt->numccbs < BT_CCB_MAX) { + if (ccbp = (struct bt_ccb *) malloc(sizeof(struct bt_ccb), + M_TEMP, + M_NOWAIT)) { + bzero(ccbp, sizeof(struct bt_ccb)); + bt->numccbs++; + ccbp->flags = CCB_ACTIVE; + /* + * put in the phystokv hash table + * Never gets taken out. + */ + ccbp->hashkey = KVTOPHYS(ccbp); + hashnum = CCB_HASH(ccbp->hashkey); + ccbp->nexthash = bt->ccbhash[hashnum]; + bt->ccbhash[hashnum] = ccbp; + } else { + printf("bt%d: Can't malloc CCB\n", unit); + } + goto gottit; + } else { + if (!(flags & SCSI_NOSLEEP)) { + tsleep((caddr_t)&bt->bt_ccb_free, PRIBIO, + "btccb", 0); + } + } + } + if (ccbp) { + /* Get CCB from from free list */ + bt->bt_ccb_free = ccbp->next; + ccbp->flags = CCB_ACTIVE; + } + gottit: + if (!(flags & SCSI_NOMASK)) + splx(opri); + + return (ccbp); +} + +/* + * given a physical address, find the ccb that + * it corresponds to: + */ +struct bt_ccb * +bt_ccb_phys_kv(bt, ccb_phys) + struct bt_data *bt; + physaddr ccb_phys; +{ + int hashnum = CCB_HASH(ccb_phys); + struct bt_ccb *ccbp = bt->ccbhash[hashnum]; + + while (ccbp) { + if (ccbp->hashkey == ccb_phys) + break; + ccbp = ccbp->nexthash; + } + return ccbp; +} + +/* + * Get a MBO and then Send it + */ +BT_MBO * +bt_send_mbo(int unit, int flags, int cmd, struct bt_ccb *ccb) +{ + struct bt_data *bt = btdata[unit]; + unsigned opri = 0; + BT_MBO *wmbo; /* Mail Box Out pointer */ + struct bt_mbx *wmbx; /* Mail Box pointer specified unit */ + int i, wait; + + wmbx = &bt->bt_mbx; + + if (!(flags & SCSI_NOMASK)) + opri = splbio(); + + /* Get the Target OUT mail Box pointer and move to Next */ + wmbo = wmbx->tmbo; + wmbx->tmbo = (wmbo == &(wmbx->mbo[BT_MBX_SIZE - 1]) ? + &(wmbx->mbo[0]) : wmbo + 1); + + /* + * Check the outmail box is free or not. + * Note: Under the normal operation, it shuld NOT happen to wait. + */ + while (wmbo->cmd != BT_MBO_FREE) { + wait = 100000; /* 1 sec enough? */ + /* Enable MBO available interrupt */ + outb(BT_CMD_DATA_PORT, BT_MBO_INTR_EN); + for (i = wait; i; i--) { + if (!(inb(BT_CTRL_STAT_PORT) & BT_CDF)) + break; + DELAY(10); + } + if (i == 0) { + printf("bt%d: bt_send_mbo, cmd/data port full\n", unit); + outb(BT_CTRL_STAT_PORT, BT_SRST); + return ((BT_MBO *) 0); + } + outb(BT_CMD_DATA_PORT, 0x01); /* Enable */ + tsleep((caddr_t)wmbx, PRIBIO, "btsend", 0); + /* XXX */ /*can't do this! */ + /* May be servicing an int */ + } + /* Link CCB to the Mail Box */ + wmbo->ccb_addr = KVTOPHYS(ccb); + ccb->mbx = wmbo; + wmbo->cmd = cmd; + + /* Send it! */ + outb(BT_CMD_DATA_PORT, BT_START_SCSI); + + if (!(flags & SCSI_NOMASK)) + splx(opri); + + return (wmbo); +} + +/* + * We have a ccb which has been processed by the + * adaptor, now we look to see how the operation + * went. Wake up the owner if waiting + */ +void +bt_done(unit, ccb) + int unit; + struct bt_ccb *ccb; +{ + struct bt_data *bt = btdata[unit]; + struct scsi_sense_data *s1, *s2; + struct scsi_xfer *xs = ccb->xfer; + + SC_DEBUG(xs->sc_link, SDEV_DB2, ("bt_done\n")); + /* + * Otherwise, put the results of the operation + * into the xfer and call whoever started it + */ + if ((ccb->host_stat != BT_OK || ccb->target_stat != SCSI_OK) + && (!(xs->flags & SCSI_ERR_OK))) { + + s1 = &(ccb->scsi_sense); + s2 = &(xs->sense); + + if (ccb->host_stat) { + switch (ccb->host_stat) { + case BT_ABORTED: /* No response */ + case BT_SEL_TIMEOUT: /* No response */ + SC_DEBUG(xs->sc_link, SDEV_DB3, + ("timeout reported back\n")); + xs->error = XS_TIMEOUT; + break; + default: /* Other scsi protocol messes */ + xs->error = XS_DRIVER_STUFFUP; + SC_DEBUG(xs->sc_link, SDEV_DB3, + ("unexpected host_stat: %x\n", + ccb->host_stat)); + } + } else { + switch (ccb->target_stat) { + case 0x02: + *s2 = *s1; + xs->error = XS_SENSE; + break; + case 0x08: + xs->error = XS_BUSY; + break; + default: + SC_DEBUG(xs->sc_link, SDEV_DB3, + ("unexpected target_stat: %x\n", + ccb->target_stat)); + xs->error = XS_DRIVER_STUFFUP; + } + } + } else { /* All went correctly OR errors expected */ + xs->resid = 0; + } + xs->flags |= ITSDONE; + bt_free_ccb(unit, ccb, xs->flags); + scsi_done(xs); +} + +/* + * Start the board, ready for normal operation + */ +int +bt_init(unit) + int unit; +{ + struct bt_data *bt = btdata[unit]; + unsigned char ad[4]; + volatile int i, sts; + struct bt_config conf; + struct bt_ext_info info; + struct bt_board_info binfo; + + /* + * reset board, If it doesn't respond, assume + * that it's not there.. good for the probe + */ + + outb(BT_CTRL_STAT_PORT, BT_HRST | BT_SRST); + + for (i = BT_RESET_TIMEOUT; i; i--) { + sts = inb(BT_CTRL_STAT_PORT); + if (sts == (BT_IDLE | BT_INIT)) + break; + DELAY(1000); + } + if (i == 0) { +#ifdef UTEST + printf("bt_init: No answer from board\n"); +#endif + return (ENXIO); + } + + /* + * Displaying Board ID and Hardware Revision + * 94/05/18 amurai@spec.co.jp + */ + bt_cmd(unit, 1, sizeof(binfo),0,&binfo,BT_GET_BOARD_INFO,sizeof(binfo)); + printf("bt%d: Bt%c%c%c%c/%c%d-", unit, + binfo.id[0], + binfo.id[1], + binfo.id[2], + binfo.id[3], + binfo.ver[0], + (unsigned) binfo.ver[1] + ); + + /* + * Make sure board has a capability of 32bit addressing. + * and Firmware also need a capability of 32bit addressing pointer + * in Extended mailbox and ccb structure. + * 94/05/18 amurai@spec.co.jp + */ + bt_cmd(unit, 1, sizeof(info),0,&info, BT_INQUIRE_EXTENDED,sizeof(info)); + switch (info.bus_type) { + case BT_BUS_TYPE_24bit: /* PC/AT 24 bit address bus */ + printf("ISA(24bit) bus\n"); + break; + case BT_BUS_TYPE_32bit: /* EISA/VLB/PCI 32 bit bus */ + printf("PCI/EISA/VLB(32bit) bus\n"); + break; + case BT_BUS_TYPE_MCA: /* forget it right now */ + printf("MCA bus architecture..."); + printf("giving up\n"); + return (ENXIO); + break; + default: + printf("Unknown state..."); + printf("giving up\n"); + return (ENXIO); + break; + } + if ( binfo.id[0] == '5' ) { + printf("bt%d: This driver is designed for using 32 bit addressing\n",unit); + printf("bt%d: mode firmware and EISA/PCI/VLB bus architecture bus\n",unit); + printf("bt%d: WITHOUT any software trick/overhead (i.e.bounce buffer).\n",unit); + printf("bt%d: If you have more than 16MBytes memory\n",unit); + printf("bt%d: your filesystem will get a serious damage.\n",unit); + } else if ( info.bus_type == BT_BUS_TYPE_24bit ) { + printf("bt%d: Your board should report a 32bit bus architecture type..\n",unit); + printf("bt%d: A firmware on your board may have a problem with over\n",unit); + printf("bt%d: 16MBytes memory handling with this driver.\n",unit); + } + + /* + * Assume we have a board at this stage + * setup dma channel from jumpers and save int + * level + */ + printf("bt%d: reading board settings, ", unit); + + bt_cmd(unit, 0, sizeof(conf), 0, &conf, BT_CONF_GET); + switch (conf.chan) { + case EISADMA: + bt->bt_dma = -1; + break; + case CHAN0: + outb(0x0b, 0x0c); + outb(0x0a, 0x00); + bt->bt_dma = 0; + break; + case CHAN5: + outb(0xd6, 0xc1); + outb(0xd4, 0x01); + bt->bt_dma = 5; + break; + case CHAN6: + outb(0xd6, 0xc2); + outb(0xd4, 0x02); + bt->bt_dma = 6; + break; + case CHAN7: + outb(0xd6, 0xc3); + outb(0xd4, 0x03); + bt->bt_dma = 7; + break; + default: + printf("illegal dma setting %x\n", conf.chan); + return (EIO); + } + if (bt->bt_dma == -1) + printf("busmastering, "); + else + printf("dma=%d, ", bt->bt_dma); + + switch (conf.intr) { + case INT9: + bt->bt_int = 9; + break; + case INT10: + bt->bt_int = 10; + break; + case INT11: + bt->bt_int = 11; + break; + case INT12: + bt->bt_int = 12; + break; + case INT14: + bt->bt_int = 14; + break; + case INT15: + bt->bt_int = 15; + break; + default: + printf("illegal int setting\n"); + return (EIO); + } + printf("int=%d\n", bt->bt_int); + + /* who are we on the scsi bus */ + bt->bt_scsi_dev = conf.scsi_dev; + /* + * Initialize mail box + */ + *((physaddr *) ad) = KVTOPHYS(&bt->bt_mbx); + bt_cmd(unit, 5, 0, 0, 0, BT_MBX_INIT_EXTENDED + ,BT_MBX_SIZE + ,ad[0] + ,ad[1] + ,ad[2] + ,ad[3]); + + /* + * Set Pointer chain null for just in case + * Link the ccb's into a free-list W/O mbox + * Initialize mail box status to free + */ + if (bt->bt_ccb_free != (struct bt_ccb *) 0) { + printf("bt%d: bt_ccb_free is NOT initialized but init here\n", + unit); + bt->bt_ccb_free = (struct bt_ccb *) 0; + } + for (i = 0; i < BT_MBX_SIZE; i++) { + bt->bt_mbx.mbo[i].cmd = BT_MBO_FREE; + bt->bt_mbx.mbi[i].stat = BT_MBI_FREE; + } + /* + * Set up initial mail box for round-robin operation. + */ + bt->bt_mbx.tmbo = &bt->bt_mbx.mbo[0]; + bt->bt_mbx.tmbi = &bt->bt_mbx.mbi[0]; + bt_inquire_setup_information(unit, &info); + + + /* + * Note that we are going and return (to probe) + */ + return 0; +} + +void +bt_inquire_setup_information( + int unit, + struct bt_ext_info *info ) +{ + struct bt_data *bt = btdata[unit]; + struct bt_setup setup; + struct bt_sync_value sync; + char dummy[8]; + char sub_ver[3]; + struct bt_boardID bID; + int i; + + /* Inquire Installed Devices */ + bzero( &dummy[0], sizeof(dummy) ); + bt_cmd(unit, 0, sizeof(dummy), 100, &dummy[0], BT_DEV_GET); + + /* + * If board has a capbility of Syncrhonouse mode, + * Get a SCSI Synchronous value + */ + if ( info->s.sync ) { + bt_cmd(unit, 1, sizeof(sync), 100, + &sync,BT_GET_SYNC_VALUE,sizeof(sync)); + } + + /* + * Inquire Board ID to board for firmware version + */ + bt_cmd(unit, 0, sizeof(bID), 0, &bID, BT_INQUIRE); + bt_cmd(unit, 0, 1, 0, &sub_ver[0], BT_INQUIRE_REV_THIRD ); + i = ((int)(bID.firm_revision-'0')) * 10 + (int)(bID.firm_version-'0'); + if ( i >= 33 ) { + bt_cmd(unit, 0, 1, 0, &sub_ver[1], BT_INQUIRE_REV_FOURTH ); + } else { + /* + * Below rev 3.3 firmware has a problem for issuing + * the BT_INQUIRE_REV_FOURTH command. + */ + sub_ver[1]='\0'; + } + sub_ver[2]='\0'; + if (sub_ver[1]==' ') + sub_ver[1]='\0'; + printf("bt%d: version %c.%c%s, ", + unit, bID.firm_revision, bID.firm_version, sub_ver ); + + /* + * Obtain setup information from board. + */ + bt_cmd(unit, 1, sizeof(setup), 0, &setup, BT_SETUP_GET, sizeof(setup)); + + if (setup.sync_neg && info->s.sync ) { + if ( info->s.maxsync ) { + printf("fast sync, "); /* Max 10MB/s */ + } else { + printf("sync, "); /* Max 5MB/s */ + } + } else { + if ( info->s.sync ) { + printf("async, "); /* Never try by board */ + } else { + printf("async only, "); /* Doesn't has a capability on board */ + } + } + if (setup.parity) { + printf("parity, "); + } else { + printf("no parity, "); + } + printf("%d mbxs, %d ccbs\n", setup.num_mbx, BT_CCB_MAX); + + /* + * Displayi SCSI negotiation value by each target. + * amurai@spec.co.jp + */ + for (i = 0; i < 8; i++) { + if (!setup.sync[i].valid ) + continue; + if ( (!setup.sync[i].offset && !setup.sync[i].period) + || !info->s.sync ) { + printf("bt%d: targ %d async\n", unit, i); + } else { + printf("bt%d: targ %d sync rate=%2d.%02dMB/s(%dns), offset=%02d\n", + unit, i, + 100 / sync.value[i], + (100 % sync.value[i]) * 100 / sync.value[i], + sync.value[i] * 10, + setup.sync[i].offset ); + } + } + + /* + * Enable round-robin scheme - appeared at firmware rev. 3.31 + * Below rev 3.XX firmware has a problem for issuing + * BT_ROUND_ROBIN command amurai@spec.co.jp + */ + if ( bID.firm_revision >= '3' ) { + printf("bt%d: Enabling Round robin scheme\n", unit); + bt_cmd(unit, 1, 0, 0, 0, BT_ROUND_ROBIN, BT_ENABLE); + } else { + printf("bt%d: Not Enabling Round robin scheme\n", unit); + } + +} + +#ifndef min +#define min(x,y) (x < y ? x : y) +#endif /* min */ + +void +btminphys(bp) + struct buf *bp; +{ + if (bp->b_bcount > ((BT_NSEG - 1) * PAGESIZ)) { + bp->b_bcount = ((BT_NSEG - 1) * PAGESIZ); + } +} + +/* + * start a scsi operation given the command and the data address. Also needs + * the unit, target and lu. + */ +int32 +bt_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + struct scsi_sense_data *s1, *s2; + struct bt_ccb *ccb; + struct bt_scat_gath *sg; + int seg; /* scatter gather seg being worked on */ + int i = 0; + int c = 0; + int thiskv; + physaddr thisphys, nextphys; + int unit = xs->sc_link->adapter_unit; + int bytes_this_seg, bytes_this_page, datalen, flags; + struct iovec *iovp; + struct bt_data *bt = btdata[unit]; + BT_MBO *mbo; + + SC_DEBUG(xs->sc_link, SDEV_DB2, ("bt_scsi_cmd\n")); + /* + * get a ccb (mbox-out) to use. If the transfer + * is from a buf (possibly from interrupt time) + * then we can't allow it to sleep + */ + flags = xs->flags; + if (xs->bp) + flags |= (SCSI_NOSLEEP); /* just to be sure */ + if (flags & ITSDONE) { + printf("bt%d: Already done?\n", unit); + xs->flags &= ~ITSDONE; + } + if (!(flags & INUSE)) { + printf("bt%d: Not in use?\n", unit); + xs->flags |= INUSE; + } + if (!(ccb = bt_get_ccb(unit, flags))) { + xs->error = XS_DRIVER_STUFFUP; + return (TRY_AGAIN_LATER); + } + SC_DEBUG(xs->sc_link, SDEV_DB3, + ("start ccb(%x)\n", ccb)); + /* + * Put all the arguments for the xfer in the ccb + */ + ccb->xfer = xs; + if (flags & SCSI_RESET) { + ccb->opcode = BT_RESET_CCB; + } else { + /* can't use S/G if zero length */ + ccb->opcode = (xs->datalen ? + BT_INIT_SCAT_GATH_CCB + : BT_INITIATOR_CCB); + } + ccb->target = xs->sc_link->target; + ccb->data_out = 0; + ccb->data_in = 0; + ccb->lun = xs->sc_link->lun; + ccb->scsi_cmd_length = xs->cmdlen; + ccb->sense_ptr = KVTOPHYS(&(ccb->scsi_sense)); + ccb->req_sense_length = sizeof(ccb->scsi_sense); + + if ((xs->datalen) && (!(flags & SCSI_RESET))) { /* can use S/G only if not zero length */ + ccb->data_addr = KVTOPHYS(ccb->scat_gath); + sg = ccb->scat_gath; + seg = 0; +#ifdef TFS + if (flags & SCSI_DATA_UIO) { + iovp = ((struct uio *) xs->data)->uio_iov; + datalen = ((struct uio *) xs->data)->uio_iovcnt; + xs->datalen = 0; + while ((datalen) && (seg < BT_NSEG)) { + sg->seg_addr = (physaddr) iovp->iov_base; + xs->datalen += sg->seg_len = iovp->iov_len; + SC_DEBUGN(xs->sc_link, SDEV_DB4, ("(0x%x@0x%x)" + ,iovp->iov_len, iovp->iov_base)); + sg++; + iovp++; + seg++; + datalen--; + } + } else +#endif /* TFS */ + { + /* + * Set up the scatter gather block + */ + + SC_DEBUG(xs->sc_link, SDEV_DB4, + ("%d @0x%x:- ", xs->datalen, xs->data)); + datalen = xs->datalen; + thiskv = (int) xs->data; + thisphys = KVTOPHYS(thiskv); + + while ((datalen) && (seg < BT_NSEG)) { + bytes_this_seg = 0; + + /* put in the base address */ + sg->seg_addr = thisphys; + + SC_DEBUGN(xs->sc_link, SDEV_DB4, + ("0x%x", thisphys)); + + /* do it at least once */ + nextphys = thisphys; + while ((datalen) && (thisphys == nextphys)) + /* + * This page is contiguous (physically) with + * the the last, just extend the length + */ + { + /* how far to the end of the page */ + nextphys = (thisphys & (~(PAGESIZ - 1))) + + PAGESIZ; + bytes_this_page = nextphys - thisphys; + /**** or the data ****/ + bytes_this_page = min(bytes_this_page + ,datalen); + bytes_this_seg += bytes_this_page; + datalen -= bytes_this_page; + + /* get more ready for the next page */ + thiskv = (thiskv & (~(PAGESIZ - 1))) + + PAGESIZ; + if (datalen) + thisphys = KVTOPHYS(thiskv); + } + /* + * next page isn't contiguous, finish the seg + */ + SC_DEBUGN(xs->sc_link, SDEV_DB4, + ("(0x%x)", bytes_this_seg)); + sg->seg_len = bytes_this_seg; + sg++; + seg++; + } + } + /* end of iov/kv decision */ + ccb->data_length = seg * sizeof(struct bt_scat_gath); + SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n")); + if (datalen) { + /* + * there's still data, must have run out of segs! + */ + printf("bt%d: bt_scsi_cmd, more than %d DMA segs\n", + unit, BT_NSEG); + xs->error = XS_DRIVER_STUFFUP; + bt_free_ccb(unit, ccb, flags); + return (HAD_ERROR); + } + } else { /* No data xfer, use non S/G values */ + ccb->data_addr = (physaddr) 0; + ccb->data_length = 0; + } + ccb->link_id = 0; + ccb->link_addr = (physaddr) 0; + /* + * Put the scsi command in the ccb and start it + */ + if (!(flags & SCSI_RESET)) { + bcopy(xs->cmd, ccb->scsi_cmd, ccb->scsi_cmd_length); + } + if (bt_send_mbo(unit, flags, BT_MBO_START, ccb) == (BT_MBO *) 0) { + xs->error = XS_DRIVER_STUFFUP; + bt_free_ccb(unit, ccb, flags); + return (TRY_AGAIN_LATER); + } + /* + * Usually return SUCCESSFULLY QUEUED + */ + SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_sent\n")); + if (!(flags & SCSI_NOMASK)) { + timeout(bt_timeout, (caddr_t)ccb, (xs->timeout * hz) / 1000); + return (SUCCESSFULLY_QUEUED); + } + /* + * If we can't use interrupts, poll on completion + */ + return (bt_poll(unit, xs, ccb)); +} + +/* + * Poll a particular unit, looking for a particular xs + */ +int +bt_poll(unit, xs, ccb) + int unit; + struct scsi_xfer *xs; + struct bt_ccb *ccb; +{ + struct bt_data *bt = btdata[unit]; + int done = 0; + int count = xs->timeout; + u_char stat; + + /* timeouts are in msec, so we loop in 1000 usec cycles */ + while (count) { + /* + * If we had interrupts enabled, would we + * have got an interrupt? + */ + stat = inb(BT_INTR_PORT); + if (stat & BT_ANY_INTR) { + btintr(unit); + } + if (xs->flags & ITSDONE) { + break; + } + DELAY(1000); /* only happens in boot so ok */ + count--; + } + if (count == 0) { + /* + * We timed out, so call the timeout handler manually, + * accounting for the fact that the clock is not running yet + * by taking out the clock queue entry it makes. + */ + bt_timeout((caddr_t)ccb, 0); + + /* + * because we are polling, take out the timeout entry + * bt_timeout made + */ + untimeout(bt_timeout, (caddr_t)ccb); + count = 2000; + while (count) { + /* + * Once again, wait for the int bit + */ + stat = inb(BT_INTR_PORT); + if (stat & BT_ANY_INTR) { + btintr(unit); + } + if (xs->flags & ITSDONE) { + break; + } + DELAY(1000); /* only happens in boot so ok */ + count--; + } + if (count == 0) { + /* + * We timed out again... This is bad. Notice that + * this time there is no clock queue entry to remove. + */ + bt_timeout((caddr_t)ccb, 0); + } + } + if (xs->error) + return (HAD_ERROR); + return (COMPLETE); +} + +void +bt_timeout(caddr_t arg1, int arg2) +{ + struct bt_ccb * ccb = (struct bt_ccb *)arg1; + int unit; + struct bt_data *bt; + int s = splbio(); + + /* + * A timeout routine in kernel DONOT unlink + * Entry chains when time outed....So infinity Loop.. + * 94/04/20 amurai@spec.co.jp + */ + untimeout(bt_timeout, (caddr_t)ccb); + + unit = ccb->xfer->sc_link->adapter_unit; + bt = btdata[unit]; + +#ifdef UTEST + bt_print_active_ccbs(unit); +#endif + + /* + * If the ccb's mbx is not free, then the board has gone Far East? + */ + if (bt_ccb_phys_kv(bt, ccb->mbx->ccb_addr) == ccb && + ccb->mbx->cmd != BT_MBO_FREE) { + printf("bt%d: not taking commands!\n", unit); + Debugger("bt742a"); + } + /* + * If it has been through before, then + * a previous abort has failed, don't + * try abort again + */ + if (ccb->flags == CCB_ABORTED) { + /* + * abort timed out + */ + printf("bt%d: Abort Operation has timed out\n", unit); + ccb->xfer->retries = 0; /* I MEAN IT ! */ + ccb->host_stat = BT_ABORTED; + bt_done(unit, ccb); + } else { + /* abort the operation that has timed out */ + printf("bt%d: Try to abort\n", unit); + bt_send_mbo(unit, ~SCSI_NOMASK, + BT_MBO_ABORT, ccb); + /* 2 secs for the abort */ + ccb->flags = CCB_ABORTED; + timeout(bt_timeout, (caddr_t)ccb, 2 * hz); + } + splx(s); +} + +#ifdef UTEST +void +bt_print_ccb(ccb) + struct bt_ccb *ccb; +{ + printf("ccb:%x op:%x cmdlen:%d senlen:%d\n" + ,ccb + ,ccb->opcode + ,ccb->scsi_cmd_length + ,ccb->req_sense_length); + printf(" datlen:%d hstat:%x tstat:%x flags:%x\n" + ,ccb->data_length + ,ccb->host_stat + ,ccb->target_stat + ,ccb->flags); +} + +void +bt_print_active_ccbs(int unit) +{ + struct bt_data *bt = btdata[unit]; + struct bt_ccb *ccb; + int i = 0; + + while (i < CCB_HASH_SIZE) { + ccb = bt->ccbhash[i]; + while (ccb) { + if (ccb->flags != CCB_FREE) + bt_print_ccb(ccb); + ccb = ccb->nexthash; + } + i++; + } +} +#endif /*UTEST */ +#endif /*KERNEL */ diff --git a/sys/i386/isa/clock.c b/sys/i386/isa/clock.c index cd87b28b90e6..5832801d24ae 100644 --- a/sys/i386/isa/clock.c +++ b/sys/i386/isa/clock.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)clock.c 7.2 (Berkeley) 5/12/91 - * $Id: clock.c,v 1.6 1994/02/06 22:48:13 davidg Exp $ + * $Id: clock.c,v 1.9 1994/05/02 09:41:24 sos Exp $ */ /* @@ -45,6 +45,7 @@ #include "time.h" #include "kernel.h" #include "machine/segments.h" +#include "machine/frame.h" #include "i386/isa/icu.h" #include "i386/isa/isa.h" #include "i386/isa/rtc.h" @@ -55,22 +56,225 @@ #ifndef TIMER_FREQ #define TIMER_FREQ 1193182 /* XXX - should be in isa.h */ #endif +#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x)) + +void hardclock(); +static int beeping; +int timer0_divisor = TIMER_DIV(100); /* XXX should be hz */ +u_int timer0_prescale; +static char timer0_state = 0, timer2_state = 0; +static char timer0_reprogram = 0; +static void (*timer_func)() = hardclock; +static void (*new_function)(); +static u_int new_rate; +static u_int hardclock_divisor; + + +void +timerintr(struct intrframe frame) +{ + timer_func(frame); + switch (timer0_state) { + case 0: + break; + case 1: + if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) { + hardclock(frame); + timer0_prescale = 0; + } + break; + case 2: + disable_intr(); + outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT); + outb(TIMER_CNTR0, TIMER_DIV(new_rate)%256); + outb(TIMER_CNTR0, TIMER_DIV(new_rate)/256); + enable_intr(); + timer0_divisor = TIMER_DIV(new_rate); + timer0_prescale = 0; + timer_func = new_function; + timer0_state = 1; + break; + case 3: + if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) { + hardclock(frame); + disable_intr(); + outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT); + outb(TIMER_CNTR0, TIMER_DIV(hz)%256); + outb(TIMER_CNTR0, TIMER_DIV(hz)/256); + enable_intr(); + timer0_divisor = TIMER_DIV(hz); + timer0_prescale = 0; + timer_func = hardclock;; + timer0_state = 0; + } + break; + } +} + + +int +acquire_timer0(int rate, void (*function)() ) +{ + if (timer0_state || !function) + return -1; + + new_function = function; + new_rate = rate; + timer0_state = 2; + return 0; +} + + +int +acquire_timer2(int mode) +{ + if (timer2_state) + return -1; + timer2_state = 1; + outb(TIMER_MODE, TIMER_SEL2 | (mode &0x3f)); + return 0; +} + + +int +release_timer0() +{ + if (!timer0_state) + return -1; + timer0_state = 3; + return 0; +} + + +int +release_timer2() +{ + if (!timer2_state) + return -1; + timer2_state = 0; + outb(TIMER_MODE, TIMER_SEL2|TIMER_SQWAVE|TIMER_16BIT); + return 0; +} + + +static int +getit() +{ + int high, low; + + disable_intr(); + /* select timer0 and latch counter value */ + outb(TIMER_MODE, TIMER_SEL0); + low = inb(TIMER_CNTR0); + high = inb(TIMER_CNTR0); + enable_intr(); + return ((high << 8) | low); +} + + +/* + * Wait "n" microseconds. + * Relies on timer 1 counting down from (TIMER_FREQ / hz) + * Note: timer had better have been programmed before this is first used! + */ +void +DELAY(int n) +{ + int counter_limit, prev_tick, tick, ticks_left, sec, usec; + +#ifdef DELAYDEBUG + int getit_calls = 1; + int n1; + static int state = 0; + + if (state == 0) { + state = 1; + for (n1 = 1; n1 <= 10000000; n1 *= 10) + DELAY(n1); + state = 2; + } + if (state == 1) + printf("DELAY(%d)...", n); +#endif + /* + * Read the counter first, so that the rest of the setup overhead is + * counted. Guess the initial overhead is 20 usec (on most systems it + * takes about 1.5 usec for each of the i/o's in getit(). The loop + * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The + * multiplications and divisions to scale the count take a while). + */ + prev_tick = getit(0, 0); + n -= 20; + /* + * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point + * and without any avoidable overflows. + */ + sec = n / 1000000; + usec = n - sec * 1000000; + ticks_left = sec * TIMER_FREQ + + usec * (TIMER_FREQ / 1000000) + + usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 + + usec * (TIMER_FREQ % 1000) / 1000000; + + while (ticks_left > 0) { + tick = getit(0, 0); +#ifdef DELAYDEBUG + ++getit_calls; +#endif + if (tick > prev_tick) + ticks_left -= prev_tick - (tick - timer0_divisor); + else + ticks_left -= prev_tick - tick; + prev_tick = tick; + } +#ifdef DELAYDEBUG + if (state == 1) + printf(" %d calls to getit() at %d usec each\n", + getit_calls, (n + 5) / getit_calls); +#endif +} + + +static void +sysbeepstop() +{ + outb(IO_PPI, inb(IO_PPI)&0xFC); /* disable counter2 output to speaker */ + release_timer2(); + beeping = 0; +} + + +int +sysbeep(int pitch, int period) +{ + + if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT)) + return -1; + disable_intr(); + outb(TIMER_CNTR2, pitch); + outb(TIMER_CNTR2, (pitch>>8)); + enable_intr(); + if (!beeping) { + outb(IO_PPI, inb(IO_PPI) | 3); /* enable counter2 output to speaker */ + beeping = period; + timeout(sysbeepstop, 0, period); + } + return 0; +} -static void findcpuspeed(void); void startrtclock() { int s; - findcpuspeed(); /* use the clock (while it's free) - to find the cpu speed */ /* initialize 8253 clock */ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT); /* Correct rounding will buy us a better precision in timekeeping */ - outb (IO_TIMER1, (TIMER_FREQ+hz/2)/hz); - outb (IO_TIMER1, ((TIMER_FREQ+hz/2)/hz)/256); + outb (IO_TIMER1, TIMER_DIV(hz)%256); + outb (IO_TIMER1, TIMER_DIV(hz)/256); + timer0_divisor = hardclock_divisor = TIMER_DIV(hz); /* initialize brain-dead battery powered clock */ outb (IO_RTC, RTC_STATUSA); @@ -83,44 +287,18 @@ startrtclock() printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS); } -unsigned int delaycount; /* calibrated loop variable (1 millisecond) */ - -#define FIRST_GUESS 0x2000 -static void -findcpuspeed() -{ - unsigned char low; - unsigned int remainder; - - /* Put counter in count down mode */ - outb(IO_TIMER1+3, 0x34); - outb(IO_TIMER1, 0xff); - outb(IO_TIMER1, 0xff); - delaycount = FIRST_GUESS; - spinwait(1); - /* Read the value left in the counter */ - low = inb(IO_TIMER1); /* least siginifcant */ - remainder = inb(IO_TIMER1); /* most significant */ - remainder = (remainder<<8) + low ; - /* Formula for delaycount is : - * (loopcount * timer clock speed)/ (counter ticks * 1000) - */ - delaycount = (FIRST_GUESS * (TIMER_FREQ/1000)) / (0xffff-remainder); -} - /* convert 2 digit BCD number */ int -bcd(i) - int i; +bcd(int i) { return ((i/16)*10 + (i%16)); } + /* convert years to seconds (from 1970) */ unsigned long -ytos(y) -int y; +ytos(int y) { int i; unsigned long ret; @@ -133,16 +311,16 @@ int y; return ret; } + /* convert months to seconds */ unsigned long -mtos(m,leap) -int m,leap; +mtos(int m, int leap) { int i; unsigned long ret; ret = 0; - for(i=1;i<m;i++) { + for(i=1; i<m; i++) { switch(i){ case 1: case 3: case 5: case 7: case 8: case 10: case 12: ret += 31*24*60*60; break; @@ -162,11 +340,10 @@ int m,leap; * from a filesystem. */ void -inittodr(base) - time_t base; +inittodr(time_t base) { unsigned long sec; - int leap,day_week,t,yd; + int leap, day_week, t, yd; int sa,s; /* do we have a realtime clock present? (otherwise we loop below) */ @@ -180,26 +357,25 @@ inittodr(base) sec = bcd(rtcin(RTC_YEAR)) + 1900; if (sec < 1970) sec += 100; - leap = !(sec % 4); sec = ytos(sec); /* year */ - yd = mtos(bcd(rtcin(RTC_MONTH)),leap); sec += yd; /* month */ - t = (bcd(rtcin(RTC_DAY))-1) * 24*60*60; sec += t; yd += t; /* date */ + + leap = !(sec % 4); sec = ytos(sec); /* year */ + yd = mtos(bcd(rtcin(RTC_MONTH)),leap); sec+=yd; /* month */ + t = (bcd(rtcin(RTC_DAY))-1) * 24*60*60; sec+=t; yd+=t; /* date */ day_week = rtcin(RTC_WDAY); /* day */ sec += bcd(rtcin(RTC_HRS)) * 60*60; /* hour */ sec += bcd(rtcin(RTC_MIN)) * 60; /* minutes */ sec += bcd(rtcin(RTC_SEC)); /* seconds */ - sec += tz.tz_minuteswest * 60; - time.tv_sec = sec; } + #ifdef garbage /* * Initialze the time of day register, based on the time base which is, e.g. * from a filesystem. */ -test_inittodr(base) - time_t base; +test_inittodr(time_t base) { outb(IO_RTC,9); /* year */ @@ -219,6 +395,7 @@ test_inittodr(base) } #endif + /* * Restart the clock. */ @@ -227,12 +404,14 @@ resettodr() { } + /* * Wire clock interrupt in. */ #define V(s) __CONCAT(V, s) extern void V(clk)(); + void enablertclock() { @@ -240,12 +419,12 @@ enablertclock() INTREN(IRQ0); } + /* * Delay for some number of milliseconds. */ void -spinwait(millisecs) - int millisecs; +spinwait(int millisecs) { DELAY(1000 * millisecs); } diff --git a/sys/i386/isa/com.c b/sys/i386/isa/com.c index dcaf878685be..25b4dee3238c 100644 --- a/sys/i386/isa/com.c +++ b/sys/i386/isa/com.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: com.c,v 1.7 1993/12/19 00:50:32 wollman Exp $ + * $Id: com.c,v 1.10 1994/05/30 03:14:13 ache Exp $ */ #include "com.h" @@ -86,7 +86,7 @@ int comconsinit; int comdefaultrate = TTYDEF_SPEED; int commajor; short com_addr[NCOM]; -struct tty com_tty[NCOM]; +struct tty *com_tty[NCOM]; struct speedtab comspeedtab[] = { 0, 0, @@ -199,12 +199,11 @@ comopen(int /*dev_t*/ dev, int flag, int mode, struct proc *p) unit = UNIT(dev); if (unit >= NCOM || (com_active & (1 << unit)) == 0) return (ENXIO); - tp = &com_tty[unit]; + tp = com_tty[unit] = ttymalloc(com_tty[unit]); tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; if ((tp->t_state & TS_ISOPEN) == 0) { - tp->t_state |= TS_WOPEN; ttychars(tp); if (tp->t_ispeed == 0) { tp->t_iflag = TTYDEF_IFLAG; @@ -223,9 +222,8 @@ comopen(int /*dev_t*/ dev, int flag, int mode, struct proc *p) tp->t_state |= TS_CARR_ON; while ((flag&O_NONBLOCK) == 0 && (tp->t_cflag&CLOCAL) == 0 && (tp->t_state & TS_CARR_ON) == 0) { - tp->t_state |= TS_WOPEN; - if (error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH, - ttopen, 0)) + if (error = tsleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH, + "comdcd", 0)) break; } (void) spl0(); @@ -247,7 +245,7 @@ comclose(dev, flag, mode, p) unit = UNIT(dev); com = com_addr[unit]; - tp = &com_tty[unit]; + tp = com_tty[unit]; (*linesw[tp->t_line].l_close)(tp, flag); outb(com+com_cfcr, inb(com+com_cfcr) & ~CFCR_SBREAK); #ifdef KGDB @@ -255,10 +253,16 @@ comclose(dev, flag, mode, p) if (kgdb_dev != makedev(commajor, unit)) #endif outb(com+com_ier, 0); - if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN || - (tp->t_state&TS_ISOPEN) == 0) + if (tp->t_cflag&HUPCL || (tp->t_state&TS_ISOPEN) == 0) (void) commctl(dev, 0, DMSET); ttyclose(tp); +#ifdef broken /* session holds a ref to the tty; can't deallocate */ + ttyfree(tp); + com_tty[unit] = (struct tty *)NULL; +#endif + return (0); + + return(0); } @@ -268,7 +272,7 @@ comread(dev, uio, flag) struct uio *uio; int flag; { - register struct tty *tp = &com_tty[UNIT(dev)]; + register struct tty *tp = com_tty[UNIT(dev)]; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } @@ -280,7 +284,7 @@ comwrite(dev, uio, flag) int flag; { int unit = UNIT(dev); - register struct tty *tp = &com_tty[unit]; + register struct tty *tp = com_tty[unit]; /* * (XXX) We disallow virtual consoles if the physical console is @@ -309,7 +313,7 @@ comintr(unit) return; case IIR_RXTOUT: case IIR_RXRDY: - tp = &com_tty[unit]; + tp = com_tty[unit]; /* * Process received bytes. Inline for speed... */ @@ -340,7 +344,7 @@ comintr(unit) } break; case IIR_TXRDY: - tp = &com_tty[unit]; + tp = com_tty[unit]; tp->t_state &=~ (TS_BUSY|TS_FLUSH); if (tp->t_line) (*linesw[tp->t_line].l_start)(tp); @@ -371,7 +375,7 @@ comeint(unit, stat, com) register struct tty *tp; register int c; - tp = &com_tty[unit]; + tp = com_tty[unit]; c = inb(com+com_data); if ((tp->t_state & TS_ISOPEN) == 0) { #ifdef KGDB @@ -401,7 +405,7 @@ commint(unit, com) register struct tty *tp; register int stat; - tp = &com_tty[unit]; + tp = com_tty[unit]; stat = inb(com+com_msr); if ((stat & MSR_DDCD) && (comsoftCAR & (1 << unit)) == 0) { if (stat & MSR_DCD) @@ -432,7 +436,7 @@ comioctl(dev, cmd, data, flag) register com; register int error; - tp = &com_tty[unit]; + tp = com_tty[unit]; error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); if (error >= 0) return (error); @@ -545,26 +549,17 @@ comstart(tp) s = spltty(); if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) goto out; - if (RB_LEN(&tp->t_out) <= tp->t_lowat) { - if (tp->t_state&TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_out); - } - if (tp->t_wsel) { - selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); - tp->t_wsel = 0; - tp->t_state &= ~TS_WCOLL; - } - } - if (RB_LEN(&tp->t_out) == 0) + if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) + ttwwakeup(tp); + if (RB_LEN(tp->t_out) == 0) goto out; if (inb(com+com_lsr) & LSR_TXRDY) { - c = getc(&tp->t_out); + c = getc(tp->t_out); tp->t_state |= TS_BUSY; outb(com+com_data, c); if (com_hasfifo & (1 << unit)) - for (c = 1; c < 16 && RB_LEN(&tp->t_out); ++c) - outb(com+com_data, getc(&tp->t_out)); + for (c = 1; c < 16 && RB_LEN(tp->t_out); ++c) + outb(com+com_data, getc(tp->t_out)); } out: splx(s); @@ -647,7 +642,7 @@ comcnprobe(cp) /* initialize required fields */ cp->cn_dev = makedev(commajor, unit); - cp->cn_tp = &com_tty[unit]; + cp->cn_tp = com_tty[unit]; #ifdef COMCONSOLE cp->cn_pri = CN_REMOTE; /* Force a serial port console */ #else @@ -747,43 +742,3 @@ comcnputc(dev, c) splx(s); } #endif - -int -comselect(dev, rw, p) - dev_t dev; - int rw; - struct proc *p; -{ - register struct tty *tp = &com_tty[UNIT(dev)]; - int nread; - int s = spltty(); - struct proc *selp; - - switch (rw) { - - case FREAD: - nread = ttnread(tp); - if (nread > 0 || - ((tp->t_cflag&CLOCAL) == 0 && (tp->t_state&TS_CARR_ON) == 0)) - goto win; - if (tp->t_rsel && (selp = pfind(tp->t_rsel)) && selp->p_wchan == (caddr_t)&selwait) - tp->t_state |= TS_RCOLL; - else - tp->t_rsel = p->p_pid; - break; - - case FWRITE: - if (RB_LEN(&tp->t_out) <= tp->t_lowat) - goto win; - if (tp->t_wsel && (selp = pfind(tp->t_wsel)) && selp->p_wchan == (caddr_t)&selwait) - tp->t_state |= TS_WCOLL; - else - tp->t_wsel = p->p_pid; - break; - } - splx(s); - return (0); - win: - splx(s); - return (1); -} diff --git a/sys/i386/isa/debug.h b/sys/i386/isa/debug.h deleted file mode 100644 index 92ecbd67f0fd..000000000000 --- a/sys/i386/isa/debug.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * from: debug.h, part of Bruce Evans interrupt code - * $Id: debug.h,v 1.3 1993/11/13 02:25:20 davidg Exp $ - */ - -#define SHOW_A_LOT_NOT - -#define BDBTRAP(name) \ - ss ; \ - cmpb $0,_bdb_exists ; \ - je 1f ; \ - testb $SEL_RPL_MASK,4(%esp) ; \ - jne 1f ; \ - ss ; \ -bdb_/**/name/**/_ljmp: ; \ - ljmp $0,$0 ; \ -1: - -#if 1 -#define COUNT_EVENT(group, event) incl (group) + (event) * 4 -#else -#define COUNT_EVENT(group, event) -#endif - -#ifdef SHOW_A_LOT - -#define GREEN 0x27 /* 0x27 for true green, 0x07 for mono */ -#define CLI_STI_X 63 -#define CPL_X 46 -#define IMEN_X 64 -#define IPENDING_X 29 -#define RED 0x47 /* 0x47 for true red, 0x70 for mono */ - -#define SHOW_BIT(bit) ; \ - movl %ecx,%eax ; \ - shr $bit,%eax ; \ - andl $1,%eax ; \ - movb bit_colors(%eax),%al ; \ - movb %al,bit * 2 + 1(%ebx) - -#define SHOW_BITS(var, screen_offset) ; \ - pushl %ebx ; \ - pushl %ecx ; \ - movl _Crtat,%ebx ; \ - addl $screen_offset * 2,%ebx ; \ - movl _/**/var,%ecx ; \ - call show_bits ; \ - popl %ecx ; \ - popl %ebx - -#define SHOW_CLI \ - COUNT_EVENT(_intrcnt_show, 0) ; \ - pushl %eax ; \ - movl _Crtat,%eax ; \ - movb $RED,CLI_STI_X * 2 + 1(%eax) ; \ - popl %eax - -#define SHOW_CPL \ - COUNT_EVENT(_intrcnt_show, 1) ; \ - SHOW_BITS(cpl, CPL_X) ; \ - -#define SHOW_IMEN \ - COUNT_EVENT(_intrcnt_show, 2) ; \ - SHOW_BITS(imen, IMEN_X) - -#define SHOW_IPENDING \ - COUNT_EVENT(_intrcnt_show, 3) ; \ - SHOW_BITS(ipending, IPENDING_X) - -#define SHOW_STI \ - COUNT_EVENT(_intrcnt_show, 4) ; \ - pushl %eax ; \ - movl _Crtat,%eax ; \ - movb $GREEN,CLI_STI_X * 2 + 1(%eax) ; \ - popl %eax - -#else /* not SHOW_A_LOT */ - -#define SHOW_CLI COUNT_EVENT(_intrcnt_show, 0) -#define SHOW_CPL COUNT_EVENT(_intrcnt_show, 1) -#define SHOW_IMEN COUNT_EVENT(_intrcnt_show, 2) -#define SHOW_IPENDING COUNT_EVENT(_intrcnt_show, 3) -#define SHOW_STI COUNT_EVENT(_intrcnt_show, 4) - -#endif /* SHOW_A_LOT */ diff --git a/sys/i386/isa/elink.c b/sys/i386/isa/elink.c new file mode 100644 index 000000000000..cd0057644a89 --- /dev/null +++ b/sys/i386/isa/elink.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1994 Charles Hannum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Charles Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: elink.c,v 1.1 1994/05/25 20:06:40 ats Exp $ + */ + +/* + * Common code for dealing with 3COM ethernet cards. + */ + +#include <sys/types.h> +#include <sys/cdefs.h> +#include <machine/pio.h> +#include <i386/isa/elink.h> + +/* + * Issue a `global reset' to all cards. We have to be careful to do this only + * once during autoconfig, to prevent resetting boards that have already been + * configured. + */ +void +elink_reset() +{ + static int x = 0; + + if (x == 0) { + x = 1; + outb(ELINK_ID_PORT, ELINK_RESET); + } +} + +/* + * The `ID sequence' is really just snapshots of an 8-bit CRC register as 0 + * bits are shifted in. Different board types use different polynomials. + */ +void +elink_idseq(p) + register u_char p; +{ + register int i; + register u_char c; + + c = 0xff; + for (i = 255; i; i--) { + outb(ELINK_ID_PORT, c); + if (c & 0x80) { + c <<= 1; + c ^= p; + } else + c <<= 1; + } +} diff --git a/sys/i386/isa/elink.h b/sys/i386/isa/elink.h new file mode 100644 index 000000000000..93a5dac6f5ce --- /dev/null +++ b/sys/i386/isa/elink.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1994 Charles Hannum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Charles Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: elink.h,v 1.1 1994/05/25 20:06:43 ats Exp $ + */ + +#define ELINK_ID_PORT 0x100 +#define ELINK_RESET 0xc0 + +#define ELINK_507_POLY 0xe7 +#define ELINK_509_POLY 0xcf + +void elink_reset __P((void)); +void elink_idseq __P((u_char p)); diff --git a/sys/i386/isa/fd.c b/sys/i386/isa/fd.c index 8f3e1dc9cc94..f4632b9f2cb1 100644 --- a/sys/i386/isa/fd.c +++ b/sys/i386/isa/fd.c @@ -6,6 +6,12 @@ * This code is derived from software contributed to Berkeley by * Don Ahn. * + * Portions Copyright (c) 1993, 1994 by + * jc@irbs.UUCP (John Capo) + * vak@zebub.msk.su (Serge Vakulenko) + * ache@astral.msk.su (Andrew A. Chernov) + * joerg_wunsch@uriah.sax.de (Joerg Wunsch) + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -35,7 +41,7 @@ * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fd.c,v 1.21.2.1 1994/03/07 02:08:43 rgrimes Exp $ + * $Id: fd.c,v 1.26 1994/05/22 12:30:32 joerg Exp $ * */ @@ -59,28 +65,29 @@ #include <sys/buf.h> #include <sys/uio.h> #include <sys/malloc.h> +#include <sys/proc.h> #include <sys/syslog.h> #include "i386/isa/isa.h" #include "i386/isa/isa_device.h" #include "i386/isa/fdreg.h" #include "i386/isa/fdc.h" -#include "i386/isa/icu.h" #include "i386/isa/rtc.h" -#if NFT > 0 -extern int ftopen(), ftintr(), ftattach(), ftclose(), ftioctl(); -#endif - #define b_cylin b_resid -#define FDBLK 512 /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX +/* + * this biotab field doubles as a field for the physical unit number + * on the controller + */ +#define id_physid id_scsiid + #define NUMTYPES 14 #define NUMDENS (NUMTYPES - 6) -/* This defines (-1) must match index for fd_types */ +/* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 @@ -132,16 +139,17 @@ struct fdc_data fdc_data[NFDC]; struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ - int type; /* Drive type (HD, DD */ + int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ - int skip; - int hddrv; - int track; /* where we think the head is */ + int skip; + int hddrv; + int track; /* where we think the head is */ + int options; /* user configurable options, see ioctl_fd.h */ } fd_data[NFD]; /***********************************************************************\ @@ -153,10 +161,48 @@ struct fd_data { * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ -#define id_physid id_scsiid /* this biotab field doubles as a field */ - /* for the physical unit number on the controller */ +#if NFT > 0 +int ftopen(dev_t, int); +int ftintr(/* ftu_t */ int ftu); +int ftclose(/* dev_t */ int, int); +void ftstrategy(struct buf *); +int ftioctl(/* dev_t */ int, int, caddr_t, int, struct proc *); +int ftdump(/* dev_t */ int); +int ftsize(/* dev_t */ int); +int ftattach(struct isa_device *, struct isa_device *); +#endif +/* autoconfig functions */ +static int fdprobe(struct isa_device *); +static int fdattach(struct isa_device *); + +/* exported functions */ +int fdsize (/* dev_t */ int); +void fdintr(fdcu_t); +int Fdopen(/* dev_t */int, int); +int fdclose(/* dev_t */int, int); +void fdstrategy(struct buf *); +int fdioctl(/* dev_t */ int, int, caddr_t, int, struct proc *); + +/* needed for ft driver, thus exported */ +int in_fdc(fdcu_t); +int out_fdc(fdcu_t, int); + +/* internal functions */ +static void set_motor(fdcu_t, int, int); +# define TURNON 1 +# define TURNOFF 0 +static void fd_turnoff(caddr_t arg1, int arg2); +static void fd_motor_on(caddr_t arg1, int arg2); +static void fd_turnon(fdu_t); +static void fdc_reset(fdc_p); +static void fdstart(fdcu_t); +static void fd_timeout(caddr_t, int); +static void fd_pseudointr(caddr_t, int); +static int fdstate(fdcu_t, fdc_p); static int retrier(fdcu_t); +static int fdformat(/* dev_t */ int, struct fd_formb *, struct proc *); + #define DEVIDLE 0 #define FINDWORK 1 @@ -188,25 +234,18 @@ char *fdstates[] = "IOTIMEDOUT" }; - -int fd_debug = 1; +/* CAUTION: fd_debug causes huge amounts of logging output */ +int fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) -#define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2) +#define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* DEBUG */ #define TRACE0(arg) -#define TRACE1(arg1,arg2) +#define TRACE1(arg1, arg2) #endif /* DEBUG */ -static void fdstart(fdcu_t); -void fdintr(fdcu_t); -static void fd_turnoff(caddr_t, int); - /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ -static int fdprobe(struct isa_device *); -static int fdattach(struct isa_device *); - struct isa_driver fdcdriver = { fdprobe, fdattach, "fdc", }; @@ -214,56 +253,55 @@ struct isa_driver fdcdriver = { /* * probe for existance of controller */ -int +static int fdprobe(dev) struct isa_device *dev; { fdcu_t fdcu = dev->id_unit; if(fdc_data[fdcu].flags & FDC_ATTACHED) { - printf("fdc: same unit (%d) used multiple times\n",fdcu); + printf("fdc: same unit (%d) used multiple times\n", fdcu); return 0; } fdc_data[fdcu].baseport = dev->id_iobase; /* First - lets reset the floppy controller */ - - outb(dev->id_iobase+fdout,0); + outb(dev->id_iobase+FDOUT, 0); DELAY(100); - outb(dev->id_iobase+fdout,FDO_FRST); + outb(dev->id_iobase+FDOUT, FDO_FRST); /* see if it can handle a command */ - if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0) + if (out_fdc(fdcu, NE7CMD_SPECIFY) < 0) { return(0); } - out_fdc(fdcu,0xDF); - out_fdc(fdcu,2); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); return (IO_FDCSIZE); } /* * wire controller into system, look for floppy units */ -int +static int fdattach(dev) struct isa_device *dev; { - unsigned fdt,st0, cyl; - int hdr; + unsigned fdt; fdu_t fdu; fdcu_t fdcu = dev->id_unit; fdc_p fdc = fdc_data + fdcu; fd_p fd; - int fdsu; + int fdsu, st0; struct isa_device *fdup; fdc->fdcu = fdcu; fdc->flags |= FDC_ATTACHED; fdc->dmachan = dev->id_drq; fdc->state = DEVIDLE; - hdr = 0; + /* reset controller, turn motor off, clear fdout mirror reg */ + outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); printf("fdc%d:", fdcu); /* check for each floppy drive */ @@ -303,25 +341,33 @@ fdattach(dev) continue; } -#ifdef notyet /* select it */ - fd_turnon1(fdu); - spinwait(1000); /* 1 sec */ - out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ - out_fdc(fdcu,fdsu); + set_motor(fdcu, fdsu, TURNON); spinwait(1000); /* 1 sec */ + out_fdc(fdcu, NE7CMD_SEEK); /* seek some steps... */ + out_fdc(fdcu, fdsu); + out_fdc(fdcu, 10); + spinwait(300); /* ...wait a moment... */ + out_fdc(fdcu, NE7CMD_SENSEI); /* make controller happy */ + (void)in_fdc(fdcu); + (void)in_fdc(fdcu); + out_fdc(fdcu, NE7CMD_RECAL); /* ...and go back to 0 */ + out_fdc(fdcu, fdsu); + spinwait(1000); /* a second be enough for full stroke seek */ /* anything responding */ - out_fdc(fdcu,NE7CMD_SENSEI); + out_fdc(fdcu, NE7CMD_SENSEI); st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - if (st0 & 0xd0) + (void)in_fdc(fdcu); + set_motor(fdcu, fdsu, TURNOFF); + + if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ continue; -#endif fd->track = -2; fd->fdc = fdc; fd->fdsu = fdsu; + fd->options = 0; printf(" [%d: fd%d: ", fdsu, fdu); switch (fdt) { @@ -346,15 +392,10 @@ fdattach(dev) fd->type = NO_TYPE; break; } - - fd_turnoff((caddr_t)fdu, 0); - hdr = 1; } printf("\n"); - /* Set transfer to 500kbps */ - outb(fdc->baseport+fdctl,0); /*XXX*/ - return 1; + return (1); } int @@ -365,102 +406,47 @@ fdsize(dev) } /****************************************************************************/ -/* fdstrategy */ -/****************************************************************************/ -void fdstrategy(struct buf *bp) -{ - register struct buf *dp,*dp0,*dp1; - long nblocks,blknum; - int s; - fdcu_t fdcu; - fdu_t fdu; - fdc_p fdc; - fd_p fd; - - fdu = FDUNIT(minor(bp->b_dev)); - fd = &fd_data[fdu]; - fdc = fd->fdc; - fdcu = fdc->fdcu; - -#if NFT > 0 - /* check for controller already busy with tape */ - if (fdc->flags & FDC_TAPE_BUSY) { - bp->b_error = EBUSY; - bp->b_flags |= B_ERROR; - return; - } -#endif - if ((fdu >= NFD) || (bp->b_blkno < 0)) { - printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n", - fdu, bp->b_blkno, bp->b_bcount); - pg("fd:error in fdstrategy"); - bp->b_error = EINVAL; - bp->b_flags |= B_ERROR; - goto bad; - } - /* - * Set up block calculations. - */ - blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK; - nblocks = fd->ft->size; - if (blknum + (bp->b_bcount / FDBLK) > nblocks) { - if (blknum == nblocks) { - bp->b_resid = bp->b_bcount; - } else { - bp->b_error = ENOSPC; - bp->b_flags |= B_ERROR; - } - goto bad; - } - bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); - dp = &(fdc->head); - s = splbio(); - disksort(dp, bp); - untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */ - fdstart(fdcu); - splx(s); - return; - -bad: - biodone(bp); - return; -} - -/****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ -void -set_motor(fdcu, fdu, reset) +static void +set_motor(fdcu, fdsu, turnon) fdcu_t fdcu; - fdu_t fdu; - int reset; + int fdsu; + int turnon; { - int m0,m1; - int selunit; - fd_p fd; - if(fd = fdc_data[fdcu].fd)/* yes an assign! */ - { - selunit = fd->fdsu; + int fdout = fdc_data[fdcu].fdout; + int needspecify = 0; + + if(turnon) { + fdout &= ~FDO_FDSEL; + fdout |= (FDO_MOEN0 << fdsu) + fdsu; + } else + fdout &= ~(FDO_MOEN0 << fdsu); + + if(!turnon + && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) + /* gonna turn off the last drive, put FDC to bed */ + fdout &= ~ (FDO_FRST|FDO_FDMAEN); + else { + /* make sure controller is selected and specified */ + if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) + needspecify = 1; + fdout |= (FDO_FRST|FDO_FDMAEN); } - else - { - selunit = 0; + + outb(fdc_data[fdcu].baseport+FDOUT, fdout); + fdc_data[fdcu].fdout = fdout; + TRACE1("[0x%x->FDOUT]", fdout); + + if(needspecify) { + out_fdc(fdcu, NE7CMD_SPECIFY); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); } - m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR; - m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR; - outb(fdc_data[fdcu].baseport+fdout, - selunit - | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) - | (m0 ? FDO_MOEN0 : 0) - | (m1 ? FDO_MOEN1 : 0)); - TRACE1("[0x%x->fdout]",( - selunit - | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) - | (m0 ? FDO_MOEN0 : 0) - | (m1 ? FDO_MOEN1 : 0))); } +/* ARGSUSED */ static void fd_turnoff(caddr_t arg1, int arg2) { @@ -470,11 +456,12 @@ fd_turnoff(caddr_t arg1, int arg2) fd_p fd = fd_data + fdu; s = splbio(); fd->flags &= ~FD_MOTOR; - set_motor(fd->fdc->fdcu,fd->fdsu,0); + set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF); splx(s); } -void +/* ARGSUSED */ +static void fd_motor_on(caddr_t arg1, int arg2) { fdu_t fdu = (fdu_t)arg1; @@ -490,27 +477,39 @@ fd_motor_on(caddr_t arg1, int arg2) splx(s); } -static void fd_turnon1(fdu_t); - -void +static void fd_turnon(fdu) fdu_t fdu; { fd_p fd = fd_data + fdu; if(!(fd->flags & FD_MOTOR)) { - fd_turnon1(fdu); - fd->flags |= FD_MOTOR_WAIT; + fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); + set_motor(fd->fdc->fdcu, fd->fdsu, TURNON); timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */ } } static void -fd_turnon1(fdu_t fdu) +fdc_reset(fdc) + fdc_p fdc; { - fd_p fd = fd_data + fdu; - fd->flags |= FD_MOTOR; - set_motor(fd->fdc->fdcu,fd->fdsu,0); + fdcu_t fdcu = fdc->fdcu; + + /* Try a reset, keep motor on */ + outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); + TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); + DELAY(100); + /* enable FDC, but defer interrupts a moment */ + outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN); + TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); + DELAY(100); + outb(fdc->baseport + FDOUT, fdc->fdout); + TRACE1("[0x%x->FDOUT]", fdc->fdout); + + out_fdc(fdcu, NE7CMD_SPECIFY); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); } /****************************************************************************/ @@ -522,17 +521,17 @@ in_fdc(fdcu) { int baseport = fdc_data[fdcu].baseport; int i, j = 100000; - while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM)) + while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return -1; if (j <= 0) return(-1); #ifdef DEBUG - i = inb(baseport+fddata); - TRACE1("[fddata->0x%x]",(unsigned char)i); + i = inb(baseport+FDDATA); + TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else - return inb(baseport+fddata); + return inb(baseport+FDDATA); #endif } @@ -546,17 +545,17 @@ out_fdc(fdcu, x) /* Check that the direction bit is set */ i = 100000; - while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0); + while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0); if (i <= 0) return (-1); /* Floppy timed out */ /* Check that the floppy controller is ready for a command */ i = 100000; - while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0); + while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) return (-1); /* Floppy timed out */ /* Send the command and return */ - outb(baseport+fddata,x); - TRACE1("[0x%x->fddata]",x); + outb(baseport+FDDATA, x); + TRACE1("[0x%x->FDDATA]", x); return (0); } @@ -647,17 +646,97 @@ fdclose(dev, flags) int flags; { fdu_t fdu = FDUNIT(minor(dev)); - int type = FDTYPE(minor(dev)); #if NFT > 0 + int type = FDTYPE(minor(dev)); + if (type & F_TAPE_TYPE) - return ftclose(0); + return ftclose(dev, flags); #endif fd_data[fdu].flags &= ~FD_OPEN; + fd_data[fdu].options &= ~FDOPT_NORETRY; return(0); } +/****************************************************************************/ +/* fdstrategy */ +/****************************************************************************/ +void +fdstrategy(struct buf *bp) +{ + register struct buf *dp; + long nblocks, blknum; + int s; + fdcu_t fdcu; + fdu_t fdu; + fdc_p fdc; + fd_p fd; + size_t fdblk; + + fdu = FDUNIT(minor(bp->b_dev)); + fd = &fd_data[fdu]; + fdc = fd->fdc; + fdcu = fdc->fdcu; + fdblk = 128 << (fd->ft->secsize); + +#if NFT > 0 + if (FDTYPE(minor(bp->b_dev)) & F_TAPE_TYPE) { + /* ft tapes do not (yet) support strategy i/o */ + bp->b_error = ENXIO; + bp->b_flags |= B_ERROR; + goto bad; + } + /* check for controller already busy with tape */ + if (fdc->flags & FDC_TAPE_BUSY) { + bp->b_error = EBUSY; + bp->b_flags |= B_ERROR; + goto bad; + } +#endif + if (!(bp->b_flags & B_FORMAT)) { + if ((fdu >= NFD) || (bp->b_blkno < 0)) { + printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n", + fdu, bp->b_blkno, bp->b_bcount); + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto bad; + } + if ((bp->b_bcount % fdblk) != 0) { + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto bad; + } + } + + /* + * Set up block calculations. + */ + blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/fdblk; + nblocks = fd->ft->size; + if (blknum + (bp->b_bcount / fdblk) > nblocks) { + if (blknum == nblocks) { + bp->b_resid = bp->b_bcount; + } else { + bp->b_error = ENOSPC; + bp->b_flags |= B_ERROR; + } + goto bad; + } + bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); + dp = &(fdc->head); + s = splbio(); + disksort(dp, bp); + untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */ + fdstart(fdcu); + splx(s); + return; + +bad: + biodone(bp); + return; +} + /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * @@ -671,9 +750,7 @@ static void fdstart(fdcu) fdcu_t fdcu; { - register struct buf *dp,*bp; int s; - fdu_t fdu; s = splbio(); if(fdc_data[fdcu].state == DEVIDLE) @@ -683,38 +760,43 @@ fdstart(fdcu) splx(s); } +/* ARGSUSED */ static void fd_timeout(caddr_t arg1, int arg2) { fdcu_t fdcu = (fdcu_t)arg1; fdu_t fdu = fdc_data[fdcu].fdu; - int st0, st3, cyl; - struct buf *dp,*bp; - int s; + int baseport = fdc_data[fdcu].baseport; + struct buf *dp, *bp; + int s; dp = &fdc_data[fdcu].head; - s = splbio(); bp = dp->b_actf; - out_fdc(fdcu,NE7CMD_SENSED); - out_fdc(fdcu,fd_data[fdu].hddrv); - st3 = in_fdc(fdcu); - - out_fdc(fdcu,NE7CMD_SENSEI); - st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n", - fdu, - st0, - NE7_ST0BITS, - cyl, - st3, - NE7_ST3BITS); + /* + * Due to IBM's brain-dead design, the FDC has a faked ready + * signal, hardwired to ready == true. Thus, any command + * issued if there's no diskette in the drive will _never_ + * complete, and must be aborted by resetting the FDC. + * Many thanks, Big Blue! + */ + + s = splbio(); + + TRACE1("fd%d[fd_timeout()]", fdu); + /* See if the controller is still busy (patiently awaiting data) */ + if(((inb(baseport + FDSTS)) & (NE7_CB|NE7_RQM)) == NE7_CB) + { + TRACE1("[FDSTS->0x%x]", inb(baseport + FDSTS)); + /* yup, it is; kill it now */ + fdc_reset(&fdc_data[fdcu]); + printf("fd%d: Operation timeout\n", fdu); + } if (bp) { retrier(fdcu); - fdc_data[fdcu].status[0] = 0xc0; + fdc_data[fdcu].status[0] = NE7_ST0_IC_RC; fdc_data[fdcu].state = IOTIMEDOUT; if( fdc_data[fdcu].retry < 6) fdc_data[fdcu].retry = 6; @@ -730,11 +812,13 @@ fd_timeout(caddr_t arg1, int arg2) } /* just ensure it has the right spl */ +/* ARGSUSED */ static void fd_pseudointr(caddr_t arg1, int arg2) { fdcu_t fdcu = (fdcu_t)arg1; int s; + s = splbio(); fdintr(fdcu); splx(s); @@ -756,25 +840,26 @@ fdintr(fdcu_t fdcu) (ftintr(fdu)); else #endif - while(fdstate(fdcu, fdc)) - ; + while(fdstate(fdcu, fdc)) + ; } /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ -int +static int fdstate(fdcu, fdc) fdcu_t fdcu; fdc_p fdc; { - int read, format, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0; + int read, format, head, sec = 0, i = 0, sectrac, st0, cyl, st3; unsigned long blknum; fdu_t fdu = fdc->fdu; fd_p fd; - register struct buf *dp,*bp; + register struct buf *dp, *bp; struct fd_formb *finfo = NULL; + size_t fdblk; dp = &(fdc->head); bp = dp->b_actf; @@ -787,16 +872,17 @@ fdstate(fdcu, fdc) fdc->state = DEVIDLE; if(fdc->fd) { - printf("unexpected valid fd pointer (fdu = %d)\n" - ,fdc->fdu); + printf("unexpected valid fd pointer (fdu = %d)\n", + fdc->fdu); fdc->fd = (fd_p) 0; fdc->fdu = -1; } - TRACE1("[fdc%d IDLE]",fdcu); + TRACE1("[fdc%d IDLE]", fdcu); return(0); } fdu = FDUNIT(minor(bp->b_dev)); fd = fd_data + fdu; + fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) { printf("confused fd pointers\n"); @@ -805,9 +891,9 @@ fdstate(fdcu, fdc) format = bp->b_flags & B_FORMAT; if(format) finfo = (struct fd_formb *)bp->b_un.b_addr; - TRACE1("fd%d",fdu); - TRACE1("[%s]",fdstates[fdc->state]); - TRACE1("(0x%x)",fd->flags); + TRACE1("fd%d", fdu); + TRACE1("[%s]", fdstates[fdc->state]); + TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, (caddr_t)fdu); timeout(fd_turnoff, (caddr_t)fdu, 4 * hz); switch (fdc->state) @@ -818,6 +904,8 @@ fdstate(fdcu, fdc) fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; + outb(fdc->baseport+FDCTL, fd->ft->trans); + TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in it's own good time * @@ -838,7 +926,7 @@ fdstate(fdcu, fdc) } else /* at least make sure we are selected */ { - set_motor(fdcu,fd->fdsu,0); + set_motor(fdcu, fd->fdsu, TURNON); } fdc->state = DOSEEK; break; @@ -848,33 +936,55 @@ fdstate(fdcu, fdc) fdc->state = SEEKCOMPLETE; break; } - out_fdc(fdcu,NE7CMD_SEEK); /* Seek function */ - out_fdc(fdcu,fd->fdsu); /* Drive number */ - out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac); + out_fdc(fdcu, NE7CMD_SEEK); /* Seek function */ + out_fdc(fdcu, fd->fdsu); /* Drive number */ + out_fdc(fdcu, bp->b_cylin * fd->ft->steptrac); fd->track = -2; fdc->state = SEEKWAIT; - timeout(fd_timeout, (caddr_t)fdcu, 2 * hz); return(0); /* will return later */ case SEEKWAIT: - untimeout(fd_timeout, (caddr_t)fdcu); /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 50); + timeout(fd_pseudointr, (caddr_t)fdcu, hz / 32); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ - break; - case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == -2) { int descyl = bp->b_cylin * fd->ft->steptrac; - out_fdc(fdcu,NE7CMD_SENSEI); - i = in_fdc(fdcu); - cyl = in_fdc(fdcu); + do { + out_fdc(fdcu, NE7CMD_SENSEI); + st0 = in_fdc(fdcu); + cyl = in_fdc(fdcu); + /* + * if this was a "ready changed" interrupt, + * fetch status again (can happen after + * enabling controller from reset state) + */ + } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); + if (0 == descyl) + { + /* + * seek to cyl 0 requested; make sure we are + * really there + */ + out_fdc(fdcu, NE7CMD_SENSED); + out_fdc(fdcu, fdu); + st3 = in_fdc(fdcu); + if ((st3 & NE7_ST3_T0) == 0) { + printf( + "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", + fdu, st3, NE7_ST3BITS); + if(fdc->retry < 3) + fdc->retry = 3; + return(retrier(fdcu)); + } + } if (cyl != descyl) { - printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", - fdu, descyl, cyl, i, NE7_ST0BITS); + printf( + "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", + fdu, descyl, cyl, st0, NE7_ST0BITS); return(retrier(fdcu)); } } @@ -884,46 +994,73 @@ fdstate(fdcu, fdc) fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip, - format ? bp->b_bcount : FDBLK, fdc->dmachan); - blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK - + fd->skip/FDBLK; + format ? bp->b_bcount : fdblk, fdc->dmachan); + blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + + fd->skip/fdblk; sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; -/*XXX*/ fd->hddrv = ((head&1)<<2)+fdu; + fd->hddrv = ((head&1)<<2)+fdu; + + if(format || !read) + { + /* make sure the drive is writable */ + out_fdc(fdcu, NE7CMD_SENSED); + out_fdc(fdcu, fdu); + st3 = in_fdc(fdcu); + if(st3 & NE7_ST3_WP) + { + /* + * XXX YES! this is ugly. + * in order to force the current operation + * to fail, we will have to fake an FDC + * error - all error handling is done + * by the retrier() + */ + fdc->status[0] = NE7_ST0_IC_AT; + fdc->status[1] = NE7_ST1_NW; + fdc->status[2] = 0; + fdc->status[3] = fd->track; + fdc->status[4] = head; + fdc->status[5] = sec; + fdc->retry = 8; /* break out immediately */ + fdc->state = IOTIMEDOUT; /* not really... */ + return (1); + } + } if(format) { /* formatting */ - out_fdc(fdcu,/* NE7CMD_FORMAT */ 0x4d); - out_fdc(fdcu,head << 2 | fdu); - out_fdc(fdcu,finfo->fd_formb_secshift); - out_fdc(fdcu,finfo->fd_formb_nsecs); - out_fdc(fdcu,finfo->fd_formb_gaplen); - out_fdc(fdcu,finfo->fd_formb_fillbyte); + out_fdc(fdcu, NE7CMD_FORMAT); + out_fdc(fdcu, head << 2 | fdu); + out_fdc(fdcu, finfo->fd_formb_secshift); + out_fdc(fdcu, finfo->fd_formb_nsecs); + out_fdc(fdcu, finfo->fd_formb_gaplen); + out_fdc(fdcu, finfo->fd_formb_fillbyte); } else { if (read) { - out_fdc(fdcu,NE7CMD_READ); /* READ */ + out_fdc(fdcu, NE7CMD_READ); /* READ */ } else { - out_fdc(fdcu,NE7CMD_WRITE); /* WRITE */ + out_fdc(fdcu, NE7CMD_WRITE); /* WRITE */ } - out_fdc(fdcu,head << 2 | fdu); /* head & unit */ - out_fdc(fdcu,fd->track); /* track */ - out_fdc(fdcu,head); - out_fdc(fdcu,sec); /* sector XXX +1? */ - out_fdc(fdcu,fd->ft->secsize); /* sector size */ - out_fdc(fdcu,sectrac); /* sectors/track */ - out_fdc(fdcu,fd->ft->gap); /* gap size */ - out_fdc(fdcu,fd->ft->datalen); /* data length */ + out_fdc(fdcu, head << 2 | fdu); /* head & unit */ + out_fdc(fdcu, fd->track); /* track */ + out_fdc(fdcu, head); + out_fdc(fdcu, sec); /* sector XXX +1? */ + out_fdc(fdcu, fd->ft->secsize); /* sector size */ + out_fdc(fdcu, sectrac); /* sectors/track */ + out_fdc(fdcu, fd->ft->gap); /* gap size */ + out_fdc(fdcu, fd->ft->datalen); /* data length */ } fdc->state = IOCOMPLETE; - timeout(fd_timeout, (caddr_t)fdcu, 2 * hz); + timeout(fd_timeout, (caddr_t)fdcu, hz); return(0); /* will return later */ case IOCOMPLETE: /* IO DONE, post-analyze */ untimeout(fd_timeout, (caddr_t)fdcu); @@ -931,30 +1068,43 @@ fdstate(fdcu, fdc) { fdc->status[i] = in_fdc(fdcu); } - case IOTIMEDOUT: /*XXX*/ + fdc->state = IOTIMEDOUT; + /* FALLTHROUGH */ + case IOTIMEDOUT: isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip, - format ? bp->b_bcount : FDBLK, fdc->dmachan); - if (fdc->status[0]&0xF8) + format ? bp->b_bcount : fdblk, fdc->dmachan); + if (fdc->status[0] & NE7_ST0_IC) { - if (fdc->status[1] & 0x10) { + if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT + && fdc->status[1] & NE7_ST1_OR) { /* - * Operation not completed in reasonable time. - * Just restart it, don't increment retry count. - * (vak) + * DMA overrun. Someone hogged the bus + * and didn't release it in time for the + * next FDC transfer. + * Just restart it, don't increment retry + * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } + else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV + && fdc->retry < 6) + fdc->retry = 6; /* force a reset */ + else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT + && fdc->status[2] & NE7_ST2_WC + && fdc->retry < 3) + fdc->retry = 3; /* force recalibrate */ return(retrier(fdcu)); } /* All OK */ - fd->skip += FDBLK; + fd->skip += fdblk; if (!format && fd->skip < bp->b_bcount) { /* set up next transfer */ - blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK - + fd->skip/FDBLK; - bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads)); + blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + + fd->skip/fdblk; + bp->b_cylin = + (blknum / (fd->ft->sectrac * fd->ft->heads)); fdc->state = DOSEEK; } else @@ -970,68 +1120,76 @@ fdstate(fdcu, fdc) } return(1); case RESETCTLR: - /* Try a reset, keep motor on */ - set_motor(fdcu,fd->fdsu,1); - DELAY(100); - set_motor(fdcu,fd->fdsu,0); - outb(fdc->baseport+fdctl,fd->ft->trans); - TRACE1("[0x%x->fdctl]",fd->ft->trans); + fdc_reset(fdc); fdc->retry++; fdc->state = STARTRECAL; break; case STARTRECAL: - out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */ - out_fdc(fdcu,0xDF); - out_fdc(fdcu,2); - out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ - out_fdc(fdcu,fdu); + out_fdc(fdcu, NE7CMD_RECAL); /* Recalibrate Function */ + out_fdc(fdcu, fdu); fdc->state = RECALWAIT; return(0); /* will return later */ case RECALWAIT: /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 30); + timeout(fd_pseudointr, (caddr_t)fdcu, hz / 32); fdc->state = RECALCOMPLETE; return(0); /* will return later */ case RECALCOMPLETE: - out_fdc(fdcu,NE7CMD_SENSEI); - st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - if (cyl != 0) + do { + out_fdc(fdcu, NE7CMD_SENSEI); + st0 = in_fdc(fdcu); + cyl = in_fdc(fdcu); + /* + * if this was a "ready changed" interrupt, + * fetch status again (can happen after + * enabling controller from reset state) + */ + } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); + if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); + if(fdc->retry < 3) fdc->retry = 3; return(retrier(fdcu)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return(1); /* will return immediatly */ - case MOTORWAIT: + case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return(0); /* time's not up yet */ } - fdc->state = DOSEEK; + /* + * since the controller was off, it has lost its + * idea about the current track it were; thus, + * recalibrate the bastard + */ + fdc->state = STARTRECAL; return(1); /* will return immediatly */ default: printf("Unexpected FD int->"); - out_fdc(fdcu,NE7CMD_SENSEI); + out_fdc(fdcu, NE7CMD_SENSEI); st0 = in_fdc(fdcu); cyl = in_fdc(fdcu); - printf("ST0 = %lx, PCN = %lx\n",i,sec); - out_fdc(fdcu,0x4A); - out_fdc(fdcu,fd->fdsu); + printf("ST0 = %x, PCN = %x\n", st0, cyl); + out_fdc(fdcu, NE7CMD_READID); + out_fdc(fdcu, fd->fdsu); for(i=0;i<7;i++) { fdc->status[i] = in_fdc(fdcu); } - printf("intr status :%lx %lx %lx %lx %lx %lx %lx ", - fdc->status[0], - fdc->status[1], - fdc->status[2], - fdc->status[3], - fdc->status[4], - fdc->status[5], - fdc->status[6] ); + if(fdc->status[0] != -1) + printf("intr status :%lx %lx %lx %lx %lx %lx %lx\n", + fdc->status[0], + fdc->status[1], + fdc->status[2], + fdc->status[3], + fdc->status[4], + fdc->status[5], + fdc->status[6] ); + else + printf("FDC timed out\n"); return(0); } return(1); /* Come back immediatly to new state */ @@ -1042,11 +1200,13 @@ retrier(fdcu) fdcu_t fdcu; { fdc_p fdc = fdc_data + fdcu; - register struct buf *dp,*bp; + register struct buf *dp, *bp; dp = &(fdc->head); bp = dp->b_actf; + if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY) + goto fail; switch(fdc->retry) { case 0: case 1: case 2: @@ -1061,12 +1221,15 @@ retrier(fdcu) case 7: break; default: + fail: { dev_t sav_b_dev = bp->b_dev; /* Trick diskerr */ - bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|3); + bp->b_dev = makedev(major(bp->b_dev), + (FDUNIT(minor(bp->b_dev))<<3)|3); diskerr(bp, "fd", "hard error", LOG_PRINTF, - fdc->fd->skip, (struct disklabel *)NULL); + fdc->fd->skip / DEV_BSIZE, + (struct disklabel *)NULL); bp->b_dev = sav_b_dev; printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS); printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS); @@ -1101,9 +1264,11 @@ fdformat(dev, finfo, p) struct buf *bp; int rv = 0, s; - + size_t fdblk; + fdu = FDUNIT(minor(dev)); fd = &fd_data[fdu]; + fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); @@ -1119,7 +1284,7 @@ fdformat(dev, finfo, p) * seek to the requested cylinder */ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) - + finfo->head * fd->ft->sectrac) * FDBLK / DEV_BSIZE; + + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->b_un.b_addr = (caddr_t)finfo; @@ -1138,41 +1303,39 @@ fdformat(dev, finfo, p) splx(s); if(rv == EWOULDBLOCK) - { /* timed out */ - biodone(bp); rv = EIO; - } + if(bp->b_flags & B_ERROR) + rv = bp->b_error; + biodone(bp); free(bp, M_TEMP); return rv; } /* - * fdioctl() from jc@irbs.UUCP (John Capo) - * i386/i386/conf.c needs to have fdioctl() declared and remove the line that - * defines fdioctl to be enxio. * - * TODO: Reformat. - * Think about allocating buffer off stack. + * TODO: Think about allocating buffer off stack. * Don't pass uncast 0's and NULL's to read/write/setdisklabel(). * Watch out for NetBSD's different *disklabel() interface. * - * Added functionality for floppy formatting - * joerg_wunsch@uriah.sax.de (Joerg Wunsch) */ int -fdioctl (dev, cmd, addr, flag, p) +fdioctl(dev, cmd, addr, flag, p) dev_t dev; int cmd; caddr_t addr; int flag; struct proc *p; { + fdu_t fdu = FDUNIT(minor(dev)); + fd_p fd = &fd_data[fdu]; + size_t fdblk; + struct fd_type *fdt; struct disklabel *dl; char buffer[DEV_BSIZE]; - int error; + int error = 0; #if NFT > 0 int type = FDTYPE(minor(dev)); @@ -1182,14 +1345,14 @@ fdioctl (dev, cmd, addr, flag, p) return ftioctl(dev, cmd, addr, flag, p); #endif - error = 0; + fdblk = 128 << fd->ft->secsize; switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; - dl->d_secsize = FDBLK; + dl->d_secsize = fdblk; fdt = fd_data[FDUNIT(minor(dev))].ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; @@ -1221,12 +1384,12 @@ fdioctl (dev, cmd, addr, flag, p) dl = (struct disklabel *)addr; - if (error = setdisklabel ((struct disklabel *)buffer, - dl, 0, NULL)) + if ((error = + setdisklabel ((struct disklabel *)buffer, dl, 0, NULL))) break; error = writedisklabel(dev, fdstrategy, - (struct disklabel *)buffer, NULL); + (struct disklabel *)buffer, NULL); break; case FD_FORM: @@ -1243,11 +1406,42 @@ fdioctl (dev, cmd, addr, flag, p) *(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft; break; + case FD_STYPE: /* set drive type */ + /* this is considered harmful; only allow for superuser */ + if(suser(p->p_ucred, &p->p_acflag) != 0) + return EPERM; + *fd_data[FDUNIT(minor(dev))].ft = *(struct fd_type *)addr; + break; + + case FD_GOPTS: /* get drive options */ + *(int *)addr = fd_data[FDUNIT(minor(dev))].options; + break; + + case FD_SOPTS: /* set drive options */ + fd_data[FDUNIT(minor(dev))].options = *(int *)addr; + break; + default: - error = EINVAL; + error = ENOTTY; break; } return (error); } #endif +/* + * Hello emacs, these are the + * Local Variables: + * c-indent-level: 8 + * c-continued-statement-offset: 8 + * c-continued-brace-offset: 0 + * c-brace-offset: -8 + * c-brace-imaginary-offset: 0 + * c-argdecl-indent: 8 + * c-label-offset: -8 + * c++-hanging-braces: 1 + * c++-access-specifier-offset: -8 + * c++-empty-arglist-indent: 8 + * c++-friend-offset: 0 + * End: + */ diff --git a/sys/i386/isa/fdc.h b/sys/i386/isa/fdc.h index 7d75d5a81b48..ef81b11a8387 100644 --- a/sys/i386/isa/fdc.h +++ b/sys/i386/isa/fdc.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fdc.h,v 1.2 1994/02/14 22:24:25 nate Exp $ + * $Id: fdc.h,v 1.3 1994/05/22 12:35:38 joerg Exp $ * */ @@ -49,12 +49,12 @@ struct fdc_data #define FDC_HASFTAPE 0x02 #define FDC_TAPE_BUSY 0x04 struct fd_data *fd; - int fdu; /* the active drive */ + int fdu; /* the active drive */ + int state; + int retry; + int fdout; /* mirror of the w/o digital output reg */ + int status[7]; /* copy of the registers */ struct buf head; /* Head of buf chain */ - struct buf rhead; /* Raw head of buf chain */ - int state; - int retry; - int status[7]; /* copy of the registers */ }; /***********************************************************************\ diff --git a/sys/i386/isa/fdreg.h b/sys/i386/isa/fdreg.h index b0e9593a93c5..9b5be2b87b1c 100644 --- a/sys/i386/isa/fdreg.h +++ b/sys/i386/isa/fdreg.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)fdreg.h 7.1 (Berkeley) 5/9/91 - * $Id: fdreg.h,v 1.4 1994/02/07 22:12:42 alm Exp $ + * $Id: fdreg.h,v 1.5 1994/05/22 12:35:40 joerg Exp $ */ /* @@ -42,24 +42,26 @@ #include "../i386/isa/ic/nec765.h" /* registers */ -#define fdout 2 /* Digital Output Register (W) */ +#define FDOUT 2 /* Digital Output Register (W) */ #define FDO_FDSEL 0x03 /* floppy device select */ #define FDO_FRST 0x04 /* floppy controller reset */ #define FDO_FDMAEN 0x08 /* enable floppy DMA and Interrupt */ #define FDO_MOEN0 0x10 /* motor enable drive 0 */ #define FDO_MOEN1 0x20 /* motor enable drive 1 */ -#define FDO_MOEN2 0x30 /* motor enable drive 2 */ -#define FDO_MOEN3 0x40 /* motor enable drive 3 */ +#define FDO_MOEN2 0x40 /* motor enable drive 2 */ +#define FDO_MOEN3 0x80 /* motor enable drive 3 */ -#define fdsts 4 /* NEC 765 Main Status Register (R) */ -#define fddata 5 /* NEC 765 Data Register (R/W) */ +#define FDSTS 4 /* NEC 765 Main Status Register (R) */ +#define FDDATA 5 /* NEC 765 Data Register (R/W) */ -#define fdctl 7 /* Control Register (W) */ +#define FDCTL 7 /* Control Register (W) */ #define FDC_500KBPS 0x00 /* 500KBPS MFM drive transfer rate */ #define FDC_300KBPS 0x01 /* 300KBPS MFM drive transfer rate */ #define FDC_250KBPS 0x02 /* 250KBPS MFM drive transfer rate */ #define FDC_125KBPS 0x03 /* 125KBPS FM drive transfer rate */ + /* for some controllers 1MPBS instead */ -#define fdin 7 /* Digital Input Register (R) */ +#define FDIN 7 /* Digital Input Register (R) */ #define FDI_DCHG 0x80 /* diskette has been changed */ - + /* requires drive and motor being selected */ + /* is cleared by any step pulse to drive */ diff --git a/sys/i386/isa/ft.c b/sys/i386/isa/ft.c index 09bc127090aa..1204613bcd8e 100644 --- a/sys/i386/isa/ft.c +++ b/sys/i386/isa/ft.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1993 Steve Gerakines + * Copyright (c) 1993, 1994 Steve Gerakines * * This is freely redistributable software. You may do anything you * wish with it, so long as the above notice stays intact. @@ -17,8 +17,15 @@ * POSSIBILITY OF SUCH DAMAGE. * * ft.c - QIC-40/80 floppy tape driver - * $Id: ft.c,v 1.4 1994/02/14 22:24:28 nate Exp $ + * $Id: ft.c,v 1.7 1994/06/22 05:52:36 jkh Exp $ * + * 06/07/94 v0.9 ++sg + * Tape stuck on segment problem should be gone. Re-wrote buffering + * scheme. Added support for drives that do not automatically perform + * seek load point. Can handle more wakeup types now and should correctly + * report most manufacturer names. Fixed places where unit 0 was being + * sent to the fdc instead of the actual unit number. Added ioctl support + * for an in-core badmap. * * 01/26/94 v0.3b - Jim Babb * Got rid of the hard coded device selection. Moved (some of) the @@ -76,12 +83,13 @@ #include "ftreg.h" /* Enable or disable debugging messages. */ -#define FTDBGALL 0 /* everything */ -/* #define DPRT(a) printf a */ -#define DPRT(a) +#define FTDBGALL 0 /* 1 if you want everything */ +/*#define DPRT(a) printf a */ +#define DPRT(a) /* Constants private to the driver */ #define FTPRI (PRIBIO) /* sleep priority */ +#define FTNBUFF 9 /* 8 for buffering, 1 for header */ /* The following items are needed from the fd driver. */ extern int in_fdc(int); /* read fdc registers */ @@ -92,11 +100,13 @@ extern int hz; /* system clock rate */ /* Type of tape attached */ /* use numbers that don't interfere with the possible floppy types */ #define NO_TYPE 0 /* (same as NO_TYPE in fd.c) */ - /* F_TAPE_TYPE must match value in fd.c */ -#define F_TAPE_TYPE 0x020 /* bit for ft->types to indicate tape */ -#define FT_MOUNTAIN (F_TAPE_TYPE | 1) -#define FT_COLORADO (F_TAPE_TYPE | 2) +/* F_TAPE_TYPE must match value in fd.c */ +#define F_TAPE_TYPE 0x020 /* bit for ft->types to indicate tape */ +#define FT_NONE (F_TAPE_TYPE | 0) /* no method required */ +#define FT_MOUNTAIN (F_TAPE_TYPE | 1) /* mountain */ +#define FT_COLORADO (F_TAPE_TYPE | 2) /* colorado */ +#define FT_INSIGHT (F_TAPE_TYPE | 3) /* insight */ /* Mode FDC is currently in: tape or disk */ enum { FDC_TAPE_MODE, FDC_DISK_MODE }; @@ -150,15 +160,25 @@ QIC_Geom *ftg = NULL; /* Current tape's geometry */ /* * things relating to asynchronous commands */ -static int astk_depth; /* async_cmd stack depth */ static int awr_state; /* state of async write */ static int ard_state; /* state of async read */ static int arq_state; /* state of async request */ static int async_retries; /* retries, one per invocation */ static int async_func; /* function to perform */ static int async_state; /* state current function is at */ -static int async_arg[5]; /* up to 5 arguments for async cmds */ +static int async_arg0; /* up to 3 arguments for async cmds */ +static int async_arg1; /**/ +static int async_arg2; /**/ static int async_ret; /* return value */ +static struct _astk { + int over_func; + int over_state; + int over_retries; + int over_arg0; + int over_arg1; + int over_arg2; +} astk[10]; +static struct _astk *astk_ptr = &astk[0]; /* Pointer to stack position */ /* List of valid async (interrupt driven) tape support functions. */ enum { @@ -172,29 +192,36 @@ enum { }; /* Call another asyncronous command from within async_cmd(). */ -#define CALL_ACMD(r,f,a,b,c,d,e) \ - astk[astk_depth].over_retries = async_retries; \ - astk[astk_depth].over_func = async_func; \ - astk[astk_depth].over_state = (r); \ - for (i = 0; i < 5; i++) \ - astk[astk_depth].over_arg[i] = async_arg[i]; \ +#define CALL_ACMD(r,f,a,b,c) \ + astk_ptr->over_retries = async_retries; \ + astk_ptr->over_func = async_func; \ + astk_ptr->over_state = (r); \ + astk_ptr->over_arg0 = async_arg0; \ + astk_ptr->over_arg1 = async_arg1; \ + astk_ptr->over_arg2 = async_arg2; \ async_func = (f); async_state = 0; async_retries = 0; \ - async_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \ - async_arg[3]=(d); async_arg[4]=(e); \ - astk_depth++; \ + async_arg0=(a); async_arg1=(b); async_arg2=(c); \ + astk_ptr++; \ goto restate /* Perform an asyncronous command from outside async_cmd(). */ -#define ACMD_FUNC(r,f,a,b,c,d,e) over_async = (r); astk_depth = 0; \ +#define ACMD_FUNC(r,f,a,b,c) over_async = (r); astk_ptr = &astk[0]; \ async_func = (f); async_state = 0; async_retries = 0; \ - async_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \ - async_arg[3]=(d); async_arg[4]=(e); \ + async_arg0=(a); async_arg1=(b); async_arg2=(c); \ async_cmd(ftu); \ return /* Various wait channels */ +static char *wc_buff_avail = "bavail"; +static char *wc_buff_done = "bdone"; +static char *wc_iosts_change = "iochg"; +static char *wc_long_delay = "ldelay"; +static char *wc_intr_wait = "intrw"; +#define ftsleep(wc,to) tsleep((caddr_t)(wc),FTPRI,(wc),(to)) + static struct { int buff_avail; + int buff_done; int iosts_change; int long_delay; int intr_wait; @@ -223,8 +250,17 @@ struct ft_data { unsigned char *xptr; /* pointer to buffer blk to xfer */ int xcnt; /* transfer count */ int xblk; /* block number to transfer */ - SegReq *curseg; /* Current segment to do I/O on */ - SegReq *bufseg; /* Buffered segment to r/w ahead */ + int xseg; /* segment being transferred */ + SegReq *segh; /* Current I/O request */ + SegReq *segt; /* Tail of queued I/O requests */ + SegReq *doneh; /* Completed I/O request queue */ + SegReq *donet; /* Completed I/O request tail */ + SegReq *segfree; /* Free segments */ + SegReq *hdr; /* Current tape header */ + int nsegq; /* Segments on request queue */ + int ndoneq; /* Segments on completed queue */ + int nfreelist; /* Segments on free list */ + /* the next 3 should be defines in 'flags' */ int active; /* TRUE if transfer is active */ int rdonly; /* TRUE if tape is read-only */ @@ -261,85 +297,237 @@ void ftstrategy(struct buf *); int ftioctl(dev_t, int, caddr_t, int, struct proc *); int ftdump(dev_t); int ftsize(dev_t); -static void ft_timeout(caddr_t arg1, int arg2); -void async_cmd(ftu_t); -void async_req(ftu_t, int); -void async_read(ftu_t, int); -void async_write(ftu_t, int); -void tape_start(ftu_t); -void tape_end(ftu_t); -void tape_inactive(ftu_t); +static void ft_timeout(caddr_t, int); +static void async_cmd(ftu_t); +static void async_req(ftu_t, int); +static void async_read(ftu_t, int); +static void async_write(ftu_t, int); +static void tape_start(ftu_t, int); +static void tape_end(ftu_t); +static void tape_inactive(ftu_t); +static int tape_cmd(ftu_t, int); +static int tape_status(ftu_t); +static int qic_status(ftu_t, int, int); +static int ftreq_rewind(ftu_t); +static int ftreq_hwinfo(ftu_t, QIC_HWInfo *); + +/*****************************************************************************/ + + +/* + * Allocate a segment I/O buffer from the free list. + */ +static SegReq * +segio_alloc(ft_p ft) +{ + SegReq *r; + + /* Grab first item from free list */ + if ((r = ft->segfree) != NULL) { + ft->segfree = ft->segfree->next; + ft->nfreelist--; + } + DPRT(("segio_alloc: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); + return(r); +} + + +/* + * Queue a segment I/O request. + */ +static void +segio_queue(ft_p ft, SegReq *sp) +{ + /* Put request on in process queue. */ + if (ft->segt == NULL) + ft->segh = sp; + else + ft->segt->next = sp; + sp->next = NULL; + ft->segt = sp; + ft->nsegq++; + DPRT(("segio_queue: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); +} +/* + * Segment I/O completed, place on correct queue. + */ +static void +segio_done(ft_p ft, SegReq *sp) +{ + /* First remove from current I/O queue */ + ft->segh = sp->next; + if (ft->segh == NULL) ft->segt = NULL; + ft->nsegq--; + + if (sp->reqtype == FTIO_WRITING) { + /* Place on free list */ + sp->next = ft->segfree; + ft->segfree = sp; + ft->nfreelist++; + wakeup((caddr_t)wc_buff_avail); + DPRT(("segio_done: (w) nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); + } else { + /* Put on completed I/O queue */ + if (ft->donet == NULL) + ft->doneh = sp; + else + ft->donet->next = sp; + sp->next = NULL; + ft->donet = sp; + ft->ndoneq++; + wakeup((caddr_t)wc_buff_done); + DPRT(("segio_done: (r) nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); + } +} +/* + * Take I/O request from finished queue to free queue. + */ +static void +segio_free(ft_p ft, SegReq *sp) +{ + /* First remove from done queue */ + ft->doneh = sp->next; + if (ft->doneh == NULL) ft->donet = NULL; + ft->ndoneq--; + + /* Place on free list */ + sp->next = ft->segfree; + ft->segfree = sp; + ft->nfreelist++; + wakeup((caddr_t)wc_buff_avail); + DPRT(("segio_free: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); +} + /* * Probe/attach floppy tapes. */ -int ftattach(isadev, fdup) +int +ftattach(isadev, fdup) struct isa_device *isadev, *fdup; { - fdcu_t fdcu = isadev->id_unit; /* fdc active unit */ - fdc_p fdc = fdc_data + fdcu; /* pointer to controller structure */ - ftu_t ftu = fdup->id_unit; - ft_p ft; - ftsu_t ftsu = fdup->id_physid; - - if (ftu >= NFT) - return 0; - ft = &ft_data[ftu]; - /* Probe for tape */ - ft->attaching = 1; - ft->type = NO_TYPE; - ft->fdc = fdc; - ft->ftsu = ftsu; - - tape_start(ftu); /* ready controller for tape */ - tape_cmd(ftu, QC_COL_ENABLE1); - tape_cmd(ftu, QC_COL_ENABLE2); - if (tape_status(ftu) >= 0) { - ft->type = FT_COLORADO; - fdc->flags |= FDC_HASFTAPE; - printf(" [%d: ft%d: Colorado tape]", - fdup->id_physid, fdup->id_unit ); - tape_cmd(ftu, QC_COL_DISABLE); - goto out; - } + fdcu_t fdcu = isadev->id_unit; /* fdc active unit */ + fdc_p fdc = fdc_data + fdcu; /* pointer to controller structure */ + ftu_t ftu = fdup->id_unit; + ft_p ft; + ftsu_t ftsu = fdup->id_physid; + QIC_HWInfo hw; + char *manu; + + if (ftu >= NFT) return 0; + ft = &ft_data[ftu]; + + /* Probe for tape */ + ft->attaching = 1; + ft->type = NO_TYPE; + ft->fdc = fdc; + ft->ftsu = ftsu; - tape_start(ftu); /* ready controller for tape */ - tape_cmd(ftu, QC_MTN_ENABLE1); - tape_cmd(ftu, QC_MTN_ENABLE2); - if (tape_status(ftu) >= 0) { - ft->type = FT_MOUNTAIN; - fdc->flags |= FDC_HASFTAPE; - printf(" [%d: ft%d: Mountain tape]", - fdup->id_physid, fdup->id_unit ); - tape_cmd(ftu, QC_MTN_DISABLE); - goto out; - } + /* + * FT_NONE - no method, just do it + */ + tape_start(ftu, 0); + if (tape_status(ftu) >= 0) { + ft->type = FT_NONE; + ftreq_hwinfo(ftu, &hw); + goto out; + } + + /* + * FT_COLORADO - colorado style + */ + tape_start(ftu, 0); + tape_cmd(ftu, QC_COL_ENABLE1); + tape_cmd(ftu, QC_COL_ENABLE2 + ftu); + if (tape_status(ftu) >= 0) { + ft->type = FT_COLORADO; + ftreq_hwinfo(ftu, &hw); + tape_cmd(ftu, QC_COL_DISABLE); + goto out; + } + + /* + * FT_MOUNTAIN - mountain style + */ + tape_start(ftu, 0); + tape_cmd(ftu, QC_MTN_ENABLE1); + tape_cmd(ftu, QC_MTN_ENABLE2); + if (tape_status(ftu) >= 0) { + ft->type = FT_MOUNTAIN; + ftreq_hwinfo(ftu, &hw); + tape_cmd(ftu, QC_MTN_DISABLE); + goto out; + } + + /* + * FT_INSIGHT - insight style + */ + tape_start(ftu, 1); + if (tape_status(ftu) >= 0) { + ft->type = FT_INSIGHT; + ftreq_hwinfo(ftu, &hw); + goto out; + } out: - tape_end(ftu); - ft->attaching = 0; - return(ft->type); + tape_end(ftu); + if (ft->type != NO_TYPE) { + fdc->flags |= FDC_HASFTAPE; + switch(hw.hw_make) { + case 0x0000: + if (ft->type == FT_COLORADO) + manu = "Colorado"; + else if (ft->type == FT_INSIGHT) + manu = "Insight"; + else if (ft->type == FT_MOUNTAIN && hw.hw_model == 0x05) + manu = "Archive"; + else if (ft->type == FT_MOUNTAIN) + manu = "Mountain"; + else + manu = "Unknown"; + break; + case 0x0001: + manu = "Colorado"; + break; + case 0x0005: + if (hw.hw_model >= 0x09) + manu = "Conner"; + else + manu = "Archive"; + break; + case 0x0006: + manu = "Mountain"; + break; + case 0x0007: + manu = "Wangtek"; + break; + case 0x0222: + manu = "IOMega"; + break; + default: + manu = "Unknown"; + break; + } + printf(" [%d: ft%d: %s tape]", fdup->id_physid, fdup->id_unit, manu); + } + ft->attaching = 0; + return(ft->type); } /* * Perform common commands asynchronously. */ -void async_cmd(ftu_t ftu) { +static void +async_cmd(ftu_t ftu) { ft_p ft = &ft_data[ftu]; fdcu_t fdcu = ft->fdc->fdcu; int cmd, i, st0, st3, pcn; static int bitn, retval, retpos, nbits, newcn; - static struct { - int over_func; - int over_state; - int over_retries; - int over_arg[5]; - } astk[15]; static int wanttrk, wantblk, wantdir; static int curpos, curtrk, curblk, curdir, curdiff; static int errcnt = 0; @@ -356,7 +544,7 @@ restate: */ switch (async_state) { case 0: - cmd = async_arg[0]; + cmd = async_arg0; #if FTDBGALL DPRT(("===>async_seek cmd = %d\n", cmd)); #endif @@ -364,7 +552,7 @@ restate: async_state = 1; i = 0; if (out_fdc(fdcu, NE7CMD_SEEK) < 0) i = 1; - if (!i && out_fdc(fdcu, 0x00) < 0) i = 1; + if (!i && out_fdc(fdcu, ftu) < 0) i = 1; if (!i && out_fdc(fdcu, newcn) < 0) i = 1; if (i) { if (++async_retries >= 10) { @@ -399,7 +587,7 @@ restate: DPRT(("ft%d: async_seek error st0 = $%02x pcn = %d\n", ftu, st0, pcn)); #endif - if (async_arg[1]) goto complete; + if (async_arg1) goto complete; async_state = 2; timeout(ft_timeout, (caddr_t)ftu, hz/50); break; @@ -420,14 +608,14 @@ restate: case 0: bitn = 0; retval = 0; - cmd = async_arg[0]; - nbits = async_arg[1]; + cmd = async_arg0; + nbits = async_arg1; DPRT(("async_status got cmd = %d nbits = %d\n", cmd,nbits)); - CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0); /* NOTREACHED */ case 1: out_fdc(fdcu, NE7CMD_SENSED); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); st3 = in_fdc(fdcu); if (st3 < 0) { DPRT(("ft%d: async_status timed out on bit %d r=$%02x\n", @@ -440,7 +628,7 @@ restate: if (bitn >= (nbits+2)) { if ((retval & 1) && (retval & (1 << (nbits+1)))) { async_ret = (retval & ~(1<<(nbits+1))) >> 1; - if (async_arg[0] == QC_STATUS && async_arg[2] == 0 && + if (async_arg0 == QC_STATUS && async_arg2 == 0 && (async_ret & (QS_ERROR|QS_NEWCART))) { async_state = 2; goto restate; @@ -453,31 +641,31 @@ restate: } goto complete; } - CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0); /* NOTREACHED */ case 2: if (async_ret & QS_NEWCART) ft->newcart = 1; - CALL_ACMD(3, ACMD_STATUS, QC_ERRCODE, 16, 1, 0, 0); + CALL_ACMD(3, ACMD_STATUS, QC_ERRCODE, 16, 1); case 3: ft->lasterr = async_ret; if ((ft->lasterr & QS_NEWCART) == 0 && ft->lasterr) { DPRT(("ft%d: QIC error %d occurred on cmd %d\n", ftu, ft->lasterr & 0xff, ft->lasterr >> 8)); } - cmd = async_arg[0]; - nbits = async_arg[1]; - CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1, 0, 0); + cmd = async_arg0; + nbits = async_arg1; + CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1); case 4: goto complete; case 5: - CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0); case 6: - CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0); case 7: - CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0); case 8: - cmd = async_arg[0]; - CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0); + cmd = async_arg0; + CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0); } break; @@ -488,9 +676,9 @@ restate: */ switch(async_state) { case 0: - CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); + CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0); case 1: - if ((async_ret & async_arg[0]) != 0) goto complete; + if ((async_ret & async_arg0) != 0) goto complete; async_state = 0; if (++async_retries == 360) { /* 90 secs. */ DPRT(("ft%d: acmd_state exceeded retry count\n", ftu)); @@ -510,13 +698,13 @@ restate: */ switch(async_state) { case 0: - cmd = async_arg[0]; - async_retries = (async_arg[2]) ? (async_arg[2]*4) : 10; - CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0); + cmd = async_arg0; + async_retries = (async_arg2) ? (async_arg2 * 4) : 10; + CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0); case 1: - CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); + CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0); case 2: - if ((async_ret & async_arg[1]) != 0) goto complete; + if ((async_ret & async_arg1) != 0) goto complete; if (--async_retries == 0) { DPRT(("ft%d: acmd_seeksts retries exceeded\n", ftu)); goto complete; @@ -534,12 +722,12 @@ restate: switch(async_state) { case 0: if (!ft->moving) { - CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); /* NOTREACHED */ } async_state = 1; out_fdc(fdcu, 0x4a); /* READ_ID */ - out_fdc(fdcu, 0); + out_fdc(fdcu, ftu); break; case 1: for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu); @@ -548,25 +736,36 @@ restate: DPRT(("readid st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d\n", ft->rid[0], ft->rid[1], ft->rid[2], ft->rid[3], ft->rid[4], ft->rid[5], async_ret)); - if ((ft->rid[0] & 0xc0) == 0x40) { - if (++errcnt >= 10) { + if ((ft->rid[0] & 0xc0) != 0 || async_ret < 0) { + /* + * Method for retry: + * errcnt == 1 regular retry + * 2 microstep head 1 + * 3 microstep head 2 + * 4 microstep head back to 0 + * 5 fail + */ + if (++errcnt >= 5) { DPRT(("ft%d: acmd_readid errcnt exceeded\n", fdcu)); - async_ret = ft->lastpos; + async_ret = -2; errcnt = 0; goto complete; } - if (errcnt > 2) { + if (errcnt == 1) { + ft->moving = 0; + CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); + } else { ft->moving = 0; - CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(4, ACMD_SEEKSTS, QC_STPAUSE, QS_READY, 0); } - DPRT(("readid retry...\n")); + DPRT(("readid retry %d...\n", errcnt)); async_state = 0; goto restate; } if ((async_ret % ftg->g_blktrk) == (ftg->g_blktrk-1)) { DPRT(("acmd_readid detected last block on track\n")); retpos = async_ret; - CALL_ACMD(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0); + CALL_ACMD(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0); /* NOTREACHED */ } ft->lastpos = async_ret; @@ -574,13 +773,13 @@ restate: goto complete; /* NOTREACHED */ case 2: - CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); + CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0); case 3: ft->moving = 0; async_ret = retpos+1; goto complete; case 4: - CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0); case 5: ft->moving = 1; async_state = 0; @@ -598,27 +797,27 @@ restate: */ switch (async_state) { case 0: - wanttrk = async_arg[0] / ftg->g_blktrk; - wantblk = async_arg[0] % ftg->g_blktrk; + wanttrk = async_arg0 / ftg->g_blktrk; + wantblk = async_arg0 % ftg->g_blktrk; wantdir = wanttrk & 1; ft->moving = 0; - CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); case 1: curtrk = wanttrk; curdir = curtrk & 1; DPRT(("Changing to track %d\n", wanttrk)); - CALL_ACMD(2, ACMD_SEEK, QC_SEEKTRACK, 0, 0, 0, 0); + CALL_ACMD(2, ACMD_SEEK, QC_SEEKTRACK, 0, 0); case 2: cmd = wanttrk+2; - CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0); + CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0); case 3: - CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); + CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0); case 4: ft->laststs = async_ret; if (wantblk == 0) { curblk = 0; cmd = (wantdir) ? QC_SEEKEND : QC_SEEKSTART; - CALL_ACMD(6, ACMD_SEEKSTS, cmd, QS_READY, 90, 0, 0); + CALL_ACMD(6, ACMD_SEEKSTS, cmd, QS_READY, 90); } if (ft->laststs & QS_BOT) { DPRT(("Tape is at BOT\n")); @@ -632,15 +831,23 @@ restate: async_state = 6; goto restate; } - CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); + CALL_ACMD(5, ACMD_READID, 0, 0, 0); case 5: + if (async_ret < 0) { + ft->moving = 0; + ft->lastpos = -2; + if (async_ret == -2) { + CALL_ACMD(9, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); + } + CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); + } curtrk = (async_ret+1) / ftg->g_blktrk; curblk = (async_ret+1) % ftg->g_blktrk; DPRT(("gotid: curtrk=%d wanttrk=%d curblk=%d wantblk=%d\n", curtrk, wanttrk, curblk, wantblk)); if (curtrk != wanttrk) { /* oops! */ DPRT(("oops!! wrong track!\n")); - CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); } async_state = 6; goto restate; @@ -650,22 +857,22 @@ restate: ft->lastpos = curblk - 1; async_ret = ft->lastpos; if (ft->moving) goto complete; - CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0, 0, 0); + CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0); } if (curblk > wantblk) { /* passed it */ ft->moving = 0; - CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); } - if ((wantblk - curblk) <= 96) { /* approaching it */ - CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); + if ((wantblk - curblk) <= 256) { /* approaching it */ + CALL_ACMD(5, ACMD_READID, 0, 0, 0); } /* way up ahead */ ft->moving = 0; - CALL_ACMD(14, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(14, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); break; case 7: ft->moving = 1; - CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0); break; case 8: async_state = 9; @@ -677,26 +884,26 @@ restate: curdiff = ((curblk - wantblk) / QCV_BLKSEG) + 2; if (curdiff >= ftg->g_segtrk) curdiff = ftg->g_segtrk - 1; DPRT(("pos %d past %d, reverse %d\n", curblk, wantblk, curdiff)); - CALL_ACMD(11, ACMD_SEEK, QC_SEEKREV, 0, 0, 0, 0); + CALL_ACMD(11, ACMD_SEEK, QC_SEEKREV, 0, 0); case 11: DPRT(("reverse 1 done\n")); - CALL_ACMD(12, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0, 0, 0); + CALL_ACMD(12, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0); case 12: DPRT(("reverse 2 done\n")); - CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90, 0, 0); + CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90); case 13: - CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); + CALL_ACMD(5, ACMD_READID, 0, 0, 0); case 14: curdiff = ((wantblk - curblk) / QCV_BLKSEG) - 2; if (curdiff < 0) curdiff = 0; DPRT(("pos %d before %d, forward %d\n", curblk, wantblk, curdiff)); - CALL_ACMD(15, ACMD_SEEK, QC_SEEKFWD, 0, 0, 0, 0); + CALL_ACMD(15, ACMD_SEEK, QC_SEEKFWD, 0, 0); case 15: DPRT(("forward 1 done\n")); - CALL_ACMD(16, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0, 0, 0); + CALL_ACMD(16, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0); case 16: DPRT(("forward 2 done\n")); - CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90, 0, 0); + CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90); } break; } @@ -704,13 +911,14 @@ restate: return; complete: - if (astk_depth) { - astk_depth--; - async_retries = astk[astk_depth].over_retries; - async_func = astk[astk_depth].over_func; - async_state = astk[astk_depth].over_state; - for(i = 0; i < 5; i++) - async_arg[i] = astk[astk_depth].over_arg[i]; + if (astk_ptr != &astk[0]) { + astk_ptr--; + async_retries = astk_ptr->over_retries; + async_func = astk_ptr->over_func; + async_state = astk_ptr->over_state; + async_arg0 = astk_ptr->over_arg0; + async_arg1 = astk_ptr->over_arg1; + async_arg2 = astk_ptr->over_arg2; goto restate; } async_func = ACMD_NONE; @@ -720,6 +928,7 @@ complete: async_req(ftu, 2); break; case FTIO_READING: + case FTIO_RDAHEAD: async_read(ftu, 2); break; case FTIO_WRITING: @@ -735,10 +944,11 @@ complete: /* * Entry point for the async request processor. */ -void async_req(ftu_t ftu, int from) +static void +async_req(ftu_t ftu, int from) { ft_p ft = &ft_data[ftu]; - SegReq *sp; + SegReq *nsp, *sp; static int over_async, lastreq, domore; int cmd; @@ -747,56 +957,73 @@ void async_req(ftu_t ftu, int from) restate: switch (arq_state) { case 0: /* Process segment */ - ft->io_sts = ft->curseg->reqtype; + sp = ft->segh; + ft->io_sts = (sp == NULL) ? FTIO_READY : sp->reqtype; + if (ft->io_sts == FTIO_WRITING) async_write(ftu, from); else async_read(ftu, from); if (ft->io_sts != FTIO_READY) return; - /* Swap buffered and current segment */ - lastreq = ft->curseg->reqtype; - ft->curseg->reqtype = FTIO_READY; - sp = ft->curseg; - ft->curseg = ft->bufseg; - ft->bufseg = sp; + /* Pull buffer from current I/O queue */ + if (sp != NULL) { + lastreq = sp->reqtype; + segio_done(ft, sp); - wakeup((caddr_t)&ftsem.buff_avail); + /* If I/O cancelled, clear finished queue. */ + if (sp->reqcan) { + while (ft->doneh != NULL) + segio_free(ft, ft->doneh); + lastreq = FTIO_READY; + } + } else + lastreq = FTIO_READY; /* Detect end of track */ if (((ft->xblk / QCV_BLKSEG) % ftg->g_segtrk) == 0) { - domore = (ft->curseg->reqtype != FTIO_READY); - ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0); + ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0); } arq_state = 1; goto restate; case 1: /* Next request */ - if (ft->curseg->reqtype != FTIO_READY) { - ft->curseg->reqcrc = 0; + /* If we have another request queued, start it running. */ + if (ft->segh != NULL) { + sp = ft->segh; + sp->reqcrc = 0; arq_state = ard_state = awr_state = 0; - ft->xblk = ft->curseg->reqblk; + ft->xblk = sp->reqblk; + ft->xseg = sp->reqseg; ft->xcnt = 0; - ft->xptr = ft->curseg->buff; - DPRT(("I/O reqblk = %d\n", ft->curseg->reqblk)); + ft->xptr = sp->buff; + DPRT(("I/O reqblk = %d\n", ft->xblk)); goto restate; } - if (lastreq == FTIO_READING) { - ft->curseg->reqtype = FTIO_RDAHEAD; - ft->curseg->reqblk = ft->xblk; - ft->curseg->reqcrc = 0; - ft->curseg->reqcan = 0; - bzero(ft->curseg->buff, QCV_SEGSIZE); + + /* If the last request was reading, do read ahead. */ + if ((lastreq == FTIO_READING || lastreq == FTIO_RDAHEAD) && + (sp = segio_alloc(ft)) != NULL) { + sp->reqtype = FTIO_RDAHEAD; + sp->reqblk = ft->xblk; + sp->reqseg = ft->xseg+1; + sp->reqcrc = 0; + sp->reqcan = 0; + segio_queue(ft, sp); + bzero(sp->buff, QCV_SEGSIZE); arq_state = ard_state = awr_state = 0; - ft->xblk = ft->curseg->reqblk; + ft->xblk = sp->reqblk; + ft->xseg = sp->reqseg; ft->xcnt = 0; - ft->xptr = ft->curseg->buff; - DPRT(("Processing readahead reqblk = %d\n", ft->curseg->reqblk)); + ft->xptr = sp->buff; + DPRT(("Processing readahead reqblk = %d\n", ft->xblk)); goto restate; } + if (ft->moving) { DPRT(("No more I/O.. Stopping.\n")); - ACMD_FUNC(7, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + ft->moving = 0; + ACMD_FUNC(7, ACMD_SEEKSTS, QC_PAUSE, QS_READY, 0); break; } arq_state = 7; @@ -804,26 +1031,26 @@ restate: case 2: /* End of track */ ft->moving = 0; - ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); + ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0); break; case 3: DPRT(("async_req seek head to track %d\n", ft->xblk / ftg->g_blktrk)); - ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0, 0, 0); + ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0); break; case 4: cmd = (ft->xblk / ftg->g_blktrk) + 2; - if (domore) { - ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0); + if (ft->segh != NULL) { + ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0); } else { - ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0); + ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0); } break; case 5: ft->moving = 1; - ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0); break; case 6: @@ -832,26 +1059,22 @@ restate: break; case 7: - ft->moving = 0; - - /* Check one last time to see if a request came in. */ - if (ft->curseg->reqtype != FTIO_READY) { - DPRT(("async_req: Never say no!\n")); - arq_state = 1; - goto restate; - } - /* Time to rest. */ ft->active = 0; - wakeup((caddr_t)&ftsem.iosts_change); /* wakeup those who want an i/o chg */ + ft->lastpos = -2; + + /* wakeup those who want an i/o chg */ + wakeup((caddr_t)wc_iosts_change); break; } } + /* * Entry for async read. */ -void async_read(ftu_t ftu, int from) +static void +async_read(ftu_t ftu, int from) { ft_p ft = &ft_data[ftu]; fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ @@ -859,6 +1082,7 @@ void async_read(ftu_t ftu, int from) int i, cmd, newcn, rddta[7]; int st0, pcn, where; static int over_async; + static int retries = 0; if (from == 2) ard_state = over_async; @@ -872,13 +1096,13 @@ restate: if (ft->lastpos != (ft->xblk-1)) { DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n", ftu, ft->lastpos, ft->xblk)); - ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0, 0, 0); + ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0); } /* Tape is in position but stopped. */ if (!ft->moving) { DPRT(("async_read ******STARTING TAPE\n")); - ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); + ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0); } ard_state = 1; goto restate; @@ -887,8 +1111,8 @@ restate: /* Tape is now moving and in position-- start DMA now! */ isa_dmastart(B_READ, ft->xptr, QCV_BLKSIZE, 2); out_fdc(fdcu, 0x66); /* read */ - out_fdc(fdcu, 0x00); /* unit */ - out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */ + out_fdc(fdcu, ftu); /* unit */ + out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */ out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */ out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */ out_fdc(fdcu, 0x03); /* 1K sectors */ @@ -905,7 +1129,8 @@ restate: #if FTDBGALL /* Compute where the controller thinks we are */ - where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5]-1; + where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + + rddta[5]-1; DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], where, ft->xblk)); @@ -913,34 +1138,44 @@ restate: /* Check for errors */ if ((rddta[0] & 0xc0) != 0x00) { - if (rddta[1] & 0x04) { +#if !FTDBGALL + where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + + rddta[5]-1; + DPRT(("xd: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", + rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], + where, ft->xblk)); +#endif + if ((rddta[1] & 0x04) == 0x04 && retries < 2) { /* Probably wrong position */ + DPRT(("async_read: doing retry %d\n", retries)); ft->lastpos = ft->xblk; ard_state = 0; + retries++; goto restate; } else { /* CRC/Address-mark/Data-mark, et. al. */ DPRT(("ft%d: CRC error on block %d\n", fdcu, ft->xblk)); - ft->curseg->reqcrc |= (1 << ft->xcnt); + ft->segh->reqcrc |= (1 << ft->xcnt); } } /* Otherwise, transfer completed okay. */ + retries = 0; ft->lastpos = ft->xblk; ft->xblk++; ft->xcnt++; ft->xptr += QCV_BLKSIZE; - if (ft->xcnt < QCV_BLKSEG && ft->curseg->reqcan == 0) { + if (ft->xcnt < QCV_BLKSEG && ft->segh->reqcan == 0) { ard_state = 0; goto restate; } - DPRT(("Read done.. Cancel = %d\n", ft->curseg->reqcan)); + DPRT(("Read done.. Cancel = %d\n", ft->segh->reqcan)); ft->io_sts = FTIO_READY; break; case 3: ft->moving = 1; - ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0); break; case 4: @@ -960,7 +1195,8 @@ restate: * routine, if it's 1 then it was a timeout, if it's 2, then an * async_cmd completed. */ -void async_write(ftu_t ftu, int from) +static void +async_write(ftu_t ftu, int from) { ft_p ft = &ft_data[ftu]; fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ @@ -982,13 +1218,13 @@ restate: if (ft->lastpos != (ft->xblk-1)) { DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n", ftu, ft->lastpos, ft->xblk)); - ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0, 0, 0); + ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0); } /* Tape is in position but stopped. */ if (!ft->moving) { DPRT(("async_write ******STARTING TAPE\n")); - ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); + ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0); } awr_state = 1; goto restate; @@ -997,8 +1233,8 @@ restate: /* Tape is now moving and in position-- start DMA now! */ isa_dmastart(B_WRITE, ft->xptr, QCV_BLKSIZE, 2); out_fdc(fdcu, 0x45); /* write */ - out_fdc(fdcu, 0x00); /* unit */ - out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */ + out_fdc(fdcu, ftu); /* unit */ + out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cyl */ out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */ out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */ out_fdc(fdcu, 0x03); /* 1K sectors */ @@ -1023,13 +1259,16 @@ restate: /* Check for errors */ if ((rddta[0] & 0xc0) != 0x00) { - if (rddta[1] & 0x04) { - /* Probably wrong position */ - ft->lastpos = ft->xblk; - awr_state = 0; - goto restate; - } else if (retries < 5) { +#if !FTDBGALL + where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + + rddta[5]-1; + DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", + rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], + where, ft->xblk)); +#endif + if (retries < 3) { /* Something happened -- try again */ + DPRT(("async_write: doing retry %d\n", retries)); ft->lastpos = ft->xblk; awr_state = 0; retries++; @@ -1037,11 +1276,11 @@ restate: } else { /* * Retries failed. Note the unrecoverable error. - * Marking the block as bad is fairly useless. + * Marking the block as bad is useless right now. */ printf("ft%d: unrecoverable write error on block %d\n", ftu, ft->xblk); - ft->curseg->reqcrc |= (1 << ft->xcnt); + ft->segh->reqcrc |= (1 << ft->xcnt); } } @@ -1063,7 +1302,7 @@ restate: case 3: ft->moving = 1; - ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0); break; case 4: @@ -1081,11 +1320,14 @@ restate: /* * Interrupt handler for active tape. Bounced off of fdintr(). */ -int ftintr(ftu_t ftu) +int +ftintr(ftu_t ftu) { int st0, pcn, i; ft_p ft = &ft_data[ftu]; - fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ + fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ + int s = splbio(); + st0 = 0; pcn = 0; @@ -1093,12 +1335,14 @@ int ftintr(ftu_t ftu) if (ft->active) { if (async_func != ACMD_NONE) { async_cmd(ftu); + splx(s); return(1); } #if FTDBGALL DPRT(("Got request interrupt\n")); #endif async_req(ftu, 0); + splx(s); return(1); } @@ -1113,20 +1357,21 @@ int ftintr(ftu_t ftu) huh_what: printf("ft%d: unexpected interrupt; st0 = $%02x pcn = %d\n", ftu, st0, pcn); + splx(s); return(1); } switch (ft->cmd_wait) { case FTCMD_RESET: ft->sts_wait = FTSTS_INTERRUPT; - wakeup((caddr_t)&ftsem.intr_wait); + wakeup((caddr_t)wc_intr_wait); break; case FTCMD_RECAL: case FTCMD_SEEK: if (st0 & 0x20) { /* seek done */ ft->sts_wait = FTSTS_INTERRUPT; ft->pcn = pcn; - wakeup((caddr_t)&ftsem.intr_wait); + wakeup((caddr_t)wc_intr_wait); } #if FTDBGALL else @@ -1137,20 +1382,23 @@ huh_what: case FTCMD_READID: for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu); ft->sts_wait = FTSTS_INTERRUPT; - wakeup((caddr_t)&ftsem.intr_wait); + wakeup((caddr_t)wc_intr_wait); break; default: goto huh_what; } + splx(s); return(1); } + /* * Interrupt timeout routine. */ -static void ft_timeout(caddr_t arg1, int arg2) +static void +ft_timeout(caddr_t arg1, int arg2) { int s; ftu_t ftu = (ftu_t)arg1; @@ -1166,17 +1414,19 @@ static void ft_timeout(caddr_t arg1, int arg2) async_req(ftu, 1); } else { ft->sts_wait = FTSTS_TIMEOUT; - wakeup((caddr_t)&ftsem.intr_wait); + wakeup((caddr_t)wc_intr_wait); } splx(s); } + /* * Wait for a particular interrupt to occur. ftintr() will wake us up * if it sees what we want. Otherwise, time out and return error. * Should always disable ints before trigger is sent and calling here. */ -int ftintr_wait(ftu_t ftu, int cmd, int ticks) +static int +ftintr_wait(ftu_t ftu, int cmd, int ticks) { int retries, st0, pcn; ft_p ft = &ft_data[ftu]; @@ -1211,8 +1461,7 @@ int ftintr_wait(ftu_t ftu, int cmd, int ticks) goto intrdone; } - if (ticks) timeout(ft_timeout, (caddr_t)ftu, ticks); - sleep((caddr_t)&ftsem.intr_wait, FTPRI); + ftsleep(wc_intr_wait, ticks); intrdone: if (ft->sts_wait == FTSTS_TIMEOUT) { /* timeout */ @@ -1230,11 +1479,13 @@ intrdone: return(0); } + /* * Recalibrate tape drive. Parameter totape is true, if we should * recalibrate to tape drive settings. */ -int tape_recal(ftu_t ftu, int totape) +static int +tape_recal(ftu_t ftu, int totape) { int s; ft_p ft = &ft_data[ftu]; @@ -1248,7 +1499,7 @@ int tape_recal(ftu_t ftu, int totape) s = splbio(); out_fdc(fdcu, NE7CMD_RECAL); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); if (ftintr_wait(ftu, FTCMD_RECAL, hz)) { splx(s); @@ -1265,18 +1516,25 @@ int tape_recal(ftu_t ftu, int totape) return(0); } -static void state_timeout(caddr_t arg1, int arg2) + +/* + * Timeout for long delays. + */ +static void +state_timeout(caddr_t arg1, int arg2) { ftu_t ftu = (ftu_t)arg1; - wakeup((caddr_t)&ftsem.long_delay); + wakeup((caddr_t)wc_long_delay); } + /* * Wait for a particular tape status to be met. If all is TRUE, then * all states must be met, otherwise any state can be met. */ -int tape_state(ftu_t ftu, int all, int mask, int seconds) +static int +tape_state(ftu_t ftu, int all, int mask, int seconds) { int r, tries, maxtries; @@ -1287,20 +1545,19 @@ int tape_state(ftu_t ftu, int all, int mask, int seconds) if (all && (r & mask) == mask) return(r); if ((r & mask) != 0) return(r); } - if (seconds) { - timeout(state_timeout, (caddr_t)ftu, hz/4); - sleep((caddr_t)&ftsem.long_delay, FTPRI); - } + if (seconds) ftsleep(wc_long_delay, hz/4); } DPRT(("ft%d: tape_state failed on mask=$%02x maxtries=%d\n", ftu, mask, maxtries)); return(-1); } + /* * Send a QIC command to tape drive, wait for completion. */ -int tape_cmd(ftu_t ftu, int cmd) +static int +tape_cmd(ftu_t ftu, int cmd) { int newcn; int retries = 0; @@ -1316,7 +1573,7 @@ retry: /* Perform seek */ s = splbio(); out_fdc(fdcu, NE7CMD_SEEK); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); out_fdc(fdcu, newcn); if (ftintr_wait(ftu, FTCMD_SEEK, hz)) { @@ -1338,25 +1595,40 @@ redo: return(0); } + /* * Return status of tape drive */ -int tape_status(ftu_t ftu) +static int +tape_status(ftu_t ftu) { int r, err, tries; - ft_p ft = &ft_data[ftu]; + ft_p ft = &ft_data[ftu]; + int max = (ft->attaching) ? 2 : 3; - for (r = -1, tries = 0; r < 0 && tries < 3; tries++) + for (r = -1, tries = 0; r < 0 && tries < max; tries++) r = qic_status(ftu, QC_STATUS, 8); - if (tries == 3) return(-1); + if (tries == max) return(-1); + +recheck: DPRT(("tape_status got $%04x\n",r)); ft->laststs = r; if (r & (QS_ERROR|QS_NEWCART)) { - if (r & QS_NEWCART) ft->newcart = 1; err = qic_status(ftu, QC_ERRCODE, 16); ft->lasterr = err; - if ((r & QS_NEWCART) == 0 && err && ft->attaching == 0) { + if (r & QS_NEWCART) { + ft->newcart = 1; + /* If tape not referenced, do a seek load point. */ + if ((r & QS_FMTOK) == 0 && !ft->attaching) { + tape_cmd(ftu, QC_SEEKLP); + do { + ftsleep(wc_long_delay, hz); + } while ((r = qic_status(ftu, QC_STATUS, 8)) < 0 || + (r & (QS_READY|QS_CART)) == QS_CART); + goto recheck; + } + } else if (err && !ft->attaching) { DPRT(("ft%d: QIC error %d occurred on cmd %d\n", ftu, err & 0xff, err >> 8)); } @@ -1364,29 +1636,36 @@ int tape_status(ftu_t ftu) ft->laststs = r; DPRT(("tape_status got error code $%04x new sts = $%02x\n",err,r)); } + ft->rdonly = (r & QS_RDONLY); return(r); } + /* * Transfer control to tape drive. */ -void tape_start(ftu_t ftu) +static void +tape_start(ftu_t ftu, int motor) { ft_p ft = &ft_data[ftu]; fdc_p fdc = ft->fdc; - int s; - - DPRT(("tape_start start\n")); + int s, mbits; s = splbio(); + DPRT(("tape_start start\n")); /* reset, dma disable */ - outb(fdc->baseport+fdout, 0x00); + outb(fdc->baseport+FDOUT, 0x00); (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); - /* raise reset, enable DMA */ - outb(fdc->baseport+fdout, FDO_FRST | FDO_FDMAEN); + /* raise reset, enable DMA, motor on if needed */ + if (motor) + mbits = (!ftu) ? FDO_MOEN0 : FDO_MOEN1; + else + mbits = 0; + + outb(fdc->baseport+FDOUT, FDO_FRST | FDO_FDMAEN | mbits); (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); splx(s); @@ -1394,16 +1673,18 @@ void tape_start(ftu_t ftu) tape_recal(ftu, 1); /* set transfer speed */ - outb(fdc->baseport+fdctl, FDC_500KBPS); + outb(fdc->baseport+FDCTL, FDC_500KBPS); DELAY(10); DPRT(("tape_start end\n")); } + /* * Transfer control back to floppy disks. */ -void tape_end(ftu_t ftu) +static void +tape_end(ftu_t ftu) { ft_p ft = &ft_data[ftu]; fdc_p fdc = ft->fdc; @@ -1415,41 +1696,59 @@ void tape_end(ftu_t ftu) s = splbio(); /* reset, dma disable */ - outb(fdc->baseport+fdout, 0x00); + outb(fdc->baseport+FDOUT, 0x00); (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); /* raise reset, enable DMA */ - outb(fdc->baseport+fdout, FDO_FRST | FDO_FDMAEN); + outb(fdc->baseport+FDOUT, FDO_FRST | FDO_FDMAEN); (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); splx(s); /* set transfer speed */ - outb(fdc->baseport+fdctl, FDC_500KBPS); + outb(fdc->baseport+FDCTL, FDC_500KBPS); DELAY(10); fdc->flags &= ~FDC_TAPE_BUSY; DPRT(("tape_end end\n")); } + /* * Wait for the driver to go inactive, cancel readahead if necessary. */ -void tape_inactive(ftu_t ftu) +static void +tape_inactive(ftu_t ftu) { ft_p ft = &ft_data[ftu]; - - if (ft->curseg->reqtype == FTIO_RDAHEAD) { - ft->curseg->reqcan = 1; /* XXX cancel rdahead */ - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + int s = splbio(); + + if (ft->segh != NULL) { + if (ft->segh->reqtype == FTIO_RDAHEAD) { + /* cancel read-ahead */ + ft->segh->reqcan = 1; + } else if (ft->segh->reqtype == FTIO_WRITING && !ft->active) { + /* flush out any remaining writes */ + DPRT(("Flushing write I/O chain\n")); + arq_state = ard_state = awr_state = 0; + ft->xblk = ft->segh->reqblk; + ft->xseg = ft->segh->reqseg; + ft->xcnt = 0; + ft->xptr = ft->segh->buff; + ft->active = 1; + timeout(ft_timeout, (caddr_t)ftu, 1); + } } - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + while (ft->active) ftsleep(wc_iosts_change, 0); + splx(s); } + /* * Get the geometry of the tape currently in the drive. */ -int ftgetgeom(ftu_t ftu) +static int +ftgetgeom(ftu_t ftu) { int r, i, tries; int cfg, qic80, ext; @@ -1459,7 +1758,7 @@ int ftgetgeom(ftu_t ftu) r = tape_status(ftu); /* XXX fix me when format mode is finished */ - if ((r & QS_CART) == 0 || (r & QS_FMTOK) == 0) { + if (r < 0 || (r & QS_CART) == 0 || (r & QS_FMTOK) == 0) { DPRT(("ftgetgeom: no cart or not formatted 0x%04x\n",r)); ftg = NULL; ft->newcart = 1; @@ -1539,20 +1838,21 @@ int ftgetgeom(ftu_t ftu) return(0); } + /* * Switch between tape/floppy. This will send the tape enable/disable * codes for this drive's manufacturer. */ -int set_fdcmode(dev_t dev, int newmode) +static int +set_fdcmode(dev_t dev, int newmode) { ftu_t ftu = FDUNIT(minor(dev)); ft_p ft = &ft_data[ftu]; fdc_p fdc = ft->fdc; - static int havebufs = 0; void *buf; int r, s, i; - SegReq *sp; + SegReq *sp, *rsp; if (newmode == FDC_TAPE_MODE) { /* Wake up the tape drive */ @@ -1560,19 +1860,22 @@ int set_fdcmode(dev_t dev, int newmode) case NO_TYPE: fdc->flags &= ~FDC_TAPE_BUSY; return(ENXIO); + case FT_NONE: + tape_start(ftu, 0); + break; case FT_COLORADO: - tape_start(ftu); + tape_start(ftu, 0); if (tape_cmd(ftu, QC_COL_ENABLE1)) { tape_end(ftu); return(EIO); } - if (tape_cmd(ftu, QC_COL_ENABLE2)) { + if (tape_cmd(ftu, QC_COL_ENABLE2 + ftu)) { tape_end(ftu); return(EIO); } break; case FT_MOUNTAIN: - tape_start(ftu); + tape_start(ftu, 0); if (tape_cmd(ftu, QC_MTN_ENABLE1)) { tape_end(ftu); return(EIO); @@ -1582,56 +1885,93 @@ int set_fdcmode(dev_t dev, int newmode) return(EIO); } break; + case FT_INSIGHT: + tape_start(ftu, 1); + break; default: DPRT(("ft%d: bad tape type\n", ftu)); return(ENXIO); } if (tape_status(ftu) < 0) { - tape_cmd(ftu, (ft->type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE); + if (ft->type == FT_COLORADO) + tape_cmd(ftu, QC_COL_DISABLE); + else if (ft->type == FT_MOUNTAIN) + tape_cmd(ftu, QC_MTN_DISABLE); tape_end(ftu); return(EIO); } /* Grab buffers from memory. */ if (!havebufs) { - ft->curseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT); - if (ft->curseg == NULL) { - printf("ft%d: not enough memory for buffers\n", ftu); - return(ENOMEM); - } - ft->bufseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT); - if (ft->bufseg == NULL) { - free(ft->curseg, M_DEVBUF); - printf("ft%d: not enough memory for buffers\n", ftu); - return(ENOMEM); + ft->segh = ft->segt = NULL; + ft->doneh = ft->donet = NULL; + ft->segfree = NULL; + ft->hdr = NULL; + ft->nsegq = ft->ndoneq = ft->nfreelist = 0; + for (i = 0; i < FTNBUFF; i++) { + sp = malloc(sizeof(SegReq), M_DEVBUF, M_WAITOK); + if (sp == NULL) { + printf("ft%d: not enough memory for buffers\n", ftu); + for (sp=ft->segfree; sp != NULL; sp=sp->next) + free(sp, M_DEVBUF); + if (ft->type == FT_COLORADO) + tape_cmd(ftu, QC_COL_DISABLE); + else if (ft->type == FT_MOUNTAIN) + tape_cmd(ftu, QC_MTN_DISABLE); + tape_end(ftu); + return(ENOMEM); + } + sp->reqtype = FTIO_READY; + sp->next = ft->segfree; + ft->segfree = sp; + ft->nfreelist++; } + /* take one buffer for header */ + ft->hdr = ft->segfree; + ft->segfree = ft->segfree->next; + ft->nfreelist--; havebufs = 1; } - ft->curseg->reqtype = FTIO_READY; - ft->bufseg->reqtype = FTIO_READY; ft->io_sts = FTIO_READY; /* tape drive is ready */ ft->active = 0; /* interrupt driver not active */ ft->moving = 0; /* tape not moving */ ft->rdonly = 0; /* tape read only */ - ft->newcart = 0; /* a new cart was inserted */ + ft->newcart = 0; /* new cartridge flag */ ft->lastpos = -1; /* tape is rewound */ + async_func = ACMD_NONE; /* No async function */ tape_state(ftu, 0, QS_READY, 60); tape_cmd(ftu, QC_RATE); tape_cmd(ftu, QCF_RT500+2); /* 500K bps */ tape_state(ftu, 0, QS_READY, 60); ft->mode = FTM_PRIMARY; - tape_cmd(ftu, QC_PRIMARY); /* Make sure we're in primary mode */ + tape_cmd(ftu, QC_PRIMARY); /* Make sure we're in primary mode */ tape_state(ftu, 0, QS_READY, 60); ftg = NULL; /* No geometry yet */ ftgetgeom(ftu); /* Get tape geometry */ ftreq_rewind(ftu); /* Make sure tape is rewound */ } else { - tape_cmd(ftu, (ft->type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE); + if (ft->type == FT_COLORADO) + tape_cmd(ftu, QC_COL_DISABLE); + else if (ft->type == FT_MOUNTAIN) + tape_cmd(ftu, QC_MTN_DISABLE); tape_end(ftu); ft->newcart = 0; /* clear new cartridge */ + if (ft->hdr != NULL) free(ft->hdr, M_DEVBUF); + if (havebufs) { + for (sp = ft->segfree; sp != NULL;) { + rsp = sp; sp = sp->next; + free(rsp, M_DEVBUF); + } + for (sp = ft->segh; sp != NULL;) { + rsp = sp; sp = sp->next; + free(rsp, M_DEVBUF); + } + for (sp = ft->doneh; sp != NULL;) { + rsp = sp; sp = sp->next; + free(rsp, M_DEVBUF); + } + } havebufs = 0; - free(ft->curseg, M_DEVBUF); - free(ft->bufseg, M_DEVBUF); } return(0); } @@ -1640,7 +1980,8 @@ int set_fdcmode(dev_t dev, int newmode) /* * Perform a QIC status function. */ -int qic_status(ftu_t ftu, int cmd, int nbits) +static int +qic_status(ftu_t ftu, int cmd, int nbits) { int st3, val, r, i; ft_p ft = &ft_data[ftu]; @@ -1653,7 +1994,7 @@ int qic_status(ftu_t ftu, int cmd, int nbits) /* Sense drive status */ out_fdc(fdcu, NE7CMD_SENSED); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); st3 = in_fdc(fdcu); if ((st3 & 0x10) == 0) { /* track 0 */ @@ -1668,7 +2009,7 @@ int qic_status(ftu_t ftu, int cmd, int nbits) } out_fdc(fdcu, NE7CMD_SENSED); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); st3 = in_fdc(fdcu); if (st3 < 0) { DPRT(("ft%d: controller timed out on bit %d r=$%02x\n", @@ -1690,11 +2031,13 @@ int qic_status(ftu_t ftu, int cmd, int nbits) return(r); } + /* * Open tape drive for use. Bounced off of Fdopen if tape minor is * detected. */ -int ftopen(dev_t dev, int arg2) { +int +ftopen(dev_t dev, int arg2) { ftu_t ftu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); fdc_p fdc; @@ -1714,19 +2057,21 @@ int ftopen(dev_t dev, int arg2) { return(set_fdcmode(dev, FDC_TAPE_MODE)); /* try to switch to tape */ } + /* * Close tape and return floppy controller to disk mode. */ -int ftclose(dev_t dev, int flags) +int +ftclose(dev_t dev, int flags) { int s; SegReq *sp; ftu_t ftu = FDUNIT(minor(dev)); ft_p ft = &ft_data[ftu]; + /* Wait for any remaining I/O activity to complete. */ - if (ft->curseg->reqtype == FTIO_RDAHEAD) ft->curseg->reqcan = 1; - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + tape_inactive(ftu); ft->mode = FTM_PRIMARY; tape_cmd(ftu, QC_PRIMARY); @@ -1735,94 +2080,124 @@ int ftclose(dev_t dev, int flags) return(set_fdcmode(dev, FDC_DISK_MODE)); /* Otherwise, close tape */ } + /* - * Perform strategy on a given buffer (not!). The driver was not - * performing very efficiently using the buffering routines. After - * support for error correction was added, this routine became - * obsolete in favor of doing ioctl's. Ugly, yes. + * Perform strategy on a given buffer (not!). Changed so that the + * driver will at least return 'Operation not supported'. */ -void ftstrategy(struct buf *bp) +void +ftstrategy(struct buf *bp) { - return; + bp->b_error = ENODEV; + bp->b_flags |= B_ERROR; + biodone(bp); } -/* Read or write a segment. */ -int ftreq_rw(ftu_t ftu, int cmd, QIC_Segment *sr, struct proc *p) + +/* + * Read or write a segment. + */ +static int +ftreq_rw(ftu_t ftu, int cmd, QIC_Segment *sr, struct proc *p) { int r, i, j; SegReq *sp; int s; - long blk, bad; + long blk, bad, seg; unsigned char *cp, *cp2; ft_p ft = &ft_data[ftu]; - if (!ft->active) { + if (!ft->active && ft->segh == NULL) { r = tape_status(ftu); - if ((r & QS_CART) == 0) { + if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */ - } - if ((r & QS_FMTOK) == 0) { + if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */ - } tape_state(ftu, 0, QS_READY, 90); } if (ftg == NULL || ft->newcart) { - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + tape_inactive(ftu); tape_state(ftu, 0, QS_READY, 90); - if (ftgetgeom(ftu) < 0) { + if (ftgetgeom(ftu) < 0) return(ENXIO); - } } /* Write not allowed on a read-only tape. */ - if (cmd == QIOWRITE && ft->rdonly) { + if (cmd == QIOWRITE && ft->rdonly) return(EROFS); - } + /* Quick check of request and buffer. */ - if (sr == NULL || sr->sg_data == NULL) { + if (sr == NULL || sr->sg_data == NULL) return(EINVAL); - } - if (sr->sg_trk >= ftg->g_trktape || - sr->sg_seg >= ftg->g_segtrk) { + + /* Make sure requested track and segment is in range. */ + if (sr->sg_trk >= ftg->g_trktape || sr->sg_seg >= ftg->g_segtrk) return(EINVAL); - } + blk = sr->sg_trk * ftg->g_blktrk + sr->sg_seg * QCV_BLKSEG; + seg = sr->sg_trk * ftg->g_segtrk + sr->sg_seg; s = splbio(); if (cmd == QIOREAD) { - if (ft->curseg->reqtype == FTIO_RDAHEAD) { - if (blk == ft->curseg->reqblk) { - sp = ft->curseg; + /* + * See if the driver is reading ahead. + */ + if (ft->doneh != NULL || + (ft->segh != NULL && ft->segh->reqtype == FTIO_RDAHEAD)) { + /* + * Eat the completion queue and see if the request + * is already there. + */ + while (ft->doneh != NULL) { + if (blk == ft->doneh->reqblk) { + sp = ft->doneh; + sp->reqtype = FTIO_READING; + sp->reqbad = sr->sg_badmap; + goto rddone; + } + segio_free(ft, ft->doneh); + } + + /* + * Not on the completed queue, in progress maybe? + */ + if (ft->segh != NULL && ft->segh->reqtype == FTIO_RDAHEAD && + blk == ft->segh->reqblk) { + sp = ft->segh; sp->reqtype = FTIO_READING; sp->reqbad = sr->sg_badmap; goto rdwait; - } else - ft->curseg->reqcan = 1; /* XXX cancel rdahead */ + } } /* Wait until we're ready. */ - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + tape_inactive(ftu); /* Set up a new read request. */ - sp = ft->curseg; + sp = segio_alloc(ft); sp->reqcrc = 0; sp->reqbad = sr->sg_badmap; sp->reqblk = blk; + sp->reqseg = seg; sp->reqcan = 0; sp->reqtype = FTIO_READING; + segio_queue(ft, sp); /* Start the read request off. */ DPRT(("Starting read I/O chain\n")); arq_state = ard_state = awr_state = 0; ft->xblk = sp->reqblk; + ft->xseg = sp->reqseg; ft->xcnt = 0; ft->xptr = sp->buff; ft->active = 1; timeout(ft_timeout, (caddr_t)ftu, 1); rdwait: - sleep((caddr_t)&ftsem.buff_avail, FTPRI); + ftsleep(wc_buff_done, 0); + +rddone: bad = sp->reqbad; sr->sg_crcmap = sp->reqcrc & ~bad; @@ -1833,17 +2208,29 @@ rdwait: copyout(cp, cp2, QCV_BLKSIZE); cp2 += QCV_BLKSIZE; } + segio_free(ft, sp); } else { - if (ft->curseg->reqtype == FTIO_RDAHEAD) { - ft->curseg->reqcan = 1; /* XXX cancel rdahead */ - while (ft->active) - sleep((caddr_t)&ftsem.iosts_change, FTPRI); + if (ft->segh != NULL && ft->segh->reqtype != FTIO_WRITING) + tape_inactive(ftu); + + /* Allocate a buffer and start tape if we're running low. */ + sp = segio_alloc(ft); + if (!ft->active && (sp == NULL || ft->nfreelist <= 1)) { + DPRT(("Starting write I/O chain\n")); + arq_state = ard_state = awr_state = 0; + ft->xblk = ft->segh->reqblk; + ft->xseg = ft->segh->reqseg; + ft->xcnt = 0; + ft->xptr = ft->segh->buff; + ft->active = 1; + timeout(ft_timeout, (caddr_t)ftu, 1); } /* Sleep until a buffer becomes available. */ - while (ft->bufseg->reqtype != FTIO_READY) - sleep((caddr_t)&ftsem.buff_avail, FTPRI); - sp = (ft->curseg->reqtype == FTIO_READY) ? ft->curseg : ft->bufseg; + while (sp == NULL) { + ftsleep(wc_buff_avail, 0); + sp = segio_alloc(ft); + } /* Copy in segment and expand bad blocks. */ bad = sr->sg_badmap; @@ -1853,28 +2240,22 @@ rdwait: copyin(cp, cp2, QCV_BLKSIZE); cp += QCV_BLKSIZE; } - sp->reqblk = blk; + sp->reqseg = seg; sp->reqcan = 0; sp->reqtype = FTIO_WRITING; - - if (!ft->active) { - DPRT(("Starting write I/O chain\n")); - arq_state = ard_state = awr_state = 0; - ft->xblk = sp->reqblk; - ft->xcnt = 0; - ft->xptr = sp->buff; - ft->active = 1; - timeout(ft_timeout, (caddr_t)ftu, 1); - } + segio_queue(ft, sp); } splx(s); return(0); } -/* Rewind to beginning of tape */ -int ftreq_rewind(ftu_t ftu) +/* + * Rewind to beginning of tape + */ +static int +ftreq_rewind(ftu_t ftu) { ft_p ft = &ft_data[ftu]; @@ -1891,8 +2272,12 @@ int ftreq_rewind(ftu_t ftu) return(0); } -/* Move to logical beginning or end of track */ -int ftreq_trkpos(ftu_t ftu, int req) + +/* + * Move to logical beginning or end of track + */ +static int +ftreq_trkpos(ftu_t ftu, int req) { int curtrk, r, cmd; ft_p ft = &ft_data[ftu]; @@ -1919,8 +2304,12 @@ int ftreq_trkpos(ftu_t ftu, int req) return(0); } -/* Seek tape head to a particular track. */ -int ftreq_trkset(ftu_t ftu, int *trk) + +/* + * Seek tape head to a particular track. + */ +static int +ftreq_trkset(ftu_t ftu, int *trk) { int curtrk, r, cmd; ft_p ft = &ft_data[ftu]; @@ -1942,27 +2331,45 @@ int ftreq_trkset(ftu_t ftu, int *trk) return(0); } -/* Start tape moving forward. */ -int ftreq_lfwd(ftu_t ftu) + +/* + * Start tape moving forward. + */ +static int +ftreq_lfwd(ftu_t ftu) { + ft_p ft = &ft_data[ftu]; + tape_inactive(ftu); tape_cmd(ftu, QC_STOP); tape_state(ftu, 0, QS_READY, 90); tape_cmd(ftu, QC_FORWARD); + ft->moving = 1; return(0); } -/* Stop the tape */ -int ftreq_stop(ftu_t ftu) + +/* + * Stop the tape + */ +static int +ftreq_stop(ftu_t ftu) { + ft_p ft = &ft_data[ftu]; + tape_inactive(ftu); tape_cmd(ftu, QC_STOP); tape_state(ftu, 0, QS_READY, 90); + ft->moving = 0; return(0); } -/* Set the particular mode the drive should be in. */ -int ftreq_setmode(ftu_t ftu, int cmd) + +/* + * Set the particular mode the drive should be in. + */ +static int +ftreq_setmode(ftu_t ftu, int cmd) { int r; ft_p ft = &ft_data[ftu]; @@ -1989,8 +2396,12 @@ int ftreq_setmode(ftu_t ftu, int cmd) return(0); } -/* Return drive status bits */ -int ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p) + +/* + * Return drive status bits + */ +static int +ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p) { ft_p ft = &ft_data[ftu]; @@ -2001,8 +2412,12 @@ int ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p) return(0); } -/* Return drive configuration bits */ -int ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p) + +/* + * Return drive configuration bits + */ +static int +ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p) { int r, tries; ft_p ft = &ft_data[ftu]; @@ -2018,8 +2433,12 @@ int ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p) return(0); } -/* Return current tape's geometry. */ -int ftreq_geom(ftu_t ftu, QIC_Geom *g) + +/* + * Return current tape's geometry. + */ +static int +ftreq_geom(ftu_t ftu, QIC_Geom *g) { tape_inactive(ftu); if (ftg == NULL && ftgetgeom(ftu) < 0) return(ENXIO); @@ -2027,8 +2446,12 @@ int ftreq_geom(ftu_t ftu, QIC_Geom *g) return(0); } -/* Return drive hardware information */ -int ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp) + +/* + * Return drive hardware information + */ +static int +ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp) { int r, tries; int rom, vend; @@ -2053,10 +2476,31 @@ int ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp) return(0); } + +/* + * Receive or Send the in-core header segment. + */ +static int +ftreq_hdr(ftu_t ftu, int cmd, QIC_Segment *sp) +{ + ft_p ft = &ft_data[ftu]; + QIC_Header *h = (QIC_Header *)ft->hdr->buff; + + if (sp == NULL || sp->sg_data == NULL) return(EINVAL); + if (cmd == QIOSENDHDR) { + copyin(sp->sg_data, ft->hdr->buff, QCV_SEGSIZE); + } else { + if (h->qh_sig != QCV_HDRMAGIC) return(EIO); + copyout(ft->hdr->buff, sp->sg_data, QCV_SEGSIZE); + } + return(0); +} + /* * I/O functions. */ -int ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) +int +ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { ftu_t ftu = FDUNIT(minor(dev)); ft_p ft = &ft_data[ftu]; @@ -2104,20 +2548,30 @@ int ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) case QIOHWINFO: return(ftreq_hwinfo(ftu, (QIC_HWInfo *)data)); + + case QIOSENDHDR: + case QIORECVHDR: + return(ftreq_hdr(ftu, cmd, (QIC_Segment *)data)); } badreq: DPRT(("ft%d: unknown ioctl(%d) request\n", ftu, cmd)); return(ENXIO); } -/* Not implemented */ -int ftdump(dev_t dev) +/* + * Not implemented + */ +int +ftdump(dev_t dev) { return(EINVAL); } -/* Not implemented */ -int ftsize(dev_t dev) +/* + * Not implemented + */ +int +ftsize(dev_t dev) { return(EINVAL); } diff --git a/sys/i386/isa/ftreg.h b/sys/i386/isa/ftreg.h index 7b4ca6a27236..c29540ce0a96 100644 --- a/sys/i386/isa/ftreg.h +++ b/sys/i386/isa/ftreg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1993 Steve Gerakines + * Copyright (c) 1993, 1994 Steve Gerakines * * This is freely redistributable software. You may do anything you * wish with it, so long as the above notice stays intact. @@ -17,6 +17,9 @@ * POSSIBILITY OF SUCH DAMAGE. * * ftreg.h - QIC-40/80 floppy tape driver header + * 06/03/94 v0.9 + * Changed seek load point to QC_SEEKLP, added reqseg to SegReq structure. + * * 10/30/93 v0.3 * More things will end up here. QC_VENDORID and QC_VERSION now used. * @@ -42,7 +45,7 @@ #define QC_SEEKSTART 11 /* seek to track start */ #define QC_SEEKEND 12 /* seek to track end */ #define QC_SEEKTRACK 13 /* seek head to track */ -#define QC_SEEKLOAD 14 /* seek load point */ +#define QC_SEEKLP 14 /* seek load point */ #define QC_FORMAT 15 /* format mode */ #define QC_WRITEREF 16 /* write reference */ #define QC_VERIFY 17 /* verify mode */ @@ -62,7 +65,7 @@ /* Colorado enable/disable. */ #define QC_COL_ENABLE1 46 /* enable */ -#define QC_COL_ENABLE2 2 /* null-op */ +#define QC_COL_ENABLE2 2 /* unit+2 */ #define QC_COL_DISABLE 47 /* disable */ /* Mountain enable/disable. */ @@ -77,5 +80,7 @@ typedef struct segq { long reqcrc; /* CRC Errors found */ long reqbad; /* Bad sector map */ long reqblk; /* Block request starts at */ + long reqseg; /* Segment request is at */ int reqcan; /* Cancel read-ahead */ + struct segq *next; /* Next request */ } SegReq; diff --git a/sys/i386/isa/ic/i82365.h b/sys/i386/isa/ic/i82365.h new file mode 100644 index 000000000000..ab381250ea72 --- /dev/null +++ b/sys/i386/isa/ic/i82365.h @@ -0,0 +1,190 @@ +#ifndef __83265_H__ +#define __83265_H__ + +/*********************************************************************** + * 82365.h -- information necessary for direct manipulation of PCMCIA + * cards and controllers + * + * Support is included for Intel 82365SL PCIC controllers and clones + * thereof. + * + * originally by Barry Jaspan; hacked over by Keith Moore + * + ***********************************************************************/ + +/* + * PCIC Registers + * Each register is given a name, and most of the bits are named too. + * I should really name them all. + * + * Finally, since the banks can be addressed with a regular syntax, + * some macros are provided for that purpose. + */ + +#define PCIC_BASE 0x03e0 /* base adddress of pcic register set */ + +/* First, all the registers */ +#define PCIC_ID_REV 0x00 /* Identification and Revision */ +#define PCIC_STATUS 0x01 /* Interface Status */ +#define PCIC_POWER 0x02 /* Power and RESETDRV control */ +#define PCIC_INT_GEN 0x03 /* Interrupt and General Control */ +#define PCIC_STAT_CHG 0x04 /* Card Status Change */ +#define PCIC_STAT_INT 0x05 /* Card Status Change Interrupt Config */ +#define PCIC_ADDRWINE 0x06 /* Address Window Enable */ +#define PCIC_IOCTL 0x07 /* I/O Control */ +#define PCIC_IO0_STL 0x08 /* I/O Address 0 Start Low Byte */ +#define PCIC_IO0_STH 0x09 /* I/O Address 0 Start High Byte */ +#define PCIC_IO0_SPL 0x0a /* I/O Address 0 Stop Low Byte */ +#define PCIC_IO0_SPH 0x0b /* I/O Address 0 Stop High Byte */ +#define PCIC_IO1_STL 0x0c /* I/O Address 1 Start Low Byte */ +#define PCIC_IO1_STH 0x0d /* I/O Address 1 Start High Byte */ +#define PCIC_IO1_SPL 0x0e /* I/O Address 1 Stop Low Byte */ +#define PCIC_IO1_SPH 0x0f /* I/O Address 1 Stop High Byte */ +#define PCIC_SM0_STL 0x10 /* System Memory Address 0 Mapping Start Low Byte */ +#define PCIC_SM0_STH 0x11 /* System Memory Address 0 Mapping Start High Byte */ +#define PCIC_SM0_SPL 0x12 /* System Memory Address 0 Mapping Stop Low Byte */ +#define PCIC_SM0_SPH 0x13 /* System Memory Address 0 Mapping Stop High Byte */ +#define PCIC_CM0_L 0x14 /* Card Memory Offset Address 0 Low Byte */ +#define PCIC_CM0_H 0x15 /* Card Memory Offset Address 0 High Byte */ +#define PCIC_CDGC 0x16 /* Card Detect and General Control */ +#define PCIC_RES17 0x17 /* Reserved */ +#define PCIC_SM1_STL 0x18 /* System Memory Address 1 Mapping Start Low Byte */ +#define PCIC_SM1_STH 0x19 /* System Memory Address 1 Mapping Start High Byte */ +#define PCIC_SM1_SPL 0x1a /* System Memory Address 1 Mapping Stop Low Byte */ +#define PCIC_SM1_SPH 0x1b /* System Memory Address 1 Mapping Stop High Byte */ +#define PCIC_CM1_L 0x1c /* Card Memory Offset Address 1 Low Byte */ +#define PCIC_CM1_H 0x1d /* Card Memory Offset Address 1 High Byte */ +#define PCIC_GLO_CTRL 0x1e /* Global Control Register */ +#define PCIC_RES1F 0x1f /* Reserved */ +#define PCIC_SM2_STL 0x20 /* System Memory Address 2 Mapping Start Low Byte */ +#define PCIC_SM2_STH 0x21 /* System Memory Address 2 Mapping Start High Byte */ +#define PCIC_SM2_SPL 0x22 /* System Memory Address 2 Mapping Stop Low Byte */ +#define PCIC_SM2_SPH 0x23 /* System Memory Address 2 Mapping Stop High Byte */ +#define PCIC_CM2_L 0x24 /* Card Memory Offset Address 2 Low Byte */ +#define PCIC_CM2_H 0x25 /* Card Memory Offset Address 2 High Byte */ +#define PCIC_RES26 0x26 /* Reserved */ +#define PCIC_RES27 0x27 /* Reserved */ +#define PCIC_SM3_STL 0x28 /* System Memory Address 3 Mapping Start Low Byte */ +#define PCIC_SM3_STH 0x29 /* System Memory Address 3 Mapping Start High Byte */ +#define PCIC_SM3_SPL 0x2a /* System Memory Address 3 Mapping Stop Low Byte */ +#define PCIC_SM3_SPH 0x2b /* System Memory Address 3 Mapping Stop High Byte */ +#define PCIC_CM3_L 0x2c /* Card Memory Offset Address 3 Low Byte */ +#define PCIC_CM3_H 0x2d /* Card Memory Offset Address 3 High Byte */ +#define PCIC_RES2E 0x2e /* Reserved */ +#define PCIC_RES2F 0x2f /* Reserved */ +#define PCIC_SM4_STL 0x30 /* System Memory Address 4 Mapping Start Low Byte */ +#define PCIC_SM4_STH 0x31 /* System Memory Address 4 Mapping Start High Byte */ +#define PCIC_SM4_SPL 0x32 /* System Memory Address 4 Mapping Stop Low Byte */ +#define PCIC_SM4_SPH 0x33 /* System Memory Address 4 Mapping Stop High Byte */ +#define PCIC_CM4_L 0x34 /* Card Memory Offset Address 4 Low Byte */ +#define PCIC_CM4_H 0x35 /* Card Memory Offset Address 4 High Byte */ +#define PCIC_RES36 0x36 /* Reserved */ +#define PCIC_RES37 0x37 /* Reserved */ +#define PCIC_RES38 0x38 /* Reserved */ +#define PCIC_RES39 0x39 /* Reserved */ +#define PCIC_RES3A 0x3a /* Reserved */ +#define PCIC_RES3B 0x3b /* Reserved */ +#define PCIC_RES3C 0x3c /* Reserved */ +#define PCIC_RES3D 0x3d /* Reserved */ +#define PCIC_RES3E 0x3e /* Reserved */ +#define PCIC_RES3F 0x3f /* Reserved */ + +/* Now register bits, ordered by reg # */ + +/* For Identification and Revision (PCIC_ID_REV) */ +#define PCIC_INTEL0 0x82 /* Intel 82365SL Rev. 0; Both Memory and I/O */ +#define PCIC_INTEL1 0x83 /* Intel 82365SL Rev. 1; Both Memory and I/O */ +#define PCIC_IBM1 0x88 /* IBM PCIC clone; Both Memory and I/O */ +#define PCIC_IBM2 0x89 /* IBM PCIC clone; Both Memory and I/O */ + +/* For Interface Status register (PCIC_STATUS) */ +#define PCIC_VPPV 0x80 /* Vpp_valid */ +#define PCIC_POW 0x40 /* PC Card power active */ +#define PCIC_READY 0x20 /* Ready/~Busy */ +#define PCIC_MWP 0x10 /* Memory Write Protect */ +#define PCIC_CD 0x0C /* Both card detect bits */ +#define PCIC_BVD 0x03 /* Both Battery Voltage Detect bits */ + +/* For the Power and RESETDRV register (PCIC_POWER) */ +#define PCIC_OUTENA 0x80 /* Output Enable */ +#define PCIC_DISRST 0x40 /* Disable RESETDRV */ +#define PCIC_APSENA 0x20 /* Auto Pwer Switch Enable */ +#define PCIC_PCPWRE 0x10 /* PC Card Power Enable */ + +/* For the Interrupt and General Control register (PCIC_INT_GEN) */ +#define PCIC_CARDTYPE 0x20 /* Card Type 0 = memory, 1 = I/O */ +#define PCIC_IOCARD 0x20 +#define PCIC_MEMCARD 0x00 +#define PCIC_CARDRESET 0x40 /* Card reset 0 = Reset, 1 = Normal */ + +/* For the Card Status Change register (PCIC_STAT_CHG) */ +#define PCIC_CDTCH 0x08 /* Card Detect Change */ +#define PCIC_RDYCH 0x04 /* Ready Change */ +#define PCIC_BATWRN 0x02 /* Battery Warning */ +#define PCIC_BATDED 0x01 /* Battery Dead */ + +/* For the Address Window Enable Register (PCIC_ADDRWINE) */ +#define PCIC_SM0_EN 0x01 /* Memory Window 0 Enable */ +#define PCIC_SM1_EN 0x02 /* Memory Window 1 Enable */ +#define PCIC_SM2_EN 0x04 /* Memory Window 2 Enable */ +#define PCIC_SM3_EN 0x08 /* Memory Window 3 Enable */ +#define PCIC_SM4_EN 0x10 /* Memory Window 4 Enable */ +#define PCIC_MEMCS16 0x20 /* ~MEMCS16 Decode A23-A12 */ +#define PCIC_IO0_EN 0x40 /* I/O Window 0 Enable */ +#define PCIC_IO1_EN 0x80 /* I/O Window 1 Enable */ + +/* For the I/O Control Register (PCIC_IOCTL) */ +#define PCIC_IO0_16BIT 0x01 /* I/O to this segment is 16 bit */ +#define PCIC_IO0_CS16 0x02 /* I/O cs16 source is the card */ +#define PCIC_IO0_0WS 0x04 /* zero wait states added on 8 bit cycles */ +#define PCIC_IO0_WS 0x08 /* Wait states added for 16 bit cycles */ +#define PCIC_IO1_16BIT 0x10 /* I/O to this segment is 16 bit */ +#define PCIC_IO1_CS16 0x20 /* I/O cs16 source is the card */ +#define PCIC_IO1_0WS 0x04 /* zero wait states added on 8 bit cycles */ +#define PCIC_IO1_WS 0x80 /* Wait states added for 16 bit cycles */ + +/* For the various I/O and Memory windows */ +#define PCIC_ADDR_LOW 0 +#define PCIC_ADDR_HIGH 1 +#define PCIC_START 0x00 /* Start of mapping region */ +#define PCIC_END 0x02 /* End of mapping region */ +#define PCIC_MOFF 0x04 /* Card Memory Mapping region offset */ +#define PCIC_IO0 0x08 /* I/O Address 0 */ +#define PCIC_IO1 0x0c /* I/O Address 1 */ +#define PCIC_SM0 0x10 /* System Memory Address 0 Mapping */ +#define PCIC_SM1 0x18 /* System Memory Address 1 Mapping */ +#define PCIC_SM2 0x20 /* System Memory Address 2 Mapping */ +#define PCIC_SM3 0x28 /* System Memory Address 3 Mapping */ +#define PCIC_SM4 0x30 /* System Memory Address 4 Mapping */ + +/* For System Memory Window start registers + (PCIC_SMx|PCIC_START|PCIC_ADDR_HIGH) */ +#define PCIC_ZEROWS 0x40 /* Zero wait states */ +#define PCIC_DATA16 0x80 /* Data width is 16 bits */ + +/* For System Memory Window stop registers + (PCIC_SMx|PCIC_END|PCIC_ADDR_HIGH) */ +#define PCIC_MW0 0x40 /* Wait state bit 0 */ +#define PCIC_MW1 0x80 /* Wait state bit 1 */ + +/* For System Memory Window offset registers + (PCIC_SMx|PCIC_MOFF|PCIC_ADDR_HIGH) */ +#define PCIC_REG 0x40 /* Attribute/Common select (why called Reg?) */ +#define PCIC_WP 0x80 /* Write-protect this window */ + +/* For Card Detect and General Control register (PCIC_CDGC) */ +#define PCIC_16_DL_INH 0x01 /* 16-bit memory delay inhibit */ +#define PCIC_CNFG_RST_EN 0x02 /* configuration reset enable */ +#define PCIC_GPI_EN 0x04 /* GPI Enable */ +#define PCIC_GPI_TRANS 0x08 /* GPI Transition Control */ +#define PCIC_CDRES_EN 0x10 /* card detect resume enable */ +#define PCIC_SW_CD_INT 0x20 /* s/w card detect interrupt */ + +/* For Global Control register (PCIC_GLO_CTRL) */ +#define PCIC_PWR_DOWN 0x01 /* power down */ +#define PCIC_LVL_MODE 0x02 /* level mode interrupt enable */ +#define PCIC_WB_CSCINT 0x04 /* explicit write-back csc intr */ +#define PCIC_IRQ14_PULSE 0x08 /* irq 14 pulse mode enable */ + +/* DON'T ADD ANYTHING AFTER THIS #endif */ +#endif /* __83265_H__ */ diff --git a/sys/i386/isa/ic/nec765.h b/sys/i386/isa/ic/nec765.h index df6c337482aa..c02e6ef362ba 100644 --- a/sys/i386/isa/ic/nec765.h +++ b/sys/i386/isa/ic/nec765.h @@ -30,8 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * from: @(#)nec765.h 7.1 (Berkeley) 5/9/91 - * $Id: nec765.h,v 1.2 1993/10/16 13:48:50 rgrimes Exp $ + * @(#)nec765.h 7.1 (Berkeley) 5/9/91 */ /* @@ -47,26 +46,90 @@ #define NE7_RQM 0x80 /* Diskette Controller ReQuest for Master */ /* Status register ST0 */ -#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005drv_chck\004drive_rdy\003top_head" +#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005equ_chck\004drive_notrdy\003top_head" + +#define NE7_ST0_IC 0xc0 /* interrupt completion code */ + +#define NE7_ST0_IC_RC 0xc0 /* terminated due to ready changed, n/a */ +#define NE7_ST0_IC_IV 0x80 /* invalid command; must reset FDC */ +#define NE7_ST0_IC_AT 0x40 /* abnormal termination, check error stat */ +#define NE7_ST0_IC_NT 0x00 /* normal termination */ + +#define NE7_ST0_SE 0x20 /* seek end */ +#define NE7_ST0_EC 0x10 /* equipment check, recalibrated but no trk0 */ +#define NE7_ST0_NR 0x08 /* not ready (n/a) */ +#define NE7_ST0_HD 0x04 /* upper head selected */ +#define NE7_ST0_DR 0x03 /* drive code */ /* Status register ST1 */ #define NE7_ST1BITS "\020\010end_of_cyl\006bad_crc\005data_overrun\003sec_not_fnd\002write_protect\001no_am" +#define NE7_ST1_EN 0x80 /* end of cylinder, access past last record */ +#define NE7_ST1_DE 0x20 /* data error, CRC fail in ID or data */ +#define NE7_ST1_OR 0x10 /* DMA overrun, DMA failed to do i/o quickly */ +#define NE7_ST1_ND 0x04 /* no data, sector not found or CRC in ID f. */ +#define NE7_ST1_NW 0x02 /* not writeable, attempt to violate WP */ +#define NE7_ST1_MA 0x01 /* missing address mark (in ID or data field)*/ + /* Status register ST2 */ #define NE7_ST2BITS "\020\007ctrl_mrk\006bad_crc\005wrong_cyl\004scn_eq\003scn_not_fnd\002bad_cyl\001no_dam" +#define NE7_ST2_CM 0x40 /* control mark; found deleted data */ +#define NE7_ST2_DD 0x20 /* data error in data field, CRC fail */ +#define NE7_ST2_WC 0x10 /* wrong cylinder, ID field mismatches cmd */ +#define NE7_ST2_SH 0x08 /* scan equal hit */ +#define NE7_ST2_SN 0x04 /* scan not satisfied */ +#define NE7_ST2_BC 0x02 /* bad cylinder, cylinder marked 0xff */ +#define NE7_ST2_MD 0x01 /* missing address mark in data field */ + /* Status register ST3 */ #define NE7_ST3BITS "\020\010fault\007write_protect\006drdy\005tk0\004two_side\003side_sel\002" +#define NE7_ST3_FT 0x80 /* fault; PC: n/a */ +#define NE7_ST3_WP 0x40 /* write protected */ +#define NE7_ST3_RD 0x20 /* ready; PC: always true */ +#define NE7_ST3_T0 0x10 /* track 0 */ +#define NE7_ST3_TS 0x08 /* two-sided; PC: n/a */ +#define NE7_ST3_HD 0x04 /* upper head select */ +#define NE7_ST3_US 0x03 /* unit select */ + /* Commands */ +/* + * the top three bits -- where appropriate -- are set as follows: + * + * 0x80 - MT multi-track; allow both sides to be handled in single cmd + * 0x40 - MFM modified frequency modulation; use MFM encoding + * 0x20 - SK skip; skip sectors marked as "deleted" + */ +#define NE7CMD_READTRK 0x42 /* read whole track */ #define NE7CMD_SPECIFY 3 /* specify drive parameters - requires unit parameters byte */ #define NE7CMD_SENSED 4 /* sense drive - requires unit select byte */ #define NE7CMD_WRITE 0xc5 /* write - requires eight additional bytes */ #define NE7CMD_READ 0xe6 /* read - requires eight additional bytes */ -#define NE7CMD_FORMAT 0x4c /* format - requires five additional bytes */ #define NE7CMD_RECAL 7 /* recalibrate drive - requires unit select byte */ #define NE7CMD_SENSEI 8 /* sense controller interrupt status */ -#define NE7CMD_SEEK 15 /* seek drive - requires unit select byte +#define NE7CMD_WRITEDEL 0xc9 /* write deleted data */ +#define NE7CMD_READID 0x4a /* read ID field */ +#define NE7CMD_READDEL 0xec /* read deleted data */ +#define NE7CMD_FORMAT 0x4d /* format - requires five additional bytes */ +#define NE7CMD_SEEK 0x0f /* seek drive - requires unit select byte and new cyl byte */ +#define NE7CMD_SCNEQU 0xf1 /* scan equal */ +#define NE7CMD_SCNLE 0xf9 /* scan less or equal */ +#define NE7CMD_SCNGE 0xfd /* scan greater or equal */ + + +/* + * "specify" definitions + * + * acronyms (times are relative to a FDC clock of 8 MHz): + * srt - step rate; PC usually 3 ms + * hut - head unload time; PC usually maximum of 240 ms + * hlt - head load time; PC usually minimum of 2 ms + * nd - no DMA flag; PC usually not set (0) + */ + +#define NE7_SPEC_1(srt, hut) (((16 - (srt)) << 4) | (((hut) / 16))) +#define NE7_SPEC_2(hlt, nd) (((hlt) & 0xFE) | ((nd) & 1)) diff --git a/sys/i386/isa/icu.h b/sys/i386/isa/icu.h index af50335f4468..523f3351a859 100644 --- a/sys/i386/isa/icu.h +++ b/sys/i386/isa/icu.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)icu.h 5.6 (Berkeley) 5/9/91 - * $Id: icu.h,v 1.2 1993/10/16 13:45:51 rgrimes Exp $ + * $Id: icu.h,v 1.3 1994/04/02 07:00:40 davidg Exp $ */ /* @@ -51,12 +51,6 @@ * Interrupt "level" mechanism variables, masks, and macros */ extern unsigned imen; /* interrupt mask enable */ -extern unsigned cpl; /* current priority level mask */ - -extern unsigned highmask; /* group of interrupts masked with splhigh() */ -extern unsigned ttymask; /* group of interrupts masked with spltty() */ -extern unsigned biomask; /* group of interrupts masked with splbio() */ -extern unsigned netmask; /* group of interrupts masked with splimp() */ #define INTREN(s) (imen &= ~(s), SET_ICUS()) #define INTRDIS(s) (imen |= (s), SET_ICUS()) @@ -74,7 +68,7 @@ extern unsigned netmask; /* group of interrupts masked with splimp() */ #endif /* - * Interrupt enable bits -- in order of priority + * Interrupt enable bits - in normal order of priority (which we change) */ #define IRQ0 0x0001 /* highest priority - timer */ #define IRQ1 0x0002 @@ -88,7 +82,7 @@ extern unsigned netmask; /* group of interrupts masked with splimp() */ #define IRQ13 0x2000 #define IRQ14 0x4000 #define IRQ15 0x8000 -#define IRQ3 0x0008 +#define IRQ3 0x0008 /* this is highest after rotation */ #define IRQ4 0x0010 #define IRQ5 0x0020 #define IRQ6 0x0040 diff --git a/sys/i386/isa/icu.s b/sys/i386/isa/icu.s index d1c843951b83..9484e5cdbedd 100644 --- a/sys/i386/isa/icu.s +++ b/sys/i386/isa/icu.s @@ -36,7 +36,7 @@ * * @(#)icu.s 7.2 (Berkeley) 5/21/91 * - * $Id: icu.s,v 1.7 1993/12/20 14:58:21 wollman Exp $ + * $Id: icu.s,v 1.8 1994/04/02 07:00:41 davidg Exp $ */ /* @@ -45,215 +45,131 @@ */ /* - * XXX - this file is now misnamed. All spls are now soft and the only thing - * related to the hardware icu is that the bit numbering is the same in the - * soft priority masks as in the hard ones. + * XXX this file should be named ipl.s. All spls are now soft and the + * only thing related to the hardware icu is that the h/w interrupt + * numbers are used without translation in the masks. */ -#include "sio.h" -#define HIGHMASK 0xffff -#define SOFTCLOCKMASK 0x8000 +#include "../net/netisr.h" .data - .globl _cpl -_cpl: .long 0xffff /* current priority (all off) */ - +_cpl: .long HWI_MASK | SWI_MASK /* current priority (all off) */ .globl _imen -_imen: .long 0xffff /* interrupt mask enable (all off) */ - -/* .globl _highmask */ -_highmask: .long HIGHMASK - - .globl _ttymask, _biomask, _netmask -_ttymask: .long 0 -_biomask: .long 0 -_netmask: .long 0 - - .globl _ipending, _astpending +_imen: .long HWI_MASK /* interrupt mask enable (all h/w off) */ +_high_imask: .long HWI_MASK | SWI_MASK + .globl _tty_imask +_tty_imask: .long 0 + .globl _bio_imask +_bio_imask: .long 0 + .globl _net_imask +_net_imask: .long 0 + .globl _ipending _ipending: .long 0 + .globl _astpending _astpending: .long 0 /* tells us an AST needs to be taken */ - .globl _netisr _netisr: .long 0 /* set with bits for which queue to service */ - vec: .long vec0, vec1, vec2, vec3, vec4, vec5, vec6, vec7 .long vec8, vec9, vec10, vec11, vec12, vec13, vec14, vec15 -#define GENSPL(name, mask, event) \ - .globl _spl/**/name ; \ - ALIGN_TEXT ; \ -_spl/**/name: ; \ - COUNT_EVENT(_intrcnt_spl, event) ; \ - movl _cpl,%eax ; \ - movl %eax,%edx ; \ - orl mask,%edx ; \ - movl %edx,_cpl ; \ - SHOW_CPL ; \ - ret - -#define FASTSPL(mask) \ - movl mask,_cpl ; \ - SHOW_CPL - -#define FASTSPL_VARMASK(varmask) \ - movl varmask,%eax ; \ - movl %eax,_cpl ; \ - SHOW_CPL - .text - ALIGN_TEXT -unpend_v: - COUNT_EVENT(_intrcnt_spl, 0) - bsfl %eax,%eax # slow, but not worth optimizing - btrl %eax,_ipending - jnc unpend_v_next # some intr cleared the in-memory bit - SHOW_IPENDING - movl Vresume(,%eax,4),%eax - testl %eax,%eax - je noresume - jmp %eax - - ALIGN_TEXT -/* - * XXX - must be some fastintr, need to register those too. - */ -noresume: -#if NSIO > 0 - call _softsio1 -#endif -unpend_v_next: - movl _cpl,%eax - movl %eax,%edx - notl %eax - andl _ipending,%eax - je none_to_unpend - jmp unpend_v - /* - * Handle return from interrupt after device handler finishes - */ - ALIGN_TEXT -doreti: - COUNT_EVENT(_intrcnt_spl, 1) - addl $4,%esp # discard unit arg - popl %eax # get previous priority -/* - * Now interrupt frame is a trap frame! - * - * XXX - setting up the interrupt frame to be almost a stack frame is mostly - * a waste of time. + * Handle return from interrupts, traps and syscalls. */ + SUPERALIGN_TEXT +_doreti: + FAKE_MCOUNT(_bintr) /* init "from" _bintr -> _doreti */ + addl $4,%esp /* discard unit number */ + popl %eax /* cpl to restore */ +doreti_next: + /* + * Check for pending HWIs and SWIs atomically with restoring cpl + * and exiting. The check has to be atomic with exiting to stop + * (ipending & ~cpl) changing from zero to nonzero while we're + * looking at it (this wouldn't be fatal but it would increase + * interrupt latency). Restoring cpl has to be atomic with exiting + * so that the stack cannot pile up (the nesting level of interrupt + * handlers is limited by the number of bits in cpl). + */ + movl %eax,%ecx + notl %ecx + cli + andl _ipending,%ecx + jne doreti_unpend +doreti_exit: movl %eax,_cpl - SHOW_CPL - movl %eax,%edx - notl %eax - andl _ipending,%eax - jne unpend_v -none_to_unpend: - testl %edx,%edx # returning to zero priority? - jne 1f # nope, going to non-zero priority - movl _netisr,%eax - testl %eax,%eax # check for softint s/traps - jne 2f # there are some - jmp test_resched # XXX - schedule jumps better - COUNT_EVENT(_intrcnt_spl, 2) # XXX - - ALIGN_TEXT # XXX -1: # XXX - COUNT_EVENT(_intrcnt_spl, 3) + MEXITCOUNT popl %es popl %ds popal addl $8,%esp iret -#include "../net/netisr.h" - -#define DONET(s, c, event) ; \ - .globl c ; \ - btrl $s,_netisr ; \ - jnc 1f ; \ - COUNT_EVENT(_intrcnt_spl, event) ; \ - call c ; \ -1: - ALIGN_TEXT -2: - COUNT_EVENT(_intrcnt_spl, 4) -/* - * XXX - might need extra locking while testing reg copy of netisr, but - * interrupt routines setting it would not cause any new problems (since we - * don't loop, fresh bits will not be processed until the next doreti or spl0). - */ - testl $~((1 << NETISR_SCLK) | (1 << NETISR_AST)),%eax - je test_ASTs # no net stuff, just temporary AST's - FASTSPL_VARMASK(_netmask) -#if 0 - DONET(NETISR_RAW, _rawintr, 5) -#endif - -#ifdef INET - DONET(NETISR_IP, _ipintr, 6) -#endif /* INET */ - -#ifdef IMP - DONET(NETISR_IMP, _impintr, 7) -#endif /* IMP */ - -#ifdef NS - DONET(NETISR_NS, _nsintr, 8) -#endif /* NS */ - -#ifdef ISO - DONET(NETISR_ISO, _clnlintr, 9) -#endif /* ISO */ +doreti_unpend: + /* + * Enabling interrupts is safe because we haven't restored cpl yet. + * The locking from the "btrl" test is probably no longer necessary. + * We won't miss any new pending interrupts because we will check + * for them again. + */ + sti + bsfl %ecx,%ecx /* slow, but not worth optimizing */ + btrl %ecx,_ipending + jnc doreti_next /* some intr cleared memory copy */ + movl ihandlers(,%ecx,4),%edx + testl %edx,%edx + je doreti_next /* "can't happen" */ + cmpl $NHWI,%ecx + jae doreti_swi + cli + movl %eax,_cpl + MEXITCOUNT + jmp %edx -#ifdef CCITT - DONET(NETISR_X25, _pkintr, 29) - DONET(NETISR_HDLC, _hdintr, 30) -#endif /* CCITT */ + ALIGN_TEXT +doreti_swi: + pushl %eax + /* + * The SWI_AST handler has to run at cpl = SWI_AST_MASK and the + * SWI_CLOCK handler at cpl = SWI_CLOCK_MASK, so we have to restore + * all the h/w bits in cpl now and have to worry about stack growth. + * The worst case is currently (30 Jan 1994) 2 SWI handlers nested + * in dying interrupt frames and about 12 HWIs nested in active + * interrupt frames. There are only 4 different SWIs and the HWI + * and SWI masks limit the nesting further. + */ + orl imasks(,%ecx,4),%eax + movl %eax,_cpl + call %edx + popl %eax + jmp doreti_next - FASTSPL($0) -test_ASTs: - btrl $NETISR_SCLK,_netisr - jnc test_resched - COUNT_EVENT(_intrcnt_spl, 10) - FASTSPL($SOFTCLOCKMASK) -/* - * Back to an interrupt frame for a moment. - */ - pushl $0 # previous cpl (probably not used) - pushl $0x7f # dummy unit number - call _softclock - addl $8,%esp # discard dummies - FASTSPL($0) -test_resched: -#ifdef notused1 - btrl $NETISR_AST,_netisr - jnc 2f -#endif -#ifdef notused2 - cmpl $0,_want_resched - je 2f -#endif - cmpl $0,_astpending # XXX - put it back in netisr to - je 2f # reduce the number of tests + ALIGN_TEXT +swi_ast: + addl $8,%esp /* discard raddr & cpl to get trap frame */ testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp) - # to non-kernel (i.e., user)? - je 2f # nope, leave - COUNT_EVENT(_intrcnt_spl, 11) - movl $0,_astpending + je swi_ast_phantom + movl $T_ASTFLT,(2+8+0)*4(%esp) call _trap -2: - COUNT_EVENT(_intrcnt_spl, 12) - popl %es - popl %ds - popal - addl $8,%esp - iret + subl %eax,%eax /* recover cpl */ + jmp doreti_next + + ALIGN_TEXT +swi_ast_phantom: + /* + * These happen when there is an interrupt in a trap handler before + * ASTs can be masked or in an lcall handler before they can be + * masked or after they are unmasked. They could be avoided for + * trap entries by using interrupt gates, and for lcall exits by + * using by using cli, but they are unavoidable for lcall entries. + */ + cli + orl $SWI_AST_PENDING,_ipending + jmp doreti_exit /* SWI_AST is highest so we must be done */ /* * Interrupt priority mechanism @@ -262,121 +178,84 @@ test_resched: * -- ipending = active interrupts currently masked by cpl */ - GENSPL(bio, _biomask, 13) - GENSPL(clock, $HIGHMASK, 14) /* splclock == splhigh ex for count */ - GENSPL(high, $HIGHMASK, 15) - GENSPL(imp, _netmask, 16) /* splimp == splnet except for count */ - GENSPL(net, _netmask, 17) - GENSPL(softclock, $SOFTCLOCKMASK, 18) - GENSPL(tty, _ttymask, 19) - - .globl _splnone - .globl _spl0 - ALIGN_TEXT -_splnone: -_spl0: - COUNT_EVENT(_intrcnt_spl, 20) -in_spl0: +ENTRY(splz) + /* + * The caller has restored cpl and checked that (ipending & ~cpl) + * is nonzero. We have to repeat the check since if there is an + * interrupt while we're looking, _doreti processing for the + * interrupt will handle all the unmasked pending interrupts + * because we restored early. We're repeating the calculation + * of (ipending & ~cpl) anyway so that the caller doesn't have + * to pass it, so this only costs one "jne". "bsfl %ecx,%ecx" + * is undefined when %ecx is 0 so we can't rely on the secondary + * btrl tests. + */ movl _cpl,%eax - pushl %eax # save old priority - testl $(1 << NETISR_RAW) | (1 << NETISR_IP),_netisr - je over_net_stuff_for_spl0 - movl _netmask,%eax # mask off those network devices - movl %eax,_cpl # set new priority - SHOW_CPL -/* - * XXX - what about other net intrs? - */ -#if 0 - DONET(NETISR_RAW, _rawintr, 21) -#endif - -#ifdef INET - DONET(NETISR_IP, _ipintr, 22) -#endif /* INET */ - -#ifdef IMP - DONET(NETISR_IMP, _impintr, 23) -#endif /* IMP */ - -#ifdef NS - DONET(NETISR_NS, _nsintr, 24) -#endif /* NS */ - -#ifdef ISO - DONET(NETISR_ISO, _clnlintr, 25) -#endif /* ISO */ - -over_net_stuff_for_spl0: - movl $0,_cpl # set new priority - SHOW_CPL - movl _ipending,%eax - testl %eax,%eax - jne unpend_V - popl %eax # return old priority +splz_next: + /* + * We don't need any locking here. (ipending & ~cpl) cannot grow + * while we're looking at it - any interrupt will shrink it to 0. + */ + movl %eax,%ecx + notl %ecx + andl _ipending,%ecx + jne splz_unpend ret - .globl _splx ALIGN_TEXT -_splx: - COUNT_EVENT(_intrcnt_spl, 26) - movl 4(%esp),%eax # new priority - testl %eax,%eax - je in_spl0 # going to "zero level" is special - COUNT_EVENT(_intrcnt_spl, 27) - movl _cpl,%edx # save old priority - movl %eax,_cpl # set new priority - SHOW_CPL - notl %eax - andl _ipending,%eax - jne unpend_V_result_edx - movl %edx,%eax # return old priority - ret - - ALIGN_TEXT -unpend_V_result_edx: - pushl %edx -unpend_V: - COUNT_EVENT(_intrcnt_spl, 28) - bsfl %eax,%eax - btrl %eax,_ipending - jnc unpend_V_next - SHOW_IPENDING - movl Vresume(,%eax,4),%edx +splz_unpend: + bsfl %ecx,%ecx + btrl %ecx,_ipending + jnc splz_next + movl ihandlers(,%ecx,4),%edx testl %edx,%edx - je noresumeV -/* - * We would prefer to call the intr handler directly here but that doesn't - * work for badly behaved handlers that want the interrupt frame. Also, - * there's a problem determining the unit number. We should change the - * interface so that the unit number is not determined at config time. - */ - jmp *vec(,%eax,4) + je splz_next /* "can't happen" */ + cmpl $NHWI,%ecx + jae splz_swi + /* + * We would prefer to call the intr handler directly here but that + * doesn't work for badly behaved handlers that want the interrupt + * frame. Also, there's a problem determining the unit number. + * We should change the interface so that the unit number is not + * determined at config time. + */ + jmp *vec(,%ecx,4) ALIGN_TEXT +splz_swi: + cmpl $SWI_AST,%ecx + je splz_next /* "can't happen" */ + pushl %eax + orl imasks(,%ecx,4),%eax + movl %eax,_cpl + call %edx + popl %eax + movl %eax,_cpl + jmp splz_next + /* - * XXX - must be some fastintr, need to register those too. + * Fake clock IRQ so that it appears to come from our caller and not from + * vec0, so that kernel profiling works. + * XXX do this more generally (for all vectors; look up the C entry point). + * XXX frame bogusness stops us from just jumping to the C entry point. */ -noresumeV: -#if NSIO > 0 - call _softsio1 -#endif -unpend_V_next: - movl _cpl,%eax - notl %eax - andl _ipending,%eax - jne unpend_V - popl %eax - ret + ALIGN_TEXT +vec0: + popl %eax /* return address */ + pushfl +#define KCSEL 8 + pushl $KCSEL + pushl %eax + cli + MEXITCOUNT + jmp _Vclk #define BUILD_VEC(irq_num) \ ALIGN_TEXT ; \ vec/**/irq_num: ; \ int $ICU_OFFSET + (irq_num) ; \ - popl %eax ; \ ret - BUILD_VEC(0) BUILD_VEC(1) BUILD_VEC(2) BUILD_VEC(3) @@ -392,3 +271,58 @@ vec/**/irq_num: ; \ BUILD_VEC(13) BUILD_VEC(14) BUILD_VEC(15) + + ALIGN_TEXT +swi_clock: + MCOUNT + subl %eax,%eax + cmpl $_splz,(%esp) /* XXX call from splz()? */ + jae 1f /* yes, usermode = 0 */ + movl 4+4+TRAPF_CS_OFF(%esp),%eax /* no, check trap frame */ + andl $SEL_RPL_MASK,%eax +1: + pushl %eax + call _softclock + addl $4,%esp + ret + +#define DONET(s, c, event) ; \ + .globl c ; \ + btrl $s,_netisr ; \ + jnc 9f ; \ + call c ; \ +9: + + ALIGN_TEXT +swi_net: + MCOUNT +#if 0 + DONET(NETISR_RAW, _rawintr,netisr_raw) +#endif +#ifdef INET + DONET(NETISR_IP, _ipintr,netisr_ip) +#endif +#ifdef IMP + DONET(NETISR_IMP, _impintr,netisr_imp) +#endif +#ifdef NS + DONET(NETISR_NS, _nsintr,netisr_ns) +#endif +#ifdef ISO + DONET(NETISR_ISO, _clnlintr,netisr_iso) +#endif +#ifdef CCITT + DONET(NETISR_X25, _pkintr, 29) + DONET(NETISR_HDLC, _hdintr, 30) +#endif + ret + + ALIGN_TEXT +swi_tty: + MCOUNT +#include "sio.h" +#if NSIO > 0 + jmp _siopoll +#else + ret +#endif diff --git a/sys/i386/isa/if_ed.c b/sys/i386/isa/if_ed.c index 079f09cc9d3b..e806b1e471f9 100644 --- a/sys/i386/isa/if_ed.c +++ b/sys/i386/isa/if_ed.c @@ -16,7 +16,7 @@ */ /* - * $Id: if_ed.c,v 1.33.2.2 1994/04/17 06:04:33 rgrimes Exp $ + * $Id: if_ed.c,v 1.42 1994/06/16 08:27:21 davidg Exp $ */ #include "ed.h" @@ -65,65 +65,71 @@ #ifndef IFF_ALTPHYS #define IFF_ALTPHYS IFF_LLC0 #endif - + /* * ed_softc: per line info and status */ -struct ed_softc { - struct arpcom arpcom; /* ethernet common */ +struct ed_softc { + struct arpcom arpcom; /* ethernet common */ - char *type_str; /* pointer to type string */ - u_char vendor; /* interface vendor */ - u_char type; /* interface type code */ + char *type_str; /* pointer to type string */ + u_char vendor; /* interface vendor */ + u_char type; /* interface type code */ - u_short asic_addr; /* ASIC I/O bus address */ - u_short nic_addr; /* NIC (DS8390) I/O bus address */ + u_short asic_addr; /* ASIC I/O bus address */ + u_short nic_addr; /* NIC (DS8390) I/O bus address */ /* * The following 'proto' variable is part of a work-around for 8013EBT asics * being write-only. It's sort of a prototype/shadow of the real thing. */ - u_char wd_laar_proto; - u_char isa16bit; /* width of access to card 0=8 or 1=16 */ - int is790; /* set by the probe code if the card is 790 based */ - - caddr_t bpf; /* BPF "magic cookie" */ - caddr_t mem_start; /* NIC memory start address */ - caddr_t mem_end; /* NIC memory end address */ - u_long mem_size; /* total NIC memory size */ - caddr_t mem_ring; /* start of RX ring-buffer (in NIC mem) */ - - u_char mem_shared; /* NIC memory is shared with host */ - u_char xmit_busy; /* transmitter is busy */ - u_char txb_cnt; /* number of transmit buffers */ - u_char txb_inuse; /* number of TX buffers currently in-use*/ - - u_char txb_new; /* pointer to where new buffer will be added */ - u_char txb_next_tx; /* pointer to next buffer ready to xmit */ - u_short txb_len[8]; /* buffered xmit buffer lengths */ - u_char tx_page_start; /* first page of TX buffer area */ - u_char rec_page_start; /* first page of RX ring-buffer */ - u_char rec_page_stop; /* last page of RX ring-buffer */ - u_char next_packet; /* pointer to next unread RX packet */ -} ed_softc[NED]; - -int ed_attach(struct isa_device *); -void ed_init(int); -void edintr(int); -int ed_ioctl(struct ifnet *, int, caddr_t); -int ed_probe(struct isa_device *); -void ed_start(struct ifnet *); -void ed_reset(int, int); -void ed_watchdog(int); - -static void ed_get_packet(struct ed_softc *, char *, int /*u_short*/); + u_char wd_laar_proto; + u_char isa16bit; /* width of access to card 0=8 or 1=16 */ + int is790; /* set by the probe code if the card is 790 + * based */ + + caddr_t bpf; /* BPF "magic cookie" */ + caddr_t mem_start; /* NIC memory start address */ + caddr_t mem_end; /* NIC memory end address */ + u_long mem_size; /* total NIC memory size */ + caddr_t mem_ring; /* start of RX ring-buffer (in NIC mem) */ + + u_char mem_shared; /* NIC memory is shared with host */ + u_char xmit_busy; /* transmitter is busy */ + u_char txb_cnt; /* number of transmit buffers */ + u_char txb_inuse; /* number of TX buffers currently in-use */ + + u_char txb_new; /* pointer to where new buffer will be added */ + u_char txb_next_tx; /* pointer to next buffer ready to xmit */ + u_short txb_len[8]; /* buffered xmit buffer lengths */ + u_char tx_page_start; /* first page of TX buffer area */ + u_char rec_page_start; /* first page of RX ring-buffer */ + u_char rec_page_stop; /* last page of RX ring-buffer */ + u_char next_packet; /* pointer to next unread RX packet */ +} ed_softc[NED]; + +int ed_attach(struct isa_device *); +void ed_init(int); +void edintr(int); +int ed_ioctl(struct ifnet *, int, caddr_t); +int ed_probe(struct isa_device *); +void ed_start(struct ifnet *); +void ed_reset(int, int); +void ed_watchdog(int); + +#ifdef MULTICAST +void ds_getmcaf(); + +#endif + +static void ed_get_packet(struct ed_softc *, char *, int /* u_short */ ); static void ed_stop(int); static inline void ed_rint(); static inline void ed_xmit(); static inline char *ed_ring_copy(); -void ed_pio_readmem(), ed_pio_writemem(); +void ed_pio_readmem(), ed_pio_writemem(); u_short ed_pio_write_mbufs(); extern int ether_output(); @@ -138,7 +144,8 @@ struct isa_driver eddriver = { ed_attach, "ed" }; -/* + +/* * Interrupt conversion table for WD/SMC ASIC * (IRQ* are defined in icu.h) */ @@ -152,7 +159,7 @@ static unsigned short ed_intr_mask[] = { IRQ15, IRQ4 }; - + /* * Interrupt conversion table for 585/790 Combo */ @@ -166,6 +173,7 @@ static unsigned short ed_790_intr_mask[] = { IRQ11, IRQ15 }; + #define ETHER_MIN_LEN 64 #define ETHER_MAX_LEN 1518 #define ETHER_ADDR_LEN 6 @@ -185,7 +193,7 @@ ed_probe(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; - int nports; + int nports; if (nports = ed_probe_WD80x3(isa_dev)) return (nports); @@ -196,7 +204,7 @@ ed_probe(isa_dev) if (nports = ed_probe_Novell(isa_dev)) return (nports); - return(0); + return (0); } /* @@ -219,7 +227,7 @@ ed_probe(isa_dev) * the others would require changing register pages (which would be * intrusive if this isn't an 8390). * - * Return 1 if 8390 was found, 0 if not. + * Return 1 if 8390 was found, 0 if not. */ int @@ -227,15 +235,15 @@ ed_probe_generic8390(sc) struct ed_softc *sc; { if ((inb(sc->nic_addr + ED_P0_CR) & - (ED_CR_RD2|ED_CR_TXP|ED_CR_STA|ED_CR_STP)) != - (ED_CR_RD2|ED_CR_STP)) + (ED_CR_RD2 | ED_CR_TXP | ED_CR_STA | ED_CR_STP)) != + (ED_CR_RD2 | ED_CR_STP)) return (0); if ((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) != ED_ISR_RST) return (0); - return(1); + return (1); } - + /* * Probe and vendor-specific initialization routine for SMC/WD80x3 boards */ @@ -244,9 +252,9 @@ ed_probe_WD80x3(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; - int i; - u_int memsize; - u_char iptr, isa16bit, sum; + int i; + u_int memsize; + u_char iptr, isa16bit, sum; sc->asic_addr = isa_dev->id_iobase; sc->nic_addr = sc->asic_addr + ED_WD_NIC_OFFSET; @@ -256,26 +264,27 @@ ed_probe_WD80x3(isa_dev) outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_POW); DELAY(10000); #endif + /* - * Attempt to do a checksum over the station address PROM. - * If it fails, it's probably not a SMC/WD board. There - * is a problem with this, though: some clone WD boards - * don't pass the checksum test. Danpex boards for one. + * Attempt to do a checksum over the station address PROM. If it + * fails, it's probably not a SMC/WD board. There is a problem with + * this, though: some clone WD boards don't pass the checksum test. + * Danpex boards for one. */ for (sum = 0, i = 0; i < 8; ++i) sum += inb(sc->asic_addr + ED_WD_PROM + i); if (sum != ED_WD_ROM_CHECKSUM_TOTAL) { + /* - * Checksum is invalid. This often happens with cheap - * WD8003E clones. In this case, the checksum byte - * (the eighth byte) seems to always be zero. + * Checksum is invalid. This often happens with cheap WD8003E + * clones. In this case, the checksum byte (the eighth byte) + * seems to always be zero. */ if (inb(sc->asic_addr + ED_WD_CARD_ID) != ED_TYPE_WD8003E || inb(sc->asic_addr + ED_WD_PROM + 7) != 0) - return(0); + return (0); } - /* reset card to force it into a known state. */ #ifdef TOSH_ETHER outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST | ED_WD_MSR_POW); @@ -318,7 +327,7 @@ ed_probe_WD80x3(isa_dev) memsize = 16384; isa16bit = 1; break; - case ED_TYPE_WD8013EP: /* also WD8003EP */ + case ED_TYPE_WD8013EP: /* also WD8003EP */ if (inb(sc->asic_addr + ED_WD_ICR) & ED_WD_ICR_16BIT) { isa16bit = 1; @@ -371,33 +380,35 @@ ed_probe_WD80x3(isa_dev) sc->type_str = ""; break; } + /* - * Make some adjustments to initial values depending on what is - * found in the ICR. + * Make some adjustments to initial values depending on what is found + * in the ICR. */ if (isa16bit && (sc->type != ED_TYPE_WD8013EBT) #ifdef TOSH_ETHER - && (sc->type != ED_TYPE_TOSHIBA1) && (sc->type != ED_TYPE_TOSHIBA4) + && (sc->type != ED_TYPE_TOSHIBA1) && (sc->type != ED_TYPE_TOSHIBA4) #endif && ((inb(sc->asic_addr + ED_WD_ICR) & ED_WD_ICR_16BIT) == 0)) { isa16bit = 0; memsize = 8192; } - #if ED_DEBUG printf("type = %x type_str=%s isa16bit=%d memsize=%d id_msize=%d\n", - sc->type,sc->type_str,isa16bit,memsize,isa_dev->id_msize); - for (i=0; i<8; i++) + sc->type, sc->type_str, isa16bit, memsize, isa_dev->id_msize); + for (i = 0; i < 8; i++) printf("%x -> %x\n", i, inb(sc->asic_addr + i)); #endif + /* * Allow the user to override the autoconfiguration */ if (isa_dev->id_msize) memsize = isa_dev->id_msize; + /* - * (note that if the user specifies both of the following flags - * that '8bit' mode intentionally has precedence) + * (note that if the user specifies both of the following flags that + * '8bit' mode intentionally has precedence) */ if (isa_dev->id_flags & ED_FLAGS_FORCE_16BIT_MODE) isa16bit = 1; @@ -406,60 +417,66 @@ ed_probe_WD80x3(isa_dev) /* * Check 83C584 interrupt configuration register if this board has one - * XXX - we could also check the IO address register. But why - * bother...if we get past this, it *has* to be correct. + * XXX - we could also check the IO address register. But why + * bother...if we get past this, it *has* to be correct. */ if ((sc->type & ED_WD_SOFTCONFIG) && (!sc->is790)) { + /* * Assemble together the encoded interrupt number. */ iptr = (inb(isa_dev->id_iobase + ED_WD_ICR) & ED_WD_ICR_IR2) | - ((inb(isa_dev->id_iobase + ED_WD_IRR) & - (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5); + ((inb(isa_dev->id_iobase + ED_WD_IRR) & + (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5); + /* - * Translate it using translation table, and check for correctness. + * Translate it using translation table, and check for + * correctness. */ if (ed_intr_mask[iptr] != isa_dev->id_irq) { printf("ed%d: kernel configured irq %d doesn't match board configured irq %d\n", - isa_dev->id_unit, ffs(isa_dev->id_irq) - 1, - ffs(ed_intr_mask[iptr]) - 1); - return(0); + isa_dev->id_unit, ffs(isa_dev->id_irq) - 1, + ffs(ed_intr_mask[iptr]) - 1); + return (0); } + /* * Enable the interrupt. */ outb(isa_dev->id_iobase + ED_WD_IRR, - inb(isa_dev->id_iobase + ED_WD_IRR) | ED_WD_IRR_IEN); + inb(isa_dev->id_iobase + ED_WD_IRR) | ED_WD_IRR_IEN); } if (sc->is790) { outb(isa_dev->id_iobase + ED_WD790_HWR, - inb(isa_dev->id_iobase + ED_WD790_HWR) | ED_WD790_HWR_SWH); + inb(isa_dev->id_iobase + ED_WD790_HWR) | ED_WD790_HWR_SWH); iptr = (((inb(isa_dev->id_iobase + ED_WD790_GCR) & ED_WD790_GCR_IR2) >> 4) | - (inb(isa_dev->id_iobase + ED_WD790_GCR) & - (ED_WD790_GCR_IR1|ED_WD790_GCR_IR0)) >> 2); + (inb(isa_dev->id_iobase + ED_WD790_GCR) & + (ED_WD790_GCR_IR1 | ED_WD790_GCR_IR0)) >> 2); outb(isa_dev->id_iobase + ED_WD790_HWR, - inb(isa_dev->id_iobase + ED_WD790_HWR) & ~ED_WD790_HWR_SWH); + inb(isa_dev->id_iobase + ED_WD790_HWR) & ~ED_WD790_HWR_SWH); if (ed_790_intr_mask[iptr] != isa_dev->id_irq) { printf("ed%d: kernel configured irq %d doesn't match board configured irq %d %d\n", - isa_dev->id_unit, ffs(isa_dev->id_irq) - 1, - ffs(ed_790_intr_mask[iptr]) - 1, iptr); + isa_dev->id_unit, ffs(isa_dev->id_irq) - 1, + ffs(ed_790_intr_mask[iptr]) - 1, iptr); return 0; } + /* * Enable interrupts. */ outb(isa_dev->id_iobase + ED_WD790_ICR, - inb(isa_dev->id_iobase + ED_WD790_ICR) | ED_WD790_ICR_EIL); + inb(isa_dev->id_iobase + ED_WD790_ICR) | ED_WD790_ICR_EIL); } - sc->isa16bit = isa16bit; -#ifdef notyet /* XXX - I'm not sure if PIO mode is even possible on WD/SMC boards */ +/* XXX - I'm not sure if PIO mode is even possible on WD/SMC boards */ +#ifdef notyet + /* * The following allows the WD/SMC boards to be used in Programmed I/O - * mode - without mapping the NIC memory shared. ...Not the prefered - * way, but it might be the only way. + * mode - without mapping the NIC memory shared. ...Not the prefered + * way, but it might be the only way. */ if (isa_dev->id_flags & ED_FLAGS_FORCE_PIO) { sc->mem_shared = 0; @@ -472,7 +489,7 @@ ed_probe_WD80x3(isa_dev) #endif isa_dev->id_msize = memsize; - sc->mem_start = (caddr_t)isa_dev->id_maddr; + sc->mem_start = (caddr_t) isa_dev->id_maddr; /* * allocate one xmit buffer if < 16k, two buffers otherwise @@ -498,27 +515,6 @@ ed_probe_WD80x3(isa_dev) sc->arpcom.ac_enaddr[i] = inb(sc->asic_addr + ED_WD_PROM + i); if (sc->mem_shared) { - /* - * Set address and enable interface shared memory. - */ - if(!sc->is790) { -#ifdef TOSH_ETHER - outb(sc->asic_addr + ED_WD_MSR + 1, ((kvtop(sc->mem_start) >> 8) & 0xe0) | 4); - outb(sc->asic_addr + ED_WD_MSR + 2, ((kvtop(sc->mem_start) >> 16) & 0x0f)); - outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB | ED_WD_MSR_POW); - -#else - outb(sc->asic_addr + ED_WD_MSR, ((kvtop(sc->mem_start) >> 13) & - ED_WD_MSR_ADDR) | ED_WD_MSR_MENB); -#endif - } else { - outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); - outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) | 0x80)); - outb(sc->asic_addr + 0x0b, ((kvtop(sc->mem_start) >> 13) & 0x0f) | - ((kvtop(sc->mem_start) >> 11) & 0x40) | - (inb(sc->asic_addr + 0x0b) & 0xb0)); - outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) & ~0x80)); - } /* * Set upper address bits and 8/16 bit access to shared memory @@ -530,57 +526,85 @@ ed_probe_WD80x3(isa_dev) (void) inb(0x84); } else { outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto = - ED_WD_LAAR_L16EN | ED_WD_LAAR_M16EN | - ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI))); + ED_WD_LAAR_L16EN | ED_WD_LAAR_M16EN | + ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI))); } - } else { + } else { if ((sc->type & ED_WD_SOFTCONFIG) || #ifdef TOSH_ETHER (sc->type == ED_TYPE_TOSHIBA1) || (sc->type == ED_TYPE_TOSHIBA4) || #endif (sc->type == ED_TYPE_WD8013EBT) && (!sc->is790)) { outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto = - ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI))); + ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI))); } } /* + * Set address and enable interface shared memory. + */ + if (!sc->is790) { +#ifdef TOSH_ETHER + outb(sc->asic_addr + ED_WD_MSR + 1, ((kvtop(sc->mem_start) >> 8) & 0xe0) | 4); + outb(sc->asic_addr + ED_WD_MSR + 2, ((kvtop(sc->mem_start) >> 16) & 0x0f)); + outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB | ED_WD_MSR_POW); + +#else + outb(sc->asic_addr + ED_WD_MSR, ((kvtop(sc->mem_start) >> 13) & + ED_WD_MSR_ADDR) | ED_WD_MSR_MENB); +#endif + } else { + outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); + outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) | 0x80)); + outb(sc->asic_addr + 0x0b, ((kvtop(sc->mem_start) >> 13) & 0x0f) | + ((kvtop(sc->mem_start) >> 11) & 0x40) | + (inb(sc->asic_addr + 0x0b) & 0xb0)); + outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) & ~0x80)); + } + + /* * Now zero memory and verify that it is clear */ bzero(sc->mem_start, memsize); for (i = 0; i < memsize; ++i) if (sc->mem_start[i]) { - printf("ed%d: failed to clear shared memory at %x - check configuration\n", - isa_dev->id_unit, kvtop(sc->mem_start + i)); + printf("ed%d: failed to clear shared memory at %x - check configuration\n", + isa_dev->id_unit, kvtop(sc->mem_start + i)); /* * Disable 16 bit access to shared memory */ if (isa16bit) { + if (sc->is790) { + outb(sc->asic_addr + ED_WD_MSR, 0x00); + (void) inb(0x84); + } outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &= - ~ED_WD_LAAR_M16EN)); + ~ED_WD_LAAR_M16EN)); (void) inb(0x84); } - - return(0); + return (0); } - + /* - * Disable 16bit access to shared memory - we leave it disabled so - * that 1) machines reboot properly when the board is set - * 16 bit mode and there are conflicting 8bit devices/ROMS - * in the same 128k address space as this boards shared - * memory. and 2) so that other 8 bit devices with shared - * memory can be used in this 128k region, too. + * Disable 16bit access to shared memory - we leave it + * disabled so that 1) machines reboot properly when the board + * is set 16 bit mode and there are conflicting 8bit + * devices/ROMS in the same 128k address space as this boards + * shared memory. and 2) so that other 8 bit devices with + * shared memory can be used in this 128k region, too. */ if (isa16bit) { + if (sc->is790) { + outb(sc->asic_addr + ED_WD_MSR, 0x00); + (void) inb(0x84); + } outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &= - ~ED_WD_LAAR_M16EN)); + ~ED_WD_LAAR_M16EN)); (void) inb(0x84); } } - return (ED_WD_IO_PORTS); } @@ -592,84 +616,84 @@ ed_probe_3Com(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; - int i; - u_int memsize; - u_char isa16bit, sum; + int i; + u_int memsize; + u_char isa16bit, sum; sc->asic_addr = isa_dev->id_iobase + ED_3COM_ASIC_OFFSET; sc->nic_addr = isa_dev->id_iobase + ED_3COM_NIC_OFFSET; /* * Verify that the kernel configured I/O address matches the board - * configured address + * configured address */ switch (inb(sc->asic_addr + ED_3COM_BCFR)) { case ED_3COM_BCFR_300: if (isa_dev->id_iobase != 0x300) - return(0); + return (0); break; case ED_3COM_BCFR_310: if (isa_dev->id_iobase != 0x310) - return(0); + return (0); break; case ED_3COM_BCFR_330: if (isa_dev->id_iobase != 0x330) - return(0); + return (0); break; case ED_3COM_BCFR_350: if (isa_dev->id_iobase != 0x350) - return(0); + return (0); break; case ED_3COM_BCFR_250: if (isa_dev->id_iobase != 0x250) - return(0); + return (0); break; case ED_3COM_BCFR_280: if (isa_dev->id_iobase != 0x280) - return(0); + return (0); break; case ED_3COM_BCFR_2A0: if (isa_dev->id_iobase != 0x2a0) - return(0); + return (0); break; case ED_3COM_BCFR_2E0: if (isa_dev->id_iobase != 0x2e0) - return(0); + return (0); break; default: - return(0); + return (0); } /* - * Verify that the kernel shared memory address matches the - * board configured address. + * Verify that the kernel shared memory address matches the board + * configured address. */ switch (inb(sc->asic_addr + ED_3COM_PCFR)) { case ED_3COM_PCFR_DC000: if (kvtop(isa_dev->id_maddr) != 0xdc000) - return(0); + return (0); break; case ED_3COM_PCFR_D8000: if (kvtop(isa_dev->id_maddr) != 0xd8000) - return(0); + return (0); break; case ED_3COM_PCFR_CC000: if (kvtop(isa_dev->id_maddr) != 0xcc000) - return(0); + return (0); break; case ED_3COM_PCFR_C8000: if (kvtop(isa_dev->id_maddr) != 0xc8000) - return(0); + return (0); break; default: - return(0); + return (0); } /* * Reset NIC and ASIC. Enable on-board transceiver throughout reset - * sequence because it'll lock up if the cable isn't connected - * if we don't. + * sequence because it'll lock up if the cable isn't connected if we + * don't. */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_RST | ED_3COM_CR_XSEL); @@ -677,10 +701,11 @@ ed_probe_3Com(isa_dev) * Wait for a while, then un-reset it */ DELAY(50); + /* * The 3Com ASIC defaults to rather strange settings for the CR after - * a reset - it's important to set it again after the following - * outb (this is done when we map the PROM below). + * a reset - it's important to set it again after the following outb + * (this is done when we map the PROM below). */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); @@ -695,17 +720,18 @@ ed_probe_3Com(isa_dev) sc->mem_shared = 1; /* - * Hmmm...a 16bit 3Com board has 16k of memory, but only an 8k - * window to it. + * Hmmm...a 16bit 3Com board has 16k of memory, but only an 8k window + * to it. */ memsize = 8192; /* * Get station address from on-board ROM */ + /* * First, map ethernet address PROM over the top of where the NIC - * registers normally appear. + * registers normally appear. */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_EALO | ED_3COM_CR_XSEL); @@ -714,9 +740,8 @@ ed_probe_3Com(isa_dev) /* * Unmap PROM - select NIC registers. The proper setting of the - * tranceiver is set in ed_init so that the attach code - * is given a chance to set the default based on a compile-time - * config option + * tranceiver is set in ed_init so that the attach code is given a + * chance to set the default based on a compile-time config option */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); @@ -727,18 +752,18 @@ ed_probe_3Com(isa_dev) /* * select page 0 registers */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP); /* - * Attempt to clear WTS bit. If it doesn't clear, then this is a - * 16bit board. + * Attempt to clear WTS bit. If it doesn't clear, then this is a 16bit + * board. */ outb(sc->nic_addr + ED_P0_DCR, 0); /* * select page 2 registers */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_2|ED_CR_RD2|ED_CR_STP); + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_2 | ED_CR_RD2 | ED_CR_STP); /* * The 3c503 forces the WTS bit to a one if this is a 16bit board @@ -751,24 +776,23 @@ ed_probe_3Com(isa_dev) /* * select page 0 registers */ - outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2|ED_CR_STP); + outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2 | ED_CR_STP); - sc->mem_start = (caddr_t)isa_dev->id_maddr; + sc->mem_start = (caddr_t) isa_dev->id_maddr; sc->mem_size = memsize; sc->mem_end = sc->mem_start + memsize; /* * We have an entire 8k window to put the transmit buffers on the - * 16bit boards. But since the 16bit 3c503's shared memory - * is only fast enough to overlap the loading of one full-size - * packet, trying to load more than 2 buffers can actually - * leave the transmitter idle during the load. So 2 seems - * the best value. (Although a mix of variable-sized packets - * might change this assumption. Nonetheless, we optimize for - * linear transfers of same-size packets.) + * 16bit boards. But since the 16bit 3c503's shared memory is only + * fast enough to overlap the loading of one full-size packet, trying + * to load more than 2 buffers can actually leave the transmitter idle + * during the load. So 2 seems the best value. (Although a mix of + * variable-sized packets might change this assumption. Nonetheless, + * we optimize for linear transfers of same-size packets.) */ if (isa16bit) { - if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING) + if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING) sc->txb_cnt = 1; else sc->txb_cnt = 2; @@ -776,22 +800,22 @@ ed_probe_3Com(isa_dev) sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_16BIT; sc->rec_page_start = ED_3COM_RX_PAGE_OFFSET_16BIT; sc->rec_page_stop = memsize / ED_PAGE_SIZE + - ED_3COM_RX_PAGE_OFFSET_16BIT; + ED_3COM_RX_PAGE_OFFSET_16BIT; sc->mem_ring = sc->mem_start; } else { sc->txb_cnt = 1; sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_8BIT; sc->rec_page_start = ED_TXBUF_SIZE + ED_3COM_TX_PAGE_OFFSET_8BIT; sc->rec_page_stop = memsize / ED_PAGE_SIZE + - ED_3COM_TX_PAGE_OFFSET_8BIT; + ED_3COM_TX_PAGE_OFFSET_8BIT; sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE); } sc->isa16bit = isa16bit; /* - * Initialize GA page start/stop registers. Probably only needed - * if doing DMA, but what the hell. + * Initialize GA page start/stop registers. Probably only needed if + * doing DMA, but what the hell. */ outb(sc->asic_addr + ED_3COM_PSTR, sc->rec_page_start); outb(sc->asic_addr + ED_3COM_PSPR, sc->rec_page_stop); @@ -814,21 +838,22 @@ ed_probe_3Com(isa_dev) break; default: printf("ed%d: Invalid irq configuration (%d) must be 2-5 for 3c503\n", - isa_dev->id_unit, ffs(isa_dev->id_irq) - 1); - return(0); + isa_dev->id_unit, ffs(isa_dev->id_irq) - 1); + return (0); } /* - * Initialize GA configuration register. Set bank and enable shared mem. + * Initialize GA configuration register. Set bank and enable shared + * mem. */ outb(sc->asic_addr + ED_3COM_GACFR, ED_3COM_GACFR_RSEL | - ED_3COM_GACFR_MBS0); + ED_3COM_GACFR_MBS0); /* - * Initialize "Vector Pointer" registers. These gawd-awful things - * are compared to 20 bits of the address on ISA, and if they - * match, the shared memory is disabled. We set them to - * 0xffff0...allegedly the reset vector. + * Initialize "Vector Pointer" registers. These gawd-awful things are + * compared to 20 bits of the address on ISA, and if they match, the + * shared memory is disabled. We set them to 0xffff0...allegedly the + * reset vector. */ outb(sc->asic_addr + ED_3COM_VPTR2, 0xff); outb(sc->asic_addr + ED_3COM_VPTR1, 0xff); @@ -841,13 +866,12 @@ ed_probe_3Com(isa_dev) for (i = 0; i < memsize; ++i) if (sc->mem_start[i]) { - printf("ed%d: failed to clear shared memory at %x - check configuration\n", - isa_dev->id_unit, kvtop(sc->mem_start + i)); - return(0); + printf("ed%d: failed to clear shared memory at %x - check configuration\n", + isa_dev->id_unit, kvtop(sc->mem_start + i)); + return (0); } - isa_dev->id_msize = memsize; - return(ED_3COM_IO_PORTS); + return (ED_3COM_IO_PORTS); } /* @@ -858,10 +882,10 @@ ed_probe_Novell(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; - u_int memsize, n; - u_char romdata[16], isa16bit = 0, tmp; + u_int memsize, n; + u_char romdata[16], isa16bit = 0, tmp; static char test_pattern[32] = "THIS is A memory TEST pattern"; - char test_buffer[32]; + char test_buffer[32]; sc->asic_addr = isa_dev->id_iobase + ED_NOVELL_ASIC_OFFSET; sc->nic_addr = isa_dev->id_iobase + ED_NOVELL_NIC_OFFSET; @@ -869,50 +893,54 @@ ed_probe_Novell(isa_dev) /* XXX - do Novell-specific probe here */ /* Reset the board */ +#ifdef GWETHER + outb(sc->asic_addr + ED_NOVELL_RESET, 0); + DELAY(200); +#endif /* GWETHER */ tmp = inb(sc->asic_addr + ED_NOVELL_RESET); /* * I don't know if this is necessary; probably cruft leftover from - * Clarkson packet driver code. Doesn't do a thing on the boards - * I've tested. -DG [note that a outb(0x84, 0) seems to work - * here, and is non-invasive...but some boards don't seem to reset - * and I don't have complete documentation on what the 'right' - * thing to do is...so we do the invasive thing for now. Yuck.] + * Clarkson packet driver code. Doesn't do a thing on the boards I've + * tested. -DG [note that a outb(0x84, 0) seems to work here, and is + * non-invasive...but some boards don't seem to reset and I don't have + * complete documentation on what the 'right' thing to do is...so we + * do the invasive thing for now. Yuck.] */ outb(sc->asic_addr + ED_NOVELL_RESET, tmp); DELAY(5000); /* * This is needed because some NE clones apparently don't reset the - * NIC properly (or the NIC chip doesn't reset fully on power-up) - * XXX - this makes the probe invasive! ...Done against my better - * judgement. -DLG + * NIC properly (or the NIC chip doesn't reset fully on power-up) XXX + * - this makes the probe invasive! ...Done against my better + * judgement. -DLG */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP); DELAY(5000); /* Make sure that we really have an 8390 based board */ if (!ed_probe_generic8390(sc)) - return(0); + return (0); sc->vendor = ED_VENDOR_NOVELL; sc->mem_shared = 0; isa_dev->id_maddr = 0; /* - * Test the ability to read and write to the NIC memory. This has - * the side affect of determining if this is an NE1000 or an NE2000. + * Test the ability to read and write to the NIC memory. This has the + * side affect of determining if this is an NE1000 or an NE2000. */ /* - * This prevents packets from being stored in the NIC memory when - * the readmem routine turns on the start bit in the CR. + * This prevents packets from being stored in the NIC memory when the + * readmem routine turns on the start bit in the CR. */ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON); /* Temporarily initialize DCR for byte operations */ - outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_LS); + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS); outb(sc->nic_addr + ED_P0_PSTART, 8192 / ED_PAGE_SIZE); outb(sc->nic_addr + ED_P0_PSTOP, 16384 / ED_PAGE_SIZE); @@ -921,8 +949,8 @@ ed_probe_Novell(isa_dev) /* * Write a test pattern in byte mode. If this fails, then there - * probably isn't any memory at 8k - which likely means - * that the board is an NE2000. + * probably isn't any memory at 8k - which likely means that the board + * is an NE2000. */ ed_pio_writemem(sc, test_pattern, 8192, sizeof(test_pattern)); ed_pio_readmem(sc, 8192, test_buffer, sizeof(test_pattern)); @@ -930,20 +958,21 @@ ed_probe_Novell(isa_dev) if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) { /* not an NE1000 - try NE2000 */ - outb(sc->nic_addr + ED_P0_DCR, ED_DCR_WTS|ED_DCR_FT1|ED_DCR_LS); + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_WTS | ED_DCR_FT1 | ED_DCR_LS); outb(sc->nic_addr + ED_P0_PSTART, 16384 / ED_PAGE_SIZE); outb(sc->nic_addr + ED_P0_PSTOP, 32768 / ED_PAGE_SIZE); sc->isa16bit = 1; + /* * Write a test pattern in word mode. If this also fails, then - * we don't know what this board is. + * we don't know what this board is. */ ed_pio_writemem(sc, test_pattern, 16384, sizeof(test_pattern)); ed_pio_readmem(sc, 16384, test_buffer, sizeof(test_pattern)); if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) - return(0); /* not an NE2000 either */ + return (0); /* not an NE2000 either */ sc->type = ED_TYPE_NE2000; sc->type_str = "NE2000"; @@ -951,11 +980,11 @@ ed_probe_Novell(isa_dev) sc->type = ED_TYPE_NE1000; sc->type_str = "NE1000"; } - + /* 8k of memory plus an additional 8k if 16bit */ memsize = 8192 + sc->isa16bit * 8192; -#if 0 /* probably not useful - NE boards only come two ways */ +#if 0 /* probably not useful - NE boards only come two ways */ /* allow kernel config file overrides */ if (isa_dev->id_msize) memsize = isa_dev->id_msize; @@ -969,9 +998,72 @@ ed_probe_Novell(isa_dev) sc->mem_end = sc->mem_start + memsize; sc->tx_page_start = memsize / ED_PAGE_SIZE; +#ifdef GWETHER + { + int x, i, mstart = 0, msize = 0; + char pbuf0[ED_PAGE_SIZE], pbuf[ED_PAGE_SIZE], tbuf[ED_PAGE_SIZE]; + + for (i = 0; i < ED_PAGE_SIZE; i++) + pbuf0[i] = 0; + + /* Clear all the memory. */ + for (x = 1; x < 256; x++) + ed_pio_writemem(sc, pbuf0, x * 256, ED_PAGE_SIZE); + + /* Search for the start of RAM. */ + for (x = 1; x < 256; x++) { + ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); + if (memcmp(pbuf0, tbuf, ED_PAGE_SIZE) == 0) { + for (i = 0; i < ED_PAGE_SIZE; i++) + pbuf[i] = 255 - x; + ed_pio_writemem(sc, pbuf, x * 256, ED_PAGE_SIZE); + ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); + if (memcmp(pbuf, tbuf, ED_PAGE_SIZE) == 0) { + mstart = x * ED_PAGE_SIZE; + msize = ED_PAGE_SIZE; + break; + } + } + } + + if (mstart == 0) { + printf("ed%d: Cannot find start of RAM.\n", isa_dev->id_unit); + return 0; + } + /* Search for the start of RAM. */ + for (x = (mstart / ED_PAGE_SIZE) + 1; x < 256; x++) { + ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); + if (memcmp(pbuf0, tbuf, ED_PAGE_SIZE) == 0) { + for (i = 0; i < ED_PAGE_SIZE; i++) + pbuf[i] = 255 - x; + ed_pio_writemem(sc, pbuf, x * 256, ED_PAGE_SIZE); + ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); + if (memcmp(pbuf, tbuf, ED_PAGE_SIZE) == 0) + msize += ED_PAGE_SIZE; + else { + break; + } + } else { + break; + } + } + + if (msize == 0) { + printf("ed%d: Cannot find any RAM, start : %d, x = %d.\n", isa_dev->id_unit, mstart, x); + return 0; + } + printf("ed%d: RAM start at %d, size : %d.\n", isa_dev->id_unit, mstart, msize); + + sc->mem_size = msize; + sc->mem_start = (char *) mstart; + sc->mem_end = (char *) (msize + mstart); + sc->tx_page_start = mstart / ED_PAGE_SIZE; + } +#endif /* GWETHER */ + /* * Use one xmit buffer if < 16k, two buffers otherwise (if not told - * otherwise). + * otherwise). */ if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)) sc->txb_cnt = 1; @@ -985,14 +1077,19 @@ ed_probe_Novell(isa_dev) ed_pio_readmem(sc, 0, romdata, 16); for (n = 0; n < ETHER_ADDR_LEN; n++) - sc->arpcom.ac_enaddr[n] = romdata[n*(sc->isa16bit+1)]; + sc->arpcom.ac_enaddr[n] = romdata[n * (sc->isa16bit + 1)]; + +#ifdef GWETHER + if (sc->arpcom.ac_enaddr[2] == 0x86) + sc->type_str = "Gateway AT"; +#endif /* GWETHER */ /* clear any pending interrupts that might have occurred above */ outb(sc->nic_addr + ED_P0_ISR, 0xff); - return(ED_NOVELL_IO_PORTS); + return (ED_NOVELL_IO_PORTS); } - + /* * Install interface into kernel networking data structures */ @@ -1004,7 +1101,7 @@ ed_attach(isa_dev) struct ifnet *ifp = &sc->arpcom.ac_if; struct ifaddr *ifa; struct sockaddr_dl *sdl; - + /* * Set interface to stopped condition (reset) */ @@ -1014,7 +1111,7 @@ ed_attach(isa_dev) * Initialize ifnet structure */ ifp->if_unit = isa_dev->id_unit; - ifp->if_name = "ed" ; + ifp->if_name = "ed"; ifp->if_mtu = ETHERMTU; ifp->if_init = ed_init; ifp->if_output = ether_output; @@ -1025,13 +1122,16 @@ ed_attach(isa_dev) /* * Set default state for ALTPHYS flag (used to disable the tranceiver - * for AUI operation), based on compile-time config option. + * for AUI operation), based on compile-time config option. */ if (isa_dev->id_flags & ED_FLAGS_DISABLE_TRANCEIVER) ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_ALTPHYS); else ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS); +#ifdef MULTICAST + ifp->if_flags |= IFF_MULTICAST; +#endif /* * Attach the interface @@ -1041,20 +1141,22 @@ ed_attach(isa_dev) /* * Search down the ifa address list looking for the AF_LINK type entry */ - ifa = ifp->if_addrlist; + ifa = ifp->if_addrlist; while ((ifa != 0) && (ifa->ifa_addr != 0) && - (ifa->ifa_addr->sa_family != AF_LINK)) + (ifa->ifa_addr->sa_family != AF_LINK)) ifa = ifa->ifa_next; + /* * If we find an AF_LINK type entry we fill in the hardware address. - * This is useful for netstat(1) to keep track of which interface - * is which. + * This is useful for netstat(1) to keep track of which interface is + * which. */ if ((ifa != 0) && (ifa->ifa_addr != 0)) { + /* * Fill in the link-level address for this interface */ - sdl = (struct sockaddr_dl *)ifa->ifa_addr; + sdl = (struct sockaddr_dl *) ifa->ifa_addr; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ETHER_ADDR_LEN; sdl->sdl_slen = 0; @@ -1065,17 +1167,17 @@ ed_attach(isa_dev) * Print additional info when attached */ printf("ed%d: address %s, ", isa_dev->id_unit, - ether_sprintf(sc->arpcom.ac_enaddr)); + ether_sprintf(sc->arpcom.ac_enaddr)); if (sc->type_str && (*sc->type_str != 0)) printf("type %s ", sc->type_str); else printf("type unknown (0x%x) ", sc->type); - printf("%s ",sc->isa16bit ? "(16 bit)" : "(8 bit)"); + printf("%s ", sc->isa16bit ? "(16 bit)" : "(8 bit)"); printf("%s\n", ((sc->vendor == ED_VENDOR_3COM) && - (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : ""); + (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : ""); /* * If BPF is in the kernel, call the attach for it @@ -1085,16 +1187,16 @@ ed_attach(isa_dev) #endif return 1; } - + /* * Reset interface. */ void ed_reset(unit, uban) - int unit; - int uban; /* XXX */ + int unit; + int uban; /* XXX */ { - int s; + int s; s = splimp(); @@ -1106,29 +1208,30 @@ ed_reset(unit, uban) (void) splx(s); } - + /* * Take interface offline. */ void ed_stop(unit) - int unit; + int unit; { struct ed_softc *sc = &ed_softc[unit]; - int n = 5000; - + int n = 5000; + /* * Stop everything on the interface, and select page 0 registers. */ if (sc->is790) { outb(sc->nic_addr + ED_P0_CR, ED_CR_STP); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP); } + /* - * Wait for interface to enter stopped state, but limit # of checks - * to 'n' (about 5ms). It shouldn't even take 5us on modern - * DS8390's, but just in case it's an old one. + * Wait for interface to enter stopped state, but limit # of checks to + * 'n' (about 5ms). It shouldn't even take 5us on modern DS8390's, but + * just in case it's an old one. */ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) == 0) && --n); @@ -1140,7 +1243,7 @@ ed_stop(unit) */ void ed_watchdog(unit) - int unit; + int unit; { struct ed_softc *sc = &ed_softc[unit]; @@ -1151,25 +1254,26 @@ ed_watchdog(unit) } /* - * Initialize device. + * Initialize device. */ void ed_init(unit) - int unit; + int unit; { struct ed_softc *sc = &ed_softc[unit]; struct ifnet *ifp = &sc->arpcom.ac_if; - int i, s; - u_char command; + int i, s; + u_char command; /* address not known */ - if (ifp->if_addrlist == (struct ifaddr *)0) return; + if (ifp->if_addrlist == (struct ifaddr *) 0) + return; /* * Initialize the NIC in the exact order outlined in the NS manual. - * This init procedure is "mandatory"...don't change what or when - * things happen. + * This init procedure is "mandatory"...don't change what or when + * things happen. */ s = splimp(); @@ -1190,19 +1294,21 @@ ed_init(unit) if (sc->is790) { outb(sc->nic_addr + ED_P0_CR, ED_CR_STP); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP); } if (sc->isa16bit) { + /* - * Set FIFO threshold to 8, No auto-init Remote DMA, - * byte order=80x86, word-wide DMA xfers, + * Set FIFO threshold to 8, No auto-init Remote DMA, byte + * order=80x86, word-wide DMA xfers, */ - outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_WTS|ED_DCR_LS); + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_WTS | ED_DCR_LS); } else { + /* * Same as above, but byte-wide DMA xfers */ - outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_LS); + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS); } /* @@ -1210,11 +1316,19 @@ ed_init(unit) */ outb(sc->nic_addr + ED_P0_RBCR0, 0); outb(sc->nic_addr + ED_P0_RBCR1, 0); +#ifndef MULTICAST /* * Enable reception of broadcast packets */ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB); +#else + + /* + * Tell RCR to do nothing for now. + */ + outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON); +#endif /* * Place NIC in internal loopback mode @@ -1238,41 +1352,62 @@ ed_init(unit) /* * Clear all interrupts. A '1' in each bit position clears the - * corresponding flag. + * corresponding flag. */ outb(sc->nic_addr + ED_P0_ISR, 0xff); /* * Enable the following interrupts: receive/transmit complete, - * receive/transmit error, and Receiver OverWrite. - * + * receive/transmit error, and Receiver OverWrite. + * * Counter overflow and Remote DMA complete are *not* enabled. */ outb(sc->nic_addr + ED_P0_IMR, - ED_IMR_PRXE|ED_IMR_PTXE|ED_IMR_RXEE|ED_IMR_TXEE|ED_IMR_OVWE); + ED_IMR_PRXE | ED_IMR_PTXE | ED_IMR_RXEE | ED_IMR_TXEE | ED_IMR_OVWE); /* * Program Command Register for page 1 */ if (sc->is790) { - outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STP); + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_STP); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STP); + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_RD2 | ED_CR_STP); } + /* * Copy out our station address */ for (i = 0; i < ETHER_ADDR_LEN; ++i) outb(sc->nic_addr + ED_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]); +#ifndef MULTICAST #if NBPFILTER > 0 + /* - * Initialize multicast address hashing registers to accept - * all multicasts (only used when in promiscuous mode) + * Initialize multicast address hashing registers to accept all + * multicasts (only used when in promiscuous mode) */ for (i = 0; i < 8; ++i) outb(sc->nic_addr + ED_P1_MAR0 + i, 0xff); #endif +#else + /* set up multicast addresses and filter modes */ + if (sc != 0 && (ifp->if_flags & IFF_MULTICAST) != 0) { + u_long mcaf[2]; + + if ((ifp->if_flags & IFF_ALLMULTI) != 0) { + mcaf[0] = 0xffffffff; + mcaf[1] = 0xffffffff; + } else + ds_getmcaf(sc, mcaf); + + /* + * Set multicast filter on chip. + */ + for (i = 0; i < 8; i++) + outb(sc->nic_addr + ED_P1_MAR0 + i, ((u_char *) mcaf)[i]); + } +#endif /* * Set Current Page pointer to next_packet (initialized above) @@ -1280,14 +1415,22 @@ ed_init(unit) outb(sc->nic_addr + ED_P1_CURR, sc->next_packet); /* - * Set Command Register for page 0, Remote DMA complete, - * and interface Start. + * Set Command Register for page 0, Remote DMA complete, and interface + * Start. */ if (sc->is790) { outb(sc->nic_addr + ED_P1_CR, ED_CR_STA); } else { - outb(sc->nic_addr + ED_P1_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P1_CR, ED_CR_RD2 | ED_CR_STA); } +#ifdef MULTICAST + + /* + * Clear all interrupts + */ + outb(sc->nic_addr + ED_P0_ISR, 0xff); +#endif + /* * Take interface out of loopback */ @@ -1295,7 +1438,7 @@ ed_init(unit) /* * If this is a 3Com board, the tranceiver must be software enabled - * (there is no settable hardware default). + * (there is no settable hardware default). */ if (sc->vendor == ED_VENDOR_3COM) { if (ifp->if_flags & IFF_ALTPHYS) { @@ -1304,6 +1447,25 @@ ed_init(unit) outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); } } +#ifdef MULTICAST + i = ED_RCR_AB; + if (sc != 0) { + if ((ifp->if_flags & IFF_PROMISC) != 0) { + + /* + * Set promiscuous mode. Also reconfigure the + * multicast filter. + */ + int j; + + i |= ED_RCR_PRO | ED_RCR_AM | ED_RCR_AR | ED_RCR_SEP; + for (j = 0; j < 8; j++) + outb(sc->nic_addr + ED_P1_MAR0 + j, 0xff); + } + i |= ED_RCR_AM; + } + outb(sc->nic_addr + ED_P0_RCR, i); +#endif /* * Set 'running' flag, and clear output active flag. @@ -1318,11 +1480,12 @@ ed_init(unit) (void) splx(s); } - + /* * This routine actually starts the transmission on the interface */ -static inline void ed_xmit(ifp) +static inline void +ed_xmit(ifp) struct ifnet *ifp; { struct ed_softc *sc = &ed_softc[ifp->if_unit]; @@ -1336,13 +1499,14 @@ static inline void ed_xmit(ifp) if (sc->is790) { outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); } + /* * Set TX buffer start page */ outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start + - sc->txb_next_tx * ED_TXBUF_SIZE); + sc->txb_next_tx * ED_TXBUF_SIZE); /* * Set TX length @@ -1356,10 +1520,10 @@ static inline void ed_xmit(ifp) if (sc->is790) { outb(sc->nic_addr + ED_P0_CR, ED_CR_TXP | ED_CR_STA); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_TXP|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_TXP | ED_CR_STA); } sc->xmit_busy = 1; - + /* * Point to next transmit buffer slot and wrap if necessary. */ @@ -1389,12 +1553,13 @@ ed_start(ifp) struct ed_softc *sc = &ed_softc[ifp->if_unit]; struct mbuf *m0, *m; caddr_t buffer; - int len; + int len; outloop: + /* - * First, see if there are buffered packets and an idle - * transmitter - should never happen at this point. + * First, see if there are buffered packets and an idle transmitter - + * should never happen at this point. */ if (sc->txb_inuse && (sc->xmit_busy == 0)) { printf("ed: packets buffers, but transmitter idle\n"); @@ -1405,23 +1570,23 @@ outloop: * See if there is room to put another packet in the buffer. */ if (sc->txb_inuse == sc->txb_cnt) { + /* - * No room. Indicate this to the outside world - * and exit. + * No room. Indicate this to the outside world and exit. */ ifp->if_flags |= IFF_OACTIVE; return; } - IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); if (m == 0) { - /* - * We are using the !OACTIVE flag to indicate to the outside - * world that we can accept an additional packet rather than - * that the transmitter is _actually_ active. Indeed, the - * transmitter may be active, but if we haven't filled all - * the buffers with data then we still want to accept more. - */ + + /* + * We are using the !OACTIVE flag to indicate to the outside + * world that we can accept an additional packet rather than + * that the transmitter is _actually_ active. Indeed, the + * transmitter may be active, but if we haven't filled all the + * buffers with data then we still want to accept more. + */ ifp->if_flags &= ~IFF_OACTIVE; return; } @@ -1436,45 +1601,49 @@ outloop: buffer = sc->mem_start + (sc->txb_new * ED_TXBUF_SIZE * ED_PAGE_SIZE); if (sc->mem_shared) { + /* * Special case setup for 16 bit boards... */ if (sc->isa16bit) { switch (sc->vendor) { - /* - * For 16bit 3Com boards (which have 16k of memory), - * we have the xmit buffers in a different page - * of memory ('page 0') - so change pages. - */ + + /* + * For 16bit 3Com boards (which have 16k of + * memory), we have the xmit buffers in a + * different page of memory ('page 0') - so + * change pages. + */ case ED_VENDOR_3COM: outb(sc->asic_addr + ED_3COM_GACFR, - ED_3COM_GACFR_RSEL); + ED_3COM_GACFR_RSEL); break; - /* - * Enable 16bit access to shared memory on WD/SMC boards - * Don't update wd_laar_proto because we want to restore the - * previous state (because an arp reply in the input code - * may cause a call-back to ed_start) - * XXX - the call-back to 'start' is a bug, IMHO. - */ - case ED_VENDOR_WD_SMC: { - outb(sc->asic_addr + ED_WD_LAAR, - (sc->wd_laar_proto | ED_WD_LAAR_M16EN)); - (void) inb(0x84); - if (sc->is790) { - outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); + + /* + * Enable 16bit access to shared memory on + * WD/SMC boards Don't update wd_laar_proto + * because we want to restore the previous + * state (because an arp reply in the input + * code may cause a call-back to ed_start) XXX + * - the call-back to 'start' is a bug, IMHO. + */ + case ED_VENDOR_WD_SMC:{ + outb(sc->asic_addr + ED_WD_LAAR, + (sc->wd_laar_proto | ED_WD_LAAR_M16EN)); (void) inb(0x84); + if (sc->is790) { + outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); + (void) inb(0x84); + } + (void) inb(0x84); + break; } - (void) inb(0x84); - break; - } } } - for (len = 0; m != 0; m = m->m_next) { bcopy(mtod(m, caddr_t), buffer, m->m_len); buffer += m->m_len; - len += m->m_len; + len += m->m_len; } /* @@ -1484,23 +1653,23 @@ outloop: switch (sc->vendor) { case ED_VENDOR_3COM: outb(sc->asic_addr + ED_3COM_GACFR, - ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0); + ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0); break; - case ED_VENDOR_WD_SMC: { - outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto); - (void) inb(0x84); - if (sc->is790) { - outb(sc->asic_addr + ED_WD_MSR, 0x00); + case ED_VENDOR_WD_SMC:{ + if (sc->is790) { + outb(sc->asic_addr + ED_WD_MSR, 0x00); + (void) inb(0x84); + } + outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto); (void) inb(0x84); + break; } - break; - } } } } else { len = ed_pio_write_mbufs(sc, m, buffer); } - + sc->txb_len[sc->txb_new] = MAX(len, ETHER_MIN_LEN); sc->txb_inuse++; @@ -1514,40 +1683,37 @@ outloop: if (sc->xmit_busy == 0) ed_xmit(ifp); + /* - * If there is BPF support in the configuration, tap off here. - * The following has support for converting trailer packets - * back to normal. - * XXX - support for trailer packets in BPF should be moved into - * the bpf code proper to avoid code duplication in all of - * the drivers. + * If there is BPF support in the configuration, tap off here. The + * following has support for converting trailer packets back to + * normal. XXX - support for trailer packets in BPF should be moved + * into the bpf code proper to avoid code duplication in all of the + * drivers. */ #if NBPFILTER > 0 if (sc->bpf) { u_short etype; - int off, datasize, resid; + int off, datasize, resid; struct ether_header *eh; struct trailer_header trailer_header; - char ether_packet[ETHER_MAX_LEN]; - char *ep; + char ether_packet[ETHER_MAX_LEN]; + char *ep; ep = ether_packet; /* - * We handle trailers below: - * Copy ether header first, then residual data, - * then data. Put all this in a temporary buffer - * 'ether_packet' and send off to bpf. Since the - * system has generated this packet, we assume - * that all of the offsets in the packet are - * correct; if they're not, the system will almost - * certainly crash in m_copydata. - * We make no assumptions about how the data is - * arranged in the mbuf chain (i.e. how much - * data is in each mbuf, if mbuf clusters are - * used, etc.), which is why we use m_copydata - * to get the ether header rather than assume - * that this is located in the first mbuf. + * We handle trailers below: Copy ether header first, then + * residual data, then data. Put all this in a temporary + * buffer 'ether_packet' and send off to bpf. Since the system + * has generated this packet, we assume that all of the + * offsets in the packet are correct; if they're not, the + * system will almost certainly crash in m_copydata. We make + * no assumptions about how the data is arranged in the mbuf + * chain (i.e. how much data is in each mbuf, if mbuf clusters + * are used, etc.), which is why we use m_copydata to get the + * ether header rather than assume that this is located in the + * first mbuf. */ /* copy ether header */ m_copydata(m0, 0, sizeof(struct ether_header), ep); @@ -1555,17 +1721,17 @@ outloop: ep += sizeof(struct ether_header); etype = ntohs(eh->ether_type); if (etype >= ETHERTYPE_TRAIL && - etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) { datasize = ((etype - ETHERTYPE_TRAIL) << 9); off = datasize + sizeof(struct ether_header); /* copy trailer_header into a data structure */ m_copydata(m0, off, sizeof(struct trailer_header), - (caddr_t)&trailer_header.ether_type); + (caddr_t) & trailer_header.ether_type); /* copy residual data */ - m_copydata(m0, off+sizeof(struct trailer_header), - resid = ntohs(trailer_header.ether_residual) - + m_copydata(m0, off + sizeof(struct trailer_header), + resid = ntohs(trailer_header.ether_residual) - sizeof(struct trailer_header), ep); ep += resid; @@ -1590,35 +1756,36 @@ outloop: */ goto outloop; } - + /* * Ethernet interface receiver interrupt. */ static inline void ed_rint(unit) - int unit; + int unit; { register struct ed_softc *sc = &ed_softc[unit]; - u_char boundry, current; + u_char boundry, current; u_short len; struct ed_ring packet_hdr; - char *packet_ptr; + char *packet_ptr; /* * Set NIC to page 1 registers to get 'current' pointer */ if (sc->is790) { - outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_STA); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_RD2 | ED_CR_STA); } + /* - * 'sc->next_packet' is the logical beginning of the ring-buffer - i.e. - * it points to where new data has been buffered. The 'CURR' - * (current) register points to the logical end of the ring-buffer - * - i.e. it points to where additional new data will be added. - * We loop here until the logical beginning equals the logical - * end (or in other words, until the ring-buffer is empty). + * 'sc->next_packet' is the logical beginning of the ring-buffer - + * i.e. it points to where new data has been buffered. The 'CURR' + * (current) register points to the logical end of the ring-buffer - + * i.e. it points to where additional new data will be added. We loop + * here until the logical beginning equals the logical end (or in + * other words, until the ring-buffer is empty). */ while (sc->next_packet != inb(sc->nic_addr + ED_P1_CURR)) { @@ -1628,25 +1795,28 @@ ed_rint(unit) /* * The byte count includes the FCS - Frame Check Sequence (a - * 32 bit CRC). + * 32 bit CRC). */ if (sc->mem_shared) - packet_hdr = *(struct ed_ring *)packet_ptr; + packet_hdr = *(struct ed_ring *) packet_ptr; else ed_pio_readmem(sc, packet_ptr, (char *) &packet_hdr, - sizeof(packet_hdr)); + sizeof(packet_hdr)); len = packet_hdr.count; if ((len >= ETHER_MIN_LEN) && (len <= ETHER_MAX_LEN)) { + /* * Go get packet. len - 4 removes CRC from length. */ ed_get_packet(sc, packet_ptr + 4, len - 4); ++sc->arpcom.ac_if.if_ipackets; } else { + /* - * Really BAD...probably indicates that the ring pointers - * are corrupted. Also seen on early rev chips under - * high load - the byte order of the length gets switched. + * Really BAD...probably indicates that the ring + * pointers are corrupted. Also seen on early rev + * chips under high load - the byte order of the + * length gets switched. */ log(LOG_ERR, "ed%d: NIC memory corrupt - invalid packet length %d\n", @@ -1662,8 +1832,8 @@ ed_rint(unit) sc->next_packet = packet_hdr.next_packet; /* - * Update NIC boundry pointer - being careful to keep it - * one buffer behind. (as recommended by NS databook) + * Update NIC boundry pointer - being careful to keep it one + * buffer behind. (as recommended by NS databook) */ boundry = sc->next_packet - 1; if (boundry < sc->rec_page_start) @@ -1675,18 +1845,18 @@ ed_rint(unit) if (sc->is790) { outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); } outb(sc->nic_addr + ED_P0_BNRY, boundry); /* - * Set NIC to page 1 registers before looping to top (prepare to - * get 'CURR' current pointer) + * Set NIC to page 1 registers before looping to top (prepare + * to get 'CURR' current pointer) */ if (sc->is790) { - outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_STA); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_RD2 | ED_CR_STA); } } } @@ -1696,10 +1866,10 @@ ed_rint(unit) */ void edintr(unit) - int unit; + int unit; { struct ed_softc *sc = &ed_softc[unit]; - u_char isr; + u_char isr; /* * Set NIC to page 0 registers @@ -1707,36 +1877,36 @@ edintr(unit) if (sc->is790) { outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); } + /* * loop until there are no more new interrupts */ while (isr = inb(sc->nic_addr + ED_P0_ISR)) { /* - * reset all the bits that we are 'acknowledging' - * by writing a '1' to each bit position that was set - * (writing a '1' *clears* the bit) + * reset all the bits that we are 'acknowledging' by writing a + * '1' to each bit position that was set (writing a '1' + * *clears* the bit) */ outb(sc->nic_addr + ED_P0_ISR, isr); /* - * Handle transmitter interrupts. Handle these first - * because the receiver will reset the board under - * some conditions. + * Handle transmitter interrupts. Handle these first because + * the receiver will reset the board under some conditions. */ - if (isr & (ED_ISR_PTX|ED_ISR_TXE)) { - u_char collisions = inb(sc->nic_addr + ED_P0_NCR) & 0x0f; + if (isr & (ED_ISR_PTX | ED_ISR_TXE)) { + u_char collisions = inb(sc->nic_addr + ED_P0_NCR) & 0x0f; /* * Check for transmit error. If a TX completed with an * error, we end up throwing the packet away. Really * the only error that is possible is excessive - * collisions, and in this case it is best to allow the - * automatic mechanisms of TCP to backoff the flow. Of - * course, with UDP we're screwed, but this is expected - * when a network is heavily loaded. + * collisions, and in this case it is best to allow + * the automatic mechanisms of TCP to backoff the + * flow. Of course, with UDP we're screwed, but this + * is expected when a network is heavily loaded. */ (void) inb(sc->nic_addr + ED_P0_TSR); if (isr & ED_ISR_TXE) { @@ -1746,8 +1916,9 @@ edintr(unit) */ if ((inb(sc->nic_addr + ED_P0_TSR) & ED_TSR_ABT) && (collisions == 0)) { + /* - * When collisions total 16, the + * When collisions total 16, the * P0_NCR will indicate 0, and the * TSR_ABT is set. */ @@ -1759,9 +1930,10 @@ edintr(unit) */ ++sc->arpcom.ac_if.if_oerrors; } else { + /* * Update total number of successfully - * transmitted packets. + * transmitted packets. */ ++sc->arpcom.ac_if.if_opackets; } @@ -1779,16 +1951,16 @@ edintr(unit) /* * Add in total number of collisions on last - * transmission. + * transmission. */ sc->arpcom.ac_if.if_collisions += collisions; /* * Decrement buffer in-use count if not zero (can only - * be zero if a transmitter interrupt occured while - * not actually transmitting). - * If data is ready to transmit, start it transmitting, - * otherwise defer until after handling receiver + * be zero if a transmitter interrupt occured while + * not actually transmitting). If data is ready to + * transmit, start it transmitting, otherwise defer + * until after handling receiver */ if (sc->txb_inuse && --sc->txb_inuse) ed_xmit(&sc->arpcom.ac_if); @@ -1797,110 +1969,111 @@ edintr(unit) /* * Handle receiver interrupts */ - if (isr & (ED_ISR_PRX|ED_ISR_RXE|ED_ISR_OVW)) { - /* - * Overwrite warning. In order to make sure that a lockup - * of the local DMA hasn't occurred, we reset and - * re-init the NIC. The NSC manual suggests only a - * partial reset/re-init is necessary - but some - * chips seem to want more. The DMA lockup has been - * seen only with early rev chips - Methinks this - * bug was fixed in later revs. -DG - */ + if (isr & (ED_ISR_PRX | ED_ISR_RXE | ED_ISR_OVW)) { + + /* + * Overwrite warning. In order to make sure that a + * lockup of the local DMA hasn't occurred, we reset + * and re-init the NIC. The NSC manual suggests only a + * partial reset/re-init is necessary - but some chips + * seem to want more. The DMA lockup has been seen + * only with early rev chips - Methinks this bug was + * fixed in later revs. -DG + */ if (isr & ED_ISR_OVW) { ++sc->arpcom.ac_if.if_ierrors; #ifdef DIAGNOSTIC log(LOG_WARNING, - "ed%d: warning - receiver ring buffer overrun\n", - unit); + "ed%d: warning - receiver ring buffer overrun\n", + unit); #endif + /* * Stop/reset/re-init NIC */ ed_reset(unit, 0); } else { - /* - * Receiver Error. One or more of: CRC error, frame - * alignment error FIFO overrun, or missed packet. - */ + /* + * Receiver Error. One or more of: CRC error, + * frame alignment error FIFO overrun, or + * missed packet. + */ if (isr & ED_ISR_RXE) { ++sc->arpcom.ac_if.if_ierrors; #ifdef ED_DEBUG printf("ed%d: receive error %x\n", unit, - inb(sc->nic_addr + ED_P0_RSR)); + inb(sc->nic_addr + ED_P0_RSR)); #endif } /* - * Go get the packet(s) - * XXX - Doing this on an error is dubious - * because there shouldn't be any data to - * get (we've configured the interface to - * not accept packets with errors). + * Go get the packet(s) XXX - Doing this on an + * error is dubious because there shouldn't be + * any data to get (we've configured the + * interface to not accept packets with + * errors). */ /* * Enable 16bit access to shared memory first - * on WD/SMC boards. + * on WD/SMC boards. */ if (sc->isa16bit && (sc->vendor == ED_VENDOR_WD_SMC)) { outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto |= - ED_WD_LAAR_M16EN)); + ED_WD_LAAR_M16EN)); (void) inb(0x84); if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, - ED_WD_MSR_MENB); + ED_WD_MSR_MENB); (void) inb(0x84); } } - - ed_rint (unit); + ed_rint(unit); /* disable 16bit access */ if (sc->isa16bit && - (sc->vendor == ED_VENDOR_WD_SMC)) { + (sc->vendor == ED_VENDOR_WD_SMC)) { - outb(sc->asic_addr + ED_WD_LAAR, - (sc->wd_laar_proto &= - ~ED_WD_LAAR_M16EN)); - (void) inb(0x84); if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, 0x00); (void) inb(0x84); } + outb(sc->asic_addr + ED_WD_LAAR, + (sc->wd_laar_proto &= + ~ED_WD_LAAR_M16EN)); + (void) inb(0x84); } } } /* * If it looks like the transmitter can take more data, - * attempt to start output on the interface. - * This is done after handling the receiver to - * give the receiver priority. + * attempt to start output on the interface. This is done + * after handling the receiver to give the receiver priority. */ if ((sc->arpcom.ac_if.if_flags & IFF_OACTIVE) == 0) ed_start(&sc->arpcom.ac_if); /* - * return NIC CR to standard state: page 0, remote DMA complete, - * start (toggling the TXP bit off, even if was just set - * in the transmit routine, is *okay* - it is 'edge' - * triggered from low to high) + * return NIC CR to standard state: page 0, remote DMA + * complete, start (toggling the TXP bit off, even if was just + * set in the transmit routine, is *okay* - it is 'edge' + * triggered from low to high) */ if (sc->is790) { outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); } + /* - * If the Network Talley Counters overflow, read them to - * reset them. It appears that old 8390's won't - * clear the ISR flag otherwise - resulting in an - * infinite loop. + * If the Network Talley Counters overflow, read them to reset + * them. It appears that old 8390's won't clear the ISR flag + * otherwise - resulting in an infinite loop. */ if (isr & ED_ISR_CNT) { (void) inb(sc->nic_addr + ED_P0_CNTR0); @@ -1909,7 +2082,7 @@ edintr(unit) } } } - + /* * Process an ioctl request. This code needs some work - it looks * pretty ugly. @@ -1917,13 +2090,13 @@ edintr(unit) int ed_ioctl(ifp, command, data) register struct ifnet *ifp; - int command; + int command; caddr_t data; { - register struct ifaddr *ifa = (struct ifaddr *)data; + register struct ifaddr *ifa = (struct ifaddr *) data; struct ed_softc *sc = &ed_softc[ifp->if_unit]; - struct ifreq *ifr = (struct ifreq *)data; - int s, error = 0; + struct ifreq *ifr = (struct ifreq *) data; + int s, error = 0; s = splimp(); @@ -1936,41 +2109,40 @@ ed_ioctl(ifp, command, data) #ifdef INET case AF_INET: ed_init(ifp->if_unit); /* before arpwhohas */ + /* - * See if another station has *our* IP address. - * i.e.: There is an address conflict! If a - * conflict exists, a message is sent to the - * console. + * See if another station has *our* IP address. i.e.: + * There is an address conflict! If a conflict exists, + * a message is sent to the console. */ - ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; - arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); + ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *) ifp, &IA_SIN(ifa)->sin_addr); break; #endif #ifdef NS - /* - * XXX - This code is probably wrong - */ + + /* + * XXX - This code is probably wrong + */ case AF_NS: - { - register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); - - if (ns_nullhost(*ina)) - ina->x_host = - *(union ns_host *)(sc->arpcom.ac_enaddr); - else { - /* - * + { + register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + + if (ns_nullhost(*ina)) + ina->x_host = + *(union ns_host *) (sc->arpcom.ac_enaddr); + else { + bcopy((caddr_t) ina->x_host.c_host, + (caddr_t) sc->arpcom.ac_enaddr, + sizeof(sc->arpcom.ac_enaddr)); + } + + /* + * Set new address */ - bcopy((caddr_t)ina->x_host.c_host, - (caddr_t)sc->arpcom.ac_enaddr, - sizeof(sc->arpcom.ac_enaddr)); + ed_init(ifp->if_unit); + break; } - /* - * Set new address - */ - ed_init(ifp->if_unit); - break; - } #endif default: ed_init(ifp->if_unit); @@ -1981,13 +2153,15 @@ ed_ioctl(ifp, command, data) case SIOCGIFADDR: { struct sockaddr *sa; - sa = (struct sockaddr *)&ifr->ifr_data; - bcopy((caddr_t)sc->arpcom.ac_enaddr, - (caddr_t) sa->sa_data, ETHER_ADDR_LEN); + + sa = (struct sockaddr *) & ifr->ifr_data; + bcopy((caddr_t) sc->arpcom.ac_enaddr, + (caddr_t) sa->sa_data, ETHER_ADDR_LEN); } break; case SIOCSIFFLAGS: + /* * If interface is marked down and it is running, then stop it */ @@ -1996,37 +2170,42 @@ ed_ioctl(ifp, command, data) ed_stop(ifp->if_unit); ifp->if_flags &= ~IFF_RUNNING; } else { - /* - * If interface is marked up and it is stopped, then start it - */ + + /* + * If interface is marked up and it is stopped, then + * start it + */ if ((ifp->if_flags & IFF_UP) && - ((ifp->if_flags & IFF_RUNNING) == 0)) + ((ifp->if_flags & IFF_RUNNING) == 0)) ed_init(ifp->if_unit); } +#ifndef MULTICAST #if NBPFILTER > 0 if (ifp->if_flags & IFF_PROMISC) { + /* - * Set promiscuous mode on interface. - * XXX - for multicasts to work, we would need to - * write 1's in all bits of multicast - * hashing array. For now we assume that - * this was done in ed_init(). + * Set promiscuous mode on interface. XXX - for + * multicasts to work, we would need to write 1's in + * all bits of multicast hashing array. For now we + * assume that this was done in ed_init(). */ outb(sc->nic_addr + ED_P0_RCR, - ED_RCR_PRO|ED_RCR_AM|ED_RCR_AB); + ED_RCR_PRO | ED_RCR_AM | ED_RCR_AB); } else { + /* * XXX - for multicasts to work, we would need to - * rewrite the multicast hashing array with the - * proper hash (would have been destroyed above). + * rewrite the multicast hashing array with the proper + * hash (would have been destroyed above). */ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB); } #endif + /* - * An unfortunate hack to provide the (required) software control - * of the tranceiver for 3Com boards. The ALTPHYS flag disables - * the tranceiver if set. + * An unfortunate hack to provide the (required) software + * control of the tranceiver for 3Com boards. The ALTPHYS flag + * disables the tranceiver if set. */ if (sc->vendor == ED_VENDOR_3COM) { if (ifp->if_flags & IFF_ALTPHYS) { @@ -2035,16 +2214,37 @@ ed_ioctl(ifp, command, data) outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); } } - break; +#else + case SIOCADDMULTI: + case SIOCDELMULTI: + + /* + * Update out multicast list. + */ + error = (command == SIOCADDMULTI) ? + ether_addmulti((struct ifreq *) data, &sc->arpcom) : + ether_delmulti((struct ifreq *) data, &sc->arpcom); + if (error == ENETRESET) { + + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + ed_stop(ifp->if_unit); /* XXX for ds_setmcaf? */ + ed_init(ifp->if_unit); + error = 0; + } + break; +#endif default: error = EINVAL; } (void) splx(s); return (error); } - + /* * Macro to calculate a new address within shared memory when given an offset * from an address, taking into account ring-wrap. @@ -2062,13 +2262,13 @@ ed_ioctl(ifp, command, data) static void ed_get_packet(sc, buf, len) struct ed_softc *sc; - char *buf; + char *buf; u_short len; { struct ether_header *eh; - struct mbuf *m, *head = 0, *ed_ring_to_mbuf(); + struct mbuf *m, *head = 0, *ed_ring_to_mbuf(); u_short off; - int resid; + int resid; u_short etype; struct trailer_header trailer_header; @@ -2086,8 +2286,8 @@ ed_get_packet(sc, buf, len) #define EOFF (EROUND - sizeof(struct ether_header)) /* - * The following assumes there is room for - * the ether header in the header mbuf + * The following assumes there is room for the ether header in the + * header mbuf */ head->m_data += EOFF; eh = mtod(head, struct ether_header *); @@ -2101,18 +2301,16 @@ ed_get_packet(sc, buf, len) head->m_len += sizeof(struct ether_header); len -= sizeof(struct ether_header); - etype = ntohs((u_short)eh->ether_type); + etype = ntohs((u_short) eh->ether_type); /* - * Deal with trailer protocol: - * If trailer protocol, calculate the datasize as 'off', - * which is also the offset to the trailer header. - * Set resid to the amount of packet data following the - * trailer header. - * Finally, copy residual data into mbuf chain. + * Deal with trailer protocol: If trailer protocol, calculate the + * datasize as 'off', which is also the offset to the trailer header. + * Set resid to the amount of packet data following the trailer + * header. Finally, copy residual data into mbuf chain. */ if (etype >= ETHERTYPE_TRAIL && - etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) { off = (etype - ETHERTYPE_TRAIL) << 9; if ((off + sizeof(struct trailer_header)) > len) @@ -2120,14 +2318,15 @@ ed_get_packet(sc, buf, len) /* * If we have shared memory, we can get info directly from the - * stored packet, otherwise we must get a local copy - * of the trailer header using PIO. + * stored packet, otherwise we must get a local copy of the + * trailer header using PIO. */ if (sc->mem_shared) { eh->ether_type = *ringoffset(sc, buf, off, u_short *); - resid = ntohs(*ringoffset(sc, buf, off+2, u_short *)); + resid = ntohs(*ringoffset(sc, buf, off + 2, u_short *)); } else { struct trailer_header trailer_header; + ed_pio_readmem(sc, ringoffset(sc, buf, off, caddr_t), (char *) &trailer_header, @@ -2136,30 +2335,35 @@ ed_get_packet(sc, buf, len) resid = trailer_header.ether_residual; } - if ((off + resid) > len) goto bad; /* insanity */ + if ((off + resid) > len) + goto bad; /* insanity */ resid -= sizeof(struct trailer_header); - if (resid < 0) goto bad; /* insanity */ + if (resid < 0) + goto bad; /* insanity */ - m = ed_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *), + m = ed_ring_to_mbuf(sc, ringoffset(sc, buf, off + 4, char *), head, resid); - if (m == 0) goto bad; + if (m == 0) + goto bad; len = off; - head->m_pkthdr.len -= 4; /* subtract trailer header */ + head->m_pkthdr.len -= 4; /* subtract trailer header */ } /* - * Pull packet off interface. Or if this was a trailer packet, - * the data portion is appended. + * Pull packet off interface. Or if this was a trailer packet, the + * data portion is appended. */ m = ed_ring_to_mbuf(sc, buf, m, len); - if (m == 0) goto bad; + if (m == 0) + goto bad; #if NBPFILTER > 0 + /* - * Check if there's a BPF listener on this interface. - * If so, hand off the raw packet to bpf. + * Check if there's a BPF listener on this interface. If so, hand off + * the raw packet to bpf. */ if (sc->bpf) { bpf_mtap(sc->bpf, head); @@ -2168,17 +2372,17 @@ ed_get_packet(sc, buf, len) * Note that the interface cannot be in promiscuous mode if * there are no BPF listeners. And if we are in promiscuous * mode, we have to check if this packet is really ours. - * + * * XXX This test does not support multicasts. */ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, - sizeof(eh->ether_dhost)) != 0 && + sizeof(eh->ether_dhost)) != 0 && bcmp(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost)) != 0) { - m_freem(head); - return; + m_freem(head); + return; } } #endif @@ -2213,8 +2417,8 @@ bad: if (head) * This routine is currently Novell-specific. */ void -ed_pio_readmem(sc,src,dst,amount) - struct ed_softc *sc; +ed_pio_readmem(sc, src, dst, amount) + struct ed_softc *sc; unsigned short src; unsigned char *dst; unsigned short amount; @@ -2222,24 +2426,25 @@ ed_pio_readmem(sc,src,dst,amount) unsigned short tmp_amount; /* select page 0 registers */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); /* round up to a word */ tmp_amount = amount; - if (amount & 1) ++amount; + if (amount & 1) + ++amount; /* set up DMA byte count */ outb(sc->nic_addr + ED_P0_RBCR0, amount); - outb(sc->nic_addr + ED_P0_RBCR1, amount>>8); + outb(sc->nic_addr + ED_P0_RBCR1, amount >> 8); /* set up source address in NIC mem */ outb(sc->nic_addr + ED_P0_RSAR0, src); - outb(sc->nic_addr + ED_P0_RSAR1, src>>8); + outb(sc->nic_addr + ED_P0_RSAR1, src >> 8); outb(sc->nic_addr + ED_P0_CR, ED_CR_RD0 | ED_CR_STA); if (sc->isa16bit) { - insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount/2); + insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount / 2); } else insb(sc->asic_addr + ED_NOVELL_DATA, dst, amount); @@ -2251,41 +2456,42 @@ ed_pio_readmem(sc,src,dst,amount) * be even. */ void -ed_pio_writemem(sc,src,dst,len) +ed_pio_writemem(sc, src, dst, len) struct ed_softc *sc; - char *src; + char *src; unsigned short dst; unsigned short len; { - int maxwait=100; /* about 120us */ + int maxwait = 100; /* about 120us */ /* select page 0 registers */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); /* reset remote DMA complete flag */ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); /* set up DMA byte count */ outb(sc->nic_addr + ED_P0_RBCR0, len); - outb(sc->nic_addr + ED_P0_RBCR1, len>>8); + outb(sc->nic_addr + ED_P0_RBCR1, len >> 8); /* set up destination address in NIC mem */ outb(sc->nic_addr + ED_P0_RSAR0, dst); - outb(sc->nic_addr + ED_P0_RSAR1, dst>>8); + outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8); /* set remote DMA write */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); if (sc->isa16bit) - outsw(sc->asic_addr + ED_NOVELL_DATA, src, len/2); + outsw(sc->asic_addr + ED_NOVELL_DATA, src, len / 2); else outsb(sc->asic_addr + ED_NOVELL_DATA, src, len); + /* * Wait for remote DMA complete. This is necessary because on the - * transmit side, data is handled internally by the NIC in bursts - * and we can't start another remote DMA until this one completes. - * Not waiting causes really bad things to happen - like the NIC - * irrecoverably jamming the ISA bus. + * transmit side, data is handled internally by the NIC in bursts and + * we can't start another remote DMA until this one completes. Not + * waiting causes really bad things to happen - like the NIC + * irrecoverably jamming the ISA bus. */ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); } @@ -2295,7 +2501,7 @@ ed_pio_writemem(sc,src,dst,len) * programmed I/O. */ u_short -ed_pio_write_mbufs(sc,m,dst) +ed_pio_write_mbufs(sc, m, dst) struct ed_softc *sc; struct mbuf *m; unsigned short dst; @@ -2303,40 +2509,40 @@ ed_pio_write_mbufs(sc,m,dst) unsigned short len, mb_offset; struct mbuf *mp; unsigned char residual[2]; - int maxwait=100; /* about 120us */ + int maxwait = 100; /* about 120us */ /* First, count up the total number of bytes to copy */ for (len = 0, mp = m; mp; mp = mp->m_next) len += mp->m_len; - + /* select page 0 registers */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); /* reset remote DMA complete flag */ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); /* set up DMA byte count */ outb(sc->nic_addr + ED_P0_RBCR0, len); - outb(sc->nic_addr + ED_P0_RBCR1, len>>8); + outb(sc->nic_addr + ED_P0_RBCR1, len >> 8); /* set up destination address in NIC mem */ outb(sc->nic_addr + ED_P0_RSAR0, dst); - outb(sc->nic_addr + ED_P0_RSAR1, dst>>8); + outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8); /* set remote DMA write */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); mb_offset = 0; + /* - * Transfer the mbuf chain to the NIC memory. - * The following code isn't too pretty. The problem is that we can only - * transfer words to the board, and if an mbuf has an odd number - * of bytes in it, this is a problem. It's not a simple matter of - * just removing a byte from the next mbuf (adjusting data++ and - * len--) because this will hose-over the mbuf chain which might - * be needed later for BPF. Instead, we maintain an offset - * (mb_offset) which let's us skip over the first byte in the - * following mbuf. + * Transfer the mbuf chain to the NIC memory. The following code isn't + * too pretty. The problem is that we can only transfer words to the + * board, and if an mbuf has an odd number of bytes in it, this is a + * problem. It's not a simple matter of just removing a byte from the + * next mbuf (adjusting data++ and len--) because this will hose-over + * the mbuf chain which might be needed later for BPF. Instead, we + * maintain an offset (mb_offset) which let's us skip over the first + * byte in the following mbuf. */ while (m) { if (m->m_len - mb_offset) { @@ -2347,24 +2553,27 @@ ed_pio_write_mbufs(sc,m,dst) (m->m_len - mb_offset) / 2); /* - * if odd number of bytes, get the odd byte from - * the next mbuf with data + * if odd number of bytes, get the odd byte + * from the next mbuf with data */ if ((m->m_len - mb_offset) & 1) { /* first the last byte in current mbuf */ residual[0] = *(mtod(m, caddr_t) + - m->m_len - 1); - + m->m_len - 1); + /* advance past any empty mbufs */ while (m->m_next && (m->m_next->m_len == 0)) m = m->m_next; if (m->m_next) { - /* remove first byte in next mbuf */ + + /* + * remove first byte in next + * mbuf + */ residual[1] = *(mtod(m->m_next, caddr_t)); mb_offset = 1; } - outw(sc->asic_addr + ED_NOVELL_DATA, *((unsigned short *) residual)); } else @@ -2378,10 +2587,10 @@ ed_pio_write_mbufs(sc,m,dst) /* * Wait for remote DMA complete. This is necessary because on the - * transmit side, data is handled internally by the NIC in bursts - * and we can't start another remote DMA until this one completes. - * Not waiting causes really bad things to happen - like the NIC - * irrecoverably jamming the ISA bus. + * transmit side, data is handled internally by the NIC in bursts and + * we can't start another remote DMA until this one completes. Not + * waiting causes really bad things to happen - like the NIC + * irrecoverably jamming the ISA bus. */ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); @@ -2390,23 +2599,22 @@ ed_pio_write_mbufs(sc,m,dst) sc->arpcom.ac_if.if_unit); ed_reset(sc->arpcom.ac_if.if_unit, 0); } - - return(len); + return (len); } - + /* * Given a source and destination address, copy 'amount' of a packet from * the ring buffer into a linear destination buffer. Takes into account * ring-wrap. */ static inline char * -ed_ring_copy(sc,src,dst,amount) +ed_ring_copy(sc, src, dst, amount) struct ed_softc *sc; - char *src; - char *dst; - u_short amount; + char *src; + char *dst; + u_short amount; { - u_short tmp_amount; + u_short tmp_amount; /* does copy wrap to lower addr in ring buffer? */ if (src + amount > sc->mem_end) { @@ -2414,21 +2622,20 @@ ed_ring_copy(sc,src,dst,amount) /* copy amount up to end of NIC memory */ if (sc->mem_shared) - bcopy(src,dst,tmp_amount); + bcopy(src, dst, tmp_amount); else - ed_pio_readmem(sc,src,dst,tmp_amount); + ed_pio_readmem(sc, src, dst, tmp_amount); amount -= tmp_amount; src = sc->mem_ring; dst += tmp_amount; } - if (sc->mem_shared) bcopy(src, dst, amount); else ed_pio_readmem(sc, src, dst, amount); - return(src + amount); + return (src + amount); } /* @@ -2441,9 +2648,9 @@ ed_ring_copy(sc,src,dst,amount) * amount = amount of data to copy */ struct mbuf * -ed_ring_to_mbuf(sc,src,dst,total_len) +ed_ring_to_mbuf(sc, src, dst, total_len) struct ed_softc *sc; - char *src; + char *src; struct mbuf *dst; u_short total_len; { @@ -2452,18 +2659,19 @@ ed_ring_to_mbuf(sc,src,dst,total_len) while (total_len) { register u_short amount = min(total_len, M_TRAILINGSPACE(m)); - if (amount == 0) { /* no more data in this mbuf, alloc another */ + if (amount == 0) { /* no more data in this mbuf, alloc + * another */ + /* - * If there is enough data for an mbuf cluster, attempt - * to allocate one of those, otherwise, a regular - * mbuf will do. - * Note that a regular mbuf is always required, even if - * we get a cluster - getting a cluster does not - * allocate any mbufs, and one is needed to assign - * the cluster to. The mbuf that has a cluster - * extension can not be used to contain data - only - * the cluster can contain data. - */ + * If there is enough data for an mbuf cluster, + * attempt to allocate one of those, otherwise, a + * regular mbuf will do. Note that a regular mbuf is + * always required, even if we get a cluster - getting + * a cluster does not allocate any mbufs, and one is + * needed to assign the cluster to. The mbuf that has + * a cluster extension can not be used to contain data + * - only the cluster can contain data. + */ dst = m; MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) @@ -2476,7 +2684,6 @@ ed_ring_to_mbuf(sc,src,dst,total_len) dst->m_next = m; amount = min(total_len, M_TRAILINGSPACE(m)); } - src = ed_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount); m->m_len += amount; @@ -2485,4 +2692,62 @@ ed_ring_to_mbuf(sc,src,dst,total_len) } return (m); } +#ifdef MULTICAST +/* + * Compute crc for ethernet address + */ +u_long +ds_crc(ep) + u_char *ep; +{ +#define POLYNOMIAL 0x04c11db6 + register u_long crc = 0xffffffffL; + register int carry, i, j; + register u_char b; + + for (i = 6; --i >= 0;) { + b = *ep++; + for (j = 8; --j >= 0;) { + carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01); + crc <<= 1; + b >>= 1; + if (carry) + crc = ((crc ^ POLYNOMIAL) | carry); + } + } + return crc; +#undef POLYNOMIAL +} + +/* + * Compute the multicast address filter from the + * list of multicast addresses we need to listen to. + */ +void +ds_getmcaf(sc, mcaf) + struct ed_softc *sc; + u_long *mcaf; +{ + register u_int index; + register u_char *af = (u_char *) mcaf; + register struct ether_multi *enm; + register struct ether_multistep step; + + mcaf[0] = 0; + mcaf[1] = 0; + + ETHER_FIRST_MULTI(step, &sc->arpcom, enm); + while (enm != NULL) { + if (bcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) { + mcaf[0] = 0xffffffff; + mcaf[1] = 0xffffffff; + return; + } + index = ds_crc(enm->enm_addrlo, 6) >> 26; + af[index >> 3] |= 1 << (index & 7); + + ETHER_NEXT_MULTI(step, enm); + } +} +#endif #endif diff --git a/sys/i386/isa/if_edreg.h b/sys/i386/isa/if_edreg.h index 49f7816a4d4e..d2a03f5b508b 100644 --- a/sys/i386/isa/if_edreg.h +++ b/sys/i386/isa/if_edreg.h @@ -1,7 +1,7 @@ /* * National Semiconductor DS8390 NIC register definitions * - * $Id: if_edreg.h,v 1.13.2.1 1994/04/17 06:07:24 rgrimes Exp $ + * $Id: if_edreg.h,v 1.14 1994/04/10 20:06:28 davidg Exp $ * * Modification history * diff --git a/sys/i386/isa/if_el.c b/sys/i386/isa/if_el.c new file mode 100644 index 000000000000..d0de274baf73 --- /dev/null +++ b/sys/i386/isa/if_el.c @@ -0,0 +1,800 @@ +/* Copyright (c) 1994, Matthew E. Kimmel. Permission is hereby granted + * to use, copy, modify and distribute this software provided that both + * the copyright notice and this permission notice appear in all copies + * of the software, derivative works or modified versions, and any + * portions thereof. + * + * Questions, comments, bug reports and fixes to kimmel@cs.umass.edu. + */ +/* Except of course for the portions of code lifted from other FreeBSD + * drivers (mainly elread, elget and el_ioctl) + */ +/* 3COM Etherlink 3C501 device driver for FreeBSD */ +/* Yeah, I know these cards suck, but you can also get them for free + * really easily... + */ +/* Bugs/possible improvements: + * - Does not currently support DMA + * - Does not currently support multicasts + */ +#include "el.h" +#if NEL > 0 +#include "bpfilter.h" + +#include "param.h" +#include "systm.h" +#include "errno.h" +#include "ioctl.h" +#include "mbuf.h" +#include "socket.h" +#include "syslog.h" + +#include "net/if.h" +#include "net/if_dl.h" +#include "net/if_types.h" + +#ifdef INET +#include "netinet/in.h" +#include "netinet/in_systm.h" +#include "netinet/in_var.h" +#include "netinet/ip.h" +#include "netinet/if_ether.h" +#endif + +#ifdef NS +#include "netns/ns.h" +#include "netns/ns_if.h" +#endif + +#if NBPFILTER > 0 +#include "net/bpf.h" +#include "net/bpfdesc.h" +#endif + +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/icu.h" +#include "i386/isa/if_elreg.h" + +#define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1518 + +/* For debugging convenience */ +#ifdef EL_DEBUG +#define dprintf(x) printf x +#else +#define dprintf(x) +#endif + +/* el_softc: per line info and status */ +struct el_softc { + struct arpcom arpcom; /* Ethernet common */ + u_short el_base; /* Base I/O addr */ + caddr_t bpf; /* BPF magic cookie */ + char el_pktbuf[EL_BUFSIZ]; /* Frame buffer */ +} el_softc[NEL]; + +/* Prototypes */ +int el_attach(struct isa_device *); +void el_init(int); +void elintr(int); +int el_ioctl(struct ifnet *,int,caddr_t); +int el_probe(struct isa_device *); +void el_start(struct ifnet *); +void el_reset(int,int); +void el_watchdog(int); + +static void el_stop(int); +static int el_xmit(struct el_softc *,int); +static inline void elread(struct el_softc *,caddr_t,int); +static struct mbuf *elget(caddr_t,int,int,struct ifnet *); +static inline void el_hardreset(int); + +/* isa_driver structure for autoconf */ +struct isa_driver eldriver = { + el_probe, el_attach, "el" +}; + +/* Probe routine. See if the card is there and at the right place. */ +int el_probe(struct isa_device *idev) +{ + struct el_softc *sc; + u_short base; /* Just for convenience */ + u_char station_addr[ETHER_ADDR_LEN]; + int i; + + /* Grab some info for our structure */ + sc = &el_softc[idev->id_unit]; + sc->el_base = idev->id_iobase; + base = sc->el_base; + + /* First check the base */ + if((base < 0x280) || (base > 0x3f0)) { + printf("el%d: ioaddr must be between 0x280 and 0x3f0\n", + idev->id_unit); + return(0); + } + + /* Now attempt to grab the station address from the PROM + * and see if it contains the 3com vendor code. + */ + dprintf(("Probing 3c501 at 0x%x...\n",base)); + + /* Reset the board */ + dprintf(("Resetting board...\n")); + outb(base+EL_AC,EL_AC_RESET); + DELAY(5); + outb(base+EL_AC,0); + dprintf(("Reading station address...\n")); + /* Now read the address */ + for(i=0;i<ETHER_ADDR_LEN;i++) { + outb(base+EL_GPBL,i); + station_addr[i] = inb(base+EL_EAW); + } + dprintf(("Address is %s\n",ether_sprintf(station_addr))); + + /* If the vendor code is ok, return a 1. We'll assume that + * whoever configured this system is right about the IRQ. + */ + if((station_addr[0] != 0x02) || (station_addr[1] != 0x60) + || (station_addr[2] != 0x8c)) { + dprintf(("Bad vendor code.\n")); + return(0); + } else { + dprintf(("Vendor code ok.\n")); + /* Copy the station address into the arpcom structure */ + bcopy(station_addr,sc->arpcom.ac_enaddr,ETHER_ADDR_LEN); + return(1); + } +} + +/* Attach the interface to the kernel data structures. By the time + * this is called, we know that the card exists at the given I/O address. + * We still assume that the IRQ given is correct. + */ +int el_attach(struct isa_device *idev) +{ + struct el_softc *sc; + struct ifnet *ifp; + struct ifaddr *ifa; + struct sockaddr_dl *sdl; + u_short base; + int t; + + dprintf(("Attaching el%d...\n",idev->id_unit)); + + /* Get things pointing to the right places. */ + sc = &el_softc[idev->id_unit]; + ifp = &sc->arpcom.ac_if; + base = sc->el_base; + + /* Now reset the board */ + dprintf(("Resetting board...\n")); + el_hardreset(idev->id_unit); + + /* Initialize ifnet structure */ + ifp->if_unit = idev->id_unit; + ifp->if_name = "el"; + ifp->if_mtu = ETHERMTU; + ifp->if_init = el_init; + ifp->if_output = ether_output; + ifp->if_start = el_start; + ifp->if_ioctl = el_ioctl; + ifp->if_reset = el_reset; + ifp->if_watchdog = el_watchdog; + ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS); + + /* Now we can attach the interface */ + dprintf(("Attaching interface...\n")); + if_attach(ifp); + + /* Put the station address in the ifa address list's AF_LINK + * entry, if any. + */ + ifa = ifp->if_addrlist; + while ((ifa != NULL) && (ifa->ifa_addr != NULL) && + (ifa->ifa_addr->sa_family != AF_LINK)) + ifa = ifa->ifa_next; + if((ifa != NULL) && (ifa->ifa_addr != NULL)) { + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = ETHER_ADDR_LEN; + sdl->sdl_slen = 0; + bcopy(sc->arpcom.ac_enaddr,LLADDR(sdl),ETHER_ADDR_LEN); + } + + /* Print out some information for the user */ + printf("el%d: 3c501 address %s\n",idev->id_unit, + ether_sprintf(sc->arpcom.ac_enaddr)); + + /* Finally, attach to bpf filter if it is present. */ +#if NBPFILTER > 0 + dprintf(("Attaching to BPF...\n")); + bpfattach(&sc->bpf,ifp,DLT_EN10MB,sizeof(struct ether_header)); +#endif + + dprintf(("el_attach() finished.\n")); + return(1); +} + +/* This routine resets the interface. */ +void el_reset(int unit,int uban) +{ + int s; + + dprintf(("elreset()\n")); + s = splimp(); + el_stop(unit); + el_init(unit); + splx(s); +} + +static void el_stop(int unit) +{ + struct el_softc *sc; + + sc = &el_softc[unit]; + outb(sc->el_base+EL_AC,0); +} + +/* Do a hardware reset of the 3c501. Do not call until after el_probe()! */ +static inline void el_hardreset(int unit) +{ + register struct el_softc *sc; + register int base; + register int j; + + sc = &el_softc[unit]; + base = sc->el_base; + + /* First reset the board */ + outb(base+EL_AC,EL_AC_RESET); + DELAY(5); + outb(base+EL_AC,0); + + /* Then give it back its ethernet address. Thanks to the mach + * source code for this undocumented goodie... + */ + for(j=0;j<ETHER_ADDR_LEN;j++) + outb(base+j,sc->arpcom.ac_enaddr[j]); +} + +/* Initialize interface. */ +void el_init(int unit) +{ + struct el_softc *sc; + struct ifnet *ifp; + int s; + u_short base; + + /* Set up pointers */ + sc = &el_softc[unit]; + ifp = &sc->arpcom.ac_if; + base = sc->el_base; + + /* If address not known, do nothing. */ + if(ifp->if_addrlist == (struct ifaddr *)0) + return; + + s = splimp(); + + /* First, reset the board. */ + dprintf(("Resetting board...\n")); + el_hardreset(unit); + + /* Configure rx */ + dprintf(("Configuring rx...\n")); + if(ifp->if_flags & IFF_PROMISC) + outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW)); + else + outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW)); + outb(base+EL_RBC,0); + + /* Configure TX */ + dprintf(("Configuring tx...\n")); + outb(base+EL_TXC,0); + + /* Start reception */ + dprintf(("Starting reception...\n")); + outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX)); + + /* Set flags appropriately */ + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* And start output. */ + el_start(ifp); + + splx(s); +} + +/* Start output on interface. Get datagrams from the queue and output + * them, giving the receiver a chance between datagrams. Call only + * from splimp or interrupt level! + */ +void el_start(struct ifnet *ifp) +{ + struct el_softc *sc; + u_short base; + struct mbuf *m, *m0; + int s, i, len, retries, done; + + /* Get things pointing in the right directions */ + sc = &el_softc[ifp->if_unit]; + base = sc->el_base; + + dprintf(("el_start()...\n")); + s = splimp(); + + /* Don't do anything if output is active */ + if(sc->arpcom.ac_if.if_flags & IFF_OACTIVE) + return; + sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; + + /* The main loop. They warned me against endless loops, but + * would I listen? NOOO.... + */ + while(1) { + /* Dequeue the next datagram */ + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd,m0); + + /* If there's nothing to send, return. */ + if(m0 == NULL) { + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + splx(s); + return; + } + + /* Disable the receiver */ + outb(base+EL_AC,EL_AC_HOST); + outb(base+EL_RBC,0); + + /* Copy the datagram to the buffer. */ + len = 0; + for(m = m0; m != NULL; m = m->m_next) { + if(m->m_len == 0) + continue; + bcopy(mtod(m,caddr_t),sc->el_pktbuf+len,m->m_len); + len += m->m_len; + } + m_freem(m0); + + len = MAX(len,ETHER_MIN_LEN); + + /* Give the packet to the bpf, if any */ +#if NBPFILTER > 0 + if(sc->bpf) + bpf_tap(sc->bpf,sc->el_pktbuf,len); +#endif + + /* Transfer datagram to board */ + dprintf(("el: xfr pkt length=%d...\n",len)); + i = EL_BUFSIZ - len; + outb(base+EL_GPBL,(i & 0xff)); + outb(base+EL_GPBH,((i>>8)&0xff)); + outsb(base+EL_BUF,sc->el_pktbuf,len); + + /* Now transmit the datagram */ + retries=0; + done=0; + while(!done) { + if(el_xmit(sc,len)) { /* Something went wrong */ + done = -1; + break; + } + /* Check out status */ + i = inb(base+EL_TXS); + dprintf(("tx status=0x%x\n",i)); + if(!(i & EL_TXS_READY)) { + dprintf(("el: err txs=%x\n",i)); + sc->arpcom.ac_if.if_oerrors++; + if(i & (EL_TXS_COLL|EL_TXS_COLL16)) { + if((!(i & EL_TXC_DCOLL16)) && retries < 15) { + retries++; + outb(base+EL_AC,EL_AC_HOST); + } + } + else + done = 1; + } + else { + sc->arpcom.ac_if.if_opackets++; + done = 1; + } + } + if(done == -1) /* Packet not transmitted */ + continue; + + /* Now give the card a chance to receive. + * Gotta love 3c501s... + */ + (void)inb(base+EL_AS); + outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX)); + splx(s); + /* Interrupt here */ + s = splimp(); + } +} + +/* This function actually attempts to transmit a datagram downloaded + * to the board. Call at splimp or interrupt, after downloading data! + * Returns 0 on success, non-0 on failure + */ +static int el_xmit(struct el_softc *sc,int len) +{ + int gpl; + int i; + + gpl = EL_BUFSIZ - len; + dprintf(("el: xmit...")); + outb((sc->el_base)+EL_GPBL,(gpl & 0xff)); + outb((sc->el_base)+EL_GPBH,((gpl>>8)&0xff)); + outb((sc->el_base)+EL_AC,EL_AC_TXFRX); + i = 20000; + while((inb((sc->el_base)+EL_AS) & EL_AS_TXBUSY) && (i>0)) + i--; + if(i == 0) { + dprintf(("tx not ready\n")); + sc->arpcom.ac_if.if_oerrors++; + return(-1); + } + dprintf(("%d cycles.\n",(20000-i))); + return(0); +} + +/* controller interrupt */ +void elintr(int unit) +{ + register struct el_softc *sc; + register base; + int stat, rxstat, len, done; + + /* Get things pointing properly */ + sc = &el_softc[unit]; + base = sc->el_base; + + dprintf(("elintr: ")); + + /* Check board status */ + stat = inb(base+EL_AS); + if(stat & EL_AS_RXBUSY) { + (void)inb(base+EL_RXC); + outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX)); + return; + } + + done = 0; + while(!done) { + rxstat = inb(base+EL_RXS); + if(rxstat & EL_RXS_STALE) { + (void)inb(base+EL_RXC); + outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX)); + return; + } + + /* If there's an overflow, reinit the board. */ + if(!(rxstat & EL_RXS_NOFLOW)) { + dprintf(("overflow.\n")); + el_hardreset(unit); + /* Put board back into receive mode */ + if(sc->arpcom.ac_if.if_flags & IFF_PROMISC) + outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW)); + else + outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW)); + (void)inb(base+EL_AS); + outb(base+EL_RBC,0); + (void)inb(base+EL_RXC); + outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX)); + return; + } + + /* Incoming packet */ + len = inb(base+EL_RBL); + len |= inb(base+EL_RBH) << 8; + dprintf(("receive len=%d rxstat=%x ",len,rxstat)); + outb(base+EL_AC,EL_AC_HOST); + + /* If packet too short or too long, restore rx mode and return + */ + if((len <= sizeof(struct ether_header)) || (len > ETHER_MAX_LEN)) { + if(sc->arpcom.ac_if.if_flags & IFF_PROMISC) + outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW)); + else + outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW)); + (void)inb(base+EL_AS); + outb(base+EL_RBC,0); + (void)inb(base+EL_RXC); + outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX)); + return; + } + + sc->arpcom.ac_if.if_ipackets++; + + /* Copy the data into our buffer */ + outb(base+EL_GPBL,0); + outb(base+EL_GPBH,0); + insb(base+EL_BUF,sc->el_pktbuf,len); + outb(base+EL_RBC,0); + outb(base+EL_AC,EL_AC_RX); + dprintf(("%s-->",ether_sprintf(sc->el_pktbuf+6))); + dprintf(("%s\n",ether_sprintf(sc->el_pktbuf))); + + /* Pass data up to upper levels */ + len -= sizeof(struct ether_header); + elread(sc,(caddr_t)(sc->el_pktbuf),len); + + /* Is there another packet? */ + stat = inb(base+EL_AS); + + /* If so, do it all again (i.e. don't set done to 1) */ + if(!(stat & EL_AS_RXBUSY)) + dprintf(("<rescan> ")); + else + done = 1; + } + + (void)inb(base+EL_RXC); + outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX)); + return; +} + +/* Pass a packet up to the higher levels. Deal with trailer protocol. */ +static inline void elread(struct el_softc *sc,caddr_t buf,int len) +{ + register struct ether_header *eh; + struct mbuf *m; + int off, resid; + + /* Deal with trailer protocol: if type is trailer type + * get true type from first 16-bit word past data. + * Remember that type was trailer by setting off. + */ + eh = (struct ether_header *)buf; + eh->ether_type = ntohs((u_short)eh->ether_type); +#define eldataaddr(eh,off,type) ((type)(((caddr_t)((eh)+1)+(off)))) + if(eh->ether_type >= ETHERTYPE_TRAIL && + eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + off = (eh->ether_type - ETHERTYPE_TRAIL) * 512; + if(off >= ETHERMTU) + return; + eh->ether_type = ntohs(*eldataaddr(eh,off,u_short *)); + resid = ntohs(*(eldataaddr(eh,off+2,u_short *))); + if((off+resid) > len) + return; + len = off + resid; + } + else + off = 0; + + if(len <= 0) + return; + +#if NBPFILTER > 0 + /* + * Check if there's a bpf filter listening on this interface. + * If so, hand off the raw packet to bpf, which must deal with + * trailers in its own way. + */ + if(sc->bpf) { + eh->ether_type = htons((u_short)eh->ether_type); + bpf_tap(sc->bpf,buf,len+sizeof(struct ether_header)); + eh->ether_type = ntohs((u_short)eh->ether_type); + + /* + * Note that the interface cannot be in promiscuous mode if + * there are no bpf listeners. And if el are in promiscuous + * mode, el have to check if this packet is really ours. + * + * This test does not support multicasts. + */ + if((sc->arpcom.ac_if.if_flags & IFF_PROMISC) + && bcmp(eh->ether_dhost,sc->arpcom.ac_enaddr, + sizeof(eh->ether_dhost)) != 0 + && bcmp(eh->ether_dhost,etherbroadcastaddr, + sizeof(eh->ether_dhost)) != 0) + return; + } +#endif + + /* + * Pull packet off interface. Off is nonzero if packet + * has trailing header; neget will then force this header + * information to be at the front, but we still have to drop + * the type and length which are at the front of any trailer data. + */ + m = elget(buf,len,off,&sc->arpcom.ac_if); + if(m == 0) + return; + + ether_input(&sc->arpcom.ac_if,eh,m); +} + +/* + * Pull read data off a interface. + * Len is length of data, with local net header stripped. + * Off is non-zero if a trailer protocol was used, and + * gives the offset of the trailer information. + * We copy the trailer information and then all the normal + * data into mbufs. When full cluster sized units are present + * we copy into clusters. + */ +struct mbuf * +elget(buf, totlen, off0, ifp) + caddr_t buf; + int totlen, off0; + struct ifnet *ifp; +{ + struct mbuf *top, **mp, *m, *p; + int off = off0, len; + register caddr_t cp = buf; + char *epkt; + + buf += sizeof(struct ether_header); + cp = buf; + epkt = cp + totlen; + + + if (off) { + cp += off + 2 * sizeof(u_short); + totlen -= 2 * sizeof(u_short); + } + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return (0); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = totlen; + m->m_len = MHLEN; + top = 0; + mp = ⊤ + while (totlen > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(top); + return (0); + } + m->m_len = MLEN; + } + len = min(totlen, epkt - cp); + if (len >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + m->m_len = len = min(len, MCLBYTES); + else + len = m->m_len; + } else { + /* + * Place initial small packet/header at end of mbuf. + */ + if (len < m->m_len) { + if (top == 0 && len + max_linkhdr <= m->m_len) + m->m_data += max_linkhdr; + m->m_len = len; + } else + len = m->m_len; + } + bcopy(cp, mtod(m, caddr_t), (unsigned)len); + cp += len; + *mp = m; + mp = &m->m_next; + totlen -= len; + if (cp == epkt) + cp = buf; + } + return (top); +} + +/* + * Process an ioctl request. This code needs some work - it looks + * pretty ugly. + */ +int +el_ioctl(ifp, command, data) + register struct ifnet *ifp; + int command; + caddr_t data; +{ + register struct ifaddr *ifa = (struct ifaddr *)data; + struct el_softc *sc = &el_softc[ifp->if_unit]; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + s = splimp(); + + switch (command) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + el_init(ifp->if_unit); /* before arpwhohas */ + /* + * See if another station has *our* IP address. + * i.e.: There is an address conflict! If a + * conflict exists, a message is sent to the + * console. + */ + ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); + break; +#endif +#ifdef NS + /* + * XXX - This code is probably wrong + */ + case AF_NS: + { + register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + + if (ns_nullhost(*ina)) + ina->x_host = + *(union ns_host *)(sc->arpcom.ac_enaddr); + else { + /* + * + */ + bcopy((caddr_t)ina->x_host.c_host, + (caddr_t)sc->arpcom.ac_enaddr, + sizeof(sc->arpcom.ac_enaddr)); + } + /* + * Set new address + */ + el_init(ifp->if_unit); + break; + } +#endif + default: + el_init(ifp->if_unit); + break; + } + break; + + case SIOCGIFADDR: + { + struct sockaddr *sa; + sa = (struct sockaddr *)&ifr->ifr_data; + bcopy((caddr_t)sc->arpcom.ac_enaddr, + (caddr_t) sa->sa_data, ETHER_ADDR_LEN); + } + break; + + case SIOCSIFFLAGS: + /* + * If interface is marked down and it is running, then stop it + */ + if (((ifp->if_flags & IFF_UP) == 0) && + (ifp->if_flags & IFF_RUNNING)) { + el_stop(ifp->if_unit); + ifp->if_flags &= ~IFF_RUNNING; + } else { + /* + * If interface is marked up and it is stopped, then start it + */ + if ((ifp->if_flags & IFF_UP) && + ((ifp->if_flags & IFF_RUNNING) == 0)) + el_init(ifp->if_unit); + } + + default: + error = EINVAL; + } + (void) splx(s); + return (error); +} + +/* Device timeout routine */ +void el_watchdog(int unit) +{ + struct el_softc *sc; + + sc = &el_softc[unit]; + + log(LOG_ERR,"el%d: device timeout\n",unit); + sc->arpcom.ac_if.if_oerrors++; + el_reset(unit,0); +} +#endif diff --git a/sys/i386/isa/if_elreg.h b/sys/i386/isa/if_elreg.h new file mode 100644 index 000000000000..806d6ff68d1b --- /dev/null +++ b/sys/i386/isa/if_elreg.h @@ -0,0 +1,76 @@ +/* Copyright (c) 1994, Matthew E. Kimmel. Permission is hereby granted + * to use, copy, modify and distribute this software provided that both + * the copyright notice and this permission notice appear in all copies + * of the software, derivative works or modified versions, and any + * portions thereof. + */ +/* 3COM Etherlink 3C501 Register Definitions */ + +/* I/O Ports */ +#define EL_RXS 0x6 /* Receive status register */ +#define EL_RXC 0x6 /* Receive command register */ +#define EL_TXS 0x7 /* Transmit status register */ +#define EL_TXC 0x7 /* Transmit command register */ +#define EL_GPBL 0x8 /* GP buffer ptr low byte */ +#define EL_GPBH 0x9 /* GP buffer ptr high byte */ +#define EL_RBL 0xa /* Receive buffer ptr low byte */ +#define EL_RBC 0xa /* Receive buffer clear */ +#define EL_RBH 0xb /* Receive buffer ptr high byte */ +#define EL_EAW 0xc /* Ethernet address window */ +#define EL_AS 0xe /* Auxillary status register */ +#define EL_AC 0xe /* Auxillary command register */ +#define EL_BUF 0xf /* Data buffer */ + +/* Receive status register bits */ +#define EL_RXS_OFLOW 0x01 /* Overflow error */ +#define EL_RXS_FCS 0x02 /* FCS error */ +#define EL_RXS_DRIB 0x04 /* Dribble error */ +#define EL_RXS_SHORT 0x08 /* Short frame */ +#define EL_RXS_NOFLOW 0x10 /* No overflow */ +#define EL_RXS_GOOD 0x20 /* Received good frame */ +#define EL_RXS_STALE 0x80 /* Stale receive status */ + +/* Receive command register bits */ +#define EL_RXC_DISABLE 0x00 /* Receiver disabled */ +#define EL_RXC_DOFLOW 0x01 /* Detect overflow */ +#define EL_RXC_DFCS 0x02 /* Detect FCS errs */ +#define EL_RXC_DDRIB 0x04 /* Detect dribble errors */ +#define EL_RXC_DSHORT 0x08 /* Detect short frames */ +#define EL_RXC_DNOFLOW 0x10 /* Detect frames w/o overflow ??? */ +#define EL_RXC_AGF 0x20 /* Accept Good Frames */ +#define EL_RXC_PROMISC 0x40 /* Promiscuous mode */ +#define EL_RXC_ABROAD 0x80 /* Accept address, broadcast */ +#define EL_RXC_AMULTI 0xc0 /* Accept address, multicast */ + +/* Transmit status register bits */ +#define EL_TXS_UFLOW 0x01 /* Underflow */ +#define EL_TXS_COLL 0x02 /* Collision */ +#define EL_TXS_COLL16 0x04 /* Collision 16 */ +#define EL_TXS_READY 0x08 /* Ready for new frame */ + +/* Transmit command register bits */ +#define EL_TXC_DUFLOW 0x01 /* Detect underflow */ +#define EL_TXC_DCOLL 0x02 /* Detect collisions */ +#define EL_TXC_DCOLL16 0x04 /* Detect collision 16 */ +#define EL_TXC_DSUCCESS 0x08 /* Detect success */ + +/* Auxillary status register bits */ +#define EL_AS_RXBUSY 0x01 /* Receive busy */ +#define EL_AS_DMADONE 0x10 /* DMA finished */ +#define EL_AS_TXBUSY 0x80 /* Transmit busy */ + +/* Auxillary command register bits */ +#define EL_AC_HOST 0x00 /* System bus can access buffer */ +#define EL_AC_IRQE 0x01 /* IRQ enable */ +#define EL_AC_TXBAD 0x02 /* Transmit frames with bad FCS */ +#define EL_AC_TXFRX 0x04 /* Transmit followed by receive */ +#define EL_AC_RX 0x08 /* Receive */ +#define EL_AC_LB 0x0c /* Loopback */ +#define EL_AC_DRQ 0x20 /* DMA request */ +#define EL_AC_RIDE 0x40 /* DRQ and IRQ enabled */ +#define EL_AC_RESET 0x80 /* Reset */ + +/* Packet buffer size */ +#define EL_BUFSIZ 2048 + +#define ETHER_ADDR_LEN 6 diff --git a/sys/i386/isa/if_ep.c b/sys/i386/isa/if_ep.c index 43edd4529485..44e9c9fc0d58 100644 --- a/sys/i386/isa/if_ep.c +++ b/sys/i386/isa/if_ep.c @@ -8,7 +8,7 @@ * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products - * derived from this software withough specific prior written permission + * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -22,62 +22,56 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * From: if_ep.c,v 1.9 1994/01/25 10:46:29 deraadt Exp $ - * $Id: if_ep.c,v 1.7 1994/02/03 11:51:06 davidg Exp $ - */ -/* - * TODO: - * Multi-509 configs. - * don't pass unit into epstop. - * epintr returns an int for magnum. 0=not for me. 1=for me. -1=whoknows? - * deallocate mbufs when ifconfig'd down. + * $Id: if_ep.c,v 1.9 1994/05/02 22:27:33 ats Exp $ */ + #include "ep.h" #if NEP > 0 #include "bpfilter.h" -#include "sys/param.h" +#include <sys/param.h> #if defined(__FreeBSD__) -#include "sys/systm.h" -#include "sys/kernel.h" +#include <sys/systm.h> +#include <sys/kernel.h> #endif -#include "sys/mbuf.h" -#include "sys/socket.h" -#include "sys/ioctl.h" -#include "sys/errno.h" -#include "sys/syslog.h" +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/syslog.h> #if defined(__NetBSD__) -#include "sys/select.h" +#include <sys/select.h> #endif -#include "net/if.h" -#include "net/if_dl.h" -#include "net/if_types.h" +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> #ifdef INET -#include "netinet/in.h" -#include "netinet/in_systm.h" -#include "netinet/in_var.h" -#include "netinet/ip.h" -#include "netinet/if_ether.h" +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> #endif #ifdef NS -#include "netns/ns.h" -#include "netns/ns_if.h" +#include <netns/ns.h> +#include <netns/ns_if.h> #endif #if NBPFILTER > 0 -#include "net/bpf.h" -#include "net/bpfdesc.h" +#include <net/bpf.h> +#include <net/bpfdesc.h> #endif -#include "machine/pio.h" +#include <machine/pio.h> -#include "i386/isa/isa.h" -#include "i386/isa/isa_device.h" -#include "i386/isa/icu.h" -#include "i386/isa/if_epreg.h" +#include <i386/isa/isa.h> +#include <i386/isa/isa_device.h> +#include <i386/isa/icu.h> +#include <i386/isa/if_epreg.h> #define ETHER_MIN_LEN 64 #define ETHER_MAX_LEN 1518 @@ -90,12 +84,13 @@ struct ep_softc { struct arpcom arpcom; /* Ethernet common part */ short ep_io_addr; /* i/o bus address */ char ep_connectors; /* Connectors on this card. */ -#define MAX_MBS 4 /* # of mbufs we keep around */ +#define MAX_MBS 8 /* # of mbufs we keep around */ struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */ int next_mb; /* Which mbuf to use next. */ int last_mb; /* Last mbuf. */ int tx_start_thresh; /* Current TX_start_thresh. */ caddr_t bpf; /* BPF "magic cookie" */ + char bus32bit; /* 32bit access possible */ } ep_softc[NEP]; static int epprobe __P((struct isa_device *)); @@ -104,7 +99,8 @@ static int epioctl __P((struct ifnet * ifp, int, caddr_t)); void epinit __P((int)); void epintr __P((int)); -void epmbufqueue __P((caddr_t, int)); +void epmbuffill __P((caddr_t, int)); +void epmbufempty __P((struct ep_softc *)); void epread __P((struct ep_softc *)); void epreset __P((int)); void epstart __P((struct ifnet *)); @@ -274,7 +270,7 @@ epinit(unit) return; s = splimp(); - while (inb(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) ; GO_WINDOW(0); @@ -350,7 +346,7 @@ epinit(unit) */ sc->last_mb = 0; sc->next_mb = 0; - epmbufqueue((caddr_t)sc, 0); + epmbuffill((caddr_t)sc, 0); epstart(ifp); @@ -421,10 +417,19 @@ startagain: outw(BASE + EP_W1_TX_PIO_WR_1, 0xffff); /* Second dword meaningless */ for (top = m; m != 0; m = m->m_next) { - outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len/2); - if (m->m_len & 1) - outb(BASE + EP_W1_TX_PIO_WR_1, - *(mtod(m, caddr_t) + m->m_len - 1)); + if (sc->bus32bit) { + outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), + m->m_len/4); + if (m->m_len & 3) + outsb(BASE + EP_W1_TX_PIO_WR_1, + mtod(m, caddr_t) + m->m_len/4, + m->m_len & 3); + } else { + outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len/2); + if (m->m_len & 1) + outb(BASE + EP_W1_TX_PIO_WR_1, + *(mtod(m, caddr_t) + m->m_len - 1)); + } } while (pad--) outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ @@ -658,7 +663,7 @@ epread(sc) if (m == 0) goto out; } else { - timeout(epmbufqueue, (caddr_t)sc, 0); + timeout(epmbuffill, (caddr_t)sc, 0); sc->next_mb = (sc->next_mb + 1) % MAX_MBS; } if (totlen >= MINCLSIZE) @@ -667,11 +672,22 @@ epread(sc) mcur->m_next = m; lenthisone = min(totlen, M_TRAILINGSPACE(m)); } - insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, - lenthisone / 2); - m->m_len += lenthisone; - if (lenthisone & 1) - *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1); + if (sc->bus32bit) { + insl(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + lenthisone / 4); + m->m_len += (lenthisone & ~3); + if (lenthisone & 3) + insb(BASE + EP_W1_RX_PIO_RD_1, + mtod(m, caddr_t) + m->m_len, + lenthisone & 3); + m->m_len += (lenthisone & 3); + } else { + insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + lenthisone / 2); + m->m_len += lenthisone; + if (lenthisone & 1) + *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1); + } totlen -= lenthisone; } if (off) { @@ -679,15 +695,17 @@ epread(sc) sc->mb[sc->next_mb] = 0; if (top == 0) { MGETHDR(m, M_DONTWAIT, MT_DATA); - if (top == 0) + if (top == 0) { + top = m0; goto out; + } } else { /* Convert one of our saved mbuf's */ sc->next_mb = (sc->next_mb + 1) % MAX_MBS; top->m_data = top->m_pktdat; top->m_flags = M_PKTHDR; } - insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + insw(BASE + EP_W1_RX_PIO_RD_1, mtod(top, caddr_t), sizeof(struct ether_header)); top->m_next = m0; top->m_len = sizeof(struct ether_header); @@ -700,7 +718,7 @@ epread(sc) top->m_pkthdr.rcvif = &sc->arpcom.ac_if; outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); - while (inb(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) ; ++sc->arpcom.ac_if.if_ipackets; #if NBPFILTER > 0 @@ -728,12 +746,11 @@ epread(sc) return; out: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); - while (inb(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) ; if (top) m_freem(top); - return; } @@ -773,7 +790,7 @@ epioctl(ifp, cmd, data) else { ifp->if_flags &= ~IFF_RUNNING; bcopy((caddr_t) ina->x_host.c_host, - (caddr_t)sc->arpcom.ns_addr + (caddr_t)sc->arpcom.ac_enaddr, sizeof(sc->arpcom.ac_enaddr)); } epinit(ifp->if_unit); @@ -789,6 +806,7 @@ epioctl(ifp, cmd, data) if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { ifp->if_flags &= ~IFF_RUNNING; epstop(ifp->if_unit); + epmbufempty(sc); break; } if (ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0) @@ -815,7 +833,6 @@ epreset(unit) epstop(unit); epinit(unit); splx(s); - return; } void @@ -838,7 +855,7 @@ epstop(unit) outw(BASE + EP_COMMAND, RX_DISABLE); outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); - while (inb(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) ; outw(BASE + EP_COMMAND, TX_DISABLE); outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); @@ -848,7 +865,6 @@ epstop(unit) outw(BASE + EP_COMMAND, SET_RD_0_MASK); outw(BASE + EP_COMMAND, SET_INTR_MASK); outw(BASE + EP_COMMAND, SET_RX_FILTER); - return; } @@ -937,24 +953,42 @@ is_eeprom_busy(is) } void -epmbufqueue(sp, dummy_arg) +epmbuffill(sp, dummy_arg) caddr_t sp; int dummy_arg; { struct ep_softc *sc = (struct ep_softc *)sp; - int i; + int s, i; - if (sc->mb[sc->last_mb]) - return; + s = splimp(); i = sc->last_mb; do { - MGET(sc->mb[i], M_DONTWAIT, MT_DATA); - if (!sc->mb[i]) + if(sc->mb[i] == NULL) + MGET(sc->mb[i], M_DONTWAIT, MT_DATA); + if(sc->mb[i] == NULL) break; i = (i + 1) % MAX_MBS; } while (i != sc->next_mb); sc->last_mb = i; - return; + splx(s); +} + +static void +epmbufempty(sc) + struct ep_softc *sc; +{ + int s, i; + + s = splimp(); + for (i = 0; i<MAX_MBS; i++) { + if (sc->mb[i]) { + m_freem(sc->mb[i]); + sc->mb[i] = NULL; + } + } + sc->last_mb = sc->next_mb = 0; + untimeout(epmbuffill, sc); + splx(s); } #endif /* NEP > 0 */ diff --git a/sys/i386/isa/if_ie507.h b/sys/i386/isa/if_ie507.h new file mode 100644 index 000000000000..4bf87fcbb597 --- /dev/null +++ b/sys/i386/isa/if_ie507.h @@ -0,0 +1,19 @@ +/* + * $Id: if_ie507.h,v 1.1 1994/05/25 20:06:49 ats Exp $ + * Definitions for 3C507 + */ + +#define IE507_CTRL 6 /* control port */ +#define IE507_ICTRL 10 /* interrupt control */ +#define IE507_ATTN 11 /* any write here sends a chan attn */ +#define IE507_MADDR 14 /* shared memory configuration */ +#define IE507_IRQ 15 /* IRQ configuration */ + +#define EL_CTRL_BNK1 0x01 /* register bank 1 */ +#define EL_CTRL_IEN 0x04 /* interrupt enable */ +#define EL_CTRL_INTL 0x08 /* interrupt active latch */ +#define EL_CTRL_16BIT 0x10 /* bus width; clear = 8-bit, set = 16-bit */ +#define EL_CTRL_LOOP 0x20 /* loopback mode */ +#define EL_CTRL_NRST 0x80 /* turn off to reset */ +#define EL_CTRL_RESET (EL_CTRL_LOOP) +#define EL_CTRL_NORMAL (EL_CTRL_NRST | EL_CTRL_IEN | EL_CTRL_BNK1) diff --git a/sys/i386/isa/if_is.c b/sys/i386/isa/if_is.c index 341885f36ed1..234a0d937c1f 100644 --- a/sys/i386/isa/if_is.c +++ b/sys/i386/isa/if_is.c @@ -332,15 +332,15 @@ is_attach(isa_dev) * are only 16 bits wide! */ -#define MAXMEM ((NRBUF+NTBUF)*(BUFSIZE) + (NRBUF+NTBUF)*sizeof(struct mds) \ +#define ISMAXMEM ((NRBUF+NTBUF)*(BUFSIZE) + (NRBUF+NTBUF)*sizeof(struct mds) \ + sizeof(struct init_block) + 8) - is->init_block = (struct init_block *)malloc(MAXMEM,M_TEMP,M_NOWAIT); + is->init_block = (struct init_block *)malloc(ISMAXMEM,M_TEMP,M_NOWAIT); if (!is->init_block) { printf("is%d : Couldn't allocate memory for card\n",unit); } /* * XXX -- should take corrective action if not - * quadword alilgned, the 8 byte slew factor in MAXMEM + * quadword alilgned, the 8 byte slew factor in ISMAXMEM * allows for this. */ @@ -483,7 +483,7 @@ is_init(unit) /* Address not known */ if (ifp->if_addrlist == (struct ifaddr *)0) return; - s = splnet(); + s = splimp(); /* * Lance must be stopped @@ -984,7 +984,7 @@ is_ioctl(ifp, cmd, data) struct ifreq *ifr = (struct ifreq *)data; int s, error = 0; - s = splnet(); + s = splimp(); switch (cmd) { diff --git a/sys/i386/isa/if_ze.c b/sys/i386/isa/if_ze.c new file mode 100644 index 000000000000..6ffb96c96dbc --- /dev/null +++ b/sys/i386/isa/if_ze.c @@ -0,0 +1,1951 @@ +/*- + * TODO: + * [1] integrate into current if_ed.c + * [2] parse tuples to find out where to map the shared memory buffer, + * and what to write into the configuration register + * [3] move pcic-specific code into a separate module. + * + * Device driver for IBM PCMCIA Credit Card Adapter for Ethernet, + * if_ze.c + * + * Based on the Device driver for National Semiconductor DS8390 ethernet + * adapters by David Greenman. Modifications for PCMCIA by Keith Moore. + * Adapted for FreeBSD 1.1.5 by Jordan Hubbard. + * + * Currently supports only the IBM Credit Card Adapter for Ethernet, but + * could probably work with other PCMCIA cards also, if it were modified + * to get the locations of the PCMCIA configuration option register (COR) + * by parsing the configuration tuples, rather than by hard-coding in + * the value expected by IBM's card. + * + * Sources for data on the PCMCIA/IBM CCAE specific portions of the driver: + * + * [1] _Local Area Network Credit Card Adapters Technical Reference_, + * IBM Corp., SC30-3585-00, part # 33G9243. + * [2] "pre-alpha" PCMCIA support code for Linux by Barry Jaspan. + * [3] Intel 82536SL PC Card Interface Controller Data Sheet, Intel + * Order Number 290423-002 + * [4] National Semiconductor DP83902A ST-NIC (tm) Serial Network + * Interface Controller for Twisted Pair data sheet. + * + * + * Copyright (C) 1993, David Greenman. This software may be used, modified, + * copied, distributed, and sold, in both source and binary form provided + * that the above copyright and these terms are retained. Under no + * circumstances is the author responsible for the proper functioning + * of this software, nor does the author assume any responsibility + * for damages incurred with its use. + */ + +#include "ze.h" +#if NZE > 0 +#include "bpfilter.h" + +#include "param.h" +#include "systm.h" +#include "errno.h" +#include "ioctl.h" +#include "mbuf.h" +#include "socket.h" +#include "syslog.h" + +#include "net/if.h" +#include "net/if_dl.h" +#include "net/if_types.h" +#include "net/netisr.h" + +#ifdef INET +#include "netinet/in.h" +#include "netinet/in_systm.h" +#include "netinet/in_var.h" +#include "netinet/ip.h" +#include "netinet/if_ether.h" +#endif + +#ifdef NS +#include "netns/ns.h" +#include "netns/ns_if.h" +#endif + +#if NBPFILTER > 0 +#include "net/bpf.h" +#include "net/bpfdesc.h" +#endif + +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/icu.h" +#include "i386/isa/if_zereg.h" + +#include "i386/include/pio.h" + + + +/***************************************************************************** + * pcmcia controller chip (PCIC) support * + * (eventually, move this to a separate file) * + *****************************************************************************/ +#include "ic/i82365.h" + +/* + * Each PCIC chip (82365SL or clone) can handle two card slots, and there + * can be up to four PCICs in a system. (On some machines, not all of the + * address lines are decoded, so a card may appear to be in more than one + * slot.) + */ +#define MAXSLOT 8 + +/* + * To access a register on the PCIC for a particular slot, you + * first write the correct OFFSET value for that slot in the + * INDEX register for the PCIC controller. You then read or write + * the value from or to the DATA register for that controller. + * + * The first pair of chips shares I/O addresss for DATA and INDEX, + * as does the second pair. (To the programmer, it looks like each + * pair is a single chip.) The i/o port addresses are hard-wired + * into the PCIC; so the following addresses should be valid for + * any machine that uses this chip. + */ + +#define PCIC_INDEX_0 0x3E0 /* index reg, chips 0 and 1 */ +#define PCIC_DATA_0 0x3E1 /* data register, chips 0 and 1 */ +#define PCIC_INDEX_1 0x3E2 /* index reg, chips 1 and 2 */ +#define PCIC_DATA_1 0x3E3 /* data register, chips 1 and 2 */ + +/* + * Given a slot number, calculate the INDEX and DATA registers + * to talk to that slot. OFFSET is added to the register number + * to address the registers for a particular slot. + */ +#define INDEX(slot) ((slot) < 4 ? PCIC_INDEX_0 : PCIC_INDEX_1) +#define DATA(slot) ((slot) < 4 ? PCIC_DATA_0 : PCIC_DATA_1) +#define OFFSET(slot) ((slot) % 4 * 0x40) + +/* + * There are 5 sets (windows) of memory mapping registers on the PCIC chip + * for each slot, numbered 0..4. + * + * They start at 10/50 hex within the chip's register space (not system + * I/O space), and are eight addresses apart. These are actually pairs of + * 8-bit-wide registers (low byte first, then high byte) since the + * address fields are actually 12 bits long. The upper bits are used + * for other things like 8/16-bit select and wait states. + * + * Memory mapping registers include start/stop addresses to define the + * region to be mapped (in terms of system memory addresses), and + * an offset register to allow for translation from system space + * to card space. The lower 12 bits aren't included in these, so memory is + * mapped in 4K chunks. + */ +#define MEM_START_ADDR(window) (((window) * 0x08) + 0x10) +#define MEM_STOP_ADDR(window) (((window) * 0x08) + 0x12) +#define MEM_OFFSET(window) (((window) * 0x08) + 0x14) +/* + * this bit gets set in the address window enable register (PCIC_ADDRWINE) + * to enable a particular address window. + */ +#define MEM_ENABLE_BIT(window) ((1) << (window)) + +/* + * There are two i/o port addressing windows. I/O ports cannot be + * relocated within system i/o space (unless the card doesn't decode + * all of the address bits); unlike card memory, there is no address + * translation offset. + */ +#define IO_START_ADDR(window) ((window) ? PCIC_IO1_STL : PCIC_IO0_STL) +#define IO_STOP_ADDR(window) ((window) ? PCIC_IO1_SPL : PCIC_IO0_SPL) +#define IO_ENABLE_BIT(window) ((window) ? PCIC_IO1_EN : PCIC_IO0_EN) +#define IO_CS16_BIT(window) ((window) ? PCIC_IO1_CS16 : PCIC_IO0_CS16) + +/* + * read a byte from a pcic register for a particular slot + */ +static inline unsigned char +pcic_getb (int slot, int reg) +{ + outb (INDEX(slot), OFFSET (slot) + reg); + return inb (DATA (slot)); +} + +/* + * write a byte to a pcic register for a particular slot + */ +static inline void +pcic_putb (int slot, int reg, unsigned char val) +{ + outb (INDEX(slot), OFFSET (slot) + reg); + outb (DATA (slot), val); +} + +/* + * read a word from a pcic register for a particular slot + */ +static inline unsigned short +pcic_getw (int slot, int reg) +{ + return pcic_getb (slot, reg) | (pcic_getb (slot, reg+1) << 8); +} + +/* + * write a word to a pcic register at a particular slot + */ +static inline void +pcic_putw (int slot, int reg, unsigned short val) +{ + pcic_putb (slot, reg, val & 0xff); + pcic_putb (slot, reg + 1, (val >> 8) & 0xff); +} + +static void +pcic_print_regs (int slot) +{ + int i, j; + + for (i = 0; i < 0x40; i += 16) { + for (j = 0; j < 16; ++j) + printf ("%02x ", pcic_getb (slot, i + j)); + printf ("\n"); + } +} + +/* + * map a portion of the card's memory space into system memory + * space. + * + * slot = # of the slot the card is plugged into + * window = which pcic memory map registers to use (0..4) + * sys_addr = base system PHYSICAL memory address where we want it. must + * be on an appropriate boundary (lower 12 bits are zero). + * card_addr = the base address of the card's memory to correspond + * to sys_addr + * length = length of the segment to map (may be rounded up as necessary) + * type = which card memory space to map (attribute or shared) + * width = 1 for byte-wide mapping; 2 for word (16-bit) mapping. + */ + +enum memtype { COMMON, ATTRIBUTE }; + +static void +pcic_map_memory (int slot, int window, unsigned long sys_addr, + unsigned long card_addr, unsigned long length, + enum memtype type, int width) +{ + unsigned short offset; + unsigned short mem_start_addr; + unsigned short mem_stop_addr; + + sys_addr >>= 12; + card_addr >>= 12; + length >>= 12; + /* + * compute an offset for the chip such that + * (sys_addr + offset) = card_addr + * but the arithmetic is done modulo 2^14 + */ + offset = (card_addr - sys_addr) & 0x3FFF; + /* + * now OR in the bit for "attribute memory" if necessary + */ + if (type == ATTRIBUTE) { + offset |= (PCIC_REG << 8); + /* REG == "region active" pin on card */ + } + /* + * okay, set up the chip memory mapping registers, and turn + * on the enable bit for this window. + * if we are doing 16-bit wide accesses (width == 2), + * turn on the appropriate bit. + * + * XXX for now, we set all of the wait state bits to zero. + * Not really sure how they should be set. + */ + mem_start_addr = sys_addr & 0xFFF; + if (width == 2) + mem_start_addr |= (PCIC_DATA16 << 8); + mem_stop_addr = (sys_addr + length) & 0xFFF; + + pcic_putw (slot, MEM_START_ADDR(window), mem_start_addr); + pcic_putw (slot, MEM_STOP_ADDR(window), mem_stop_addr); + pcic_putw (slot, MEM_OFFSET(window), offset); + /* + * Assert the bit (PCIC_MEMCS16) that says to decode all of + * the address lines. + */ + pcic_putb (slot, PCIC_ADDRWINE, + pcic_getb (slot, PCIC_ADDRWINE) | + MEM_ENABLE_BIT(window) | PCIC_MEMCS16); +} + +static void +pcic_unmap_memory (int slot, int window) +{ + /* + * seems like we need to turn off the enable bit first, after which + * we can clear the registers out just to be sure. + */ + pcic_putb (slot, PCIC_ADDRWINE, + pcic_getb (slot, PCIC_ADDRWINE) & ~MEM_ENABLE_BIT(window)); + pcic_putw (slot, MEM_START_ADDR(window), 0); + pcic_putw (slot, MEM_STOP_ADDR(window), 0); + pcic_putw (slot, MEM_OFFSET(window), 0); +} + +/* + * map a range of addresses into system i/o space + * (no translation of i/o addresses is possible) + * + * 'width' is: + * + 0 to tell the PCIC to generate the ISA IOCS16* signal from + * the PCMCIA IOIS16* signal. + * + 1 to select 8-bit width + * + 2 to select 16-bit width + */ + +static void +pcic_map_io (int slot, int window, unsigned short base, unsigned short length, + unsigned short width) +{ + unsigned char x; + + pcic_putw (slot, IO_START_ADDR(window), base); + pcic_putw (slot, IO_STOP_ADDR(window), base+length-1); + /* + * select the bits that determine whether + * an i/o operation is 8 or 16 bits wide + */ + x = pcic_getb (slot, PCIC_IOCTL); + switch (width) { + case 0: /* PCMCIA card decides */ + if (window) + x = (x & 0xf0) | PCIC_IO1_CS16; + else + x = (x & 0x0f) | PCIC_IO0_CS16; + break; + case 1: /* 8 bits wide */ + break; + case 2: /* 16 bits wide */ + if (window) + x = (x & 0xf0) | PCIC_IO1_16BIT; + else + x = (x & 0x0f) | PCIC_IO0_16BIT; + break; + } + pcic_putb (slot, PCIC_IOCTL, x); + pcic_putb (slot, PCIC_ADDRWINE, + pcic_getb (slot, PCIC_ADDRWINE) | IO_ENABLE_BIT(window)); +} + +#ifdef TEST +static void +pcic_unmap_io (int slot, int window) +{ + pcic_putb (slot, PCIC_ADDRWINE, + pcic_getb (slot, PCIC_ADDRWINE) & ~IO_ENABLE_BIT(window)); + pcic_putw (slot, IO_START_ADDR(window), 0); + pcic_putw (slot, IO_STOP_ADDR(window), 0); +} +#endif /* TEST */ + +/* + * tell the PCIC which irq we want to use. only the following are legal: + * 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 + * + * NB: 'irq' is an interrupt NUMBER, not a MASK as in struct isa_device. + */ + +static void +pcic_map_irq (int slot, int irq) +{ + if (irq < 3 || irq == 6 || irq == 8 || irq == 13 || irq > 15) { + printf ("ze: pcic_map_irq (slot %d): illegal irq %d\n", slot, irq); + return; + } + pcic_putb (slot, PCIC_INT_GEN, + pcic_getb (slot, PCIC_INT_GEN) | (irq & 0x0F)); +} + +static void +pcic_power_on (int slot) +{ + pcic_putb (slot, PCIC_POWER, + pcic_getb (slot, PCIC_POWER) | PCIC_DISRST | PCIC_PCPWRE); + DELAY (50000); + pcic_putb (slot, PCIC_POWER, + pcic_getb (slot, PCIC_POWER) | PCIC_OUTENA); +} + +static void +pcic_reset (int slot) +{ + /* assert RESET (by clearing a bit!), wait a bit, and de-assert it */ + pcic_putb (slot, PCIC_INT_GEN, + pcic_getb (slot, PCIC_INT_GEN) & ~PCIC_CARDRESET); + DELAY (50000); + pcic_putb (slot, PCIC_INT_GEN, + pcic_getb (slot, PCIC_INT_GEN) | PCIC_CARDRESET); +} + + +/***************************************************************************** + * Driver for Ethernet Adapter * + *****************************************************************************/ +/* + * ze_softc: per line info and status + */ +struct ze_softc { + struct arpcom arpcom; /* ethernet common */ + + char *type_str; /* pointer to type string */ + char *mau; /* type of media access unit */ +#if 0 + u_char vendor; /* interface vendor */ + u_char type; /* interface type code */ +#endif + +#if 0 + u_short vector; /* interrupt vector */ +#endif + u_short nic_addr; /* NIC (DS8390) I/O bus address */ + + caddr_t smem_start; /* shared memory start address */ + caddr_t smem_end; /* shared memory end address */ + u_long smem_size; /* total shared memory size */ + caddr_t smem_ring; /* start of RX ring-buffer (in smem) */ + + caddr_t bpf; /* BPF "magic cookie" */ + + u_char memwidth; /* width of access to card mem 8 or 16 */ + u_char xmit_busy; /* transmitter is busy */ + u_char txb_cnt; /* Number of transmit buffers */ + u_char txb_next; /* Pointer to next buffer ready to xmit */ + u_short txb_next_len; /* next xmit buffer length */ + u_char data_buffered; /* data has been buffered in interface memory */ + u_char tx_page_start; /* first page of TX buffer area */ + + u_char rec_page_start; /* first page of RX ring-buffer */ + u_char rec_page_stop; /* last page of RX ring-buffer */ + u_char next_packet; /* pointer to next unread RX packet */ +} ze_softc[NZE]; + +int ze_attach(), ze_ioctl(), ze_probe(); +void ze_init(), ze_start(), ze_stop(), ze_intr(); +void ze_reset(), ze_watchdog(), ze_get_packet(); + +static inline void ze_rint(); +static inline void ze_xmit(); +static inline char *ze_ring_copy(); + +extern int ether_output(); + +struct isa_driver zedriver = { + ze_probe, + ze_attach, + "ze" +}; + +#define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1518 +#define ETHER_ADDR_LEN 6 +#define ETHER_HDR_SIZE 14 + +static unsigned char enet_addr[6]; +static unsigned char card_info[256]; + +#define CARD_INFO "IBM Corp.~Ethernet~0933495" + +/* + * scan the card information structure looking for the version/product info + * tuple. when we find it, compare it to the string we are looking for. + * return 1 if we find it, 0 otherwise. + */ + +static int +ze_check_cis (unsigned char *scratch) +{ + int i,j,k; + + card_info[0] = '\0'; + i = 0; + while (scratch[i] != 0xff && i < 1024) { + unsigned char link = scratch[i+2]; + +#if 0 + printf ("[%02x] %02x ", i, link); + for (j = 4; j < 2 * link + 4 && j < 32; j += 2) + printf ("%02x ", scratch[j + i]); + printf ("\n"); +#endif + if (scratch[i] == 0x15) { + /* + * level 1 version/product info + * copy to card_info, translating '\0' to '~' + */ + k = 0; + for (j = i+8; scratch[j] != 0xff; j += 2) + card_info[k++] = scratch[j] == '\0' ? '~' : scratch[j]; + card_info[k++] = '\0'; + return (memcmp (card_info, CARD_INFO, sizeof(CARD_INFO)-1) == 0); + } + i += 4 + 2 * link; + } + return 0; +} + +/* + * Probe each slot looking for an IBM Credit Card Adapter for Ethernet + * For each card that we find, map its card information structure + * into system memory at 'scratch' and see whether it's one of ours. + * Return the slot number if we find a card, or -1 otherwise. + * + * Side effects: + * + On success, leaves CIS mapped into memory at 'scratch'; + * caller must free it. + * + On success, leaves ethernet address in enet_addr. + * + Leaves product/vendor id of last card probed in 'card_info' + */ + +static int +ze_find_adapter (unsigned char *scratch) +{ + int slot; + + for (slot = 0; slot < MAXSLOT; ++slot) { + /* + * see if there's a PCMCIA controller here + * Intel PCMCIA controllers use 0x82 and 0x83 + * IBM clone chips use 0x88 and 0x89, apparently + */ + unsigned char idbyte = pcic_getb (slot, PCIC_ID_REV); + + if (idbyte != 0x82 && idbyte != 0x83 && + idbyte != 0x88 && idbyte != 0x89) { +#if 0 + printf ("ibmccae: pcic slot %d: wierd id/rev code 0x%02x\n", + slot, idbyte); +#endif + continue; + } + if ((pcic_getb (slot, PCIC_STATUS) & PCIC_CD) != PCIC_CD) { + printf ("ze: slot %d: no card in slot\n", slot); + /* no card in slot */ + continue; + } + pcic_power_on (slot); + pcic_reset (slot); + /* + * map the card's attribute memory and examine its + * card information structure tuples for something + * we recognize. + */ + pcic_map_memory (slot, 0, kvtop (scratch), 0L, + 0xFFFL, ATTRIBUTE, 1); + + if ((ze_check_cis (scratch)) > 0) { + /* found it */ + printf ("ze: found card in slot %d\n", slot); + return slot; + } + else + printf ("ze: pcmcia slot %d: %s\n", slot, card_info); + pcic_unmap_memory (slot, 0); + } + return -1; +} + + +/* + * macros to handle casting unsigned long to (char *) so we can + * read/write into physical memory space. + */ + +#define PEEK(addr) (*((unsigned char *)(addr))) +#define POKE(addr,val) do { PEEK(addr) = (val); } while (0) + +/* + * Determine if the device is present + * + * on entry: + * a pointer to an isa_device struct + * on exit: + * NULL if device not found + * or # of i/o addresses used (if found) + */ +int +ze_probe(isa_dev) + struct isa_device *isa_dev; +{ + struct ze_softc *sc = &ze_softc[isa_dev->id_unit]; + int i, x; + u_int memsize; + u_char iptr, memwidth, sum, tmp; + int slot; + + if ((slot = ze_find_adapter (isa_dev->id_maddr)) < 0) + return NULL; + + /* + * okay, we found a card, so set it up + */ + /* + * Inhibit 16 bit memory delay. + * POINTETH.SYS apparently does this, for what reason I don't know. + */ + pcic_putb (slot, PCIC_CDGC, + pcic_getb (slot, PCIC_CDGC) | PCIC_16_DL_INH); + /* + * things to map + * (1) card's EEPROM is already mapped by the find_adapter routine + * but we still need to get the card's ethernet address. + * after that we unmap that part of attribute memory. + * (2) card configuration registers need to be mapped in so we + * can set the configuration and socket # registers. + * (3) shared memory packet buffer + * (4) i/o ports + * (5) IRQ + */ + /* + * Sigh. Location of the ethernet address isn't documented in [1]. + * It was derived by doing a hex dump of all of attribute memory + * and looking for the IBM vendor prefix. + */ + enet_addr[0] = PEEK(isa_dev->id_maddr+0xff0); + enet_addr[1] = PEEK(isa_dev->id_maddr+0xff2); + enet_addr[2] = PEEK(isa_dev->id_maddr+0xff4); + enet_addr[3] = PEEK(isa_dev->id_maddr+0xff6); + enet_addr[4] = PEEK(isa_dev->id_maddr+0xff8); + enet_addr[5] = PEEK(isa_dev->id_maddr+0xffa); + pcic_unmap_memory (slot, 0); + + /* + * (2) map card configuration registers. these are offset + * in card memory space by 0x20000. normally we could get + * this offset from the card information structure, but I'm + * too lazy and am not quite sure if I understand the CIS anyway. + * + * XXX IF YOU'RE TRYING TO PORT THIS DRIVER FOR A DIFFERENT + * PCMCIA CARD, the most likely thing to change is the constant + * 0x20000 in the next statement. Oh yes, also change the + * card id string that we probe for. + */ + pcic_map_memory (slot, 0, kvtop (isa_dev->id_maddr), 0x20000, 8L, + ATTRIBUTE, 1); + POKE(isa_dev->id_maddr, 0x80); /* reset the card (how long?) */ + DELAY (10000); + /* + * Set the configuration index. According to [1], the adapter won't + * respond to any i/o signals until we do this; it uses the + * Memory Only interface (whatever that is; it's not documented). + * Also turn on "level" (not pulse) interrupts. + * + * XXX probably should init the socket and copy register also, + * so that we can deal with multiple instances of the same card. + */ + POKE(isa_dev->id_maddr, 0x41); + pcic_unmap_memory (slot, 0); + + /* + * (3) now map in the shared memory buffer. This has to be mapped + * as words, not bytes, and on a 16k boundary. The offset value + * was derived by installing IBM's POINTETH.SYS under DOS and + * looking at the PCIC registers; it's not documented in IBM's + * tech ref manual ([1]). + */ + pcic_map_memory (slot, 0, kvtop (isa_dev->id_maddr), 0x4000L, 0x4000L, + COMMON, 2); + + /* + * (4) map i/o ports. + * + * XXX is it possible that the config file leaves this unspecified, + * in which case we have to pick one? + * + * At least one PCMCIA device driver I'v seen maps a block + * of 32 consecutive i/o ports as two windows of 16 ports each. + * Maybe some other pcic chips are restricted to 16-port windows; + * the 82365SL doesn't seem to have that problem. But since + * we have an extra window anyway... + */ +#ifdef SHARED_MEMORY + pcic_map_io (slot, 0, isa_dev->id_iobase, 32, 1); +#else + pcic_map_io (slot, 0, isa_dev->id_iobase, 16, 1); + pcic_map_io (slot, 1, isa_dev->id_iobase+16, 16, 2); +#endif /* SHARED_MEMORY */ + + /* + * (5) configure the card for the desired interrupt + * + * XXX is it possible that the config file leaves this unspecified? + */ + pcic_map_irq (slot, ffs (isa_dev->id_irq) - 1); + + /* tell the PCIC that this is an I/O card (not memory) */ + pcic_putb (slot, PCIC_INT_GEN, + pcic_getb (slot, PCIC_INT_GEN) | PCIC_CARDTYPE); + +#if 0 + /* tell the PCIC to use level-mode interrupts */ + /* XXX this register may not be present on all controllers */ + pcic_putb (slot, PCIC_GLO_CTRL, + pcic_getb (slot, PCIC_GLO_CTRL) | PCIC_LVL_MODE); +#endif + +#if 0 + pcic_print_regs (slot); +#endif + /* + * Setup i/o addresses + */ + sc->nic_addr = isa_dev->id_iobase; +#if 0 + sc->vector = isa_dev->id_irq; +#endif + sc->smem_start = (caddr_t)isa_dev->id_maddr; + +#if 0 + sc->vendor = ZE_VENDOR_IBM; + sc->type = xxx; +#endif + + /* reset card to force it into a known state */ + tmp = inb (isa_dev->id_iobase + ZE_RESET); + DELAY(5000); + outb (isa_dev->id_iobase + ZE_RESET, tmp); + DELAY(5000); + + /* + * query MAM bit in misc register for 10base2 + */ + tmp = inb (isa_dev->id_iobase + ZE_MISC); + sc->mau = tmp & 0x09 ? "10base2" : "10baseT"; + + /* set width/size */ + sc->type_str = "IBM PCMCIA"; + memsize = 16*1024; + sc->memwidth = 16; + + /* allocate 1 xmit buffer */ + sc->smem_ring = sc->smem_start + (ZE_PAGE_SIZE * ZE_TXBUF_SIZE); + sc->txb_cnt = 1; + sc->rec_page_start = ZE_TXBUF_SIZE + ZE_PAGE_OFFSET; + sc->smem_size = memsize; + sc->smem_end = sc->smem_start + memsize; + sc->rec_page_stop = memsize / ZE_PAGE_SIZE + ZE_PAGE_OFFSET; + sc->tx_page_start = ZE_PAGE_OFFSET; + + /* get station address */ + for (i = 0; i < ETHER_ADDR_LEN; ++i) + sc->arpcom.ac_enaddr[i] = enet_addr[i]; + + isa_dev->id_msize = memsize; + return 32; +} + +/* + * Install interface into kernel networking data structures + */ +int +ze_attach(isa_dev) + struct isa_device *isa_dev; +{ + struct ze_softc *sc = &ze_softc[isa_dev->id_unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct ifaddr *ifa; + struct sockaddr_dl *sdl; + + /* + * Set interface to stopped condition (reset) + */ + ze_stop(isa_dev->id_unit); + + /* + * Initialize ifnet structure + */ + ifp->if_unit = isa_dev->id_unit; + ifp->if_name = "ze" ; + ifp->if_mtu = ETHERMTU; + ifp->if_init = ze_init; + ifp->if_output = ether_output; + ifp->if_start = ze_start; + ifp->if_ioctl = ze_ioctl; + ifp->if_reset = ze_reset; + ifp->if_watchdog = ze_watchdog; + + /* + * Set default state for LLC0 flag (used to disable the tranceiver + * for AUI operation), based on compile-time config option. + */ + if (isa_dev->id_flags & ZE_FLAGS_DISABLE_TRANCEIVER) + ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS + | IFF_LLC0); + else + ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS); + + /* + * Attach the interface + */ + if_attach(ifp); + + /* + * Search down the ifa address list looking for the AF_LINK type entry + */ + ifa = ifp->if_addrlist; + while ((ifa != 0) && (ifa->ifa_addr != 0) && + (ifa->ifa_addr->sa_family != AF_LINK)) + ifa = ifa->ifa_next; + /* + * If we find an AF_LINK type entry we fill in the hardware address. + * This is useful for netstat(1) to keep track of which interface + * is which. + */ + if ((ifa != 0) && (ifa->ifa_addr != 0)) { + /* + * Fill in the link-level address for this interface + */ + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = ETHER_ADDR_LEN; + sdl->sdl_slen = 0; + bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN); + } + + /* + * Print additional info when attached + */ + printf("ze%d: address %s, type %s (%dbit)%s, MAU %s\n", + isa_dev->id_unit, + ether_sprintf(sc->arpcom.ac_enaddr), sc->type_str, + sc->memwidth, + (ifp->if_flags & IFF_LLC0 ? " [tranceiver disabled]" : ""), + sc->mau); + + /* + * If BPF is in the kernel, call the attach for it + */ +#if NBPFILTER > 0 + bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + return 1; +} + +/* + * Reset interface. + */ +void +ze_reset(unit) + int unit; +{ + int s; + + s = splnet(); + + /* + * Stop interface and re-initialize. + */ + ze_stop(unit); + ze_init(unit); + + (void) splx(s); +} + +/* + * Take interface offline. + */ +void +ze_stop(unit) + int unit; +{ + struct ze_softc *sc = &ze_softc[unit]; + int n = 5000; + + /* + * Stop everything on the interface, and select page 0 registers. + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STP); + + /* + * Wait for interface to enter stopped state, but limit # of checks + * to 'n' (about 5ms). It shouldn't even take 5us on modern + * DS8390's, but just in case it's an old one. + */ + while (((inb(sc->nic_addr + ZE_P0_ISR) & ZE_ISR_RST) == 0) && --n); + +} + +/* + * Device timeout/watchdog routine. Entered if the device neglects to + * generate an interrupt after a transmit has been started on it. + */ +void +ze_watchdog(unit) + int unit; +{ +#if 1 + struct ze_softc *sc = &ze_softc[unit]; + u_char isr, imr; + u_short imask; + + /* select page zero */ + outb (sc->nic_addr + ZE_P0_CR, + (inb (sc->nic_addr + ZE_P0_CR) & 0x3f) | ZE_CR_PAGE_0); + + /* read interrupt status register */ + isr = inb (sc->nic_addr + ZE_P0_ISR) & 0xff; + + /* select page two */ + outb (sc->nic_addr + ZE_P0_CR, + (inb (sc->nic_addr + ZE_P0_CR) & 0x3f) | ZE_CR_PAGE_2); + + /* read interrupt mask register */ + imr = inb (sc->nic_addr + ZE_P2_IMR) & 0xff; + + imask = inb(IO_ICU2) << 8 | inb(IO_ICU1); + + log (LOG_ERR, "ze%d: device timeout, isr=%02x, imr=%02x, imask=%04x\n", + unit, isr, imr, imask); +#else + log(LOG_ERR, "ze%d: device timeout\n", unit); +#endif + + ze_reset(unit); +} + +/* + * Initialize device. + */ +void +ze_init(unit) + int unit; +{ + struct ze_softc *sc = &ze_softc[unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + int i, s; + u_char command; + + + /* address not known */ + if (ifp->if_addrlist == (struct ifaddr *)0) return; + + /* + * Initialize the NIC in the exact order outlined in the NS manual. + * This init procedure is "mandatory"...don't change what or when + * things happen. + */ + s = splnet(); + + /* reset transmitter flags */ + sc->data_buffered = 0; + sc->xmit_busy = 0; + sc->arpcom.ac_if.if_timer = 0; + + sc->txb_next = 0; + + /* This variable is used below - don't move this assignment */ + sc->next_packet = sc->rec_page_start + 1; + + /* + * Set interface for page 0, Remote DMA complete, Stopped + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STP); + + if (sc->memwidth == 16) { + /* + * Set FIFO threshold to 8, No auto-init Remote DMA, + * byte order=80x86, word-wide DMA xfers + */ + outb(sc->nic_addr + ZE_P0_DCR, ZE_DCR_FT1|ZE_DCR_WTS); + } else { + /* + * Same as above, but byte-wide DMA xfers + */ + outb(sc->nic_addr + ZE_P0_DCR, ZE_DCR_FT1); + } + + /* + * Clear Remote Byte Count Registers + */ + outb(sc->nic_addr + ZE_P0_RBCR0, 0); + outb(sc->nic_addr + ZE_P0_RBCR1, 0); + + /* + * Enable reception of broadcast packets + */ + outb(sc->nic_addr + ZE_P0_RCR, ZE_RCR_AB); + + /* + * Place NIC in internal loopback mode + */ + outb(sc->nic_addr + ZE_P0_TCR, ZE_TCR_LB0); + + /* + * Initialize transmit/receive (ring-buffer) Page Start + */ + outb(sc->nic_addr + ZE_P0_TPSR, sc->tx_page_start); + outb(sc->nic_addr + ZE_P0_PSTART, sc->rec_page_start); + + /* + * Initialize Receiver (ring-buffer) Page Stop and Boundry + */ + outb(sc->nic_addr + ZE_P0_PSTOP, sc->rec_page_stop); + outb(sc->nic_addr + ZE_P0_BNRY, sc->rec_page_start); + + /* + * Clear all interrupts. A '1' in each bit position clears the + * corresponding flag. + */ + outb(sc->nic_addr + ZE_P0_ISR, 0xff); + + /* + * Enable the following interrupts: receive/transmit complete, + * receive/transmit error, and Receiver OverWrite. + * + * Counter overflow and Remote DMA complete are *not* enabled. + */ + outb(sc->nic_addr + ZE_P0_IMR, + ZE_IMR_PRXE|ZE_IMR_PTXE|ZE_IMR_RXEE|ZE_IMR_TXEE|ZE_IMR_OVWE); + + /* + * Program Command Register for page 1 + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_PAGE_1|ZE_CR_RD2|ZE_CR_STP); + + /* + * Copy out our station address + */ + for (i = 0; i < ETHER_ADDR_LEN; ++i) + outb(sc->nic_addr + ZE_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]); + +#if NBPFILTER > 0 + /* + * Initialize multicast address hashing registers to accept + * all multicasts (only used when in promiscuous mode) + */ + for (i = 0; i < 8; ++i) + outb(sc->nic_addr + ZE_P1_MAR0 + i, 0xff); +#endif + + /* + * Set Current Page pointer to next_packet (initialized above) + */ + outb(sc->nic_addr + ZE_P1_CURR, sc->next_packet); + + /* + * Set Command Register for page 0, Remote DMA complete, + * and interface Start. + */ + outb(sc->nic_addr + ZE_P1_CR, ZE_CR_RD2|ZE_CR_STA); + + /* + * Take interface out of loopback + */ + outb(sc->nic_addr + ZE_P0_TCR, 0); + +#if 0 + /* + * If this is a 3Com board, the tranceiver must be software enabled + * (there is no settable hardware default). + */ + if (sc->vendor == ZE_VENDOR_3COM) { + if (ifp->if_flags & IFF_LLC0) { + outb(sc->asic_addr + ZE_3COM_CR, 0); + } else { + outb(sc->asic_addr + ZE_3COM_CR, ZE_3COM_CR_XSEL); + } + } +#endif + + /* + * Set 'running' flag, and clear output active flag. + */ + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* + * ...and attempt to start output + */ + ze_start(ifp); + + (void) splx(s); +} + +/* + * This routine actually starts the transmission on the interface + */ +static inline void +ze_xmit(ifp) + struct ifnet *ifp; +{ + struct ze_softc *sc = &ze_softc[ifp->if_unit]; + u_short len = sc->txb_next_len; + + /* + * Set NIC for page 0 register access + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA); + + /* + * Set TX buffer start page + */ + outb(sc->nic_addr + ZE_P0_TPSR, sc->tx_page_start + + sc->txb_next * ZE_TXBUF_SIZE); + + /* + * Set TX length + */ + outb(sc->nic_addr + ZE_P0_TBCR0, len & 0xff); + outb(sc->nic_addr + ZE_P0_TBCR1, len >> 8); + + /* + * Set page 0, Remote DMA complete, Transmit Packet, and *Start* + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_TXP|ZE_CR_STA); + + sc->xmit_busy = 1; + sc->data_buffered = 0; + + /* + * Switch buffers if we are doing double-buffered transmits + */ + if ((sc->txb_next == 0) && (sc->txb_cnt > 1)) + sc->txb_next = 1; + else + sc->txb_next = 0; + + /* + * Set a timer just in case we never hear from the board again + */ + ifp->if_timer = 2; +} + +/* + * Start output on interface. + * We make two assumptions here: + * 1) that the current priority is set to splnet _before_ this code + * is called *and* is returned to the appropriate priority after + * return + * 2) that the IFF_OACTIVE flag is checked before this code is called + * (i.e. that the output part of the interface is idle) + */ +void +ze_start(ifp) + struct ifnet *ifp; +{ + struct ze_softc *sc = &ze_softc[ifp->if_unit]; + struct mbuf *m0, *m; + caddr_t buffer; + int len; + u_char laar_tmp; + +outloop: + /* + * See if there is room to send more data (i.e. one or both of the + * buffers is empty). + */ + if (sc->data_buffered) + if (sc->xmit_busy) { + /* + * No room. Indicate this to the outside world + * and exit. + */ + ifp->if_flags |= IFF_OACTIVE; + return; + } else { + /* + * Data is buffered, but we're not transmitting, so + * start the xmit on the buffered data. + * Note that ze_xmit() resets the data_buffered flag + * before returning. + */ + ze_xmit(ifp); + } + + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); + if (m == NULL) { + /* + * The following isn't pretty; we are using the !OACTIVE flag to + * indicate to the outside world that we can accept an additional + * packet rather than that the transmitter is _actually_ + * active. Indeed, the transmitter may be active, but if we haven't + * filled the secondary buffer with data then we still want to + * accept more. + * Note that it isn't necessary to test the data_buffered flag - + * we wouldn't have tried to de-queue the packet in the first place + * if it was set. + */ + ifp->if_flags &= ~IFF_OACTIVE; + return; + } + + /* + * Copy the mbuf chain into the transmit buffer + */ +#if 0 + /* + * Enable 16bit access to shared memory on WD/SMC boards + */ + if (sc->memwidth == 16) + if (sc->vendor == ZE_VENDOR_WD_SMC) { + laar_tmp = inb(sc->asic_addr + ZE_WD_LAAR); + outb(sc->asic_addr + ZE_WD_LAAR, laar_tmp | ZE_WD_LAAR_M16EN); + } +#endif + + buffer = sc->smem_start + (sc->txb_next * ZE_TXBUF_SIZE * ZE_PAGE_SIZE); + len = 0; + for (m0 = m; m != 0; m = m->m_next) { + bcopy(mtod(m, caddr_t), buffer, m->m_len); + buffer += m->m_len; + len += m->m_len; + } + +#if 0 + /* + * Restore previous shared mem access type + */ + if (sc->memwidth == 16) + if (sc->vendor == ZE_VENDOR_WD_SMC) { + outb(sc->asic_addr + ZE_WD_LAAR, laar_tmp); + } +#endif + + sc->txb_next_len = MAX(len, ETHER_MIN_LEN); + + if (sc->txb_cnt > 1) + /* + * only set 'buffered' flag if doing multiple buffers + */ + sc->data_buffered = 1; + + if (sc->xmit_busy == 0) + ze_xmit(ifp); + /* + * If there is BPF support in the configuration, tap off here. + * The following has support for converting trailer packets + * back to normal. + */ +#if NBPFILTER > 0 + if (sc->bpf) { + u_short etype; + int off, datasize, resid; + struct ether_header *eh; + struct trailer_header { + u_short ether_type; + u_short ether_residual; + } trailer_header; + char ether_packet[ETHER_MAX_LEN]; + char *ep; + + ep = ether_packet; + + /* + * We handle trailers below: + * Copy ether header first, then residual data, + * then data. Put all this in a temporary buffer + * 'ether_packet' and send off to bpf. Since the + * system has generated this packet, we assume + * that all of the offsets in the packet are + * correct; if they're not, the system will almost + * certainly crash in m_copydata. + * We make no assumptions about how the data is + * arranged in the mbuf chain (i.e. how much + * data is in each mbuf, if mbuf clusters are + * used, etc.), which is why we use m_copydata + * to get the ether header rather than assume + * that this is located in the first mbuf. + */ + /* copy ether header */ + m_copydata(m0, 0, sizeof(struct ether_header), ep); + eh = (struct ether_header *) ep; + ep += sizeof(struct ether_header); + etype = ntohs(eh->ether_type); + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + datasize = ((etype - ETHERTYPE_TRAIL) << 9); + off = datasize + sizeof(struct ether_header); + + /* copy trailer_header into a data structure */ + m_copydata(m0, off, sizeof(struct trailer_header), + &trailer_header.ether_type); + + /* copy residual data */ + m_copydata(m0, off+sizeof(struct trailer_header), + resid = ntohs(trailer_header.ether_residual) - + sizeof(struct trailer_header), ep); + ep += resid; + + /* copy data */ + m_copydata(m0, sizeof(struct ether_header), + datasize, ep); + ep += datasize; + + /* restore original ether packet type */ + eh->ether_type = trailer_header.ether_type; + + bpf_tap(sc->bpf, ether_packet, ep - ether_packet); + } else + bpf_mtap(sc->bpf, m0); + } +#endif + + m_freem(m0); + + /* + * If we are doing double-buffering, a buffer might be free to + * fill with another packet, so loop back to the top. + */ + if (sc->txb_cnt > 1) + goto outloop; + else { + ifp->if_flags |= IFF_OACTIVE; + return; + } +} + +/* + * Ethernet interface receiver interrupt. + */ +static inline void /* only called from one place, so may as well integrate */ +ze_rint(unit) + int unit; +{ + register struct ze_softc *sc = &ze_softc[unit]; + u_char boundry, current; + u_short len; + struct ze_ring *packet_ptr; + + /* + * Set NIC to page 1 registers to get 'current' pointer + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_PAGE_1|ZE_CR_RD2|ZE_CR_STA); + + /* + * 'sc->next_packet' is the logical beginning of the ring-buffer - i.e. + * it points to where new data has been buffered. The 'CURR' + * (current) register points to the logical end of the ring-buffer + * - i.e. it points to where additional new data will be added. + * We loop here until the logical beginning equals the logical + * end (or in other words, until the ring-buffer is empty). + */ + while (sc->next_packet != inb(sc->nic_addr + ZE_P1_CURR)) { + + /* get pointer to this buffer header structure */ + packet_ptr = (struct ze_ring *)(sc->smem_ring + + (sc->next_packet - sc->rec_page_start) * ZE_PAGE_SIZE); + + /* + * The byte count includes the FCS - Frame Check Sequence (a + * 32 bit CRC). + */ + len = packet_ptr->count; + if ((len >= ETHER_MIN_LEN) && (len <= ETHER_MAX_LEN)) { + /* + * Go get packet. len - 4 removes CRC from length. + * (packet_ptr + 1) points to data just after the packet ring + * header (+4 bytes) + */ + ze_get_packet(sc, (caddr_t)(packet_ptr + 1), len - 4); + ++sc->arpcom.ac_if.if_ipackets; + } else { + /* + * Really BAD...probably indicates that the ring pointers + * are corrupted. Also seen on early rev chips under + * high load - the byte order of the length gets switched. + */ + log(LOG_ERR, + "ze%d: shared memory corrupt - invalid packet length %d\n", + unit, len); + ze_reset(unit); + return; + } + + /* + * Update next packet pointer + */ + sc->next_packet = packet_ptr->next_packet; + + /* + * Update NIC boundry pointer - being careful to keep it + * one buffer behind. (as recommended by NS databook) + */ + boundry = sc->next_packet - 1; + if (boundry < sc->rec_page_start) + boundry = sc->rec_page_stop - 1; + + /* + * Set NIC to page 0 registers to update boundry register + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA); + + outb(sc->nic_addr + ZE_P0_BNRY, boundry); + + /* + * Set NIC to page 1 registers before looping to top (prepare to + * get 'CURR' current pointer) + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_PAGE_1|ZE_CR_RD2|ZE_CR_STA); + } +} + +/* + * Ethernet interface interrupt processor + */ +void +zeintr(unit) + int unit; +{ + struct ze_softc *sc = &ze_softc[unit]; + u_char isr; + + /* + * Set NIC to page 0 registers + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA); + + /* + * loop until there are no more new interrupts + */ + while (isr = inb(sc->nic_addr + ZE_P0_ISR)) { + + /* + * reset all the bits that we are 'acknowleging' + * by writing a '1' to each bit position that was set + * (writing a '1' *clears* the bit) + */ + outb(sc->nic_addr + ZE_P0_ISR, isr); + + /* + * Transmit error. If a TX completed with an error, we end up + * throwing the packet away. Really the only error that is + * possible is excessive collisions, and in this case it is + * best to allow the automatic mechanisms of TCP to backoff + * the flow. Of course, with UDP we're screwed, but this is + * expected when a network is heavily loaded. + */ + if (isr & ZE_ISR_TXE) { + u_char tsr = inb(sc->nic_addr + ZE_P0_TSR); + u_char ncr = inb(sc->nic_addr + ZE_P0_NCR); + + /* + * Excessive collisions (16) + */ + if ((tsr & ZE_TSR_ABT) && (ncr == 0)) { + /* + * When collisions total 16, the P0_NCR will + * indicate 0, and the TSR_ABT is set. + */ + sc->arpcom.ac_if.if_collisions += 16; + } else + sc->arpcom.ac_if.if_collisions += ncr; + + /* + * update output errors counter + */ + ++sc->arpcom.ac_if.if_oerrors; + + /* + * reset tx busy and output active flags + */ + sc->xmit_busy = 0; + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + + /* + * clear watchdog timer + */ + sc->arpcom.ac_if.if_timer = 0; + } + + + /* + * Receiver Error. One or more of: CRC error, frame alignment error + * FIFO overrun, or missed packet. + */ + if (isr & ZE_ISR_RXE) { + ++sc->arpcom.ac_if.if_ierrors; +#ifdef ZE_DEBUG +#if 0 + printf("ze%d: receive error %x\n", unit, + inb(sc->nic_addr + ZE_P0_RSR)); +#else + printf("ze%d: receive error %b\n", unit, + inb(sc->nic_addr + ZE_P0_RSR), + "\20\8DEF\7REC DISAB\6PHY/MC\5MISSED\4OVR\3ALIGN\2FCS\1RCVD"); +#endif +#endif + } + + /* + * Overwrite warning. In order to make sure that a lockup + * of the local DMA hasn't occurred, we reset and + * re-init the NIC. The NSC manual suggests only a + * partial reset/re-init is necessary - but some + * chips seem to want more. The DMA lockup has been + * seen only with early rev chips - Methinks this + * bug was fixed in later revs. -DG + */ + if (isr & ZE_ISR_OVW) { + ++sc->arpcom.ac_if.if_ierrors; +#if 0 + /* sigh. this happens too often on our net */ + log(LOG_WARNING, + "ze%d: warning - receiver ring buffer overrun\n", + unit); +#endif + /* + * Stop/reset/re-init NIC + */ + ze_reset(unit); + } + + /* + * Transmission completed normally. + */ + if (isr & ZE_ISR_PTX) { + + /* + * reset tx busy and output active flags + */ + sc->xmit_busy = 0; + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + + /* + * clear watchdog timer + */ + sc->arpcom.ac_if.if_timer = 0; + + /* + * Update total number of successfully transmitted + * packets. + */ + ++sc->arpcom.ac_if.if_opackets; + + /* + * Add in total number of collisions on last + * transmission. + */ + sc->arpcom.ac_if.if_collisions += inb(sc->nic_addr + + ZE_P0_TBCR0); + } + + /* + * Receive Completion. Go and get the packet. + * XXX - Doing this on an error is dubious because there + * shouldn't be any data to get (we've configured the + * interface to not accept packets with errors). + */ + if (isr & (ZE_ISR_PRX|ZE_ISR_RXE)) { +#if 0 + /* + * Enable access to shared memory on WD/SMC boards + */ + if (sc->memwidth == 16) + if (sc->vendor == ZE_VENDOR_WD_SMC) { + outb(sc->asic_addr + ZE_WD_LAAR, + inb(sc->asic_addr + ZE_WD_LAAR) + | ZE_WD_LAAR_M16EN); + } +#endif + ze_rint (unit); + +#if 0 + /* + * Disable access to shared memory + */ + if (sc->memwidth == 16) + if (sc->vendor == ZE_VENDOR_WD_SMC) { + outb(sc->asic_addr + ZE_WD_LAAR, + inb(sc->asic_addr + ZE_WD_LAAR) + & ~ZE_WD_LAAR_M16EN); + } +#endif + } + + /* + * If it looks like the transmitter can take more data, + * attempt to start output on the interface. If data is + * already buffered and ready to go, send it first. + */ + if ((sc->arpcom.ac_if.if_flags & IFF_OACTIVE) == 0) { + if (sc->data_buffered) + ze_xmit(&sc->arpcom.ac_if); + ze_start(&sc->arpcom.ac_if); + } + + /* + * return NIC CR to standard state: page 0, remote DMA complete, + * start (toggling the TXP bit off, even if was just set + * in the transmit routine, is *okay* - it is 'edge' + * triggered from low to high) + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA); + + /* + * If the Network Talley Counters overflow, read them to + * reset them. It appears that old 8390's won't + * clear the ISR flag otherwise - resulting in an + * infinite loop. + */ + if (isr & ZE_ISR_CNT) { + (void) inb(sc->nic_addr + ZE_P0_CNTR0); + (void) inb(sc->nic_addr + ZE_P0_CNTR1); + (void) inb(sc->nic_addr + ZE_P0_CNTR2); + } + } +} + +/* + * Process an ioctl request. This code needs some work - it looks + * pretty ugly. + */ +int +ze_ioctl(ifp, command, data) + register struct ifnet *ifp; + int command; + caddr_t data; +{ + register struct ifaddr *ifa = (struct ifaddr *)data; + struct ze_softc *sc = &ze_softc[ifp->if_unit]; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + s = splnet(); + + switch (command) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + ze_init(ifp->if_unit); /* before arpwhohas */ + /* + * See if another station has *our* IP address. + * i.e.: There is an address conflict! If a + * conflict exists, a message is sent to the + * console. + */ + ((struct arpcom *)ifp)->ac_ipaddr = + IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); + break; +#endif +#ifdef NS + /* + * XXX - This code is probably wrong + */ + case AF_NS: + { + register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + + if (ns_nullhost(*ina)) + ina->x_host = + *(union ns_host *)(sc->arpcom.ac_enaddr); + else { + /* + * + */ + bcopy((caddr_t)ina->x_host.c_host, + (caddr_t)sc->arpcom.ac_enaddr, + sizeof(sc->arpcom.ac_enaddr)); + } + /* + * Set new address + */ + ze_init(ifp->if_unit); + break; + } +#endif + default: + ze_init(ifp->if_unit); + break; + } + break; + + case SIOCSIFFLAGS: + /* + * If interface is marked down and it is running, then stop it + */ + if (((ifp->if_flags & IFF_UP) == 0) && + (ifp->if_flags & IFF_RUNNING)) { + ze_stop(ifp->if_unit); + ifp->if_flags &= ~IFF_RUNNING; + } else { + /* + * If interface is marked up and it is stopped, then start it + */ + if ((ifp->if_flags & IFF_UP) && + ((ifp->if_flags & IFF_RUNNING) == 0)) + ze_init(ifp->if_unit); + } +#if NBPFILTER > 0 + if (ifp->if_flags & IFF_PROMISC) { + /* + * Set promiscuous mode on interface. + * XXX - for multicasts to work, we would need to + * write 1's in all bits of multicast + * hashing array. For now we assume that + * this was done in ze_init(). + */ + outb(sc->nic_addr + ZE_P0_RCR, + ZE_RCR_PRO|ZE_RCR_AM|ZE_RCR_AB); + } else { + /* + * XXX - for multicasts to work, we would need to + * rewrite the multicast hashing array with the + * proper hash (would have been destroyed above). + */ + outb(sc->nic_addr + ZE_P0_RCR, ZE_RCR_AB); + } +#endif +#if 0 + /* + * An unfortunate hack to provide the (required) software control + * of the tranceiver for 3Com boards. The LLC0 flag disables + * the tranceiver if set. + */ + if (sc->vendor == ZE_VENDOR_3COM) { + if (ifp->if_flags & IFF_LLC0) { + outb(sc->asic_addr + ZE_3COM_CR, 0); + } else { + outb(sc->asic_addr + ZE_3COM_CR, ZE_3COM_CR_XSEL); + } + } +#endif + + break; + + default: + error = EINVAL; + } + (void) splx(s); + return (error); +} + +/* + * Macro to calculate a new address within shared memory when given an offset + * from an address, taking into account ring-wrap. + */ +#define ringoffset(sc, start, off, type) \ + ((type)( ((caddr_t)(start)+(off) >= (sc)->smem_end) ? \ + (((caddr_t)(start)+(off))) - (sc)->smem_end \ + + (sc)->smem_ring: \ + ((caddr_t)(start)+(off)) )) + +/* + * Retreive packet from shared memory and send to the next level up via + * ether_input(). If there is a BPF listener, give a copy to BPF, too. + */ +void +ze_get_packet(sc, buf, len) + struct ze_softc *sc; + char *buf; + u_short len; +{ + struct ether_header *eh; + struct mbuf *m, *head = NULL, *ze_ring_to_mbuf(); + u_short off; + int resid; + u_short etype; + struct trailer_header { + u_short trail_type; + u_short trail_residual; + } trailer_header; + + /* Allocate a header mbuf */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + goto bad; + m->m_pkthdr.rcvif = &sc->arpcom.ac_if; + m->m_pkthdr.len = len; + m->m_len = 0; + head = m; + + eh = (struct ether_header *)buf; + + /* The following sillines is to make NFS happy */ +#define EROUND ((sizeof(struct ether_header) + 3) & ~3) +#define EOFF (EROUND - sizeof(struct ether_header)) + + /* + * The following assumes there is room for + * the ether header in the header mbuf + */ + head->m_data += EOFF; + bcopy(buf, mtod(head, caddr_t), sizeof(struct ether_header)); + buf += sizeof(struct ether_header); + head->m_len += sizeof(struct ether_header); + len -= sizeof(struct ether_header); + + etype = ntohs((u_short)eh->ether_type); + + /* + * Deal with trailer protocol: + * If trailer protocol, calculate the datasize as 'off', + * which is also the offset to the trailer header. + * Set resid to the amount of packet data following the + * trailer header. + * Finally, copy residual data into mbuf chain. + */ + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + + off = (etype - ETHERTYPE_TRAIL) << 9; + if ((off + sizeof(struct trailer_header)) > len) + goto bad; /* insanity */ + + eh->ether_type = *ringoffset(sc, buf, off, u_short *); + resid = ntohs(*ringoffset(sc, buf, off+2, u_short *)); + + if ((off + resid) > len) goto bad; /* insanity */ + + resid -= sizeof(struct trailer_header); + if (resid < 0) goto bad; /* insanity */ + + m = ze_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *), head, resid); + if (m == NULL) goto bad; + + len = off; + head->m_pkthdr.len -= 4; /* subtract trailer header */ + } + + /* + * Pull packet off interface. Or if this was a trailer packet, + * the data portion is appended. + */ + m = ze_ring_to_mbuf(sc, buf, m, len); + if (m == NULL) goto bad; + +#if NBPFILTER > 0 + /* + * Check if there's a BPF listener on this interface. + * If so, hand off the raw packet to bpf. + */ + if (sc->bpf) { + bpf_mtap(sc->bpf, head); + + /* + * Note that the interface cannot be in promiscuous mode if + * there are no BPF listeners. And if we are in promiscuous + * mode, we have to check if this packet is really ours. + * + * XXX This test does not support multicasts. + */ + if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && + bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, + sizeof(eh->ether_dhost)) != 0 && + bcmp(eh->ether_dhost, etherbroadcastaddr, + sizeof(eh->ether_dhost)) != 0) { + + m_freem(head); + return; + } + } +#endif + + /* + * Fix up data start offset in mbuf to point past ether header + */ + m_adj(head, sizeof(struct ether_header)); + + /* + * silly ether_input routine needs 'type' in host byte order + */ + eh->ether_type = ntohs(eh->ether_type); + + ether_input(&sc->arpcom.ac_if, eh, head); + return; + +bad: if (head) + m_freem(head); + return; +} + +/* + * Supporting routines + */ + +/* + * Given a source and destination address, copy 'amount' of a packet from + * the ring buffer into a linear destination buffer. Takes into account + * ring-wrap. + */ +static inline char * +ze_ring_copy(sc,src,dst,amount) + struct ze_softc *sc; + char *src; + char *dst; + u_short amount; +{ + u_short tmp_amount; + + /* does copy wrap to lower addr in ring buffer? */ + if (src + amount > sc->smem_end) { + tmp_amount = sc->smem_end - src; + bcopy(src,dst,tmp_amount); /* copy amount up to end of smem */ + amount -= tmp_amount; + src = sc->smem_ring; + dst += tmp_amount; + } + + bcopy(src, dst, amount); + + return(src + amount); +} + +/* + * Copy data from receive buffer to end of mbuf chain + * allocate additional mbufs as needed. return pointer + * to last mbuf in chain. + * sc = ze info (softc) + * src = pointer in ze ring buffer + * dst = pointer to last mbuf in mbuf chain to copy to + * amount = amount of data to copy + */ +struct mbuf * +ze_ring_to_mbuf(sc,src,dst,total_len) + struct ze_softc *sc; + char *src; + struct mbuf *dst; + u_short total_len; +{ + register struct mbuf *m = dst; + + while (total_len) { + register u_short amount = min(total_len, M_TRAILINGSPACE(m)); + + if (amount == 0) { /* no more data in this mbuf, alloc another */ + /* + * If there is enough data for an mbuf cluster, attempt + * to allocate one of those, otherwise, a regular + * mbuf will do. + * Note that a regular mbuf is always required, even if + * we get a cluster - getting a cluster does not + * allocate any mbufs, and one is needed to assign + * the cluster to. The mbuf that has a cluster + * extension can not be used to contain data - only + * the cluster can contain data. + */ + dst = m; + MGET(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return (0); + + if (total_len >= MINCLSIZE) + MCLGET(m, M_DONTWAIT); + + m->m_len = 0; + dst->m_next = m; + amount = min(total_len, M_TRAILINGSPACE(m)); + } + + src = ze_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount); + + m->m_len += amount; + total_len -= amount; + + } + return (m); +} +#endif + diff --git a/sys/i386/isa/if_zereg.h b/sys/i386/isa/if_zereg.h new file mode 100644 index 000000000000..3cd501f682cb --- /dev/null +++ b/sys/i386/isa/if_zereg.h @@ -0,0 +1,859 @@ +/* + * National Semiconductor DS8390 NIC register definitions + * + * if_edreg.h,v + * Revision 1.1.2.1 1993/07/21 13:50:04 cgd + * from davidg: + * Added config file override for memory size and added flags to force + * 8bit or 16bit operation, and a flag to disable transmitter double buffering. + * See the updated "ed.relnotes" file for information about how to set + * the flags. + * This should be considered the first "production" release. It still + * needs a manual page, though. + * + * Revision 1.1 1993/07/03 12:21:07 cgd + * add support for David Greenman "ed" driver + * + * Revision 1.2 93/06/23 03:03:05 davidg + * added some additional definitions for the 83C584 bus interface + * chip (SMC/WD boards) + * + * Revision 1.1 93/06/23 03:01:07 davidg + * Initial revision + * + */ + +/* + * Page 0 register offsets + */ +#define ZE_P0_CR 0x00 /* Command Register */ + +#define ZE_P0_CLDA0 0x01 /* Current Local DMA Addr low (read) */ +#define ZE_P0_PSTART 0x01 /* Page Start register (write) */ + +#define ZE_P0_CLDA1 0x02 /* Current Local DMA Addr high (read) */ +#define ZE_P0_PSTOP 0x02 /* Page Stop register (write) */ + +#define ZE_P0_BNRY 0x03 /* Boundary Pointer */ + +#define ZE_P0_TSR 0x04 /* Transmit Status Register (read) */ +#define ZE_P0_TPSR 0x04 /* Transmit Page Start (write) */ + +#define ZE_P0_NCR 0x05 /* Number of Collisions Reg (read) */ +#define ZE_P0_TBCR0 0x05 /* Transmit Byte count, low (write) */ + +#define ZE_P0_FIFO 0x06 /* FIFO register (read) */ +#define ZE_P0_TBCR1 0x06 /* Transmit Byte count, high (write) */ + +#define ZE_P0_ISR 0x07 /* Interrupt Status Register */ + +#define ZE_P0_CRDA0 0x08 /* Current Remote DMA Addr low (read) */ +#define ZE_P0_RSAR0 0x08 /* Remote Start Address low (write) */ + +#define ZE_P0_CRDA1 0x09 /* Current Remote DMA Addr high (read) */ +#define ZE_P0_RSAR1 0x09 /* Remote Start Address high (write) */ + +#define ZE_P0_RBCR0 0x0a /* Remote Byte Count low (write) */ + +#define ZE_P0_RBCR1 0x0b /* Remote Byte Count high (write) */ + +#define ZE_P0_RSR 0x0c /* Receive Status (read) */ +#define ZE_P0_RCR 0x0c /* Receive Configuration Reg (write) */ + +#define ZE_P0_CNTR0 0x0d /* frame alignment error counter (read) */ +#define ZE_P0_TCR 0x0d /* Transmit Configuration Reg (write) */ + +#define ZE_P0_CNTR1 0x0e /* CRC error counter (read) */ +#define ZE_P0_DCR 0x0e /* Data Configuration Reg (write) */ + +#define ZE_P0_CNTR2 0x0f /* missed packet counter (read) */ +#define ZE_P0_IMR 0x0f /* Interrupt Mask Register (write) */ + +/* + * Page 1 register offsets + */ +#define ZE_P1_CR 0x00 /* Command Register */ +#define ZE_P1_PAR0 0x01 /* Physical Address Register 0 */ +#define ZE_P1_PAR1 0x02 /* Physical Address Register 1 */ +#define ZE_P1_PAR2 0x03 /* Physical Address Register 2 */ +#define ZE_P1_PAR3 0x04 /* Physical Address Register 3 */ +#define ZE_P1_PAR4 0x05 /* Physical Address Register 4 */ +#define ZE_P1_PAR5 0x06 /* Physical Address Register 5 */ +#define ZE_P1_CURR 0x07 /* Current RX ring-buffer page */ +#define ZE_P1_MAR0 0x08 /* Multicast Address Register 0 */ +#define ZE_P1_MAR1 0x09 /* Multicast Address Register 1 */ +#define ZE_P1_MAR2 0x0a /* Multicast Address Register 2 */ +#define ZE_P1_MAR3 0x0b /* Multicast Address Register 3 */ +#define ZE_P1_MAR4 0x0c /* Multicast Address Register 4 */ +#define ZE_P1_MAR5 0x0d /* Multicast Address Register 5 */ +#define ZE_P1_MAR6 0x0e /* Multicast Address Register 6 */ +#define ZE_P1_MAR7 0x0f /* Multicast Address Register 7 */ + +/* + * Page 2 register offsets + */ +#define ZE_P2_CR 0x00 /* Command Register */ +#define ZE_P2_PSTART 0x01 /* Page Start (read) */ +#define ZE_P2_CLDA0 0x01 /* Current Local DMA Addr 0 (write) */ +#define ZE_P2_PSTOP 0x02 /* Page Stop (read) */ +#define ZE_P2_CLDA1 0x02 /* Current Local DMA Addr 1 (write) */ +#define ZE_P2_RNPP 0x03 /* Remote Next Packet Pointer */ +#define ZE_P2_TPSR 0x04 /* Transmit Page Start (read) */ +#define ZE_P2_LNPP 0x05 /* Local Next Packet Pointer */ +#define ZE_P2_ACU 0x06 /* Address Counter Upper */ +#define ZE_P2_ACL 0x07 /* Address Counter Lower */ +#define ZE_P2_RCR 0x0c /* Receive Configuration Register (read) */ +#define ZE_P2_TCR 0x0d /* Transmit Configuration Register (read) */ +#define ZE_P2_DCR 0x0e /* Data Configuration Register (read) */ +#define ZE_P2_IMR 0x0f /* Interrupt Mask Register (read) */ + +/* + * Command Register (CR) definitions + */ + +/* + * STP: SToP. Software reset command. Takes the controller offline. No + * packets will be received or transmitted. Any reception or + * transmission in progress will continue to completion before + * entering reset state. To exit this state, the STP bit must + * reset and the STA bit must be set. The software reset has + * executed only when indicated by the RST bit in the ISR being + * set. + */ +#define ZE_CR_STP 0x01 + +/* + * STA: STArt. This bit is used to activate the NIC after either power-up, + * or when the NIC has been put in reset mode by software command + * or error. + */ +#define ZE_CR_STA 0x02 + +/* + * TXP: Transmit Packet. This bit must be set to indicate transmission of + * a packet. TXP is internally reset either after the transmission is + * completed or aborted. This bit should be set only after the Transmit + * Byte Count and Transmit Page Start register have been programmed. + */ +#define ZE_CR_TXP 0x04 + +/* + * RD0, RD1, RD2: Remote DMA Command. These three bits control the operation + * of the remote DMA channel. RD2 can be set to abort any remote DMA + * command in progress. The Remote Byte Count registers should be cleared + * when a remote DMA has been aborted. The Remote Start Addresses are not + * restored to the starting address if the remote DMA is aborted. + * + * RD2 RD1 RD0 function + * 0 0 0 not allowed + * 0 0 1 remote read + * 0 1 0 remote write + * 0 1 1 send packet + * 1 X X abort + */ +#define ZE_CR_RD0 0x08 +#define ZE_CR_RD1 0x10 +#define ZE_CR_RD2 0x20 + +/* + * PS0, PS1: Page Select. The two bits select which register set or 'page' to + * access. + * + * PS1 PS0 page + * 0 0 0 + * 0 1 1 + * 1 0 2 + * 1 1 reserved + */ +#define ZE_CR_PS0 0x40 +#define ZE_CR_PS1 0x80 +/* bit encoded aliases */ +#define ZE_CR_PAGE_0 0x00 /* (for consistency) */ +#define ZE_CR_PAGE_1 0x40 +#define ZE_CR_PAGE_2 0x80 + +/* + * Interrupt Status Register (ISR) definitions + */ + +/* + * PRX: Packet Received. Indicates packet received with no errors. + */ +#define ZE_ISR_PRX 0x01 + +/* + * PTX: Packet Transmitted. Indicates packet transmitted with no errors. + */ +#define ZE_ISR_PTX 0x02 + +/* + * RXE: Receive Error. Indicates that a packet was received with one or more + * the following errors: CRC error, frame alignment error, FIFO overrun, + * missed packet. + */ +#define ZE_ISR_RXE 0x04 + +/* + * TXE: Transmission Error. Indicates that an attempt to transmit a packet + * resulted in one or more of the following errors: excessive + * collisions, FIFO underrun. + */ +#define ZE_ISR_TXE 0x08 + +/* + * OVW: OverWrite. Indicates a receive ring-buffer overrun. Incoming network + * would exceed (has exceeded?) the boundry pointer, resulting in data + * that was previously received and not yet read from the buffer to be + * overwritten. + */ +#define ZE_ISR_OVW 0x10 + +/* + * CNT: Counter Overflow. Set when the MSB of one or more of the Network Talley + * Counters has been set. + */ +#define ZE_ISR_CNT 0x20 + +/* + * RDC: Remote Data Complete. Indicates that a Remote DMA operation has completed. + */ +#define ZE_ISR_RDC 0x40 + +/* + * RST: Reset status. Set when the NIC enters the reset state and cleared when a + * Start Command is issued to the CR. This bit is also set when a receive + * ring-buffer overrun (OverWrite) occurs and is cleared when one or more + * packets have been removed from the ring. This is a read-only bit. + */ +#define ZE_ISR_RST 0x80 + +/* + * Interrupt Mask Register (IMR) definitions + */ + +/* + * PRXE: Packet Received interrupt Enable. If set, a received packet will cause + * an interrupt. + */ +#define ZE_IMR_PRXE 0x01 + +/* + * PTXE: Packet Transmit interrupt Enable. If set, an interrupt is generated when + * a packet transmission completes. + */ +#define ZE_IMR_PTXE 0x02 + +/* + * RXEE: Receive Error interrupt Enable. If set, an interrupt will occur whenever a + * packet is received with an error. + */ +#define ZE_IMR_RXEE 0x04 + +/* + * TXEE: Transmit Error interrupt Enable. If set, an interrupt will occur whenever + * a transmission results in an error. + */ +#define ZE_IMR_TXEE 0x08 + +/* + * OVWE: OverWrite error interrupt Enable. If set, an interrupt is generated whenever + * the receive ring-buffer is overrun. i.e. when the boundry pointer is exceeded. + */ +#define ZE_IMR_OVWE 0x10 + +/* + * CNTE: Counter overflow interrupt Enable. If set, an interrupt is generated whenever + * the MSB of one or more of the Network Statistics counters has been set. + */ +#define ZE_IMR_CNTE 0x20 + +/* + * RDCE: Remote DMA Complete interrupt Enable. If set, an interrupt is generated + * when a remote DMA transfer has completed. + */ +#define ZE_IMR_RDCE 0x40 + +/* + * bit 7 is unused/reserved + */ + +/* + * Data Configuration Register (DCR) definitions + */ + +/* + * WTS: Word Transfer Select. WTS establishes byte or word transfers for + * both remote and local DMA transfers + */ +#define ZE_DCR_WTS 0x01 + +/* + * BOS: Byte Order Select. BOS sets the byte order for the host. + * Should be 0 for 80x86, and 1 for 68000 series processors + */ +#define ZE_DCR_BOS 0x02 + +/* + * LAS: Long Address Select. When LAS is 1, the contents of the remote + * DMA registers RSAR0 and RSAR1 are used to provide A16-A31 + */ +#define ZE_DCR_LAS 0x04 + +/* + * LS: Loopback Select. When 0, loopback mode is selected. Bits D1 and D2 + * of the TCR must also be programmed for loopback operation. + * When 1, normal operation is selected. + */ +#define ZE_DCR_LS 0x08 + +/* + * AR: Auto-initialize Remote. When 0, data must be removed from ring-buffer + * under program control. When 1, remote DMA is automatically initiated + * and the boundry pointer is automatically updated + */ +#define ZE_DCR_AR 0x10 + +/* + * FT0, FT1: Fifo Threshold select. + * FT1 FT0 Word-width Byte-width + * 0 0 1 word 2 bytes + * 0 1 2 words 4 bytes + * 1 0 4 words 8 bytes + * 1 1 8 words 12 bytes + * + * During transmission, the FIFO threshold indicates the number of bytes + * or words that the FIFO has filled from the local DMA before BREQ is + * asserted. The transmission threshold is 16 bytes minus the receiver + * threshold. + */ +#define ZE_DCR_FT0 0x20 +#define ZE_DCR_FT1 0x40 + +/* + * bit 7 (0x80) is unused/reserved + */ + +/* + * Transmit Configuration Register (TCR) definitions + */ + +/* + * CRC: Inhibit CRC. If 0, CRC will be appended by the transmitter, if 0, CRC + * is not appended by the transmitter. + */ +#define ZE_TCR_CRC 0x01 + +/* + * LB0, LB1: Loopback control. These two bits set the type of loopback that is + * to be performed. + * + * LB1 LB0 mode + * 0 0 0 - normal operation (DCR_LS = 0) + * 0 1 1 - internal loopback (DCR_LS = 0) + * 1 0 2 - external loopback (DCR_LS = 1) + * 1 1 3 - external loopback (DCR_LS = 0) + */ +#define ZE_TCR_LB0 0x02 +#define ZE_TCR_LB1 0x04 + +/* + * ATD: Auto Transmit Disable. Clear for normal operation. When set, allows + * another station to disable the NIC's transmitter by transmitting to + * a multicast address hashing to bit 62. Reception of a multicast address + * hashing to bit 63 enables the transmitter. + */ +#define ZE_TCR_ATD 0x08 + +/* + * OFST: Collision Offset enable. This bit when set modifies the backoff + * algorithm to allow prioritization of nodes. + */ +#define ZE_TCR_OFST 0x10 + +/* + * bits 5, 6, and 7 are unused/reserved + */ + +/* + * Transmit Status Register (TSR) definitions + */ + +/* + * PTX: Packet Transmitted. Indicates successful transmission of packet. + */ +#define ZE_TSR_PTX 0x01 + +/* + * bit 1 (0x02) is unused/reserved + */ + +/* + * COL: Transmit Collided. Indicates that the transmission collided at least + * once with another station on the network. + */ +#define ZE_TSR_COL 0x04 + +/* + * ABT: Transmit aborted. Indicates that the transmission was aborted due to + * excessive collisions. + */ +#define ZE_TSR_ABT 0x08 + +/* + * CRS: Carrier Sense Lost. Indicates that carrier was lost during the + * transmission of the packet. (Transmission is not aborted because + * of a loss of carrier) + */ +#define ZE_TSR_CRS 0x10 + +/* + * FU: FIFO Underrun. Indicates that the NIC wasn't able to access bus/ + * transmission memory before the FIFO emptied. Transmission of the + * packet was aborted. + */ +#define ZE_TSR_FU 0x20 + +/* + * CDH: CD Heartbeat. Indicates that the collision detection circuitry + * isn't working correctly during a collision heartbeat test. + */ +#define ZE_TSR_CDH 0x40 + +/* + * OWC: Out of Window Collision: Indicates that a collision occurred after + * a slot time (51.2us). The transmission is rescheduled just as in + * normal collisions. + */ +#define ZE_TSR_OWC 0x80 + +/* + * Receiver Configuration Register (RCR) definitions + */ + +/* + * SEP: Save Errored Packets. If 0, error packets are discarded. If set to 1, + * packets with CRC and frame errors are not discarded. + */ +#define ZE_RCR_SEP 0x01 + +/* + * AR: Accept Runt packet. If 0, packet with less than 64 byte are discarded. + * If set to 1, packets with less than 64 byte are not discarded. + */ +#define ZE_RCR_AR 0x02 + +/* + * AB: Accept Broadcast. If set, packets sent to the broadcast address will be + * accepted. + */ +#define ZE_RCR_AB 0x04 + +/* + * AM: Accept Multicast. If set, packets sent to a multicast address are checked + * for a match in the hashing array. If clear, multicast packets are ignored. + */ +#define ZE_RCR_AM 0x08 + +/* + * PRO: Promiscuous Physical. If set, all packets with a physical addresses are + * accepted. If clear, a physical destination address must match this + * station's address. Note: for full promiscuous mode, RCR_AB and RCR_AM + * must also be set. In addition, the multicast hashing array must be set + * to all 1's so that all multicast addresses are accepted. + */ +#define ZE_RCR_PRO 0x10 + +/* + * MON: Monitor Mode. If set, packets will be checked for good CRC and framing, + * but are not stored in the ring-buffer. If clear, packets are stored (normal + * operation). + */ +#define ZE_RCR_MON 0x20 + +/* + * bits 6 and 7 are unused/reserved. + */ + +/* + * Receiver Status Register (RSR) definitions + */ + +/* + * PRX: Packet Received without error. + */ +#define ZE_RSR_PRX 0x01 + +/* + * CRC: CRC error. Indicates that a packet has a CRC error. Also set for frame + * alignment errors. + */ +#define ZE_RSR_CRC 0x02 + +/* + * FAE: Frame Alignment Error. Indicates that the incoming packet did not end on + * a byte boundry and the CRC did not match at the last byte boundry. + */ +#define ZE_RSR_FAE 0x04 + +/* + * FO: FIFO Overrun. Indicates that the FIFO was not serviced (during local DMA) + * causing it to overrun. Reception of the packet is aborted. + */ +#define ZE_RSR_FO 0x08 + +/* + * MPA: Missed Packet. Indicates that the received packet couldn't be stored in + * the ring-buffer because of insufficient buffer space (exceeding the + * boundry pointer), or because the transfer to the ring-buffer was inhibited + * by RCR_MON - monitor mode. + */ +#define ZE_RSR_MPA 0x10 + +/* + * PHY: Physical address. If 0, the packet received was sent to a physical address. + * If 1, the packet was accepted because of a multicast/broadcast address + * match. + */ +#define ZE_RSR_PHY 0x20 + +/* + * DIS: Receiver Disabled. Set to indicate that the receiver has enetered monitor + * mode. Cleared when the receiver exits monitor mode. + */ +#define ZE_RSR_DIS 0x40 + +/* + * DFR: Deferring. Set to indicate a 'jabber' condition. The CRS and COL inputs + * are active, and the transceiver has set the CD line as a result of the + * jabber. + */ +#define ZE_RSR_DFR 0x80 + +/* + * receive ring discriptor + * + * The National Semiconductor DS8390 Network interface controller uses + * the following receive ring headers. The way this works is that the + * memory on the interface card is chopped up into 256 bytes blocks. + * A contiguous portion of those blocks are marked for receive packets + * by setting start and end block #'s in the NIC. For each packet that + * is put into the receive ring, one of these headers (4 bytes each) is + * tacked onto the front. + */ +struct ze_ring { + struct edr_status { /* received packet status */ + u_char rs_prx:1, /* packet received intack */ + rs_crc:1, /* crc error */ + rs_fae:1, /* frame alignment error */ + rs_fo:1, /* fifo overrun */ + rs_mpa:1, /* packet received intack */ + rs_phy:1, /* packet received intack */ + rs_dis:1, /* packet received intack */ + rs_dfr:1; /* packet received intack */ + } ze_rcv_status; /* received packet status */ + u_char next_packet; /* pointer to next packet */ + u_short count; /* bytes in packet (length + 4) */ +}; + +/* + * Common constants + */ +#define ZE_PAGE_SIZE 256 /* Size of RAM pages in bytes */ +#define ZE_TXBUF_SIZE 6 /* Size of TX buffer in pages */ +#define ZE_PAGE_OFFSET 0x40 /* mem buffer starts at 0x4000 */ + +/* + * Vendor types + */ +#define ZE_VENDOR_WD_SMC 0x00 /* Western Digital/SMC */ +#define ZE_VENDOR_3COM 0x01 /* 3Com */ + +/* + * Compile-time config flags + */ +/* + * this sets the default for enabling/disablng the tranceiver + */ +#define ZE_FLAGS_DISABLE_TRANCEIVER 0x01 + +/* + * This forces the board to be used in 8/16bit mode even if it + * autoconfigs differently + */ +#define ZE_FLAGS_FORCE_8BIT_MODE 0x02 +#define ZE_FLAGS_FORCE_16BIT_MODE 0x04 + +/* + * This disables the use of double transmit buffers. + */ +#define ZE_FLAGS_NO_DOUBLE_BUFFERING 0x08 + +/* + * definitions for IBM credit card adapter for ethernet + */ + +#define ZE_DATA_IO 0x10 +#define ZE_MISC 0x18 +#define ZE_RESET 0x1F + +#if 0 +/* + * Definitions for Western digital/SMC WD80x3 series ASIC + */ +/* + * Memory Select Register (MSR) + */ +#define ZE_WD_MSR 0 + +#define ZE_WD_MSR_ADDR 0x3f /* Memory decode bits 18-13 */ +#define ZE_WD_MSR_MENB 0x40 /* Memory enable */ +#define ZE_WD_MSR_RST 0x80 /* Reset board */ + +/* + * Interface Configuration Register (ICR) + */ +#define ZE_WD_ICR 1 + +#define ZE_WD_ICR_16BIT 0x01 /* 16-bit interface */ +#define ZE_WD_ICR_OAR 0x02 /* select register. 0=BIO 1=EAR */ +#define ZE_WD_ICR_IR2 0x04 /* high order bit of encoded IRQ */ +#define ZE_WD_ICR_MSZ 0x08 /* memory size (0=8k 1=32k) */ +#define ZE_WD_ICR_RLA 0x10 /* recall LAN address */ +#define ZE_WD_ICR_RX7 0x20 /* recall all but i/o and LAN address */ +#define ZE_WD_ICR_RIO 0x40 /* recall i/o address */ +#define ZE_WD_ICR_STO 0x80 /* store to non-volatile memory */ + +/* + * IO Address Register (IAR) + */ +#define ZE_WD_IAR 2 + +/* + * EEROM Address Register + */ +#define ZE_WD_EAR 3 + +/* + * Interrupt Request Register (IRR) + */ +#define ZE_WD_IRR 4 + +#define ZE_WD_IRR_0WS 0x01 /* use 0 wait-states on 8 bit bus */ +#define ZE_WD_IRR_OUT1 0x02 /* WD83C584 pin 1 output */ +#define ZE_WD_IRR_OUT2 0x04 /* WD83C584 pin 2 output */ +#define ZE_WD_IRR_OUT3 0x08 /* WD83C584 pin 3 output */ +#define ZE_WD_IRR_FLASH 0x10 /* Flash RAM is in the ROM socket */ + +/* + * The three bit of the encoded IRQ are decoded as follows: + * + * IR2 IR1 IR0 IRQ + * 0 0 0 2/9 + * 0 0 1 3 + * 0 1 0 5 + * 0 1 1 7 + * 1 0 0 10 + * 1 0 1 11 + * 1 1 0 15 + * 1 1 1 4 + */ +#define ZE_WD_IRR_IR0 0x20 /* bit 0 of encoded IRQ */ +#define ZE_WD_IRR_IR1 0x40 /* bit 1 of encoded IRQ */ +#define ZE_WD_IRR_IEN 0x80 /* Interrupt enable */ + +/* + * LA Address Register (LAAR) + */ +#define ZE_WD_LAAR 5 + +#define ZE_WD_LAAR_ADDRHI 0x1f /* bits 23-19 of RAM address */ +#define ZE_WD_LAAR_0WS16 0x20 /* enable 0 wait-states on 16 bit bus */ +#define ZE_WD_LAAR_L16EN 0x40 /* enable 16-bit operation */ +#define ZE_WD_LAAR_M16EN 0x80 /* enable 16-bit memory access */ + +/* i/o base offset to station address/card-ID PROM */ +#define ZE_WD_PROM 8 + +/* i/o base offset to CARD ID */ +#define ZE_WD_CARD_ID ZE_WD_PROM+6 + +#define ZE_TYPE_WD8003S 0x02 +#define ZE_TYPE_WD8003E 0x03 +#define ZE_TYPE_WD8013EBT 0x05 +#define ZE_TYPE_WD8013EB 0x27 +#define ZE_TYPE_WD8013EBP 0x2c +#define ZE_TYPE_WD8013EPC 0x29 + +/* Bit definitions in card ID */ +#define ZE_WD_REV_MASK 0x1f /* Revision mask */ +#define ZE_WD_SOFTCONFIG 0x20 /* Soft config */ +#define ZE_WD_LARGERAM 0x40 /* Large RAM */ +#define ZE_MICROCHANEL 0x80 /* Microchannel bus (vs. isa) */ + +/* + * Checksum total. All 8 bytes in station address PROM will add up to this + */ +#define ZE_WD_ROM_CHECKSUM_TOTAL 0xFF + +#define ZE_WD_NIC_OFFSET 0x10 /* I/O base offset to NIC */ +#define ZE_WD_ASIC_OFFSET 0 /* I/O base offset to ASIC */ +#define ZE_WD_IO_PORTS 32 /* # of i/o addresses used */ + +#define ZE_WD_PAGE_OFFSET 0 /* page offset for NIC access to mem */ + +/* + * Definitions for 3Com 3c503 + */ +#define ZE_3COM_NIC_OFFSET 0 +#define ZE_3COM_ASIC_OFFSET 0x400 /* offset to nic i/o regs */ + +/* + * XXX - The I/O address range is fragmented in the 3c503; this is the + * number of regs at iobase. + */ +#define ZE_3COM_IO_PORTS 16 /* # of i/o addresses used */ + +#define ZE_3COM_PAGE_OFFSET 0x20 /* memory starts in second bank */ + +/* + * Page Start Register. Must match PSTART in NIC + */ +#define ZE_3COM_PSTR 0 + +/* + * Page Stop Register. Must match PSTOP in NIC + */ +#define ZE_3COM_PSPR 1 + +/* + * Drq Timer Register. Determines number of bytes to be transfered during + * a DMA burst. + */ +#define ZE_3COM_DQTR 2 + +/* + * Base Configuration Register. Read-only register which contains the + * board-configured I/O base address of the adapter. Bit encoded. + */ +#define ZE_3COM_BCFR 3 + +#define ZE_3COM_BCFR_2E0 0x01 +#define ZE_3COM_BCFR_2A0 0x02 +#define ZE_3COM_BCFR_280 0x04 +#define ZE_3COM_BCFR_250 0x08 +#define ZE_3COM_BCFR_350 0x10 +#define ZE_3COM_BCFR_330 0x20 +#define ZE_3COM_BCFR_310 0x40 +#define ZE_3COM_BCFR_300 0x80 + +/* + * EPROM Configuration Register. Read-only register which contains the + * board-configured memory base address. Bit encoded. + */ +#define ZE_3COM_PCFR 4 + +#define ZE_3COM_PCFR_C8000 0x10 +#define ZE_3COM_PCFR_CC000 0x20 +#define ZE_3COM_PCFR_D8000 0x40 +#define ZE_3COM_PCFR_DC000 0x80 + +/* + * GA Configuration Register. Gate-Array Configuration Register. + */ +#define ZE_3COM_GACFR 5 + +/* + * mbs2 mbs1 mbs0 start address + * 0 0 0 0x0000 + * 0 0 1 0x2000 + * 0 1 0 0x4000 + * 0 1 1 0x6000 + * + * Note that with adapters with only 8K, the setting for 0x2000 must + * always be used. + */ +#define ZE_3COM_GACFR_MBS0 0x01 +#define ZE_3COM_GACFR_MBS1 0x02 +#define ZE_3COM_GACFR_MBS2 0x04 + +#define ZE_3COM_GACFR_RSEL 0x08 /* enable shared memory */ +#define ZE_3COM_GACFR_TEST 0x10 /* for GA testing */ +#define ZE_3COM_GACFR_OWS 0x20 /* select 0WS access to GA */ +#define ZE_3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */ +#define ZE_3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */ + +/* + * Control Register. Miscellaneous control functions. + */ +#define ZE_3COM_CR 6 + +#define ZE_3COM_CR_RST 0x01 /* Reset GA and NIC */ +#define ZE_3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */ +#define ZE_3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */ +#define ZE_3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */ +#define ZE_3COM_CR_SHARE 0x10 /* select interrupt sharing option */ +#define ZE_3COM_CR_DBSEL 0x20 /* Double buffer select */ +#define ZE_3COM_CR_DDIR 0x40 /* DMA direction select */ +#define ZE_3COM_CR_START 0x80 /* Start DMA controller */ + +/* + * Status Register. Miscellaneous status information. + */ +#define ZE_3COM_STREG 7 + +#define ZE_3COM_STREG_REV 0x07 /* GA revision */ +#define ZE_3COM_STREG_DIP 0x08 /* DMA in progress */ +#define ZE_3COM_STREG_DTC 0x10 /* DMA terminal count */ +#define ZE_3COM_STREG_OFLW 0x20 /* Overflow */ +#define ZE_3COM_STREG_UFLW 0x40 /* Underflow */ +#define ZE_3COM_STREG_DPRDY 0x80 /* Data port ready */ + +/* + * Interrupt/DMA Configuration Register + */ +#define ZE_3COM_IDCFR 8 + +#define ZE_3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */ +#define ZE_3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */ +#define ZE_3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */ +#define ZE_3COM_IDCFR_UNUSED 0x08 /* not used */ +#define ZE_3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */ +#define ZE_3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */ +#define ZE_3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */ +#define ZE_3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */ + +/* + * DMA Address Register MSB + */ +#define ZE_3COM_DAMSB 9 + +/* + * DMA Address Register LSB + */ +#define ZE_3COM_DALSB 0x0a + +/* + * Vector Pointer Register 2 + */ +#define ZE_3COM_VPTR2 0x0b + +/* + * Vector Pointer Register 1 + */ +#define ZE_3COM_VPTR1 0x0c + +/* + * Vector Pointer Register 0 + */ +#define ZE_3COM_VPTR0 0x0d + +/* + * Register File Access MSB + */ +#define ZE_3COM_RFMSB 0x0e + +/* + * Register File Access LSB + */ +#define ZE_3COM_RFLSB 0x0f +#endif diff --git a/sys/i386/isa/ipl.h b/sys/i386/isa/ipl.h new file mode 100644 index 000000000000..248ca5666d67 --- /dev/null +++ b/sys/i386/isa/ipl.h @@ -0,0 +1,7 @@ +#ifndef _ISA_IPL_H_ +#define _ISA_IPL_H_ + +#define NHWI 16 /* number of h/w interrupts */ +#define HWI_MASK 0xffff /* bits corresponding to h/w interrupts */ + +#endif /* _ISA_IPL_H_ */ diff --git a/sys/i386/isa/isa.c b/sys/i386/isa/isa.c index 3dbc748d9d55..588be2cccc7f 100644 --- a/sys/i386/isa/isa.c +++ b/sys/i386/isa/isa.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 - * $Id: isa.c,v 1.14 1994/01/22 21:52:04 rgrimes Exp $ + * $Id: isa.c,v 1.19 1994/06/22 05:52:39 jkh Exp $ */ /* @@ -124,9 +124,11 @@ haveseen(dvp, tmpdvp) if ((dvp->id_iobase >= tmpdvp->id_iobase) && (dvp->id_iobase <= (tmpdvp->id_iobase + tmpdvp->id_alive - 1))) { +#ifndef ALLOW_CONFLICT_IOADDR conflict(dvp, tmpdvp, dvp->id_iobase, "I/O address", "0x%x"); status = 1; +#endif } } /* @@ -143,12 +145,14 @@ haveseen(dvp, tmpdvp) if((KERNBASE + dvp->id_maddr >= tmpdvp->id_maddr) && (KERNBASE + dvp->id_maddr <= (tmpdvp->id_maddr + tmpdvp->id_msize - 1))) { +#ifndef ALLOW_CONFLICT_MEMADDR conflict(dvp, tmpdvp, dvp->id_maddr, "maddr", "0x%x"); status = 1; +#endif } } -#ifndef COM_MULTIPORT +#ifndef ALLOW_CONFLICT_IRQ /* * Check for IRQ conflicts. */ @@ -160,6 +164,7 @@ haveseen(dvp, tmpdvp) } } #endif +#ifndef ALLOW_CONFLICT_DRQ /* * Check for DRQ conflicts. */ @@ -170,6 +175,7 @@ haveseen(dvp, tmpdvp) status = 1; } } +#endif } return (status); } @@ -213,38 +219,45 @@ isa_configure() { printf("Probing for devices on the ISA bus:\n"); for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) { if (!haveseen_isadev(dvp)) - config_isadev(dvp,&ttymask); + config_isadev(dvp,&tty_imask); } for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) { if (!haveseen_isadev(dvp)) - config_isadev(dvp,&biomask); + config_isadev(dvp,&bio_imask); } for (dvp = isa_devtab_net; dvp->id_driver; dvp++) { if (!haveseen_isadev(dvp)) - config_isadev(dvp,&netmask); + config_isadev(dvp,&net_imask); } for (dvp = isa_devtab_null; dvp->id_driver; dvp++) { if (!haveseen_isadev(dvp)) config_isadev(dvp,(u_int *) NULL); } + bio_imask |= SWI_CLOCK_MASK; + net_imask |= SWI_NET_MASK; + tty_imask |= SWI_TTY_MASK; + /* - * XXX We should really add the tty device to netmask when the line is + * XXX we should really add the tty device to net_imask when the line is * switched to SLIPDISC, and then remove it when it is switched away from - * SLIPDISC. No need to block out ALL ttys during a splnet when only one + * SLIPDISC. No need to block out ALL ttys during a splimp when only one * of them is running slip. + * + * XXX actually, blocking all ttys during a splimp doesn't matter so much + * with sio because the serial interrupt layer doesn't use tty_imask. Only + * non-serial ttys suffer. It's more stupid that ALL 'net's are blocked + * during spltty. */ #include "sl.h" #if NSL > 0 - netmask |= ttymask; - ttymask |= netmask; + net_imask |= tty_imask; + tty_imask = net_imask; +#endif + /* bio_imask |= tty_imask ; can some tty devices use buffers? */ +#ifdef DIAGNOSTIC + printf("bio_imask %x tty_imask %x net_imask %x\n", + bio_imask, tty_imask, net_imask); #endif - /* if netmask == 0, then the loopback code can do some really - * bad things. - */ - if (netmask == 0) - netmask = 0x10000; - /* biomask |= ttymask ; can some tty devices use buffers? */ - printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask); splnone(); } @@ -337,15 +350,12 @@ extern inthand_t IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11), IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15); -static inthand_func_t defvec[16] = { +static inthand_func_t defvec[ICU_LEN] = { &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) }; -/* out of range default interrupt vector gate entry */ -extern inthand_t IDTVEC(intrdefault); - /* * Fill in default interrupt table (in case of spuruious interrupt * during configuration of kernel, setup interrupt control unit @@ -356,12 +366,8 @@ isa_defaultirq() int i; /* icu vectors */ - for (i = NRSVIDT ; i < NRSVIDT+ICU_LEN ; i++) - setidt(i, defvec[i], SDT_SYS386IGT, SEL_KPL); - - /* out of range vectors */ - for (i = NRSVIDT; i < NIDT; i++) - setidt(i, &IDTVEC(intrdefault), SDT_SYS386IGT, SEL_KPL); + for (i = 0; i < ICU_LEN; i++) + setidt(ICU_OFFSET + i, defvec[i], SDT_SYS386IGT, SEL_KPL); /* initialize 8259's */ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ @@ -530,7 +536,7 @@ isa_dmarangecheck(caddr_t va, unsigned length, unsigned chan) { #define ISARAM_END RAM_END if (phys == 0) panic("isa_dmacheck: no physical page present"); - if (phys > ISARAM_END) + if (phys >= ISARAM_END) return (1); if (priorpage) { if (priorpage + NBPG != phys) @@ -588,14 +594,18 @@ isa_freephysmem(caddr_t va, unsigned length) { /* * Handle a NMI, possibly a machine check. + * This is generally one of two things, either an memory parity error + * or a bus master timeout failure. A bus-master timeout is indicated + * by bit 4 of port 0x461 going high. + * * return true to panic system, false to ignore. */ int isa_nmi(cd) int cd; { - - log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70)); + log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x, port 461 %x\n", + inb(0x61), inb(0x70), inb(0x461)); return(0); } @@ -627,165 +637,6 @@ isa_strayintr(d) } /* - * Wait "n" microseconds. - * Relies on timer 1 counting down from (TIMER_FREQ / hz) at - * (1 * TIMER_FREQ) Hz. - * Note: timer had better have been programmed before this is first used! - * (The standard programming causes the timer to generate a square wave and - * the counter is decremented twice every cycle.) - */ -#define CF (1 * TIMER_FREQ) -#define TIMER_FREQ 1193182 /* XXX - should be elsewhere */ - -void -DELAY(n) - int n; -{ - int counter_limit; - int prev_tick; - int tick; - int ticks_left; - int sec; - int usec; - -#ifdef DELAYDEBUG - int getit_calls = 1; - int n1; - static int state = 0; - - if (state == 0) { - state = 1; - for (n1 = 1; n1 <= 10000000; n1 *= 10) - DELAY(n1); - state = 2; - } - if (state == 1) - printf("DELAY(%d)...", n); -#endif - - /* - * Read the counter first, so that the rest of the setup overhead is - * counted. Guess the initial overhead is 20 usec (on most systems it - * takes about 1.5 usec for each of the i/o's in getit(). The loop - * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The - * multiplications and divisions to scale the count take a while). - */ - prev_tick = getit(0, 0); - n -= 20; - - /* - * Calculate (n * (CF / 1e6)) without using floating point and without - * any avoidable overflows. - */ - sec = n / 1000000; - usec = n - sec * 1000000; - ticks_left = sec * CF - + usec * (CF / 1000000) - + usec * ((CF % 1000000) / 1000) / 1000 - + usec * (CF % 1000) / 1000000; - - counter_limit = TIMER_FREQ / hz; - while (ticks_left > 0) { - tick = getit(0, 0); -#ifdef DELAYDEBUG - ++getit_calls; -#endif - if (tick > prev_tick) - ticks_left -= prev_tick - (tick - counter_limit); - else - ticks_left -= prev_tick - tick; - prev_tick = tick; - } -#ifdef DELAYDEBUG - if (state == 1) - printf(" %d calls to getit() at %d usec each\n", - getit_calls, (n + 5) / getit_calls); -#endif -} - -int -getit(unit, timer) - int unit; - int timer; -{ - int high; - int low; - - /* - * XXX - isa.h defines bogus timers. There's no such timer as - * IO_TIMER_2 = 0x48. There's a timer in the CMOS RAM chip but - * its interface is quite different. Neither timer is an 8252. - * We actually only call this with unit = 0 and timer = 0. It - * could be static... - */ - /* - * Protect ourself against interrupts. - * XXX - sysbeep() and sysbeepstop() need protection. - */ - disable_intr(); - /* - * Latch the count for 'timer' (cc00xxxx, c = counter, x = any). - */ - outb(IO_TIMER1 + 3, timer << 6); - - low = inb(IO_TIMER1 + timer); - high = inb(IO_TIMER1 + timer); - enable_intr(); - return ((high << 8) | low); -} - -static int beeping; - -static void -sysbeepstop(f, dummy) - caddr_t f; - int dummy; -{ - /* disable counter 2 */ - outb(0x61, inb(0x61) & 0xFC); - if (f) - timeout(sysbeepstop, (caddr_t)0, (int)f); - else - beeping = 0; -} - -void -sysbeep(int pitch, int period) -{ - - outb(0x61, inb(0x61) | 3); /* enable counter 2 */ - /* - * XXX - move timer stuff to clock.c. - * Program counter 2: - * ccaammmb, c counter, a = access, m = mode, b = BCD - * 1011x110, 11 for aa = LSB then MSB, x11 for mmm = square wave. - */ - outb(0x43, 0xb6); /* set command for counter 2, 2 byte write */ - - outb(0x42, pitch); - outb(0x42, (pitch>>8)); - - if (!beeping) { - beeping = period; - timeout(sysbeepstop, (caddr_t)(period/2), period); - } -} - -/* - * Pass command to keyboard controller (8042) - */ -unsigned -kbc_8042cmd(val) - int val; -{ - - while (inb(KBSTATP)&KBS_IBF); - if (val) outb(KBCMDP, val); - while (inb(KBSTATP)&KBS_IBF); - return (inb(KBDATAP)); -} - -/* * find an ISA device in a given isa_devtab_* table, given * the table to search, the expected id_driver entry, and the unit number. * diff --git a/sys/i386/isa/isa.h b/sys/i386/isa/isa.h index cb083fdcf4cb..32786e6008c1 100644 --- a/sys/i386/isa/isa.h +++ b/sys/i386/isa/isa.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)isa.h 5.7 (Berkeley) 5/9/91 - * $Id: isa.h,v 1.4 1994/01/05 15:03:28 rgrimes Exp $ + * $Id: isa.h,v 1.5 1994/04/21 14:20:54 sos Exp $ */ #ifndef _I386_ISA_ISA_H_ @@ -47,12 +47,8 @@ #ifndef LOCORE #include <sys/cdefs.h> -unsigned char rtcin __P((int)); extern unsigned int atdevbase; /* offset in virtual memory of ISA io mem */ -void sysbeep __P((int, int)); -unsigned kbd_8042cmd __P((int)); -struct isa_device; -int isa_irq_pending __P((struct isa_device *dvp)); +unsigned char rtcin __P((int)); #endif @@ -69,6 +65,7 @@ int isa_irq_pending __P((struct isa_device *dvp)); #define IO_TIMER1 0x040 /* 8253 Timer #1 */ #define IO_TIMER2 0x048 /* 8253 Timer #2 */ #define IO_KBD 0x060 /* 8042 Keyboard */ +#define IO_PPI 0x061 /* Programmabel Peripheral Interface */ #define IO_RTC 0x070 /* RTC */ #define IO_NMI IO_RTC /* NMI Control */ #define IO_DMAPG 0x080 /* DMA Page Registers */ diff --git a/sys/i386/isa/isa_device.h b/sys/i386/isa/isa_device.h index 6299f89912a4..670dacf4dfa6 100644 --- a/sys/i386/isa/isa_device.h +++ b/sys/i386/isa/isa_device.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)isa_device.h 7.1 (Berkeley) 5/9/91 - * $Id: isa_device.h,v 1.5 1994/01/04 20:06:30 nate Exp $ + * $Id: isa_device.h,v 1.7 1994/06/08 14:01:04 davidg Exp $ */ #ifndef _I386_ISA_ISA_DEVICE_H_ @@ -46,7 +46,7 @@ */ struct isa_device { struct isa_driver *id_driver; - short id_iobase; /* base i/o address */ + int id_iobase; /* base i/o address */ u_short id_irq; /* interrupt request */ short id_drq; /* DMA request */ caddr_t id_maddr; /* physical i/o memory address on bus (if any)*/ diff --git a/sys/i386/isa/kbdtables.h b/sys/i386/isa/kbdtables.h index 011bc2cd1879..1fbd3215f367 100644 --- a/sys/i386/isa/kbdtables.h +++ b/sys/i386/isa/kbdtables.h @@ -14,7 +14,7 @@ * DK9210 Aalborg SO Phone: +45 9814 8076 * * @(#)kbdtables.h 1.3 940123 - * $Id: kbdtables.h,v 1.11 1994/02/01 09:27:43 ache Exp $ + * $Id: kbdtables.h,v 1.13 1994/05/27 01:09:16 ache Exp $ */ #define SET8 0x80 /* eight bit for emacs SET8-key */ @@ -374,17 +374,17 @@ keymap_t key_map = { 0x69, /* swedish iso8859 keymap */ /* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, /* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00, /* sc=02 */ '1', '!', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, -/* sc=03 */ '2', '"', NOP, NOP, NOP, '@', NOP, NOP, 0x3B, 0x00, -/* sc=04 */ '3', 0xA3, NOP, NOP, NOP, '#', NOP, NOP, 0x3B, 0x00, -/* sc=05 */ '4', '$', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=03 */ '2', '"', NOP, NOP, '@', NOP, NOP, NOP, 0x37, 0x00, +/* sc=04 */ '3', '#', NOP, NOP, 0xA3, NOP, NOP, NOP, 0x37, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, 0xA4, NOP, NOP, NOP, 0x37, 0x00, /* sc=06 */ '5', '%', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, -/* sc=07 */ '6', '&', 0x1E, NOP, NOP, '^', 0x1E, NOP, 0x19, 0x00, -/* sc=08 */ '7', '/', NOP, NOP, NOP, '&', NOP, NOP, 0x3B, 0x00, -/* sc=09 */ '8', '(', NOP, NOP, NOP, '*', NOP, NOP, 0x3B, 0x00, -/* sc=0a */ '9', ')', NOP, NOP, NOP, '(', NOP, NOP, 0x3B, 0x00, -/* sc=0b */ '0', '=', NOP, NOP, NOP, ')', NOP, NOP, 0x3B, 0x00, -/* sc=0c */ '+', '?', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, -/* sc=0d */ 0xB4, '`', NOP, NOP, '=', '+', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '&', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=08 */ '7', '/', NOP, NOP, '{', NOP, NOP, NOP, 0x37, 0x00, +/* sc=09 */ '8', '(', NOP, NOP, '[', NOP, NOP, NOP, 0x37, 0x00, +/* sc=0a */ '9', ')', NOP, NOP, ']', NOP, NOP, NOP, 0x37, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, '}', NOP, NOP, NOP, 0x37, 0x00, +/* sc=0c */ '+', '?', NOP, NOP, '\\', NOP, 0x1C, NOP, 0x35, 0x00, +/* sc=0d */ 0x180, '`', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, /* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, /* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00, /* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, @@ -397,8 +397,8 @@ keymap_t key_map = { 0x69, /* swedish iso8859 keymap */ /* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, /* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, /* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, -/* sc=1a */ 0xE5, 0xC5, NOP, NOP, '[', '{', 0x1B, NOP, 0x31, 0x01, -/* sc=1b */ 0xA8, '^', NOP, NOP, ']', '}', 0x1D, NOP, 0x31, 0x00, +/* sc=1a */ 0xE5, 0xC5, NOP, NOP, '}', ']', NOP, NOP, 0x33, 0x01, +/* sc=1b */ 0xA8, '^', NOP, NOP, '~', NOP, NOP, NOP, 0x37, 0x00, /* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, /* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, /* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, @@ -410,11 +410,11 @@ keymap_t key_map = { 0x69, /* swedish iso8859 keymap */ /* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, /* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, /* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, -/* sc=27 */ 0xF8, 0xD8, NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x01, -/* sc=28 */ 0xE6, 0xC6, NOP, NOP, '\'', '"', NOP, NOP, 0x33, 0x01, -/* sc=29 */ '<', '>', NOP, NOP, '\\', '|', 0x1C, NOP, 0x31, 0x00, +/* sc=27 */ 0xF6, 0xD6, NOP, NOP, '|', '\\', NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0xE4, 0xC4, NOP, NOP, '{', '[', NOP, NOP, 0x33, 0x01, +/* sc=29 */ 0xA7, 0xBD, NOP, NOP, '\\', '|', NOP, NOP, 0x33, 0x00, /* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, -/* sc=2b */ '\'', '*', NOP, NOP, '`', '~', NOP, NOP, 0x33, 0x00, +/* sc=2b */ '\'', '*', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, /* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, /* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, /* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, @@ -457,7 +457,7 @@ keymap_t key_map = { 0x69, /* swedish iso8859 keymap */ /* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02, /* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, /* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, -/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ '<', '>', NOP, NOP, '|', NOP, NOP, NOP, 0x37, 0x00, /* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, /* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, /* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, @@ -569,7 +569,7 @@ keymap_t key_map = { 0xe9, /* keys number */ /* sc=50 */ F(58), '2', '2', '2', SET8|'2', SET8|'2', SET8|'2', SET8|'2', 0x80, 0x02, /* sc=51 */ F(59), '3', '3', '3', SET8|'3', SET8|'3', SET8|'3', SET8|'3', 0x80, 0x02, /* sc=52 */ F(60), '0', '0', '0', SET8|'0', SET8|'0', SET8|'0', SET8|'0', 0x80, 0x02, -/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0x82, 0x02, +/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, RBT, 0x83, 0x02, /* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00, /* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, /* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, @@ -698,7 +698,7 @@ keymap_t key_map = { 0xe9, /* keys number */ /* sc=50 */ F(58), '2', '2', '2', SET8|'2', SET8|'2', SET8|'2', SET8|'2', 0x80, 0x02, /* sc=51 */ F(59), '3', '3', '3', SET8|'3', SET8|'3', SET8|'3', SET8|'3', 0x80, 0x02, /* sc=52 */ F(60), '0', '0', '0', SET8|'0', SET8|'0', SET8|'0', SET8|'0', 0x80, 0x02, -/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0x82, 0x02, +/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, RBT, 0x83, 0x02, /* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00, /* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, /* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, diff --git a/sys/i386/isa/lpa.c b/sys/i386/isa/lpa.c index 89434cbf3da7..8d4aa3f7470f 100644 --- a/sys/i386/isa/lpa.c +++ b/sys/i386/isa/lpa.c @@ -45,7 +45,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: lpa.c,v 1.5 1994/02/22 09:04:08 rgrimes Exp $ + * $Id: lpa.c,v 1.6 1994/04/11 07:57:36 csgr Exp $ */ /* @@ -178,6 +178,18 @@ lpaprobe(struct isa_device *dvp) u_char data; u_char mask; int i; + static int warned = 0; + + + /* Warn users that the lpa driver should no longer be used */ + if(!warned) { + printf("*************************************************\n"); + printf("WARNING: The lpa driver is now obsolete, and will\n"); + printf("WARNING: be removed soon.\n"); + printf("WARNING: Please change your config to use lpt\n"); + printf("*************************************************\n"); + warned = 1; + } status = IO_LPTSIZE; diff --git a/sys/i386/isa/lpt.c b/sys/i386/isa/lpt.c index 42f92c74f8d0..2715ea8b193f 100644 --- a/sys/i386/isa/lpt.c +++ b/sys/i386/isa/lpt.c @@ -46,7 +46,7 @@ * SUCH DAMAGE. * * from: unknown origin, 386BSD 0.1 - * $Id: lpt.c,v 1.9 1994/02/22 09:05:13 rgrimes Exp $ + * $Id: lpt.c,v 1.13 1994/06/08 14:34:54 davidg Exp $ */ /* @@ -66,11 +66,14 @@ #include "ioctl.h" #include "tty.h" #include "uio.h" +#include "syslog.h" #include "i386/isa/isa.h" #include "i386/isa/isa_device.h" #include "i386/isa/lptreg.h" +#include "i386/include/lpt.h" + #define LPINITRDY 4 /* wait up to 4 seconds for a ready */ #define LPTOUTTIME 4 /* wait up to 4 seconds for a ready */ #define LPPRI (PZERO+8) @@ -90,15 +93,6 @@ int lptflag = 1; #endif -void lptout(); - -int lptprobe(), lptattach(); -void lptintr(); - -struct isa_driver lptdriver = { - lptprobe, lptattach, "lpt" -}; - #define LPTUNIT(s) ((s)&0x03) #define LPTFLAGS(s) ((s)&0xfc) @@ -122,6 +116,8 @@ struct lpt_softc { u_char sc_irq ; /* IRQ status of port */ #define LP_HAS_IRQ 0x01 /* we have an irq available */ #define LP_USE_IRQ 0x02 /* we are using our irq */ +#define LP_ENABLE_IRQ 0x04 /* enable IRQ on open */ + } lpt_sc[NLPT] ; /* bits for state */ @@ -149,12 +145,21 @@ struct lpt_softc { #define MAX_SPIN 20 /* Max delay for device ready in usecs */ +static void lptout (struct lpt_softc * sc); +int lptprobe (struct isa_device *dvp); +int lptattach (struct isa_device *isdp); +void lptintr (int unit); + +struct isa_driver lptdriver = { + lptprobe, lptattach, "lpt" +}; + /* * Internal routine to lptprobe to do port tests of one byte value */ -int +static int lpt_port_test(short port, u_char data, u_char mask) { int temp, timeout; @@ -279,8 +284,7 @@ end_probe: /* XXX Todo - try and detect if interrupt is working */ int -lptattach(isdp) - struct isa_device *isdp; +lptattach(struct isa_device *isdp) { struct lpt_softc *sc; @@ -292,7 +296,7 @@ lptattach(isdp) /* check if we can use interrupt */ lprintf("oldirq %x\n", sc->sc_irq); if(isdp->id_irq) { - sc->sc_irq = LP_HAS_IRQ | LP_USE_IRQ; + sc->sc_irq = LP_HAS_IRQ | LP_USE_IRQ | LP_ENABLE_IRQ; printf("lpt%d: Interrupt-driven port\n", isdp->id_unit); } else { sc->sc_irq = 0; @@ -308,9 +312,7 @@ lptattach(isdp) */ int -lptopen(dev, flag) - dev_t dev; - int flag; +lptopen(dev_t dev, int flag) { struct lpt_softc *sc; int s; @@ -321,7 +323,6 @@ lptopen(dev, flag) if ((unit >= NLPT) || (sc->sc_port == 0)) return (ENXIO); - /* Only check open bit */ if (sc->sc_state) { lprintf("lp: still open\n") ; lprintf("still open %x\n", sc->sc_state); @@ -333,6 +334,13 @@ lptopen(dev, flag) lprintf("lp flags 0x%x\n", sc->sc_flags); port = sc->sc_port; + /* set IRQ status according to ENABLE_IRQ flag */ + if(sc->sc_irq & LP_ENABLE_IRQ) + sc->sc_irq |= LP_USE_IRQ; + else + sc->sc_irq &= ~LP_USE_IRQ; + + /* init printer */ if((sc->sc_flags & LP_NO_PRIME) == 0) { if((sc->sc_flags & LP_PRIMEOPEN) || sc->sc_primed == 0) { @@ -384,20 +392,19 @@ lptopen(dev, flag) lprintf("irq %x\n", sc->sc_irq); if(sc->sc_irq & LP_USE_IRQ) { sc->sc_state |= TOUT; - timeout (lptout, (caddr_t)sc, hz/2); + timeout ((timeout_func_t)lptout, (caddr_t)sc, hz/2); } lprintf("opened.\n"); return(0); } -void -lptout (sc) - struct lpt_softc *sc; +static void +lptout (struct lpt_softc * sc) { int pl; lprintf ("T %x ", inb(sc->sc_port+lpt_status)); if (sc->sc_state&OPEN) - timeout (lptout, (caddr_t)sc, hz/2); + timeout ((timeout_func_t)lptout, (caddr_t)sc, hz/2); else sc->sc_state &= ~TOUT; if (sc->sc_state & ERROR) @@ -423,9 +430,7 @@ lptout (sc) */ int -lptclose(dev, flag) - dev_t dev; - int flag; +lptclose(dev_t dev, int flag) { struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev)); int port = sc->sc_port; @@ -458,13 +463,13 @@ lptclose(dev, flag) * This code is only used when we are polling the port */ static int -pushbytes(sc) - struct lpt_softc *sc; +pushbytes(struct lpt_softc * sc) { int spin, err, tic; char ch; int port = sc->sc_port; + lprintf("p"); /* loop for every character .. */ while (sc->sc_xfercnt > 0) { /* printer data */ @@ -518,9 +523,7 @@ pushbytes(sc) */ int -lptwrite(dev, uio) - dev_t dev; - struct uio *uio; +lptwrite(dev_t dev, struct uio * uio) { register unsigned n; int pl, err; @@ -531,26 +534,26 @@ lptwrite(dev, uio) sc->sc_cp = sc->sc_inbuf->b_un.b_addr ; uiomove(sc->sc_cp, n, uio); sc->sc_xfercnt = n ; - if(sc->sc_irq & LP_USE_IRQ) - while (sc->sc_xfercnt > 0) { - lprintf("i"); - /* if the printer is ready for a char, */ - /* give it one */ - if ((sc->sc_state & OBUSY) == 0){ - lprintf("\nC %d. ", sc->sc_xfercnt); - pl = spltty(); - lptintr(sc - lpt_sc); - (void) splx(pl); - } - lprintf("W "); - if (sc->sc_state & OBUSY) - if (err = tsleep ((caddr_t)sc, - LPPRI|PCATCH, "lpwrite", 0)) { - sc->sc_state |= INTERRUPTED; - return(err); - } + while ((sc->sc_xfercnt > 0)&&(sc->sc_irq & LP_USE_IRQ)) { + lprintf("i"); + /* if the printer is ready for a char, */ + /* give it one */ + if ((sc->sc_state & OBUSY) == 0){ + lprintf("\nC %d. ", sc->sc_xfercnt); + pl = spltty(); + lptintr(sc - lpt_sc); + (void) splx(pl); } - else { /* polled write */ + lprintf("W "); + if (sc->sc_state & OBUSY) + if (err = tsleep ((caddr_t)sc, + LPPRI|PCATCH, "lpwrite", 0)) { + sc->sc_state |= INTERRUPTED; + return(err); + } + } + /* check to see if we must do a polled write */ + if(!(sc->sc_irq & LP_USE_IRQ) && (sc->sc_xfercnt)) { lprintf("p"); if((err = pushbytes(sc))) return(err); @@ -567,8 +570,7 @@ lptwrite(dev, uio) */ void -lptintr(unit) - int unit; +lptintr(int unit) { struct lpt_softc *sc = lpt_sc + unit; int port = sc->sc_port, sts; @@ -607,22 +609,40 @@ lptintr(unit) } int -lptioctl(dev, cmd, data, flag) - dev_t dev; - int cmd; - caddr_t data; - int flag; +lptioctl(dev_t dev, int cmd, caddr_t data, int flag) { - int error; + int error = 0; + struct lpt_softc *sc; + u_int unit = LPTUNIT(minor(dev)); + u_char old_sc_irq; /* old printer IRQ status */ + + sc = lpt_sc + unit; - error = 0; switch (cmd) { -#ifdef THISISASAMPLE - case XXX: - dothis; andthis; andthat; - error=x; - break; -#endif /* THISISASAMPLE */ + case LPT_IRQ : + if(sc->sc_irq & LP_HAS_IRQ) { + /* + * NOTE: + * If the IRQ status is changed, + * this will only be visible on the + * next open. + * + * If interrupt status changes, + * this gets syslog'd. + */ + old_sc_irq = sc->sc_irq; + if(*(int*)data == 0) + sc->sc_irq &= (~LP_ENABLE_IRQ); + else + sc->sc_irq |= LP_ENABLE_IRQ; + if (old_sc_irq != sc->sc_irq ) + log(LOG_NOTICE, "lpt%c switched to %s mode\n", + (char)unit+'0', + (sc->sc_irq & LP_ENABLE_IRQ)? + "interrupt-driven":"polled"); + } else /* polled port */ + error = EOPNOTSUPP; + break; default: error = ENODEV; } diff --git a/sys/i386/isa/mcd.c b/sys/i386/isa/mcd.c index 190d42be3b75..3245dadc4097 100644 --- a/sys/i386/isa/mcd.c +++ b/sys/i386/isa/mcd.c @@ -39,7 +39,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: mcd.c,v 1.10.2.2 1994/03/23 23:51:39 rgrimes Exp $ + * $Id: mcd.c,v 1.16 1994/04/30 17:03:33 gclarkii Exp $ */ static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore"; @@ -350,6 +350,9 @@ MCD_TRACE("strategy: drive not valid\n",0,0,0,0); if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) { goto done; } + } else { + bp->b_pblkno = bp->b_blkno; + bp->b_resid = 0; } /* queue it */ diff --git a/sys/i386/isa/npx.c b/sys/i386/isa/npx.c index 2979e0cfd7ab..e83a503feeed 100644 --- a/sys/i386/isa/npx.c +++ b/sys/i386/isa/npx.c @@ -32,7 +32,7 @@ * SUCH DAMAGE. * * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 - * $Id: npx.c,v 1.6 1994/01/03 07:55:43 davidg Exp $ + * $Id: npx.c,v 1.9 1994/06/11 02:36:32 davidg Exp $ */ #include "npx.h" @@ -114,7 +114,7 @@ struct isa_driver npxdriver = { npxprobe, npxattach, "npx", }; -u_int npx0mask; +u_int npx0_imask = SWI_CLOCK_MASK; struct proc *npxproc; static bool_t npx_ex16; @@ -292,7 +292,7 @@ npxprobe1(dvp) * Bad, we are stuck with IRQ13. */ npx_irq13 = 1; - npx0mask = dvp->id_irq; /* npxattach too late */ + npx0_imask = dvp->id_irq; /* npxattach too late */ return (IO_NPXSIZE); } /* @@ -321,10 +321,12 @@ npxattach(dvp) struct isa_device *dvp; { if (!npx_ex16 && !npx_irq13) { - if (npx_exists) + if (npx_exists) { printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit); - else + npx_exists = 0; + } else { printf("npx%d: 387 Emulator\n",dvp->id_unit); + } } npxinit(__INITIAL_NPXCW__); return (1); /* XXX unused */ @@ -528,8 +530,8 @@ npxsave(addr) old_icu1_mask = inb(IO_ICU1 + 1); old_icu2_mask = inb(IO_ICU2 + 1); save_idt_npxintr = idt[npx_intrno]; - outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0mask)); - outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0mask >> 8)); + outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask)); + outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8)); idt[npx_intrno] = npx_idt_probeintr; enable_intr(); stop_emulating(); @@ -541,10 +543,10 @@ npxsave(addr) icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ icu2_mask = inb(IO_ICU2 + 1); outb(IO_ICU1 + 1, - (icu1_mask & ~npx0mask) | (old_icu1_mask & npx0mask)); + (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask)); outb(IO_ICU2 + 1, - (icu2_mask & ~(npx0mask >> 8)) - | (old_icu2_mask & (npx0mask >> 8))); + (icu2_mask & ~(npx0_imask >> 8)) + | (old_icu2_mask & (npx0_imask >> 8))); idt[npx_intrno] = save_idt_npxintr; enable_intr(); /* back to usual state */ } diff --git a/sys/i386/isa/pcaudio.c b/sys/i386/isa/pcaudio.c new file mode 100644 index 000000000000..0c48e407a7be --- /dev/null +++ b/sys/i386/isa/pcaudio.c @@ -0,0 +1,430 @@ +/*- + * Copyright (c) 1994 Søren Schmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: pcaudio.c,v 1.5 1994/05/27 08:51:03 sos Exp $ + */ + +#include "systm.h" +#include "uio.h" +#include "ioctl.h" +#include "proc.h" +#include "file.h" +#include "sound/ulaw.h" +#include "machine/cpufunc.h" +#include "machine/pio.h" +#include "machine/pcaudioio.h" +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/timerreg.h" + +#include "pca.h" +#if NPCA > 0 + +#define BUF_SIZE 4*8192 +#define SAMPLE_RATE 8000 +#define INTERRUPT_RATE 16000 + +static struct pca_status { + char open; /* device open */ + char queries; /* did others try opening */ + unsigned char *buf[2]; /* double buffering */ + unsigned char *buffer; /* current buffer ptr */ + unsigned in_use[2]; /* buffers fill */ + unsigned index; /* index in current buffer */ + unsigned counter; /* sample counter */ + unsigned scale; /* sample counter scale */ + unsigned sample_rate; /* sample rate */ + unsigned processed; /* samples processed */ + unsigned volume; /* volume for pc-speaker */ + char encoding; /* Ulaw, Alaw or linear */ + char current; /* current buffer */ + unsigned char oldval; /* old timer port value */ + char timer_on; /* is playback running */ + char coll; /* select collision */ + pid_t wsel; /* pid of select'ing proc */ +} pca_status; + +static char buffer1[BUF_SIZE]; +static char buffer2[BUF_SIZE]; +static char volume_table[256]; + +static int pca_sleep = 0; +static int pca_initialized = 0; + +void pcaintr(int regs); +int pcaprobe(struct isa_device *dvp); +int pcaattach(struct isa_device *dvp); +int pcaclose(dev_t dev, int flag); +int pcaopen(dev_t dev, int flag); +int pcawrite(dev_t dev, struct uio *uio, int flag); +int pcaioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p); +int pcaselect(dev_t dev, int rw, struct proc *p); + +struct isa_driver pcadriver = { + pcaprobe, pcaattach, "pca", +}; + + +inline void conv(const void *table, void *buff, unsigned long n) +{ + __asm__("1:\tmovb (%2), %3\n" + "\txlatb\n" + "\tmovb %3, (%2)\n" + "\tinc %2\n" + "\tdec %1\n" + "\tjnz 1b\n" + : + :"b" ((long)table), "c" (n), "D" ((long)buff), "a" ((char)n) + :"bx","cx","di","ax"); +} + + +static void +pca_volume(int volume) +{ + int i, j; + + for (i=0; i<256; i++) { + j = ((i-128)*volume)/100; + if (j<-128) + j = -128; + if (j>127) + j = 127; + volume_table[i] = (((255-(j + 128))/4)+1); + } +} + + +static void +pca_init() +{ + pca_status.open = 0; + pca_status.queries = 0; + pca_status.timer_on = 0; + pca_status.buf[0] = (unsigned char *)&buffer1[0]; + pca_status.buf[1] = (unsigned char *)&buffer2[0]; + pca_status.buffer = pca_status.buf[0]; + pca_status.in_use[0] = pca_status.in_use[1] = 0; + pca_status.current = 0; + pca_status.sample_rate = SAMPLE_RATE; + pca_status.scale = (pca_status.sample_rate << 8) / INTERRUPT_RATE; + pca_status.encoding = AUDIO_ENCODING_ULAW; + pca_status.volume = 100; + + pca_volume(pca_status.volume); +} + + +static int +pca_start(void) +{ + /* use the first buffer */ + pca_status.current = 0; + pca_status.index = 0; + pca_status.counter = 0; + pca_status.buffer = pca_status.buf[pca_status.current]; + pca_status.oldval = inb(IO_PPI) | 0x03; + /* acquire the timers */ + if (acquire_timer2(TIMER_LSB|TIMER_ONESHOT)) { + return -1; + } + if (acquire_timer0(INTERRUPT_RATE, pcaintr)) { + release_timer2(); + return -1; + } + pca_status.timer_on = 1; + return 0; +} + + +static void +pca_stop(void) +{ + /* release the timers */ + release_timer0(); + release_timer2(); + /* reset the buffer */ + pca_status.in_use[0] = pca_status.in_use[1] = 0; + pca_status.index = 0; + pca_status.counter = 0; + pca_status.current = 0; + pca_status.buffer = pca_status.buf[pca_status.current]; + pca_status.timer_on = 0; +} + + +static void +pca_pause() +{ + release_timer0(); + release_timer2(); + pca_status.timer_on = 0; +} + + +static void +pca_continue() +{ + pca_status.oldval = inb(IO_PPI) | 0x03; + acquire_timer2(TIMER_LSB|TIMER_ONESHOT); + acquire_timer0(INTERRUPT_RATE, pcaintr); + pca_status.timer_on = 1; +} + + +static void +pca_wait(void) +{ + while (pca_status.in_use[0] || pca_status.in_use[1]) { + pca_sleep = 1; + tsleep((caddr_t)&pca_sleep, PZERO|PCATCH, "pca_drain", 0); + } +} + + +int +pcaprobe(struct isa_device *dvp) +{ + return(-1); +} + + +int +pcaattach(struct isa_device *dvp) +{ + printf(" PCM audio driver\n", dvp->id_unit); + pca_init(); + return 1; +} + + +int +pcaopen(dev_t dev, int flag) +{ + /* audioctl device can always be opened */ + if (minor(dev) == 128) + return 0; + if (minor(dev) > 0) + return ENXIO; + + if (!pca_initialized) { + pca_init(); + pca_initialized = 1; + } + + /* audio device can only be open by one process */ + if (pca_status.open) { + pca_status.queries = 1; + return EBUSY; + } + pca_status.buffer = pca_status.buf[0]; + pca_status.in_use[0] = pca_status.in_use[1] = 0; + pca_status.timer_on = 0; + pca_status.open = 1; + pca_status.processed = 0; + return 0; +} + + +int +pcaclose(dev_t dev, int flag) +{ + /* audioctl device can always be closed */ + if (minor(dev) == 128) + return 0; + if (minor(dev) > 0) + return ENXIO; + /* audio device close drains all output and restores timers */ + pca_wait(); + pca_stop(); + pca_status.open = 0; + return 0; +} + + +int +pcawrite(dev_t dev, struct uio *uio, int flag) +{ + int count, which; + + /* only audio device can be written */ + if (minor(dev) > 0) + return ENXIO; + + while ((count = min(BUF_SIZE, uio->uio_resid)) > 0) { + if (pca_status.in_use[0] && pca_status.in_use[1]) { + pca_sleep = 1; + tsleep((caddr_t)&pca_sleep, PZERO|PCATCH, "pca_wait",0); + } + which = pca_status.in_use[0] ? 1 : 0; + if (count && !pca_status.in_use[which]) { + uiomove(pca_status.buf[which], count, uio); + pca_status.processed += count; + switch (pca_status.encoding) { + case AUDIO_ENCODING_ULAW: + conv(ulaw_dsp, pca_status.buf[which], count); + break; + + case AUDIO_ENCODING_ALAW: + break; + + case AUDIO_ENCODING_RAW: + break; + } + pca_status.in_use[which] = count; + if (!pca_status.timer_on) + if (pca_start()) + return EBUSY; + } + } + return 0; +} + + +int +pcaioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) +{ + audio_info_t *auptr; + + switch(cmd) { + + case AUDIO_GETINFO: + auptr = (audio_info_t *)data; + auptr->play.sample_rate = pca_status.sample_rate; + auptr->play.channels = 1; + auptr->play.precision = 8; + auptr->play.encoding = pca_status.encoding; + + auptr->play.gain = pca_status.volume; + auptr->play.port = 0; + + auptr->play.samples = pca_status.processed; + auptr->play.eof = 0; + auptr->play.pause = !pca_status.timer_on; + auptr->play.error = 0; + auptr->play.waiting = pca_status.queries; + + auptr->play.open = pca_status.open; + auptr->play.active = pca_status.timer_on; + return 0; + + case AUDIO_SETINFO: + auptr = (audio_info_t *)data; + if (auptr->play.sample_rate != (unsigned int)~0) { + pca_status.sample_rate = auptr->play.sample_rate; + pca_status.scale = + (pca_status.sample_rate << 8) / INTERRUPT_RATE; + } + if (auptr->play.encoding != (unsigned int)~0) { + pca_status.encoding = auptr->play.encoding; + } + if (auptr->play.gain != (unsigned int)~0) { + pca_status.volume = auptr->play.gain; + pca_volume(pca_status.volume); + } + if (auptr->play.pause != (unsigned char)~0) { + if (auptr->play.pause) + pca_pause(); + else + pca_continue(); + } + + return 0; + + case AUDIO_DRAIN: + pca_wait(); + return 0; + + case AUDIO_FLUSH: + pca_stop(); + return 0; + + } + return ENXIO; +} + + +void +pcaintr(int regs) +{ + if (pca_status.index < pca_status.in_use[pca_status.current]) { + disable_intr(); + __asm__("outb %0,$0x61\n" + "andb $0xFE,%0\n" + "outb %0,$0x61" + : : "a" ((char)pca_status.oldval) ); + __asm__("xlatb\n" + "outb %0,$0x42" + : : "a" ((char)pca_status.buffer[pca_status.index]), + "b" ((long)volume_table) ); + enable_intr(); + pca_status.counter += pca_status.scale; + pca_status.index = (pca_status.counter >> 8); + } + if (pca_status.index >= pca_status.in_use[pca_status.current]) { + pca_status.index = pca_status.counter = 0; + pca_status.in_use[pca_status.current] = 0; + pca_status.current ^= 1; + pca_status.buffer = pca_status.buf[pca_status.current]; + if (pca_sleep) { + wakeup((caddr_t)&pca_sleep); + pca_sleep = 0; + } + if (pca_status.wsel) { + selwakeup(pca_status.wsel, pca_status.coll); + pca_status.wsel = 0; + pca_status.coll = 0; + } + } +} + + +int +pcaselect(dev_t dev, int rw, struct proc *p) +{ + int s = spltty(); + struct proc *p1; + + switch (rw) { + + case FWRITE: + if (!pca_status.in_use[0] || !pca_status.in_use[1]) { + splx(s); + return(1); + } + if (pca_status.wsel && (p1 = pfind(pca_status.wsel)) + && p1->p_wchan == (caddr_t)&selwait) + pca_status.coll = 1; + else + pca_status.wsel = p->p_pid; + splx(s); + return 0; + default: + splx(s); + return(0); + } +} +#endif diff --git a/sys/i386/isa/pccons.c b/sys/i386/isa/pccons.c index 43fba38b1552..3d729da6dc54 100644 --- a/sys/i386/isa/pccons.c +++ b/sys/i386/isa/pccons.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)pccons.c 5.11 (Berkeley) 5/21/91 - * $Id: pccons.c,v 1.13.2.1 1994/05/04 05:09:30 rgrimes Exp $ + * $Id: pccons.c,v 1.17 1994/05/30 03:15:09 ache Exp $ */ /* @@ -63,7 +63,7 @@ int pc_xmode; #endif /* XSERVER */ -struct tty pccons; +struct tty *pccons; struct pcconsoftc { char cs_flags; @@ -289,13 +289,12 @@ pcopen(dev, flag, mode, p) if (minor(dev) != 0) return (ENXIO); - tp = &pccons; + tp = pccons = ttymalloc(pccons); tp->t_oproc = pcstart; tp->t_param = pcparam; tp->t_dev = dev; openf++; if ((tp->t_state & TS_ISOPEN) == 0) { - tp->t_state |= TS_WOPEN; ttychars(tp); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; @@ -316,8 +315,12 @@ pcclose(dev, flag, mode, p) int flag, mode; struct proc *p; { - (*linesw[pccons.t_line].l_close)(&pccons, flag); - ttyclose(&pccons); + (*linesw[pccons->t_line].l_close)(pccons, flag); + ttyclose(pccons); +#ifdef broken /* session holds a ref to the tty; can't deallocate */ + ttyfree(pccons); + pccons = (struct tty *)NULL; +#endif return(0); } @@ -328,7 +331,7 @@ pcread(dev, uio, flag) struct uio *uio; int flag; { - return ((*linesw[pccons.t_line].l_read)(&pccons, uio, flag)); + return ((*linesw[pccons->t_line].l_read)(pccons, uio, flag)); } /*ARGSUSED*/ @@ -338,7 +341,7 @@ pcwrite(dev, uio, flag) struct uio *uio; int flag; { - return ((*linesw[pccons.t_line].l_write)(&pccons, uio, flag)); + return ((*linesw[pccons->t_line].l_write)(pccons, uio, flag)); } /* @@ -347,10 +350,8 @@ pcwrite(dev, uio, flag) * Catch the character, and see who it goes to. */ void -pcrint(dev, irq, cpl) - dev_t dev; - int irq; /* XXX ??? */ - int cpl; +pcrint(unit) + int unit; { int c; char *cp; @@ -361,7 +362,7 @@ pcrint(dev, irq, cpl) if (pcconsoftc.cs_flags & CSF_POLLING) return; #ifdef KDB - if (kdbrintr(c, &pccons)) + if (kdbrintr(c, pccons)) return; #endif if (!openf) @@ -369,11 +370,11 @@ pcrint(dev, irq, cpl) #ifdef XSERVER /* 15 Aug 92*/ /* send at least one character, because cntl-space is a null */ - (*linesw[pccons.t_line].l_rint)(*cp++ & 0xff, &pccons); + (*linesw[pccons->t_line].l_rint)(*cp++ & 0xff, pccons); #endif /* XSERVER */ while (*cp) - (*linesw[pccons.t_line].l_rint)(*cp++ & 0xff, &pccons); + (*linesw[pccons->t_line].l_rint)(*cp++ & 0xff, pccons); } #ifdef XSERVER /* 15 Aug 92*/ @@ -389,7 +390,7 @@ pcioctl(dev, cmd, data, flag) caddr_t data; int flag; { - register struct tty *tp = &pccons; + register struct tty *tp = pccons; register error; #ifdef XSERVER /* 15 Aug 92*/ @@ -436,12 +437,12 @@ pcxint(dev) if (!pcconsintr) return; - pccons.t_state &= ~TS_BUSY; + pccons->t_state &= ~TS_BUSY; pcconsoftc.cs_timo = 0; - if (pccons.t_line) - (*linesw[pccons.t_line].l_start)(&pccons); + if (pccons->t_line) + (*linesw[pccons->t_line].l_start)(pccons); else - pcstart(&pccons); + pcstart(pccons); } void @@ -454,20 +455,11 @@ pcstart(tp) if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) goto out; do { - if (RB_LEN(&tp->t_out) <= tp->t_lowat) { - if (tp->t_state&TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_out); - } - if (tp->t_wsel) { - selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); - tp->t_wsel = 0; - tp->t_state &= ~TS_WCOLL; - } - } - if (RB_LEN(&tp->t_out) == 0) + if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) + ttwwakeup(tp); + if (RB_LEN(tp->t_out) == 0) goto out; - c = getc(&tp->t_out); + c = getc(tp->t_out); tp->t_state |= TS_BUSY; /* 21 Aug 92*/ splx(s); sput(c, 0); @@ -491,7 +483,7 @@ pccnprobe(cp) /* initialize required fields */ cp->cn_dev = makedev(maj, 0); - cp->cn_tp = &pccons; + cp->cn_tp = pccons; cp->cn_pri = CN_INTERNAL; } @@ -639,26 +631,13 @@ static u_char shift_down, ctrl_down, alt_down, caps, num, scroll; /* translate ANSI color codes to standard pc ones */ static char fgansitopc[] = { FG_BLACK, FG_RED, FG_GREEN, FG_BROWN, FG_BLUE, - FG_MAGENTA, FG_CYAN, FG_LIGHTGREY}; + FG_MAGENTA, FG_CYAN, FG_LIGHTGREY +}; static char bgansitopc[] = { BG_BLACK, BG_RED, BG_GREEN, BG_BROWN, BG_BLUE, - BG_MAGENTA, BG_CYAN, BG_LIGHTGREY}; - -static void move_up(u_short *s, u_short *d, u_int len) -{ - s += len; - d += len; - while (len-- > 0) - *--d = *--s; -} - - -static void move_down(u_short *s, u_short *d, u_int len) -{ - while (len-- > 0) - *d++ = *s++; -} + BG_MAGENTA, BG_CYAN, BG_LIGHTGREY +}; /* * sput has support for emulation of the 'pc3' termcap entry. @@ -872,7 +851,7 @@ sput(c, ka) posy = (crtat - Crtat) / vs.ncol; if (vs.cx > posy) vs.cx = posy; - bcopy(Crtat+vs.ncol*vs.cx, Crtat, vs.ncol*(vs.nrow-vs.cx)*CHR); + bcopyw(Crtat+vs.ncol*vs.cx, Crtat, vs.ncol*(vs.nrow-vs.cx)*CHR); fillw((at <<8)+' ', (Crtat + vs.ncol * (vs.nrow - vs.cx)), vs.ncol * vs.cx); @@ -884,7 +863,7 @@ sput(c, ka) posy = (crtat - Crtat) / vs.ncol; if (vs.cx > vs.nrow - posy) vs.cx = vs.nrow - posy; - bcopy(Crtat, Crtat+vs.ncol*vs.cx, vs.ncol*(vs.nrow-vs.cx)*CHR); + bcopyw(Crtat, Crtat+vs.ncol*vs.cx, vs.ncol*(vs.nrow-vs.cx)*CHR); fillw((at <<8)+' ', Crtat, vs.ncol*vs.cx); /* crtat += vs.ncol*vs.cx;*/ /* XXX */ vs.esc = 0; vs.ebrac = 0; vs.eparm = 0; @@ -897,7 +876,7 @@ sput(c, ka) src = Crtat + posy * vs.ncol; dst = src + vs.cx * vs.ncol; count = vs.nrow - (posy + vs.cx); - move_up(src, dst, count * vs.ncol); + bcopyw(src, dst, count * vs.ncol * CHR); fillw((at <<8)+' ', src, vs.cx * vs.ncol); vs.esc = 0; vs.ebrac = 0; vs.eparm = 0; break; @@ -909,7 +888,7 @@ sput(c, ka) dst = Crtat + posy * vs.ncol; src = dst + vs.cx * vs.ncol; count = vs.nrow - (posy + vs.cx); - move_down(src, dst, count * vs.ncol); + bcopyw(src, dst, count * vs.ncol * CHR); src = dst + count * vs.ncol; fillw((at <<8)+' ', src, vs.cx * vs.ncol); vs.esc = 0; vs.ebrac = 0; vs.eparm = 0; @@ -999,7 +978,7 @@ sput(c, ka) } if (sc && crtat >= Crtat+vs.ncol*vs.nrow) { /* scroll check */ if (openf) do (void)sgetc(1); while (scroll); - bcopy(Crtat+vs.ncol, Crtat, vs.ncol*(vs.nrow-1)*CHR); + bcopyw(Crtat+vs.ncol, Crtat, vs.ncol*(vs.nrow-1)*CHR); fillw ((at << 8) + ' ', Crtat + vs.ncol*(vs.nrow-1), vs.ncol); crtat -= vs.ncol; @@ -1557,6 +1536,12 @@ loop: #endif /* !XSERVER*/ } + /* + * Check for cntl-alt-del + */ + if ((dt == 83) && ctrl_down && alt_down) + cpu_reset(); + #include "ddb.h" #if NDDB > 0 /* @@ -1564,7 +1549,7 @@ loop: */ if ((dt == 1) && ctrl_down && alt_down) { Debugger("manual escape to debugger"); - dt |= 0x80; /* discard esc (ddb discarded ctrl-alt) */ + goto loop; } #endif @@ -1797,7 +1782,7 @@ void cons_normal() int pcmmap(dev_t dev, int offset, int nprot) { - if (offset > 0x20000) + if (offset > 0x20000 - PAGE_SIZE) return -1; return i386_btop((0xa0000 + offset)); } diff --git a/sys/i386/isa/psm.c b/sys/i386/isa/psm.c new file mode 100644 index 000000000000..d12298727ca3 --- /dev/null +++ b/sys/i386/isa/psm.c @@ -0,0 +1,464 @@ +/*- + * Copyright (c) 1992, 1993 Erik Forsberg. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Ported to 386bsd Oct 17, 1992 + * Sandi Donno, Computer Science, University of Cape Town, South Africa + * Please send bug reports to sandi@cs.uct.ac.za + * + * Thanks are also due to Rick Macklem, rick@snowhite.cis.uoguelph.ca - + * although I was only partially successful in getting the alpha release + * of his "driver for the Logitech and ATI Inport Bus mice for use with + * 386bsd and the X386 port" to work with my Microsoft mouse, I nevertheless + * found his code to be an invaluable reference when porting this driver + * to 386bsd. + * + * Further modifications for latest 386BSD+patchkit and port to NetBSD, + * Andrew Herbert <andrew@werple.apana.org.au> - 8 June 1993 + * + * Cloned from the Microsoft Bus Mouse driver, also by Erik Forsberg, by + * Andrew Herbert - 12 June 1993 + * + * Modified for PS/2 mouse by Charles Hannum <mycroft@ai.mit.edu> + * - 13 June 1993 + * + * Modified for PS/2 AUX mouse by Shoji Yuen <yuen@nuie.nagoya-u.ac.jp> + * - 24 October 1993 + */ + +#include "psm.h" + +#if NPSM > 0 + +#include "param.h" +#include "kernel.h" +#include "systm.h" +#include "buf.h" +#include "malloc.h" +#include "ioctl.h" +#include "tty.h" +#include "file.h" +#include "proc.h" +#include "vnode.h" + +#include "i386/include/mouse.h" +#include "i386/include/pio.h" /* Julian's fast IO macros */ +#include "i386/isa/isa_device.h" + +#ifdef 0 +#include "syslog.h" /* For debugging */ +#endif + +#define DATA 0 /* Offset for data port, read-write */ +#define CNTRL 4 /* Offset for control port, write-only */ +#define STATUS 4 /* Offset for status port, read-only */ + +/* status bits */ +#define PSM_OUTPUT_ACK 0x02 /* output acknowledge */ + +/* controller commands */ +#define PSM_ENABLE 0xa8 /* enable auxiliary port */ +#define PSM_DISABLE 0xa7 /* disable auxiliary port */ +#define PSM_INT_ENABLE 0x47 /* enable controller interrupts */ +#define PSM_INT_DISABLE 0x65 /* disable controller interrupts */ + +/* m+use commands */ +#define PSM_SET_SCALE11 0xe6 /* set 1:1 scaling */ +#define PSM_SET_SCALE21 0xe7 /* set 2:1 scaling */ +#define PSM_SET_RES 0xe8 /* set resolution */ +#define PSM_GET_SCALE 0xe9 /* set scaling factor */ +#define PSM_SET_STREAM 0xea /* set streaming mode */ +#define PSM_SET_SAMPLE 0xf3 /* set sampling rate */ +#define PSM_DEV_ENABLE 0xf4 /* mouse on */ +#define PSM_DEV_DISABLE 0xf5 /* mouse off */ +#define PSM_RESET 0xff /* reset */ + +#define PSMUNIT(dev) (minor(dev) >> 1) + +#ifndef min +#define min(x,y) (x < y ? x : y) +#endif min + +int psmprobe (struct isa_device *); +int psmattach (struct isa_device *); +void psm_poll_status(void); + +static int psmaddr[NPSM]; /* Base I/O port addresses per unit */ + +#define MSBSZ 1024 /* Output queue size (pwr of 2 is best) */ + +struct ringbuf { + int count, first, last; + char queue[MSBSZ]; +}; + +static struct psm_softc { /* Driver status information */ + struct ringbuf inq; /* Input queue */ + pid_t rsel; /* Process selecting for Input */ + unsigned char state; /* Mouse driver state */ + unsigned char status; /* Mouse button status */ + unsigned char button; /* Previous mouse button status bits */ + int x, y; /* accumulated motion in the X,Y axis */ +} psm_softc[NPSM]; + +#define OPEN 1 /* Device is open */ +#define ASLP 2 /* Waiting for mouse data */ + +struct isa_driver psmdriver = { psmprobe, psmattach, "psm" }; + +#define AUX_PORT 0x60 /* AUX_PORT base (S.Yuen) */ + +static void psm_write_dev(int inport, u_char value) +{ + psm_poll_status(); + outb(inport+CNTRL, 0xd4); + psm_poll_status(); + outb(inport+DATA,value); +} + +static inline void psm_command(int ioport, u_char value) +{ + psm_poll_status(); + outb(ioport+CNTRL, 0x60); + psm_poll_status(); + outb(ioport+DATA, value); +} + +int psmprobe(struct isa_device *dvp) +{ + /* XXX: Needs a real probe routine. */ + + int ioport,c,unit; + + ioport=dvp->id_iobase; + unit=dvp->id_unit; + psm_write_dev(ioport,0xff); /* Reset aux device */ + psm_poll_status(); + outb(ioport+CNTRL,0xa9); + psm_poll_status(); + outb(ioport+CNTRL,0xaa); + c = inb(ioport+DATA); + if(c&0x04) { +/* printf("PS/2 AUX mouse is not found\n");*/ + psm_command(ioport,0x65); + psmaddr[unit] = 0; /* Device not found */ + return(0);} +/* printf("PS/2 AUX mouse found. Installing driver\n");*/ + return (4); +} + +int psmattach(struct isa_device *dvp) +{ + int unit = dvp->id_unit; + int ioport = dvp->id_iobase; + struct psm_softc *sc = &psm_softc[unit]; + + /* Save I/O base address */ + + psmaddr[unit] = ioport; + + /* Disable mouse interrupts */ + + psm_poll_status(); + outb(ioport+CNTRL, PSM_ENABLE); +#ifdef 0 + psm_write(ioport, PSM_SET_RES); + psm_write(ioport, 0x03); /* 8 counts/mm */ + psm_write(ioport, PSM_SET_SCALE); + psm_write(ioport, 0x02); /* 2:1 */ + psm_write(ioport, PSM_SET_SCALE21); + psm_write(ioport, PSM_SET_SAMPLE); + psm_write(ioport, 0x64); /* 100 samples/sec */ + psm_write(ioport, PSM_SET_STREAM); +#endif + psm_poll_status(); + outb(ioport+CNTRL, PSM_DISABLE); + psm_command(ioport, PSM_INT_DISABLE); + + /* Setup initial state */ + + sc->state = 0; + + /* Done */ + + return(0); +} + +int psmopen(dev_t dev, int flag, int fmt, struct proc *p) +{ + int unit = PSMUNIT(dev); + struct psm_softc *sc; + int ioport; + + /* Validate unit number */ + + if (unit >= NPSM) + return(ENXIO); + + /* Get device data */ + + sc = &psm_softc[unit]; + ioport = psmaddr[unit]; + + /* If device does not exist */ + + if (ioport == 0) + return(ENXIO); + + /* Disallow multiple opens */ + if (sc->state & OPEN) + return(EBUSY); + + /* Initialize state */ + + sc->state |= OPEN; + sc->rsel = 0; + sc->status = 0; + sc->button = 0; + sc->x = 0; + sc->y = 0; + + /* Allocate and initialize a ring buffer */ + + sc->inq.count = sc->inq.first = sc->inq.last = 0; + + /* Enable Bus Mouse interrupts */ + + psm_write_dev(ioport, PSM_DEV_ENABLE); + psm_poll_status(); + outb(ioport+CNTRL, PSM_ENABLE); + psm_command(ioport, PSM_INT_ENABLE); + + /* Successful open */ + + return(0); +} + +void psm_poll_status(void) +{ + + while(inb(AUX_PORT+STATUS)&0x03) { + if(inb(AUX_PORT+STATUS) & 0x2 == 0x2) + inb(AUX_PORT+DATA);} + return; +} + + +int psmclose(dev_t dev, int flag, int fmt, struct proc *p) +{ + int unit, ioport; + struct psm_softc *sc; + + /* Get unit and associated info */ + + unit = PSMUNIT(dev); + sc = &psm_softc[unit]; + ioport = psmaddr[unit]; + + /* Disable further mouse interrupts */ + + psm_command(ioport,PSM_INT_DISABLE); + psm_poll_status(); + outb(ioport+CNTRL,PSM_DISABLE ); + + /* Complete the close */ + + sc->state &= ~OPEN; + + /* close is almost always successful */ + + return(0); +} + +int psmread(dev_t dev, struct uio *uio, int flag) +{ + int s; + int error = 0; /* keep compiler quiet, even though initialisation + is unnecessary */ + unsigned length; + struct psm_softc *sc; + unsigned char buffer[100]; + + /* Get device information */ + + sc = &psm_softc[PSMUNIT(dev)]; + + /* Block until mouse activity occured */ + + s = spltty(); + while (sc->inq.count == 0) { + if (minor(dev) & 0x1) { + splx(s); + return(EWOULDBLOCK); + } + sc->state |= ASLP; + error = tsleep((caddr_t)sc, PZERO | PCATCH, "psmrea", 0); + if (error != 0) { + splx(s); + return(error); + } + } + + /* Transfer as many chunks as possible */ + + while (sc->inq.count > 0 && uio->uio_resid > 0) { + length = min(sc->inq.count, uio->uio_resid); + if (length > sizeof(buffer)) + length = sizeof(buffer); + + /* Remove a small chunk from input queue */ + + if (sc->inq.first + length >= MSBSZ) { + bcopy(&sc->inq.queue[sc->inq.first], + buffer, MSBSZ - sc->inq.first); + bcopy(sc->inq.queue, &buffer[MSBSZ-sc->inq.first], + length - (MSBSZ - sc->inq.first)); + } + else + bcopy(&sc->inq.queue[sc->inq.first], buffer, length); + + sc->inq.first = (sc->inq.first + length) % MSBSZ; + sc->inq.count -= length; + + /* Copy data to user process */ + + error = uiomove(buffer, length, uio); + if (error) + break; + } + + sc->x = sc->y = 0; + + /* Allow interrupts again */ + + splx(s); + return(error); +} + +int psmioctl(dev_t dev, caddr_t addr, int cmd, int flag, struct proc *p) +{ + struct psm_softc *sc; + struct mouseinfo info; + int s, error; + + /* Get device information */ + + sc = &psm_softc[PSMUNIT(dev)]; + + /* Perform IOCTL command */ + + switch (cmd) { + + case MOUSEIOCREAD: + + /* Don't modify info while calculating */ + + s = spltty(); + + /* Build mouse status octet */ + + info.status = sc->status; + if (sc->x || sc->y) + info.status |= MOVEMENT; + + /* Encode X and Y motion as good as we can */ + + if (sc->x > 127) + info.xmotion = 127; + else if (sc->x < -128) + info.xmotion = -128; + else + info.xmotion = sc->x; + + if (sc->y > 127) + info.ymotion = 127; + else if (sc->y < -128) + info.ymotion = -128; + else + info.ymotion = sc->y; + + /* Reset historical information */ + + sc->x = 0; + sc->y = 0; + sc->status &= ~BUTCHNGMASK; + + /* Allow interrupts and copy result buffer */ + + splx(s); + error = copyout(&info, addr, sizeof(struct mouseinfo)); + break; + + default: + error = EINVAL; + break; + } + + /* Return error code */ + + return(error); +} + +void psmintr(unit) + int unit; +{ + struct psm_softc *sc = &psm_softc[unit]; + int ioport = psmaddr[unit]; + + sc->inq.queue[sc->inq.last++ % MSBSZ] = inb(ioport+DATA); + sc->inq.count++; + if (sc -> state & ASLP) { + sc->state &= ~ASLP; + wakeup((caddr_t)sc); + } + if (sc->rsel) { + selwakeup(sc->rsel, 0); + sc->rsel = 0; + } +} + +int psmselect(dev_t dev, int rw, struct proc *p) +{ + int s, ret; + struct psm_softc *sc = &psm_softc[PSMUNIT(dev)]; + + /* Silly to select for output */ + + if (rw == FWRITE) + return(0); + + /* Return true if a mouse event available */ + + s = spltty(); + if (sc->inq.count) + ret = 1; + else { + sc->rsel = p->p_pid; + ret = 0; + } + splx(s); + + return(ret); +} +#endif + + diff --git a/sys/i386/isa/seagate.c b/sys/i386/isa/seagate.c new file mode 100644 index 000000000000..31c515166663 --- /dev/null +++ b/sys/i386/isa/seagate.c @@ -0,0 +1,2036 @@ +/* + * (Free/Net/386)BSD ST01/02, Future Domain TMC-885, TMC-950 SCSI driver for + * Julians SCSI-code + * + * Copyright 1994, Kent Palmkvist (kentp@isy.liu.se) + * Copyright 1994, Robert Knier (rknier@qgraph.com) + * Copyright 1992, 1994 Drew Eckhardt (drew@colorado.edu) + * Copyright 1994, Julian Elischer (julian@tfs.com) + * + * Others that has contributed by example code is + * Glen Overby (overby@cray.com) + * Tatu Yllnen + * Brian E Litzinger + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPERS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * + * kentp 940307 alpha version based on newscsi-03 version of Julians SCSI-code + * kentp 940314 Added possibility to not use messages + * rknier 940331 Added fast transfer code + * rknier 940407 Added assembler coded data transfers + * + * $Id: seagate.c,v 1.3 1994/06/16 13:26:14 sean Exp $ + */ + +/* + * What should really be done: + * + * Add missing tests for timeouts + * Restructure interrupt enable/disable code (runs to long with int disabled) + * Find bug? giving problem with tape status + * Add code to handle Future Domain 840, 841, 880 and 881 + * adjust timeouts (startup is very slow) + * add code to use tagged commands in SCSI2 + * Add code to handle slow devices better (sleep if device not disconnecting) + * Fix unnecessary interrupts + */ + +/* Note to users trying to share a disk between DOS and unix: + * The ST01/02 is a translating host-adapter. It is not giving DOS + * the same number of heads/tracks/sectors as specified by the disk. + * It is therefore important to look at what numbers DOS thinks the + * disk has. Use these to disklabel your disk in an appropriate manner + */ + +#include <sys/types.h> + +#ifdef KERNEL /* don't laugh.. look for main() */ +#include <sea.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <i386/isa/isa_device.h> +#endif /* KERNEL */ +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#ifdef KERNEL +#include "ddb.h" +#include "kernel.h" +#else /* KERNEL */ +#define NSEA 1 +#endif /* KERNEL */ + +extern int hz; + +#define SEA_SCB_MAX 8 /* allow maximally 8 scsi control blocks */ +#define SCB_TABLE_SIZE 8 /* start with 8 scb entries in table */ +#define BLOCK_SIZE 512 /* size of READ/WRITE areas on SCSI card */ + +/* + * defining PARITY causes parity data to be checked + */ +#define PARITY 1 + +/* + * defining SEA_BLINDTRANSFER will make DATA IN and DATA OUT to be done with + * blind transfers, i.e. no check is done for scsi phase changes. This will + * result in data loss if the scsi device does not send its data using + * BLOCK_SIZE bytes at a time. + * If SEA_BLINDTRANSFER defined and SEA_ASSEMBLER also defined will result in + * the use of blind transfers coded in assembler. SEA_ASSEMBLER is no good + * without SEA_BLINDTRANSFER defined. + */ +#define SEA_BLINDTRANSFER 1 /* do blind transfers */ +#define SEA_ASSEMBLER 1 /* Use assembly code for fast transfers */ + +/* + * defining SEANOMSGS causes messages not to be used (thereby disabling + * disconnects) + */ +/* #define SEANOMSGS 1 */ + +/* + * defining SEA_NODATAOUT makes dataout phase being aborted + */ +/* #define SEA_NODATAOUT 1 */ + +/* + * defining SEA_SENSEFIRST make REQUEST_SENSE opcode to be placed first + */ +/* #define SEA_SENSEFIRST 1 */ + +#define SEA_FREEBSD11 1 /* intermediate def. for FreeBSD 1.1 BETA */ + /* timeout function has changed */ + +/* Debugging definitions. Should not be used unless you want a lot of + printouts even under normal conditions */ + +/* #define SEADEBUG 1 */ /* General info about errors */ +/* #define SEADEBUG1 1 */ /* Info about internal results and errors */ +/* #define SEADEBUG2 1 */ /* Display a lot about timeouts etc */ +/* #define SEADEBUG3 1 */ +/* #define SEADEBUG4 1 */ +/* #define SEADEBUG5 1 */ +/* #define SEADEBUG6 1 */ /* Display info about queue-lengths */ +/* #define SEADEBUG7 1 */ /* Extra check on STATUS before phase check */ +/* #define SEADEBUG8 1 */ /* Disregard non-BSY state in + sea_information_transfer */ +/* #define SEADEBUG9 1 */ /* Enable printouts */ +/* #define SEADEBUG11 1 */ /* stop everything except access to scsi id 1 */ +/* #define SEADEBUG15 1 */ /* Display every byte sent/received */ + +#define NUM_CONCURRENT 1 /* number of concurrent ops per board */ + +/******************************* board definitions **************************/ +/* + * CONTROL defines + */ + +#define CMD_RST 0x01 /* scsi reset */ +#define CMD_SEL 0x02 /* scsi select */ +#define CMD_BSY 0x04 /* scsi busy */ +#define CMD_ATTN 0x08 /* scsi attention */ +#define CMD_START_ARB 0x10 /* start arbitration bit */ +#define CMD_EN_PARITY 0x20 /* enable scsi parity generation */ +#define CMD_INTR 0x40 /* enable scsi interrupts */ +#define CMD_DRVR_ENABLE 0x80 /* scsi enable */ + +/* + * STATUS + */ + +#define STAT_BSY 0x01 /* scsi busy */ +#define STAT_MSG 0x02 /* scsi msg */ +#define STAT_IO 0x04 /* scsi I/O */ +#define STAT_CD 0x08 /* scsi C/D */ +#define STAT_REQ 0x10 /* scsi req */ +#define STAT_SEL 0x20 /* scsi select */ +#define STAT_PARITY 0x40 /* parity error bit */ +#define STAT_ARB_CMPL 0x80 /* arbitration complete bit */ + +/* + * REQUESTS + */ + +#define REQ_MASK (STAT_CD | STAT_IO | STAT_MSG) +#define REQ_DATAOUT 0 +#define REQ_DATAIN STAT_IO +#define REQ_CMDOUT STAT_CD +#define REQ_STATIN (STAT_CD | STAT_IO) +#define REQ_MSGOUT (STAT_MSG | STAT_CD) +#define REQ_MSGIN (STAT_MSG | STAT_CD | STAT_IO) + +#define REQ_UNKNOWN 0xff + +#define SEAGATERAMOFFSET 0x00001800 + +#ifdef PARITY + #define BASE_CMD (CMD_EN_PARITY | CMD_INTR) +#else + #define BASE_CMD (CMD_INTR) +#endif + +#define SEAGATE 1 +#define FD 2 + +/****************************************************************************** + * This should be placed in a more generic file (presume in /sys/scsi) + * Message codes: + */ +#define MSG_ABORT 0x06 +#define MSG_NOP 0x08 +#define MSG_COMMAND_COMPLETE 0x00 +#define MSG_DISCONNECT 0x04 +#define MSG_IDENTIFY 0x80 +#define MSG_BUS_DEV_RESET 0x0c +#define MSG_MESSAGE_REJECT 0x07 +#define MSG_SAVE_POINTERS 0x02 +#define MSG_RESTORE_POINTERS 0x03 +/******************************************************************************/ + +#define IDENTIFY(can_disconnect,lun) (MSG_IDENTIFY | ((can_disconnect) ? \ + 0x40 : 0) | ((lun) & 0x07)) + +/* scsi control block used to keep info about a scsi command */ +struct sea_scb +{ + int flags; /* status of the instruction */ +#define SCB_FREE 0 +#define SCB_ACTIVE 1 +#define SCB_ABORTED 2 +#define SCB_TIMEOUT 4 +#define SCB_ERROR 8 +#define SCB_TIMECHK 16 /* We have set a timeout on this one */ + struct sea_scb *next; /* in free list */ + struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */ + u_char * data; /* position in data buffer so far */ + int32 datalen; /* bytes remaining to transfer */; +}; + +/* + * data structure describing current status of the scsi bus. One for each + * controller card. + */ +struct sea_data +{ + caddr_t basemaddr; /* Base address for card */ + char ctrl_type; /* FD or SEAGATE */ + caddr_t st0x_cr_sr; /* Address of control and status register */ + caddr_t st0x_dr; /* Address of data register */ + u_short vect; /* interrupt vector for this card */ + int our_id; /* our scsi id */ + int numscb; /* number of scsi control blocks */ + struct scsi_link sc_link; /* struct connecting different data */ + struct sea_scb *connected; /* currently connected command */ + struct sea_scb *issue_queue; /* waiting to be issued */ + struct sea_scb *disconnected_queue; /* waiting to reconnect */ + struct sea_scb scbs[SCB_TABLE_SIZE]; + struct sea_scb *free_scb; /* free scb list */ + volatile unsigned char busy[8]; /* index=target, bit=lun, Keep track of + busy luns at device target */ +} *seadata[NSEA]; + +/* flag showing if main routine is running. */ +static volatile int main_running = 0; + +#define STATUS (*(volatile unsigned char *) sea->st0x_cr_sr) +#define CONTROL STATUS +#define DATA (*(volatile unsigned char *) sea->st0x_dr) + +/* + * These are "special" values for the tag parameter passed to sea_select + * Not implemented right now. + */ + +#define TAG_NEXT -1 /* Use next free tag */ +#define TAG_NONE -2 /* + * Establish I_T_L nexus instead of I_T_L_Q + * even on SCSI-II devices. + */ + +typedef struct { + char *signature ; + unsigned offset; + unsigned length; + unsigned char type; +} BiosSignature; + +/* + * Signatures for automatic recognition of board type + */ + +static const BiosSignature signatures[] = { +{"ST01 v1.7 (C) Copyright 1987 Seagate", 15, 37, SEAGATE}, +{"SCSI BIOS 2.00 (C) Copyright 1987 Seagate", 15, 40, SEAGATE}, + +/* + * The following two lines are NOT mistakes. One detects ROM revision + * 3.0.0, the other 3.2. Since seagate has only one type of SCSI adapter, + * and this is not going to change, the "SEAGATE" and "SCSI" together + * are probably "good enough" + */ + +{"SEAGATE SCSI BIOS ", 16, 17, SEAGATE}, +{"SEAGATE SCSI BIOS ", 17, 17, SEAGATE}, + + /* + * However, future domain makes several incompatible SCSI boards, so specific + * signatures must be used. + */ + + {"FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89", 5, 45, FD}, + {"FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89", 5, 46, FD}, + {"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90",5, 47, FD}, + {"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90",5, 47, FD}, + {"FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90", 5, 46, FD}, + {"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92", 5, 44, FD}, + {"FUTURE DOMAIN TMC-950", 5, 21, FD}, + }; + +#define NUM_SIGNATURES (sizeof(signatures) / sizeof(BiosSignature)) + +static const char * seagate_bases[] = { + (char *) 0xc8000, (char *) 0xca000, (char *) 0xcc000, + (char *) 0xce000, (char *) 0xdc000, (char *) 0xde000 +}; + +#define NUM_BASES (sizeof(seagate_bases)/sizeof(char *)) + +int sea_probe(struct isa_device *dev); +int sea_attach(struct isa_device *dev); +int seaintr(int unit); +int32 sea_scsi_cmd(struct scsi_xfer *xs); +#ifdef SEA_FREEBSD11 +void sea_timeout(caddr_t, int); +#else +void sea_timeout(struct sea_scb *scb); +#endif +void seaminphys(struct buf *bp); +void sea_done(int unit, struct sea_scb *scb); +u_int32 sea_adapter_info(int unit); +struct sea_scb *sea_get_scb(int unit, int flags); +void sea_free_scb(int unit, struct sea_scb *scb, int flags); +static void sea_main(void); +static void sea_information_transfer(struct sea_data *sea); +int sea_poll(int unit, struct scsi_xfer *xs, struct sea_scb *scb); +int sea_init(int unit); +int sea_send_scb(struct sea_data *sea, struct sea_scb *scb); +int sea_reselect(struct sea_data *sea); +int sea_select(struct sea_data *sea, struct sea_scb *scb); +int sea_transfer_pio(struct sea_data *sea, u_char *phase, int32 *count, + u_char **data); +int sea_abort(int unit, struct sea_scb *scb); + +static sea_unit = 0; +static sea_slot = -1; /* last found board seagate_bases address index */ +#define FAIL 1 +#define SUCCESS 0 + +#ifdef KERNEL +struct scsi_adapter sea_switch = +{ + sea_scsi_cmd, + seaminphys, + 0, + 0, + sea_adapter_info, + "sea", + 0,0 +}; + +/* the below structure is so we have a default dev struct for our link struct */ +struct scsi_device sea_dev = +{ + NULL, /* use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ + "sea", + 0, + 0,0 +}; + +struct isa_driver seadriver = +{ + sea_probe, + sea_attach, + "sea" +}; + +#endif /* KERNEL */ + +#ifdef SEADEBUG6 +void sea_queue_length() +{ + struct sea_scb *tmp; + int length = 0; + + if(seadata[0]->connected) + length = 1; + for(tmp = seadata[0]->issue_queue; tmp != NULL; tmp = tmp->next, length++); + for(tmp = seadata[0]->disconnected_queue ; tmp != NULL; tmp->next, length++); + printf("length:%d ",length); +} +#endif + +/***********************************************************************\ +* Check if the device can be found at the port given and if so, detect * +* the type of board. Set it up ready for further work. Takes the * +* isa_dev structure from autoconf as an argument. * +* Returns 1 if card recognized, 0 if errors * +\***********************************************************************/ +int +sea_probe(dev) +struct isa_device *dev; +{ + int j; + int unit = sea_unit; + struct sea_data *sea; + dev->id_unit = unit; + +#ifdef SEADEBUG2 + printf("sea_probe "); +#endif + + /* find unit and check we have that many defined */ + if(unit >= NSEA) { + printf("sea%d: unit number too high\n",unit); + return(0); + } + dev->id_unit = unit; +#ifdef SEADEBUG2 + printf("unit: %d\n",unit); + printf("dev_addr: 0x%lx\n",dev->id_maddr); +#endif + /* allocate a storage area for us */ + + if (seadata[unit]) { + printf("sea%d: memory already allocated\n", unit); + return(0); + } +#ifdef SEADEBUG2 + printf("Before malloc\n"); +#endif + sea = malloc(sizeof(struct sea_data), M_TEMP, M_NOWAIT); + if (!sea) { + printf("sea%d: cannot malloc!\n", unit); + return(0); + } + +#ifdef SEADEBUG2 + printf("after malloc\n"); + for(j=0;j<32767;j++); +#endif + bzero(sea,sizeof(struct sea_data)); + seadata[unit] = sea; + + /* check for address if no one specified */ + sea->basemaddr = NULL; + + /* Could try to find a board by looking through all possible addresses */ + /* This is not done the right way now, because I have not found a way */ + /* to get a boards virtual memory address given its physical. There is */ + /* a function that returns the physical address for a given virtual */ + /* address, but not the other way around */ + + if(dev->id_maddr == 0) { +/* + for(sea_slot++;sea_slot<NUM_BASES;sea_slot++) + for(j = 0; !sea->basemaddr && j < NUM_SIGNATURES; ++j) + if(!memcmp((void *)(seagate_bases[sea_slot]+signatures[j].offset), + (void *) signatures[j].signature, signatures[j].length)) { + sea->basemaddr = (void *)seagate_bases[sea_slot]; + break; + } +*/ + } else { + +#ifdef SEADEBUG2 + printf("id_maddr != 0\n"); + for(j = 0; j < 32767 ; j++); + for(j = 0; j < 32767 ; j++); +#endif + /* find sea_slot position for overridden memory address */ + for(j = 0; ((char *)vtophys(dev->id_maddr) != seagate_bases[j]) && + j<NUM_BASES; ++j); + if(j == NUM_BASES) { + printf("sea: board not expected at address 0x%lx\n",dev->id_maddr); + seadata[unit]=NULL; + free(sea, M_TEMP); + return(0); + } else if(sea_slot > j) { + printf("sea: board address 0x%lx already probed!\n", dev->id_maddr); + seadata[unit]=NULL; + free(sea, M_TEMP); + return(0); + } else { + sea->basemaddr = dev->id_maddr; + } + + } +#ifdef SEADEBUG2 + printf("sea->basemaddr = %lx\n", sea->basemaddr); +#endif + + /* check board type */ /* No way to define this through config */ + for(j = 0; j < NUM_SIGNATURES; j++) + if(!memcmp((void *) (sea->basemaddr + signatures[j].offset), + (void *) signatures[j].signature, signatures[j].length)) { + sea->ctrl_type = signatures[j].type; + break; + } + if(j == NUM_SIGNATURES) { + printf("sea: Board type unknown at address 0x%lx\n", + sea->basemaddr); + seadata[unit]=NULL; + free(sea, M_TEMP); + return(0); + } + + /* Find controller and data memory addresses */ + sea->st0x_cr_sr = (void *) (((unsigned char *) sea->basemaddr) + + ((sea->ctrl_type == SEAGATE) ? 0x1a00 : 0x1c00)); + sea->st0x_dr = (void *) (((unsigned char *) sea->basemaddr) + + ((sea->ctrl_type == SEAGATE) ? 0x1c00 : 0x1e00)); + + /* Test controller RAM (works the same way on future domain cards?) */ + *(sea->basemaddr + SEAGATERAMOFFSET) = 0xa5; + *(sea->basemaddr + SEAGATERAMOFFSET + 1) = 0x5a; + + if((*(sea->basemaddr + SEAGATERAMOFFSET) != (char) 0xa5) || + (*(sea->basemaddr + SEAGATERAMOFFSET + 1) != (char) 0x5a)) { + printf("sea%d: Board RAM failure\n",unit); + } + + if(sea_init(unit) != 0) { + seadata[unit] = NULL; + free(sea,M_TEMP); + return(0); + } + + /* if its there put in it's interrupt vector */ + /* (Doesn't use dma, so no drq is set) */ + sea->vect = dev->id_irq; + + sea_unit++; + return(1); +} + +/***********************************************\ +* Attach all sub-devices we can find * +\***********************************************/ +int +sea_attach(dev) + struct isa_device *dev; +{ + int unit = dev->id_unit; + struct sea_data *sea = seadata[unit]; + +#ifdef SEADEBUG2 + printf("sea_attach called\n"); +#endif + + /* fill in the prototype scsi_link */ + sea->sc_link.adapter_unit = unit; + sea->sc_link.adapter_targ = sea->our_id; + sea->sc_link.adapter = &sea_switch; + sea->sc_link.device = &sea_dev; + + /*****************************************************\ + * ask the adapter what subunits are present * + \*****************************************************/ + scsi_attachdevs(&(sea->sc_link)); + return 1; +} + +/***********************************************\ +* Return some information to the caller about * +* the adapter and its capabilities * +\***********************************************/ +u_int32 +sea_adapter_info(unit) + int unit; +{ +#ifdef SEADEBUG2 + printf("sea_adapter_info called\n"); +#endif + return 1; +} + +/***********************************************\ +* Catch an interrupt from the adaptor * +\***********************************************/ +int +seaintr(unit) + int unit; +{ + int done; + struct sea_data *sea = seadata[unit]; + int oldpri; + +#if SEADEBUG2 + printf(";"); +#endif + + do { + done = 1; + /* dispatch to appropriate routine if found and done=0 */ + /* should check to see that this card really caused the interrupt */ + if ((STATUS & (STAT_SEL | STAT_IO)) == (STAT_SEL | STAT_IO)) { + /* Reselect interrupt */ +#ifdef SEADEBUG2 + printf(";2"); +#endif + done = 0; +/* enable_intr(); */ /* ?? How should this be done ?? */ + sea_reselect(sea); + } else if (STATUS & STAT_PARITY) { + /* Parity error interrupt */ +#ifdef SEADEBUG2 + printf(";3"); +#endif + printf("sea%d: PARITY interrupt\n", unit); + } else { +#ifdef SEADEBUG2 +/* printf("sea%d: unknown interrupt\n",unit); */ + printf(";4%x", STATUS); +#endif + } + if (!done) { + oldpri = splbio(); /* disable_intr(); */ + if (!main_running) { +#ifdef SEADEBUG2 + printf(";5"); +#endif + main_running = 1; + sea_main(); + /* main_running is cleared in sea_main once it can't + * do more work, and sea_main exits with interrupts + * disabled + */ + splx(oldpri); /* enable_intr(); */ + } else { + splx(oldpri); /* enable_intr(); */ + } + } + } while (!done); + return 1; +} + +/***********************************************\ +* Setup data structures, and reset the board * +* and the scsi bus * +\***********************************************/ +int +sea_init(unit) + int unit; +{ + long l; + int i; + struct sea_data *sea = seadata[unit]; + +#ifdef SEADEBUG2 + printf("sea_init called\n"); +#endif +/* Reset the scsi bus (I don't know if this is needed */ + CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_RST; + DELAY(25); /* hold reset for at least 25 microseconds */ + CONTROL = BASE_CMD; + DELAY(10); /* wait a Bus Clear Delay (800 ns + bus free delay (800 ns) */ + /* Set our id (don't know anything about this) */ + if(sea->ctrl_type == SEAGATE) + sea->our_id = 7; + else + sea->our_id = 6; + /* init fields used by our routines */ + sea->connected = NULL; + sea->issue_queue = NULL; + sea->disconnected_queue = NULL; + for (i=0; i<8 ; i++) + sea->busy[i] = 0; + + /* link up the free list of scbs */ + sea->numscb = SCB_TABLE_SIZE; + sea->free_scb = (struct sea_scb *) & (sea->scbs[0]); + for(i=1;i< SCB_TABLE_SIZE ; i++) { + sea->scbs[i-1].next = &(sea->scbs[i]); + } + sea->scbs[SCB_TABLE_SIZE - 1].next = NULL; + + return(0); +} + +/***********************************************\ +* * +\***********************************************/ +void seaminphys(bp) + struct buf *bp; +{ +#ifdef SEADEBUG2 +/* printf("seaminphys called\n"); */ + printf(","); +#endif +} + +/***********************************************\ +* start a scsi operation given the command and * +* the data address. Also needs the unit, target * +* and lu * +* get a free scb and set it up * +* call send_scb * +* either start timer or wait until done * +\***********************************************/ +int32 sea_scsi_cmd(xs) +struct scsi_xfer *xs; +{ + struct scsi_sense_data *s1, *s2; + struct sea_scb *scb; + int i = 0; + int flags; + int unit = xs->sc_link->adapter_unit; + struct sea_data *sea = seadata[unit]; + int s; + unsigned int stat; + int32 result; + +#ifdef SEADEBUG2 + /* printf("scsi_cmd\n"); */ + printf("="); +#endif + +#ifdef SEADEBUG11 + if(xs->sc_link->target != 1) { + xs->flags |= ITSDONE; + xs->error = XS_TIMEOUT; + return(HAD_ERROR); + } +#endif + + flags = xs->flags; + if(xs->bp) flags |= (SCSI_NOSLEEP); + if(flags & ITSDONE) { + printf("sea%d: Already done?", unit); + xs->flags &= ~ITSDONE; + } + if(!(flags & INUSE)) { + printf("sea%d: Not in use?", unit); + xs->flags |= INUSE; + } + if (!(scb = sea_get_scb(unit, flags))) { +#ifdef SEADEBUG2 + printf("=2"); +#endif + xs->error = XS_DRIVER_STUFFUP; + return(TRY_AGAIN_LATER); + } + + /* + * Put all the arguments for the xfer in the scb + */ + scb->xfer = xs; + scb->datalen = xs->datalen; + scb->data = xs->data; + + if(flags & SCSI_RESET) { + /* Try to send a reset command to the card. This is done by calling the + * Reset function. Should then return COMPLETE. Need to take care of the + * possible current connected command. + * Not implemented right now. + */ + printf("sea%d: Got a SCSI_RESET!\n",unit); + } + + /* setup the scb to contain necessary values */ + /* The interresting values can be read from the xs that is saved */ + /* I therefore think that the structure can be kept very small */ + /* the driver doesn't use DMA so the scatter/gather is not needed ? */ +#ifdef SEADEBUG6 + sea_queue_length(); +#endif + if (sea_send_scb(sea, scb) == 0) { +#ifdef SEADEBUG2 + printf("=3"); +#endif + xs->error = XS_DRIVER_STUFFUP; + sea_free_scb(unit, scb, flags); + return (TRY_AGAIN_LATER); + } + + /* + * Usually return SUCCESSFULLY QUEUED + */ + if (!(flags & SCSI_NOMASK)) { + if(xs->flags & ITSDONE) { /* timout timer not started, already finished */ + /* Tried to return COMPLETE but the machine hanged with this */ +#ifdef SEADEBUG2 + printf("=6"); +#endif + return(SUCCESSFULLY_QUEUED); + } +#ifdef SEA_FREEBSD11 + timeout(sea_timeout, (caddr_t)scb, (xs->timeout * hz) / 1000); +#else + timeout(sea_timeout, scb, (xs->timeout * hz) / 1000); +#endif + scb->flags |= SCB_TIMECHK; +#ifdef SEADEBUG2 + printf("=4"); +#endif + return(SUCCESSFULLY_QUEUED); + } + + /* + * If we can't use interrupts, poll on completion + */ + + result = sea_poll(unit, xs, scb); +#ifdef SEADEBUG2 + printf("=5 %lx", result); +#endif + return result; +} + +/* + * Get a free scb. If there are none, see if we can allocate a new one. If so, + * put it in the hash table too, otherwise return an error or sleep. + */ + +struct sea_scb * +sea_get_scb(unit, flags) + int unit; + int flags; +{ + struct sea_data *sea = seadata[unit]; + unsigned opri = 0; + struct sea_scb * scbp; + int hashnum; + +#ifdef SEADEBUG2 +/* printf("get_scb\n"); */ + printf("("); +#endif + + if (!(flags & SCSI_NOMASK)) + opri = splbio(); + +#ifdef SEADEBUG3 + printf("(2 %lx ", sea->free_scb); +#endif + + /* + * If we can and have to, sleep waiting for one to come free + * but only if we can´t allocate a new one. + */ + while (!(scbp = sea->free_scb)) { +#ifdef SEADEBUG12 + printf("(3"); +#endif + if (sea->numscb < SEA_SCB_MAX) { + printf("malloced new scbs\n"); + if (scbp = (struct sea_scb *) malloc(sizeof(struct sea_scb), + M_TEMP, M_NOWAIT)) { + bzero(scbp, sizeof(struct sea_scb)); + sea->numscb++; + scbp->flags = SCB_ACTIVE; + scbp->next = NULL; + } else { + printf("sea%d: Can't malloc SCB\n",unit); + } + goto gottit; + } else { +#ifdef SEADEBUG12 + printf("(4"); +#endif + if(!(flags & SCSI_NOSLEEP)) { +#ifdef SEADEBUG2 + printf("(5"); +#endif + tsleep(&sea->free_scb, PRIBIO, "seascb", 0); + } + } + } + if (scbp) { +#ifdef SEADEBUG2 + printf("(6"); +#endif + /* Get SCB from free list */ + sea->free_scb = scbp->next; + scbp->next = NULL; + scbp->flags = SCB_ACTIVE; + } + gottit: + if (!(flags & SCSI_NOMASK)) + splx(opri); + + return(scbp); +} + +/* + * sea_send_scb + * + * Try to send this command to the board. Because this board does not use any + * mailboxes, this routine simply adds the command to the queue held by the + * sea_data structure. + * A check is done to see if the command contains a REQUEST_SENSE command, and + * if so the command is put first in the queue, otherwise the command is added + * to the end of the queue. ?? Not correct ?? + */ +int +sea_send_scb(struct sea_data *sea, struct sea_scb *scb) +{ + struct sea_scb *tmp; + int oldpri = 0; + +#ifdef SEADEBUG2 + printf("+"); +#endif + + if(!(scb->xfer->flags & SCSI_NOSLEEP)) { + oldpri = splbio(); + } + + /* add to head of queue if queue empty or command is REQUEST_SENSE */ + + if (!(sea->issue_queue) +#ifdef SEA_SENSEFIRST + || (scb->xfer->cmd->opcode == (u_char) REQUEST_SENSE) +#endif + ) { +#ifdef SEADEBUG2 + printf("+2"); +#endif + scb->next = sea->issue_queue; + sea->issue_queue = scb; + } else { +#ifdef SEADEBUG2 + printf("+3"); +#endif + for (tmp = sea->issue_queue; tmp->next; tmp = tmp->next); + tmp->next = scb; + scb->next = NULL; /* placed at the end of the queue */ + } + /* Try to do some work on the card */ + if (!main_running) { + main_running = 1; + sea_main(); + /* main running is cleared in sea_main once it can't + * do more work, and sea_main exits with interrupts + * disabled + */ + } + if(!(scb->xfer->flags & SCSI_NOSLEEP)) { + splx(oldpri); + } + return (1); /* No possible errors right now */ +} + +/* + * sea_main(void) + * + * corroutine that runs as long as more work can be done on the seagate host + * adapter in a system. Both sea_scsi_cmd and sea_intr will try to start it in + * case it is not running. + */ + +static void sea_main(void) +{ + struct sea_data *sea; /* This time we look at all cards */ + struct sea_scb *tmp, *prev; + int done; + int unit; + int oldpri; + +#ifdef SEADEBUG2 + printf("."); +#endif + + /* + * This should not be run with interrupts disabled, but use the splx code + * instead + */ + do { + done = 1; + for (sea=seadata[unit=0]; (unit < NSEA) && seadata[unit] ; + sea=seadata[++unit]) { + oldpri = splbio(); + if (!sea->connected) { +#ifdef SEADEBUG2 + printf(".2"); +#endif + /* + * Search through the issue_queue for a command destined for a + * target that's not busy. + */ + for (tmp = sea->issue_queue, prev = NULL; tmp ; + prev = tmp, tmp = tmp->next) + /* When we find one, remove it from the issue queue. */ + if (!(sea->busy[tmp->xfer->sc_link->target] & + (1 << tmp->xfer->sc_link->lun))) { + if (prev) + prev->next = tmp->next; + else + sea->issue_queue = tmp->next; + tmp->next = NULL; + + /* re-enable interrupts after finding one */ + splx(oldpri); + + /* + * Attempt to establish an I_T_L nexus here. + * On success, sea->connected is set. + * On failure, we must add the command back to + * the issue queue so we can keep trying. + */ +#ifdef SEADEBUG2 + printf(".3"); +#endif + + /* REQUEST_SENSE commands are issued without tagged + * queueing, even on SCSI-II devices because the + * contingent alligence condition exists for the + * entire unit. + */ + + /* First check that if any device has tried a reconnect while + * we have done other things with interrupts disabled + */ + + if ((STATUS & (STAT_SEL | STAT_IO)) == (STAT_SEL | STAT_IO)) { +#ifdef SEADEBUG2 + printf(".7"); +#endif + sea_reselect(sea); + break; + } + if (!sea_select(sea, tmp)) { +#ifdef SEADEBUG2 + /* printf("Select returned ok\n"); */ + printf(".4"); +#endif + break; + } else { + oldpri = splbio(); + tmp->next = sea->issue_queue; + sea->issue_queue = tmp; + splx(oldpri); + printf("sea_main: select failed\n"); + } + } /* if target/lun is not busy */ + } /* if (!sea->connected) */ + + if (sea->connected) { /* we are connected. Do the task */ + splx(oldpri); +#ifdef SEADEBUG2 +/* printf("sea_main: starting information transfer!\n"); */ + printf(".5"); +#endif + sea_information_transfer(sea); +#ifdef SEADEBUG2 +/* printf("sea_main: sea->connected:%lx\n", sea->connected); */ + printf(".6%lx ", sea->connected); +#endif + done = 0; + } else + break; + } /* for instance */ + } while (!done); + main_running = 0; +} + +void +sea_free_scb(unit, scb, flags) + int unit; + struct sea_scb *scb; + int flags; +{ + struct sea_data *sea = seadata[unit]; + unsigned int opri = 0; + +#ifdef SEADEBUG2 +/* printf("free_scb\n"); */ + printf(")"); +#endif + + if(!(flags & SCSI_NOMASK)) + opri = splbio(); + + scb->next = sea->free_scb; + sea->free_scb = scb; + scb->flags = SCB_FREE; + /* + * If there were none, wake anybody waiting for one to come free, + * starting with queued entries. + */ + if(!scb->next) { +#ifdef SEADEBUG2 +/* printf("free_scb waking up sleep\n"); */ + printf(")2"); +#endif +#ifdef SEA_FREEBSD11 + wakeup((caddr_t)&sea->free_scb); +#else + wakeup(&sea->free_scb); +#endif + } + + if (!(flags & SCSI_NOMASK)) + splx(opri); +} + +#ifdef SEA_FREEBSD11 +void +sea_timeout(caddr_t arg1, int arg2) +#else +void +sea_timeout(struct sea_scb *scb) +#endif +{ +#ifdef SEA_FREEBSD11 + struct sea_scb *scb = (struct sea_scb *)arg1; +#endif + int unit; + struct sea_data *sea; + int s=splbio(); + +#ifdef SEADEBUG2 +/* printf("sea_timeout called\n"); */ + printf(":"); +#endif + + unit = scb->xfer->sc_link->adapter_unit; + sea = seadata[unit]; +#ifndef SEADEBUG /* print message only if not waiting unless debug */ + if(!(scb->xfer->flags & SCSI_NOMASK)) +#endif + printf("sea%d:%d:%d (%s%d) timed out ", unit, + scb->xfer->sc_link->target, + scb->xfer->sc_link->lun, + scb->xfer->sc_link->device->name, + scb->xfer->sc_link->dev_unit); + + /* + * If it has been through before, then + * a previous abort has failed, don't + * try abort again + */ + if (/* (sea_abort(unit, scb) != 1) ||*/ (scb->flags & SCB_ABORTED)) { + /* + * abort timed out + */ +#ifdef SEADEBUG2 +/* printf("sea%d: Abort Operation has timed out\n", unit); */ + printf(":2"); +#endif + scb->xfer->retries = 0; + scb->flags |= SCB_ABORTED; + sea_done(unit, scb); + } else { + #ifdef SEADEBUG2 + /* printf("sea%d: Try to abort\n", unit); */ + printf(":3"); + #endif + sea_abort(unit, scb); + /* sea_send_scb(sea, ~SCSI_NOMASK, SEA_SCB_ABORT, scb); */ + /* 2 seconds for the abort */ + #ifdef SEA_FREEBSD11 + timeout(sea_timeout, (caddr_t)scb, 2*hz); + #else + timeout(sea_timeout, scb, 2*hz); + #endif + scb->flags |= (SCB_ABORTED | SCB_TIMECHK); + } + splx(s); +} + +int +sea_reselect(sea) + struct sea_data *sea; +{ + unsigned char target_mask; + long l; + unsigned char lun, phase; + unsigned char msg[3]; + int32 len; + u_char *data; + struct sea_scb *tmp = 0, *prev = 0; + int abort = 0; + +#if SEADEBUG2 +/* printf("sea_reselect called\n"); */ + printf("}"); +#endif + + if (!((target_mask = STATUS) & STAT_SEL)) { + printf("sea: wrong state 0x%x\n", target_mask); + return(0); + } + /* wait for a device to win the reselection phase */ + /* signals this by asserting the I/O signal */ + for(l=10; l && (STATUS & (STAT_SEL | STAT_IO | STAT_BSY)) + != (STAT_SEL | STAT_IO | 0);l--); + /* !! Check for timeout here */ + /* the data bus contains original initiator id ORed with target id */ + target_mask = DATA; + /* see that we really are the initiator */ + if (!(target_mask & ((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40))) { + printf("sea: polled reselection was not for me: %x\n",target_mask); + return(0); + } + /* find target who won */ + target_mask &= ((sea->ctrl_type == SEAGATE) ? ~0x80 : ~0x40); + /* host responds by asserting the BSY signal */ + CONTROL = (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY); + /* target should respond by deasserting the SEL signal */ + for(l=50000;l && (STATUS & STAT_SEL);l++); + /* remove the busy status */ + CONTROL = (BASE_CMD | CMD_DRVR_ENABLE); + /* we are connected. Now we wait for the MSGIN condition */ + for(l=50000; l && !(STATUS & STAT_REQ);l--); + /* !! Add timeout check here */ + /* hope we get an IDENTIFY message */ + len = 3; + data = msg; + phase = REQ_MSGIN; + sea_transfer_pio(sea, &phase, &len, &data); + + if (!(msg[0] & 0x80)) { + printf("sea: Expecting IDENTIFY message, got 0x%x\n", msg[0]); + abort = 1; + } else { + lun = (msg[0] & 0x07); + + /* + * Find the command corresponding to the I_T_L or I_T_L_Q nexus we + * just restablished, and remove it from the disconnected queue. + */ + + for(tmp = sea->disconnected_queue, prev = NULL; + tmp; prev=tmp, tmp = tmp->next) + if((target_mask == (1 << tmp->xfer->sc_link->target)) && + (lun == tmp->xfer->sc_link->lun)) { + if(prev) { +#ifdef SEADEBUG2 + printf("}2"); +#endif + prev->next = tmp->next; + } else { +#ifdef SEADEBUG2 + printf("}3"); +#endif + sea->disconnected_queue = tmp->next; + } + tmp->next = NULL; + break; + } + if (!tmp) { + printf("sea: warning : target %02x lun %d not in disconnect_queue\n", + target_mask, lun); + /* + * Since we have an established nexus that we can't do anything with, + * we must abort it. + */ + abort = 1; + } + } + + if(abort) { +#ifdef SEADEBUG2 + printf("}4"); +#endif + msg[0] = MSG_ABORT; + len = 1; + data = msg; + phase = REQ_MSGOUT; + CONTROL = (BASE_CMD | CMD_ATTN); + sea_transfer_pio(sea, &phase, &len, &data); + } else { +#ifdef SEADEBUG2 + printf("}5"); +#endif + sea->connected = tmp; + } + /* return value not used yet */ + return 0; +} + +/* Transfer data in given phase using polled I/O +*/ + +int sea_transfer_pio(struct sea_data *sea, u_char *phase, int32 *count, + u_char **data) +{ + register unsigned char p = *phase, tmp; + register int c = *count; + register unsigned char *d = *data; + unsigned long int timeout; + +#if SEADEBUG2 +/* printf("sea_transfer_pio called: len:%x\n",c); */ + printf("-1 %x %x", c, p); +#endif + + do { + /* wait for assertion of REQ, after which the phase bits will be valid */ + for(timeout = 0; timeout < 5000000L ; timeout++) + if ((tmp = STATUS) & STAT_REQ) + break; + if (!(tmp & STAT_REQ)) { + printf("sea_transfer_pio: timeout waiting for STAT_REQ\n"); + break; + } + + /* check for phase mismatch */ + /* Reached if the target decides that it has finished the transfer */ + if ((tmp & REQ_MASK) != p) { +#ifdef SEADEBUG1 +/* printf("-2 %x", tmp); */ + printf("sea:pio phase mismatch:%x, want:%x, len:%x\n",tmp,p,c); +#endif + break; + } + + /* Do actual transfer from SCSI bus to/from memory */ + if (!(p & STAT_IO)) + DATA = *d; + else + *d = DATA; +#ifdef SEADEBUG15 + printf("-7%x", *d); +#endif + ++d; + + /* The SCSI standard suggests that in MSGOUT phase, the initiator + * should drop ATN on the last byte of the message phase + * after REQ has been asserted for the handshake but before + * the initiator raises ACK. + * Don't know how to accomplish this on the ST01/02 + */ + /* We don't mind right now. */ + + /* The st01 code doesn't wait for STAT_REQ to be deasserted. Is this ok? */ +/* for(timeout=0;timeout<200000L;timeout++) + if(!(STATUS & STAT_REQ)) + break; + if(STATUS & STAT_REQ) + printf("timeout on wait for !STAT_REQ"); */ +/* printf("*"); */ + } while (--c); + + *count = c; + *data = d; + tmp = STATUS; + if (tmp & STAT_REQ) { +#if SEADEBUG2 + printf("-3%x", tmp); +#endif + *phase = tmp & REQ_MASK; + } else { +#if SEADEBUG2 + printf("-4%x", tmp); +#endif + *phase = REQ_UNKNOWN; + } + if (!c || (*phase == p)) { +#if SEADEBUG2 + printf("-5%x %x", c, *phase); +#endif + return 0; + } else { +#if SEADEBUG2 + printf("-6"); +#endif + return -1; + } +} + +/* sea_select + * establish I_T_L or I_T_L_Q nexus for new or existing command + * including ARBITRATION, SELECTION, and initial message out for IDENTIFY and + * queue messages. + * return -1 if selection could not execute for some reason, 0 if selection + * succeded or failed because the taget did not respond + */ +int sea_select(struct sea_data *sea, struct sea_scb *scb) +{ + unsigned char tmp[3], phase; + u_char *data; + int32 len; + unsigned long timeout; + +#ifdef SEADEBUG2 +/* printf("sea_select called\n"); */ + printf("{"); +#endif + + CONTROL = BASE_CMD; + DATA = ((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40); + CONTROL = (BASE_CMD & ~CMD_INTR) | CMD_START_ARB; + /* wait for arbitration to complete */ + for (timeout = 0; timeout < 3000000L ; timeout++) { + if (STATUS & STAT_ARB_CMPL) + break; + } + if (!(STATUS & STAT_ARB_CMPL)) { + if (STATUS & STAT_SEL) { + printf("sea: arbitration lost\n"); + scb->flags |= SCB_ERROR; + } else { + printf("sea: arbitration timeout.\n"); + scb->flags |=SCB_TIMEOUT; + } + CONTROL = BASE_CMD; + return(-1); + } + DELAY(2); + +#if SEADEBUG2 +/* printf("after arbitration: STATUS=%x\n", STATUS); */ + printf("{2 %x", STATUS); +#endif + + DATA = (unsigned char)((1 << scb->xfer->sc_link->target) | + ((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40)); +#ifdef SEANOMSGS + CONTROL = (BASE_CMD & (~CMD_INTR))| CMD_DRVR_ENABLE | CMD_SEL; +#else + CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE | CMD_SEL | CMD_ATTN; +#endif + DELAY(1); + /* wait for a bsy from target */ + for (timeout = 0; timeout < 2000000L; timeout++) { + if (STATUS & STAT_BSY) + break; + } + +#if SEADEBUG2 +/* printf("after wait for BSY: STATUS=%x,count=%lx\n", STATUS, timeout); */ + printf("{3 %x %x", STATUS, timeout); +#endif + + if (!(STATUS & STAT_BSY)) { + /* should return some error to the higher level driver */ + CONTROL = BASE_CMD; +#if SEADEBUG2 +/* printf("sea: target did not respond\n"); */ + printf("{4"); +#endif + scb->flags |= SCB_TIMEOUT; + return 0; + } + + /* Try to make the target to take a message from us */ +#ifdef SEANOMSGS + CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE; +#else + CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE | CMD_ATTN; +#endif + + DELAY(1); + + /* should start a msg_out phase */ + for (timeout = 0; timeout < 2000000L ; timeout++) { + if (STATUS & STAT_REQ) + break; + } + + CONTROL = BASE_CMD | CMD_DRVR_ENABLE; + +#if SEADEBUG2 || SEADEBUG9 +/* printf("after wait for STAT_REQ: STATUS=%x,count=%lx\n", STATUS, timeout); + printf("2:nd try after wait for STAT_REQ: STATUS=%x\n", STATUS); */ + printf("{5%x", timeout); +#endif + + if (!(STATUS & STAT_REQ)) { + /* This should not be taken as an error, but more like an unsupported + * feature! + * Should set a flag indicating that the target don't support messages, and + * continue without failure. (THIS IS NOT AN ERROR!) + */ +#if SEADEBUG +/* printf("{6"); */ + printf("sea: WARNING: target %x don't support messages?\n", + scb->xfer->sc_link->target); +#endif + } else { + tmp[0] = IDENTIFY(1, scb->xfer->sc_link->lun); /* allow disconnects */ + len = 1; + data = tmp; + phase = REQ_MSGOUT; + /* Should do test on result of sea_transfer_pio */ +#if SEADEBUG2 +/* printf("Trying a msg out phase\n"); */ + printf("{7"); +#endif + sea_transfer_pio(sea, &phase, &len, &data); + } + if (!(STATUS & STAT_BSY)) { + printf("sea: after successful arbitrate: No STAT_BSY!\n"); + } + +#if SEADEBUG2 + printf("{8"); +#endif + sea->connected = scb; + sea->busy[scb->xfer->sc_link->target] |= (1 << scb->xfer->sc_link->lun); + /* this assignment should depend on possibility to send a message to target */ + CONTROL = BASE_CMD | CMD_DRVR_ENABLE; + /* reset pointer in command ??? */ + return 0; +} + +/* sea_abort + send an abort to the target + return 1 success, 0 on failure + */ +int sea_abort(int unit, struct sea_scb *scb) +{ + struct sea_data *sea = seadata[unit]; + struct sea_scb *tmp, **prev; + unsigned char msg, phase, *msgptr; + int32 len; + int oldpri; + +#ifdef SEADEBUG2 +/* printf("sea_abort called\n"); */ + printf("\\"); +#endif + + oldpri = splbio(); + + /* If the command hasn't been issued yet, we simply remove it from the + * issue queue + */ + for (prev = (struct sea_scb **) &(sea->issue_queue), + tmp = sea->issue_queue; tmp; + prev = (struct sea_scb **) &(tmp->next), tmp = tmp->next) + if (scb == tmp) { + (*prev) = tmp->next; + tmp->next = NULL; + /* set some type of error result for this operation */ + splx(oldpri); +#ifdef SEADEBUG2 + printf("\\2"); +#endif + return 1; + } + + /* If any commands are connected, we're going to fail the abort + * and let the high level SCSI driver retry at a later time or issue a + * reset + */ + + if(sea->connected) { + splx(oldpri); +#ifdef SEADEBUG2 +/* printf("sea:abort error connected\n"); */ + printf("\\3"); +#endif + return 0; + } + + /* If the command is currently disconnected from the bus, and there are + * no connected commands, we reconnect the I_T_L or I_T_L_Q nexus + * associated with it, go into message out, and send an abort message. + */ + + for (tmp = sea->disconnected_queue; tmp; tmp = tmp->next) + if (scb == tmp) { + splx(oldpri); +#ifdef SEADEBUG2 + printf("\\4"); +#endif + if (sea_select(sea,scb)) { +#ifdef SEADEBUG2 + printf("\\5"); +#endif + return 0; + } + msg = MSG_ABORT; + msgptr = &msg; + len = 1; + phase = REQ_MSGOUT; + CONTROL = BASE_CMD | CMD_ATTN; + sea_transfer_pio(sea, &phase, &len, &msgptr); + + oldpri = splbio(); + for (prev = (struct sea_scb **) &(sea->disconnected_queue), + tmp = sea->disconnected_queue; tmp ; + prev = (struct sea_scb **) &(tmp->next), tmp = tmp->next) + if (scb == tmp) { + *prev = tmp->next; + tmp->next = NULL; + /* set some type of error result for the operation */ +#ifdef SEADEBUG2 + printf("\\6"); +#endif + splx(oldpri); + return 1; + } + } + + /* command not found in any queue, race condition in the code ? */ + + splx(oldpri); +#ifdef SEADEBUG2 +/* printf("sea: WARNING: SCSI command probably completed successfully\n" + " before abortion\n"); */ + printf("\\7"); +#endif + return 1; + +} + +void sea_done(int unit, struct sea_scb *scb) +{ + struct sea_data *sea = seadata[unit]; + struct scsi_xfer *xs = scb->xfer; + + +#ifdef SEADEBUG2 +/* printf("sea_done called\n"); */ + printf("&"); +#endif + + if (scb->flags & SCB_TIMECHK) { +#ifdef SEADEBUG2 + printf("&2"); +#endif +#ifdef SEA_FREEBSD11 + untimeout(sea_timeout, (caddr_t)scb); +#else + untimeout(sea_timeout, scb); +#endif + } + + xs->resid = scb->datalen; /* How much of the buffer was not touched */ + + if ((scb->flags == SCB_ACTIVE) || (xs->flags & SCSI_ERR_OK)) { +#ifdef SEADEBUG2 +/* printf("sea_done:Report no err in xs\n"); */ + printf("&3"); +#endif +/* xs->resid = 0; */ +/* xs->error = 0; */ + } else { + + if (!(scb->flags == SCB_ACTIVE)) { + if ((scb->flags & SCB_TIMEOUT) || (scb->flags & SCB_ABORTED)) { +#ifdef SEADEBUG2 + printf("&6"); +#endif + xs->error = XS_TIMEOUT; + } + if (scb->flags & SCB_ERROR) { +#ifdef SEADEBUG2 + printf("&7"); +#endif + xs->error = XS_DRIVER_STUFFUP; + } + } else { + + /* !!! Add code to check for target status */ + /* say all error now */ + xs->error = XS_DRIVER_STUFFUP; +#ifdef SEADEBUG2 + printf("&4"); +#endif + } + } + xs->flags |= ITSDONE; + sea_free_scb(unit, scb, xs->flags); + scsi_done(xs); +#ifdef SEADEBUG2 +/* printf("Leaving sea_done\n"); */ + printf("&5"); +#endif +} + +/* wait for completion of command in polled mode */ + +int sea_poll(int unit, struct scsi_xfer *xs, struct sea_scb *scb) +{ + int count = 500; /* xs->timeout; */ + int oldpri; + +#ifdef SEADEBUG2 +/* printf("sea_poll called\n"); */ + printf("?"); +#endif + + while (count) { + /* try to do something */ + oldpri = splbio(); + if (!main_running) { + main_running = 1; + sea_main(); + /* main_running is cleared in sea_main once it can't + * do more work, and sea_main exits with interrupts + * disabled + */ + splx(oldpri); + } else { + splx(oldpri); + } + if (xs->flags & ITSDONE) { + break; + } + DELAY(10); + count--; + } +#ifdef SEADEBUG2 + printf("?2 %x ", count); +/* printf("sea_poll: count:%x\n",count); */ +#endif + if (count == 0) { + /* we timed out, so call the timeout handler manually, + * accounting for the fact that the clock is not running yet + * by taking out the clock queue entry it makes. + */ +#ifdef SEADEBUG2 + printf("?3"); +#endif +#ifdef SEA_FREEBSD11 + sea_timeout((caddr_t)scb, 0); +#else + sea_timeout(scb); +#endif + + /* because we are polling, take out the timeout entry + * sea_timeout made + */ +#ifdef SEADEBUG2 + printf("?4"); +#endif +#ifdef SEA_FREEBSD11 + untimeout(sea_timeout, (caddr_t) scb); +#else + untimeout(sea_timeout, scb); +#endif + count = 50; + while (count) { + /* once again, wait for the int bit */ + oldpri = splbio(); + if (!main_running) { + main_running = 1; + sea_main(); + /* main_running is cleared by sea_main once it can't + * do more work, and sea_main exits with interrupts + * disabled + */ + splx(oldpri); + } else { + splx(oldpri); + } + if (xs->flags & ITSDONE) { + break; + } + DELAY(10); + count--; + } + if (count == 0) { + /* we timed out again... This is bad. Notice that + * this time there is no clock queue entry to remove + */ +#ifdef SEADEBUG2 + printf("?5"); +#endif +#ifdef SEA_FREEBSD11 + sea_timeout((caddr_t)scb, 0); +#else + sea_timeout(scb); +#endif + } + } +#ifdef SEADEBUG2 +/* printf("sea_poll: xs->error:%x\n",xs->error); */ + printf("?6%x",xs->error); +#endif + if (xs->error) { +#ifdef SEADEBUG2 +/* printf("done return error\n"); */ + printf("?7"); +#endif + return (HAD_ERROR); + } +#ifdef SEADEBUG2 +/* printf("done return complete\n"); */ + printf("?8"); +#endif + return (COMPLETE); +} + +/* + * sea_information_transfer + * Do the transfer. We know we are connected. Update the flags, + * call sea_done when task accomplished. Dialog controlled by the + * target + */ +static void sea_information_transfer (struct sea_data *sea) +{ + long int timeout; + int unit = sea->sc_link.adapter_unit; + unsigned char msgout = MSG_NOP; + int32 len; + int oldpri; + u_char *data; + unsigned char phase, tmp, old_phase=REQ_UNKNOWN; + struct sea_scb *scb = sea->connected; + int loop; + +#if SEADEBUG2 +/* printf("sea_information_transfer called\n"); */ + printf("!"); +#endif + + for(timeout = 0; timeout < 10000000L ; timeout++) { + tmp = STATUS; + if (!(tmp & STAT_BSY)) { +/* for(loop=0;loop < 20 ; loop++) { + if((tmp=STATUS) & STAT_BSY) + break; + } */ +#ifndef SEADEBUG8 + if(!(tmp & STAT_BSY)) { + printf("sea: !STAT_BSY unit in data transfer!\n"); + oldpri = splbio(); + sea->connected = NULL; + scb->flags = SCB_ERROR; + splx(oldpri); + sea_done(unit, scb); + return; + } +#endif + } + + /* we only have a valid SCSI phase when REQ is asserted */ + if (tmp & STAT_REQ) { + phase = (tmp & REQ_MASK); + if (phase != old_phase) { + old_phase = phase; + } + +#ifdef SEADEBUG7 + printf("!2%x", phase); + for(loop=0;loop < 20; loop++) { + phase = STATUS; + printf("!6%x",phase); + phase = phase & REQ_MASK; + } +#endif + + switch (phase) { + case REQ_DATAOUT: +#ifdef SEA_NODATAOUT + printf("sea: SEA_NODATAOUT set, attempted DATAOUT aborted\n"); + msgout = MSG_ABORT; + CONTROL = BASE_CMD | CMD_ATTN; + break; +#endif + case REQ_DATAIN: +/* data = scb->xfer->data; + len = scb->xfer->datalen; +*/ if(!(scb->data)) { + printf("no data address!\n"); + } +#ifdef SEA_BLINDTRANSFER + if (scb->datalen && !(scb->datalen % BLOCK_SIZE)) { + while (scb->datalen) { + for(timeout = 0; timeout < 5000000L ; timeout++) + if((tmp = STATUS) & STAT_REQ) + break; + if(!(tmp & STAT_REQ)) { + printf("sea_transfer_pio: timeout waiting for STAT_REQ\n"); + /* getchar(); */ + } + if((tmp & REQ_MASK) != phase) { +#ifdef SEADEBUG1 + printf("sea:infotransfer phase mismatch:%x, want:%x, len:%x\n", + tmp,phase,scb->datalen); + /* getchar(); */ +#endif + break; + } + if(!(phase & STAT_IO)) { +#ifdef SEA_ASSEMBLER + asm(" + shr $2, %%ecx; + cld; + rep; + movsl; " : : + "D" (sea->st0x_dr), "S" (scb->data), "c" (BLOCK_SIZE) : + "cx", "si", "di" ); + scb->data += BLOCK_SIZE; +#else + for(count=0; count < BLOCK_SIZE; count++) { + DATA = *(scb->data); + scb->data++; + } +#endif + } else { +#ifdef SEA_ASSEMBLER + asm(" + shr $2, %%ecx; + cld; + rep; + movsl; " : : + "S" (sea->st0x_dr), "D" (scb->data), "c" (BLOCK_SIZE) : + "cx", "si", "di" ); + scb->data += BLOCK_SIZE; +#else + for(count=0; count < BLOCK_SIZE; count++) { + *scb->data = DATA; + scb->data++; + } +#endif + } + scb->datalen -= BLOCK_SIZE; + } + } + + /* save current position into the command structure */ +/* scb->xfer->data = data; + scb->xfer->datalen = len; */ +#endif + + sea_transfer_pio(sea, &phase, &(scb->datalen), &(scb->data)); +/* scb->xfer->data = data; + scb->xfer->datalen = len; +*/ break; + + case REQ_MSGIN: + /* don't handle multi-byte messages here, because they + * should not be present here + */ + len = 1; + data = &tmp; + sea_transfer_pio(sea, &phase, &len, &data); + /* scb->MessageIn = tmp; */ + + switch (tmp) { + + case MSG_ABORT: + scb->flags = SCB_ABORTED; + printf("sea:Command aborted by target\n"); + CONTROL = BASE_CMD; + sea_done(unit, scb); + return; + + case MSG_COMMAND_COMPLETE: + oldpri = splbio(); + sea->connected = NULL; + splx(oldpri); +#ifdef SEADEBUG2 + printf("!3"); +#endif + sea->busy[scb->xfer->sc_link->target] &= + ~(1 << scb->xfer->sc_link->lun); + + CONTROL = BASE_CMD; + sea_done(unit, scb); + return; + case MSG_MESSAGE_REJECT: + /* printf("sea: message_reject recieved\n"); */ + printf("!4"); + break; + case MSG_DISCONNECT: + oldpri = splbio(); + scb->next = sea->disconnected_queue; + sea->disconnected_queue = scb; + sea->connected = NULL; + CONTROL = BASE_CMD; + splx(oldpri); +#ifdef SEADEBUG2 +/* printf("msg_disconnect\n"); */ + printf("!5"); +#endif + return; + /* save/restore of pointers are ignored */ + case MSG_SAVE_POINTERS: + case MSG_RESTORE_POINTERS: +#if SEADEBUG2 + printf("sea: rec save/restore ptrs\n"); +#endif + break; + default: + /* this should be handled in the pio data transfer phase, as the + * ATN should be raised before ACK goes false when rejecting a message + */ +#ifdef SEADEBUG + printf("sea: Unknown message in:%x\n", tmp); +#endif + break; + } /* switch (tmp) */ + break; + case REQ_MSGOUT: + len = 1; + data = &msgout; + /* sea->last_message = msgout; */ + sea_transfer_pio(sea, &phase, &len, &data); + if (msgout == MSG_ABORT) { + printf("sea: sent message abort to target\n"); + oldpri = splbio(); + sea->busy[scb->xfer->sc_link->target] &= + ~(1 << scb->xfer->sc_link->lun); + sea->connected = NULL; + scb->flags = SCB_ABORTED; + splx(oldpri); + /* enable interrupt from scsi */ + sea_done(unit, scb); + return; + } + msgout = MSG_NOP; + break; + case REQ_CMDOUT: + len = scb->xfer->cmdlen; + data = (char *) scb->xfer->cmd; + sea_transfer_pio(sea, &phase, &len, &data); + break; + case REQ_STATIN: + len = 1; + data = &tmp; + sea_transfer_pio(sea, &phase, &len, &data); + scb->xfer->status = tmp; + break; + default: + printf("sea: unknown phase\n"); + } /* switch (phase) */ + } /* if (tmp & STAT_REQ) */ + } /* for (...) */ + /* if we get here we have got a timeout! */ + printf("sea: Timeout in data transfer\n"); + scb->flags = SCB_TIMEOUT; + /* should I clear scsi-bus state? */ + sea_done(unit, scb); +} + + diff --git a/sys/i386/isa/sio.c b/sys/i386/isa/sio.c index 4cee4fe8c60f..6fa09247e5f3 100644 --- a/sys/i386/isa/sio.c +++ b/sys/i386/isa/sio.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.28 1994/02/07 18:37:21 ache Exp $ + * $Id: sio.c,v 1.56 1994/06/16 08:08:44 ache Exp $ */ #include "sio.h" @@ -49,17 +49,19 @@ #include "proc.h" #include "user.h" #include "conf.h" +#include "dkstat.h" #include "file.h" #include "uio.h" #include "kernel.h" +#include "malloc.h" #include "syslog.h" +#include "i386/isa/icu.h" /* XXX */ #include "i386/isa/isa.h" #include "i386/isa/isa_device.h" #include "i386/isa/comreg.h" #include "i386/isa/ic/ns16550.h" -#define FAKE_DCD(unit) ((unit) == comconsole) #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RB_I_HIGH_WATER (RBSZ - 2 * RS_IBUFSIZE) #define RB_I_LOW_WATER ((RBSZ - 2 * RS_IBUFSIZE) * 7 / 8) @@ -67,38 +69,27 @@ #define TTY_BI TTY_FE /* XXX */ #define TTY_OE TTY_PE /* XXX */ -#ifndef COM_BIDIR -#define UNIT(x) (minor(x)) /* XXX */ -#else /* COM_BIDIR */ -#define COM_UNITMASK 0x7f -#define COM_CALLOUTMASK 0x80 /* for both minor and dev */ -#define UNIT(x) (minor(x) & COM_UNITMASK) -#define CALLOUT(x) (minor(x) & COM_CALLOUTMASK) -#endif /* COM_BIDIR */ +#define CALLOUT_MASK 0x80 +#define CONTROL_MASK 0x60 +#define CONTROL_INIT_STATE 0x20 +#define CONTROL_LOCK_STATE 0x40 +#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) +#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) +#define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) #ifdef COM_MULTIPORT /* checks in flags for multiport and which is multiport "master chip" * for a given card */ -#define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01) -#define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff) +#define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01) +#define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff) +#define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) #endif /* COM_MULTIPORT */ -#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) -#ifndef FIFO_TRIGGER -/* - * This driver is fast enough to work with any value and for high values - * to be only slightly more efficient. Low values may be better because - * they give lower latency. - * TODO: always use low values for low speeds. Mouse movements are jerky - * if more than one packet arrives at once. The low speeds used for - * serial mice help avoid this, but not if (large) fifos are enabled. - */ -#define FIFO_TRIGGER FIFO_TRIGGER_14 -#endif +#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) +#define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ -#define setsofttty() (ipending |= 1 << 4) /* XXX */ /* * Input buffer watermarks. @@ -124,17 +115,19 @@ * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. - * Bug: I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. + * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. + * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ +#define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ -static char *error_desc[] = { +static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 @@ -153,20 +146,19 @@ typedef u_char bool_t; /* boolean */ /* com device structure */ struct com_s { u_char state; /* miscellaneous flag bits */ + bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ + u_char ftl; /* current rx fifo trigger level */ + u_char ftl_init; /* ftl_max for next open() */ + u_char ftl_max; /* maximum ftl for curent open() */ bool_t hasfifo; /* nonzero for 16550 UARTs */ u_char mcr_image; /* copy of value written to MCR */ -#ifdef COM_BIDIR - bool_t bidir; /* is this unit bidirectional? */ - bool_t active; /* is the port active _at all_? */ - bool_t active_in; /* is the incoming port in use? */ - bool_t active_out; /* is the outgoing port in use? */ -#endif /* COM_BIDIR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ - int dtr_wait; /* time to hold DTR down on close (* 1/HZ) */ + int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; + u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly @@ -177,6 +169,7 @@ struct com_s { u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ + u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ @@ -195,6 +188,19 @@ struct com_s { struct tty *tp; /* cross reference */ + /* Initial state. */ + struct termios it_in; /* should be in struct tty */ + struct termios it_out; + + /* Lock state. */ + struct termios lt_in; /* should be in struct tty */ + struct termios lt_out; + +#ifdef TIOCTIMESTAMP + bool_t do_timestamp; + struct timeval timestamp; +#endif + u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; @@ -210,48 +216,56 @@ struct com_s { }; /* - * These functions in the com module ought to be declared (with a prototype) - * in a com-driver system header. The void ones may need to be int to match - * ancient devswitch declarations, but they don't actually return anything. + * The public functions in the com module ought to be declared in a com-driver + * system header. */ #define Dev_t int /* promoted dev_t */ -struct consdev; +/* Interrupt handling entry points. */ +void siointr __P((int unit)); +void siopoll __P((void)); + +/* Device switch entry points. */ +int sioopen __P((Dev_t dev, int oflags, int devtype, + struct proc *p)); int sioclose __P((Dev_t dev, int fflag, int devtype, struct proc *p)); -void siointr __P((int unit)); +int sioread __P((Dev_t dev, struct uio *uio, int ioflag)); +int siowrite __P((Dev_t dev, struct uio *uio, int ioflag)); int sioioctl __P((Dev_t dev, int cmd, caddr_t data, int fflag, struct proc *p)); +void siostop __P((struct tty *tp, int rw)); +#define sioreset noreset +int sioselect __P((Dev_t dev, int rw, struct proc *p)); +#define siommap nommap +#define siostrategy nostrategy + +/* Console device entry points. */ int siocngetc __P((Dev_t dev)); +struct consdev; void siocninit __P((struct consdev *cp)); void siocnprobe __P((struct consdev *cp)); void siocnputc __P((Dev_t dev, int c)); -int sioopen __P((Dev_t dev, int oflags, int devtype, - struct proc *p)); -void siopoll __P((void)); -int sioread __P((Dev_t dev, struct uio *uio, int ioflag)); -int sioselect __P((Dev_t dev, int rw, struct proc *p)); -void siostop __P((struct tty *tp, int rw)); -int siowrite __P((Dev_t dev, struct uio *uio, int ioflag)); -void softsio1 __P((void)); static int sioattach __P((struct isa_device *dev)); +static void siodtrwakeup __P((caddr_t chan, int ticks)); static void comflush __P((struct com_s *com)); static void comhardclose __P((struct com_s *com)); -static void cominit __P((int unit, int rate)); -static void comintr1 __P((struct com_s *com)); +static void siointr1 __P((struct com_s *com)); static void commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); static int sioprobe __P((struct isa_device *dev)); -static void comstart __P((struct tty *tp)); -static void comwakeup __P((caddr_t chan, int ticks)); +static void comstart __P((struct tty *tp)); +static void comwakeup __P((caddr_t chan, int ticks)); static int tiocm_xxx2mcr __P((int tiocm_xxx)); /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIO]; #define com_addr(unit) (p_com_addr[unit]) -static struct com_s com_structs[NSIO]; +#ifdef TIOCTIMESTAMP +static struct timeval intr_timestamp; +#endif struct isa_driver siodriver = { sioprobe, sioattach, "sio" @@ -262,15 +276,11 @@ static int comconsole = COMCONSOLE; #else static int comconsole = -1; #endif -static bool_t comconsinit; static speed_t comdefaultrate = TTYDEF_SPEED; static u_int com_events; /* input chars + weighted output completions */ static int commajor; -struct tty sio_tty[NSIO]; -extern struct tty *constty; -extern u_int ipending; /* XXX */ -extern int tk_nin; /* XXX */ -extern int tk_rawcc; /* XXX */ +struct tty *sio_tty[NSIO]; +extern struct tty *constty; /* XXX */ #ifdef KGDB #include "machine/remote-sl.h" @@ -311,7 +321,11 @@ sioprobe(dev) { static bool_t already_init; Port_t *com_ptr; + bool_t failures[10]; + int fn; + struct isa_device *idev; Port_t iobase; + u_char mcr_image; int result; if (!already_init) { @@ -319,6 +333,7 @@ sioprobe(dev) * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. + * XXX the gate enable is elsewhere for some multiports. */ for (com_ptr = likely_com_ports; com_ptr < &likely_com_ports[sizeof likely_com_ports @@ -327,8 +342,45 @@ sioprobe(dev) outb(*com_ptr + com_mcr, 0); already_init = TRUE; } + + /* + * If the port is on a multiport card and has a master port, + * initialize the common interrupt control register in the + * master and prepare to leave MCR_IENABLE clear in the mcr. + * Otherwise, prepare to set MCR_IENABLE in the mcr. + * Point idev to the device struct giving the correct id_irq. + * This is the struct for the master device if there is one. + */ + idev = dev; + mcr_image = MCR_IENABLE; +#ifdef COM_MULTIPORT + if (COM_ISMULTIPORT(dev)) { + idev = find_isadev(isa_devtab_tty, &siodriver, + COM_MPMASTER(dev)); + if (idev == NULL) { + printf("sio%d: master device %d not found\n", + dev->id_unit, COM_MPMASTER(dev)); + return (0); + } + if (idev->id_irq == 0) { + printf("sio%d: master device %d irq not configured\n", + dev->id_unit, COM_MPMASTER(dev)); + return (0); + } + if (!COM_NOTAST4(dev)) { + outb(idev->id_iobase + com_scr, 0x80); + mcr_image = 0; + } + } + else +#endif /* COM_MULTIPORT */ + if (idev->id_irq == 0) { + printf("sio%d: irq not configured\n", dev->id_unit); + return (0); + } + + bzero(failures, sizeof failures); iobase = dev->id_iobase; - result = IO_COMSIZE; /* * We don't want to get actual interrupts, just masked ones. @@ -339,55 +391,115 @@ sioprobe(dev) disable_intr(); /* - * Initialize the speed so that any junk in the THR or output fifo will - * be transmitted in a known time. (There may be lots of junk after a - * soft reboot, and output interrupts don't work right after a master - * reset, at least for 16550s. (The speed is undefined after MR, but - * MR empties the THR and the TSR so it's not clear why this matters)). - * Enable output interrupts (only) and check the following: + * XXX DELAY() reenables CPU interrupts. This is a problem for + * shared interrupts after the first device using one has been + * successfully probed - config_isadev() has enabled the interrupt + * in the ICU. + */ + outb(IO_ICU1 + 1, 0xff); + + /* + * Initialize the speed and the word size and wait long enough to + * drain the maximum of 16 bytes of junk in device output queues. + * The speed is undefined after a master reset and must be set + * before relying on anything related to output. There may be + * junk after a (very fast) soft reboot and (apparently) after + * master reset. + * XXX what about the UART bug avoided by waiting in comparam()? + * We don't want to to wait long enough to drain at 2 bps. + */ + outb(iobase + com_cfcr, CFCR_DLAB); + outb(iobase + com_dlbl, COMBRD(9600) & 0xff); + outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8); + outb(iobase + com_cfcr, CFCR_8BITS); + DELAY((16 + 1) * 9600 / 10); + + /* + * Enable the interrupt gate and disable device interupts. This + * should leave the device driving the interrupt line low and + * guarantee an edge trigger if an interrupt can be generated. + */ + outb(iobase + com_mcr, mcr_image); + outb(iobase + com_ier, 0); + + /* + * Attempt to set loopback mode so that we can send a null byte + * without annoying any external device. + */ + outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); + + /* + * Attempt to generate an output interrupt. On 8250's, setting + * IER_ETXRDY generates an interrupt independent of the current + * setting and independent of whether the THR is empty. On 16450's, + * setting IER_ETXRDY generates an interrupt independent of the + * current setting. On 16550A's, setting IER_ETXRDY only + * generates an interrupt when IER_ETXRDY is not already set. + */ + outb(iobase + com_ier, IER_ETXRDY); + + /* + * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate + * an interrupt. They'd better generate one for actually doing + * output. Loopback may be broken on the same incompatibles but + * it's unlikely to do more than allow the null byte out. + */ + outb(iobase + com_data, 0); + DELAY((2 + 1) * 9600 / 10); + + /* + * Turn off loopback mode so that the interrupt gate works again + * (MCR_IENABLE was hidden). This should leave the device driving + * an interrupt line high. It doesn't matter if the interrupt + * line oscillates while we are not looking at it, since interrupts + * are disabled. + */ + outb(iobase + com_mcr, mcr_image); + + /* + * Check that * o the CFCR, IER and MCR in UART hold the values written to them * (the values happen to be all distinct - this is good for * avoiding false positive tests from bus echoes). * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ - outb(iobase + com_cfcr, CFCR_DLAB); - outb(iobase + com_dlbl, COMBRD(9600) & 0xff); - outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8); - outb(iobase + com_cfcr, CFCR_8BITS); /* ensure IER is addressed */ - outb(iobase + com_mcr, MCR_IENABLE); /* open gate early */ - outb(iobase + com_ier, 0); /* ensure edge on next intr */ - outb(iobase + com_ier, IER_ETXRDY); /* generate interrupt */ - DELAY((16 + 1) * 9600 / 10); /* enough to drain 16 bytes */ - if ( inb(iobase + com_cfcr) != CFCR_8BITS - || inb(iobase + com_ier) != IER_ETXRDY - || inb(iobase + com_mcr) != MCR_IENABLE -#ifndef COM_MULTIPORT /* XXX - need to do more to enable interrupts */ - || !isa_irq_pending(dev) -#endif - || (inb(iobase + com_iir) & IIR_IMASK) != IIR_TXRDY - || isa_irq_pending(dev) - || !(inb(iobase + com_iir) & IIR_NOPEND)) - result = 0; + failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; + failures[1] = inb(iobase + com_ier) - IER_ETXRDY; + failures[2] = inb(iobase + com_mcr) - mcr_image; + if (idev->id_irq != 0) + failures[3] = isa_irq_pending(idev) ? 0 : 1; + failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; + failures[5] = isa_irq_pending(idev) ? 1 : 0; + failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; /* * Turn off all device interrupts and check that they go off properly. - * Leave MCR_IENABLE set. It gates the OUT2 output of the UART to + * Leave MCR_IENABLE alone. For ports without a master port, it gates + * the OUT2 output of the UART to * the ICU input. Closing the gate would give a floating ICU input * (unless there is another device driving at) and spurious interrupts. * (On the system that this was first tested on, the input floats high * and gives a (masked) interrupt as soon as the gate is closed.) */ outb(iobase + com_ier, 0); - outb(iobase + com_mcr, MCR_IENABLE); /* dummy to avoid bus echo */ - if ( inb(iobase + com_ier) != 0 - || isa_irq_pending(dev) - || !(inb(iobase + com_iir) & IIR_NOPEND)) - result = 0; - if (result == 0) - outb(iobase + com_mcr, 0); + outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ + failures[7] = inb(iobase + com_ier); + failures[8] = isa_irq_pending(idev) ? 1 : 0; + failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; + outb(IO_ICU1 + 1, imen); /* XXX */ enable_intr(); + + result = IO_COMSIZE; + for (fn = 0; fn < sizeof failures; ++fn) + if (failures[fn]) { + outb(iobase + com_mcr, 0); + result = 0; + if (COM_VERBOSE(dev)) + printf("sio%d: probe test %d failed\n", + dev->id_unit, fn); + } return (result); } @@ -403,9 +515,9 @@ sioattach(isdp) iobase = isdp->id_iobase; unit = isdp->id_unit; - if (unit == comconsole) - DELAY(1000); /* XXX */ - s = spltty(); + com = malloc(sizeof *com, M_TTYS, M_NOWAIT); + if (com == NULL) + return (0); /* * sioprobe() has initialized the device registers as follows: @@ -414,15 +526,13 @@ sioattach(isdp) * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. - * o mcr = MCR_IENABLE. + * o mcr = MCR_IENABLE, or 0 if the port has a master port. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ - - com = &com_structs[unit]; + bzero(com, sizeof *com); com->cfcr_image = CFCR_8BITS; - com->mcr_image = MCR_IENABLE; - com->dtr_wait = 200; + com->dtr_wait = 3 * hz; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; @@ -431,9 +541,30 @@ sioattach(isdp) com->data_port = iobase + com_data; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; + com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; - com->tp = &sio_tty[unit]; + + /* + * We don't use all the flags from <sys/ttydefaults.h> since they + * are only relevant for logins. It's important to have echo off + * initially so that the line doesn't start blathering before the + * echo flag can be turned off. + */ + com->it_in.c_iflag = 0; + com->it_in.c_oflag = 0; + com->it_in.c_cflag = TTYDEF_CFLAG; + com->it_in.c_lflag = 0; + if (unit == comconsole) { + com->it_in.c_iflag = TTYDEF_IFLAG; + com->it_in.c_oflag = TTYDEF_OFLAG; + com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; + com->it_in.c_lflag = TTYDEF_LFLAG; + com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; + } + termioschars(&com->it_in); + com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; + com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); @@ -470,10 +601,11 @@ sioattach(isdp) break; case FIFO_TRIGGER_14: printf(" 16550A"); - if (COM_NOFIFO(isdp)) { - printf(" fifo software disabled"); - } else { + if (COM_NOFIFO(isdp)) + printf(" fifo disabled"); + else { com->hasfifo = TRUE; + com->ftl_init = FIFO_TRIGGER_14; com->tx_fifo_size = 16; } break; @@ -483,31 +615,36 @@ determined_type: ; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(isdp)) { - struct isa_device *masterdev; - com->multiport = TRUE; - printf(" (multiport)"); - - /* set the master's common-interrupt-enable reg., - * as appropriate. YYY See your manual - */ - /* enable only common interrupt for port */ - outb(com->modem_ctl_port, com->mcr_image = 0); - - masterdev = find_isadev(isa_devtab_tty, &siodriver, - COM_MPMASTER(isdp)); - outb(masterdev->id_iobase + com_scr, 0x80); - } else - com->multiport = FALSE; + printf(" (multiport"); + if (unit == COM_MPMASTER(isdp)) + printf(" master"); + printf(")"); + } #endif /* COM_MULTIPORT */ printf("\n"); #ifdef KGDB if (kgdb_dev == makedev(commajor, unit)) { - if (comconsole == unit) + if (unit == comconsole) kgdb_dev = -1; /* can't debug over console port */ else { - cominit(unit, kgdb_rate); + int divisor; + + /* + * XXX now unfinished and broken. Need to do + * something more like a full open(). There's no + * suitable interrupt handler so don't enable device + * interrupts. Watch out for null tp's. + */ + outb(iobase + com_cfcr, CFCR_DLAB); + divisor = ttspeedtab(kgdb_rate, comspeedtab); + outb(iobase + com_dlbl, divisor & 0xFF); + outb(iobase + com_dlbh, (u_int) divisor >> 8); + outb(iobase + com_cfcr, CFCR_8BITS); + outb(com->modem_status_port, + com->mcr_image |= MCR_DTR | MCR_RTS); + if (kgdb_debug_init) { /* * Print prefix of device name, @@ -521,16 +658,11 @@ determined_type: ; } #endif - /* - * Need to reset baud rate, etc. of next print so reset comconsinit. - */ - if (unit == comconsole) - comconsinit = FALSE; - + s = spltty(); com_addr(unit) = com; splx(s); if (!comwakeup_started) { - comwakeup((caddr_t) NULL, 0); + comwakeup((caddr_t)NULL, 0); comwakeup_started = TRUE; } return (1); @@ -544,207 +676,127 @@ sioopen(dev, flag, mode, p) int mode; struct proc *p; { -#ifdef COM_BIDIR - bool_t callout; -#endif /* COM_BIDIR */ struct com_s *com; - int error = 0; + int error; Port_t iobase; + int mynor; int s; struct tty *tp; int unit; - unit = UNIT(dev); + mynor = minor(dev); + unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); -#ifdef COM_BIDIR - /* if it's a callout device, and bidir not possible on that dev, die */ - callout = CALLOUT(dev); - if (callout && !(com->bidir)) - return (ENXIO); -#endif /* COM_BIDIR */ - - tp = com->tp; + if (mynor & CONTROL_MASK) + return (0); + tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); s = spltty(); - -#ifdef COM_BIDIR - -bidir_open_top: - /* if it's bidirectional, we've gotta deal with it... */ - if (com->bidir) { - if (callout) { - if (com->active_in) { - /* it's busy. die */ - splx(s); - return (EBUSY); - } else { - /* it's ours. lock it down, and set it up */ - com->active_out = TRUE; + /* + * We jump to this label after all non-interrupted sleeps to pick + * up any changes of the device state. + */ +open_top: + while (com->state & CS_DTR_OFF) { + error = tsleep((caddr_t)&com->dtr_wait, TTIPRI | PCATCH, + "siodtr", 0); + if (error != 0) + goto out; + } + if (tp->t_state & TS_ISOPEN) { + /* + * The device is open, so everything has been initialized. + * Handle conflicts. + */ + if (mynor & CALLOUT_MASK) { + if (!com->active_out) { + error = EBUSY; + goto out; } } else { if (com->active_out) { - /* it's busy, outgoing. wait, if possible */ if (flag & O_NONBLOCK) { - /* can't wait; bail */ - splx(s); - return (EBUSY); - } else { - /* wait for it... */ - error = tsleep((caddr_t)&com->active_out, - TTIPRI|PCATCH, - "siooth", - 0); - /* if there was an error, take off. */ - if (error != 0) { - splx(s); - return (error); - } - /* else take it from the top */ - goto bidir_open_top; - } - } else if (com->prev_modem_status & MSR_DCD - || FAKE_DCD(unit)) { - /* there's a carrier on the line; we win */ - com->active_in = TRUE; - } else { - /* there is no carrier on the line */ - if (flag & O_NONBLOCK) { - /* can't wait; let it open */ - com->active_in = TRUE; - } else { - /* put DTR & RTS up */ - /* XXX - bring up RTS earlier? */ - commctl(com, MCR_DTR | MCR_RTS, DMSET); - outb(com->iobase + com_ier, IER_EMSC); - - /* wait for it... */ - error = tsleep((caddr_t)&com->active_in, - TTIPRI|PCATCH, - "siodcd", - 0); - - /* if not active, turn intrs and DTR off */ - if (!com->active) { - outb(com->iobase + com_ier, 0); - commctl(com, MCR_DTR, DMBIC); - } - - /* if there was an error, take off. */ - if (error != 0) { - splx(s); - return (error); - } - /* else take it from the top */ - goto bidir_open_top; + error = EBUSY; + goto out; } + error = tsleep((caddr_t)&com->active_out, + TTIPRI | PCATCH, "siobi", 0); + if (error != 0) + goto out; + goto open_top; } } - } - - com->active = TRUE; -#endif /* COM_BIDIR */ - - tp->t_oproc = comstart; - tp->t_param = comparam; - tp->t_dev = dev; - if (!(tp->t_state & TS_ISOPEN)) { - tp->t_state |= TS_WOPEN; - ttychars(tp); - if (tp->t_ispeed == 0) { - /* - * We no longer use the flags from <sys/ttydefaults.h> - * since those are only relevant for logins. It's - * important to have echo off initially so that the - * line doesn't start blathering before the echo flag - * can be turned off. - */ - tp->t_iflag = 0; - tp->t_oflag = 0; -#ifdef COMCONSOLE - if (unit == comconsole) - tp->t_oflag = TTYDEF_OFLAG; -#endif - tp->t_cflag = CREAD | CS8 | HUPCL; - tp->t_lflag = 0; - tp->t_ispeed = tp->t_ospeed = comdefaultrate; + if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { + error = EBUSY; + goto out; } - + } else { /* - * XXX the full state after a first open() needs to be - * programmable and separate for callin and callout. + * The device isn't open, so there are no conflicts. + * Initialize it. Initialization is done twice in many + * cases: to preempt sleeping callin opens if we are + * callout, and to complete a callin open after DCD rises. */ -#ifdef COM_BIDIR - if (com->bidir) { - if (callout) - tp->t_cflag |= CLOCAL; - else - tp->t_cflag &= ~CLOCAL; - } -#endif - + tp->t_oproc = comstart; + tp->t_param = comparam; + tp->t_dev = dev; + tp->t_termios = mynor & CALLOUT_MASK + ? com->it_out : com->it_in; commctl(com, MCR_DTR | MCR_RTS, DMSET); + com->ftl_max = com->ftl_init; + ++com->wopeners; error = comparam(tp, &tp->t_termios); + --com->wopeners; if (error != 0) goto out; + /* + * XXX we should goto open_top if comparam() slept. + */ ttsetwater(tp); iobase = com->iobase; if (com->hasfifo) { - /* (re)enable and drain FIFO */ - outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER - | FIFO_RCV_RST | FIFO_XMT_RST); + /* Drain fifo. */ + outb(iobase + com_fifo, + FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST + | com->ftl); DELAY(100); } disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); - com->last_modem_status = - com->prev_modem_status = inb(com->modem_status_port); + com->prev_modem_status = + com->last_modem_status = inb(com->modem_status_port); outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); enable_intr(); - if (com->prev_modem_status & MSR_DCD || FAKE_DCD(unit)) - tp->t_state |= TS_CARR_ON; - } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { - splx(s); - return (EBUSY); - } - while (!(flag & O_NONBLOCK) && !(tp->t_cflag & CLOCAL) -#ifdef COM_BIDIR - /* We went through a lot of trouble to open it, - * but it's certain we have a carrier now, so - * don't spend any time on it now. + /* + * Handle initial DCD. Callout devices get a fake initial + * DCD (trapdoor DCD). If we are callout, then any sleeping + * callin opens get woken up and resume sleeping on "siobi" + * instead of "siodcd". */ - && !(com->bidir) -#endif /* COM_BIDIR */ - && !(tp->t_state & TS_CARR_ON)) { - tp->t_state |= TS_WOPEN; - error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH, - ttopen, 0); + if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) + (*linesw[tp->t_line].l_modem)(tp, 1); + } + /* + * Wait for DCD if necessary. + */ + if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) + && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { + ++com->wopeners; + error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); + --com->wopeners; if (error != 0) - break; + goto out; + goto open_top; } + error = (*linesw[tp->t_line].l_open)(dev, tp, 0); + if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) + com->active_out = TRUE; out: splx(s); - if (error == 0) - error = (*linesw[tp->t_line].l_open)(dev, tp, 0); - -#ifdef COM_BIDIR - /* wakeup sleepers */ - wakeup((caddr_t) &com->active_in); -#endif /* COM_BIDIR */ - - /* - * XXX - the next step was once not done, so interrupts, DTR and RTS - * remained hot if the process was killed while it was sleeping - * waiting for carrier. Now there is the opposite problem. If several - * processes are sleeping waiting for carrier on the same line and one - * is killed, interrupts are turned off so the other processes will - * never see the carrier rise. - */ - if (error != 0 && !(tp->t_state & TS_ISOPEN)) + if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); - tp->t_state &= ~TS_WOPEN; - return (error); } @@ -757,13 +809,21 @@ sioclose(dev, flag, mode, p) struct proc *p; { struct com_s *com; + int mynor; + int s; struct tty *tp; - com = com_addr(UNIT(dev)); + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (0); + com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; + s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); + siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); + splx(s); return (0); } @@ -776,10 +836,13 @@ comhardclose(com) struct tty *tp; int unit; - s = spltty(); + unit = DEV_TO_UNIT(com->tp->t_dev); iobase = com->iobase; + s = spltty(); +#ifdef TIOCTIMESTAMP + com->do_timestamp = 0; +#endif outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); - unit = com - &com_structs[0]; #ifdef KGDB /* do not disable interrupts or hang up if debugging */ if (kgdb_dev != makedev(commajor, unit)) @@ -787,29 +850,29 @@ comhardclose(com) { outb(iobase + com_ier, 0); tp = com->tp; - if (tp->t_cflag & HUPCL || tp->t_state & TS_WOPEN - || !(com->prev_modem_status & MSR_DCD) && !FAKE_DCD(unit) + if (tp->t_cflag & HUPCL + /* + * XXX we will miss any carrier drop between here and the + * next open. Perhaps we should watch DCD even when the + * port is closed; it is not sufficient to check it at + * the next open because it might go up and down while + * we're not watching. + */ + || !com->active_out + && !(com->prev_modem_status & MSR_DCD) + && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { commctl(com, MCR_RTS, DMSET); - if (com->dtr_wait != 0) - /* - * Uninterruptible sleep since we want to - * wait a fixed time. - * XXX - delay in open() (if necessary), - * not here (always). - */ - tsleep((caddr_t)&com->dtr_wait, TTIPRI, - "sioclose", com->dtr_wait); + if (com->dtr_wait != 0) { + timeout(siodtrwakeup, (caddr_t)com, + com->dtr_wait); + com->state |= CS_DTR_OFF; + } } } - -#ifdef COM_BIDIR - com->active = com->active_in = com->active_out = FALSE; - - /* wakeup sleepers who are waiting for out to finish */ - wakeup((caddr_t) &com->active_out); -#endif /* COM_BIDIR */ - + com->active_out = FALSE; + wakeup((caddr_t)&com->active_out); + wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } @@ -819,8 +882,13 @@ sioread(dev, uio, flag) struct uio *uio; int flag; { - struct tty *tp = com_addr(UNIT(dev))->tp; + int mynor; + struct tty *tp; + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (ENODEV); + tp = com_addr(MINOR_TO_UNIT(mynor))->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } @@ -830,9 +898,15 @@ siowrite(dev, uio, flag) struct uio *uio; int flag; { - int unit = UNIT(dev); - struct tty *tp = com_addr(unit)->tp; + int mynor; + struct tty *tp; + int unit; + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (ENODEV); + unit = MINOR_TO_UNIT(mynor); + tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that @@ -844,17 +918,37 @@ siowrite(dev, uio, flag) return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } +static void +siodtrwakeup(chan, ticks) + caddr_t chan; + int ticks; +{ + struct com_s *com; + + com = (struct com_s *)chan; + com->state &= ~CS_DTR_OFF; + wakeup((caddr_t)&com->dtr_wait); +} + +#ifdef TIOCTIMESTAMP +/* Interrupt routine for timekeeping purposes */ +void +siointrts(unit) + int unit; +{ + microtime(&intr_timestamp); + siointr(unit); +} +#endif + void siointr(unit) int unit; { - struct com_s *com; - #ifndef COM_MULTIPORT - com = com_addr(unit); - if (com != NULL) - comintr1(com); + siointr1(com_addr(unit)); #else /* COM_MULTIPORT */ + struct com_s *com; bool_t possibly_more_intrs; /* @@ -869,29 +963,30 @@ siointr(unit) for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL - && !(inb(com->int_id_port) & IIR_NOPEND)) { - /* - * XXX call comintr1() instead of here from - * comwakeup(). The interrupt edge problem - * only exists for real interrupts. - */ - comintr1(com); + && (inb(com->int_id_port) & IIR_IMASK) + != IIR_NOPEND) { + siointr1(com); possibly_more_intrs = TRUE; } } } while (possibly_more_intrs); -#endif /* COM_MULTIPORT */ +#endif /* COM_MULTIPORT */ } static void -comintr1(com) +siointr1(com) struct com_s *com; { - u_char line_status; - u_char modem_status; - u_char *ioptr; - u_char recv_data; - + u_char line_status; + u_char modem_status; + u_char *ioptr; + u_char recv_data; + +#ifdef TIOCTIMESTAMP + if (com->do_timestamp) + /* XXX a little bloat here... */ + com->timestamp = intr_timestamp; +#endif while (TRUE) { line_status = inb(com->line_status_port); @@ -903,6 +998,8 @@ comintr1(com) else recv_data = inb(com->data_port); ++com->bytes_in; + if (com->hotchar != 0 && recv_data == com->hotchar) + setsofttty(); #ifdef KGDB /* trap into kgdb? (XXX - needs testing and optim) */ if (recv_data == FRAME_END @@ -981,7 +1078,7 @@ if (com->iptr - com->ibuf == 8) while (--ocount != 0); } else { outb(com->data_port, *ioptr++); - ++com->bytes_out; + ++com->bytes_out; } com->optr = ioptr; if (ioptr >= com->obufend) { @@ -994,7 +1091,7 @@ if (com->iptr - com->ibuf == 8) /* finished? */ #ifndef COM_MULTIPORT - if (inb(com->int_id_port) & IIR_NOPEND) + if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) #endif /* COM_MULTIPORT */ return; } @@ -1002,9 +1099,9 @@ if (com->iptr - com->ibuf == 8) static int tiocm_xxx2mcr(tiocm_xxx) - int tiocm_xxx; + int tiocm_xxx; { - int mcr; + int mcr; mcr = 0; if (tiocm_xxx & TIOCM_DTR) @@ -1027,26 +1124,75 @@ sioioctl(dev, cmd, data, flag, p) Port_t iobase; int mcr; int msr; + int mynor; int s; int tiocm_xxx; struct tty *tp; - com = com_addr(UNIT(dev)); + mynor = minor(dev); + com = com_addr(MINOR_TO_UNIT(mynor)); + if (mynor & CONTROL_MASK) { + struct termios *ct; + + switch (mynor & CONTROL_MASK) { + case CONTROL_INIT_STATE: + ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; + break; + case CONTROL_LOCK_STATE: + ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; + break; + default: + return (ENODEV); /* /dev/nodev */ + } + switch (cmd) { + case TIOCSETA: + error = suser(p->p_ucred, &p->p_acflag); + if (error) + return (error); + *ct = *(struct termios *)data; + return (0); + case TIOCGETA: + *(struct termios *)data = *ct; + return (0); + case TIOCGETD: + *(int *)data = TTYDISC; + return (0); + case TIOCGWINSZ: + bzero(data, sizeof(struct winsize)); + return (0); + default: + return (ENOTTY); + } + } tp = com->tp; + if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { + int cc; + struct termios *dt = (struct termios *)data; + struct termios *lt = mynor & CALLOUT_MASK + ? &com->lt_out : &com->lt_in; + + dt->c_iflag = (tp->t_iflag & lt->c_iflag) + | (dt->c_iflag & ~lt->c_iflag); + dt->c_oflag = (tp->t_oflag & lt->c_oflag) + | (dt->c_oflag & ~lt->c_oflag); + dt->c_cflag = (tp->t_cflag & lt->c_cflag) + | (dt->c_cflag & ~lt->c_cflag); + dt->c_lflag = (tp->t_lflag & lt->c_lflag) + | (dt->c_lflag & ~lt->c_lflag); + for (cc = 0; cc < NCCS; ++cc) + if (lt->c_cc[cc] != 0) + dt->c_cc[cc] = tp->t_cc[cc]; + if (lt->c_ispeed != 0) + dt->c_ispeed = tp->t_ispeed; + if (lt->c_ospeed != 0) + dt->c_ospeed = tp->t_ospeed; + } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); if (error >= 0) return (error); error = ttioctl(tp, cmd, data, flag); - -#ifdef COM_BIDIR - /* XXX: plug security hole while sticky bits not yet implemented */ - if (com->bidir && com->active_in && p->p_ucred->cr_uid != 0) - tp->t_cflag &= ~CLOCAL; -#endif - if (error >= 0) return (error); - iobase = com->iobase; s = spltty(); switch (cmd) { @@ -1094,57 +1240,24 @@ sioioctl(dev, cmd, data, flag, p) tiocm_xxx |= TIOCM_RI; *(int *)data = tiocm_xxx; break; -#ifdef COM_BIDIR - case TIOCMSBIDIR: - /* must be root to set bidir. capability */ - error = suser(p->p_ucred, &p->p_acflag); - if (error != 0) { - splx(s); - return(EPERM); - } - - /* if it's the console, can't do it (XXX why?) */ - if (UNIT(dev) == comconsole) { - splx(s); - return(ENOTTY); - } - -#if 0 - /* XXX - can't do the next, for obvious reasons... - * but there are problems to be looked at... - */ - /* if the port is active, don't do it */ - if (com->active) { - splx(s); - return(EBUSY); - } -#endif - - com->bidir = *(int *)data; - break; - case TIOCMGBIDIR: - *(int *)data = com->bidir; - break; -#endif /* COM_BIDIR */ case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); - return(EPERM); + return (EPERM); } - - /* if it's the console, can't do it (XXX why?) */ - if (UNIT(dev) == comconsole) { - splx(s); - return(ENOTTY); - } - - com->dtr_wait = *(int *)data; + com->dtr_wait = *(int *)data * 100 / hz; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait; break; +#ifdef TIOCTIMESTAMP + case TIOCTIMESTAMP: + com->do_timestamp = TRUE; + *(struct timeval *)data = com->timestamp; + break; +#endif default: splx(s); return (ENOTTY); @@ -1165,7 +1278,7 @@ comflush(com) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); enable_intr(); - rbp = &com->tp->t_out; + rbp = com->tp->t_out; rbp->rb_hd += com->ocount; rbp->rb_hd = RB_ROLLOVER(rbp, rbp->rb_hd); com->ocount = 0; @@ -1175,24 +1288,14 @@ comflush(com) void siopoll() { - static bool_t awake = FALSE; - int s; int unit; if (com_events == 0) return; - disable_intr(); - if (awake) { - enable_intr(); - return; - } - awake = TRUE; - enable_intr(); - s = spltty(); repeat: for (unit = 0; unit < NSIO; ++unit) { - u_char *buf; - struct com_s *com; + u_char *buf; + struct com_s *com; u_char *ibuf; int incc; struct tty *tp; @@ -1201,12 +1304,25 @@ repeat: if (com == NULL) continue; tp = com->tp; + if (tp == NULL) + continue; /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { buf = NULL; /* not used, but compiler can't tell */ incc = 0; } else { + /* + * Prepare to reduce input latency for packet + * discplines with a end of packet character. + * XXX should be elsewhere. + */ + if (tp->t_line == SLIPDISC) + com->hotchar = 0xc0; + else if (tp->t_line == PPPDISC) + com->hotchar = 0x7e; + else + com->hotchar = 0; buf = ibuf; disable_intr(); incc = com->iptr - buf; @@ -1224,7 +1340,15 @@ repeat: * of input, so enable RTS if it is now disabled and * there is room in the high-level buffer. */ - if (!(com->mcr_image & MCR_RTS) + /* + * XXX this used not to look at CS_RTS_IFLOW. The + * change is to allow full control of MCR_RTS via + * ioctls after turning CS_RTS_IFLOW off. Check + * for races. We shouldn't allow the ioctls while + * CS_RTS_IFLOW is on. + */ + if ((com->state & CS_RTS_IFLOW) + && !(com->mcr_image & MCR_RTS) && !(tp->t_state & TS_RTS_IFLOW)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); @@ -1242,35 +1366,45 @@ repeat: com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); - if (delta_modem_status & MSR_DCD && !FAKE_DCD(unit)) { - if (com->prev_modem_status & MSR_DCD) { - (*linesw[tp->t_line].l_modem)(tp, 1); -#ifdef COM_BIDIR - wakeup((caddr_t) &com->active_in); -#endif /* COM_BIDIR */ - } else - (*linesw[tp->t_line].l_modem)(tp, 0); - } + if (delta_modem_status & MSR_DCD) + (*linesw[tp->t_line].l_modem) + (tp, com->prev_modem_status & MSR_DCD); } /* XXX */ if (TRUE) { - u_int delta; - int errnum; - u_long total; + u_int delta; + int errnum; + u_long total; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { - disable_intr(); + disable_intr(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; - enable_intr(); - if (delta != 0) { - total = - com->error_counts[errnum] += delta; + enable_intr(); + if (delta == 0 || !(tp->t_state & TS_ISOPEN)) + continue; + total = com->error_counts[errnum] += delta; log(LOG_WARNING, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); + if (errnum == CE_OVERRUN && com->hasfifo + && com->ftl > FIFO_TRIGGER_1) { + static u_char ftl_in_bytes[] = + { 1, 4, 8, 14, }; + + com->ftl_init = FIFO_TRIGGER_8; +#define FIFO_TRIGGER_DELTA FIFO_TRIGGER_4 + com->ftl_max = + com->ftl -= FIFO_TRIGGER_DELTA; + outb(com->iobase + com_fifo, + FIFO_ENABLE | com->ftl); + log(LOG_WARNING, + "sio%d: reduced fifo trigger level to %d\n", + unit, + ftl_in_bytes[com->ftl + / FIFO_TRIGGER_DELTA]); } } } @@ -1285,7 +1419,7 @@ repeat: if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) continue; if (com->state & CS_RTS_IFLOW - && RB_LEN(&tp->t_raw) + incc >= RB_I_HIGH_WATER + && RB_LEN(tp->t_raw) + incc >= RB_I_HIGH_WATER && !(tp->t_state & TS_RTS_IFLOW) /* * XXX - need RTS flow control for all line disciplines. @@ -1312,7 +1446,7 @@ repeat: tk_rawcc += incc; tp->t_rawcc += incc; com->delta_error_counts[CE_TTY_BUF_OVERFLOW] - += incc - rb_write(&tp->t_raw, (char *) buf, + += incc - rb_write(tp->t_raw, (char *) buf, incc); ttwakeup(tp); if (tp->t_state & TS_TTSTOP @@ -1348,8 +1482,6 @@ repeat: } if (com_events >= LOTS_OF_EVENTS) goto repeat; - splx(s); - awake = FALSE; } static int @@ -1370,11 +1502,11 @@ comparam(tp, t) divisor = ttspeedtab(t->c_ospeed, comspeedtab); if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; - if (divisor < 0 || t->c_ispeed != t->c_ospeed) + if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ - unit = UNIT(tp->t_dev); + unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; s = spltty(); @@ -1405,12 +1537,28 @@ comparam(tp, t) if (cflag & CSTOPB) cfcr |= CFCR_STOPB; + if (com->hasfifo) { + /* + * Use a fifo trigger level low enough so that the input + * latency from the fifo is less than about 16 msec and + * the total latency is less than about 30 msec. These + * latencies are reasonable for humans. Serial comms + * protocols shouldn't expect anything better since modem + * latencies are larger. + */ + com->ftl = t->c_ospeed <= 4800 + ? FIFO_TRIGGER_1 : FIFO_TRIGGER_14; + if (com->ftl > com->ftl_max) + com->ftl = com->ftl_max; + outb(iobase + com_fifo, FIFO_ENABLE | com->ftl); + } + /* * Some UARTs lock up if the divisor latch registers are selected * while the UART is doing output (they refuse to transmit anything * more until given a hard reset). Fix this by stopping filling * the device buffers and waiting for them to drain. Reading the - * line status port outside of siointr() might lose some receiver + * line status port outside of siointr1() might lose some receiver * error bits, but that is acceptable here. */ disable_intr(); @@ -1419,8 +1567,8 @@ retry: enable_intr(); while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) { - error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH, - "sioparam", 1); + error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH, + "siotx", hz / 100); if (error != 0 && error != EAGAIN) { if (!(tp->t_state & TS_TTSTOP)) { disable_intr(); @@ -1441,7 +1589,7 @@ retry: * sufficient, for similar reasons. */ if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) - != (LSR_TSRE | LSR_TXRDY)) + != (LSR_TSRE | LSR_TXRDY)) goto retry; if (divisor != 0) { @@ -1460,7 +1608,7 @@ retry: /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? - * Now has 16+ msec latency, while CTS flow has 50- usec latency. + * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state &= ~CS_CTS_OFLOW; com->state |= CS_ODEVREADY; @@ -1471,15 +1619,12 @@ retry: } /* - * Recover from fiddling with CS_TTGO. We used to call siointr() + * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). - * - * XXX sioopen() is not careful waiting for carrier for the callout - * case. */ if (com->state >= (CS_BUSY | CS_TTGO)) - comintr1(com); + siointr1(com); enable_intr(); splx(s); @@ -1494,7 +1639,7 @@ comstart(tp) int s; int unit; - unit = UNIT(tp->t_dev); + unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); s = spltty(); disable_intr(); @@ -1506,35 +1651,31 @@ comstart(tp) if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } else { + /* + * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it + * appropriately in comparam() if RTS-flow is being changed. + * Check for races. + */ if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) goto out; - if (RB_LEN(&tp->t_out) <= tp->t_lowat) { - if (tp->t_state & TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_out); - } - if (tp->t_wsel) { - selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); - tp->t_wsel = 0; - tp->t_state &= ~TS_WCOLL; - } - } + if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) + ttwwakeup(tp); if (com->ocount != 0) { disable_intr(); - comintr1(com); + siointr1(com); enable_intr(); - } else if (RB_LEN(&tp->t_out) != 0) { + } else if (RB_LEN(tp->t_out) != 0) { tp->t_state |= TS_BUSY; - com->ocount = RB_CONTIGGET(&tp->t_out); + com->ocount = RB_CONTIGGET(tp->t_out); disable_intr(); - com->obufend = (com->optr = (u_char *) tp->t_out.rb_hd) + com->obufend = (com->optr = (u_char *)tp->t_out->rb_hd) + com->ocount; com->state |= CS_BUSY; - comintr1(com); /* fake interrupt to start output */ + siointr1(com); /* fake interrupt to start output */ enable_intr(); } out: @@ -1548,7 +1689,7 @@ siostop(tp, rw) { struct com_s *com; - com = com_addr(UNIT(tp->t_dev)); + com = com_addr(DEV_TO_UNIT(tp->t_dev)); if (rw & FWRITE) comflush(com); disable_intr(); @@ -1569,11 +1710,9 @@ sioselect(dev, rw, p) int rw; struct proc *p; { -#ifdef COM_BIDIR - return ttselect(dev & ~COM_CALLOUTMASK, rw, p); -#else - return ttselect(dev, rw, p); -#endif + if (minor(dev) & CONTROL_MASK) + return (ENODEV); + return (ttselect(dev & ~MINOR_MAGIC_MASK, rw, p)); } static void @@ -1600,15 +1739,20 @@ commctl(com, bits, how) static void comwakeup(chan, ticks) - caddr_t chan; - int ticks; + caddr_t chan; + int ticks; { - int unit; + int unit; + + timeout(comwakeup, (caddr_t)NULL, hz / 100); + + if (com_events != 0) { + int s; - timeout(comwakeup, (caddr_t) NULL, hz / 100); - if (com_events != 0) - /* schedule siopoll() to run when the cpl allows */ - setsofttty(); + s = splsofttty(); + siopoll(); + splx(s); + } /* recover from lost output interrupts */ for (unit = 0; unit < NSIO; ++unit) { @@ -1617,27 +1761,97 @@ comwakeup(chan, ticks) com = com_addr(unit); if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) { disable_intr(); - comintr1(com); + siointr1(com); enable_intr(); } } - return; -} - -void -softsio1() -{ - siopoll(); } /* * Following are all routines needed for SIO to act as console - * XXX - not tested in this version - * XXX - i386/cons.c only knows about the com driver (NCOM and not NSIO) - * XXX - check that the corresponding serial interrupts are never enabled */ #include "i386/i386/cons.h" +struct siocnstate { + u_char dlbl; + u_char dlbh; + u_char ier; + u_char cfcr; + u_char mcr; +}; + +static Port_t siocniobase; + +static void siocnclose __P((struct siocnstate *sp)); +static void siocnopen __P((struct siocnstate *sp)); +static void siocntxwait __P((void)); + +static void +siocntxwait() +{ + int timo; + + /* + * Wait for any pending transmission to finish. Required to avoid + * the UART lockup bug when the speed is changed, and for normal + * transmits. + */ + timo = 100000; + while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) + != (LSR_TSRE | LSR_TXRDY) && --timo != 0) + ; +} + +static void +siocnopen(sp) + struct siocnstate *sp; +{ + int divisor; + Port_t iobase; + + /* + * Save all the device control registers except the fifo register + * and set our default ones (cs8 -parenb speed=comdefaultrate). + * We can't save the fifo register since it is read-only. + */ + iobase = siocniobase; + sp->ier = inb(iobase + com_ier); + outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ + siocntxwait(); + sp->cfcr = inb(iobase + com_cfcr); + outb(iobase + com_cfcr, CFCR_DLAB); + sp->dlbl = inb(iobase + com_dlbl); + sp->dlbh = inb(iobase + com_dlbh); + divisor = ttspeedtab(comdefaultrate, comspeedtab); + outb(iobase + com_dlbl, divisor & 0xFF); + outb(iobase + com_dlbh, (u_int) divisor >> 8); + outb(iobase + com_cfcr, CFCR_8BITS); + sp->mcr = inb(iobase + com_mcr); + outb(iobase + com_mcr, MCR_DTR | MCR_RTS); +} + +static void +siocnclose(sp) + struct siocnstate *sp; +{ + Port_t iobase; + + /* + * Restore the device control registers. + */ + siocntxwait(); + iobase = siocniobase; + outb(iobase + com_cfcr, CFCR_DLAB); + outb(iobase + com_dlbl, sp->dlbl); + outb(iobase + com_dlbh, sp->dlbh); + outb(iobase + com_cfcr, sp->cfcr); + /* + * XXX damp oscllations of MCR_DTR and MCR_RTS by not restoring them. + */ + outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); + outb(iobase + com_ier, sp->ier); +} + void siocnprobe(cp) struct consdev *cp; @@ -1645,14 +1859,14 @@ siocnprobe(cp) int unit; /* locate the major number */ + /* XXX - should be elsewhere since KGDB uses it */ for (commajor = 0; commajor < nchrdev; commajor++) if (cdevsw[commajor].d_open == sioopen) break; /* XXX: ick */ - unit = UNIT(CONUNIT); - com_addr(unit) = &com_structs[unit]; - com_addr(unit)->iobase = CONADDR; + unit = DEV_TO_UNIT(CONUNIT); + siocniobase = CONADDR; /* make sure hardware exists? XXX */ @@ -1669,43 +1883,11 @@ void siocninit(cp) struct consdev *cp; { - int unit; - - unit = UNIT(cp->cn_dev); - cominit(unit, comdefaultrate); - comconsole = unit; - comconsinit = TRUE; -} - -static void -cominit(unit, rate) - int unit; - int rate; -{ - Port_t iobase; - int s; - - iobase = com_addr(unit)->iobase; - s = splhigh(); - outb(iobase + com_cfcr, CFCR_DLAB); - rate = ttspeedtab(comdefaultrate, comspeedtab); - outb(iobase + com_dlbl, rate & 0xFF); - outb(iobase + com_dlbh, rate >> 8); - outb(iobase + com_cfcr, CFCR_8BITS); - outb(iobase + com_fifo, - FIFO_ENABLE | FIFO_TRIGGER | FIFO_RCV_RST | FIFO_XMT_RST); - DELAY(100); - (void) inb(iobase + com_lsr); - (void) inb(iobase + com_data); - (void) inb(iobase + com_msr); - /* - * XXX - fishy to enable interrupts and then poll. - * It shouldn't be necessary to ready the iir. + * XXX can delete more comconsole stuff now that i/o routines are + * fairly reentrant. */ - outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); - (void) inb(iobase + com_iir); - splx(s); + comconsole = DEV_TO_UNIT(cp->cn_dev); } int @@ -1715,13 +1897,15 @@ siocngetc(dev) int c; Port_t iobase; int s; + struct siocnstate sp; - iobase = com_addr(UNIT(dev))->iobase; - s = splhigh(); + iobase = siocniobase; + s = spltty(); + siocnopen(&sp); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); - (void) inb(iobase + com_iir); + siocnclose(&sp); splx(s); return (c); } @@ -1731,30 +1915,14 @@ siocnputc(dev, c) dev_t dev; int c; { - Port_t iobase; int s; - int timo; + struct siocnstate sp; - iobase = com_addr(UNIT(dev))->iobase; - s = splhigh(); -#ifdef KGDB - if (dev != kgdb_dev) -#endif - if (!comconsinit) { - cominit(UNIT(dev), comdefaultrate); - comconsinit = TRUE; - } - /* wait for any pending transmission to finish */ - timo = 50000; - while (!(inb(iobase + com_lsr) & LSR_TXRDY) && --timo) - ; - outb(iobase + com_data, c); - /* wait for this transmission to complete */ - timo = 1500000; - while (!(inb(iobase + com_lsr) & LSR_TXRDY) && --timo) - ; - /* clear any interrupts generated by this transmission */ - (void) inb(iobase + com_iir); + s = spltty(); + siocnopen(&sp); + siocntxwait(); + outb(siocniobase + com_data, c); + siocnclose(&sp); splx(s); } diff --git a/sys/i386/isa/sound/CHANGELOG b/sys/i386/isa/sound/CHANGELOG new file mode 100644 index 000000000000..6a9bef153ce3 --- /dev/null +++ b/sys/i386/isa/sound/CHANGELOG @@ -0,0 +1,75 @@ +Changelog for version 2.5 +------------------------- + +Since 2.5-beta2 +- Some fine tuning to the GUS v3.7 mixer code. +- Fixed speed limits for the plain SB (1.0 to 2.0). + +Since 2.5-beta +- Fixed OPL-3 detection with SB. Caused problems with PAS16. +- GUS v3.7 mixer support. + +Since 2.4 +- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h). +- Fixed truncated sound on /dev/dsp when the device is closed. +- Linear volume mode for GUS +- Pitch bends larger than +/- 2 octaves. +- MIDI recording for SB and SB Pro. (Untested). +- Some other fixes. +- SB16 MIDI and DSP drivers only initialized if SB16 actually installed. +- Implemented better detection for OPL-3. This should be usefull if you + have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3. +- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested). + +Since 2.3b +- Fixed bug which made it impossible to make long recordings to disk. + Recording was not restarted after a buffer overflow situation. +- Limited mixer support for GUS. +- Numerous improvements to the GUS driver by Andrew Robinson. Including + some click removal etc. + +Since 2.3 +- Fixed some minor bugs in the SB16 driver. + +Since 2.2b +- Full SB16 DSP support. 8/16 bit, mono/stereo +- The SCO and FreeBSD versions should be in sync now. There are some + problems with SB16 and GUS in the freebsd versions. + The DMA buffer allocation of the SCO version has been polished but + there could still be some problems. At least it hogs memory. + The DMA channel + configuration method used in the sco/System is a hack. +- Support for the MPU emulation of the SB16. +- Some big arrays are now allocated boot time. This makes the bss segment + smaller which makes it possible to use the full driver with + NetBSD. These arrays are not allocated if no suitable soundcard is available. +- Fixed a bug in the compute_and_set_volume in gus_wave.c +- Fixed the too fast mono playback problem of SB Pro and PAS16. + +Since 2.2 +- Stereo recording for SB Pro. Somehow it was missing and nobody + had noticed it earlier. +- Minor polishing. +- Interpreting of boot time arguments (sound=) for Linux. +- Breakup of sb_dsp.c. Parts of the code has been moved to + sb_mixer.c and sb_midi.c + +Since 2.1 +- Preliminary support for SB16. + - The SB16 mixer is supported in it's native mode. + - Digitized voice capability up to 44.1 kHz/8 bit/mono + (16 bit and stereo support coming in the next release). +- Fixed some bugs in the digitized voice driver for PAS16. +- Proper initialization of the SB emulation of latest PAS16 models. + +- Significantly improved /dev/dsp and /dev/audio support. + - Now supports half duplex mode. It's now possible to record and + playback without closing and reopening the device. + - It's possible to use smaller buffers than earlier. There is a new + ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4. + This call instructs the driver to use smaller buffers. The default + buffer size (0.5 to 1.0 seconds) is divided by n. Should be called + immediately after opening the device. + +Since 2.0 +Just cosmetic changes. diff --git a/sys/i386/isa/sound/RELNOTES.Linux b/sys/i386/isa/sound/RELNOTES.Linux index 4082f4e255b0..ea57d0a950c4 100644 --- a/sys/i386/isa/sound/RELNOTES.Linux +++ b/sys/i386/isa/sound/RELNOTES.Linux @@ -1,67 +1,74 @@ -Release notes for the Linux Sound Driver 1.99.9 ------------------------------------------------ - -******** THIS IS A BETA TEST RELEASE ******** -which means that there can be some untested things. In theory -there is a risk that this driver causes some trouble to your system. -You should not use this driver before backing up your disks. - - - - -Welcome to use the Gravis UltraSound driver for Linux. This -driver still supports the same cards than version 1.0c -(SoundBlaster, SB Pro, Pro Audio Spectrum 16 and AdLib). -In addition there is rather limited support for MPU-401 +Release notes for the Linux Sound Driver 2.5 +-------------------------------------------- +There is also a version called 2.5-beta floating around the net. This +version contains some fixes after it. Mainly to the SB and GUS code. + +CAUTION! The SVR4.2 port has not been tested much. Backup your system + carefully before trying it. + +This is mainly a bug fix release. There are couple of new things such as +linear volume mode for GUS and MIDI recording for SB 2.0 and SB Pro. +Also this version supports the mixer of GUS v3.7. (Support for GUS MAX and +the 16-bit daughtercard is coming sooner or later). + +NOTE! The sound driver is a part of the Linux kernel distribution also. + Check that your kernel doesn't have more recent version than this + when installing a separately distributed sound driver. The + version number of this driver is defined in the makefile. + +This version contains a driver for the SB16 also. +The SB16 driver requires separate DMA channels for the 8 and 16 bit +modes. There should be a way to share the 8 bit DMA channels between +these modes but this feature is not supported yet. +The SB16 DSP support is by Joerg Schubert (jsb@sth.ruhr-uni-bochum.de). + +The SB16 driver has also the Midi input capability even at the same +time with the /dev/dsp. Also the WaveBlaster daughter board is supported. +No support for the ASP chip yet (the ASP chip can be installed but it's +not used by the driver). + +You will need the snd-util-2.5.tar.gz and snd-data-0.1.tar.Z +packages to use this driver. They should be in the same +ftp site or BBS from where you got this driver. For +example at nic.funet.fi:pub/OS/Linux/*. + +If you are looking for the installation instructions, please +look at $OS/Readme. + +This version supports the following soundcards: +GUS, SoundBlaster, SB Pro, SB16, Pro Audio Spectrum 16 and AdLib. +In addition there is rather limited support for MPU-401. (and compatible) midi cards. Also the OPL-3 synthesizer -of the SB Pro and PAS16 cards is now supported in the 4 OP -modes. Most of the features of the /dev/sequencer device file are available just for GUS owners. -The SoundBlaster 16 and SB 16 ASP cards are not supported. -They could work in mono mode with speeds < 22 kHz. -The OPL-3 chicp of the SB 16 should work (without problems?). -Is there anybody willing to implement the SB 16 support -(have the SB 16 and the SDK for it)? - - -This is the first version of the driver which has almost -all of the features which I have planned to include into -version 2.0. Some features are still missing and some ones -doesn't work. - NOTE! There are separate driver for CD-ROMS supported by some soundcards. The driver for CDU31A (Fusion 16) is called cdu31a-0.6.diff.z. It will be contained in the Linux version 0.99.12. The driver for the CD-ROM of SB Pro is sbpcd0.4.tar.gz (these were the latest versions when I wrote this). These files should be at least at sunsite.unc.edu. - As far as I know, there is no driver for the SCSI interface of PAS16 - (yet). + Also the SCSI interface of the PAS16 should be supported by + Linux 0.99.13k and later. There is also a driver for joystick. Look for file joystick-0.5.tar.gz (sunsite). - Since this driver is a sound driver, it will not contain support - for SCSI/CD-ROM/Joystick -devices. Compatibility with the earlier versions --------------------------------------- -This is just like the version 1.99.7/1.99.8. There is just some minor -enhancements. Most of them are portability fixes. If you are porting -this driver to any OS, please look at the 386bsd/os.h. There is some -new macros and some macros have more parameters. In addition this file -contains some usefull comments. +In this version the ultrasound.h no longer includes the sys/soundcard.h +You have to change the gmod.c of the snd-util-2.0 package and to add an +include for it. -**** There is some ISC and 386bsd stuff in this driver. Please stay away **** -This stuff is here just because I want to be in sync with the porters. These -ports don't work yet. +IMPORTANT!!!!!!!!!!!!!!!!!!!!!! + +This version is not binary or source compatible with the version 1.0c. The ioctl() interface has changed completely since version 1.0c. All programs using this driver must be at least recompiled. -The snd-util-1.99.6 package contains some utilities for this version. +The snd-util-2.0 package contains some utilities for this version. The version 1.0c and earlier used a 'nonportable' ioctl calling scheme where the input argument was passed by value and the output value was @@ -78,59 +85,46 @@ After version 1.99.0 this must be done as the following: If you have an application written for the version 1.0, you should search for the strings SNDCTL_ and SOUND_ and to check the parameters. +The following ioctl calls have changed: + + SNDCTL_SEQ_GETOUTCOUNT + SNDCTL_SEQ_GETINCOUNT + SNDCTL_SEQ_TESTMIDI + SNDCTL_DSP_SPEED + SNDCTL_DSP_STEREO + SNDCTL_DSP_GETBLKSIZE + SNDCTL_DSP_SAMPLESIZE + SOUND_PCM_WRITE_CHANNELS + SOUND_PCM_WRITE_FILTER + SOUND_PCM_READ_RATE + SOUND_PCM_READ_CHANNELS + SOUND_PCM_READ_BITS + SOUND_PCM_READ_FILTER + SOUND_PCM_WRITE_BITS + SOUND_PCM_WRITE_RATE + SOUND_MIXER_READ_* (several ones) + SOUND_MIXER_WRITE_* (several ones) Since the this version will support more than one synthesizer devices at the same time, the ioctl(SNDCTL_FM_LOAD_INSTR) is obsolete. In addition there is some new fields which must be initialized. Look at the sbiset.c in -the snd-util-1.99.6 package for further info. - -The GUS patch format has changed since the version 1.99.3. You have to -use latest versions of the programs in the sound/gustest directory. In -addition the version 0.4g of the Adagio package supports this format. - -New features ------------- - -There is also some changes which make this version more usable than -the version 1.0c. - -- /dev/dsp and /dev/audio - -The DMA buffering is now little bit more intelligent than earlier. The -buffer size is selected run-time so that a buffer holds data for 0.5 to -1.0 seconds of recording or playback. This makes recording more comfortable -than with version 1.0. With the previous version there was sometimes more -than 10 seconds of delay before the driver returned the first input byte. - -There is also support for more than one digitized voice devices. The device -files /dev/dsp1 and /dev/audio1 (minor 19 and 20) are available with PAS16. -The /dev/dsp (/dev/audio) is connected to the PCM circuit of the PAS16 itself -and the /dev/dsp1 (/dev/audio1) to the SB emulation of PAS16 card. Two -dsp/audio devices are available also if you have combination of SB and GUS. -With GUS and PAS16 you will have even three dsp/audio devices. These devices -can be used independently and can be active at the same time (3 channels -at the same time propably don't work). +the snd-util-2.0 package for further info. -The dsp/audio support of PAS16 should be much cleaner now since the -constant clicking sound between the DMA blocks (about once per second) has -been eliminated. +This version is almost 100% compatible with the alpha test version (1.99.9). The +difference is in the installation procedure. -The stereo playback of GUS doesn't work perfectly. There is lot of -clicking in the output. +Using this driver with other operating systems than Linux +--------------------------------------------------------- -- /dev/mixer +This package contains just the Linux version. The version 2.3 +for SCO is available at nic.funet.fi:pub/OS/Linux/ALPHA/sound. +The version 2.3 doesn't work well with xxxxxBSD. Use the version +2.3 for them. -No changes. - -There is no mixer for the GUS yet. - -- /dev/sequencer - -This part has the most changes. Mostly to support the rich -features of the Gravis UltraSound. There is also the support -for the OPL-3 synthesizer chip. +/dev/sndstat +------------ -- /dev/sndstat +The /dev/sndstat is now available in the SCO and BSD versions also. This is a new devicefile for debugging purposes. A better place for it is in the /proc -directory but I was just too lazy to implement it @@ -139,11 +133,13 @@ info about the current configuration (see the example below). If you send me a error/problem report, please include a printout from this device to your message (cat /dev/sndstat). +Note! This device file is currently present only in the Linux version + of this driver. + ------ cut here --- cat /dev/sndstat example -------- Sound Driver:1.99.7 (Fri Jul 9 17:01:47 GMT 1993 root@lucifer.savolai.fi) Config options: 0x00000d4b -Major number: 14 HW config: Type 4: Gravis Ultrasound at 0x210 irq 15 drq 6 Type 3: ProAudioSpectrum at 0x388 irq 10 drq 3 @@ -166,12 +162,15 @@ Midi devices: Mixer(s) installed ------ cut here ---- End of Example ----------- +Known bugs/limitations +---------------------- -Known bugs ----------- - -- There was clicking during stereo playback to /dev/dsp with GUS. - * Fixed in 1.99.9 * +- High speed recording of long audio samples (>20 second) to disk + is not possible. Everything works until next sync() which delays the + recording process too much. A delay longer than 0.1 to 0.3 seconds is + too much. +- The SB16 driver sometimes swaps the left and right channels together. +- Midi input doesn't work with SB and SB Pro (SB16 works). - It's not possible to open /dev/dsp (or /dev/audio) while the /dev/sequencer is open for output and GUS is the only soundcard installed. It's possible if /dev/dsp is opened before /dev/sequencer @@ -180,12 +179,77 @@ Known bugs - MPU-401 driver hangs the computer on boot if there is no MPU-401 installed. It uses by default the I/O port 0x330 whic is used by Adaptec 1542 SCSI adapter. +- There are some problems in midi input with MPU-401 and the SB16 midi + (MPU-401 emulation). This makes it impossible to read long sysex dumps + using these devices. - The /dev/sequencer playback to GUS sounds sometimes rather weird. Hitting ^C and playing again should solve this problem. This is propably caused by - incompatibilities between GUS and certain VLB motherboards. Try to avoid + incompatibilities between GUS and certain VLB motherboards (like mine). + Try to avoid switching between VTs while patches are being loaded to the GUS. -- There was some problems with GUS and Mitsumi CD in version 1.99.8. Fixed - in 1.99.9. -- /dev/audio sounded like stereo with GUS. Fixed in 1.99.9. + This problem disappears completely if you define GUS_PATCH_NO_DMA in the + local.h (after make config in linux). The drawback is that patch loading + without DMA takes several times longer than with DMA. - There is a skeleton of the patch manager support. It don't work in this version. + + +Future development +------------------ + +- Since this driver is no longer just the Linux Sound Driver, it's time + to give it a new name. I have planned to use name VoxWare. +- I'm writing a Hacker's guide to the VoxWare sound driver. Should + be ready within this(/next) year (alpha version). +- Completion of the ISC, SCO and BSD ports. Port to SVR4.2. +- I'm interested to implement/include support for new soundcards and + operating systems. + + Hint for the soundcard and OS manufacturers: + I'm collecting soundcards (high end ones) and SDKs for them. In + addition I'm collecting PC operating systems. I will be happy if + somebody sends me such items. In addition such kind of donation + makes it easier to change the VoxWare driver to support your + soundcard or operating system. However, please contact me before + sending anything. + +I will propably release some fix versions within this and next year. At +least when the non-Linux versions get ready. The next major release (3.0) +will be quite complete rewrite and released after about a year (end of 94 or +beginning of 95). + + +Contributors +------------ + +This driver contains code by several contributors. In addition several other +persons have given usefull suggestions. The following is a list of major +contributors. (I could have forgotten some names.) + + Craig Metz 1/2 of the PAS16 Mixer and PCM support + Rob Hooft Volume computation algorithm for the FM synth. + Mika Liljeberg uLaw encoding and decoding routines + Greg Lee Volume computation algorithm for the GUS and + lot's of valuable suggestions. + Andy Warner Initial ISC port + Jim Lowe Initial FreeBSD port + Anders Baekgaard Bughunting and valuable suggestions. + Joerg Schubert SB16 DSP support. + Andrew Robinson Improvements to the GUS driver + Megens SA MIDI recording for SB and SB Pro. + Mikael Nordqvist Linear volume support for GUS. + Ian Hartas SVR4.2 port + Markus Aroharju and + Risto Kankkunen Major contributions to the mixer support + of GUS v3.7. + Hunyue Yau Sound Galaxy NX Pro mixer support. + +Regards, + +Hannu Savolainen +hannu@voxware.pp.fi, Hannu.Savolainen@Helsinki.fi + +Snail mail: Hannu Savolainen + Pallaksentie 4 A 2 + 00970 Helsinki + Finland diff --git a/sys/i386/isa/sound/adlib_card.c b/sys/i386/isa/sound/adlib_card.c index 29e521e914d9..6365069384a5 100644 --- a/sys/i386/isa/sound/adlib_card.c +++ b/sys/i386/isa/sound/adlib_card.c @@ -1,11 +1,10 @@ - /* - * linux/kernel/chr_drv/sound/adlib_card.c - * + * sound/adlib_card.c + * * Detection routine for the AdLib card. - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -13,7 +12,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -25,7 +24,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #include "sound_config.h" diff --git a/sys/i386/isa/sound/audio.c b/sys/i386/isa/sound/audio.c index 775e6abeb021..f27f9d5fa09f 100644 --- a/sys/i386/isa/sound/audio.c +++ b/sys/i386/isa/sound/audio.c @@ -1,10 +1,10 @@ /* - * linux/kernel/chr_drv/sound/audio.c - * + * sound/audio.c + * * Device file manager for /dev/audio - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -12,7 +12,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -24,7 +24,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #include "sound_config.h" @@ -38,23 +38,45 @@ #define OFF 0 static int wr_buff_no[MAX_DSP_DEV]; /* != -1, if there is a + * incomplete output block */ static int wr_buff_size[MAX_DSP_DEV], wr_buff_ptr[MAX_DSP_DEV]; + +static int audio_mode[MAX_DSP_DEV]; + +#define AM_NONE 0 +#define AM_WRITE 1 +#define AM_READ 2 + static char *wr_dma_buf[MAX_DSP_DEV]; int audio_open (int dev, struct fileinfo *file) { - int mode; int ret; + int bits; + int dev_type = dev & 0x0f; + int mode = file->mode & O_ACCMODE; dev = dev >> 4; - mode = file->mode & O_ACCMODE; + + if (dev_type == SND_DEV_DSP16) + bits = 16; + else + bits = 8; if ((ret = DMAbuf_open (dev, mode)) < 0) return ret; + if (DMAbuf_ioctl (dev, SNDCTL_DSP_SAMPLESIZE, bits, 1) != bits) + { + audio_release (dev, file); + return RET_ERROR (ENXIO); + } + wr_buff_no[dev] = -1; + audio_mode[dev] = AM_NONE; + return ret; } @@ -106,12 +128,20 @@ audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { int c, p, l; int err; + int dev_type = dev & 0x0f; dev = dev >> 4; p = 0; c = count; + if (audio_mode[dev] == AM_READ) /* Direction changed */ + { + wr_buff_no[dev] = -1; + } + + audio_mode[dev] = AM_WRITE; + if (!count) /* Flush output */ { if (wr_buff_no[dev] >= 0) @@ -136,15 +166,25 @@ audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) if (l > (wr_buff_size[dev] - wr_buff_ptr[dev])) l = (wr_buff_size[dev] - wr_buff_ptr[dev]); - COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l); + if (!dsp_devs[dev]->copy_from_user) + { /* No device specific copy routine */ + COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l); + } + else + dsp_devs[dev]->copy_from_user (dev, + wr_dma_buf[dev], wr_buff_ptr[dev], buf, p, l); + /* Insert local processing here */ + if (dev_type == SND_DEV_AUDIO) + { #ifdef linux - /* This just allows interrupts while the conversion is running */ - __asm__ ("sti"); + /* This just allows interrupts while the conversion is running */ + __asm__ ("sti"); #endif - translate_bytes (ulaw_dsp, &wr_dma_buf[dev][wr_buff_ptr[dev]], l); + translate_bytes (ulaw_dsp, (unsigned char *) &wr_dma_buf[dev][wr_buff_ptr[dev]], l); + } c -= l; p += l; @@ -169,11 +209,24 @@ audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) int c, p, l; char *dmabuf; int buff_no; + int dev_type = dev & 0x0f; dev = dev >> 4; p = 0; c = count; + if (audio_mode[dev] == AM_WRITE) + { + if (wr_buff_no[dev] >= 0) + { + DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); + + wr_buff_no[dev] = -1; + } + } + + audio_mode[dev] = AM_READ; + while (c) { if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l)) < 0) @@ -183,12 +236,16 @@ audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) l = c; /* Insert any local processing here. */ + + if (dev_type == SND_DEV_AUDIO) + { #ifdef linux - /* This just allows interrupts while the conversion is running */ - __asm__ ("sti"); + /* This just allows interrupts while the conversion is running */ + __asm__ ("sti"); #endif - translate_bytes (dsp_ulaw, dmabuf, l); + translate_bytes (dsp_ulaw, (unsigned char *) dmabuf, l); + } COPY_TO_USER (buf, p, dmabuf, l); @@ -205,6 +262,8 @@ int audio_ioctl (int dev, struct fileinfo *file, unsigned int cmd, unsigned int arg) { + int dev_type = dev & 0x0f; + dev = dev >> 4; switch (cmd) @@ -235,11 +294,10 @@ audio_ioctl (int dev, struct fileinfo *file, break; default: -#if 1 - return RET_ERROR (EIO); -#else + if (dev_type == SND_DEV_AUDIO) + return RET_ERROR (EIO); + return DMAbuf_ioctl (dev, cmd, arg, 0); -#endif } } @@ -266,14 +324,14 @@ audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) int audio_open (int dev, struct fileinfo *file) - { - return RET_ERROR (ENXIO); - } +{ + return RET_ERROR (ENXIO); +} void audio_release (int dev, struct fileinfo *file) - { - }; +{ +}; int audio_ioctl (int dev, struct fileinfo *file, unsigned int cmd, unsigned int arg) diff --git a/sys/i386/isa/sound/debug.h b/sys/i386/isa/sound/debug.h deleted file mode 100644 index 79b4acdeba8f..000000000000 --- a/sys/i386/isa/sound/debug.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Kernel driver debugging stuff - cmetz@thor.tjhsst.edu */ - -#ifndef _DEBUG_H_ -#define _DEBUG_H_ - -#if defined (DEBUG_ME) && !defined(HALT_DEBUGGING) - -#define DEB(X) printk("%s %s: ", __FILE__, __FUNCTION__); X -#define DEB1(X) printk("%s %s: ", __FILE__, __FUNCTION__); X -#define RETURN_HEX(X, Y) { Y _foo; _foo = X; printk("%s %s: 0x%x\n", __FILE__, __FUNCTION__, ((int)_foo)); return(_foo); } -#define RETURN_DEC(X, Y) { Y _foo; _foo = X; printk("%s %s: %d\n", __FILE__, __FUNCTION__, ((int)_foo)); return(_foo); } -#define RETURN_PTR(X, Y) { Y _foo; _foo = X; printk("%s %s: 0x%08x\n", __FILE__, __FUNCTION__, ((void *)_foo)); return(_foo); } -#define RETURN_ERR(X) { int _foo; _foo = X; printk("%s %s: ", __FILE__, __FUNCTION__); switch(_foo) { case 0: printk("No error"); break; case -ENODEV: printk("ENODEV"); break; case -EBUSY: printk("EBUSY"); break; default: printk("Error %d", _foo); } printk(".\n"); return(_foo); } -#define DEB_OUTB OUTB -#define DEB_INB INB - -#else - -#define DEB(X) -#define DEB1(X) -#define RETURN_HEX(X, Y) return(X) -#define RETURN_DEC(X, Y) return(X) -#define RETURN_PTR(X, Y) return(X) -#define RETURN_ERR(X) return(X) -#define DEB_OUTB OUTB -#define DEB_INB INB - -#endif -#endif diff --git a/sys/i386/isa/sound/dev_table.c b/sys/i386/isa/sound/dev_table.c index 7eabad4ab8a0..7f7cae11d02e 100644 --- a/sys/i386/isa/sound/dev_table.c +++ b/sys/i386/isa/sound/dev_table.c @@ -1,10 +1,10 @@ /* - * linux/kernel/chr_drv/sound/dev_table.c - * + * sound/dev_table.c + * * Device call tables. - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -12,7 +12,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -24,7 +24,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #define _DEV_TABLE_C_ @@ -38,21 +38,24 @@ sndtable_init (long mem_start) int i, n = sizeof (supported_drivers) / sizeof (struct card_info); for (i = 0; i < (n - 1); i++) - if (supported_drivers[i].probe (&supported_drivers[i].config)) - { + if (supported_drivers[i].enabled) + if (supported_drivers[i].probe (&supported_drivers[i].config)) + { #ifndef SHORT_BANNERS - printk ("snd%d", - supported_drivers[i].card_type); + printk ("snd%d", + supported_drivers[i].card_type); #endif - mem_start = supported_drivers[i].attach (mem_start, &supported_drivers[i].config); + mem_start = supported_drivers[i].attach (mem_start, &supported_drivers[i].config); #ifndef SHORT_BANNERS - printk (" at 0x%03x irq %d drq %d\n", - supported_drivers[i].config.io_base, - supported_drivers[i].config.irq, - supported_drivers[i].config.dma); + printk (" at 0x%x irq %d drq %d\n", + supported_drivers[i].config.io_base, + supported_drivers[i].config.irq, + supported_drivers[i].config.dma); #endif - } + } + else + supported_drivers[i].enabled = 0; /* Mark as not detected */ return mem_start; } @@ -66,7 +69,15 @@ sndtable_probe (int unit, struct address_info *hw_config) for (i = 0; i < (n - 1); i++) if (supported_drivers[i].card_type == unit) - return supported_drivers[i].probe (hw_config); + { + supported_drivers[i].config.io_base = hw_config->io_base; + supported_drivers[i].config.irq = hw_config->irq; + supported_drivers[i].config.dma = hw_config->dma; + if (supported_drivers[i].probe (hw_config)) + return 1; + supported_drivers[i].enabled = 0; /* Mark as not detected */ + return 0; + } return FALSE; } @@ -86,6 +97,10 @@ sndtable_init_card (int unit, struct address_info *hw_config) for (i = 0; i < (n - 1); i++) if (supported_drivers[i].card_type == unit) { + supported_drivers[i].config.io_base = hw_config->io_base; + supported_drivers[i].config.irq = hw_config->irq; + supported_drivers[i].config.dma = hw_config->dma; + if (supported_drivers[i].attach (0, hw_config) != 0) panic ("snd#: Invalid memory allocation\n"); return TRUE; @@ -100,4 +115,103 @@ sndtable_get_cardcount (void) return num_dspdevs + num_mixers + num_synths + num_midis; } +#ifdef linux +void +sound_setup (char *str, int *ints) +{ + int i, n = sizeof (supported_drivers) / sizeof (struct card_info); + + /* + * First disable all drivers + */ + + for (i = 0; i < n; i++) + supported_drivers[i].enabled = 0; + + if (ints[0] == 0 || ints[1] == 0) + return; + /* + * Then enable them one by time + */ + + for (i = 1; i <= ints[0]; i++) + { + int card_type, ioaddr, irq, dma, ptr, j; + unsigned int val; + + val = (unsigned int) ints[i]; + + card_type = (val & 0x0ff00000) >> 20; + + if (card_type > 127) + { + /* Add any future extensions here */ + return; + } + + ioaddr = (val & 0x000fff00) >> 8; + irq = (val & 0x000000f0) >> 4; + dma = (val & 0x0000000f); + + ptr = -1; + for (j = 0; j < n && ptr == -1; j++) + if (supported_drivers[j].card_type == card_type) + ptr = j; + + if (ptr == -1) + printk ("Sound: Invalid setup parameter 0x%08x\n", val); + else + { + supported_drivers[ptr].enabled = 1; + supported_drivers[ptr].config.io_base = ioaddr; + supported_drivers[ptr].config.irq = irq; + supported_drivers[ptr].config.dma = dma; + } + } +} + +#else +void +sound_chconf (int card_type, int ioaddr, int irq, int dma) +{ + int i, n = sizeof (supported_drivers) / sizeof (struct card_info); + + int ptr, j; + + ptr = -1; + for (j = 0; j < n && ptr == -1; j++) + if (supported_drivers[j].card_type == card_type) + ptr = j; + + if (ptr != -1) + { + supported_drivers[ptr].enabled = 1; + if (ioaddr) + supported_drivers[ptr].config.io_base = ioaddr; + if (irq) + supported_drivers[ptr].config.irq = irq; + if (dma) + supported_drivers[ptr].config.dma = dma; + } +} + +#endif + +struct address_info * +sound_getconf (int card_type) +{ + int j, ptr; + int n = sizeof (supported_drivers) / sizeof (struct card_info); + + ptr = -1; + for (j = 0; j < n && ptr == -1; j++) + if (supported_drivers[j].card_type == card_type) + ptr = j; + + if (ptr == -1) + return (struct address_info *) NULL; + + return &supported_drivers[ptr].config; +} + #endif diff --git a/sys/i386/isa/sound/dev_table.h b/sys/i386/isa/sound/dev_table.h index 9bfd7847ecd2..4b656ba39c2b 100644 --- a/sys/i386/isa/sound/dev_table.h +++ b/sys/i386/isa/sound/dev_table.h @@ -46,6 +46,7 @@ struct card_info { long (*attach) (long mem_start, struct address_info *hw_config); int (*probe) (struct address_info *hw_config); struct address_info config; + int enabled; }; /** UWM -- new MIDI structure here.. **/ @@ -57,10 +58,15 @@ struct generic_midi_info{ struct audio_operations { char name[32]; + int flags; +#define NOTHING_SPECIAL 0 +#define NEEDS_RESTART 1 int (*open) (int dev, int mode); void (*close) (int dev); - void (*output_block) (int dev, unsigned long buf, int count, int intrflag); - void (*start_input) (int dev, unsigned long buf, int count, int intrflag); + void (*output_block) (int dev, unsigned long buf, + int count, int intrflag, int dma_restart); + void (*start_input) (int dev, unsigned long buf, + int count, int intrflag, int dma_restart); int (*ioctl) (int dev, unsigned int cmd, unsigned int arg, int local); int (*prepare_for_input) (int dev, int bufsize, int nbufs); int (*prepare_for_output) (int dev, int bufsize, int nbufs); @@ -93,6 +99,7 @@ struct synth_operations { void (*aftertouch) (int dev, int voice, int pressure); void (*controller) (int dev, int voice, int ctrl_num, int value); void (*panning) (int dev, int voice, int value); + void (*volume_method) (int dev, int mode); int (*pmgr_interface) (int dev, struct patmgr_info *info); }; @@ -159,31 +166,42 @@ struct generic_midi_operations { */ struct card_info supported_drivers[] = { -#ifndef EXCLUDE_MPU401 +#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI) {SNDCARD_MPU401,"Roland MPU-401", attach_mpu401, probe_mpu401, - {MPU_BASE, MPU_IRQ, 0}}, -#endif - -#ifndef EXCLUDE_GUS - {SNDCARD_GUS, "Gravis Ultrasound", attach_gus_card, probe_gus, - {GUS_BASE, GUS_IRQ, GUS_DMA}}, + {MPU_BASE, MPU_IRQ, 0}, SND_DEFAULT_ENABLE}, #endif #ifndef EXCLUDE_PAS {SNDCARD_PAS, "ProAudioSpectrum", attach_pas_card, probe_pas, - {PAS_BASE, PAS_IRQ, PAS_DMA}}, + {PAS_BASE, PAS_IRQ, PAS_DMA}, SND_DEFAULT_ENABLE}, #endif #ifndef EXCLUDE_SB {SNDCARD_SB, "SoundBlaster", attach_sb_card, probe_sb, - {SBC_BASE, SBC_IRQ, SBC_DMA}}, + {SBC_BASE, SBC_IRQ, SBC_DMA}, SND_DEFAULT_ENABLE}, +#endif + +#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SBPRO) +#ifndef EXCLUDE_AUDIO + {SNDCARD_SB16, "SoundBlaster16", sb16_dsp_init, sb16_dsp_detect, + {SBC_BASE, SBC_IRQ, SB16_DMA}, SND_DEFAULT_ENABLE}, +#endif +#ifndef EXCLUDE_MIDI + {SNDCARD_SB16MIDI,"SB16 MPU-401", attach_sb16midi, probe_sb16midi, + {SB16MIDI_BASE, SBC_IRQ, 0}, SND_DEFAULT_ENABLE}, +#endif +#endif + +#ifndef EXCLUDE_GUS + {SNDCARD_GUS, "Gravis Ultrasound", attach_gus_card, probe_gus, + {GUS_BASE, GUS_IRQ, GUS_DMA}, SND_DEFAULT_ENABLE}, #endif #ifndef EXCLUDE_YM3812 {SNDCARD_ADLIB, "AdLib", attach_adlib_card, probe_adlib, - {FM_MONO, 0, 0}}, + {FM_MONO, 0, 0}, SND_DEFAULT_ENABLE}, #endif - {0, "*?*", NULL} + {0, "*?*", NULL, 0} }; int num_sound_drivers = @@ -220,6 +238,8 @@ struct generic_midi_operations { long sndtable_init(long mem_start); int sndtable_get_cardcount (void); long CMIDI_init(long mem_start); /* */ +struct address_info *sound_getconf(int card_type); +void sound_chconf(int card_type, int ioaddr, int irq, int dma); #endif #endif diff --git a/sys/i386/isa/sound/dmabuf.c b/sys/i386/isa/sound/dmabuf.c index ace02d149a41..851a70a16b1b 100644 --- a/sys/i386/isa/sound/dmabuf.c +++ b/sys/i386/isa/sound/dmabuf.c @@ -1,10 +1,10 @@ /* - * linux/kernel/chr_drv/sound/dmabuf.c - * + * sound/dmabuf.c + * * The DMA buffer manager for digitized voice applications - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -12,7 +12,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -24,7 +24,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #include "sound_config.h" @@ -35,7 +35,7 @@ #if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS) -#define MAX_SUB_BUFFERS 16 +#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR) /* * The DSP channel can be used either for input or output. Variable @@ -48,7 +48,6 @@ #define DMODE_NONE 0 #define DMODE_OUTPUT 1 #define DMODE_INPUT 2 -#define DMODE_INIT 3 DEFINE_WAIT_QUEUES (dev_sleeper[MAX_DSP_DEV], dev_sleep_flag[MAX_DSP_DEV]); @@ -58,22 +57,6 @@ static int dma_mode[MAX_DSP_DEV] = static volatile int dmabuf_interrupted[MAX_DSP_DEV] = {0}; -#ifdef ISC -/* I don't like this. */ -#undef INTERRUPTIBLE_SLEEP_ON -#define INTERRUPTIBLE_SLEEP_ON(A,F) { \ - A = F = 1; \ - if (sleep(&(A), (PZERO + 5) | PCATCH)) { \ - A = F = 0; \ - dmabuf_interrupted[dev] = 1; \ - dev_busy[dev] = 0; \ - dma_reset(dev); \ - dmabuf_interrupted[dev] = 0; \ - /* longjmp(u.u_qsav, 1); Where it goes??? */ \ - } \ - } -#endif - /* * Pointers to raw buffers */ @@ -89,7 +72,10 @@ int snd_raw_count[MAX_DSP_DEV]; */ static int dev_busy[MAX_DSP_DEV]; +static int dev_needs_restart[MAX_DSP_DEV]; +static int dev_modes[MAX_DSP_DEV]; static int dev_active[MAX_DSP_DEV]; +static int dev_started[MAX_DSP_DEV]; static int dev_qlen[MAX_DSP_DEV]; static int dev_qhead[MAX_DSP_DEV]; static int dev_qtail[MAX_DSP_DEV]; @@ -102,8 +88,10 @@ static int bufferalloc_done[MAX_DSP_DEV] = */ static int dev_nbufs[MAX_DSP_DEV]; /* # of logical buffers ( >= - * sound_buffcounts[dev] */ + + * sound_buffcounts[dev] */ static int dev_counts[MAX_DSP_DEV][MAX_SUB_BUFFERS]; +static int dev_subdivision[MAX_DSP_DEV]; static unsigned long dev_buf_phys[MAX_DSP_DEV][MAX_SUB_BUFFERS]; static char *dev_buf[MAX_DSP_DEV][MAX_SUB_BUFFERS] = { @@ -117,8 +105,8 @@ reorganize_buffers (int dev) * This routine breaks the physical device buffers to logical ones. */ - unsigned long i, p, n; - unsigned long sr, nc, sz, bsz; + unsigned i, p, n; + unsigned sr, nc, sz, bsz; sr = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1); nc = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1); @@ -148,6 +136,17 @@ reorganize_buffers (int dev) if (sound_buffcounts[dev] == 1 && bsz == sound_buffsizes[dev]) bsz >>= 1; /* Need at least 2 buffers */ + if (dev_subdivision[dev] == 0) + dev_subdivision[dev] = 1; /* Default value */ + + bsz /= dev_subdivision[dev]; /* Use smaller buffers */ + + if (bsz == 0) + bsz = 4096; /* Just a sanity check */ + + while ((sound_buffsizes[dev] * sound_buffcounts[dev]) / bsz > MAX_SUB_BUFFERS) + bsz <<= 1; /* Too much buffers */ + dev_buffsize[dev] = bsz; n = 0; @@ -178,6 +177,21 @@ reorganize_buffers (int dev) bufferalloc_done[dev] = 1; } +static void +dma_init_buffers (int dev) +{ + RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]); + dev_underrun[dev] = 0; + + dev_busy[dev] = 1; + + bufferalloc_done[dev] = 0; + + dev_active[dev] = dev_qlen[dev] = dev_qtail[dev] = dev_qhead[dev] = 0; + dev_needs_restart[dev] = dev_started[dev] = 0; + dma_mode[dev] = DMODE_NONE; +} + int DMAbuf_open (int dev, int mode) { @@ -198,20 +212,23 @@ DMAbuf_open (int dev, int mode) return RET_ERROR (ENXIO); } +#ifdef USE_RUNTIME_DMAMEM + sound_dma_malloc (dev); +#endif + if (snd_raw_buf[dev][0] == NULL) return RET_ERROR (ENOSPC); /* Memory allocation failed during boot */ if ((retval = dsp_devs[dev]->open (dev, mode)) < 0) return retval; - dev_underrun[dev] = 0; - - dev_busy[dev] = 1; + dev_modes[dev] = mode; + dev_subdivision[dev] = 0; - reorganize_buffers (dev); - bufferalloc_done[dev] = 0; - - dev_qlen[dev] = dev_qtail[dev] = dev_qhead[dev] = 0; + dma_init_buffers (dev); + dsp_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_BITS, 8, 1); + dsp_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_CHANNELS, 1, 1); + dsp_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_RATE, DSP_DEFAULT_SPEED, 1); return 0; } @@ -219,35 +236,37 @@ DMAbuf_open (int dev, int mode) static void dma_reset (int dev) { + int retval; + unsigned long flags; + + DISABLE_INTR (flags); dsp_devs[dev]->reset (dev); + dsp_devs[dev]->close (dev); - dev_qlen[dev] = 0; - dev_qhead[dev] = 0; - dev_qtail[dev] = 0; - dev_active[dev] = 0; + if ((retval = dsp_devs[dev]->open (dev, dev_modes[dev])) < 0) + printk ("Sound: Reset failed - Can't reopen device\n"); + RESTORE_INTR (flags); + + dma_init_buffers (dev); + reorganize_buffers (dev); } static int dma_sync (int dev) { unsigned long flags; - unsigned long time; - int timed_out; if (dma_mode[dev] == DMODE_OUTPUT) { DISABLE_INTR (flags); - timed_out = 0; - time = GET_TIME (); - - while ((!(PROCESS_ABORTING || dmabuf_interrupted[dev]) && !timed_out) + while ((!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) || + dmabuf_interrupted[dev])) && dev_qlen[dev]) { - REQUEST_TIMEOUT (10 * HZ, dev_sleeper[dev]); - INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]); - if ((GET_TIME () - time) > (10 * HZ)) - timed_out = 1; + DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 10 * HZ); + if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev])) + return dev_qlen[dev]; } RESTORE_INTR (flags); @@ -259,11 +278,11 @@ dma_sync (int dev) DISABLE_INTR (flags); if (dsp_devs[dev]->has_output_drained) /* Device has hidden buffers */ { - while (!(PROCESS_ABORTING || dmabuf_interrupted[dev]) + while (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) || + dmabuf_interrupted[dev]) && !dsp_devs[dev]->has_output_drained (dev)) { - REQUEST_TIMEOUT (HZ / 4, dev_sleeper[dev]); - INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]); + DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], HZ / 4); } } RESTORE_INTR (flags); @@ -275,16 +294,20 @@ int DMAbuf_release (int dev, int mode) { - if (!(PROCESS_ABORTING || dmabuf_interrupted[dev]) + if (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) || + dmabuf_interrupted[dev]) && (dma_mode[dev] == DMODE_OUTPUT)) { dma_sync (dev); } - dma_reset (dev); +#ifdef USE_RUNTIME_DMAMEM + sound_dma_free (dev); +#endif - if (!dev_active[dev]) - dsp_devs[dev]->close (dev); + dsp_devs[dev]->reset (dev); + + dsp_devs[dev]->close (dev); dma_mode[dev] = DMODE_NONE; dev_busy[dev] = 0; @@ -296,40 +319,65 @@ int DMAbuf_getrdbuffer (int dev, char **buf, int *len) { unsigned long flags; + int err = EIO; - if (!bufferalloc_done[dev]) - reorganize_buffers (dev); - - if (!dma_mode[dev]) + DISABLE_INTR (flags); + if (!dev_qlen[dev]) { - int err; + if (dev_needs_restart[dev]) + { + dma_reset (dev); + dev_needs_restart[dev] = 0; + } - if ((err = dsp_devs[dev]->prepare_for_input (dev, - dev_buffsize[dev], dev_nbufs[dev])) < 0) - return err; - dma_mode[dev] = DMODE_INPUT; - } + if (dma_mode[dev] == DMODE_OUTPUT) /* Was output -> direction change */ + { + dma_sync (dev); + dma_reset (dev); + dma_mode[dev] = DMODE_NONE; + } - if (dma_mode[dev] != DMODE_INPUT) - return RET_ERROR (EBUSY); /* Can't change mode on fly */ + if (!bufferalloc_done[dev]) + reorganize_buffers (dev); + + if (!dma_mode[dev]) + { + int err; + + if ((err = dsp_devs[dev]->prepare_for_input (dev, + dev_buffsize[dev], dev_nbufs[dev])) < 0) + { + RESTORE_INTR (flags); + return err; + } + dma_mode[dev] = DMODE_INPUT; + } - DISABLE_INTR (flags); - if (!dev_qlen[dev]) - { if (!dev_active[dev]) { - dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]], dev_buffsize[dev], 0); + dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]], + dev_buffsize[dev], 0, + !sound_dma_automode[dev] || + !dev_started[dev]); dev_active[dev] = 1; + dev_started[dev] = 1; } /* Wait for the next block */ - REQUEST_TIMEOUT (10 * HZ, dev_sleeper[dev]); - INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]); + DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ); + if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev])) + { + printk ("Sound: DMA timed out - IRQ/DRQ config error?\n"); + err = EIO; + SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]); + } + else + err = EINTR; } RESTORE_INTR (flags); if (!dev_qlen[dev]) - return RET_ERROR (EINTR); + return RET_ERROR (err); *buf = &dev_buf[dev][dev_qhead[dev]][dev_counts[dev][dev_qhead[dev]]]; *len = dev_buffsize[dev] - dev_counts[dev][dev_qhead[dev]]; @@ -391,6 +439,7 @@ DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) case SNDCTL_DSP_SYNC: dma_sync (dev); + dma_reset (dev); return 0; break; @@ -401,10 +450,37 @@ DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) return IOCTL_OUT (arg, dev_buffsize[dev]); break; + case SNDCTL_DSP_SUBDIVIDE: + { + int fact = IOCTL_IN (arg); + + if (fact == 0) + { + fact = dev_subdivision[dev]; + if (fact == 0) + fact = 1; + return IOCTL_OUT (arg, fact); + } + + if (dev_subdivision[dev] != 0) /* Too late to change */ + return RET_ERROR (EINVAL); + + if (fact > MAX_REALTIME_FACTOR) + return RET_ERROR (EINVAL); + + if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16) + return RET_ERROR (EINVAL); + + dev_subdivision[dev] = fact; + return IOCTL_OUT (arg, fact); + } + break; + default: return dsp_devs[dev]->ioctl (dev, cmd, arg, local); } + /* NOTREACHED */ return RET_ERROR (EIO); } @@ -412,6 +488,20 @@ int DMAbuf_getwrbuffer (int dev, char **buf, int *size) { unsigned long flags; + int err = EIO; + + if (dma_mode[dev] == DMODE_INPUT) /* Was input -> Direction change */ + { + dma_reset (dev); + dma_mode[dev] = DMODE_NONE; + } + else if (dev_needs_restart[dev]) /* Restart buffering */ + { + dma_sync (dev); + dma_reset (dev); + } + + dev_needs_restart[dev] = 0; if (!bufferalloc_done[dev]) reorganize_buffers (dev); @@ -426,10 +516,11 @@ DMAbuf_getwrbuffer (int dev, char **buf, int *size) return err; } - if (dma_mode[dev] != DMODE_OUTPUT) - return RET_ERROR (EBUSY); /* Can't change mode on fly */ DISABLE_INTR (flags); + + RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]); + if (dev_qlen[dev] == dev_nbufs[dev]) { if (!dev_active[dev]) @@ -440,14 +531,20 @@ DMAbuf_getwrbuffer (int dev, char **buf, int *size) } /* Wait for free space */ - REQUEST_TIMEOUT (60 * HZ, dev_sleeper[dev]); /* GUS requires up to 60 - * sec */ - INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]); + DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ); + if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev])) + { + printk ("Sound: DMA timed out - IRQ/DRQ config error?\n"); + err = EIO; + SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]); + } + else if (PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])) + err = EINTR; } RESTORE_INTR (flags); if (dev_qlen[dev] == dev_nbufs[dev]) - return RET_ERROR (EIO); /* We have got signal (?) */ + return RET_ERROR (err); /* We have got signal (?) */ *buf = dev_buf[dev][dev_qtail[dev]]; *size = dev_buffsize[dev]; @@ -466,12 +563,18 @@ DMAbuf_start_output (int dev, int buff_no, int l) dev_counts[dev][dev_qtail[dev]] = l; + dev_needs_restart[dev] = (l != dev_buffsize[dev]) && + (sound_dma_automode[dev] || dsp_devs[dev]->flags & NEEDS_RESTART); + dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev]; if (!dev_active[dev]) { dev_active[dev] = 1; - dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]], dev_counts[dev][dev_qhead[dev]], 0); + dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]], + dev_counts[dev][dev_qhead[dev]], 0, + !sound_dma_automode[dev] || !dev_started[dev]); + dev_started[dev] = 1; } return 0; @@ -504,27 +607,33 @@ DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) set_dma_count (chan, sound_buffsizes[dev]); enable_dma (chan); RESTORE_INTR (flags); -#else +#else /* linux */ #ifdef __386BSD__ printk ("sound: Invalid DMA mode for device %d\n", dev); isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE, (caddr_t)snd_raw_buf_phys[dev][0], - (unsigned)sound_buffsizes[dev], - (unsigned)chan); -#else -#ifdef ISC + sound_buffsizes[dev], + chan); +#else /* __386BSD__ */ +#if defined(ISC) || defined(SCO) || defined(SVR42) +#ifndef DMAMODE_AUTO printk ("sound: Invalid DMA mode for device %d\n", dev); - dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode) | DMAMODE_AUTO, - snd_raw_buf_phys[dev][0], count - 1); +#endif /* DMAMODE_AUTO */ + dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode) +#ifdef DMAMODE_AUTO + | DMAMODE_AUTO +#endif /* DMAMODE_AUTO */ + , + snd_raw_buf_phys[dev][0], count); dma_enable (chan); -#else -# error This routine is not valid for this OS. -#endif -#endif +#else /* SYSV */ +#error This routine is not valid for this OS. +#endif /* SYSV */ +#endif /* __386BSD__ */ -#endif +#endif /* linux */ } else { @@ -537,24 +646,24 @@ DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) set_dma_count (chan, count); enable_dma (chan); RESTORE_INTR (flags); -#else +#else /* linux */ #ifdef __386BSD__ isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE, (caddr_t)physaddr, count, chan); -#else +#else /* __386BSD__ */ -#ifdef ISC +#if defined(ISC) || defined(SCO) || defined(SVR42) dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode), - physaddr, count - 1); + physaddr, count); dma_enable (chan); -#else -# error This routine is not valid for this OS. -#endif /* !ISC */ -#endif +#else /* SYSV */ +#error This routine is not valid for this OS. +#endif /* SYSV */ +#endif /* __386BSD__ */ -#endif +#endif /* linux */ } return count; @@ -584,37 +693,33 @@ DMAbuf_init (long mem_start) } void -DMAbuf_outputintr (int dev) +DMAbuf_outputintr (int dev, int underrun_flag) { unsigned long flags; - dev_active[dev] = 0; dev_qlen[dev]--; dev_qhead[dev] = (dev_qhead[dev] + 1) % dev_nbufs[dev]; + dev_active[dev] = 0; if (dev_qlen[dev]) { - dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]], dev_counts[dev][dev_qhead[dev]], 1); + dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]], + dev_counts[dev][dev_qhead[dev]], 1, + !sound_dma_automode[dev]); dev_active[dev] = 1; } - else + else if (underrun_flag) { - if (dev_busy[dev]) - { - dev_underrun[dev]++; - dsp_devs[dev]->halt_xfer (dev); - } - else - { /* Device has been closed */ - dsp_devs[dev]->close (dev); - } + dev_underrun[dev]++; + dsp_devs[dev]->halt_xfer (dev); + dev_needs_restart[dev] = (sound_dma_automode[dev] || + dsp_devs[dev]->flags & NEEDS_RESTART); } DISABLE_INTR (flags); - if (dev_sleep_flag[dev]) + if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev])) { - dev_sleep_flag[dev] = 0; - WAKE_UP (dev_sleeper[dev]); + WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]); } RESTORE_INTR (flags); } @@ -624,30 +729,33 @@ DMAbuf_inputintr (int dev) { unsigned long flags; - dev_active[dev] = 0; if (!dev_busy[dev]) { dsp_devs[dev]->close (dev); } else if (dev_qlen[dev] == (dev_nbufs[dev] - 1)) { + printk ("Sound: Recording overrun\n"); dev_underrun[dev]++; dsp_devs[dev]->halt_xfer (dev); + dev_active[dev] = 0; + dev_needs_restart[dev] = sound_dma_automode[dev]; } else { dev_qlen[dev]++; dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev]; - dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]], dev_buffsize[dev], 1); + dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]], + dev_buffsize[dev], 1, + !sound_dma_automode[dev]); dev_active[dev] = 1; } DISABLE_INTR (flags); - if (dev_sleep_flag[dev]) + if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev])) { - dev_sleep_flag[dev] = 0; - WAKE_UP (dev_sleeper[dev]); + WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]); } RESTORE_INTR (flags); } @@ -691,13 +799,13 @@ DMAbuf_reset_dma (int chan) /* * The sound_mem_init() is called by mem_init() immediately after mem_map is * initialized and before free_page_list is created. - * + * * This routine allocates DMA buffers at the end of available physical memory ( * <16M) and marks pages reserved at mem_map. */ #else -/* Stub versions if audio services not included */ +/* Stub versions if audio services not included */ int DMAbuf_open (int dev, int mode) @@ -784,7 +892,7 @@ DMAbuf_inputintr (int dev) } void -DMAbuf_outputintr (int dev) +DMAbuf_outputintr (int dev, int underrun_flag) { return; } diff --git a/sys/i386/isa/sound/dsp.c b/sys/i386/isa/sound/dsp.c deleted file mode 100644 index fca931afab3e..000000000000 --- a/sys/i386/isa/sound/dsp.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * linux/kernel/chr_drv/sound/dsp.c - * - * Device file manager for /dev/dsp - * - * Copyright by Hannu Savolainen 1993 - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. 2. - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ - -#include "sound_config.h" - -#ifdef CONFIGURE_SOUNDCARD - -#ifndef EXCLUDE_AUDIO - -#define ON 1 -#define OFF 0 - -static int wr_buff_no[MAX_DSP_DEV]; /* != -1, if there is a - * incomplete output block */ -static int wr_buff_size[MAX_DSP_DEV], wr_buf_ptr[MAX_DSP_DEV]; -static char *wr_dma_buf[MAX_DSP_DEV]; - -int -dsp_open (int dev, struct fileinfo *file, int bits) -{ - int mode; - int ret; - - dev = dev >> 4; - mode = file->mode & O_ACCMODE; - - if ((ret = DMAbuf_open (dev, mode)) < 0) - return ret; - - if (DMAbuf_ioctl (dev, SNDCTL_DSP_SAMPLESIZE, bits, 1) != bits) - { - dsp_release (dev, file); - return RET_ERROR (ENXIO); - } - - wr_buff_no[dev] = -1; - - return ret; -} - -void -dsp_release (int dev, struct fileinfo *file) -{ - int mode; - - dev = dev >> 4; - mode = file->mode & O_ACCMODE; - - if (wr_buff_no[dev] >= 0) - { - DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev]); - - wr_buff_no[dev] = -1; - } - - DMAbuf_release (dev, mode); -} - - -int -dsp_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) -{ - int c, p, l; - int err; - - dev = dev >> 4; - - p = 0; - c = count; - - if (!count) /* Flush output */ - { - if (wr_buff_no[dev] >= 0) - { - DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev]); - - wr_buff_no[dev] = -1; - } - return 0; - } - - while (c) - { /* Perform output blocking */ - if (wr_buff_no[dev] < 0) /* There is no incomplete buffers */ - { - if ((wr_buff_no[dev] = DMAbuf_getwrbuffer (dev, &wr_dma_buf[dev], - &wr_buff_size[dev])) < 0) - return wr_buff_no[dev]; - wr_buf_ptr[dev] = 0; - } - - l = c; - if (l > (wr_buff_size[dev] - wr_buf_ptr[dev])) - l = (wr_buff_size[dev] - wr_buf_ptr[dev]); - - if (!dsp_devs[dev]->copy_from_user) - { /* No device specific copy routine */ - COPY_FROM_USER (&wr_dma_buf[dev][wr_buf_ptr[dev]], buf, p, l); - } - else - dsp_devs[dev]->copy_from_user (dev, - wr_dma_buf[dev], wr_buf_ptr[dev], buf, p, l); - - c -= l; - p += l; - wr_buf_ptr[dev] += l; - - if (wr_buf_ptr[dev] >= wr_buff_size[dev]) - { - if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev])) < 0) - return err; - - wr_buff_no[dev] = -1; - } - - } - - return count; -} - - -int -dsp_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) -{ - int c, p, l; - char *dmabuf; - int buff_no; - - dev = dev >> 4; - p = 0; - c = count; - - while (c) - { - if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l)) < 0) - return buff_no; - - if (l > c) - l = c; - - /* Insert any local processing here. */ - - COPY_TO_USER (buf, 0, dmabuf, l); - - DMAbuf_rmchars (dev, buff_no, l); - - p += l; - c -= l; - } - - return count - c; -} - -int -dsp_ioctl (int dev, struct fileinfo *file, - unsigned int cmd, unsigned int arg) -{ - - dev = dev >> 4; - - switch (cmd) - { - case SNDCTL_DSP_SYNC: - if (wr_buff_no[dev] >= 0) - { - DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev]); - - wr_buff_no[dev] = -1; - } - return DMAbuf_ioctl (dev, cmd, arg, 0); - break; - - case SNDCTL_DSP_POST: - if (wr_buff_no[dev] >= 0) - { - DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev]); - - wr_buff_no[dev] = -1; - } - return 0; - break; - - case SNDCTL_DSP_RESET: - wr_buff_no[dev] = -1; - return DMAbuf_ioctl (dev, cmd, arg, 0); - break; - - default: - return DMAbuf_ioctl (dev, cmd, arg, 0); - } -} - -long -dsp_init (long mem_start) -{ - return mem_start; -} - -#else -/* Stub version */ -int -dsp_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) -{ - return RET_ERROR (EIO); -} - -int -dsp_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) -{ - return RET_ERROR (EIO); -} - -int -dsp_open (int dev, struct fileinfo *file, int bits) -{ - return RET_ERROR (ENXIO); -} - -void -dsp_release (int dev, struct fileinfo *file) - { - }; -int -dsp_ioctl (int dev, struct fileinfo *file, - unsigned int cmd, unsigned int arg) -{ - return RET_ERROR (EIO); -} - -int -dsp_lseek (int dev, struct fileinfo *file, off_t offset, int orig) -{ - return RET_ERROR (EIO); -} - -long -dsp_init (long mem_start) -{ - return mem_start; -} - -#endif - -#endif diff --git a/sys/i386/isa/sound/gus_card.c b/sys/i386/isa/sound/gus_card.c index 414034deef6f..c7cfc0a7ab6c 100644 --- a/sys/i386/isa/sound/gus_card.c +++ b/sys/i386/isa/sound/gus_card.c @@ -1,10 +1,10 @@ /* - * linux/kernel/chr_drv/sound/gus_card.c - * + * sound/gus_card.c + * * Detection routine for the Gravis Ultrasound. - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -12,7 +12,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -24,7 +24,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #include "sound_config.h" @@ -37,76 +37,12 @@ void gusintr (int); int gus_base, gus_irq, gus_dma; -static int -set_gus_irq (int interrupt_level) -{ - int retcode = EINVAL; - -#ifdef linux - struct sigaction sa; - - sa.sa_handler = gusintr; - -#ifdef SND_SA_INTERRUPT - sa.sa_flags = SA_INTERRUPT; -#else - sa.sa_flags = 0; -#endif - - sa.sa_mask = 0; - sa.sa_restorer = NULL; - - retcode = irqaction (interrupt_level, &sa); - - if (retcode < 0) - { - printk ("GUS: IRQ%d already in use\n", interrupt_level); - } - -#else - /* # error Unimplemented for this OS */ -#endif - return retcode; -} - -int -gus_set_midi_irq (int interrupt_level) -{ - int retcode = EINVAL; - -#ifdef linux - struct sigaction sa; - - sa.sa_handler = gus_midi_interrupt; - -#ifdef SND_SA_INTERRUPT - sa.sa_flags = SA_INTERRUPT; -#else - sa.sa_flags = 0; -#endif - - sa.sa_mask = 0; - sa.sa_restorer = NULL; - - retcode = irqaction (interrupt_level, &sa); - - if (retcode < 0) - { - printk ("GUS: IRQ%d already in use\n", interrupt_level); - } - -#else - /* # error Unimplemented for this OS */ -#endif - return retcode; -} - long attach_gus_card (long mem_start, struct address_info *hw_config) { int io_addr; - set_gus_irq (hw_config->irq); + snd_set_irq_handler (hw_config->irq, gusintr); if (gus_wave_detect (hw_config->io_base)) /* Try first the default */ { @@ -127,7 +63,7 @@ attach_gus_card (long mem_start, struct address_info *hw_config) if (io_addr != hw_config->io_base) /* Already tested */ if (gus_wave_detect (io_addr)) { - printk (" WARNING! GUS found at %03x, config was %03x ", io_addr, hw_config->io_base); + printk (" WARNING! GUS found at %x, config was %x ", io_addr, hw_config->io_base); mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma); #ifndef EXCLUDE_MIDI mem_start = gus_midi_init (mem_start); @@ -168,7 +104,10 @@ void gusintr (int unit) { unsigned char src; - unsigned long flags; + +#ifdef linux + sti (); +#endif while (1) { @@ -195,9 +134,7 @@ gusintr (int unit) if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ)) { - DISABLE_INTR (flags); gus_voice_irq (); - RESTORE_INTR (flags); } } } diff --git a/sys/i386/isa/sound/gus_hw.h b/sys/i386/isa/sound/gus_hw.h index 48233e7e1e32..f97a0b8670e3 100644 --- a/sys/i386/isa/sound/gus_hw.h +++ b/sys/i386/isa/sound/gus_hw.h @@ -24,6 +24,8 @@ #define u_Command (gus_base + 0x103) #define u_DataLo (gus_base + 0x104) #define u_DataHi (gus_base + 0x105) +#define u_MixData (gus_base + 0x106) /* Rev. 3.7+ mixing */ +#define u_MixSelect (gus_base + 0x506) /* registers. */ #define u_IrqStatus u_Status # define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */ # define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */ @@ -32,4 +34,17 @@ # define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */ # define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */ # define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */ + +#define ICS2101 1 +# define ICS_MIXDEVS 6 +# define DEV_MIC 0 +# define DEV_LINE 1 +# define DEV_CD 2 +# define DEV_GF1 3 +# define DEV_UNUSED 4 +# define DEV_VOL 5 + +# define CHN_LEFT 0 +# define CHN_RIGHT 1 +#define CS4231 2 #define u_DRAMIO (gus_base + 0x107) diff --git a/sys/i386/isa/sound/gus_linearvol.h b/sys/i386/isa/sound/gus_linearvol.h new file mode 100644 index 000000000000..7ad0c30d4fd9 --- /dev/null +++ b/sys/i386/isa/sound/gus_linearvol.h @@ -0,0 +1,18 @@ +static unsigned short gus_linearvol[128] = { + 0x0000, 0x08ff, 0x09ff, 0x0a80, 0x0aff, 0x0b40, 0x0b80, 0x0bc0, + 0x0bff, 0x0c20, 0x0c40, 0x0c60, 0x0c80, 0x0ca0, 0x0cc0, 0x0ce0, + 0x0cff, 0x0d10, 0x0d20, 0x0d30, 0x0d40, 0x0d50, 0x0d60, 0x0d70, + 0x0d80, 0x0d90, 0x0da0, 0x0db0, 0x0dc0, 0x0dd0, 0x0de0, 0x0df0, + 0x0dff, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38, + 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78, + 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8, + 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8, + 0x0eff, 0x0f04, 0x0f08, 0x0f0c, 0x0f10, 0x0f14, 0x0f18, 0x0f1c, + 0x0f20, 0x0f24, 0x0f28, 0x0f2c, 0x0f30, 0x0f34, 0x0f38, 0x0f3c, + 0x0f40, 0x0f44, 0x0f48, 0x0f4c, 0x0f50, 0x0f54, 0x0f58, 0x0f5c, + 0x0f60, 0x0f64, 0x0f68, 0x0f6c, 0x0f70, 0x0f74, 0x0f78, 0x0f7c, + 0x0f80, 0x0f84, 0x0f88, 0x0f8c, 0x0f90, 0x0f94, 0x0f98, 0x0f9c, + 0x0fa0, 0x0fa4, 0x0fa8, 0x0fac, 0x0fb0, 0x0fb4, 0x0fb8, 0x0fbc, + 0x0fc0, 0x0fc4, 0x0fc8, 0x0fcc, 0x0fd0, 0x0fd4, 0x0fd8, 0x0fdc, + 0x0fe0, 0x0fe4, 0x0fe8, 0x0fec, 0x0ff0, 0x0ff4, 0x0ff8, 0x0ffc +}; diff --git a/sys/i386/isa/sound/gus_midi.c b/sys/i386/isa/sound/gus_midi.c index b5cd6844cd1d..935c5c90b324 100644 --- a/sys/i386/isa/sound/gus_midi.c +++ b/sys/i386/isa/sound/gus_midi.c @@ -1,10 +1,10 @@ /* - * linux/kernel/chr_drv/sound/gus2_midi.c - * + * sound/gus2_midi.c + * * The low level driver for the GUS Midi Interface. - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -12,7 +12,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -24,7 +24,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #include "sound_config.h" @@ -215,7 +215,7 @@ gus_midi_buffer_status (int dev) static struct midi_operations gus_midi_operations = { - {"Gravis UltraSound", 0}, + {"Gravis UltraSound", 0, 0, SNDCARD_GUS}, gus_midi_open, gus_midi_close, gus_midi_ioctl, diff --git a/sys/i386/isa/sound/gus_vol.c b/sys/i386/isa/sound/gus_vol.c index b3f1e84060ad..055a1170e9fb 100644 --- a/sys/i386/isa/sound/gus_vol.c +++ b/sys/i386/isa/sound/gus_vol.c @@ -1,10 +1,11 @@ /* * gus_vol.c - Compute volume for GUS. - * + * * Greg Lee 1993. */ #include "sound_config.h" #ifndef EXCLUDE_GUS +#include "gus_linearvol.h" #define GUS_VOLUME gus_wave_volume @@ -20,7 +21,7 @@ extern int gus_wave_volume; * to expression controller messages, if they were found to be used for * dynamic volume adjustments, so here, main volume can be assumed to be * constant throughout a song.) - * + * * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so * we can give a big boost to very weak voices like nylon guitar and the * basses. The normal value is 64. Strings are assigned lower values. @@ -38,24 +39,37 @@ gus_adagio_vol (int vel, int mainv, int xpn, int voicev) */ x = 256 + 6 * (voicev - 64); - /* Boost expression by voice volume above neutral. */ + /* + * Boost expression by voice volume above neutral. + */ if (voicev > 65) xpn += voicev - 64; xpn += (voicev - 64) / 2; - /* Combine multiplicative and level components. */ + /* + * Combine multiplicative and level components. + */ x = vel * xpn * 6 + (voicev / 4) * x; #ifdef GUS_VOLUME /* * Further adjustment by installation-specific master volume control - * (default 50). + * (default 60). */ x = (x * GUS_VOLUME * GUS_VOLUME) / 10000; #endif - if (x < (1 << 11)) - return (11 << 8); +#ifdef GUS_USE_CHN_MAIN_VOLUME + /* + * Experimental support for the channel main volume + */ + + mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */ + x = (x * mainv * mainv) / 16384; +#endif + + if (x < 2) + return (0); else if (x >= 65535) return ((15 << 8) | 255); @@ -82,7 +96,9 @@ gus_adagio_vol (int vel, int mainv, int xpn, int voicev) */ m = x - (1 << i); - /* Adjust mantissa to 8 bits. */ + /* + * Adjust mantissa to 8 bits. + */ if (m > 0) { if (i > 8) @@ -91,11 +107,41 @@ gus_adagio_vol (int vel, int mainv, int xpn, int voicev) m <<= 8 - i; } - /* low volumes give occasional sour notes */ - if (i < 11) - return (11 << 8); - return ((i << 8) + m); } +/* + * Volume-values are interpreted as linear values. Volume is based on the + * value supplied with SEQ_START_NOTE(), channel main volume (if compiled in) + * and the volume set by the mixer-device (default 60%). + */ + +unsigned short +gus_linear_vol (int vol, int mainvol) +{ + int mixer_mainvol; + + if (vol <= 0) + vol = 0; + else if (vol >= 127) + vol = 127; + +#ifdef GUS_VOLUME + mixer_mainvol = GUS_VOLUME; +#else + mixer_mainvol = 100; +#endif + +#ifdef GUS_USE_CHN_MAIN_VOLUME + if (mainvol <= 0) + mainvol = 0; + else if (mainvol >= 127) + mainvol = 127; +#else + mainvol = 128; +#endif + + return gus_linearvol[(((vol * mainvol) / 128) * mixer_mainvol) / 100]; +} + #endif diff --git a/sys/i386/isa/sound/gus_wave.c b/sys/i386/isa/sound/gus_wave.c index deb3a1525991..9f6442e0fcff 100644 --- a/sys/i386/isa/sound/gus_wave.c +++ b/sys/i386/isa/sound/gus_wave.c @@ -1,11 +1,10 @@ - /* - * linux/kernel/chr_drv/sound/gus_wave.c - * + * sound/gus_wave.c + * * Driver for the Gravis UltraSound wave table synth. - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -13,7 +12,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -25,18 +24,20 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ -/* #define GUS_LINEAR_VOLUME */ - #include "sound_config.h" +#ifdef __FreeBSD__ #include <machine/ultrasound.h> +#else +#include "ultrasound.h" +#endif #include "gus_hw.h" #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS) -#define MAX_SAMPLE 256 +#define MAX_SAMPLE 128 #define MAX_PATCH 256 struct voice_info @@ -57,6 +58,7 @@ struct voice_info int volume_irq_mode, volume_irq_parm; #define VMODE_HALT 1 #define VMODE_ENVELOPE 2 +#define VMODE_START_NOTE 3 int env_phase; unsigned char env_rate[6]; @@ -67,6 +69,11 @@ struct voice_info */ int main_vol, expression_vol, patch_vol; + /* Variables for "Ultraclick" removal */ + int dev_pending, note_pending, volume_pending, sample_pending; + char kill_pending; + long offset_pending; + }; extern int gus_base; @@ -77,24 +84,30 @@ extern int snd_raw_count[MAX_DSP_DEV]; static long gus_mem_size = 0; static long free_mem_ptr = 0; static int gus_busy = 0; -static int nr_voices = 0; /* Number of currently allowed voices */ +static int nr_voices = 0; static int gus_devnum = 0; static int volume_base, volume_scale, volume_method; +static int gus_line_vol = 100, gus_mic_vol = 0; +static int gus_recmask = SOUND_MASK_MIC; +static int recording_active = 0; -#define VOL_METHOD_ADAGIO 1 -int gus_wave_volume = 60; /* Master wolume for wave (0 to 100) */ +int gus_wave_volume = 60; +int gus_pcm_volume = 80; static unsigned char mix_image = 0x00; /* - * Current version of this_one driver doesn't allow synth and PCM functions + * Current version of this driver doesn't allow synth and PCM functions * at the same time. The active_device specifies the active driver */ static int active_device = 0; -#define GUS_DEV_WAVE 1 /* Wave table synth */ -#define GUS_DEV_PCM_DONE 2 /* PCM device, transfer done */ -#define GUS_DEV_PCM_CONTINUE 3 /* PCM device, transfer the second - * chn */ +#define GUS_DEV_WAVE 1 /* + * * * Wave table synth */ +#define GUS_DEV_PCM_DONE 2 /* + * * * PCM device, transfer done */ +#define GUS_DEV_PCM_CONTINUE 3 /* + * * * PCM device, transfer the + * second * * * chn */ static int gus_sampling_speed; static int gus_sampling_channels; @@ -105,13 +118,34 @@ DEFINE_WAIT_QUEUE (dram_sleeper, dram_sleep_flag); /* * Variables and buffers for PCM output */ -#define MAX_PCM_BUFFERS 32 /* Don't change */ -static int pcm_bsize, /* Current blocksize */ - pcm_nblk, /* Current # of blocks */ - pcm_banksize; /* # bytes allocated for channels */ -static int pcm_datasize[MAX_PCM_BUFFERS]; /* Actual # of bytes in blk */ -static volatile int pcm_head, pcm_tail, pcm_qlen; /* DRAM queue */ +#define MAX_PCM_BUFFERS (32*MAX_REALTIME_FACTOR) /* + * * * Don't + * * * change + * + */ + +static int pcm_bsize, /* + * Current blocksize + */ + pcm_nblk, /* + * Current # of blocks + */ + pcm_banksize; /* + + + * * * * # bytes allocated for channels */ +static int pcm_datasize[MAX_PCM_BUFFERS]; /* + + + * * * * Actual # of bytes + * in blk * */ +static volatile int pcm_head, pcm_tail, pcm_qlen; /* + + + * * * * DRAM queue + * */ static volatile int pcm_active; +static int pcm_opened = 0; static int pcm_current_dev; static int pcm_current_block; static unsigned long pcm_current_buf; @@ -122,28 +156,66 @@ struct voice_info voices[32]; static int freq_div_table[] = { - 44100, /* 14 */ - 41160, /* 15 */ - 38587, /* 16 */ - 36317, /* 17 */ - 34300, /* 18 */ - 32494, /* 19 */ - 30870, /* 20 */ - 29400, /* 21 */ - 28063, /* 22 */ - 26843, /* 23 */ - 25725, /* 24 */ - 24696, /* 25 */ - 23746, /* 26 */ - 22866, /* 27 */ - 22050, /* 28 */ - 21289, /* 29 */ - 20580, /* 30 */ - 19916, /* 31 */ - 19293 /* 32 */ + 44100, /* + * 14 + */ + 41160, /* + * 15 + */ + 38587, /* + * 16 + */ + 36317, /* + * 17 + */ + 34300, /* + * 18 + */ + 32494, /* + * 19 + */ + 30870, /* + * 20 + */ + 29400, /* + * 21 + */ + 28063, /* + * 22 + */ + 26843, /* + * 23 + */ + 25725, /* + * 24 + */ + 24696, /* + * 25 + */ + 23746, /* + * 26 + */ + 22866, /* + * 27 + */ + 22050, /* + * 28 + */ + 21289, /* + * 29 + */ + 20580, /* + * 30 + */ + 19916, /* + * 31 + */ + 19293 /* + * 32 + */ }; -static struct patch_info samples[MAX_SAMPLE + 1]; +static struct patch_info *samples; static long sample_ptrs[MAX_SAMPLE + 1]; static int sample_map[32]; static int free_sample; @@ -158,10 +230,15 @@ static struct synth_info gus_info = static void gus_poke (long addr, unsigned char data); static void compute_and_set_volume (int voice, int volume, int ramp_time); extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev); +extern unsigned short gus_linear_vol (int vol, int mainvol); static void compute_volume (int voice, int volume); +static void do_volume_irq (int voice); +static void set_input_volumes (void); -#define INSTANT_RAMP -1 /* Dont use ramping */ -#define FAST_RAMP 0 /* Fastest possible ramp */ +#define INSTANT_RAMP -1 /* + * * * Dont use ramping */ +#define FAST_RAMP 0 /* + * * * Fastest possible ramp */ static void reset_sample_memory (void) @@ -175,7 +252,9 @@ reset_sample_memory (void) for (i = 0; i < 32; i++) patch_map[i] = -1; - gus_poke (0, 0); /* Put silence here */ + gus_poke (0, 0); /* + * Put silence here + */ gus_poke (1, 0); free_mem_ptr = 2; @@ -230,14 +309,14 @@ gus_peek (long addr) } void -gus_write8 (int reg, unsigned char data) +gus_write8 (int reg, unsigned int data) { unsigned long flags; DISABLE_INTR (flags); OUTB (reg, u_Command); - OUTB (data, u_DataHi); + OUTB ((unsigned char) (data & 0xff), u_DataHi); RESTORE_INTR (flags); } @@ -271,7 +350,7 @@ gus_look8 (int reg) } void -gus_write16 (int reg, unsigned short data) +gus_write16 (int reg, unsigned int data) { unsigned long flags; @@ -279,8 +358,8 @@ gus_write16 (int reg, unsigned short data) OUTB (reg, u_Command); - OUTB (data & 0xff, u_DataLo); - OUTB ((data >> 8) & 0xff, u_DataHi); + OUTB ((unsigned char) (data & 0xff), u_DataLo); + OUTB ((unsigned char) ((data >> 8) & 0xff), u_DataHi); RESTORE_INTR (flags); } @@ -322,6 +401,10 @@ gus_write_addr (int reg, unsigned long address, int is16bit) gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff)); gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff)); + /* Could writing twice fix problems with GUS_VOICE_POS() ? Lets try... */ + gus_delay (); + gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff)); + gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff)); } static void @@ -347,11 +430,11 @@ gus_select_max_voices (int nvoices) } static void -gus_voice_on (unsigned char mode) +gus_voice_on (unsigned int mode) { - gus_write8 (0x00, mode & 0xfc); + gus_write8 (0x00, (unsigned char) (mode & 0xfc)); gus_delay (); - gus_write8 (0x00, mode & 0xfc); + gus_write8 (0x00, (unsigned char) (mode & 0xfc)); } static void @@ -361,10 +444,18 @@ gus_voice_off (void) } static void -gus_voice_mode (unsigned char mode) -{ - gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); /* Don't start or stop - * voice */ +gus_voice_mode (unsigned int m) +{ + unsigned char mode = (unsigned char) (m & 0xff); + + gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); /* + * Don't + * start + * or + * stop + * * + * voice + */ gus_delay (); gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); } @@ -382,44 +473,56 @@ gus_voice_freq (unsigned long freq) } static void -gus_voice_volume (unsigned short vol) +gus_voice_volume (unsigned int vol) { - gus_write8 (0x0d, 0x03); /* Stop ramp before setting volume */ - gus_write16 (0x09, vol << 4); + gus_write8 (0x0d, 0x03); /* + * Stop ramp before setting volume + */ + gus_write16 (0x09, (unsigned short) (vol << 4)); } static void -gus_voice_balance (unsigned char balance) +gus_voice_balance (unsigned int balance) { - gus_write8 (0x0c, balance); + gus_write8 (0x0c, (unsigned char) (balance & 0xff)); } static void -gus_ramp_range (unsigned short low, unsigned short high) +gus_ramp_range (unsigned int low, unsigned int high) { - gus_write8 (0x07, (low >> 4) & 0xff); - gus_write8 (0x08, (high >> 4) & 0xff); + gus_write8 (0x07, (unsigned char) ((low >> 4) & 0xff)); + gus_write8 (0x08, (unsigned char) ((high >> 4) & 0xff)); } static void -gus_ramp_rate (unsigned char scale, unsigned char rate) +gus_ramp_rate (unsigned int scale, unsigned int rate) { - gus_write8 (0x06, ((scale & 0x03) << 6) | (rate & 0x3f)); + gus_write8 (0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f))); } static void -gus_rampon (unsigned char mode) +gus_rampon (unsigned int m) { + unsigned char mode = (unsigned char) (m & 0xff); + gus_write8 (0x0d, mode & 0xfc); gus_delay (); gus_write8 (0x0d, mode & 0xfc); } static void -gus_ramp_mode (unsigned char mode) -{ - gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); /* Don't start or stop - * ramping */ +gus_ramp_mode (unsigned int m) +{ + unsigned char mode = (unsigned char) (m & 0xff); + + gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); /* + * Don't + * start + * or + * stop + * * + * ramping + */ gus_delay (); gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); } @@ -431,6 +534,20 @@ gus_rampoff (void) } static void +gus_set_voice_pos (int voice, long position) +{ + int sample_no; + + if ((sample_no = sample_map[voice]) != -1) + if (position < samples[sample_no].len) + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + voices[voice].offset_pending = position; + else + gus_write_addr (0x0a, sample_ptrs[sample_no] + position, + samples[sample_no].mode & WAVE_16_BITS); +} + +static void gus_voice_init (int voice) { unsigned long flags; @@ -438,11 +555,22 @@ gus_voice_init (int voice) DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_volume (0); - gus_write_addr (0x0a, 0, 0); /* Set current position to 0 */ - gus_write8 (0x00, 0x03); /* Voice off */ - gus_write8 (0x0d, 0x03); /* Ramping off */ + gus_write_addr (0x0a, 0, 0); /* + * Set current position to 0 + */ + gus_write8 (0x00, 0x03); /* + * Voice off + */ + gus_write8 (0x0d, 0x03); /* + * Ramping off + */ RESTORE_INTR (flags); +} + +static void +gus_voice_init2 (int voice) +{ voices[voice].panning = 0; voices[voice].mode = 0; voices[voice].orig_freq = 20000; @@ -459,6 +587,7 @@ gus_voice_init (int voice) voices[voice].main_vol = 127; voices[voice].patch_vol = 127; voices[voice].expression_vol = 127; + voices[voice].sample_pending = -1; } static void @@ -466,11 +595,17 @@ step_envelope (int voice) { unsigned vol, prev_vol, phase; unsigned char rate; + long int flags; if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) { + DISABLE_INTR (flags); + gus_select_voice (voice); gus_rampoff (); - return; /* Sustain */ + RESTORE_INTR (flags); + return; /* + * Sustain + */ } if (voices[voice].env_phase >= 5) @@ -484,20 +619,31 @@ step_envelope (int voice) } prev_vol = voices[voice].current_volume; - gus_voice_volume (prev_vol); phase = ++voices[voice].env_phase; - compute_volume (voice, voices[voice].midi_volume); - vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255; rate = voices[voice].env_rate[phase]; - gus_write8 (0x06, rate); /* Ramping rate */ + + DISABLE_INTR (flags); + gus_select_voice (voice); + + gus_voice_volume (prev_vol); + + + gus_write8 (0x06, rate); /* + * Ramping rate + */ voices[voice].volume_irq_mode = VMODE_ENVELOPE; - if (((vol - prev_vol) / 64) == 0) /* No significant volume change */ + if (((vol - prev_vol) / 64) == 0) /* + * No significant volume change + */ { - step_envelope (voice); /* Continue with the next phase */ + RESTORE_INTR (flags); + step_envelope (voice); /* + * Continue with the next phase + */ return; } @@ -506,16 +652,21 @@ step_envelope (int voice) if (vol >= (4096 - 64)) vol = 4096 - 65; gus_ramp_range (0, vol); - gus_rampon (0x20); /* Increasing, irq */ + gus_rampon (0x20); /* + * Increasing, irq + */ } else { if (vol <= 64) vol = 65; - gus_ramp_range (vol, 4095); - gus_rampon (0x60); /* Decreasing, irq */ + gus_ramp_range (vol, 4030); + gus_rampon (0x60); /* + * Decreasing, irq + */ } voices[voice].current_volume = vol; + RESTORE_INTR (flags); } static void @@ -528,19 +679,26 @@ init_envelope (int voice) } static void -start_release (int voice) +start_release (int voice, long int flags) { if (gus_read8 (0x00) & 0x03) - return; /* Voice already stopped */ + return; /* + * Voice already stopped + */ - voices[voice].env_phase = 2; /* Will be incremented by step_envelope */ + voices[voice].env_phase = 2; /* + * Will be incremented by step_envelope + */ voices[voice].current_volume = voices[voice].initial_volume = - gus_read16 (0x09) >> 4; /* Get current volume */ + gus_read16 (0x09) >> 4; /* + * Get current volume + */ voices[voice].mode &= ~WAVE_SUSTAIN_ON; gus_rampoff (); + RESTORE_INTR (flags); step_envelope (voice); } @@ -548,25 +706,38 @@ static void gus_voice_fade (int voice) { int instr_no = sample_map[voice], is16bits; + long int flags; + + DISABLE_INTR (flags); + gus_select_voice (voice); if (instr_no < 0 || instr_no > MAX_SAMPLE) { - gus_write8 (0x00, 0x03); /* Hard stop */ + gus_write8 (0x00, 0x03); /* + * Hard stop + */ + RESTORE_INTR (flags); return; } - is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bit samples */ + is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* + * 8 or 16 + * bit + * samples + */ if (voices[voice].mode & WAVE_ENVELOPES) { - start_release (voice); + start_release (voice, flags); return; } /* * Ramp the volume down but not too quickly. */ - if ((gus_read16 (0x09) >> 4) < 100) /* Get current volume */ + if ((gus_read16 (0x09) >> 4) < 100) /* + * Get current volume + */ { gus_voice_off (); gus_rampoff (); @@ -574,10 +745,13 @@ gus_voice_fade (int voice) return; } - gus_ramp_range (65, 4095); + gus_ramp_range (65, 4030); gus_ramp_rate (2, 4); - gus_rampon (0x40 | 0x20); /* Down, once, irq */ + gus_rampon (0x40 | 0x20); /* + * Down, once, irq + */ voices[voice].volume_irq_mode = VMODE_HALT; + RESTORE_INTR (flags); } static void @@ -592,14 +766,25 @@ gus_reset (void) for (i = 0; i < 32; i++) { - gus_voice_init (i); /* Turn voice off */ + gus_voice_init (i); /* + * Turn voice off + */ + gus_voice_init2 (i); } - INB (u_Status); /* Touch the status register */ + INB (u_Status); /* + * Touch the status register + */ - gus_look8 (0x41); /* Clear any pending DMA IRQs */ - gus_look8 (0x49); /* Clear any pending sample IRQs */ - gus_read8 (0x0f); /* Clear pending IRQs */ + gus_look8 (0x41); /* + * Clear any pending DMA IRQs + */ + gus_look8 (0x49); /* + * Clear any pending sample IRQs + */ + gus_read8 (0x0f); /* + * Clear pending IRQs + */ } @@ -607,7 +792,7 @@ static void gus_initialize (void) { unsigned long flags; - unsigned char dma_image, irq_image, tmp; + register unsigned char dma_image, irq_image, tmp; static unsigned char gus_irq_map[16] = {0, 0, 1, 3, 0, 2, 0, 4, 0, 0, 0, 5, 6, 0, 0, 7}; @@ -617,11 +802,15 @@ gus_initialize (void) DISABLE_INTR (flags); - gus_write8 (0x4c, 0); /* Reset GF1 */ + gus_write8 (0x4c, 0); /* + * Reset GF1 + */ gus_delay (); gus_delay (); - gus_write8 (0x4c, 1); /* Release Reset */ + gus_write8 (0x4c, 1); /* + * Release Reset + */ gus_delay (); gus_delay (); @@ -629,25 +818,49 @@ gus_initialize (void) * Clear all interrupts */ - gus_write8 (0x41, 0); /* DMA control */ - gus_write8 (0x45, 0); /* Timer control */ - gus_write8 (0x49, 0); /* Sample control */ + gus_write8 (0x41, 0); /* + * DMA control + */ + gus_write8 (0x45, 0); /* + * Timer control + */ + gus_write8 (0x49, 0); /* + * Sample control + */ gus_select_max_voices (24); - INB (u_Status); /* Touch the status register */ - - gus_look8 (0x41); /* Clear any pending DMA IRQs */ - gus_look8 (0x49); /* Clear any pending sample IRQs */ - gus_read8 (0x0f); /* Clear pending IRQs */ - - gus_reset (); /* Resets all voices */ - - gus_look8 (0x41); /* Clear any pending DMA IRQs */ - gus_look8 (0x49); /* Clear any pending sample IRQs */ - gus_read8 (0x0f); /* Clear pending IRQs */ - - gus_write8 (0x4c, 7); /* Master reset | DAC enable | IRQ enable */ + INB (u_Status); /* + * Touch the status register + */ + + gus_look8 (0x41); /* + * Clear any pending DMA IRQs + */ + gus_look8 (0x49); /* + * Clear any pending sample IRQs + */ + gus_read8 (0x0f); /* + * Clear pending IRQs + */ + + gus_reset (); /* + * Resets all voices + */ + + gus_look8 (0x41); /* + * Clear any pending DMA IRQs + */ + gus_look8 (0x49); /* + * Clear any pending sample IRQs + */ + gus_read8 (0x0f); /* + * Clear pending IRQs + */ + + gus_write8 (0x4c, 7); /* + * Master reset | DAC enable | IRQ enable + */ /* * Set up for Digital ASIC @@ -655,7 +868,9 @@ gus_initialize (void) OUTB (0x05, gus_base + 0x0f); - mix_image |= 0x02; /* Disable line out */ + mix_image |= 0x02; /* + * Disable line out + */ OUTB (mix_image, u_Mixer); OUTB (0x00, u_IRQDMAControl); @@ -664,12 +879,9 @@ gus_initialize (void) /* * Now set up the DMA and IRQ interface - * + * * The GUS supports two IRQs and two DMAs. - * - * If GUS_MIDI_IRQ is defined and if it's != GUS_IRQ, separate Midi IRQ is set - * up. Otherwise the same IRQ is shared by the both devices. - * + * * Just one DMA channel is used. This prevents simultaneous ADC and DAC. * Adding this support requires significant changes to the dmabuf.c, dsp.c * and audio.c also. @@ -680,23 +892,13 @@ gus_initialize (void) if (!tmp) printk ("Warning! GUS IRQ not selected\n"); irq_image |= tmp; + irq_image |= 0x40; /* + * Combine IRQ1 (GF1) and IRQ2 (Midi) + */ - if (GUS_MIDI_IRQ != gus_irq) - { /* The midi irq was defined and != wave irq */ - tmp = gus_irq_map[GUS_MIDI_IRQ]; - tmp <<= 3; - - if (!tmp) - printk ("Warning! GUS Midi IRQ not selected\n"); - else - gus_set_midi_irq (GUS_MIDI_IRQ); - - irq_image |= tmp; - } - else - irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */ - - dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */ + dma_image = 0x40; /* + * Combine DMA1 (DRAM) and IRQ2 (ADC) + */ tmp = gus_dma_map[gus_dma]; if (!tmp) printk ("Warning! GUS DMA not selected\n"); @@ -706,37 +908,73 @@ gus_initialize (void) * For some reason the IRQ and DMA addresses must be written twice */ - /* Doing it first time */ - - OUTB (mix_image, u_Mixer); /* Select DMA control */ - OUTB (dma_image, u_IRQDMAControl); /* Set DMA address */ - - OUTB (mix_image | 0x40, u_Mixer); /* Select IRQ control */ - OUTB (irq_image, u_IRQDMAControl); /* Set IRQ address */ - - /* Doing it second time */ - - OUTB (mix_image, u_Mixer); /* Select DMA control */ - OUTB (dma_image, u_IRQDMAControl); /* Set DMA address */ - - OUTB (mix_image | 0x40, u_Mixer); /* Select IRQ control */ - OUTB (irq_image, u_IRQDMAControl); /* Set IRQ address */ + /* + * Doing it first time + */ - gus_select_voice (0); /* This disables writes to IRQ/DMA reg */ + OUTB (mix_image, u_Mixer); /* + * Select DMA control + */ + OUTB (dma_image | 0x80, u_IRQDMAControl); /* + * Set DMA address + */ - mix_image &= ~0x02; /* Enable line out */ - mix_image |= 0x08; /* Enable IRQ */ - OUTB (mix_image, u_Mixer); /* Turn mixer channels on */ + OUTB (mix_image | 0x40, u_Mixer); /* + * Select IRQ control + */ + OUTB (irq_image, u_IRQDMAControl); /* + * Set IRQ address + */ - gus_select_voice (0); /* This disables writes to IRQ/DMA reg */ + /* + * Doing it second time + */ - gusintr (0); /* Serve pending interrupts */ + OUTB (mix_image, u_Mixer); /* + * Select DMA control + */ + OUTB (dma_image, u_IRQDMAControl); /* + * Set DMA address + */ + + OUTB (mix_image | 0x40, u_Mixer); /* + * Select IRQ control + */ + OUTB (irq_image, u_IRQDMAControl); /* + * Set IRQ address + */ + + gus_select_voice (0); /* + * This disables writes to IRQ/DMA reg + */ + + mix_image &= ~0x02; /* + * Enable line out + */ + mix_image |= 0x08; /* + * Enable IRQ + */ + OUTB (mix_image, u_Mixer); /* + * Turn mixer channels on + * Note! Mic in is left off. + */ + + gus_select_voice (0); /* + * This disables writes to IRQ/DMA reg + */ + + gusintr (0); /* + * Serve pending interrupts + */ RESTORE_INTR (flags); } int gus_wave_detect (int baseaddr) { + unsigned long i; + unsigned long loc; + gus_base = baseaddr; gus_write8 (0x4c, 0); /* Reset GF1 */ @@ -747,31 +985,37 @@ gus_wave_detect (int baseaddr) gus_delay (); gus_delay (); - gus_poke (0x000, 0xaa); - gus_poke (0x100, 0x55); + /* See if there is first block there.... */ + gus_poke (0L, 0xaa); + if (gus_peek (0L) != 0xaa) + return (0); - if (gus_peek (0x000) != 0xaa) - return 0; - if (gus_peek (0x100) != 0x55) - return 0; - - gus_mem_size = 0x40000; /* 256k */ - gus_poke (0x40000, 0xaa); - if (gus_peek (0x40000) != 0xaa) - return 1; + /* Now zero it out so that I can check for mirroring .. */ + gus_poke (0L, 0x00); + for (i = 1L; i < 1024L; i++) + { + int n, failed; - gus_mem_size = 0x80000; /* 512k */ - gus_poke (0x80000, 0xaa); - if (gus_peek (0x80000) != 0xaa) - return 1; + /* check for mirroring ... */ + if (gus_peek (0L) != 0) + break; + loc = i << 10; - gus_mem_size = 0xc0000; /* 768k */ - gus_poke (0xc0000, 0xaa); - if (gus_peek (0xc0000) != 0xaa) - return 1; + for (n = loc - 1, failed = 0; n <= loc; n++) + { + gus_poke (loc, 0xaa); + if (gus_peek (loc) != 0xaa) + failed = 1; - gus_mem_size = 0x100000; /* 1M */ + gus_poke (loc, 0x55); + if (gus_peek (loc) != 0x55) + failed = 1; + } + if (failed) + break; + } + gus_mem_size = i << 10; return 1; } @@ -816,16 +1060,26 @@ guswave_set_instr (int dev, int voice, int instr_no) if (voice < 0 || voice > 31) return RET_ERROR (EINVAL); + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].sample_pending = instr_no; + return 0; + } + sample_no = patch_table[instr_no]; patch_map[voice] = -1; if (sample_no < 0) { printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice); - return RET_ERROR (EINVAL);/* Patch not defined */ + return RET_ERROR (EINVAL);/* + * Patch not defined + */ } - if (sample_ptrs[sample_no] == -1) /* Sample not loaded */ + if (sample_ptrs[sample_no] == -1) /* + * Sample not loaded + */ { printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice); return RET_ERROR (EINVAL); @@ -837,14 +1091,25 @@ guswave_set_instr (int dev, int voice, int instr_no) } static int +#ifdef FUTURE_VERSION +guswave_kill_note (int dev, int voice, int note, int velocity) +#else guswave_kill_note (int dev, int voice, int velocity) +#endif { unsigned long flags; DISABLE_INTR (flags); - gus_select_voice (voice); - gus_voice_fade (voice); - RESTORE_INTR (flags); + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].kill_pending = 1; + RESTORE_INTR (flags); + } + else + { + RESTORE_INTR (flags); + gus_voice_fade (voice); + } return 0; } @@ -855,20 +1120,26 @@ guswave_aftertouch (int dev, int voice, int pressure) short lo_limit, hi_limit; unsigned long flags; - return; /* Currently disabled */ + return; /* + * Currently disabled + */ if (voice < 0 || voice > 31) return; if (voices[voice].mode & WAVE_ENVELOPES && voices[voice].env_phase != 2) - return; /* Don't mix with envelopes */ + return; /* + * Don't mix with envelopes + */ if (pressure < 32) { DISABLE_INTR (flags); gus_select_voice (voice); gus_rampoff (); - compute_and_set_volume (voice, 255, 0); /* Back to original volume */ + compute_and_set_volume (voice, 255, 0); /* + * Back to original volume + */ RESTORE_INTR (flags); return; } @@ -887,7 +1158,9 @@ guswave_aftertouch (int dev, int voice, int pressure) } gus_ramp_range (lo_limit, hi_limit); gus_ramp_rate (3, 8); - gus_rampon (0x58); /* Bidirectional, Down, Loop */ + gus_rampon (0x58); /* + * Bidirectional, Down, Loop + */ RESTORE_INTR (flags); } @@ -899,38 +1172,57 @@ guswave_panning (int dev, int voice, int value) } static void +guswave_volume_method (int dev, int mode) +{ + if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO) + volume_method = mode; +} + +static void compute_volume (int voice, int volume) { if (volume < 128) + voices[voice].midi_volume = volume; + + switch (volume_method) { - voices[voice].midi_volume = volume; + case VOL_METHOD_ADAGIO: + voices[voice].initial_volume = + gus_adagio_vol (voices[voice].midi_volume, voices[voice].main_vol, + voices[voice].expression_vol, + voices[voice].patch_vol); + break; - switch (volume_method) - { - case VOL_METHOD_ADAGIO: - voices[voice].initial_volume = - gus_adagio_vol (volume, voices[voice].main_vol, - voices[voice].expression_vol, - voices[voice].patch_vol); - break; + case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */ + voices[voice].initial_volume = + gus_linear_vol (volume, voices[voice].main_vol); + break; - default: - voices[voice].initial_volume = volume_base + (volume * volume_scale); - } + default: + voices[voice].initial_volume = volume_base + + (voices[voice].midi_volume * volume_scale); } - if (voices[voice].initial_volume > 4095) - voices[voice].initial_volume = 4095; + if (voices[voice].initial_volume > 4030) + voices[voice].initial_volume = 4030; } static void compute_and_set_volume (int voice, int volume, int ramp_time) { int current, target, rate; + unsigned long flags; compute_volume (voice, volume); voices[voice].current_volume = voices[voice].initial_volume; + DISABLE_INTR (flags); + /* + * CAUTION! Interrupts disabled. Enable them before returning + */ + + gus_select_voice (voice); + current = gus_read16 (0x09) >> 4; target = voices[voice].initial_volume; @@ -938,6 +1230,7 @@ compute_and_set_volume (int voice, int volume, int ramp_time) { gus_rampoff (); gus_voice_volume (target); + RESTORE_INTR (flags); return; } @@ -947,10 +1240,13 @@ compute_and_set_volume (int voice, int volume, int ramp_time) rate = 16; gus_ramp_rate (0, rate); - if ((target - current) / 64 == 0) /* Too close */ + if ((target - current) / 64 == 0) /* + * Too close + */ { gus_rampoff (); gus_voice_volume (target); + RESTORE_INTR (flags); return; } @@ -959,7 +1255,9 @@ compute_and_set_volume (int voice, int volume, int ramp_time) if (target > (4095 - 65)) target = 4095 - 65; gus_ramp_range (current, target); - gus_rampon (0x00); /* Ramp up, once, no irq */ + gus_rampon (0x00); /* + * Ramp up, once, no irq + */ } else { @@ -967,8 +1265,11 @@ compute_and_set_volume (int voice, int volume, int ramp_time) target = 65; gus_ramp_range (target, current); - gus_rampon (0x40); /* Ramp down, once, no irq */ + gus_rampon (0x40); /* + * Ramp down, once, no irq + */ } + RESTORE_INTR (flags); } static void @@ -979,11 +1280,15 @@ dynamic_volume_change (int voice) DISABLE_INTR (flags); gus_select_voice (voice); - status = gus_read8 (0x00); /* Voice status */ + status = gus_read8 (0x00); /* + * Voice status + */ RESTORE_INTR (flags); if (status & 0x03) - return; /* Voice not started */ + return; /* + * Voice not started + */ if (!(voices[voice].mode & WAVE_ENVELOPES)) { @@ -997,10 +1302,14 @@ dynamic_volume_change (int voice) DISABLE_INTR (flags); gus_select_voice (voice); - status = gus_read8 (0x0d); /* Ramping status */ + status = gus_read8 (0x0d); /* + * Ramping status + */ RESTORE_INTR (flags); - if (status & 0x03) /* Sustain phase? */ + if (status & 0x03) /* + * Sustain phase? + */ { compute_and_set_volume (voice, voices[voice].midi_volume, 1); return; @@ -1011,9 +1320,12 @@ dynamic_volume_change (int voice) compute_volume (voice, voices[voice].midi_volume); -#if 0 /* Is this really required */ +#if 0 /* + * * * Is this really required */ voices[voice].current_volume = - gus_read16 (0x09) >> 4; /* Get current volume */ + gus_read16 (0x09) >> 4; /* + * Get current volume + */ voices[voice].env_phase--; step_envelope (voice); @@ -1033,38 +1345,59 @@ guswave_controller (int dev, int voice, int ctrl_num, int value) { case CTRL_PITCH_BENDER: voices[voice].bender = value; - freq = compute_finetune (voices[voice].orig_freq, value, voices[voice].bender_range); - voices[voice].current_freq = freq; - DISABLE_INTR (flags); - gus_select_voice (voice); - gus_voice_freq (freq); - RESTORE_INTR (flags); + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + { + freq = compute_finetune (voices[voice].orig_freq, value, voices[voice].bender_range); + voices[voice].current_freq = freq; + + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_freq (freq); + RESTORE_INTR (flags); + } break; case CTRL_PITCH_BENDER_RANGE: voices[voice].bender_range = value; break; - +#ifdef FUTURE_VERSION + case CTL_EXPRESSION: + value /= 128; +#endif case CTRL_EXPRESSION: - volume_method = VOL_METHOD_ADAGIO; - voices[voice].expression_vol = value; - dynamic_volume_change (voice); + if (volume_method == VOL_METHOD_ADAGIO) + { + voices[voice].expression_vol = value; + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + dynamic_volume_change (voice); + } break; +#ifdef FUTURE_VERSION + case CTL_PAN: + voices[voice].panning = (value * 2) - 128; + break; + + case CTL_MAIN_VOLUME: + value = (value * 100) / 16383; +#endif + case CTRL_MAIN_VOLUME: - volume_method = VOL_METHOD_ADAGIO; voices[voice].main_vol = value; - dynamic_volume_change (voice); + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + dynamic_volume_change (voice); break; - default: /* Ignore */ + default: /* + * Ignore + */ break; } } static int -guswave_start_note (int dev, int voice, int note_num, int volume) +guswave_start_note2 (int dev, int voice, int note_num, int volume) { int sample, best_sample, best_delta, delta_freq; int is16bits, samplep, patch, pan; @@ -1123,7 +1456,9 @@ guswave_start_note (int dev, int voice, int note_num, int volume) if (samples[samplep].low_note <= note_freq && note_freq <= samples[samplep].high_note) sample = samplep; else - samplep = samples[samplep].key; /* Follow link */ + samplep = samples[samplep].key; /* + * Follow link + */ } if (sample == -1) sample = best_sample; @@ -1131,10 +1466,16 @@ guswave_start_note (int dev, int voice, int note_num, int volume) if (sample == -1) { printk ("GUS: Patch %d not defined for note %d\n", patch, note_num); - return 0; /* Should play default patch ??? */ + return 0; /* + * Should play default patch ??? + */ } - is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bit samples */ + is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; /* + * 8 or 16 + * bit + * samples + */ voices[voice].mode = samples[sample].mode; voices[voice].patch_vol = samples[sample].volume; @@ -1151,7 +1492,9 @@ guswave_start_note (int dev, int voice, int note_num, int volume) sample_map[voice] = sample; - base_note = samples[sample].base_note / 100; /* To avoid overflows */ + base_note = samples[sample].base_note / 100; /* + * To avoid overflows + */ note_freq /= 100; freq = samples[sample].base_freq * note_freq / base_note; @@ -1175,20 +1518,27 @@ guswave_start_note (int dev, int voice, int note_num, int volume) if (samples[sample].mode & WAVE_16_BITS) { - mode |= 0x04; /* 16 bits */ + mode |= 0x04; /* + * 16 bits + */ if ((sample_ptrs[sample] >> 18) != ((sample_ptrs[sample] + samples[sample].len) >> 18)) printk ("GUS: Sample address error\n"); } /************************************************************************* - * CAUTION! Interrupts disabled. Don't return before enabling - *************************************************************************/ + * CAUTION! Interrupts disabled. Don't return before enabling + *************************************************************************/ DISABLE_INTR (flags); gus_select_voice (voice); - gus_voice_off (); /* It may still be running */ + gus_voice_off (); /* + * It may still be running + */ gus_rampoff (); + + RESTORE_INTR (flags); + if (voices[voice].mode & WAVE_ENVELOPES) { compute_volume (voice, volume); @@ -1197,36 +1547,66 @@ guswave_start_note (int dev, int voice, int note_num, int volume) else compute_and_set_volume (voice, volume, 0); + DISABLE_INTR (flags); + gus_select_voice (voice); + if (samples[sample].mode & WAVE_LOOP_BACK) - gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len, is16bits); /* Sample start=end */ + gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len - + voices[voice].offset_pending, is16bits); /* Sample + * start=end */ else - gus_write_addr (0x0a, sample_ptrs[sample], is16bits); /* Sample start=begin */ + gus_write_addr (0x0a, sample_ptrs[sample] + voices[voice].offset_pending, + is16bits); /* Sample start=begin */ if (samples[sample].mode & WAVE_LOOPING) { - mode |= 0x08; /* Looping on */ + mode |= 0x08; /* + * Looping on + */ if (samples[sample].mode & WAVE_BIDIR_LOOP) - mode |= 0x10; /* Bidirectional looping on */ + mode |= 0x10; /* + * Bidirectional looping on + */ if (samples[sample].mode & WAVE_LOOP_BACK) { - gus_write_addr (0x0a, /* Put the current location = loop_end */ - sample_ptrs[sample] + samples[sample].loop_end, is16bits); - mode |= 0x40; /* Loop backwards */ + gus_write_addr (0x0a, + sample_ptrs[sample] + samples[sample].loop_end - + voices[voice].offset_pending, is16bits); + mode |= 0x40; } - gus_write_addr (0x02, sample_ptrs[sample] + samples[sample].loop_start, is16bits); /* Loop start location */ - gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].loop_end, is16bits); /* Loop end location */ + gus_write_addr (0x02, sample_ptrs[sample] + samples[sample].loop_start, is16bits); /* + * Loop + * start + * location + */ + gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].loop_end, is16bits); /* + * Loop + * end + * location + */ } else { - mode |= 0x20; /* Loop irq at the end */ - voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp it down at the - * end */ + mode |= 0x20; /* + * Loop irq at the end + */ + voices[voice].loop_irq_mode = LMODE_FINISH; /* + * Ramp it down at + * the * end + */ voices[voice].loop_irq_parm = 1; - gus_write_addr (0x02, sample_ptrs[sample], is16bits); /* Loop start location */ - gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].len, is16bits); /* Loop end location */ + gus_write_addr (0x02, sample_ptrs[sample], is16bits); /* + * Loop start + * location + */ + gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].len - 1, is16bits); /* + * Loop + * end + * location + */ } gus_voice_freq (freq); gus_voice_balance (pan); @@ -1236,13 +1616,81 @@ guswave_start_note (int dev, int voice, int note_num, int volume) return 0; } +/* + * * New guswave_start_note by Andrew J. Robinson attempts to minimize + * clicking * when the note playing on the voice is changed. It uses volume + * ramping. */ + +static int +guswave_start_note (int dev, int voice, int note_num, int volume) +{ + long int flags; + int mode; + int ret_val = 0; + + DISABLE_INTR (flags); + if (note_num == 255) + { + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + voices[voice].volume_pending = volume; + else + { + RESTORE_INTR (flags); + ret_val = guswave_start_note2 (dev, voice, note_num, volume); + } + } + else + { + gus_select_voice (voice); + mode = gus_read8 (0x00); + if (mode & 0x20) + gus_write8 (0x00, mode & 0xdf); /* No interrupt! */ + + voices[voice].offset_pending = 0; + voices[voice].kill_pending = 0; + voices[voice].volume_irq_mode = 0; + voices[voice].loop_irq_mode = 0; + + if (voices[voice].sample_pending >= 0) + { + RESTORE_INTR (flags); + guswave_set_instr (voices[voice].dev_pending, voice, + voices[voice].sample_pending); + voices[voice].sample_pending = -1; + DISABLE_INTR (flags); + } + + if ((mode & 0x01) || ((gus_read16 (0x09) >> 4) < 2065)) + { + ret_val = guswave_start_note2 (dev, voice, note_num, volume); + } + else + { + voices[voice].dev_pending = dev; + voices[voice].note_pending = note_num; + voices[voice].volume_pending = volume; + voices[voice].volume_irq_mode = VMODE_START_NOTE; + + gus_rampoff (); + gus_ramp_range (2000, 4065); + gus_ramp_rate (0, 63);/* Fastest possible rate */ + gus_rampon (0x20 | 0x40); /* Ramp down, once, irq */ + RESTORE_INTR (flags); + } + } + return ret_val; +} + static void guswave_reset (int dev) { int i; for (i = 0; i < 32; i++) - gus_voice_init (i); + { + gus_voice_init (i); + gus_voice_init2 (i); + } } static int @@ -1253,9 +1701,12 @@ guswave_open (int dev, int mode) if (gus_busy) return RET_ERROR (EBUSY); + gus_initialize (); + if ((err = DMAbuf_open_dma (gus_devnum))) return err; + RESET_WAIT_QUEUE (dram_sleeper, dram_sleep_flag); gus_busy = 1; active_device = GUS_DEV_WAVE; @@ -1280,22 +1731,29 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr, { struct patch_info patch; int instr; + long sizeof_patch; unsigned long blk_size, blk_end, left, src_offs, target; + sizeof_patch = (long) &patch.data[0] - (long) &patch; /* + * Size of + * the header + * * info + */ + if (format != GUS_PATCH) { - printk ("GUS Error: Invalid patch format (key) 0x%04x\n", format); + printk ("GUS Error: Invalid patch format (key) 0x%x\n", format); return RET_ERROR (EINVAL); } - if (count < sizeof (patch)) + if (count < sizeof_patch) { printk ("GUS Error: Patch header too short\n"); return RET_ERROR (EINVAL); } - count -= sizeof (patch); + count -= sizeof_patch; if (free_sample >= MAX_SAMPLE) { @@ -1308,7 +1766,7 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr, * been transferred already. */ - COPY_FROM_USER (&((char *) &patch)[offs], addr, offs, sizeof (patch) - offs); + COPY_FROM_USER (&((char *) &patch)[offs], addr, offs, sizeof_patch - offs); instr = patch.instr_no; @@ -1321,13 +1779,13 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr, if (count < patch.len) { printk ("GUS Warning: Patch record too short (%d<%d)\n", - count, patch.len); + count, (int) patch.len); patch.len = count; } if (patch.len <= 0 || patch.len > gus_mem_size) { - printk ("GUS: Invalid sample length %d\n", patch.len); + printk ("GUS: Invalid sample length %d\n", (int) patch.len); return RET_ERROR (EINVAL); } @@ -1346,7 +1804,9 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr, } } - free_mem_ptr = (free_mem_ptr + 31) & ~31; /* Alignment 32 bytes */ + free_mem_ptr = (free_mem_ptr + 31) & ~31; /* + * Alignment 32 bytes + */ #define GUS_BANK_SIZE (256*1024) @@ -1357,20 +1817,24 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr, */ if (patch.len >= GUS_BANK_SIZE) { - printk ("GUS: Sample (16 bit) too long %d\n", patch.len); + printk ("GUS: Sample (16 bit) too long %d\n", (int) patch.len); return RET_ERROR (ENOSPC); } if ((free_mem_ptr / GUS_BANK_SIZE) != ((free_mem_ptr + patch.len) / GUS_BANK_SIZE)) { - unsigned long tmp_mem = /* Align to 256K*N */ + unsigned long tmp_mem = /* + * Align to 256K*N + */ ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE; if ((tmp_mem + patch.len) > gus_mem_size) return RET_ERROR (ENOSPC); - free_mem_ptr = tmp_mem; /* This leaves unusable memory */ + free_mem_ptr = tmp_mem; /* + * This leaves unusable memory + */ } } @@ -1379,12 +1843,14 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr, sample_ptrs[free_sample] = free_mem_ptr; - /* Tremolo is not possible with envelopes */ + /* + * Tremolo is not possible with envelopes + */ if (patch.mode & WAVE_ENVELOPES) patch.mode &= ~WAVE_TREMOLO; - memcpy ((char *) &samples[free_sample], &patch, sizeof (patch)); + memcpy ((char *) &samples[free_sample], &patch, sizeof_patch); /* * Link this_one sample to the list of samples for patch 'instr'. @@ -1401,7 +1867,9 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr, src_offs = 0; target = free_mem_ptr; - while (left) /* Not all moved */ + while (left) /* + * Not all moved + */ { blk_size = sound_buffsizes[gus_devnum]; if (blk_size > left) @@ -1413,13 +1881,15 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr, blk_end = target + blk_size; if ((target >> 18) != (blk_end >> 18)) - { /* Have to split the block */ + { /* + * Have to split the block + */ blk_end &= ~(256 * 1024 - 1); blk_size = blk_end - target; } -#ifdef GUS_NO_DMA +#if defined(GUS_NO_DMA) || defined(GUS_PATCH_NO_DMA) /* * For some reason the DMA is not possible. We have to use PIO. */ @@ -1429,24 +1899,35 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr, for (i = 0; i < blk_size; i++) { - GET_BYTE_FROM_USER (data, addr, sizeof (patch) + i); + GET_BYTE_FROM_USER (data, addr, sizeof_patch + i); + if (patch.mode & WAVE_UNSIGNED) + + if (!(patch.mode & WAVE_16_BITS) || (i & 0x01)) + data ^= 0x80; /* + * Convert to signed + */ gus_poke (target + i, data); } } -#else /* GUS_NO_DMA */ +#else /* + * * * GUS_NO_DMA */ { unsigned long address, hold_address; unsigned char dma_command; + unsigned long flags; /* * OK, move now. First in and then out. */ COPY_FROM_USER (snd_raw_buf[gus_devnum][0], - addr, sizeof (patch) + src_offs, + addr, sizeof_patch + src_offs, blk_size); - gus_write8 (0x41, 0); /* Disable GF1 DMA */ + DISABLE_INTR (flags); /******** INTERRUPTS DISABLED NOW ********/ + gus_write8 (0x41, 0); /* + * Disable GF1 DMA + */ DMAbuf_start_dma (gus_devnum, snd_raw_buf_phys[gus_devnum][0], blk_size, DMA_MODE_WRITE); @@ -1464,30 +1945,46 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr, address |= (hold_address & 0x000c0000L); } - gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ + gus_write16 (0x42, (address >> 4) & 0xffff); /* + * DRAM DMA address + */ /* * Start the DMA transfer */ - dma_command = 0x21; /* IRQ enable, DMA start */ + dma_command = 0x21; /* + * IRQ enable, DMA start + */ if (patch.mode & WAVE_UNSIGNED) - dma_command |= 0x80; /* Invert MSB */ + dma_command |= 0x80; /* + * Invert MSB + */ if (patch.mode & WAVE_16_BITS) - dma_command |= 0x40; /* 16 bit _DATA_ */ + dma_command |= 0x40; /* + * 16 bit _DATA_ + */ if (sound_dsp_dmachan[gus_devnum] > 3) - dma_command |= 0x04; /* 16 bit DMA channel */ + dma_command |= 0x04; /* + * 16 bit DMA channel + */ - gus_write8 (0x41, dma_command); /* Let's go luteet (=bugs) */ + gus_write8 (0x41, dma_command); /* + * Let's go luteet (=bugs) + */ /* * Sleep here until the DRAM DMA done interrupt is served */ active_device = GUS_DEV_WAVE; - INTERRUPTIBLE_SLEEP_ON (dram_sleeper, dram_sleep_flag); + DO_SLEEP (dram_sleeper, dram_sleep_flag, HZ); + if (TIMED_OUT (dram_sleeper, dram_sleep_flag)) + printk ("GUS: DMA Transfer timed out\n"); + RESTORE_INTR (flags); } -#endif /* GUS_NO_DMA */ +#endif /* + * * * GUS_NO_DMA */ /* * Now the next part @@ -1497,7 +1994,9 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr, src_offs += blk_size; target += blk_size; - gus_write8 (0x41, 0); /* Stop DMA */ + gus_write8 (0x41, 0); /* + * Stop DMA + */ } free_mem_ptr += patch.len; @@ -1521,6 +2020,10 @@ guswave_hw_control (int dev, unsigned char *event) p2 = *(unsigned short *) &event[6]; plong = *(unsigned long *) &event[4]; + if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) && + (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS)) + do_volume_irq (voice); + switch (cmd) { @@ -1538,7 +2041,9 @@ guswave_hw_control (int dev, unsigned char *event) case _GUS_VOICEON: DISABLE_INTR (flags); gus_select_voice (voice); - p1 &= ~0x20; /* Disable intr */ + p1 &= ~0x20; /* + * Disable intr + */ gus_voice_on (p1); RESTORE_INTR (flags); break; @@ -1551,16 +2056,15 @@ guswave_hw_control (int dev, unsigned char *event) break; case _GUS_VOICEFADE: - DISABLE_INTR (flags); - gus_select_voice (voice); gus_voice_fade (voice); - RESTORE_INTR (flags); break; case _GUS_VOICEMODE: DISABLE_INTR (flags); gus_select_voice (voice); - p1 &= ~0x20; /* Disable intr */ + p1 &= ~0x20; /* + * Disable intr + */ gus_voice_mode (p1); RESTORE_INTR (flags); break; @@ -1586,14 +2090,18 @@ guswave_hw_control (int dev, unsigned char *event) RESTORE_INTR (flags); break; - case _GUS_VOICEVOL2: /* Just update the voice value */ + case _GUS_VOICEVOL2: /* + * Just update the voice value + */ voices[voice].initial_volume = voices[voice].current_volume = p1; break; case _GUS_RAMPRANGE: if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NO-NO */ + break; /* + * NO-NO + */ DISABLE_INTR (flags); gus_select_voice (voice); gus_ramp_range (p1, p2); @@ -1602,7 +2110,9 @@ guswave_hw_control (int dev, unsigned char *event) case _GUS_RAMPRATE: if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NO-NO */ + break; /* + * NO-NO + */ DISABLE_INTR (flags); gus_select_voice (voice); gus_ramp_rate (p1, p2); @@ -1611,27 +2121,37 @@ guswave_hw_control (int dev, unsigned char *event) case _GUS_RAMPMODE: if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NO-NO */ + break; /* + * NO-NO + */ DISABLE_INTR (flags); gus_select_voice (voice); - p1 &= ~0x20; /* Disable intr */ + p1 &= ~0x20; /* + * Disable intr + */ gus_ramp_mode (p1); RESTORE_INTR (flags); break; case _GUS_RAMPON: if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NO-NO */ + break; /* + * NO-NO + */ DISABLE_INTR (flags); gus_select_voice (voice); - p1 &= ~0x20; /* Disable intr */ + p1 &= ~0x20; /* + * Disable intr + */ gus_rampon (p1); RESTORE_INTR (flags); break; case _GUS_RAMPOFF: if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NO-NO */ + break; /* + * NO-NO + */ DISABLE_INTR (flags); gus_select_voice (voice); gus_rampoff (); @@ -1643,6 +2163,13 @@ guswave_hw_control (int dev, unsigned char *event) volume_scale = p2; break; + case _GUS_VOICE_POS: + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_set_voice_pos (voice, plong); + RESTORE_INTR (flags); + break; + default:; } } @@ -1710,6 +2237,8 @@ gus_sampling_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) break; case SOUND_PCM_WRITE_CHANNELS: + if (local) + return gus_sampling_set_channels (arg); return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg))); break; @@ -1730,7 +2259,9 @@ gus_sampling_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) return gus_sampling_bits; return IOCTL_OUT (arg, gus_sampling_bits); - case SOUND_PCM_WRITE_FILTER: /* NOT YET IMPLEMENTED */ + case SOUND_PCM_WRITE_FILTER: /* + * NOT YET IMPLEMENTED + */ return IOCTL_OUT (arg, RET_ERROR (EINVAL)); break; @@ -1760,6 +2291,8 @@ gus_sampling_open (int dev, int mode) if (gus_busy) return RET_ERROR (EBUSY); + gus_initialize (); + gus_busy = 1; active_device = 0; @@ -1767,10 +2300,13 @@ gus_sampling_open (int dev, int mode) reset_sample_memory (); gus_select_max_voices (14); - gus_sampling_set_bits (8); - gus_sampling_set_channels (1); - gus_sampling_set_speed (DSP_DEFAULT_SPEED); pcm_active = 0; + pcm_opened = 1; + if (mode & OPEN_READ) + { + recording_active = 1; + set_input_volumes (); + } return 0; } @@ -1780,7 +2316,31 @@ gus_sampling_close (int dev) { gus_reset (); gus_busy = 0; + pcm_opened = 0; active_device = 0; + + if (recording_active) + set_input_volumes (); + + recording_active = 0; +} + +static void +gus_sampling_update_volume (void) +{ + unsigned long flags; + int voice; + + DISABLE_INTR (flags); + if (pcm_active && pcm_opened) + for (voice = 0; voice < gus_sampling_channels; voice++) + { + gus_select_voice (voice); + gus_rampoff (); + gus_voice_volume (1530 + (25 * gus_pcm_volume)); + gus_ramp_range (65, 1530 + (25 * gus_pcm_volume)); + } + RESTORE_INTR (flags); } static void @@ -1800,18 +2360,24 @@ play_next_pcm_block (void) for (chn = 0; chn < gus_sampling_channels; chn++) { mode[chn] = 0x00; - ramp_mode[chn] = 0x03; /* Ramping and rollover off */ + ramp_mode[chn] = 0x03; /* + * Ramping and rollover off + */ if (chn == 0) { - mode[chn] |= 0x20; /* Loop irq */ + mode[chn] |= 0x20; /* + * Loop irq + */ voices[chn].loop_irq_mode = LMODE_PCM; } if (gus_sampling_bits != 8) { is16bits = 1; - mode[chn] |= 0x04; /* 16 bit data */ + mode[chn] |= 0x04; /* + * 16 bit data + */ } else is16bits = 0; @@ -1819,15 +2385,23 @@ play_next_pcm_block (void) dram_loc = this_one * pcm_bsize; dram_loc += chn * pcm_banksize; - if (this_one == (pcm_nblk - 1)) /* Last of the DRAM buffers */ + if (this_one == (pcm_nblk - 1)) /* + * Last of the DRAM buffers + */ { - mode[chn] |= 0x08; /* Enable loop */ - ramp_mode[chn] = 0x03;/* Disable rollover */ + mode[chn] |= 0x08; /* + * Enable loop + */ + ramp_mode[chn] = 0x03;/* + * Disable rollover + */ } else { if (chn == 0) - ramp_mode[chn] = 0x04; /* Enable rollover bit */ + ramp_mode[chn] = 0x04; /* + * Enable rollover bit + */ } DISABLE_INTR (flags); @@ -1835,13 +2409,21 @@ play_next_pcm_block (void) gus_voice_freq (speed); if (gus_sampling_channels == 1) - gus_voice_balance (7); /* mono */ + gus_voice_balance (7); /* + * mono + */ else if (chn == 0) - gus_voice_balance (0); /* left */ + gus_voice_balance (0); /* + * left + */ else - gus_voice_balance (15); /* right */ + gus_voice_balance (15); /* + * right + */ - if (!pcm_active) /* Voice not started yet */ + if (!pcm_active) /* + * Voice not started yet + */ { /* * The playback was not started yet (or there has been a pause). @@ -1850,38 +2432,67 @@ play_next_pcm_block (void) * the normal loop with irq. */ - gus_voice_off (); /* It could already be running */ + gus_voice_off (); /* + * It could already be running + */ gus_rampoff (); - gus_voice_volume (4000); - gus_ramp_range (65, 4030); + gus_voice_volume (1530 + (25 * gus_pcm_volume)); + gus_ramp_range (65, 1530 + (25 * gus_pcm_volume)); - gus_write_addr (0x0a, dram_loc, is16bits); /* Starting position */ - gus_write_addr (0x02, chn * pcm_banksize, is16bits); /* Loop start location */ + gus_write_addr (0x0a, dram_loc, is16bits); /* + * Starting position + */ + gus_write_addr (0x02, chn * pcm_banksize, is16bits); /* + * Loop start + * location + */ if (chn != 0) gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk), - is16bits); /* Loop end location */ + is16bits); /* + * Loop end location + */ } if (chn == 0) - gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /* Loop end location */ + gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /* + * Loop + * end + * location + */ else - mode[chn] |= 0x08; /* Enable loop */ + mode[chn] |= 0x08; /* + * Enable loop + */ if (pcm_datasize[this_one] != pcm_bsize) { - /* Incomplete block. Possibly the last one. */ + /* + * Incomplete block. Possibly the last one. + */ if (chn == 0) { - mode[chn] &= ~0x08; /* Disable loop */ - mode[chn] |= 0x20;/* Enable loop IRQ */ + mode[chn] &= ~0x08; /* + * Disable loop + */ + mode[chn] |= 0x20;/* + * Enable loop IRQ + */ voices[0].loop_irq_mode = LMODE_PCM_STOP; - ramp_mode[chn] = 0x03; /* No rollover bit */ + ramp_mode[chn] = 0x03; /* + * No rollover bit + */ } else { - gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /* Loop end location */ - mode[chn] &= ~0x08; /* Disable loop */ + gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /* + * Loop + * end + * location + */ + mode[chn] &= ~0x08; /* + * Disable loop + */ } } @@ -1908,7 +2519,7 @@ gus_transfer_output_block (int dev, unsigned long buf, * This routine transfers one block of audio data to the DRAM. In mono mode * it's called just once. When in stereo mode, this_one routine is called * once for both channels. - * + * * The left/mono channel data is transferred to the beginning of dram and the * right data to the area pointed by gus_page_size. */ @@ -1935,7 +2546,9 @@ gus_transfer_output_block (int dev, unsigned long buf, else this_one = pcm_current_block; - gus_write8 (0x41, 0); /* Disable GF1 DMA */ + gus_write8 (0x41, 0); /* + * Disable GF1 DMA + */ DMAbuf_start_dma (dev, buf + (chn * count), count, DMA_MODE_WRITE); address = this_one * pcm_bsize; @@ -1949,38 +2562,56 @@ gus_transfer_output_block (int dev, unsigned long buf, address |= (hold_address & 0x000c0000L); } - gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ + gus_write16 (0x42, (address >> 4) & 0xffff); /* + * DRAM DMA address + */ - dma_command = 0x21; /* IRQ enable, DMA start */ + dma_command = 0x21; /* + * IRQ enable, DMA start + */ if (gus_sampling_bits != 8) - dma_command |= 0x40; /* 16 bit _DATA_ */ + dma_command |= 0x40; /* + * 16 bit _DATA_ + */ else - dma_command |= 0x80; /* Invert MSB */ + dma_command |= 0x80; /* + * Invert MSB + */ if (sound_dsp_dmachan[dev] > 3) - dma_command |= 0x04; /* 16 bit DMA channel */ + dma_command |= 0x04; /* + * 16 bit DMA channel + */ - gus_write8 (0x41, dma_command); /* Kick on */ + gus_write8 (0x41, dma_command); /* + * Kick on + */ - if (chn == (gus_sampling_channels - 1)) /* Last channel */ + if (chn == (gus_sampling_channels - 1)) /* + * Last channel + */ { - /* Last (right or mono) channel data */ + /* + * Last (right or mono) channel data + */ active_device = GUS_DEV_PCM_DONE; if (!pcm_active && (pcm_qlen > 2 || count < pcm_bsize)) { play_next_pcm_block (); } } - else /* Left channel data. The right channel is - * transferred after DMA interrupt */ + else /* + * * * Left channel data. The right channel + * is * * * transferred after DMA interrupt */ active_device = GUS_DEV_PCM_CONTINUE; RESTORE_INTR (flags); } static void -gus_sampling_output_block (int dev, unsigned long buf, int total_count, int intrflag) +gus_sampling_output_block (int dev, unsigned long buf, int total_count, + int intrflag, int restart_dma) { pcm_current_buf = buf; pcm_current_count = total_count; @@ -1990,7 +2621,8 @@ gus_sampling_output_block (int dev, unsigned long buf, int total_count, int intr } static void -gus_sampling_start_input (int dev, unsigned long buf, int count, int intrflag) +gus_sampling_start_input (int dev, unsigned long buf, int count, + int intrflag, int restart_dma) { unsigned long flags; unsigned char mode; @@ -1999,13 +2631,21 @@ gus_sampling_start_input (int dev, unsigned long buf, int count, int intrflag) DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); - mode = 0xa0; /* DMA IRQ enable, invert MSB */ + mode = 0xa0; /* + * DMA IRQ enable, invert MSB + */ if (sound_dsp_dmachan[dev] > 3) - mode |= 0x04; /* 16 bit DMA channel */ + mode |= 0x04; /* + * 16 bit DMA channel + */ if (gus_sampling_channels > 1) - mode |= 0x02; /* Stereo */ - mode |= 0x01; /* DMA enable */ + mode |= 0x02; /* + * Stereo + */ + mode |= 0x01; /* + * DMA enable + */ gus_write8 (0x49, mode); @@ -2019,7 +2659,9 @@ gus_sampling_prepare_for_input (int dev, int bsize, int bcount) rate = (9878400 / (gus_sampling_speed + 2)) / 16; - gus_write8 (0x48, rate & 0xff); /* Set sampling frequency */ + gus_write8 (0x48, rate & 0xff); /* + * Set sampling frequency + */ if (gus_sampling_bits != 8) { @@ -2072,7 +2714,9 @@ gus_copy_from_user (int dev, char *localbuf, int localoffs, snd_rw_buf * userbuf, int useroffs, int len) { if (gus_sampling_channels == 1) - COPY_FROM_USER (&localbuf[localoffs], userbuf, useroffs, len); + { + COPY_FROM_USER (&localbuf[localoffs], userbuf, useroffs, len); + } else if (gus_sampling_bits == 8) { int in_left = useroffs; @@ -2119,19 +2763,39 @@ gus_copy_from_user (int dev, char *localbuf, int localoffs, static struct audio_operations gus_sampling_operations = { "Gravis UltraSound", - gus_sampling_open, /* */ - gus_sampling_close, /* */ - gus_sampling_output_block, /* */ - gus_sampling_start_input, /* */ - gus_sampling_ioctl, /* */ - gus_sampling_prepare_for_input, /* */ - gus_sampling_prepare_for_output, /* */ - gus_sampling_reset, /* */ - gus_sampling_reset, /* halt_xfer */ + NEEDS_RESTART, + gus_sampling_open, + gus_sampling_close, + gus_sampling_output_block, + gus_sampling_start_input, + gus_sampling_ioctl, + gus_sampling_prepare_for_input, + gus_sampling_prepare_for_output, + gus_sampling_reset, + gus_sampling_reset, gus_has_output_drained, gus_copy_from_user }; +#ifdef FUTURE_VERSION +static void +guswave_bender (int dev, int voice, int value) +{ + int freq; + unsigned long flags; + + voices[voice].bender = value - 8192; + freq = compute_finetune (voices[voice].orig_freq, value, voices[voice].bender_range); + voices[voice].current_freq = freq; + + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_freq (freq); + RESTORE_INTR (flags); +} + +#endif + static int guswave_patchmgr (int dev, struct patmgr_info *rec) { @@ -2161,7 +2825,9 @@ guswave_patchmgr (int dev, struct patmgr_info *rec) while (ptr >= 0 && ptr < free_sample) { rec->data.data8[i]++; - ptr = samples[ptr].key; /* Follow link */ + ptr = samples[ptr].key; /* + * Follow link + */ } } return 0; @@ -2176,7 +2842,9 @@ guswave_patchmgr (int dev, struct patmgr_info *rec) while (ptr >= 0 && ptr < free_sample) { rec->data.data32[n++] = ptr; - ptr = samples[ptr].key; /* Follow link */ + ptr = samples[ptr].key; /* + * Follow link + */ } } rec->parm1 = n; @@ -2196,8 +2864,12 @@ guswave_patchmgr (int dev, struct patmgr_info *rec) pat = (struct patch_info *) rec->data.data8; - pat->key = GUS_PATCH; /* Restore patch type */ - rec->parm1 = sample_ptrs[ptr]; /* DRAM address */ + pat->key = GUS_PATCH; /* + * Restore patch type + */ + rec->parm1 = sample_ptrs[ptr]; /* + * DRAM address + */ rec->parm2 = sizeof (struct patch_info); } return 0; @@ -2213,10 +2885,14 @@ guswave_patchmgr (int dev, struct patmgr_info *rec) pat = (struct patch_info *) rec->data.data8; - if (pat->len > samples[ptr].len) /* Cannot expand sample */ + if (pat->len > samples[ptr].len) /* + * Cannot expand sample + */ return RET_ERROR (EINVAL); - pat->key = samples[ptr].key; /* Ensure the link is correct */ + pat->key = samples[ptr].key; /* + * Ensure the link is correct + */ memcpy ((char *) &samples[ptr], rec->data.data8, sizeof (struct patch_info)); @@ -2226,7 +2902,9 @@ guswave_patchmgr (int dev, struct patmgr_info *rec) return 0; break; - case PM_READ_PATCH: /* Returns a block of wave data from the DRAM */ + case PM_READ_PATCH: /* + * Returns a block of wave data from the DRAM + */ { int sample = rec->parm1; int n; @@ -2237,9 +2915,13 @@ guswave_patchmgr (int dev, struct patmgr_info *rec) return RET_ERROR (EINVAL); if (offs < 0 || offs >= samples[sample].len) - return RET_ERROR (EINVAL); /* Invalid offset */ + return RET_ERROR (EINVAL); /* + * Invalid offset + */ - n = samples[sample].len - offs; /* Nr of bytes left */ + n = samples[sample].len - offs; /* + * Nr of bytes left + */ if (l > n) l = n; @@ -2248,18 +2930,26 @@ guswave_patchmgr (int dev, struct patmgr_info *rec) l = sizeof (rec->data.data8); if (l <= 0) - return RET_ERROR (EINVAL); /* Was there a bug? */ + return RET_ERROR (EINVAL); /* + * Was there a bug? + */ - offs += sample_ptrs[sample]; /* Begin offsess + offset to DRAM */ + offs += sample_ptrs[sample]; /* + * Begin offsess + offset to DRAM + */ for (n = 0; n < l; n++) rec->data.data8[n] = gus_peek (offs++); - rec->parm1 = n; /* Nr of bytes copied */ + rec->parm1 = n; /* + * Nr of bytes copied + */ } return 0; break; - case PM_WRITE_PATCH: /* Writes a block of wave data to the DRAM */ + case PM_WRITE_PATCH: /* + * Writes a block of wave data to the DRAM + */ { int sample = rec->parm1; int n; @@ -2270,9 +2960,13 @@ guswave_patchmgr (int dev, struct patmgr_info *rec) return RET_ERROR (EINVAL); if (offs < 0 || offs >= samples[sample].len) - return RET_ERROR (EINVAL); /* Invalid offset */ + return RET_ERROR (EINVAL); /* + * Invalid offset + */ - n = samples[sample].len - offs; /* Nr of bytes left */ + n = samples[sample].len - offs; /* + * Nr of bytes left + */ if (l > n) l = n; @@ -2281,13 +2975,19 @@ guswave_patchmgr (int dev, struct patmgr_info *rec) l = sizeof (rec->data.data8); if (l <= 0) - return RET_ERROR (EINVAL); /* Was there a bug? */ + return RET_ERROR (EINVAL); /* + * Was there a bug? + */ - offs += sample_ptrs[sample]; /* Begin offsess + offset to DRAM */ + offs += sample_ptrs[sample]; /* + * Begin offsess + offset to DRAM + */ for (n = 0; n < l; n++) gus_poke (offs++, rec->data.data8[n]); - rec->parm1 = n; /* Nr of bytes copied */ + rec->parm1 = n; /* + * Nr of bytes copied + */ } return 0; break; @@ -2300,6 +3000,9 @@ guswave_patchmgr (int dev, struct patmgr_info *rec) static struct synth_operations guswave_operations = { &gus_info, +#ifdef FUTURE_VERSION + 0, +#endif SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, guswave_open, @@ -2314,13 +3017,273 @@ static struct synth_operations guswave_operations = guswave_aftertouch, guswave_controller, guswave_panning, - guswave_patchmgr + guswave_volume_method, + guswave_patchmgr, +#ifdef FUTURE_VERSION + guswave_bender +#endif +}; + +static void +set_input_volumes (void) +{ + unsigned long flags; + unsigned char mask = 0xff & ~0x06; /* Just line out enabled */ + + DISABLE_INTR (flags); + + /* + * Enable channels having vol > 10% + * Note! bit 0x01 means line in DISABLED while 0x04 means + * mic in ENABLED. + */ + if (gus_line_vol > 10) + mask &= ~0x01; + if (gus_mic_vol > 10) + mask |= 0x04; + + if (recording_active) + { + /* + * Disable channel, if not selected for recording + */ + if (!(gus_recmask & SOUND_MASK_LINE)) + mask |= 0x01; + if (!(gus_recmask & SOUND_MASK_MIC)) + mask &= ~0x04; + } + + mix_image &= ~0x07; + mix_image |= mask & 0x07; + OUTB (mix_image, u_Mixer); + + RESTORE_INTR (flags); +} + +int +gus_default_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) +{ +#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ + SOUND_MASK_SYNTH|SOUND_MASK_PCM) + if (((cmd >> 8) & 0xff) == 'M') + { + if (cmd & IOC_IN) + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + gus_recmask = IOCTL_IN (arg) & MIX_DEVS; + if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE))) + gus_recmask = SOUND_MASK_MIC; + /* Note! Input volumes are updated during next open for recording */ + return IOCTL_OUT (arg, gus_recmask); + break; + + case SOUND_MIXER_MIC: + { + int vol = IOCTL_IN (arg) & 0xff; + + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + gus_mic_vol = vol; + set_input_volumes (); + return IOCTL_OUT (arg, vol | (vol << 8)); + } + break; + + case SOUND_MIXER_LINE: + { + int vol = IOCTL_IN (arg) & 0xff; + + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + gus_line_vol = vol; + set_input_volumes (); + return IOCTL_OUT (arg, vol | (vol << 8)); + } + break; + + case SOUND_MIXER_PCM: + gus_pcm_volume = IOCTL_IN (arg) & 0xff; + if (gus_pcm_volume < 0) + gus_pcm_volume = 0; + if (gus_pcm_volume > 100) + gus_pcm_volume = 100; + gus_sampling_update_volume (); + return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8)); + break; + + case SOUND_MIXER_SYNTH: + { + int voice; + + gus_wave_volume = IOCTL_IN (arg) & 0xff; + + if (gus_wave_volume < 0) + gus_wave_volume = 0; + if (gus_wave_volume > 100) + gus_wave_volume = 100; + + if (active_device == GUS_DEV_WAVE) + for (voice = 0; voice < nr_voices; voice++) + dynamic_volume_change (voice); /* + * Apply the new + * volume + */ + + return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8)); + } + break; + + default: + return RET_ERROR (EINVAL); + } + else + switch (cmd & 0xff) /* + * Return parameters + */ + { + + case SOUND_MIXER_RECSRC: + return IOCTL_OUT (arg, gus_recmask); + break; + + case SOUND_MIXER_DEVMASK: + return IOCTL_OUT (arg, MIX_DEVS); + break; + + case SOUND_MIXER_STEREODEVS: + return IOCTL_OUT (arg, 0); + break; + + case SOUND_MIXER_RECMASK: + return IOCTL_OUT (arg, SOUND_MASK_MIC | SOUND_MASK_LINE); + break; + + case SOUND_MIXER_CAPS: + return IOCTL_OUT (arg, 0); + break; + + case SOUND_MIXER_MIC: + return IOCTL_OUT (arg, gus_mic_vol | (gus_mic_vol << 8)); + break; + + case SOUND_MIXER_LINE: + return IOCTL_OUT (arg, gus_line_vol | (gus_line_vol << 8)); + break; + + case SOUND_MIXER_PCM: + return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8)); + break; + + case SOUND_MIXER_SYNTH: + return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8)); + break; + + default: + return RET_ERROR (EINVAL); + } + } + else + return RET_ERROR (EINVAL); +} + +static struct mixer_operations gus_mixer_operations = +{ + gus_default_mixer_ioctl }; +static long +gus_default_mixer_init (long mem_start) +{ + if (num_mixers < MAX_MIXER_DEV) /* + * Don't install if there is another + * mixer + */ + mixer_devs[num_mixers++] = &gus_mixer_operations; + + return mem_start; +} + long gus_wave_init (long mem_start, int irq, int dma) { - printk ("snd4: <Gravis UltraSound %dk>", gus_mem_size / 1024); + unsigned long flags; + unsigned char val; + char *model_num = "2.4"; + int gus_type = 0x24; /* 2.4 */ + int mixer_type = 0; + + /* + * Try to identify the GUS model. + * + * Versions < 3.6 don't have the digital ASIC. Try to probe it first. + */ + + DISABLE_INTR (flags); + OUTB (0x20, gus_base + 0x0f); + val = INB (gus_base + 0x0f); + RESTORE_INTR (flags); + + if (val != 0xff && (val & 0x06)) /* Should be 0x02? */ + { + /* + * It has the digital ASIC so the card is at least v3.4. + * Next try to detect the true model. + */ + + val = INB (u_MixSelect); + + /* + * Value 255 means pre-3.7 which don't have mixer. + * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer. + * 10 and above is GUS MAX which has the CS4231 codec/mixer. + * + * Sorry. No GUS max support yet but it should be available + * soon after the SDK for GUS MAX is available. + */ + + if (val == 255 || val < 5) + { + model_num = "3.4"; + gus_type = 0x34; + } + else if (val < 10) + { + model_num = "3.7"; + gus_type = 0x37; + mixer_type = ICS2101; + } + else + { + model_num = "MAX"; + gus_type = 0x40; + mixer_type = CS4231; + } + } + else + { + /* + * ASIC not detected so the card must be 2.2 or 2.4. + * There could still be the 16-bit/mixer daughter card. + * It has the same codec/mixer than MAX. + * At this time there is no support for it but it will appear soon. + */ + } + + +#ifdef __FreeBSD__ + printk ("snd4: <Gravis UltraSound %s (%dk)>", model_num, (int) gus_mem_size / 1024); +#else + printk (" <Gravis UltraSound %s (%dk)>", model_num, (int) gus_mem_size / 1024); +#endif + +#ifndef SCO + sprintf (gus_info.name, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024); +#endif if (irq < 0 || irq > 15) { @@ -2342,6 +3305,9 @@ gus_wave_init (long mem_start, int irq, int dma) else synth_devs[num_synths++] = &guswave_operations; + PERMANENT_MALLOC (struct patch_info *, samples, + (MAX_SAMPLE + 1) * sizeof (*samples), mem_start); + reset_sample_memory (); gus_initialize (); @@ -2350,13 +3316,29 @@ gus_wave_init (long mem_start, int irq, int dma) { dsp_devs[gus_devnum = num_dspdevs++] = &gus_sampling_operations; sound_dsp_dmachan[gus_devnum] = dma; - sound_buffcounts[gus_devnum] = 1; + sound_buffcounts[gus_devnum] = DSP_BUFFCOUNT; sound_buffsizes[gus_devnum] = DSP_BUFFSIZE; sound_dma_automode[gus_devnum] = 0; } else printk ("GUS: Too many PCM devices available\n"); + /* + * Mixer dependent initialization. + */ + + switch (mixer_type) + { + case ICS2101: + gus_line_vol=gus_mic_vol=gus_wave_volume = gus_pcm_volume = 100; + return ics2101_mixer_init (mem_start); + + case CS4231: + /* Available soon */ + default: + return gus_default_mixer_init (mem_start); + } + return mem_start; } @@ -2371,7 +3353,9 @@ do_loop_irq (int voice) gus_select_voice (voice); tmp = gus_read8 (0x00); - tmp &= ~0x20; /* Disable wave IRQ for this_one voice */ + tmp &= ~0x20; /* + * Disable wave IRQ for this_one voice + */ gus_write8 (0x00, tmp); mode = voices[voice].loop_irq_mode; @@ -2381,23 +3365,33 @@ do_loop_irq (int voice) switch (mode) { - case LMODE_FINISH: /* Final loop finished, shoot volume down */ + case LMODE_FINISH: /* + * Final loop finished, shoot volume down + */ - if ((gus_read16 (0x09) >> 4) < 100) /* Get current volume */ + if ((gus_read16 (0x09) >> 4) < 100) /* + * Get current volume + */ { gus_voice_off (); gus_rampoff (); gus_voice_init (voice); - return; + break; } gus_ramp_range (65, 4065); - gus_ramp_rate (0, 63); /* Fastest possible rate */ - gus_rampon (0x20 | 0x40); /* Ramp down, once, irq */ + gus_ramp_rate (0, 63); /* + * Fastest possible rate + */ + gus_rampon (0x20 | 0x40); /* + * Ramp down, once, irq + */ voices[voice].volume_irq_mode = VMODE_HALT; break; case LMODE_PCM_STOP: - pcm_active = 0; /* Requires extensive processing */ + pcm_active = 0; /* + * Requires extensive processing + */ case LMODE_PCM: { int orig_qlen = pcm_qlen; @@ -2409,7 +3403,9 @@ do_loop_irq (int voice) play_next_pcm_block (); } else - { /* Out of data. Just stop the voice */ + { /* + * Out of data. Just stop the voice + */ gus_voice_off (); gus_rampoff (); pcm_active = 0; @@ -2417,7 +3413,7 @@ do_loop_irq (int voice) if (orig_qlen == pcm_nblk) { - DMAbuf_outputintr (gus_devnum); + DMAbuf_outputintr (gus_devnum, 0); } } break; @@ -2439,7 +3435,9 @@ do_volume_irq (int voice) gus_select_voice (voice); tmp = gus_read8 (0x0d); - tmp &= ~0x20; /* Disable volume ramp IRQ */ + tmp &= ~0x20; /* + * Disable volume ramp IRQ + */ gus_write8 (0x0d, tmp); mode = voices[voice].volume_irq_mode; @@ -2448,19 +3446,35 @@ do_volume_irq (int voice) switch (mode) { - case VMODE_HALT: /* Decay phase finished */ + case VMODE_HALT: /* + * Decay phase finished + */ + RESTORE_INTR (flags); gus_voice_init (voice); break; case VMODE_ENVELOPE: gus_rampoff (); + RESTORE_INTR (flags); step_envelope (voice); break; + case VMODE_START_NOTE: + RESTORE_INTR (flags); + guswave_start_note2 (voices[voice].dev_pending, voice, + voices[voice].note_pending, voices[voice].volume_pending); + if (voices[voice].kill_pending) + guswave_kill_note (voices[voice].dev_pending, voice, 0); + if (voices[voice].sample_pending >= 0) + { + guswave_set_instr (voices[voice].dev_pending, voice, + voices[voice].sample_pending); + voices[voice].sample_pending = -1; + } + break; + default:; } - - RESTORE_INTR (flags); } void @@ -2473,24 +3487,38 @@ gus_voice_irq (void) while (1) { - src = gus_read8 (0x0f); /* Get source info */ + src = gus_read8 (0x0f); /* + * Get source info + */ voice = src & 0x1f; src &= 0xc0; if (src == (0x80 | 0x40)) - return; /* No interrupt */ + return; /* + * No interrupt + */ voice_bit = 1 << voice; - if (!(src & 0x80)) /* Wave IRQ pending */ - if (!(wave_ignore & voice_bit) && voice < nr_voices) /* Not done yet */ + if (!(src & 0x80)) /* + * Wave IRQ pending + */ + if (!(wave_ignore & voice_bit) && voice < nr_voices) /* + * Not done + * yet + */ { wave_ignore |= voice_bit; do_loop_irq (voice); } - if (!(src & 0x40)) /* Volume IRQ pending */ - if (!(volume_ignore & voice_bit) && voice < nr_voices) /* Not done yet */ + if (!(src & 0x40)) /* + * Volume IRQ pending + */ + if (!(volume_ignore & voice_bit) && voice < nr_voices) /* + * Not done + * yet + */ { volume_ignore |= voice_bit; do_volume_irq (voice); @@ -2503,13 +3531,17 @@ guswave_dma_irq (void) { unsigned char status; - status = gus_look8 (0x41); /* Get DMA IRQ Status */ - if (status & 0x40) /* DMA Irq pending */ + status = gus_look8 (0x41); /* + * Get DMA IRQ Status + */ + if (status & 0x40) /* + * DMA Irq pending + */ switch (active_device) { case GUS_DEV_WAVE: - if (dram_sleep_flag) - WAKE_UP (dram_sleeper); + if (SOMEONE_WAITING (dram_sleeper, dram_sleep_flag)) + WAKE_UP (dram_sleeper, dram_sleep_flag); break; case GUS_DEV_PCM_CONTINUE: @@ -2521,15 +3553,19 @@ guswave_dma_irq (void) case GUS_DEV_PCM_DONE: if (pcm_qlen < pcm_nblk) { - DMAbuf_outputintr (gus_devnum); + DMAbuf_outputintr (gus_devnum, pcm_qlen == 0); } break; default:; } - status = gus_look8 (0x49); /* Get Sampling IRQ Status */ - if (status & 0x40) /* Sampling Irq pending */ + status = gus_look8 (0x49); /* + * Get Sampling IRQ Status + */ + if (status & 0x40) /* + * Sampling Irq pending + */ { DMAbuf_inputintr (gus_devnum); } diff --git a/sys/i386/isa/sound/ics2101.c b/sys/i386/isa/sound/ics2101.c new file mode 100644 index 000000000000..0e54c608de36 --- /dev/null +++ b/sys/i386/isa/sound/ics2101.c @@ -0,0 +1,265 @@ +/* + * sound/ics2101.c + * + * Driver for the ICS2101 mixer of GUS v3.7. + * + * Copyright by Hannu Savolainen 1994 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS) + +#ifdef __FreeBSD__ +#include <machine/ultrasound.h> +#else +#include "ultrasound.h" +#endif +#include "gus_hw.h" + +#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ + SOUND_MASK_SYNTH| \ + SOUND_MASK_CD | SOUND_MASK_VOLUME) + +extern int gus_base; +static int volumes[ICS_MIXDEVS]; +static int left_fix[ICS_MIXDEVS] = +{1, 1, 1, 2, 1, 2}; +static int right_fix[ICS_MIXDEVS] = +{2, 2, 2, 1, 2, 1}; + +static int +scale_vol(int vol) +{ +#if 1 +/* + * Experimental volume scaling by Risto Kankkunen. + * This should give smoother volume response than just + * a plain multiplication. + */ + int e; + + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + vol = (31 * vol + 50) / 100; + e = 0; + if (vol) { + while (vol < 16) { + vol <<= 1; + e--; + } + vol -= 16; + e += 7; + } + return ((e << 4) + vol); +#else + return ((vol*127)+50)/100; +#endif +} + +static void +write_mix (int dev, int chn, int vol) +{ + int *selector; + unsigned long flags; + int ctrl_addr = dev << 3; + int attn_addr = dev << 3; + + vol=scale_vol(vol); + + if (chn == CHN_LEFT) + { + selector = left_fix; + ctrl_addr |= 0x00; + attn_addr |= 0x02; + } + else + { + selector = right_fix; + ctrl_addr |= 0x01; + attn_addr |= 0x03; + } + + DISABLE_INTR (flags); + OUTB (ctrl_addr, u_MixSelect); + OUTB (selector[dev], u_MixData); + OUTB (attn_addr, u_MixSelect); + OUTB ((unsigned char) vol, u_MixData); + RESTORE_INTR (flags); +} + +static int +set_volumes (int dev, int vol) +{ + int left = vol & 0x00ff; + int right = (vol >> 8) & 0x00ff; + + if (left < 0) + left = 0; + if (left > 100) + left = 100; + if (right < 0) + right = 0; + if (right > 100) + right = 100; + + write_mix (dev, CHN_LEFT, left); + write_mix (dev, CHN_RIGHT, right); + + vol = left + (right << 8); + volumes[dev] = vol; + return vol; +} + +static int +ics2101_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) +{ + if (((cmd >> 8) & 0xff) == 'M') + { + if (cmd & IOC_IN) + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + return gus_default_mixer_ioctl (dev, cmd, arg); + break; + + case SOUND_MIXER_MIC: + return IOCTL_OUT (arg, set_volumes (DEV_MIC, IOCTL_IN (arg))); + break; + + case SOUND_MIXER_CD: + return IOCTL_OUT (arg, set_volumes (DEV_CD, IOCTL_IN (arg))); + break; + + case SOUND_MIXER_LINE: + return IOCTL_OUT (arg, set_volumes (DEV_LINE, IOCTL_IN (arg))); + break; + + case SOUND_MIXER_SYNTH: + return IOCTL_OUT (arg, set_volumes (DEV_GF1, IOCTL_IN (arg))); + break; + + case SOUND_MIXER_VOLUME: + return IOCTL_OUT (arg, set_volumes (DEV_VOL, IOCTL_IN (arg))); + break; + + default: + return RET_ERROR (EINVAL); + } + else + switch (cmd & 0xff) /* + * Return parameters + */ + { + + case SOUND_MIXER_RECSRC: + return gus_default_mixer_ioctl (dev, cmd, arg); + break; + + case SOUND_MIXER_DEVMASK: + return IOCTL_OUT (arg, MIX_DEVS); + break; + + case SOUND_MIXER_STEREODEVS: + return IOCTL_OUT (arg, SOUND_MASK_LINE | SOUND_MASK_CD | + SOUND_MASK_SYNTH | SOUND_MASK_VOLUME| + SOUND_MASK_MIC); + break; + + case SOUND_MIXER_RECMASK: + return IOCTL_OUT (arg, SOUND_MASK_MIC | SOUND_MASK_LINE); + break; + + case SOUND_MIXER_CAPS: + return IOCTL_OUT (arg, 0); + break; + + case SOUND_MIXER_MIC: + return IOCTL_OUT (arg, volumes[DEV_MIC]); + break; + + case SOUND_MIXER_LINE: + return IOCTL_OUT (arg, volumes[DEV_LINE]); + break; + + case SOUND_MIXER_CD: + return IOCTL_OUT (arg, volumes[DEV_CD]); + break; + + case SOUND_MIXER_VOLUME: + return IOCTL_OUT (arg, volumes[DEV_VOL]); + break; + + case SOUND_MIXER_SYNTH: + return IOCTL_OUT (arg, volumes[DEV_GF1]); + break; + + default: + return RET_ERROR (EINVAL); + } + } + + return RET_ERROR (EINVAL); +} + +static struct mixer_operations ics2101_mixer_operations = +{ + ics2101_mixer_ioctl +}; + +long +ics2101_mixer_init (long mem_start) +{ + int i; + + if (num_mixers < MAX_MIXER_DEV) + { + mixer_devs[num_mixers++] = &ics2101_mixer_operations; + + /* + * Some GUS v3.7 cards had some channels flipped. Disable + * the flipping feature if the model id is other than 5. + */ + + if (INB (u_MixSelect) != 5) + { + for (i = 0; i < ICS_MIXDEVS; i++) + left_fix[i] = 1; + for (i = 0; i < ICS_MIXDEVS; i++) + right_fix[i] = 2; + } + + set_volumes (DEV_GF1, 0x5a5a); + set_volumes (DEV_CD, 0x5a5a); + set_volumes (DEV_MIC, 0x0000); + set_volumes (DEV_LINE, 0x5a5a); + set_volumes (DEV_VOL, 0x5a5a); + set_volumes (DEV_UNUSED, 0x0000); + } + + return mem_start; +} + +#endif diff --git a/sys/i386/isa/sound/local.h b/sys/i386/isa/sound/local.h index f81bd7821673..36092e37d978 100644 --- a/sys/i386/isa/sound/local.h +++ b/sys/i386/isa/sound/local.h @@ -1,10 +1,15 @@ -/* These few lines are used by FreeBSD (only??). */ - +/* for FreeBSD */ #include "snd.h" #if NSND > 0 -#define CONFIGURE_SOUNDCARD +#define KERNEL_SOUNDCARD #endif -#define DSP_BUFFSIZE 32768 +#define DSP_BUFFSIZE 65536 +#define NO_AUTODMA /* still */ #define SELECTED_SOUND_OPTIONS 0xffffffff +#define SOUND_VERSION_STRING "2.5" +#define SOUND_CONFIG_DATE "Sat Apr 23 07:45:17 MSD 1994" +#define SOUND_CONFIG_BY "ache" +#define SOUND_CONFIG_HOST "dream.demos.su" +#define SOUND_CONFIG_DOMAIN "" diff --git a/sys/i386/isa/sound/midi.c b/sys/i386/isa/sound/midi.c index 8d604a80abc2..6ea51b061b9c 100644 --- a/sys/i386/isa/sound/midi.c +++ b/sys/i386/isa/sound/midi.c @@ -31,24 +31,27 @@ #ifndef EXCLUDE_CHIP_MIDI -static int generic_midi_busy[MAX_MIDI_DEV]; +static int generic_midi_busy[MAX_MIDI_DEV]; -long CMIDI_init (long mem_start) +long +CMIDI_init (long mem_start) { - - int i; - int n = num_midi_drivers; - /* int n = sizeof (midi_supported) / sizeof( struct generic_midi_info ); - */ - for (i = 0; i < n; i++) - { - if ( midi_supported[i].attach (mem_start) ) + + int i; + int n = num_midi_drivers; + + /* + * int n = sizeof (midi_supported) / sizeof( struct generic_midi_info ); + */ + for (i = 0; i < n; i++) { - printk("MIDI: Successfully attached %s\n",midi_supported[i].name); - } + if (midi_supported[i].attach (mem_start)) + { + printk ("MIDI: Successfully attached %s\n", midi_supported[i].name); + } - } - return (mem_start); + } + return (mem_start); } @@ -56,142 +59,143 @@ int CMIDI_open (int dev, struct fileinfo *file) { - int mode, err, retval; + int mode, err, retval; - dev = dev >> 4; + dev = dev >> 4; - mode = file->mode & O_ACCMODE; + mode = file->mode & O_ACCMODE; - if (generic_midi_busy[dev]) - return (RET_ERROR(EBUSY)); + if (generic_midi_busy[dev]) + return (RET_ERROR (EBUSY)); - - if (dev >= num_generic_midis) - { - printk(" MIDI device %d not installed.\n", dev); - return (ENXIO); - } - if (!generic_midi_devs[dev]) - { - printk(" MIDI device %d not initialized\n",dev); + if (dev >= num_generic_midis) + { + printk (" MIDI device %d not installed.\n", dev); return (ENXIO); - } + } + + if (!generic_midi_devs[dev]) + { + printk (" MIDI device %d not initialized\n", dev); + return (ENXIO); + } + + /* If all good and healthy, go ahead and issue call! */ - /* If all good and healthy, go ahead and issue call! */ - - retval = generic_midi_devs[dev]->open (dev, mode) ; + retval = generic_midi_devs[dev]->open (dev, mode); - /* If everything ok, set device as busy */ + /* If everything ok, set device as busy */ - if ( retval >= 0 ) - generic_midi_busy[dev] = 1; - - return ( retval ); + if (retval >= 0) + generic_midi_busy[dev] = 1; + + return (retval); } -int -CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +int +CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { - int retval; - - dev = dev >> 4; + int retval; + + dev = dev >> 4; - if (dev >= num_generic_midis) - { - printk(" MIDI device %d not installed.\n", dev); - return (ENXIO); - } + if (dev >= num_generic_midis) + { + printk (" MIDI device %d not installed.\n", dev); + return (ENXIO); + } - /* Make double sure of healthiness -- doubt - * Need we check this again?? - * - */ + /* + * Make double sure of healthiness -- doubt Need we check this again?? + * + */ - if (!generic_midi_devs[dev]) - { - printk(" MIDI device %d not initialized\n",dev); + if (!generic_midi_devs[dev]) + { + printk (" MIDI device %d not initialized\n", dev); return (ENXIO); - } + } - /* If all good and healthy, go ahead and issue call! */ + /* If all good and healthy, go ahead and issue call! */ - - retval = generic_midi_devs[dev]->write (dev, buf); - return ( retval ); + retval = generic_midi_devs[dev]->write (dev, buf); -} + return (retval); + +} int -CMIDI_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count) +CMIDI_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { - int retval; - - dev = dev >> 4; - - if (dev >= num_generic_midis) - { - printk(" MIDI device %d not installed.\n", dev); - return (ENXIO); - } - - /* Make double sure of healthiness -- doubt - * Need we check this again?? - * - */ - - if (!generic_midi_devs[dev]) - { - printk(" MIDI device %d not initialized\n",dev); + int retval; + + dev = dev >> 4; + + if (dev >= num_generic_midis) + { + printk (" MIDI device %d not installed.\n", dev); return (ENXIO); - } + } - /* If all good and healthy, go ahead and issue call! */ + /* + * Make double sure of healthiness -- doubt Need we check this again?? + * + */ + + if (!generic_midi_devs[dev]) + { + printk (" MIDI device %d not initialized\n", dev); + return (ENXIO); + } + + /* If all good and healthy, go ahead and issue call! */ - - retval = generic_midi_devs[dev]->read(dev,buf); - return (retval); + retval = generic_midi_devs[dev]->read (dev, buf); + + return (retval); + +} -} - int CMIDI_close (int dev, struct fileinfo *file) { - int retval; - dev = dev >> 4; - - if (dev >= num_generic_midis) - { - printk(" MIDI device %d not installed.\n", dev); - return (ENXIO); - } - - /* Make double sure of healthiness -- doubt - * Need we check this again?? - * - */ - - if (!generic_midi_devs[dev]) - { - printk(" MIDI device %d not initialized\n",dev); + int retval; + + dev = dev >> 4; + + if (dev >= num_generic_midis) + { + printk (" MIDI device %d not installed.\n", dev); + return (ENXIO); + } + + /* + * Make double sure of healthiness -- doubt Need we check this again?? + * + */ + + if (!generic_midi_devs[dev]) + { + printk (" MIDI device %d not initialized\n", dev); return (ENXIO); - } + } - /* If all good and healthy, go ahead and issue call! */ + /* If all good and healthy, go ahead and issue call! */ - generic_midi_devs[dev]->close(dev); + generic_midi_devs[dev]->close (dev); - generic_midi_busy[dev] = 0; /* Free the device */ + generic_midi_busy[dev] = 0; /* Free the device */ - return (0) ; + return (0); } diff --git a/sys/i386/isa/sound/midibuf.c b/sys/i386/isa/sound/midibuf.c index 0196f84876bf..7dadb3f8fa74 100644 --- a/sys/i386/isa/sound/midibuf.c +++ b/sys/i386/isa/sound/midibuf.c @@ -1,12 +1,12 @@ /* - * linux/kernel/chr_drv/sound/midibuf.c - * + * sound/midibuf.c + * * Device file manager for /dev/midi - * + * * NOTE! This part of the driver is currently just a stub. - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -14,7 +14,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -26,7 +26,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #include "sound_config.h" diff --git a/sys/i386/isa/sound/mpu401.c b/sys/i386/isa/sound/mpu401.c index e8c011bc7940..38ba486b142b 100644 --- a/sys/i386/isa/sound/mpu401.c +++ b/sys/i386/isa/sound/mpu401.c @@ -1,12 +1,12 @@ /* - * linux/kernel/chr_drv/sound/mpu401.c - * + * sound/mpu401.c + * * The low level driver for Roland MPU-401 compatible Midi cards. - * + * * This version supports just the DUMB UART mode. - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -14,7 +14,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -26,7 +26,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #include "sound_config.h" @@ -61,102 +61,16 @@ static int my_dev; static int reset_mpu401 (void); static void (*midi_input_intr) (int dev, unsigned char data); -static void -mpu401_input_loop (void) -{ - int count; - - count = 10; - - while (count) /* Not timed out */ - if (input_avail ()) - { - unsigned char c = mpu401_read (); - - count = 100; - - if (mpu401_opened & OPEN_READ) - midi_input_intr (my_dev, c); - } - else - while (!input_avail () && count) - count--; -} - void mpuintr (int unit) { - if (input_avail ()) - mpu401_input_loop (); -} - -/* - * It looks like there is no input interrupts in the UART mode. Let's try - * polling. - */ - -/* XXX WARNING: FreeBSD doesn't seem to have timer_lists like this, - * so until I figure out how to do the analogous thing in FreeBSD, I'm - * not all that sure WHAT this driver will do! It might work, depending - * on the condition taken, but then again it might not! -jkh - * XXX WARNING - */ -static void -poll_mpu401 (unsigned long dummy) -{ - unsigned long flags; - -#ifdef linux - static struct timer_list mpu401_timer = - {NULL, 0, 0, poll_mpu401}; -#endif - - if (!(mpu401_opened & OPEN_READ)) - return; /* No longer required */ - - DISABLE_INTR (flags); - - if (input_avail ()) - mpu401_input_loop (); - -#ifdef linux - mpu401_timer.expires = 1; - add_timer (&mpu401_timer); /* Come back later */ -#endif - - RESTORE_INTR (flags); -} - -static int -set_mpu401_irq (int interrupt_level) -{ - int retcode = EINVAL; - -#ifdef linux - struct sigaction sa; - - sa.sa_handler = mpuintr; - -#ifdef SND_SA_INTERRUPT - sa.sa_flags = SA_INTERRUPT; -#else - sa.sa_flags = 0; -#endif - - sa.sa_mask = 0; - sa.sa_restorer = NULL; - - retcode = irqaction (interrupt_level, &sa); - - if (retcode < 0) + while (input_avail ()) { - printk ("MPU-401: IRQ%d already in use\n", interrupt_level); - } + unsigned char c = mpu401_read (); -#else - /* # error Unimplemented for this OS */ -#endif - return retcode; + if (mpu401_opened & OPEN_READ) + midi_input_intr (my_dev, c); + } } static int @@ -171,11 +85,10 @@ mpu401_open (int dev, int mode, return RET_ERROR (EBUSY); } - mpu401_input_loop (); + mpuintr (0); midi_input_intr = input; mpu401_opened = mode; - poll_mpu401 (0); /* Enable input polling */ return 0; } @@ -199,7 +112,7 @@ mpu401_out (int dev, unsigned char midi_byte) DISABLE_INTR (flags); if (input_avail ()) - mpu401_input_loop (); + mpuintr (0); RESTORE_INTR (flags); @@ -257,7 +170,7 @@ mpu401_buffer_status (int dev) static struct midi_operations mpu401_operations = { - {"MPU-401", 0}, + {"MPU-401", 0, 0, SNDCARD_MPU401}, mpu401_open, mpu401_close, mpu401_ioctl, @@ -294,12 +207,14 @@ attach_mpu401 (long mem_start, struct address_info *hw_config) RESTORE_INTR (flags); +#ifdef __FreeBSD__ printk ("snd5: <Roland MPU-401>"); +#else + printk (" <Roland MPU-401>"); +#endif my_dev = num_midis; -#ifdef linux mpu401_dev = num_midis; -#endif midi_devs[num_midis++] = &mpu401_operations; return mem_start; } @@ -311,7 +226,7 @@ reset_mpu401 (void) int ok, timeout, n; /* - * Send the RESET command. Try twice if no success at the first time. + * Send the RESET command. Try again if no success at the first time. */ ok = 0; @@ -337,7 +252,7 @@ reset_mpu401 (void) mpu401_opened = 0; if (ok) - mpu401_input_loop (); /* Flush input before enabling interrupts */ + mpuintr (0); /* Flush input before enabling interrupts */ RESTORE_INTR (flags); @@ -353,7 +268,7 @@ probe_mpu401 (struct address_info *hw_config) mpu401_base = hw_config->io_base; mpu401_irq = hw_config->irq; - if (set_mpu401_irq (mpu401_irq) < 0) + if (snd_set_irq_handler (mpu401_irq, mpuintr) < 0) return 0; ok = reset_mpu401 (); diff --git a/sys/i386/isa/sound/opl3.c b/sys/i386/isa/sound/opl3.c index 2dbed1f96e00..6e3dccab8c43 100644 --- a/sys/i386/isa/sound/opl3.c +++ b/sys/i386/isa/sound/opl3.c @@ -1,10 +1,10 @@ /* - * linux/kernel/chr_drv/sound/opl3.c - * + * sound/opl3.c + * * A low level driver for Yamaha YM3812 and OPL-3 -chips - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -12,7 +12,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -24,7 +24,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ /* Major improvements to the FM handling 30AUG92 by Rob Hooft, */ @@ -38,7 +38,7 @@ #define MAX_VOICE 18 #define OFFS_4OP 11 /* Definitions for the operators OP3 and OP4 - * begin here */ + * begin here */ static int opl3_enabled = 0; static int left_address = 0x388, right_address = 0x388, both_address = 0; @@ -59,8 +59,7 @@ struct voice_info static struct voice_info voices[MAX_VOICE]; -typedef struct sbi_instrument instr_array[SBFM_MAXINSTR]; -static instr_array instrmap; +static struct sbi_instrument *instrmap; static struct sbi_instrument *active_instrument[MAX_VOICE] = {NULL}; @@ -71,17 +70,20 @@ static int already_initialized = 0; static int opl3_ok = 0; static int opl3_busy = 0; -static int fm_model = 0; /* 0=no fm, 1=mono, 2=SB Pro 1, 3=SB Pro 2 */ +static int fm_model = 0; /* 0=no fm, 1=mono, 2=SB Pro 1, 3=SB Pro 2 */ static int store_instr (int instr_no, struct sbi_instrument *instr); static void freq_to_fnum (int freq, int *block, int *fnum); -static void opl3_command (int io_addr, const unsigned char addr, const unsigned char val); +static void opl3_command (int io_addr, unsigned int addr, unsigned int val); static int opl3_kill_note (int dev, int voice, int velocity); static unsigned char connection_mask = 0x00; void enable_opl3_mode (int left, int right, int both) { + if (opl3_enabled) + return; + opl3_enabled = 1; left_address = left; right_address = right; @@ -112,7 +114,7 @@ enter_4op_mode (void) for (i = 0; i < 12; i++) logical_voices[i] = voices_4op[i]; - nr_voices = 6; + nr_voices = 12; } static int @@ -140,7 +142,7 @@ opl3_ioctl (int dev, break; case SNDCTL_SYNTH_INFO: - fm_info.nr_voices = nr_voices; + fm_info.nr_voices = (nr_voices == 12) ? 6 : nr_voices; IOCTL_TO_USER ((char *) arg, 0, &fm_info, sizeof (fm_info)); return 0; @@ -169,10 +171,10 @@ opl3_detect (int ioaddr) * This function returns 1 if the FM chicp is present at the given I/O port * The detection algorithm plays with the timer built in the FM chip and * looks for a change in the status register. - * + * * Note! The timers of the FM chip are not connected to AdLib (and compatible) * boards. - * + * * Note2! The chip is initialized if detected. */ @@ -184,6 +186,9 @@ opl3_detect (int ioaddr) return 0; /* Do avoid duplicate initializations */ } + if (opl3_enabled) + ioaddr = left_address; + opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); /* Reset timers 1 and 2 */ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); /* Reset the IRQ of FM * chicp */ @@ -192,7 +197,7 @@ opl3_detect (int ioaddr) if ((stat1 & 0xE0) != 0x00) { - return 0; /* Should be 0x00 */ + return 0; /* Should be 0x00 */ } opl3_command (ioaddr, TIMER1_REGISTER, 0xff); /* Set timer 1 to 0xff */ @@ -270,7 +275,7 @@ store_instr (int instr_no, struct sbi_instrument *instr) { if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || !opl3_enabled)) - printk ("FM warning: Invalid patch format field (key) 0x%04x\n", instr->key); + printk ("FM warning: Invalid patch format field (key) 0x%x\n", instr->key); memcpy ((char *) &(instrmap[instr_no]), (char *) instr, sizeof (*instr)); return 0; @@ -362,7 +367,7 @@ set_voice_volume (int voice, int volume) vol2 = instr->operators[3]; if ((instr->operators[10] & 0x01)) - { /* Additive synthesis */ + { /* Additive synthesis */ calc_vol (&vol1, volume); calc_vol (&vol2, volume); } @@ -616,7 +621,7 @@ freq_to_fnum (int freq, int *block, int *fnum) } static void -opl3_command (int io_addr, const unsigned char addr, const unsigned char val) +opl3_command (int io_addr, unsigned int addr, unsigned int val) { int i; @@ -625,7 +630,7 @@ opl3_command (int io_addr, const unsigned char addr, const unsigned char val) * register. The OPL-3 survives with just two INBs */ - OUTB (addr, io_addr); /* Select register */ + OUTB ((unsigned char) (addr & 0xff), io_addr); /* Select register */ if (!opl3_enabled) tenmicrosec (); @@ -633,7 +638,7 @@ opl3_command (int io_addr, const unsigned char addr, const unsigned char val) for (i = 0; i < 2; i++) INB (io_addr); - OUTB (val, io_addr + 1); /* Write to register */ + OUTB ((unsigned char) (val & 0xff), io_addr + 1); /* Write to register */ if (!opl3_enabled) { @@ -745,6 +750,11 @@ opl3_panning (int dev, int voice, int pressure) { } +static void +opl3_volume_method (int dev, int mode) +{ +} + #define SET_VIBRATO(cell) { \ tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \ if (pressure > 110) \ @@ -850,7 +860,7 @@ opl3_controller (int dev, int voice, int ctrl_num, int value) opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data); data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); /* KEYON|OCTAVE|MS bits - * of f-num */ + * of f-num */ voices[voice].keyon_byte = data; opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data); break; @@ -884,6 +894,7 @@ static struct synth_operations opl3_operations = opl3_aftertouch, opl3_controller, opl3_panning, + opl3_volume_method, opl3_patchmgr }; @@ -892,17 +903,26 @@ opl3_init (long mem_start) { int i; + PERMANENT_MALLOC (struct sbi_instrument *, instrmap, + SBFM_MAXINSTR * sizeof (*instrmap), mem_start); + synth_devs[num_synths++] = &opl3_operations; fm_model = 0; opl3_ok = 1; if (opl3_enabled) { +#ifdef __FreeBSD__ printk ("snd1: <Yamaha OPL-3 FM>"); +#else + printk (" <Yamaha OPL-3 FM>"); +#endif fm_model = 2; nr_voices = 18; fm_info.nr_drums = 0; fm_info.capabilities |= SYNTH_CAP_OPL3; +#ifndef SCO strcpy (fm_info.name, "Yamaha OPL-3"); +#endif for (i = 0; i < 18; i++) if (physical_voices[i].ioaddr == USE_LEFT) @@ -917,7 +937,11 @@ opl3_init (long mem_start) } else { +#ifdef __FreeBSD__ printk ("snd1: <Yamaha 2-OP FM>"); +#else + printk (" <Yamaha 2-OP FM>"); +#endif fm_model = 1; nr_voices = 9; fm_info.nr_drums = 0; diff --git a/sys/i386/isa/sound/os.h b/sys/i386/isa/sound/os.h index fba20980c3f0..c6b688ac6aa9 100644 --- a/sys/i386/isa/sound/os.h +++ b/sys/i386/isa/sound/os.h @@ -3,7 +3,7 @@ /* * OS specific settings for FreeBSD * - * Copyright by Hannu Savolainen 1993 + * Copyright by UWM - comments to soft-eng@cs.uwm.edu * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * This should be used as an example when porting the driver to a new + * This chould be used as an example when porting the driver to a new * operating systems. * * What you should do is to rewrite the soundcard.c and os.h (this file). @@ -44,7 +44,6 @@ #include "param.h" #include "systm.h" -#include "kernel.h" #include "ioctl.h" #include "tty.h" #include "proc.h" @@ -52,6 +51,7 @@ #include "conf.h" #include "file.h" #include "uio.h" +#include "kernel.h" #include "syslog.h" #include "errno.h" #include "malloc.h" @@ -63,10 +63,6 @@ */ #ifdef CONFIGURE_SOUNDCARD -/* lbolt is required by the FreeBSD version (only???) */ -extern int __timeout_val; -extern int __process_aborting; - /* * select() is currently implemented in Linux specific way. Don't enable. * I don't remember what the SHORT_BANNERS means so forget it. @@ -163,14 +159,25 @@ typedef struct uio snd_rw_buf; * The following macros define an interface to the process management. */ +struct snd_wait { + int mode; int aborting; + }; + /* * DEFINE_WAIT_QUEUE is used where a wait queue is required. It must define * a structure which can be passed as a parameter to a sleep(). The second * parameter is name of a flag variable (must be defined as int). */ -#define DEFINE_WAIT_QUEUE(qname, flag) static int *qname = NULL; static int flag = 0 +#define DEFINE_WAIT_QUEUE(qname, flag) static int *qname = NULL; \ + static volatile struct snd_wait flag = {0} /* Like the above but defines an array of wait queues and flags */ -#define DEFINE_WAIT_QUEUES(qname, flag) static int *qname = {NULL}; static int flag = {0} +#define DEFINE_WAIT_QUEUES(qname, flag) static int *qname = {NULL}; \ + static volatile struct snd_wait flag = {{0}} + +#define RESET_WAIT_QUEUE(q, f) {f.aborting = 0;f.mode = WK_NONE;} +#define SET_ABORT_FLAG(q, f) f.aborting = 1 +#define TIMED_OUT(q, f) (f.mode & WK_TIMEOUT) +#define SOMEONE_WAITING(q, f) (f.mode & WK_SLEEP) /* * This driver handles interrupts little bit nonstandard way. The following * macro is used to test if the current process has received a signal which @@ -179,34 +186,28 @@ typedef struct uio snd_rw_buf; * 1 or 0 could be returned (1 should be better than 0). * I'm not sure if the following is correct for FreeBSD. */ -#define PROCESS_ABORTING (__process_aborting | curproc->p_sig) -/* - * REQUEST_TIMEOUT is called before sleep. It shoud ensure that the - * process is woken up after given number of ticks (1/HZ secs.). - * The wqueue gives the wait queue. - */ -#define REQUEST_TIMEOUT(nticks, wqueue) __timeout_val = nticks; +#define PROCESS_ABORTING(q, f) (f.aborting | curproc->p_sig) /* * The following macro calls sleep. It should be implemented such that * the process is resumed if it receives a signal. The following is propably * not the way how it should be done on 386bsd. - * The on_what parameter is a wait_queue defined with DEFINE_WAIT_QUEUE() - * The second parameter is a flag. It must be initialized to 1 before sleep - * and to zero after proces continues. + * The on_what parameter is a wait_queue defined with DEFINE_WAIT_QUEUE(), + * and the second is a workarea parameter. The third is a timeout + * in ticks. Zero means no timeout. */ -#define INTERRUPTIBLE_SLEEP_ON(on_what, flag) \ +#define DO_SLEEP(q, f, time_limit) \ { \ - flag = 1; \ - flag=tsleep((caddr_t)&(on_what), (PRIBIO-5)|PCATCH, "sndint", __timeout_val); \ - if(flag == ERESTART) __process_aborting = 1;\ - else __process_aborting = 0;\ - __timeout_val = 0; \ - flag = 0; \ + int flag, chn; \ + f.mode = WK_SLEEP; \ + q = &chn; \ + flag=tsleep((caddr_t)&(chn), (PRIBIO-5)|PCATCH, "sndint", time_limit); \ + if(flag == ERESTART) f.aborting = 1;\ + else f.aborting = 0;\ + f.mode &= ~WK_SLEEP; \ } - /* An the following wakes up a process */ -#define WAKE_UP(who) wakeup((caddr_t)&(who)) +#define WAKE_UP(q, f) {f.mode = WK_WAKEUP;wakeup((caddr_t)q);} /* * Timing macros. This driver assumes that there is a timer running in the @@ -215,6 +216,7 @@ typedef struct uio snd_rw_buf; */ #ifndef HZ +extern int hz; #define HZ hz #endif @@ -223,9 +225,9 @@ typedef struct uio snd_rw_buf; * ticks. This can overflow, so the timeout might be real big... * */ +extern unsigned long get_time(void); #define GET_TIME() get_time() -extern long get_time(void); -/*#define GET_TIME() (lbolt)*/ /* Returns current time (1/HZ secs since boot) */ +/*#define GET_TIME() (lbolt) */ /* Returns current time (1/HZ secs since boot) */ /* * The following three macros are called before and after atomic @@ -248,7 +250,11 @@ extern long get_time(void); */ #define INB inb -#define OUTB(addr, data) outb(data, addr) +/* + * The outb(0, 0x80) is just for slowdown. It's bit unsafe since + * this address could be used for something usefull. + */ +#define OUTB(addr, data) {outb(data, addr);outb(0, 0x80);} /* memcpy() was not defined og 386bsd. Lets define it here */ #define memcpy(d, s, c) bcopy(s, d, c) @@ -274,6 +280,32 @@ extern long get_time(void); #define KERNEL_FREE(addr) free(addr, M_TEMP) /* + * The macro PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr) + * returns size bytes of + * (kernel virtual) memory which will never get freed by the driver. + * This macro is called only during boot. The linux_ptr is a linux specific + * parameter which should be ignored in other operating systems. + * The mem_ptr is a pointer variable where the macro assigns pointer to the + * memory area. The type is the type of the mem_ptr. + */ +#define PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr) \ + (mem_ptr) = (typecast)malloc((size), M_TEMP, M_WAITOK) + +/* + * The macro DEFINE_TIMER defines variables for the ACTIVATE_TIMER if + * required. The name is the variable/name to be used and the proc is + * the procedure to be called when the timer expires. + */ + +#define DEFINE_TIMER(name, proc) + +/* + * The ACTIVATE_TIMER requests system to call 'proc' after 'time' ticks. + */ + +#define ACTIVATE_TIMER(name, proc, time) \ + timeout((timeout_func_t)proc, 0, time); +/* * The rest of this file is not complete yet. The functions using these * macros will not work */ diff --git a/sys/i386/isa/sound/pas.h b/sys/i386/isa/sound/pas.h index 4dadea3713b8..55d83737b609 100644 --- a/sys/i386/isa/sound/pas.h +++ b/sys/i386/isa/sound/pas.h @@ -128,24 +128,26 @@ #define F_F_PCM_RATE_COUNTER 0x40 /* R W PCM 1=enable, 0=disable sample rate counter */ #define F_F_PCM_BUFFER_COUNTER 0x80 /* R W PCM 1=enable, 0=disable sample buffer counter */ +#define CHIP_REV 0xFF88 /* R 0=PAS, 1=PAS+, 2=CDPC, 3=PAS16C, 4=PAS16D */ #define PAS_NONE 0 #define PAS_PLUS 1 #define PAS_CDPC 2 #define PAS_16 3 +#define PAS_16D 4 #ifdef DEFINE_TRANSLATIONS char I_C_2_PCM_DMA_translate[] = /* R W PCM PCM DMA channel value translations */ { 4, 1, 2, 3, 0, 5, 6, 7 }; char I_C_3_PCM_IRQ_translate[] = /* R W PCM PCM IRQ level value translation */ - { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 }; + { 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 }; char E_C_MPU401_IRQ_translate[] = /* R W MIDI MPU401 emulation IRQ value translation */ - { 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x05, 0x06, 0x07 }; + { 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01, 0x05, 0x06, 0x07 }; char E_C_SB_IRQ_translate[] = /* R W PCM SB emulation IRQ translate */ - { 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x00, 0x28, 0x30, 0x38 }; + { 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 }; char E_C_SB_DMA_translate[] = /* R W PCM SB emulation DMA translate */ - { 0x00, 0x40, 0x80, 0xC0 }; + { 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 }; char O_M_1_to_card[] = /* R W Control Translate (OM1 & 0x0f) to card type */ - { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3, 0, 2, 3 }; + { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, 0, 2, 3 }; #else extern char I_C_2_PCM_DMA_translate[]; /* R W PCM PCM DMA channel value translations */ extern char I_C_3_PCM_IRQ_translate[]; /* R W PCM PCM IRQ level value translation */ diff --git a/sys/i386/isa/sound/pas2_card.c b/sys/i386/isa/sound/pas2_card.c index 96fe651fbaac..a8b79df2c8cd 100644 --- a/sys/i386/isa/sound/pas2_card.c +++ b/sys/i386/isa/sound/pas2_card.c @@ -1,12 +1,12 @@ #define _PAS2_CARD_C_ #define SND_SA_INTERRUPT /* - * linux/kernel/chr_drv/sound/pas2_card.c - * + * sound/pas2_card.c + * * Detection routine for the Pro Audio Spectrum cards. - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -14,7 +14,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -26,7 +26,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #include "sound_config.h" @@ -46,8 +46,9 @@ static int pas_intr_mask = 0; static int pas_irq = 0; static char pas_model; +static unsigned char board_rev_id; static char *pas_model_names[] = -{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16"}; +{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16", "Pro AudioSpectrum 16D"}; /* pas_read() and pas_write() are equivalents of INB() and OUTB() */ /* These routines perform the I/O address translation required */ @@ -65,6 +66,23 @@ pas_write (unsigned char data, int ioaddr) OUTB (data, ioaddr ^ translat_code); } +/* + * The Revision D cards have a problem with their MVA508 interface. The + * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and + * MSBs out of the output byte and to do a 16-bit out to the mixer port - + * 1. + */ + +void +mix_write (unsigned char data, int ioaddr) +{ + if (pas_model == PAS_16D) { + outw ((ioaddr ^ translat_code) - 1, data | (data << 8)); + outb (0, 0x80); + } else + OUTB (data, ioaddr ^ translat_code); +} + void pas2_msg (char *foo) { @@ -100,39 +118,6 @@ pasintr (int unused) } -static int -set_pas_irq (int interrupt_level) -{ -#ifdef linux - int retcode; - struct sigaction sa; - - pas_write (0xff, INTERRUPT_STATUS); /* Reset pending interrupts */ - - sa.sa_handler = pasintr; - -#ifdef SND_SA_INTERRUPT - sa.sa_flags = SA_INTERRUPT; -#else - sa.sa_flags = 0; -#endif - - sa.sa_mask = 0; - sa.sa_restorer = NULL; - - retcode = irqaction (interrupt_level, &sa); - - if (retcode < 0) - { - printk ("ProAudioSpectrum: IRQ%d already in use\n", interrupt_level); - } - return retcode; -#else - /* # error This routine does not work with this OS */ - return EINVAL; -#endif -} - int pas_set_intr (int mask) { @@ -143,7 +128,7 @@ pas_set_intr (int mask) if (!pas_intr_mask) { - if ((err = set_pas_irq (pas_irq)) < 0) + if ((err = snd_set_irq_handler (pas_irq, pasintr)) < 0) return err; } pas_intr_mask |= mask; @@ -163,7 +148,7 @@ pas_remove_intr (int mask) if (!pas_intr_mask) { - RELEASE_IRQ (pas_irq); + snd_release_irq (pas_irq); } return 0; } @@ -230,36 +215,66 @@ config_pas_hw (struct address_info *hw_config) } } + /* + * This fixes the timing problems of the PAS due to the Symphony chipset + * as per Media Vision. Only define this if your PAS doesn't work correctly. + */ +#ifdef SYMPHONY_PAS + OUTB (0x05, 0xa8); + OUTB (0x60, 0xa9); +#endif + #ifdef BROKEN_BUS_CLOCK pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND | S_C_1_FM_EMULATE_CLOCK, SYSTEM_CONFIGURATION_1); #else /* pas_write(S_C_1_PCS_ENABLE, SYSTEM_CONFIGURATION_1); */ pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND, SYSTEM_CONFIGURATION_1); #endif - /* pas_write(S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2); Don't do this */ pas_write (0x18, SYSTEM_CONFIGURATION_3); /* ??? */ pas_write (F_F_MIXER_UNMUTE | 0x01, FILTER_FREQUENCY); /* Sets mute off and * selects filter rate * of 17.897 kHz */ - if (pas_model == PAS_16) + if (pas_model == PAS_16 || pas_model == PAS_16D) pas_write (8, PRESCALE_DIVIDER); else pas_write (0, PRESCALE_DIVIDER); - pas_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER); - pas_write (5, PARALLEL_MIXER); + mix_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER); + mix_write (5, PARALLEL_MIXER); #if !defined(EXCLUDE_SB_EMULATION) || !defined(EXCLUDE_SB) - /* Turn on Sound Blaster compatibility */ - /* bit 1 = SB emulation */ - /* bit 0 = MPU401 emulation (CDPC only :-( ) */ - pas_write (0x02, COMPATIBILITY_ENABLE); + { + struct address_info *sb_config; + + if ((sb_config = sound_getconf (SNDCARD_SB))) + { + unsigned char irq_dma; - /* "Emulation address" */ - pas_write ((SBC_BASE >> 4) & 0x0f, EMULATION_ADDRESS); + /* Turn on Sound Blaster compatibility */ + /* bit 1 = SB emulation */ + /* bit 0 = MPU401 emulation (CDPC only :-( ) */ + pas_write (0x02, COMPATIBILITY_ENABLE); + + /* "Emulation address" */ + pas_write ((sb_config->io_base >> 4) & 0x0f, EMULATION_ADDRESS); + + if (!E_C_SB_DMA_translate[sb_config->dma]) + printk ("\n\nPAS16 Warning: Invalid SB DMA %d\n\n", + sb_config->dma); + + if (!E_C_SB_IRQ_translate[sb_config->irq]) + printk ("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n", + sb_config->irq); + + irq_dma = E_C_SB_DMA_translate[sb_config->dma] | + E_C_SB_IRQ_translate[sb_config->irq]; + + pas_write (irq_dma, EMULATION_CONFIGURATION); + } + } #endif if (!ok) @@ -305,7 +320,7 @@ detect_pas_hw (struct address_info *hw_config) if (board_id != foo) /* Not a PAS2 */ return 0; - if ((pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f])); + pas_model = pas_read (CHIP_REV); return pas_model; } @@ -318,9 +333,14 @@ attach_pas_card (long mem_start, struct address_info *hw_config) if (detect_pas_hw (hw_config)) { - if ((pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f])) + board_rev_id = pas_read (BOARD_REV_ID); + if (pas_model = pas_read (CHIP_REV)) { - printk ("snd3: <%s rev %d>", pas_model_names[(int) pas_model], pas_read (BOARD_REV_ID)); +#ifdef __FreeBSD__ + printk ("snd3: <%s rev %d>", pas_model_names[(int) pas_model], board_rev_id); +#else + printk (" <%s rev %d>", pas_model_names[(int) pas_model], board_rev_id); +#endif } if (config_pas_hw (hw_config)) @@ -330,11 +350,11 @@ attach_pas_card (long mem_start, struct address_info *hw_config) mem_start = pas_pcm_init (mem_start, hw_config); #endif -# if !defined(EXCLUDE_SB_EMULATION) && !defined(EXCLUDE_SB) +#if !defined(EXCLUDE_SB_EMULATION) && !defined(EXCLUDE_SB) sb_dsp_disable_midi (); /* The SB emulation don't support * midi */ -# endif +#endif #ifndef EXCLUDE_YM3812 enable_opl3_mode (0x388, 0x38a, 0); @@ -350,7 +370,6 @@ attach_pas_card (long mem_start, struct address_info *hw_config) } } - printk ("\n"); return mem_start; } diff --git a/sys/i386/isa/sound/pas2_midi.c b/sys/i386/isa/sound/pas2_midi.c index 1a605905420f..4a07b0b59bf8 100644 --- a/sys/i386/isa/sound/pas2_midi.c +++ b/sys/i386/isa/sound/pas2_midi.c @@ -1,10 +1,10 @@ /* - * linux/kernel/chr_drv/sound/pas2_midi.c - * + * sound/pas2_midi.c + * * The low level driver for the PAS Midi Interface. - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -12,7 +12,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -24,7 +24,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #include "sound_config.h" @@ -212,7 +212,7 @@ pas_buffer_status (int dev) static struct midi_operations pas_midi_operations = { - {"Pro Audio Spectrum", 0}, + {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS}, pas_midi_open, pas_midi_close, pas_midi_ioctl, @@ -283,7 +283,7 @@ pas_midi_interrupt (void) if (stat & M_S_OUTPUT_OVERRUN) { - printk ("MIDI output overrun %02x,%02x,%d \n", pas_read (MIDI_FIFO_STATUS), stat, ofifo_bytes); + printk ("MIDI output overrun %x,%x,%d \n", pas_read (MIDI_FIFO_STATUS), stat, ofifo_bytes); ofifo_bytes = 100; } diff --git a/sys/i386/isa/sound/pas2_mixer.c b/sys/i386/isa/sound/pas2_mixer.c index 33135d24e859..b3868773d64c 100644 --- a/sys/i386/isa/sound/pas2_mixer.c +++ b/sys/i386/isa/sound/pas2_mixer.c @@ -1,12 +1,12 @@ #define _PAS2_MIXER_C_ /* - * linux/kernel/chr_drv/sound/pas2_mixer.c - * + * sound/pas2_mixer.c + * * Mixer routines for the Pro Audio Spectrum cards. - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -14,7 +14,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -26,7 +26,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #include "sound_config.h" @@ -46,7 +46,7 @@ static int mode_control = 0; SOUND_MASK_CD | SOUND_MASK_ALTPCM) #define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD /*|SOUND_MASK_ALTPCM*/ | SOUND_MASK_IMIX | \ + SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \ SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV | \ SOUND_MASK_MUTE | SOUND_MASK_ENHANCE | SOUND_MASK_LOUD) @@ -72,14 +72,6 @@ mixer_output (int right_vol, int left_vol, int div, int bits, int left = left_vol * div / 100; int right = right_vol * div / 100; - /* - * The Revision D cards have a problem with their MVA508 interface. The - * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and - * MSBs out of the output byte and to do a 16-bit out to the mixer port - - * 1. We don't need to do this because the call to pas_write more than - * compensates for the timing problems. - */ - if (bits & P_M_MV508_MIXER) { /* Select input or output mixer */ left |= mixer; @@ -87,17 +79,17 @@ mixer_output (int right_vol, int left_vol, int div, int bits, } if (bits == P_M_MV508_BASS || bits == P_M_MV508_TREBLE) - { /* Bass and trebble are mono devices */ - pas_write (P_M_MV508_ADDRESS | bits, PARALLEL_MIXER); - pas_write (left, PARALLEL_MIXER); + { /* Bass and trebble are mono devices */ + mix_write (P_M_MV508_ADDRESS | bits, PARALLEL_MIXER); + mix_write (left, PARALLEL_MIXER); right_vol = left_vol; } else { - pas_write (P_M_MV508_ADDRESS | P_M_MV508_LEFT | bits, PARALLEL_MIXER); - pas_write (left, PARALLEL_MIXER); - pas_write (P_M_MV508_ADDRESS | P_M_MV508_RIGHT | bits, PARALLEL_MIXER); - pas_write (right, PARALLEL_MIXER); + mix_write (P_M_MV508_ADDRESS | P_M_MV508_LEFT | bits, PARALLEL_MIXER); + mix_write (left, PARALLEL_MIXER); + mix_write (P_M_MV508_ADDRESS | P_M_MV508_RIGHT | bits, PARALLEL_MIXER); + mix_write (right, PARALLEL_MIXER); } return (left_vol | (right_vol << 8)); @@ -106,8 +98,8 @@ mixer_output (int right_vol, int left_vol, int div, int bits, void set_mode (int new_mode) { - pas_write (P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER); - pas_write (new_mode, PARALLEL_MIXER); + mix_write (P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER); + mix_write (new_mode, PARALLEL_MIXER); mode_control = new_mode; } diff --git a/sys/i386/isa/sound/pas2_pcm.c b/sys/i386/isa/sound/pas2_pcm.c index 878de5aaca94..b6d53fbccd01 100644 --- a/sys/i386/isa/sound/pas2_pcm.c +++ b/sys/i386/isa/sound/pas2_pcm.c @@ -1,11 +1,11 @@ #define _PAS2_PCM_C_ /* - * linux/kernel/chr_drv/sound/pas2_pcm.c - * + * sound/pas2_pcm.c + * * The low level driver for the Pro Audio Spectrum ADC/DAC. - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -13,7 +13,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -25,7 +25,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #include "sound_config.h" @@ -75,6 +75,31 @@ pcm_set_speed (int arg) tmp = pas_read (FILTER_FREQUENCY); +/* + * Set anti-aliasing filters according to sample rate. You reall *NEED* + * to enable this feature for all normal recording unless you want to + * experiment with aliasing effects. + * These filters apply to the selected "recording" source. + * I (pfw) don't know the encoding of these 5 bits. The values shown + * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/. +*/ +#if !defined NO_AUTO_FILTER_SET + tmp &= 0xe0; + if(pcm_speed >= 2*17897) + tmp |= 0x21; + else if(pcm_speed >= 2*15909) + tmp |= 0x22; + else if(pcm_speed >= 2*11931) + tmp |= 0x29; + else if(pcm_speed >= 2*8948) + tmp |= 0x31; + else if(pcm_speed >= 2*5965) + tmp |= 0x39; + else if(pcm_speed >= 2*2982) + tmp |= 0x24; + pcm_filter = tmp; +#endif + DISABLE_INTR (flags); pas_write (tmp & ~(F_F_PCM_RATE_COUNTER | F_F_PCM_BUFFER_COUNTER), FILTER_FREQUENCY); @@ -148,6 +173,8 @@ pas_pcm_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) break; case SOUND_PCM_WRITE_CHANNELS: + if (local) + return pcm_set_channels (arg); return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg))); break; @@ -200,12 +227,6 @@ pas_pcm_open (int dev, int mode) TRACE (printk ("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode)); - if (mode != OPEN_READ && mode != OPEN_WRITE) - { - printk ("PAS2: Attempt to open PCM device for simultaneous read and write"); - return RET_ERROR (EINVAL); - } - if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0) return err; @@ -217,10 +238,6 @@ pas_pcm_open (int dev, int mode) pcm_count = 0; - pcm_set_bits (8); - pcm_set_channels (1); - pcm_set_speed (DSP_DEFAULT_SPEED); - return 0; } @@ -242,7 +259,8 @@ pas_pcm_close (int dev) } static void -pas_pcm_output_block (int dev, unsigned long buf, int count, int intrflag) +pas_pcm_output_block (int dev, unsigned long buf, int count, + int intrflag, int restart_dma) { unsigned long flags, cnt; @@ -251,7 +269,6 @@ pas_pcm_output_block (int dev, unsigned long buf, int count, int intrflag) cnt = count; if (sound_dsp_dmachan[dev] > 3) cnt >>= 1; - cnt--; if (sound_dma_automode[dev] && intrflag && @@ -263,11 +280,11 @@ pas_pcm_output_block (int dev, unsigned long buf, int count, int intrflag) pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL); - DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); + if (restart_dma) + DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); if (sound_dsp_dmachan[dev] > 3) count >>= 1; - count--; if (count != pcm_count) { @@ -288,7 +305,8 @@ pas_pcm_output_block (int dev, unsigned long buf, int count, int intrflag) } static void -pas_pcm_start_input (int dev, unsigned long buf, int count, int intrflag) +pas_pcm_start_input (int dev, unsigned long buf, int count, + int intrflag, int restart_dma) { unsigned long flags; int cnt; @@ -298,7 +316,6 @@ pas_pcm_start_input (int dev, unsigned long buf, int count, int intrflag) cnt = count; if (sound_dsp_dmachan[dev] > 3) cnt >>= 1; - cnt--; if (sound_dma_automode[my_devnum] && intrflag && @@ -307,13 +324,12 @@ pas_pcm_start_input (int dev, unsigned long buf, int count, int intrflag) DISABLE_INTR (flags); - DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); + if (restart_dma) + DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); if (sound_dsp_dmachan[dev] > 3) count >>= 1; - count--; - if (count != pcm_count) { pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); @@ -346,14 +362,15 @@ pas_pcm_prepare_for_output (int dev, int bsize, int bcount) static struct audio_operations pas_pcm_operations = { "Pro Audio Spectrum", - pas_pcm_open, /* */ - pas_pcm_close, /* */ - pas_pcm_output_block, /* */ - pas_pcm_start_input, /* */ - pas_pcm_ioctl, /* */ - pas_pcm_prepare_for_input, /* */ - pas_pcm_prepare_for_output, /* */ - pas_pcm_reset, /* */ + NOTHING_SPECIAL, + pas_pcm_open, + pas_pcm_close, + pas_pcm_output_block, + pas_pcm_start_input, + pas_pcm_ioctl, + pas_pcm_prepare_for_input, + pas_pcm_prepare_for_output, + pas_pcm_reset, pas_pcm_reset, /* halt_xfer */ NULL, /* has_output_drained */ NULL /* copy_from_user */ @@ -374,6 +391,7 @@ pas_pcm_init (long mem_start, struct address_info *hw_config) { dsp_devs[my_devnum = num_dspdevs++] = &pas_pcm_operations; sound_dsp_dmachan[my_devnum] = hw_config->dma; +#ifndef NO_AUTODMA if (hw_config->dma > 3) { sound_buffcounts[my_devnum] = 1; @@ -386,6 +404,11 @@ pas_pcm_init (long mem_start, struct address_info *hw_config) sound_buffsizes[my_devnum] = DSP_BUFFSIZE; sound_dma_automode[my_devnum] = 1; } +#else + sound_buffcounts[my_devnum] = DSP_BUFFCOUNT; + sound_buffsizes[my_devnum] = DSP_BUFFSIZE; + sound_dma_automode[my_devnum] = 0; +#endif } else printk ("PAS2: Too many PCM devices available\n"); @@ -413,7 +436,7 @@ pas_pcm_interrupt (unsigned char status, int cause) { case PCM_DAC: - DMAbuf_outputintr (my_devnum); + DMAbuf_outputintr (my_devnum, 1); break; case PCM_ADC: diff --git a/sys/i386/isa/sound/patmgr.c b/sys/i386/isa/sound/patmgr.c index 140a42845e5b..042d42d4067c 100644 --- a/sys/i386/isa/sound/patmgr.c +++ b/sys/i386/isa/sound/patmgr.c @@ -1,10 +1,10 @@ /* - * linux/kernel/chr_drv/sound/patmgr.c - * + * sound/patmgr.c + * * The patch maneger interface for the /dev/sequencer - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -12,7 +12,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -24,7 +24,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #define PATMGR_C @@ -58,6 +58,8 @@ pmgr_open (int dev) return RET_ERROR (EBUSY); pmgr_opened[dev] = 1; + RESET_WAIT_QUEUE (server_procs[dev], server_wait_flag[dev]); + return 0; } @@ -71,8 +73,8 @@ pmgr_release (int dev) mbox[dev]->key = PM_ERROR; mbox[dev]->parm1 = RET_ERROR (EIO); - if (appl_wait_flag) - WAKE_UP (appl_proc); + if (SOMEONE_WAITING (appl_proc, appl_wait_flag)) + WAKE_UP (appl_proc, appl_wait_flag); } pmgr_opened[dev] = 0; @@ -90,13 +92,14 @@ pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) return RET_ERROR (EIO); } - while (!ok && !PROCESS_ABORTING) + while (!ok && !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev])) { DISABLE_INTR (flags); - while (!(mbox[dev] && msg_direction[dev] == A_TO_S) && !PROCESS_ABORTING) + while (!(mbox[dev] && msg_direction[dev] == A_TO_S) && + !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev])) { - INTERRUPTIBLE_SLEEP_ON (server_procs[dev], server_wait_flag[dev]); + DO_SLEEP (server_procs[dev], server_wait_flag[dev], 0); } if (mbox[dev] && msg_direction[dev] == A_TO_S) @@ -158,9 +161,9 @@ pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) COPY_FROM_USER (&((char *) mbox[dev])[4], buf, 4, count - 4); msg_direction[dev] = S_TO_A; - if (appl_wait_flag) + if (SOMEONE_WAITING (appl_proc, appl_wait_flag)) { - WAKE_UP (appl_proc); + WAKE_UP (appl_proc, appl_wait_flag); } } @@ -185,12 +188,12 @@ pmgr_access (int dev, struct patmgr_info *rec) mbox[dev] = rec; msg_direction[dev] = A_TO_S; - if (server_wait_flag[dev]) + if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev])) { - WAKE_UP (server_procs[dev]); + WAKE_UP (server_procs[dev], server_wait_flag[dev]); } - INTERRUPTIBLE_SLEEP_ON (appl_proc, appl_wait_flag); + DO_SLEEP (appl_proc, appl_wait_flag, 0); if (msg_direction[dev] != S_TO_A) { @@ -239,12 +242,12 @@ pmgr_inform (int dev, int event, unsigned long p1, unsigned long p2, mbox[dev]->parm3 = p3; msg_direction[dev] = A_TO_S; - if (server_wait_flag[dev]) + if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev])) { - WAKE_UP (server_procs[dev]); + WAKE_UP (server_procs[dev], server_wait_flag[dev]); } - INTERRUPTIBLE_SLEEP_ON (appl_proc, appl_wait_flag); + DO_SLEEP (appl_proc, appl_wait_flag, 0); if (mbox[dev]) KERNEL_FREE (mbox[dev]); mbox[dev] = NULL; diff --git a/sys/i386/isa/sound/pro_midi.c b/sys/i386/isa/sound/pro_midi.c index 606657d403da..b9ffa26a9ab2 100644 --- a/sys/i386/isa/sound/pro_midi.c +++ b/sys/i386/isa/sound/pro_midi.c @@ -1,5 +1,5 @@ /* - * Copyright by UWM -- comments to soft-eng@cs.uwm.edu + * Copyright by UWM - comments to soft-eng@cs.uwm.edu * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,140 +37,148 @@ /** Structure for handling operations **/ -static struct generic_midi_operations pro_midi_operations = { +static struct generic_midi_operations pro_midi_operations = +{ - {"Pro_Audio_Spectrum 16 MV101", 0}, - pro_midi_open, - pro_midi_close, - pro_midi_write, - pro_midi_read + {"Pro_Audio_Spectrum 16 MV101", 0}, + pro_midi_open, + pro_midi_close, + pro_midi_write, + pro_midi_read }; /* - * Note! Note! Note! - * Follow the same model for any other attach function you + * Note! Note! Note! Follow the same model for any other attach function you * may write */ -long pro_midi_attach( long mem_start) +long +pro_midi_attach (long mem_start) { pro_midi_dev = num_generic_midis; generic_midi_devs[num_generic_midis++] = &pro_midi_operations; return mem_start; -} +} -int pro_midi_open(int dev, int mode) +int +pro_midi_open (int dev, int mode) { - int intr_mask, s; + int intr_mask, s; - s = splhigh(); + s = splhigh (); - /* Reset the input and output FIFO pointers */ + /* Reset the input and output FIFO pointers */ - outb(MIDI_CONTROL,M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO); + outb (MIDI_CONTROL, M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO); - /* Get the interrupt status */ + /* Get the interrupt status */ - intr_mask = inb(INTERRUPT_MASK); + intr_mask = inb (INTERRUPT_MASK); - /* Enable MIDI IRQ */ + /* Enable MIDI IRQ */ - intr_mask |= I_M_MIDI_IRQ_ENABLE; - outb(INTERRUPT_MASK, intr_mask); + intr_mask |= I_M_MIDI_IRQ_ENABLE; + outb (INTERRUPT_MASK, intr_mask); /* Enable READ/WRITE on MIDI port. This part is quite unsure though */ - outb(MIDI_CONTROL,M_C_ENA_OUTPUT_IRQ | M_C_ENA_INPUT_IRQ); + outb (MIDI_CONTROL, M_C_ENA_OUTPUT_IRQ | M_C_ENA_INPUT_IRQ); /* Acknowledge pending interrupts */ - outb(MIDI_STATUS,0xff); + outb (MIDI_STATUS, 0xff); - splx(s); + splx (s); - return(ESUCCESS); + return (ESUCCESS); } -void pro_midi_close(int dev) +void +pro_midi_close (int dev) { - int intr_mask; + int intr_mask; - /* Clean up */ + /* Clean up */ - outb(MIDI_CONTROL,M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO); - intr_mask = inb(INTERRUPT_MASK); - intr_mask &= ~I_M_MIDI_IRQ_ENABLE; - outb(INTERRUPT_MASK,intr_mask); + outb (MIDI_CONTROL, M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO); + intr_mask = inb (INTERRUPT_MASK); + intr_mask &= ~I_M_MIDI_IRQ_ENABLE; + outb (INTERRUPT_MASK, intr_mask); - return; + return; } -int pro_midi_write(int dev, struct uio *uio) +int +pro_midi_write (int dev, struct uio *uio) { - int s; - unsigned char data; + int s; + unsigned char data; - /* printf("midi: Going to do write routine..\n"); */ - while(uio->uio_resid) { + /* printf("midi: Going to do write routine..\n"); */ + while (uio->uio_resid) + { - if ( uiomove(&data,1,uio) ) return(ENOTTY); + if (uiomove (&data, 1, uio)) + return (ENOTTY); - s = splhigh(); + s = splhigh (); - DELAY(30); - outb(MIDI_DATA,data); - DELAY(70); /* Ze best pause.. find a better one if - * you can :) - */ - splx(s); - } + DELAY (30); + outb (MIDI_DATA, data); + DELAY (70); /* Ze best pause.. find a better one if you + * can :) */ + splx (s); + } - return(ESUCCESS); + return (ESUCCESS); } -int pro_midi_read(int dev, struct uio *uio) +int +pro_midi_read (int dev, struct uio *uio) { - int s; - unsigned char data; + int s; + unsigned char data; - s = splhigh(); + s = splhigh (); - /* For each uio_iov[] entry .... */ + /* For each uio_iov[] entry .... */ - while (uio->uio_resid) { + while (uio->uio_resid) + { - if((( inb(MIDI_STATUS) & M_S_INPUT_AVAIL) == 0 ) && - ((inb(MIDI_FIFO_STATUS) & MIDI_INPUT_AVAILABLE) == 0 ) ) + if (((inb (MIDI_STATUS) & M_S_INPUT_AVAIL) == 0) && + ((inb (MIDI_FIFO_STATUS) & MIDI_INPUT_AVAILABLE) == 0)) - data = 0xfe; - else - data = inb(MIDI_DATA); + data = 0xfe; + else + data = inb (MIDI_DATA); - if ( uiomove(&data, 1 , uio)) { + if (uiomove (&data, 1, uio)) + { - printf("midi: Bad copyout()!\n"); - return(ENOTTY); + printf ("midi: Bad copyout()!\n"); + return (ENOTTY); - } + } - } - splx(s); - return(ESUCCESS); + } + splx (s); + return (ESUCCESS); } diff --git a/sys/i386/isa/sound/sb.h b/sys/i386/isa/sound/sb.h new file mode 100644 index 000000000000..bb8ae12d7e60 --- /dev/null +++ b/sys/i386/isa/sound/sb.h @@ -0,0 +1,28 @@ +#define DSP_RESET (sbc_base + 0x6) +#define DSP_READ (sbc_base + 0xA) +#define DSP_WRITE (sbc_base + 0xC) +#define DSP_COMMAND (sbc_base + 0xC) +#define DSP_STATUS (sbc_base + 0xC) +#define DSP_DATA_AVAIL (sbc_base + 0xE) +#define DSP_DATA_AVL16 (sbc_base + 0xF) +#define MIXER_ADDR (sbc_base + 0x4) +#define MIXER_DATA (sbc_base + 0x5) +#define OPL3_LEFT (sbc_base + 0x0) +#define OPL3_RIGHT (sbc_base + 0x2) +#define OPL3_BOTH (sbc_base + 0x8) +/* DSP Commands */ + +#define DSP_CMD_SPKON 0xD1 +#define DSP_CMD_SPKOFF 0xD3 +#define DSP_CMD_DMAON 0xD0 +#define DSP_CMD_DMAOFF 0xD4 + +#define IMODE_NONE 0 +#define IMODE_OUTPUT 1 +#define IMODE_INPUT 2 +#define IMODE_INIT 3 +#define IMODE_MIDI 4 + +#define NORMAL_MIDI 0 +#define UART_MIDI 1 + diff --git a/sys/i386/isa/sound/sb16_dsp.c b/sys/i386/isa/sound/sb16_dsp.c new file mode 100644 index 000000000000..b545f8cac222 --- /dev/null +++ b/sys/i386/isa/sound/sb16_dsp.c @@ -0,0 +1,627 @@ +/* + * sound/sb16_dsp.c + * + * The low level driver for the SoundBlaster DSP chip. + * + * (C) 1993 J. Schubert (jsb@sth.ruhr-uni-bochum.de) + * + * based on SB-driver by (C) Hannu Savolainen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define DEB(x) +#define DEB1(x) +/* + #define DEB_DMARES + */ +#include "sound_config.h" +#include "sb.h" +#include "sb_mixer.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_AUDIO) && !defined(EXCLUDE_SBPRO) + +extern int sbc_base, sbc_minor, sbc_major; + +static int sb16_dsp_ok = 0;/* Set to 1 after successful initialization */ +static int dsp_16bit = 0; +static int dsp_stereo = 0; +static int dsp_current_speed = 8000; /*DSP_DEFAULT_SPEED; */ +static int dsp_busy = 0; +static int dma16, dma8; +static unsigned long dsp_count = 0; + +static int irq_mode = IMODE_NONE; /* IMODE_INPUT, IMODE_OUTPUT or + + IMODE_NONE */ +static int my_dev = 0; + +static volatile int intr_active = 0; + +static int sb16_dsp_open (int dev, int mode); +static void sb16_dsp_close (int dev); +static void sb16_dsp_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart); +static void sb16_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart); +static int sb16_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local); +static int sb16_dsp_prepare_for_input (int dev, int bsize, int bcount); +static int sb16_dsp_prepare_for_output (int dev, int bsize, int bcount); +static void sb16_dsp_reset (int dev); +static void sb16_dsp_halt (int dev); +static int dsp_set_speed (int); +static int dsp_set_stereo (int); +static void dsp_cleanup (void); +int sb_reset_dsp (void); + +static struct audio_operations sb16_dsp_operations = +{ + "SoundBlaster 16", + NOTHING_SPECIAL, + sb16_dsp_open, + sb16_dsp_close, + sb16_dsp_output_block, + sb16_dsp_start_input, + sb16_dsp_ioctl, + sb16_dsp_prepare_for_input, + sb16_dsp_prepare_for_output, + sb16_dsp_reset, + sb16_dsp_halt, + NULL, + NULL +}; + +static int +sb_dsp_command01 (unsigned char val) +{ + int i = 1 << 16; + + while (--i & (!INB (DSP_STATUS) & 0x80)); + if (!i) + printk ("SB16 sb_dsp_command01 Timeout\n"); + return sb_dsp_command (val); +} + +static int +wait_data_avail (unsigned long t) +{ + int loopc = 5000000; + + t += GET_TIME (); + do + { + if (INB (DSP_DATA_AVAIL) & 0x80) + return 1; + } + while (--loopc && GET_TIME () < t); + printk ("!data_avail l=%d\n", loopc); + return 0; +} + +static int +read_dsp (int t) +{ + if (!wait_data_avail ((unsigned long) t)) + return -1; + else + return INB (DSP_READ); +} + +static int +dsp_ini2 (void) +{ +#if 0 + /* sb_setmixer(0x83, sb_getmixer(0x83) | 0x03); */ + sb_dsp_command (0xe2); + sb_dsp_command (0x76); /* E0 ??? */ + sb_dsp_command (0xe2); + sb_dsp_command (0x30); /* A0 ??? */ + sb_dsp_command (0xe4); + sb_dsp_command (0xaa); + sb_dsp_command (0xe8); + if (read_dsp (100) != 0xaa) + printk ("Error dsp_ini2\n"); +#endif + return 0; +} + +/* + static char *dsp_getmessage(unsigned char command,int maxn) + { + static char buff[100]; + int n=0; + + sb_dsp_command(command); + while(n<maxn && wait_data_avail(2L)) { + buff[++n]=INB(DSP_READ); + if(!buff[n]) + break; + } + buff[0]=n; + return buff; + } + + static void dsp_showmessage(unsigned char command,int len) + { + int n; + unsigned char *c; + c=dsp_getmessage(command,len); + printk("DSP C=%x l=%d,lr=%d b=",command,len,c[0]); + for(n=1;n<=c[0];n++) + if(c[n]>=' ' & c[n]<='z') + printk("%c",c[n]); + else + printk("|%x|",c[n]); + printk("\n"); + } + */ +static int +dsp_set_speed (int mode) +{ + DEB (printk ("dsp_set_speed(%d)\n", mode)); + if (mode) + { + if (mode < 5000) + mode = 5000; + if (mode > 44100) + mode = 44100; + dsp_current_speed = mode; + } + return mode; +} + +static int +dsp_set_stereo (int mode) +{ + DEB (printk ("dsp_set_stereo(%d)\n", mode)); + + dsp_stereo = mode; + + return mode; +} + +static int +dsp_set_bits (int arg) +{ + DEB (printk ("dsp_set_bits(%d)\n", arg)); + + if (arg) + switch (arg) + { + case 8: + dsp_16bit = 0; + break; + case 16: + dsp_16bit = 1; + break; + default: + return RET_ERROR (EINVAL); + } + return dsp_16bit ? 16 : 8; +} + +static int +sb16_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +{ + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (local) + return dsp_set_speed (arg); + return IOCTL_OUT (arg, dsp_set_speed (IOCTL_IN (arg))); + + case SOUND_PCM_READ_RATE: + if (local) + return dsp_current_speed; + return IOCTL_OUT (arg, dsp_current_speed); + + case SNDCTL_DSP_STEREO: + if (local) + return dsp_set_stereo (arg); + return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg))); + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return dsp_set_stereo (arg - 1) + 1; + return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1); + + case SOUND_PCM_READ_CHANNELS: + if (local) + return dsp_stereo + 1; + return IOCTL_OUT (arg, dsp_stereo + 1); + + case SNDCTL_DSP_SAMPLESIZE: + if (local) + return dsp_set_bits (arg); + return IOCTL_OUT (arg, dsp_set_bits (IOCTL_IN (arg))); + + case SOUND_PCM_READ_BITS: + if (local) + return dsp_16bit ? 16 : 8; + return IOCTL_OUT (arg, dsp_16bit ? 16 : 8); + + case SOUND_PCM_WRITE_FILTER: /* NOT YET IMPLEMENTED */ + if (IOCTL_IN (arg) > 1) + return IOCTL_OUT (arg, RET_ERROR (EINVAL)); + default: + return RET_ERROR (EINVAL); + } + + return RET_ERROR (EINVAL); +} + +static int +sb16_dsp_open (int dev, int mode) +{ + int retval; + + DEB (printk ("sb16_dsp_open()\n")); + if (!sb16_dsp_ok) + { + printk ("SB16 Error: SoundBlaster board not installed\n"); + return RET_ERROR (ENXIO); + } + + if (intr_active) + return RET_ERROR (EBUSY); + + retval = sb_get_irq (); + if (retval < 0) + return retval; + + if (ALLOC_DMA_CHN (dma8)) + { + printk ("SB16: Unable to grab DMA%d\n", dma8); + sb_free_irq (); + return RET_ERROR (EBUSY); + } + + if (dma16 != dma8) + if (ALLOC_DMA_CHN (dma16)) + { + printk ("SB16: Unable to grab DMA%d\n", dma16); + sb_free_irq (); + RELEASE_DMA_CHN (dma8); + return RET_ERROR (EBUSY); + } + + dsp_ini2 (); + + irq_mode = IMODE_NONE; + dsp_busy = 1; + + return 0; +} + +static void +sb16_dsp_close (int dev) +{ + unsigned long flags; + + DEB (printk ("sb16_dsp_close()\n")); + sb_dsp_command01 (0xd9); + sb_dsp_command01 (0xd5); + + DISABLE_INTR (flags); + RELEASE_DMA_CHN (dma8); + + if (dma16 != dma8) + RELEASE_DMA_CHN (dma16); + sb_free_irq (); + dsp_cleanup (); + dsp_busy = 0; + RESTORE_INTR (flags); +} + +static void +sb16_dsp_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart) +{ + unsigned long flags, cnt; + + cnt = count; + if (dsp_16bit) + cnt >>= 1; + cnt--; + +#ifdef DEB_DMARES + printk ("output_block: %x %d %d\n", buf, count, intrflag); + if (intrflag) + { + int pos, chan = sound_dsp_dmachan[dev]; + + DISABLE_INTR (flags); + clear_dma_ff (chan); + disable_dma (chan); + pos = get_dma_residue (chan); + enable_dma (chan); + RESTORE_INTR (flags); + printk ("dmapos=%d %x\n", pos, pos); + } +#endif + if (sound_dma_automode[dev] && + intrflag && + cnt == dsp_count) + { + irq_mode = IMODE_OUTPUT; + intr_active = 1; + return; /* Auto mode on. No need to react */ + } + DISABLE_INTR (flags); + + if (dma_restart) + { + sb16_dsp_halt (dev); + DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); + } + sb_dsp_command (0x41); + sb_dsp_command ((unsigned char) ((dsp_current_speed >> 8) & 0xff)); + sb_dsp_command ((unsigned char) (dsp_current_speed & 0xff)); + sb_dsp_command ((unsigned char) (dsp_16bit ? 0xb6 : 0xc6)); + sb_dsp_command ((unsigned char) ((dsp_stereo ? 0x20 : 0) + + (dsp_16bit ? 0x10 : 0))); + sb_dsp_command01 ((unsigned char) (cnt & 0xff)); + sb_dsp_command ((unsigned char) (cnt >> 8)); + /* sb_dsp_command (0); + sb_dsp_command (0); */ + + RESTORE_INTR (flags); + dsp_count = cnt; + irq_mode = IMODE_OUTPUT; + intr_active = 1; +} + +static void +sb16_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart) +{ + unsigned long flags, cnt; + + cnt = count; + if (dsp_16bit) + cnt >>= 1; + cnt--; + +#ifdef DEB_DMARES + printk ("start_input: %x %d %d\n", buf, count, intrflag); + if (intrflag) + { + int pos, chan = sound_dsp_dmachan[dev]; + + DISABLE_INTR (flags); + clear_dma_ff (chan); + disable_dma (chan); + pos = get_dma_residue (chan); + enable_dma (chan); + RESTORE_INTR (flags); + printk ("dmapos=%d %x\n", pos, pos); + } +#endif + if (sound_dma_automode[dev] && + intrflag && + cnt == dsp_count) + { + irq_mode = IMODE_INPUT; + intr_active = 1; + return; /* Auto mode on. No need to react */ + } + DISABLE_INTR (flags); + + if (dma_restart) + { + sb16_dsp_halt (dev); + DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); + } + + sb_dsp_command (0x42); + sb_dsp_command ((unsigned char) ((dsp_current_speed >> 8) & 0xff)); + sb_dsp_command ((unsigned char) (dsp_current_speed & 0xff)); + sb_dsp_command ((unsigned char) (dsp_16bit ? 0xbe : 0xce)); + sb_dsp_command ((unsigned char) ((dsp_stereo ? 0x20 : 0) + + (dsp_16bit ? 0x10 : 0))); + sb_dsp_command01 ((unsigned char) (cnt & 0xff)); + sb_dsp_command ((unsigned char) (cnt >> 8)); + + /* sb_dsp_command (0); + sb_dsp_command (0); */ + RESTORE_INTR (flags); + dsp_count = cnt; + irq_mode = IMODE_INPUT; + intr_active = 1; +} + +static int +sb16_dsp_prepare_for_input (int dev, int bsize, int bcount) +{ + sound_dsp_dmachan[my_dev] = dsp_16bit ? dma16 : dma8; + dsp_count = 0; + dsp_cleanup (); + return 0; +} + +static int +sb16_dsp_prepare_for_output (int dev, int bsize, int bcount) +{ + sound_dsp_dmachan[my_dev] = dsp_16bit ? dma16 : dma8; + dsp_count = 0; + dsp_cleanup (); + return 0; +} + +static void +dsp_cleanup (void) +{ + irq_mode = IMODE_NONE; + intr_active = 0; +} + +static void +sb16_dsp_reset (int dev) +{ + unsigned long flags; + + DISABLE_INTR (flags); + + sb_reset_dsp (); + dsp_cleanup (); + + RESTORE_INTR (flags); +} + +static void +sb16_dsp_halt (int dev) +{ + if (dsp_16bit) + { + sb_dsp_command01 (0xd9); + sb_dsp_command01 (0xd5); + } + else + { + sb_dsp_command01 (0xda); + sb_dsp_command01 (0xd0); + } +} + +static void +set_irq_hw (int level) +{ + int ival; + + switch (level) + { + case 5: + ival = 2; + break; + case 7: + ival = 4; + break; + case 10: + ival = 8; + break; + default: + printk ("SB16_IRQ_LEVEL %d does not exist\n", level); + return; + } + sb_setmixer (IRQ_NR, ival); +} + +long +sb16_dsp_init (long mem_start, struct address_info *hw_config) +{ + if (sbc_major < 4) + return mem_start; + +#ifndef SCO + sprintf (sb16_dsp_operations.name, "SoundBlaster 16 %d.%d", sbc_major, sbc_minor); +#endif + +#ifdef __FreeBSD__ + printk ("snd6: <%s>", sb16_dsp_operations.name); +#else + printk (" <%s>", sb16_dsp_operations.name); +#endif + + if (num_dspdevs < MAX_DSP_DEV) + { + dsp_devs[my_dev = num_dspdevs++] = &sb16_dsp_operations; + sound_dsp_dmachan[my_dev] = hw_config->dma; +#ifndef NO_AUTODMA + sound_buffcounts[my_dev] = 1; + sound_dma_automode[my_dev] = 1; +#else + sound_buffcounts[my_dev] = DSP_BUFFCOUNT; + sound_dma_automode[my_dev] = 0; +#endif + sound_buffsizes[my_dev] = DSP_BUFFSIZE; + } + else + printk ("SB: Too many DSP devices available\n"); + sb16_dsp_ok = 1; + return mem_start; +} + +int +sb16_dsp_detect (struct address_info *hw_config) +{ + struct address_info *sb_config; + + if (sb16_dsp_ok) + return 1; /* Already initialized */ + + if (!(sb_config = sound_getconf (SNDCARD_SB))) + { + printk ("SB16 Error: Plain SB not configured\n"); + return 0; + } + + /* sb_setmixer(OPSW,0xf); + if(sb_getmixer(OPSW)!=0xf) + return 0; */ + + if (!sb_reset_dsp ()) + return 0; + + if (hw_config->dma < 4) + if (hw_config->dma != sb_config->dma) + { + printk ("SB16 Error: Invalid DMA channel %d/%d\n", + sb_config->dma, hw_config->dma); + return 0; + } + + dma16 = hw_config->dma; + dma8 = sb_config->dma; + set_irq_hw (sb_config->irq); + sb_setmixer (DMA_NR, (1 << hw_config->dma) | (1 << sb_config->dma)); + + DEB (printk ("SoundBlaster 16: IRQ %d DMA %d OK\n", sb_config->irq, hw_config->dma)); + + /* + dsp_showmessage(0xe3,99); + */ + sb16_dsp_ok = 1; + return 1; +} + +void +sb16_dsp_interrupt (int unused) +{ + int data; + + data = INB (DSP_DATA_AVL16); /* Interrupt acknowledge */ + + if (intr_active) + switch (irq_mode) + { + case IMODE_OUTPUT: + intr_active = 0; + DMAbuf_outputintr (my_dev, 1); + break; + + case IMODE_INPUT: + intr_active = 0; + DMAbuf_inputintr (my_dev); + break; + + default: + printk ("SoundBlaster: Unexpected interrupt\n"); + } +} + +#endif diff --git a/sys/i386/isa/sound/sb16_midi.c b/sys/i386/isa/sound/sb16_midi.c new file mode 100644 index 000000000000..39808c80fe0d --- /dev/null +++ b/sys/i386/isa/sound/sb16_midi.c @@ -0,0 +1,287 @@ +/* + * sound/sb16_midi.c + * + * The low level driver for the MPU-401 UART emulation of the SB16. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_MIDI) + +#define DATAPORT (sb16midi_base) /* MPU-401 Data I/O Port on IBM */ +#define COMDPORT (sb16midi_base+1) /* MPU-401 Command Port on IBM */ +#define STATPORT (sb16midi_base+1) /* MPU-401 Status Port on IBM */ + +#define sb16midi_status() INB(STATPORT) +#define input_avail() (!(sb16midi_status()&INPUT_AVAIL)) +#define output_ready() (!(sb16midi_status()&OUTPUT_READY)) +#define sb16midi_cmd(cmd) OUTB(cmd, COMDPORT) +#define sb16midi_read() INB(DATAPORT) +#define sb16midi_write(byte) OUTB(byte, DATAPORT) + +#define OUTPUT_READY 0x40 /* Mask for Data Read Redy Bit */ +#define INPUT_AVAIL 0x80 /* Mask for Data Send Ready Bit */ +#define MPU_ACK 0xFE /* MPU-401 Acknowledge Response */ +#define MPU_RESET 0xFF /* MPU-401 Total Reset Command */ +#define UART_MODE_ON 0x3F /* MPU-401 "Dumb UART Mode" */ + +static int sb16midi_opened = 0; +static int sb16midi_base = 0x330; +static int sb16midi_detected = 0; +static int my_dev; + +static int reset_sb16midi (void); +static void (*midi_input_intr) (int dev, unsigned char data); + +extern int sbc_major; + +static void +sb16midi_input_loop (void) +{ + + while (input_avail ()) + { + unsigned char c = sb16midi_read (); + + if (sb16midi_opened & OPEN_READ) + midi_input_intr (my_dev, c); + } +} + +void +sb16midiintr (int unit) +{ + if (input_avail ()) + sb16midi_input_loop (); +} + +static int +sb16midi_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + if (sb16midi_opened) + { + return RET_ERROR (EBUSY); + } + + sb16midi_input_loop (); + + midi_input_intr = input; + sb16midi_opened = mode; + + return 0; +} + +static void +sb16midi_close (int dev) +{ + sb16midi_opened = 0; +} + +static int +sb16midi_out (int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + + /* + * Test for input since pending input seems to block the output. + */ + + DISABLE_INTR (flags); + + if (input_avail ()) + sb16midi_input_loop (); + + RESTORE_INTR (flags); + + /* + * Sometimes it takes about 13000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /* Wait */ + + if (!output_ready ()) + { + printk ("MPU-401: Timeout\n"); + return 0; + } + + sb16midi_write (midi_byte); + return 1; +} + +static int +sb16midi_command (int dev, unsigned char midi_byte) +{ + return 1; +} + +static int +sb16midi_start_read (int dev) +{ + return 0; +} + +static int +sb16midi_end_read (int dev) +{ + return 0; +} + +static int +sb16midi_ioctl (int dev, unsigned cmd, unsigned arg) +{ + return RET_ERROR (EINVAL); +} + +static void +sb16midi_kick (int dev) +{ +} + +static int +sb16midi_buffer_status (int dev) +{ + return 0; /* No data in buffers */ +} + +static struct midi_operations sb16midi_operations = +{ + {"SoundBlaster MPU-401", 0, 0, SNDCARD_SB16MIDI}, + sb16midi_open, + sb16midi_close, + sb16midi_ioctl, + sb16midi_out, + sb16midi_start_read, + sb16midi_end_read, + sb16midi_kick, + sb16midi_command, + sb16midi_buffer_status +}; + + +long +attach_sb16midi (long mem_start, struct address_info *hw_config) +{ + int ok, timeout; + unsigned long flags; + + sb16midi_base = hw_config->io_base; + + if (!sb16midi_detected) + return RET_ERROR (EIO); + + DISABLE_INTR (flags); + for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */ + sb16midi_cmd (UART_MODE_ON); + + ok = 0; + for (timeout = 50000; timeout > 0 && !ok; timeout--) + if (input_avail ()) + if (sb16midi_read () == MPU_ACK) + ok = 1; + + RESTORE_INTR (flags); + +#ifdef __FreeBSD__ + printk ("snd7: <SoundBlaster MPU-401>"); +#else + printk (" <SoundBlaster MPU-401>"); +#endif + + my_dev = num_midis; + midi_devs[num_midis++] = &sb16midi_operations; + return mem_start; +} + +static int +reset_sb16midi (void) +{ + unsigned long flags; + int ok, timeout, n; + + /* + * Send the RESET command. Try again if no success at the first time. + */ + + ok = 0; + + DISABLE_INTR (flags); + + for (n = 0; n < 2 && !ok; n++) + { + for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */ + sb16midi_cmd (MPU_RESET); /* Send MPU-401 RESET Command */ + + /* + * Wait at least 25 msec. This method is not accurate so let's make the + * loop bit longer. Cannot sleep since this is called during boot. + */ + + for (timeout = 50000; timeout > 0 && !ok; timeout--) + if (input_avail ()) + if (sb16midi_read () == MPU_ACK) + ok = 1; + + } + + sb16midi_opened = 0; + if (ok) + sb16midi_input_loop (); /* Flush input before enabling interrupts */ + + RESTORE_INTR (flags); + + return ok; +} + + +int +probe_sb16midi (struct address_info *hw_config) +{ + int ok = 0; + + sb16midi_base = hw_config->io_base; + if (sbc_major < 4) + return 0; /* SB16 not detected */ + + if (sb_get_irq () < 0) + return 0; + + ok = reset_sb16midi (); + + sb16midi_detected = ok; + return ok; +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/sb_card.c b/sys/i386/isa/sound/sb_card.c index e2527c51a35e..f7588e1ca172 100644 --- a/sys/i386/isa/sound/sb_card.c +++ b/sys/i386/isa/sound/sb_card.c @@ -1,11 +1,10 @@ - /* - * linux/kernel/chr_drv/sound/sb_card.c - * + * sound/sb_card.c + * * Detection routine for the SoundBlaster cards. - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -13,7 +12,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -25,7 +24,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #include "sound_config.h" diff --git a/sys/i386/isa/sound/sb_dsp.c b/sys/i386/isa/sound/sb_dsp.c index 4c273646bdd3..17fb4b70dc6a 100644 --- a/sys/i386/isa/sound/sb_dsp.c +++ b/sys/i386/isa/sound/sb_dsp.c @@ -1,7 +1,7 @@ /* - * linux/kernel/chr_drv/sound/sb_dsp.c + * sound/sb_dsp.c * - * The low level driver for the SoundBlaster DS chips. + * The low level driver for the SoundBlaster DSP chip. * * Copyright by Hannu Savolainen 1993 * @@ -25,152 +25,81 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * The mixer support is based on the SB-BSD 1.5 driver by (C) Steve Haehnichen - * <shaehnic@ucsd.edu> + * Modified: + * Hunyue Yau Jan 6 1994 + * Added code to support Sound Galaxy NX Pro + * */ #include "sound_config.h" #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) +#include "sb.h" +#include "sb_mixer.h" #undef SB_TEST_IRQ -#define DSP_RESET (sbc_base + 0x6) -#define DSP_READ (sbc_base + 0xA) -#define DSP_WRITE (sbc_base + 0xC) -#define DSP_COMMAND (sbc_base + 0xC) -#define DSP_STATUS (sbc_base + 0xC) -#define DSP_DATA_AVAIL (sbc_base + 0xE) -#define MIXER_ADDR (sbc_base + 0x4) -#define MIXER_DATA (sbc_base + 0x5) -#define OPL3_LEFT (sbc_base + 0x0) -#define OPL3_RIGHT (sbc_base + 0x2) -#define OPL3_BOTH (sbc_base + 0x8) - -static int sbc_base = 0; +int sbc_base = 0; static int sbc_irq = 0; - -#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) - -#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | SOUND_MASK_VOLUME) - -/* - * Mixer registers - * - * NOTE! RECORD_SRC == IN_FILTER - */ - -#define VOC_VOL 0x04 -#define MIC_VOL 0x0A -#define MIC_MIX 0x0A -#define RECORD_SRC 0x0C -#define IN_FILTER 0x0C -#define OUT_FILTER 0x0E -#define MASTER_VOL 0x22 -#define FM_VOL 0x26 -#define CD_VOL 0x28 -#define LINE_VOL 0x2E - -#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */ -#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */ -#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */ -#define FILT_OFF (1 << 5) - -/* Convenient byte masks */ -#define B1(x) ((x) & 0x01) -#define B2(x) ((x) & 0x03) -#define B3(x) ((x) & 0x07) -#define B4(x) ((x) & 0x0f) -#define B5(x) ((x) & 0x1f) -#define B6(x) ((x) & 0x3f) -#define B7(x) ((x) & 0x7f) -#define B8(x) ((x) & 0xff) -#define F(x) (!!(x)) /* 0 or 1 only */ - -#define MONO_DAC 0x00 -#define STEREO_DAC 0x02 - -/* DSP Commands */ - -#define DSP_CMD_SPKON 0xD1 -#define DSP_CMD_SPKOFF 0xD3 +static int open_mode=0; /* * The DSP channel can be used either for input or output. Variable - * 'irq_mode' will be set when the program calls read or write first time + * 'sb_irq_mode' will be set when the program calls read or write first time * after open. Current version doesn't support mode changes without closing * and reopening the device. Support for this feature may be implemented in a * future version of this driver. */ -#define IMODE_NONE 0 -#define IMODE_OUTPUT 1 -#define IMODE_INPUT 2 -#define IMODE_INIT 3 -#define IMODE_MIDI 4 - -#define NORMAL_MIDI 0 -#define UART_MIDI 1 - -static int sb_dsp_ok = 0; /* Set to 1 after successful initialization */ +int sb_dsp_ok = 0; /* Set to 1 after successful initialization */ static int midi_disabled = 0; -static int dsp_highspeed = 0, dsp_stereo = 0; +int sb_dsp_highspeed = 0; +int sbc_major = 1; +int sbc_minor = 0; /* DSP version */ +static int dsp_stereo = 0; static int dsp_current_speed = DSP_DEFAULT_SPEED; +static int sb16 = 0; +static int irq_verified = 0; -#ifndef EXCLUDE_SBPRO -static int rec_devices = SOUND_MASK_MIC; -static int hi_filter = 0, filter_in = 0, filter_out = 0; +int sb_midi_mode = NORMAL_MIDI; +int sb_midi_busy = 0; /* 1 if the process has output to MIDI */ +int sb_dsp_busy = 0; -#endif +volatile int sb_irq_mode = IMODE_NONE; /* IMODE_INPUT, IMODE_OUTPUT -static int midi_mode = NORMAL_MIDI; -static int midi_busy = 0; /* 1 if the process has output to MIDI */ -static int dsp_busy = 0; - -static volatile int irq_mode = IMODE_NONE; /* IMODE_INPUT, IMODE_OUTPUT * or IMODE_NONE */ static volatile int irq_ok = 0; -static int dsp_model = 1; /* DSP version */ -static int dsp_mono = 1; /* 1 SB, 0 SB Pro */ -static int duplex_midi = 0; +int sb_duplex_midi = 0; static int my_dev = 0; -static volatile int intr_active = 0; +volatile int sb_intr_active = 0; static int dsp_speed (int); static int dsp_set_stereo (int mode); -static int dsp_command (unsigned char val); - -#ifndef EXCLUDE_SBPRO -static void setmixer (unsigned char port, unsigned char value); -static int getmixer (unsigned char port); -static void init_mixer (void); -static int detect_mixer (void); - -#endif +int sb_dsp_command (unsigned char val); #if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO) /* Common code for the midi and pcm functions */ -static int -dsp_command (unsigned char val) +int +sb_dsp_command (unsigned char val) { - int i, limit; + int i; + unsigned long limit; - limit = GET_TIME () + 10; /* The timeout is 0.1 secods */ + limit = GET_TIME () + HZ / 10;/* The timeout is 0.1 secods */ /* - * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes + * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes * called while interrupts are disabled. This means that the timer is * disabled also. However the timeout situation is a abnormal condition. * Normally the DSP should be ready to accept commands after just couple of * loops. */ - for (i = 0; i < 5000000 && GET_TIME () < limit; i++) + for (i = 0; i < 500000 && GET_TIME () < limit; i++) { if ((INB (DSP_STATUS) & 0x80) == 0) { @@ -179,42 +108,60 @@ dsp_command (unsigned char val) } } - printk ("SoundBlaster: DSP Command(%02x) Timeout.\n", val); + printk ("SoundBlaster: DSP Command(%x) Timeout.\n", val); printk ("IRQ conflict???\n"); return 0; } void -sbintr (int unused) +sbintr (int unit) { - int status, data; + int status; + +#ifndef EXCLUDE_SBPRO + if (sb16) + { + unsigned char src = sb_getmixer (IRQ_STAT); /* Interrupt source register */ + +#ifndef EXCLUDE_SB16 + if (src & 3) + sb16_dsp_interrupt (unit); + +#ifndef EXCLUDE_MIDI + if (src & 4) + sb16midiintr (unit); /* MPU401 interrupt */ +#endif + +#endif + + if (!(src & 1)) + return; /* Not a DSP interupt */ + } +#endif status = INB (DSP_DATA_AVAIL);/* Clear interrupt */ - if (intr_active) - switch (irq_mode) + if (sb_intr_active) + switch (sb_irq_mode) { case IMODE_OUTPUT: - intr_active = 0; - DMAbuf_outputintr (my_dev); + sb_intr_active = 0; + DMAbuf_outputintr (my_dev, 1); break; case IMODE_INPUT: - intr_active = 0; + sb_intr_active = 0; DMAbuf_inputintr (my_dev); /* A complete buffer has been input. Let's start new one */ break; case IMODE_INIT: - intr_active = 0; + sb_intr_active = 0; irq_ok = 1; break; case IMODE_MIDI: - printk ("+"); - data = INB (DSP_READ); - printk ("%02x", data); - + sb_midi_interrupt (unit); break; default: @@ -222,40 +169,36 @@ sbintr (int unused) } } -static int -set_dsp_irq (int interrupt_level) -{ - int retcode = EINVAL; +static int sb_irq_usecount = 0; -#ifdef linux - struct sigaction sa; +int +sb_get_irq (void) +{ + int ok; - sa.sa_handler = sbintr; + if (!sb_irq_usecount) + if ((ok = snd_set_irq_handler (sbc_irq, sbintr)) < 0) + return ok; -#ifdef SND_SA_INTERRUPT - sa.sa_flags = SA_INTERRUPT; -#else - sa.sa_flags = 0; -#endif + sb_irq_usecount++; - sa.sa_mask = 0; - sa.sa_restorer = NULL; + return 0; +} - retcode = irqaction (interrupt_level, &sa); +void +sb_free_irq (void) +{ + if (!sb_irq_usecount) + return; - if (retcode < 0) - { - printk ("SoundBlaster: IRQ%d already in use\n", interrupt_level); - } + sb_irq_usecount--; -#else - /* # error Unimplemented for this OS */ -#endif - return retcode; + if (!sb_irq_usecount) + snd_release_irq (sbc_irq); } -static int -reset_dsp (void) +int +sb_reset_dsp (void) { int loopc; @@ -283,9 +226,9 @@ static void dsp_speaker (char state) { if (state) - dsp_command (DSP_CMD_SPKON); + sb_dsp_command (DSP_CMD_SPKON); else - dsp_command (DSP_CMD_SPKOFF); + sb_dsp_command (DSP_CMD_SPKOFF); } static int @@ -293,61 +236,81 @@ dsp_speed (int speed) { unsigned char tconst; unsigned long flags; - + int max_speed = 44100; if (speed < 4000) speed = 4000; - if (speed > 44100) - speed = 44100; /* Invalid speed */ + /* + * Older SB models don't support higher speeds than 22050. + */ + + if (sbc_major < 2 || + (sbc_major == 2 && sbc_minor == 0)) + max_speed = 22050; - if (dsp_model == 1 && speed > 22050) - speed = 22050; - /* SB Classic doesn't support higher speed */ + /* + * SB models earlier than SB Pro have low limit for the input speed. + */ + if (open_mode != OPEN_WRITE) /* Recording is possible */ + if (sbc_major < 3) /* Limited input speed with these cards */ + if (sbc_major == 2 && sbc_minor > 0) + max_speed = 15000; + else + max_speed = 13000; + if (speed > max_speed) + speed = max_speed; /* Invalid speed */ if (dsp_stereo && speed > 22050) speed = 22050; /* Max. stereo speed is 22050 */ - if ((speed > 22050) && midi_busy) + if ((speed > 22050) && sb_midi_busy) { printk ("SB Warning: High speed DSP not possible simultaneously with MIDI output\n"); speed = 22050; } if (dsp_stereo) - speed <<= 1; + speed *= 2; /* Now the speed should be valid */ if (speed > 22050) { /* High speed mode */ - tconst = (unsigned char) ((65536 - (256000000 / speed)) >> 8); - dsp_highspeed = 1; + int tmp; + + tconst = (unsigned char) ((65536 - + ((256000000 + speed / 2) / speed)) >> 8); + sb_dsp_highspeed = 1; DISABLE_INTR (flags); - if (dsp_command (0x40)) - dsp_command (tconst); + if (sb_dsp_command (0x40)) + sb_dsp_command (tconst); RESTORE_INTR (flags); - speed = (256000000 / (65536 - (tconst << 8))); + tmp = 65536 - (tconst << 8); + speed = (256000000 + tmp / 2) / tmp; } else { - dsp_highspeed = 0; - tconst = (256 - (1000000 / speed)) & 0xff; + int tmp; + + sb_dsp_highspeed = 0; + tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; DISABLE_INTR (flags); - if (dsp_command (0x40)) /* Set time constant */ - dsp_command (tconst); + if (sb_dsp_command (0x40))/* Set time constant */ + sb_dsp_command (tconst); RESTORE_INTR (flags); - speed = 1000000 / (256 - tconst); + tmp = 256 - tconst; + speed = (1000000 + tmp / 2) / tmp; } if (dsp_stereo) - speed >>= 1; + speed /= 2; dsp_current_speed = speed; return speed; @@ -358,49 +321,47 @@ dsp_set_stereo (int mode) { dsp_stereo = 0; - if (dsp_mono == 1) +#ifdef EXCLUDE_SBPRO + return 0; +#else + if (sbc_major < 3 || sb16) return 0; /* Sorry no stereo */ - if (mode && midi_busy) + if (mode && sb_midi_busy) { printk ("SB Warning: Stereo DSP not possible simultaneously with MIDI output\n"); return 0; } dsp_stereo = !!mode; - -#ifndef EXCLUDE_SBPRO - setmixer (OUT_FILTER, ((getmixer (OUT_FILTER) & ~STEREO_DAC) - | (mode ? STEREO_DAC : MONO_DAC))); + return dsp_stereo; #endif - dsp_speed (dsp_current_speed);/* Speed must be recalculated if #channels - * changes */ - return mode; } static void -sb_dsp_output_block (int dev, unsigned long buf, int count, int intrflag) +sb_dsp_output_block (int dev, unsigned long buf, int count, + int intrflag, int restart_dma) { unsigned long flags; - if (!irq_mode) + if (!sb_irq_mode) dsp_speaker (ON); - irq_mode = IMODE_OUTPUT; + sb_irq_mode = IMODE_OUTPUT; DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); if (sound_dsp_dmachan[dev] > 3) count >>= 1; count--; - if (dsp_highspeed) + if (sb_dsp_highspeed) { DISABLE_INTR (flags); - if (dsp_command (0x48)) /* High speed size */ + if (sb_dsp_command (0x48))/* High speed size */ { - dsp_command (count & 0xff); - dsp_command ((count >> 8) & 0xff); - dsp_command (0x91); /* High speed 8 bit DAC */ + sb_dsp_command ((unsigned char) (count & 0xff)); + sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); + sb_dsp_command (0x91);/* High speed 8 bit DAC */ } else printk ("SB Error: Unable to start (high speed) DAC\n"); @@ -409,43 +370,44 @@ sb_dsp_output_block (int dev, unsigned long buf, int count, int intrflag) else { DISABLE_INTR (flags); - if (dsp_command (0x14)) /* 8-bit DAC (DMA) */ + if (sb_dsp_command (0x14))/* 8-bit DAC (DMA) */ { - dsp_command (count & 0xff); - dsp_command ((count >> 8) & 0xff); + sb_dsp_command ((unsigned char) (count & 0xff)); + sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); } else printk ("SB Error: Unable to start DAC\n"); RESTORE_INTR (flags); } - intr_active = 1; + sb_intr_active = 1; } static void -sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag) +sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, + int restart_dma) { /* Start a DMA input to the buffer pointed by dmaqtail */ unsigned long flags; - if (!irq_mode) + if (!sb_irq_mode) dsp_speaker (OFF); - irq_mode = IMODE_INPUT; + sb_irq_mode = IMODE_INPUT; DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); if (sound_dsp_dmachan[dev] > 3) count >>= 1; count--; - if (dsp_highspeed) + if (sb_dsp_highspeed) { DISABLE_INTR (flags); - if (dsp_command (0x48)) /* High speed size */ + if (sb_dsp_command (0x48))/* High speed size */ { - dsp_command (count & 0xff); - dsp_command ((count >> 8) & 0xff); - dsp_command (0x99); /* High speed 8 bit ADC */ + sb_dsp_command ((unsigned char) (count & 0xff)); + sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); + sb_dsp_command (0x99);/* High speed 8 bit ADC */ } else printk ("SB Error: Unable to start (high speed) ADC\n"); @@ -454,23 +416,23 @@ sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag) else { DISABLE_INTR (flags); - if (dsp_command (0x24)) /* 8-bit ADC (DMA) */ + if (sb_dsp_command (0x24))/* 8-bit ADC (DMA) */ { - dsp_command (count & 0xff); - dsp_command ((count >> 8) & 0xff); + sb_dsp_command ((unsigned char) (count & 0xff)); + sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); } else printk ("SB Error: Unable to start ADC\n"); RESTORE_INTR (flags); } - intr_active = 1; + sb_intr_active = 1; } static void dsp_cleanup (void) { - intr_active = 0; + sb_intr_active = 0; } static int @@ -478,6 +440,17 @@ sb_dsp_prepare_for_input (int dev, int bsize, int bcount) { dsp_cleanup (); dsp_speaker (OFF); + + if (sbc_major == 3) /* SB Pro */ + { + if (dsp_stereo) + sb_dsp_command (0xa8); + else + sb_dsp_command (0xa0); + + dsp_speed (dsp_current_speed); /* Speed must be recalculated if #channels + * changes */ + } return 0; } @@ -486,6 +459,15 @@ sb_dsp_prepare_for_output (int dev, int bsize, int bcount) { dsp_cleanup (); dsp_speaker (ON); + +#ifndef EXCLUDE_SBPRO + if (sbc_major == 3) /* SB Pro */ + { + sb_mixer_set_stereo (dsp_stereo); + dsp_speed (dsp_current_speed); /* Speed must be recalculated if #channels + * changes */ + } +#endif return 0; } @@ -495,50 +477,80 @@ sb_dsp_halt_xfer (int dev) } static int -sb_dsp_open (int dev, int mode) +verify_irq (void) { - int retval; +#if 0 + DEFINE_WAIT_QUEUE (testq, testf); - if (!sb_dsp_ok) + irq_ok = 0; + + if (sb_get_irq () == -1) { - printk ("SB Error: SoundBlaster board not installed\n"); - return RET_ERROR (ENXIO); + printk ("*** SB Error: Irq %d already in use\n", sbc_irq); + return 0; } + + sb_irq_mode = IMODE_INIT; + + sb_dsp_command (0xf2); /* This should cause immediate interrupt */ + + DO_SLEEP (testq, testf, HZ / 5); + + sb_free_irq (); + if (!irq_ok) { - printk ("SB Error: Incorrect IRQ setting (%d)\n", sbc_irq); + printk ("SB Warning: IRQ%d test not passed!", sbc_irq); + irq_ok = 1; + } +#else + irq_ok = 1; +#endif + return irq_ok; +} + +static int +sb_dsp_open (int dev, int mode) +{ + int retval; + + if (!sb_dsp_ok) + { + printk ("SB Error: SoundBlaster board not installed\n"); return RET_ERROR (ENXIO); } - if (intr_active || (midi_busy && midi_mode == UART_MIDI)) + if (sb_intr_active || (sb_midi_busy && sb_midi_mode == UART_MIDI)) { printk ("SB: PCM not possible during MIDI input\n"); return RET_ERROR (EBUSY); } - if (mode != OPEN_READ && mode != OPEN_WRITE) + if (!irq_verified) { - printk ("SoundBlaster error: DAC and ACD not possible simultaneously\n"); - return RET_ERROR (EINVAL); + verify_irq (); + irq_verified = 1; } + else if (!irq_ok) + printk ("SB Warning: Incorrect IRQ setting %d\n", + sbc_irq); - retval = set_dsp_irq (sbc_irq); + retval = sb_get_irq (); if (retval) return retval; if (!DMAbuf_open_dma (dev)) { - RELEASE_IRQ (sbc_irq); + sb_free_irq (); printk ("SB: DMA Busy\n"); return RET_ERROR (EBUSY); } - dsp_set_stereo (OFF); - dsp_speed (DSP_DEFAULT_SPEED); - irq_mode = IMODE_NONE; + sb_irq_mode = IMODE_NONE; - dsp_busy = 1; + sb_dsp_busy = 1; + open_mode = mode; return 0; } @@ -547,12 +559,12 @@ static void sb_dsp_close (int dev) { DMAbuf_close_dma (dev); - RELEASE_IRQ (sbc_irq); + sb_free_irq (); dsp_cleanup (); - dsp_speed (DSP_DEFAULT_SPEED); - dsp_set_stereo (OFF); dsp_speaker (OFF); - dsp_busy = 0; + sb_dsp_busy = 0; + sb_dsp_highspeed = 0; + open_mode = 0; } static int @@ -573,6 +585,8 @@ sb_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) break; case SOUND_PCM_WRITE_CHANNELS: + if (local) + return dsp_set_stereo (arg - 1) + 1; return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1); break; @@ -614,7 +628,8 @@ sb_dsp_reset (int dev) DISABLE_INTR (flags); - reset_dsp (); + sb_reset_dsp (); + dsp_speed (dsp_current_speed); dsp_cleanup (); RESTORE_INTR (flags); @@ -631,542 +646,19 @@ sb_dsp_detect (struct address_info *hw_config) if (sb_dsp_ok) return 0; /* Already initialized */ - if (!reset_dsp ()) + if (!sb_reset_dsp ()) return 0; return 1; /* Detected */ } -#ifndef EXCLUDE_SBPRO - -static void -setmixer (unsigned char port, unsigned char value) -{ - OUTB (port, MIXER_ADDR); /* Select register */ - tenmicrosec (); - OUTB (value, MIXER_DATA); - tenmicrosec (); -} - -static int -getmixer (unsigned char port) -{ - int val; - - OUTB (port, MIXER_ADDR); /* Select register */ - tenmicrosec (); - val = INB (MIXER_DATA); - tenmicrosec (); - - return val; -} - -static int -detect_mixer (void) -{ - /* - * Detect the mixer by changing parameters of two volume channels. If the - * values read back match with the values written, the mixer is there (is - * it?) - */ - setmixer (FM_VOL, 0xff); - setmixer (VOC_VOL, 0x33); - - if (getmixer (FM_VOL) != 0xff) - return 0; /* No match */ - if (getmixer (VOC_VOL) != 0x33) - return 0; - - return 1; -} - -static void -init_mixer (void) -{ - setmixer (MASTER_VOL, 0xbb); - setmixer (VOC_VOL, 0x99); - setmixer (LINE_VOL, 0xbb); - setmixer (FM_VOL, 0x99); - setmixer (CD_VOL, 0x11); - setmixer (MIC_MIX, 0x11); - setmixer (RECORD_SRC, 0x31); - setmixer (OUT_FILTER, 0x31); -} - -static void -set_filter (int record_source, int hifreq_filter, int filter_input, int filter_output) -{ - setmixer (RECORD_SRC, (record_source - | (hifreq_filter ? FREQ_HI : FREQ_LOW) - | (filter_input ? FILT_ON : FILT_OFF))); - - setmixer (OUT_FILTER, ((dsp_stereo ? STEREO_DAC : MONO_DAC) - | (filter_output ? FILT_ON : FILT_OFF))); - - hi_filter = hifreq_filter; - filter_in = filter_input; - filter_out = filter_output; -} - -static int -mixer_output (int right_vol, int left_vol, int div, int device) -{ - int left = ((left_vol * div) + 50) / 100; - int right = ((right_vol * div) + 50) / 100; - - setmixer (device, ((left & 0xf) << 4) | (right & 0xf)); - - return (left_vol | (right_vol << 8)); -} - -static int -sbp_mixer_set (int whichDev, unsigned int level) -{ - int left, right, devmask; - - left = level & 0x7f; - right = (level & 0x7f00) >> 8; - - switch (whichDev) - { - case SOUND_MIXER_VOLUME: /* Master volume (0-15) */ - return mixer_output (right, left, 15, MASTER_VOL); - break; - case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-15) */ - return mixer_output (right, left, 15, FM_VOL); - break; - case SOUND_MIXER_PCM: /* PAS PCM (0-15) */ - return mixer_output (right, left, 15, VOC_VOL); - break; - case SOUND_MIXER_LINE: /* External line (0-15) */ - return mixer_output (right, left, 15, LINE_VOL); - break; - case SOUND_MIXER_CD: /* CD (0-15) */ - return mixer_output (right, left, 15, CD_VOL); - break; - case SOUND_MIXER_MIC: /* External microphone (0-7) */ - return mixer_output (right, left, 7, MIC_VOL); - break; - - case SOUND_MIXER_RECSRC: - devmask = level & POSSIBLE_RECORDING_DEVICES; - - if (devmask != SOUND_MASK_MIC && - devmask != SOUND_MASK_LINE && - devmask != SOUND_MASK_CD) - { /* More than one devices selected. Drop the - * previous selection */ - devmask &= ~rec_devices; - } - - if (devmask != SOUND_MASK_MIC && - devmask != SOUND_MASK_LINE && - devmask != SOUND_MASK_CD) - { /* More than one devices selected. Default to - * mic */ - devmask = SOUND_MASK_MIC; - } - - if (devmask ^ rec_devices)/* Input source changed */ - { - switch (devmask) - { - - case SOUND_MASK_MIC: - set_filter (SRC_MIC, hi_filter, filter_in, filter_out); - break; - - case SOUND_MASK_LINE: - set_filter (SRC_LINE, hi_filter, filter_in, filter_out); - break; - - case SOUND_MASK_CD: - set_filter (SRC_CD, hi_filter, filter_in, filter_out); - break; - - default: - set_filter (SRC_MIC, hi_filter, filter_in, filter_out); - } - } - - rec_devices = devmask; - - return rec_devices; - break; - - default: - return RET_ERROR (EINVAL); - } - -} - -static int -mixer_input (int div, int device) -{ - int level, left, right, half; - - level = getmixer (device); - half = div / 2; - - left = ((((level & 0xf0) >> 4) * 100) + half) / div; - right = (((level & 0x0f) * 100) + half) / div; - - return (right << 8) | left; -} - -static int -sbp_mixer_get (int whichDev) -{ - - switch (whichDev) - { - case SOUND_MIXER_VOLUME: /* Master volume (0-15) */ - return mixer_input (15, MASTER_VOL); - break; - case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-15) */ - return mixer_input (15, FM_VOL); - break; - case SOUND_MIXER_PCM: /* PAS PCM (0-15) */ - return mixer_input (15, VOC_VOL); - break; - case SOUND_MIXER_LINE: /* External line (0-15) */ - return mixer_input (15, LINE_VOL); - break; - case SOUND_MIXER_CD: /* CD (0-15) */ - return mixer_input (15, CD_VOL); - break; - case SOUND_MIXER_MIC: /* External microphone (0-7) */ - return mixer_input (7, MIC_VOL); - break; - - default: - return RET_ERROR (EINVAL); - } - -} - -/* - * Sets mixer volume levels. All levels except mic are 0 to 15, mic is 7. See - * sbinfo.doc for details on granularity and such. Basically, the mixer - * forces the lowest bit high, effectively reducing the possible settings by - * one half. Yes, that's right, volume levels have 8 settings, and - * microphone has four. Sucks. - */ -static int -mixer_set_levels (struct sb_mixer_levels *user_l) -{ - struct sb_mixer_levels l; - - IOCTL_FROM_USER ((char *) &l, ((char *) user_l), 0, sizeof (l)); - - if (l.master.l & ~0xF || l.master.r & ~0xF - || l.line.l & ~0xF || l.line.r & ~0xF - || l.voc.l & ~0xF || l.voc.r & ~0xF - || l.fm.l & ~0xF || l.fm.r & ~0xF - || l.cd.l & ~0xF || l.cd.r & ~0xF - || l.mic & ~0x7) - return (RET_ERROR (EINVAL)); - - setmixer (MASTER_VOL, (l.master.l << 4) | l.master.r); - setmixer (LINE_VOL, (l.line.l << 4) | l.line.r); - setmixer (VOC_VOL, (l.voc.l << 4) | l.voc.r); - setmixer (FM_VOL, (l.fm.l << 4) | l.fm.r); - setmixer (CD_VOL, (l.cd.l << 4) | l.cd.r); - setmixer (MIC_VOL, l.mic); - return (0); -} - -/* - * This sets aspects of the Mixer that are not volume levels. (Recording - * source, filter level, I/O filtering, and stereo.) - */ - -static int -mixer_set_params (struct sb_mixer_params *user_p) -{ - struct sb_mixer_params p; - - IOCTL_FROM_USER ((char *) &p, (char *) user_p, 0, sizeof (p)); - - if (p.record_source != SRC_MIC - && p.record_source != SRC_CD - && p.record_source != SRC_LINE) - return (EINVAL); - - /* - * I'm not sure if this is The Right Thing. Should stereo be entirely - * under control of DSP? I like being able to toggle it while a sound is - * playing, so I do this... because I can. - */ - - dsp_stereo = !!p.dsp_stereo; - - set_filter (p.record_source, p.hifreq_filter, p.filter_input, p.filter_output); - - switch (p.record_source) - { - - case SRC_MIC: - rec_devices = SOUND_MASK_MIC; - break; - - case SRC_LINE: - rec_devices = SOUND_MASK_LINE; - break; - - case SRC_CD: - rec_devices = SOUND_MASK_CD; - } - - return (0); -} - -/* Read the current mixer level settings into the user's struct. */ -static int -mixer_get_levels (struct sb_mixer_levels *user_l) -{ - S_BYTE val; - struct sb_mixer_levels l; - - val = getmixer (MASTER_VOL); /* Master */ - l.master.l = B4 (val >> 4); - l.master.r = B4 (val); - - val = getmixer (LINE_VOL); /* FM */ - l.line.l = B4 (val >> 4); - l.line.r = B4 (val); - - val = getmixer (VOC_VOL); /* DAC */ - l.voc.l = B4 (val >> 4); - l.voc.r = B4 (val); - - val = getmixer (FM_VOL); /* FM */ - l.fm.l = B4 (val >> 4); - l.fm.r = B4 (val); - - val = getmixer (CD_VOL); /* CD */ - l.cd.l = B4 (val >> 4); - l.cd.r = B4 (val); - - val = getmixer (MIC_VOL); /* Microphone */ - l.mic = B3 (val); - - IOCTL_TO_USER ((char *) user_l, 0, (char *) &l, sizeof (l)); - - return (0); -} - -/* Read the current mixer parameters into the user's struct. */ -static int -mixer_get_params (struct sb_mixer_params *user_params) -{ - S_BYTE val; - struct sb_mixer_params params; - - val = getmixer (RECORD_SRC); - params.record_source = val & 0x07; - params.hifreq_filter = !!(val & FREQ_HI); - params.filter_input = (val & FILT_OFF) ? OFF : ON; - params.filter_output = (getmixer (OUT_FILTER) & FILT_OFF) ? OFF : ON; - params.dsp_stereo = dsp_stereo; - - IOCTL_TO_USER ((char *) user_params, 0, (char *) ¶ms, sizeof (params)); - return (0); -} - -static int -sb_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) -{ - if (((cmd >> 8) & 0xff) == 'M') - { - if (cmd & IOC_IN) - return IOCTL_OUT (arg, sbp_mixer_set (cmd & 0xff, IOCTL_IN (arg))); - else - { /* Read parameters */ - - switch (cmd & 0xff) - { - - case SOUND_MIXER_RECSRC: - return IOCTL_OUT (arg, rec_devices); - break; - - case SOUND_MIXER_DEVMASK: - return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES); - break; - - case SOUND_MIXER_STEREODEVS: - return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~SOUND_MASK_MIC); - break; - - case SOUND_MIXER_RECMASK: - return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES); - break; - - case SOUND_MIXER_CAPS: - return IOCTL_OUT (arg, SOUND_CAP_EXCL_INPUT); - break; - - default: - return IOCTL_OUT (arg, sbp_mixer_get (cmd & 0xff)); - } - } - } - else - { - switch (cmd) - { - case MIXER_IOCTL_SET_LEVELS: - return (mixer_set_levels ((struct sb_mixer_levels *) arg)); - case MIXER_IOCTL_SET_PARAMS: - return (mixer_set_params ((struct sb_mixer_params *) arg)); - case MIXER_IOCTL_READ_LEVELS: - return (mixer_get_levels ((struct sb_mixer_levels *) arg)); - case MIXER_IOCTL_READ_PARAMS: - return (mixer_get_params ((struct sb_mixer_params *) arg)); - case MIXER_IOCTL_RESET: - init_mixer (); - return (0); - default: - return RET_ERROR (EINVAL); - } - } -} - -/* End of mixer code */ -#endif - -#ifndef EXCLUDE_MIDI - -/* Midi code */ - -static int -sb_midi_open (int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) -) -{ - int ret; - - if (!sb_dsp_ok) - { - printk ("SB Error: MIDI hardware not installed\n"); - return RET_ERROR (ENXIO); - } - - if (mode != OPEN_WRITE && !duplex_midi) - { - if (num_midis == 1) - printk ("SoundBlaster: Midi input not currently supported\n"); - return RET_ERROR (EPERM); - } - - midi_mode = NORMAL_MIDI; - if (mode != OPEN_WRITE) - { - if (dsp_busy || intr_active) - return RET_ERROR (EBUSY); - midi_mode = UART_MIDI; - } - - if (dsp_highspeed || dsp_stereo) - { - printk ("SB Error: Midi output not possible during stereo or high speed audio\n"); - return RET_ERROR (EBUSY); - } - - if (midi_mode == UART_MIDI) - { - irq_mode = IMODE_MIDI; - - reset_dsp (); - dsp_speaker (OFF); - - if (!dsp_command (0x35)) - return RET_ERROR (EIO); /* Enter the UART mode */ - intr_active = 1; - - if ((ret = set_dsp_irq (sbc_irq)) < 0) - { - reset_dsp (); - return 0; /* IRQ not free */ - } - } - - midi_busy = 1; - - return 0; -} - -static void -sb_midi_close (int dev) -{ - if (midi_mode == UART_MIDI) - { - reset_dsp (); /* The only way to kill the UART mode */ - RELEASE_IRQ (sbc_irq); - } - intr_active = 0; - midi_busy = 0; -} - -static int -sb_midi_out (int dev, unsigned char midi_byte) -{ - unsigned long flags; - - midi_busy = 1; /* Kill all notes after close */ - - if (midi_mode == NORMAL_MIDI) - { - DISABLE_INTR (flags); - if (dsp_command (0x38)) - dsp_command (midi_byte); - else - printk ("SB Error: Unable to send a MIDI byte\n"); - RESTORE_INTR (flags); - } - else - dsp_command (midi_byte); /* UART write */ - - return 1; -} - -static int -sb_midi_start_read (int dev) -{ - if (midi_mode != UART_MIDI) - { - printk ("SoundBlaster: MIDI input not implemented.\n"); - return RET_ERROR (EPERM); - } - return 0; -} - -static int -sb_midi_end_read (int dev) -{ - if (midi_mode == UART_MIDI) - { - reset_dsp (); - intr_active = 0; - } - return 0; -} - -static int -sb_midi_ioctl (int dev, unsigned cmd, unsigned arg) -{ - return RET_ERROR (EPERM); -} - -/* End of midi code */ -#endif +static char card_name[32] = "SoundBlaster"; #ifndef EXCLUDE_AUDIO static struct audio_operations sb_dsp_operations = { "SoundBlaster", + NOTHING_SPECIAL, sb_dsp_open, sb_dsp_close, sb_dsp_output_block, @@ -1182,132 +674,102 @@ static struct audio_operations sb_dsp_operations = #endif -#ifndef EXCLUDE_SBPRO -static struct mixer_operations sb_mixer_operations = -{ - sb_mixer_ioctl -}; - -#endif - -#ifndef EXCLUDE_MIDI -static struct midi_operations sb_midi_operations = -{ - {"SoundBlaster", 0}, - sb_midi_open, - sb_midi_close, - sb_midi_ioctl, - sb_midi_out, - sb_midi_start_read, - sb_midi_end_read, - NULL, /* Kick */ - NULL, /* command */ - NULL /* buffer_status */ -}; - -#endif - -static int -verify_irq (void) -{ -#if 0 - unsigned long loop; - - irq_ok = 0; - - if (set_dsp_irq (sbc_irq) == -1) - { - printk ("*** SB Error: Irq %d already in use\n", sbc_irq); - return 0; - } - - - irq_mode = IMODE_INIT; - - dsp_command (0xf2); /* This should cause immediate interrupt */ - - for (loop = 100000; loop > 0 && !irq_ok; loop--); - - RELEASE_IRQ (sbc_irq); - - if (!irq_ok) - { - printk ("SB Warning: IRQ test not passed!"); - irq_ok = 1; - } -#else - irq_ok = 1; -#endif - return irq_ok; -} - long sb_dsp_init (long mem_start, struct address_info *hw_config) { - int i, major, minor; + int i; + int prostat = 0; - major = minor = 0; - dsp_command (0xe1); /* Get version */ + sbc_major = sbc_minor = 0; + sb_dsp_command (0xe1); /* Get version */ for (i = 1000; i; i--) { - if (inb (DSP_DATA_AVAIL) & 0x80) + if (INB (DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */ - if (major == 0) - major = inb (DSP_READ); + if (sbc_major == 0) + sbc_major = INB (DSP_READ); else { - minor = inb (DSP_READ); + sbc_minor = INB (DSP_READ); break; } } } - dsp_model = major; + + if (sbc_major == 2 || sbc_major == 3) /* SB 2.0 or SB Pro */ + sb_duplex_midi = 1; + + if (sbc_major == 4) + sb16 = 1; #ifndef EXCLUDE_SBPRO - if (detect_mixer ()) - { - dsp_mono = 0; - sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", major, minor); - init_mixer (); - mixer_devs[num_mixers++] = &sb_mixer_operations; + if (sbc_major >= 3 || + (sbc_major == 2 && sbc_minor == 1)) /* Sound Galaxy ??? */ + prostat = sb_mixer_init (sbc_major); +#endif - if (major >= 2) - duplex_midi = 1; +#ifndef EXCLUDE_YM3812 + if (sbc_major > 3 || + (sbc_major == 3 && INB (0x388) == 0x00)) /* Non OPL-3 should return 0x06 */ + enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH); +#endif -#ifndef EXCLUDE_YM8312 - if (major > 3 || (major == 3 && minor > 0)) /* SB Pro2 or later */ + if (sbc_major >= 3) + { +#ifndef SCO + if (prostat) { - enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH); +#ifndef EXCLUDE_AUDIO + sprintf (sb_dsp_operations.name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor); +#endif + sprintf (card_name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor); + } + else + { +#ifndef EXCLUDE_AUDIO + sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor); +#endif + sprintf (card_name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor); } #endif } else + { +#ifndef SCO +#ifndef EXCLUDE_AUDIO + sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", sbc_major, sbc_minor); #endif - sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", major, minor); - - printk ("snd2: <%s>", sb_dsp_operations.name); + sprintf (card_name, "SoundBlaster %d.%d", sbc_major, sbc_minor); +#endif + } - if (!verify_irq ()) - return mem_start; +#ifdef __FreeBSD__ + printk ("snd2: <%s>", card_name); +#else + printk (" <%s>", card_name); +#endif #ifndef EXCLUDE_AUDIO - if (num_dspdevs < MAX_DSP_DEV) - { - dsp_devs[my_dev = num_dspdevs++] = &sb_dsp_operations; - sound_buffcounts[my_dev] = DSP_BUFFCOUNT; - sound_buffsizes[my_dev] = DSP_BUFFSIZE; - sound_dsp_dmachan[my_dev] = hw_config->dma; - sound_dma_automode[my_dev] = 0; - } - else - printk ("SB: Too many DSP devices available\n"); +#if !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SBPRO) + if (!sb16) /* There is a better driver for SB16 */ +#endif + if (num_dspdevs < MAX_DSP_DEV) + { + dsp_devs[my_dev = num_dspdevs++] = &sb_dsp_operations; + sound_buffcounts[my_dev] = DSP_BUFFCOUNT; + sound_buffsizes[my_dev] = DSP_BUFFSIZE; + sound_dsp_dmachan[my_dev] = hw_config->dma; + sound_dma_automode[my_dev] = 0; + } + else + printk ("SB: Too many DSP devices available\n"); #endif #ifndef EXCLUDE_MIDI - if (!midi_disabled) /* Midi don't work in the SB emulation mode - * of PAS */ - midi_devs[num_midis++] = &sb_midi_operations; + if (!midi_disabled && !sb16) /* Midi don't work in the SB emulation mode + * of PAS, SB16 has better midi interface */ + sb_midi_init (sbc_major); #endif sb_dsp_ok = 1; diff --git a/sys/i386/isa/sound/sb_midi.c b/sys/i386/isa/sound/sb_midi.c new file mode 100644 index 000000000000..fed19aba3a08 --- /dev/null +++ b/sys/i386/isa/sound/sb_midi.c @@ -0,0 +1,224 @@ +/* + * sound/sb_dsp.c + * + * The low level driver for the SoundBlaster DS chips. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_MIDI) + +#include "sb.h" +#undef SB_TEST_IRQ + +/* + * The DSP channel can be used either for input or output. Variable + * 'sb_irq_mode' will be set when the program calls read or write first time + * after open. Current version doesn't support mode changes without closing + * and reopening the device. Support for this feature may be implemented in a + * future version of this driver. + */ + +extern int sb_dsp_ok; /* Set to 1 after successful initialization */ + +extern int sb_midi_mode; +extern int sb_midi_busy; /* 1 if the process has output to MIDI */ +extern int sb_dsp_busy; +extern int sb_dsp_highspeed; + +extern volatile int sb_irq_mode;/* IMODE_INPUT, IMODE_OUTPUT + + * or IMODE_NONE */ +extern int sb_duplex_midi; +extern int sb_intr_active; +extern int sbc_base; + +static int input_opened = 0; +static void (*midi_input_intr) (int dev, unsigned char data); +static int my_dev = 0; + +static int +sb_midi_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + int ret; + + if (!sb_dsp_ok) + { + printk ("SB Error: MIDI hardware not installed\n"); + return RET_ERROR (ENXIO); + } + + if (mode != OPEN_WRITE && !sb_duplex_midi) + { + if (num_midis == 1) + printk ("SoundBlaster: MIDI input not supported with plain SB\n"); + return RET_ERROR (EPERM); + } + + sb_midi_mode = NORMAL_MIDI; + if (mode != OPEN_WRITE) + { + if (sb_dsp_busy || sb_intr_active) + return RET_ERROR (EBUSY); + sb_midi_mode = UART_MIDI; + } + + if (sb_dsp_highspeed) + { + printk ("SB Error: Midi output not possible during stereo or high speed audio\n"); + return RET_ERROR (EBUSY); + } + + if (sb_midi_mode == UART_MIDI) + { + sb_irq_mode = IMODE_MIDI; + + sb_reset_dsp (); + + if (!sb_dsp_command (0xf2)) /* This is undodumented, isn't it */ + return RET_ERROR (EIO); /* be nice to DSP */ + + if (!sb_dsp_command (0x35)) + return RET_ERROR (EIO); /* Enter the UART mode */ + sb_intr_active = 1; + + if ((ret = sb_get_irq ()) < 0) + { + sb_reset_dsp (); + return 0; /* IRQ not free */ + } + input_opened = 1; + my_dev = dev; + midi_input_intr = input; + } + + sb_midi_busy = 1; + + return 0; +} + +static void +sb_midi_close (int dev) +{ + if (sb_midi_mode == UART_MIDI) + { + sb_reset_dsp (); /* The only way to kill the UART mode */ + sb_free_irq (); + } + sb_intr_active = 0; + sb_midi_busy = 0; + input_opened = 0; +} + +static int +sb_midi_out (int dev, unsigned char midi_byte) +{ + unsigned long flags; + + sb_midi_busy = 1; /* Kill all notes after close */ + + if (sb_midi_mode == NORMAL_MIDI) + { + DISABLE_INTR (flags); + if (sb_dsp_command (0x38)) + sb_dsp_command (midi_byte); + else + printk ("SB Error: Unable to send a MIDI byte\n"); + RESTORE_INTR (flags); + } + else + sb_dsp_command (midi_byte); /* UART write */ + + return 1; +} + +static int +sb_midi_start_read (int dev) +{ + if (sb_midi_mode != UART_MIDI) + { + printk ("SoundBlaster: MIDI input not implemented.\n"); + return RET_ERROR (EPERM); + } + return 0; +} + +static int +sb_midi_end_read (int dev) +{ + if (sb_midi_mode == UART_MIDI) + { + sb_reset_dsp (); + sb_intr_active = 0; + } + return 0; +} + +static int +sb_midi_ioctl (int dev, unsigned cmd, unsigned arg) +{ + return RET_ERROR (EPERM); +} + +void +sb_midi_interrupt (int dummy) +{ + unsigned long flags; + unsigned char data; + + DISABLE_INTR (flags); + + data = INB (DSP_READ); + if (input_opened) + midi_input_intr (my_dev, data); + + RESTORE_INTR (flags); +} + +static struct midi_operations sb_midi_operations = +{ + {"SoundBlaster", 0, 0, SNDCARD_SB}, + sb_midi_open, + sb_midi_close, + sb_midi_ioctl, + sb_midi_out, + sb_midi_start_read, + sb_midi_end_read, + NULL, /* Kick */ + NULL, /* command */ + NULL /* buffer_status */ +}; + +void +sb_midi_init (int model) +{ + midi_devs[num_midis++] = &sb_midi_operations; +} + +#endif diff --git a/sys/i386/isa/sound/sb_mixer.c b/sys/i386/isa/sound/sb_mixer.c new file mode 100644 index 000000000000..39b97caf8cd7 --- /dev/null +++ b/sys/i386/isa/sound/sb_mixer.c @@ -0,0 +1,422 @@ + +/* + * sound/sb_mixer.c + * + * The low level mixer driver for the SoundBlaster Pro and SB16 cards. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Modified: + * Hunyue Yau Jan 6 1994 + * Added code to support the Sound Galaxy NX Pro mixer. + * + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_SBPRO) +#define __SB_MIXER_C__ + +#include "sb.h" +#include "sb_mixer.h" +#undef SB_TEST_IRQ + +extern int sbc_base; + +static int mixer_initialized = 0; + +static int supported_rec_devices; +static int supported_devices; +static int recmask = 0; +static int mixer_model; +static int mixer_caps; +static mixer_tab *iomap; + +void +sb_setmixer (unsigned int port, unsigned int value) +{ + unsigned long flags; + + DISABLE_INTR (flags); + OUTB ((unsigned char) (port & 0xff), MIXER_ADDR); /* Select register */ + tenmicrosec (); + OUTB ((unsigned char) (value & 0xff), MIXER_DATA); + tenmicrosec (); + RESTORE_INTR (flags); +} + +int +sb_getmixer (unsigned int port) +{ + int val; + unsigned long flags; + + DISABLE_INTR (flags); + OUTB ((unsigned char) (port & 0xff), MIXER_ADDR); /* Select register */ + tenmicrosec (); + val = INB (MIXER_DATA); + tenmicrosec (); + RESTORE_INTR (flags); + + return val; +} + +void +sb_mixer_set_stereo (int mode) +{ + if (!mixer_initialized) + return; + + sb_setmixer (OUT_FILTER, ((sb_getmixer (OUT_FILTER) & ~STEREO_DAC) + | (mode ? STEREO_DAC : MONO_DAC))); +} + +/* + * Returns: + * 0 No mixer detected. + * 1 Only a plain Sound Blaster Pro style mixer detected. + * 2 The Sound Galaxy NX Pro mixer detected. + */ +static int +detect_mixer (void) +{ +#ifdef __SGNXPRO__ + int oldbass, oldtreble; + +#endif + int retcode = 1; + + /* + * Detect the mixer by changing parameters of two volume channels. If the + * values read back match with the values written, the mixer is there (is + * it?) + */ + sb_setmixer (FM_VOL, 0xff); + sb_setmixer (VOC_VOL, 0x33); + + if (sb_getmixer (FM_VOL) != 0xff) + return 0; /* No match */ + if (sb_getmixer (VOC_VOL) != 0x33) + return 0; + +#ifdef __SGNXPRO__ + /* Attempt to detect the SG NX Pro by check for valid bass/treble + * registers. + */ + oldbass = sb_getmixer (BASS_LVL); + oldtreble = sb_getmixer (TREBLE_LVL); + + sb_setmixer (BASS_LVL, 0xaa); + sb_setmixer (TREBLE_LVL, 0x55); + + if ((sb_getmixer (BASS_LVL) != 0xaa) || + (sb_getmixer (TREBLE_LVL) != 0x55)) + { + retcode = 1; /* 1 == Only SB Pro detected */ + } + else + retcode = 2; /* 2 == SG NX Pro detected */ + /* Restore register in either case since SG NX Pro has EEPROM with + * 'preferred' values stored. + */ + sb_setmixer (BASS_LVL, oldbass); + sb_setmixer (TREBLE_LVL, oldtreble); +#endif + return retcode; +} + +static void +change_bits (unsigned char *regval, int dev, int chn, int newval) +{ + unsigned char mask; + int shift; + + mask = (1 << (*iomap)[dev][chn].nbits) - 1; + newval = ((newval * mask) + 50) / 100; /* Scale it */ + + shift = (*iomap)[dev][chn].bitoffs - (*iomap)[dev][LEFT_CHN].nbits + 1; + + *regval &= ~(mask << shift); /* Filter out the previous value */ + *regval |= (newval & mask) << shift; /* Set the new value */ +} + +static int +sb_mixer_get (int dev) +{ + if (!((1 << dev) & supported_devices)) + return RET_ERROR (EINVAL); + + return levels[dev]; +} + +static int +sb_mixer_set (int dev, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + + int regoffs; + unsigned char val; + + if (left > 100) + left = 100; + if (right > 100) + right = 100; + + if (dev > 31) + return RET_ERROR (EINVAL); + + if (!(supported_devices & (1 << dev))) /* Not supported */ + return RET_ERROR (EINVAL); + + regoffs = (*iomap)[dev][LEFT_CHN].regno; + + if (regoffs == 0) + return RET_ERROR (EINVAL); + + val = sb_getmixer (regoffs); + change_bits (&val, dev, LEFT_CHN, left); + + levels[dev] = left | (left << 8); + + if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) /* Change register */ + { + sb_setmixer (regoffs, val); /* Save the old one */ + regoffs = (*iomap)[dev][RIGHT_CHN].regno; + + if (regoffs == 0) + return left | (left << 8); /* Just left channel present */ + + val = sb_getmixer (regoffs); /* Read the new one */ + } + + change_bits (&val, dev, RIGHT_CHN, right); + sb_setmixer (regoffs, val); + + levels[dev] = left | (right << 8); + return left | (right << 8); +} + +static void +set_recsrc (int src) +{ + sb_setmixer (RECORD_SRC, (sb_getmixer (RECORD_SRC) & ~7) | (src & 0x7)); +} + +static int +set_recmask (int mask) +{ + int devmask, i; + unsigned char regimageL, regimageR; + + devmask = mask & supported_rec_devices; + + switch (mixer_model) + { + case 3: + + if (devmask != SOUND_MASK_MIC && + devmask != SOUND_MASK_LINE && + devmask != SOUND_MASK_CD) + { /* More than one devices selected. Drop the + * previous selection */ + devmask &= ~recmask; + } + + if (devmask != SOUND_MASK_MIC && + devmask != SOUND_MASK_LINE && + devmask != SOUND_MASK_CD) + { /* More than one devices selected. Default to + * mic */ + devmask = SOUND_MASK_MIC; + } + + + if (devmask ^ recmask) /* Input source changed */ + { + switch (devmask) + { + + case SOUND_MASK_MIC: + set_recsrc (SRC_MIC); + break; + + case SOUND_MASK_LINE: + set_recsrc (SRC_LINE); + break; + + case SOUND_MASK_CD: + set_recsrc (SRC_CD); + break; + + default: + set_recsrc (SRC_MIC); + } + } + + break; + + case 4: + if (!devmask) + devmask = SOUND_MASK_MIC; + + regimageL = regimageR = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if ((1 << i) & devmask) + { + regimageL |= sb16_recmasks_L[i]; + regimageR |= sb16_recmasks_R[i]; + } + sb_setmixer (SB16_IMASK_L, regimageL); + sb_setmixer (SB16_IMASK_R, regimageR); + break; + } + + recmask = devmask; + return recmask; +} + +static int +sb_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) +{ + if (((cmd >> 8) & 0xff) == 'M') + { + if (cmd & IOC_IN) + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + return IOCTL_OUT (arg, set_recmask (IOCTL_IN (arg))); + break; + + default: + return IOCTL_OUT (arg, sb_mixer_set (cmd & 0xff, IOCTL_IN (arg))); + } + else + switch (cmd & 0xff) /* Return parameters */ + { + + case SOUND_MIXER_RECSRC: + return IOCTL_OUT (arg, recmask); + break; + + case SOUND_MIXER_DEVMASK: + return IOCTL_OUT (arg, supported_devices); + break; + + case SOUND_MIXER_STEREODEVS: + return IOCTL_OUT (arg, supported_devices & + ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER)); + break; + + case SOUND_MIXER_RECMASK: + return IOCTL_OUT (arg, supported_rec_devices); + break; + + case SOUND_MIXER_CAPS: + return IOCTL_OUT (arg, mixer_caps); + break; + + default: + return IOCTL_OUT (arg, sb_mixer_get (cmd & 0xff)); + } + } + else + return RET_ERROR (EINVAL); +} + +static struct mixer_operations sb_mixer_operations = +{ + sb_mixer_ioctl +}; + +static void +sb_mixer_reset (void) +{ + int i; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + sb_mixer_set (i, levels[i]); + set_recmask (SOUND_MASK_MIC); +} + +/* + * Returns a code depending on whether a SG NX Pro was detected. + * 0 == Plain SB 16 or SB Pro + * 1 == SG NX Pro detected. + * + * Used to update message. + */ +int +sb_mixer_init (int major_model) +{ + int mixerstat; + + sb_setmixer (0x00, 0); /* Reset mixer */ + + mixerstat = detect_mixer (); + + if (!mixerstat) + return 0; /* No mixer. Why? */ + + mixer_initialized = 1; + mixer_model = major_model; + + switch (major_model) + { + case 3: + mixer_caps = SOUND_CAP_EXCL_INPUT; +#ifdef __SGNXPRO__ + if (mixerstat == 2) + { /* A SGNXPRO was detected */ + supported_devices = SGNXPRO_MIXER_DEVICES; + supported_rec_devices = SGNXPRO_RECORDING_DEVICES; + iomap = &sgnxpro_mix; + } + else +#endif + { /* Otherwise plain SB Pro */ + supported_devices = SBPRO_MIXER_DEVICES; + supported_rec_devices = SBPRO_RECORDING_DEVICES; + iomap = &sbpro_mix; + } + + break; + + case 4: + mixer_caps = 0; + supported_devices = SB16_MIXER_DEVICES; + supported_rec_devices = SB16_RECORDING_DEVICES; + iomap = &sb16_mix; + break; + + default: + printk ("SB Warning: Unsupported mixer type\n"); + return 0; + } + + mixer_devs[num_mixers++] = &sb_mixer_operations; + sb_mixer_reset (); + return (mixerstat == 2); +} + +#endif diff --git a/sys/i386/isa/sound/sb_mixer.h b/sys/i386/isa/sound/sb_mixer.h new file mode 100644 index 000000000000..4caf7730226f --- /dev/null +++ b/sys/i386/isa/sound/sb_mixer.h @@ -0,0 +1,212 @@ +/* + * sound/sb_mixer.h + * + * Definitions for the SB Pro and SB16 mixers + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Modified: + * Hunyue Yau Jan 6 1994 + * Added defines for the Sound Galaxy NX Pro mixer. + * + */ + +#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) + +/* Same as SB Pro, unless I find otherwise */ +#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES + +#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_VOLUME) + +/* SG NX Pro has treble and bass settings on the mixer. The 'speaker' + * channel is the COVOX/DisneySoundSource emulation volume control + * on the mixer. It does NOT control speaker volume. Should have own + * mask eventually? + */ +#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \ + SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER ) + +#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD) + +#define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_RECLEV | \ + SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE) + +/* + * Mixer registers + * + * NOTE! RECORD_SRC == IN_FILTER + */ + +/* + * Mixer registers of SB Pro + */ +#define VOC_VOL 0x04 +#define MIC_VOL 0x0A +#define MIC_MIX 0x0A +#define RECORD_SRC 0x0C +#define IN_FILTER 0x0C +#define OUT_FILTER 0x0E +#define MASTER_VOL 0x22 +#define FM_VOL 0x26 +#define CD_VOL 0x28 +#define LINE_VOL 0x2E +#define IRQ_NR 0x80 +#define DMA_NR 0x81 +#define IRQ_STAT 0x82 +#define OPSW 0x3c + +/* + * Additional registers on the SG NX Pro + */ +#define COVOX_VOL 0x42 +#define TREBLE_LVL 0x44 +#define BASS_LVL 0x46 + +#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */ +#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */ +#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */ +#define FILT_OFF (1 << 5) + +#define MONO_DAC 0x00 +#define STEREO_DAC 0x02 + +/* + * Mixer registers of SB16 + */ +#define SB16_IMASK_L 0x3d +#define SB16_IMASK_R 0x3e + +#define LEFT_CHN 0 +#define RIGHT_CHN 1 + +struct mixer_def { + unsigned int regno: 8; + unsigned int bitoffs:4; + unsigned int nbits:4; +}; + + +typedef struct mixer_def mixer_tab[32][2]; +typedef struct mixer_def mixer_ent; + +#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \ + {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}} + +#ifdef __SB_MIXER_C__ +mixer_tab sbpro_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0) +}; + +#ifdef __SGNXPRO__ +mixer_tab sgnxpro_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4), +MIX_ENT(SOUND_MIXER_BASS, 0x46, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x42, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0) +}; +#endif + +mixer_tab sb16_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5), +MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4), +MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4), +MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5), +MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5), +MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2) +}; + +static unsigned short levels[SOUND_MIXER_NRDEVICES] = +{ + 0x5a5a, /* Master Volume */ + 0x3232, /* Bass */ + 0x3232, /* Treble */ + 0x4b4b, /* FM */ + 0x4b4b, /* PCM */ + 0x4b4b, /* PC Speaker */ + 0x4b4b, /* Ext Line */ + 0x0000, /* Mic */ + 0x4b4b, /* CD */ + 0x4b4b, /* Recording monitor */ + 0x4b4b, /* SB PCM */ + 0x4b4b}; /* Recording level */ + +static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] = +{ + 0x00, /* SOUND_MIXER_VOLUME */ + 0x00, /* SOUND_MIXER_BASS */ + 0x00, /* SOUND_MIXER_TREBLE */ + 0x40, /* SOUND_MIXER_SYNTH */ + 0x00, /* SOUND_MIXER_PCM */ + 0x00, /* SOUND_MIXER_SPEAKER */ + 0x10, /* SOUND_MIXER_LINE */ + 0x01, /* SOUND_MIXER_MIC */ + 0x04, /* SOUND_MIXER_CD */ + 0x00, /* SOUND_MIXER_IMIX */ + 0x00, /* SOUND_MIXER_ALTPCM */ + 0x00 /* SOUND_MIXER_RECLEV */ +}; + +static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] = +{ + 0x00, /* SOUND_MIXER_VOLUME */ + 0x00, /* SOUND_MIXER_BASS */ + 0x00, /* SOUND_MIXER_TREBLE */ + 0x20, /* SOUND_MIXER_SYNTH */ + 0x00, /* SOUND_MIXER_PCM */ + 0x00, /* SOUND_MIXER_SPEAKER */ + 0x08, /* SOUND_MIXER_LINE */ + 0x01, /* SOUND_MIXER_MIC */ + 0x02, /* SOUND_MIXER_CD */ + 0x00, /* SOUND_MIXER_IMIX */ + 0x00, /* SOUND_MIXER_ALTPCM */ + 0x00 /* SOUND_MIXER_RECLEV */ +}; +#endif diff --git a/sys/i386/isa/sound/sequencer.c b/sys/i386/isa/sound/sequencer.c index 7df08120ebcc..1a3943289958 100644 --- a/sys/i386/isa/sound/sequencer.c +++ b/sys/i386/isa/sound/sequencer.c @@ -1,10 +1,10 @@ /* - * linux/kernel/chr_drv/sound/sequencer.c - * + * sound/sequencer.c + * * The sequencer personality manager. - * + * * Copyright by Hannu Savolainen 1993 - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright @@ -12,7 +12,7 @@ * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -24,7 +24,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * + * */ #define SEQUENCER_C @@ -37,20 +37,24 @@ static int sequencer_ok = 0; DEFINE_WAIT_QUEUE (seq_sleeper, seq_sleep_flag); -DEFINE_WAIT_QUEUE (midi_sleeper, midi_sleep_flag); +/* DEFINE_WAIT_QUEUE (midi_sleeper, midi_sleep_flag); */ +#define midi_sleeper seq_sleeper +#define midi_sleep_flag seq_sleep_flag static int midi_opened[MAX_MIDI_DEV] = {0}; /* 1 if the process has opened MIDI */ static int midi_written[MAX_MIDI_DEV] = {0}; -long seq_time = 0; /* Reference point for the timer */ +unsigned long seq_time = 0; /* Reference point for the timer */ #include "tuning.h" #define EV_SZ 8 -static unsigned char queue[SEQ_MAX_QUEUE][EV_SZ]; -static unsigned char iqueue[SEQ_MAX_QUEUE][4]; +#define IEV_SZ 4 +static unsigned char *queue = NULL; /* SEQ_MAX_QUEUE * EV_SZ bytes */ +static unsigned char *iqueue = NULL; /* SEQ_MAX_QUEUE * IEV_SZ bytes */ + static volatile int qhead = 0, qtail = 0, qlen = 0; static volatile int iqhead = 0, iqtail = 0, iqlen = 0; static volatile int seq_playing = 0; @@ -83,13 +87,16 @@ sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) { if (!iqlen) { - INTERRUPTIBLE_SLEEP_ON (midi_sleeper, midi_sleep_flag); + if (c != count) /* Some data has been received */ + return count - c; /* Return what we have */ + + DO_SLEEP (midi_sleeper, midi_sleep_flag, 0); if (!iqlen) return count - c; } - COPY_TO_USER (buf, p, &iqueue[iqhead][0], 4); + COPY_TO_USER (buf, p, &iqueue[iqhead * IEV_SZ], IEV_SZ); p += 4; c -= 4; @@ -114,14 +121,14 @@ copy_to_input (unsigned char *event) if (iqlen >= (SEQ_MAX_QUEUE - 1)) return; /* Overflow */ - memcpy (iqueue[iqtail], event, 4); + memcpy (&iqueue[iqtail * IEV_SZ], event, IEV_SZ); iqlen++; iqtail = (iqtail + 1) % SEQ_MAX_QUEUE; DISABLE_INTR (flags); - if (midi_sleep_flag) + if (SOMEONE_WAITING (midi_sleeper, midi_sleep_flag)) { - WAKE_UP (midi_sleeper); + WAKE_UP (midi_sleeper, midi_sleep_flag); } RESTORE_INTR (flags); } @@ -266,16 +273,16 @@ seq_queue (unsigned char *note) if (!seq_playing) seq_startplay (); /* Give chance to drain the queue */ - if (qlen >= SEQ_MAX_QUEUE && !seq_sleep_flag) + if (qlen >= SEQ_MAX_QUEUE && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) { /* Sleep until there is enough space on the queue */ - INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag); + DO_SLEEP (seq_sleeper, seq_sleep_flag, 0); } if (qlen >= SEQ_MAX_QUEUE) return 0; /* To be sure */ - memcpy (&queue[qtail][0], note, EV_SZ); + memcpy (&queue[qtail * EV_SZ], note, EV_SZ); qtail = (qtail + 1) % SEQ_MAX_QUEUE; qlen++; @@ -323,6 +330,10 @@ extended_event (unsigned char *q) synth_devs[dev]->controller (dev, q[3], q[4], *(short *) &q[5]); break; + case SEQ_VOLMODE: + synth_devs[dev]->volume_method (dev, q[3]); + break; + default: return RET_ERROR (EINVAL); } @@ -342,7 +353,7 @@ seq_startplay (void) qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE; qlen--; - q = &queue[this_one][0]; + q = &queue[this_one * EV_SZ]; switch (q[0]) { @@ -378,10 +389,9 @@ seq_startplay (void) unsigned long flags; DISABLE_INTR (flags); - if (seq_sleep_flag) + if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) { - seq_sleep_flag = 0; - WAKE_UP (seq_sleeper); + WAKE_UP (seq_sleeper, seq_sleep_flag); } RESTORE_INTR (flags); } @@ -449,10 +459,9 @@ seq_startplay (void) unsigned long flags; DISABLE_INTR (flags); - if (seq_sleep_flag) + if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) { - seq_sleep_flag = 0; - WAKE_UP (seq_sleeper); + WAKE_UP (seq_sleeper, seq_sleep_flag); } RESTORE_INTR (flags); } @@ -461,86 +470,87 @@ seq_startplay (void) int sequencer_open (int dev, struct fileinfo *file) - { - int retval, mode, i; +{ + int retval, mode, i; - dev = dev >> 4; - mode = file->mode & O_ACCMODE; + dev = dev >> 4; + mode = file->mode & O_ACCMODE; - DEB (printk ("sequencer_open(dev=%d)\n", dev)); + DEB (printk ("sequencer_open(dev=%d)\n", dev)); - if (!sequencer_ok) - { - printk ("Soundcard: Sequencer not initialized\n"); - return RET_ERROR (ENXIO); - } + if (!sequencer_ok) + { + printk ("Soundcard: Sequencer not initialized\n"); + return RET_ERROR (ENXIO); + } - if (dev) /* Patch manager device */ - { - int err; + if (dev) /* Patch manager device */ + { + int err; - dev--; - if (pmgr_present[dev]) - return RET_ERROR (EBUSY); - if ((err = pmgr_open (dev)) < 0) - return err; /* Failed */ + dev--; + if (pmgr_present[dev]) + return RET_ERROR (EBUSY); + if ((err = pmgr_open (dev)) < 0) + return err; /* Failed */ - pmgr_present[dev] = 1; - return err; - } + pmgr_present[dev] = 1; + return err; + } - if (sequencer_busy) - { - printk ("Sequencer busy\n"); - return RET_ERROR (EBUSY); - } + if (sequencer_busy) + { + printk ("Sequencer busy\n"); + return RET_ERROR (EBUSY); + } - if (!(num_synths + num_midis)) - return RET_ERROR (ENXIO); + if (!(num_synths + num_midis)) + return RET_ERROR (ENXIO); - synth_open_mask = 0; + synth_open_mask = 0; - if (mode == OPEN_WRITE || mode == OPEN_READWRITE) - for (i = 0; i < num_synths; i++) /* Open synth devices */ - if (synth_devs[i]->open (i, mode) < 0) - printk ("Sequencer: Warning! Cannot open synth device #%d\n", i); - else - synth_open_mask |= (1 << i); + if (mode == OPEN_WRITE || mode == OPEN_READWRITE) + for (i = 0; i < num_synths; i++) /* Open synth devices */ + if (synth_devs[i]->open (i, mode) < 0) + printk ("Sequencer: Warning! Cannot open synth device #%d\n", i); + else + synth_open_mask |= (1 << i); - seq_time = GET_TIME (); + seq_time = GET_TIME (); - for (i = 0; i < num_midis; i++) - { - midi_opened[i] = 0; - midi_written[i] = 0; - } + for (i = 0; i < num_midis; i++) + { + midi_opened[i] = 0; + midi_written[i] = 0; + } - if (mode == OPEN_READ || mode == OPEN_READWRITE) - { /* Initialize midi input devices */ - if (!num_midis) - { - printk ("Sequencer: No Midi devices. Input not possible\n"); - return RET_ERROR (ENXIO); - } + if (mode == OPEN_READ || mode == OPEN_READWRITE) + { /* Initialize midi input devices */ + if (!num_midis) + { + printk ("Sequencer: No Midi devices. Input not possible\n"); + return RET_ERROR (ENXIO); + } - for (i = 0; i < num_midis; i++) - { - if ((retval = midi_devs[i]->open (i, mode, + for (i = 0; i < num_midis; i++) + { + if ((retval = midi_devs[i]->open (i, mode, sequencer_midi_input, sequencer_midi_output)) >= 0) - midi_opened[i] = 1; - } - } + midi_opened[i] = 1; + } + } - sequencer_busy = 1; - seq_sleep_flag = midi_sleep_flag = 0; - output_treshold = SEQ_MAX_QUEUE / 2; + sequencer_busy = 1; + RESET_WAIT_QUEUE (seq_sleeper, seq_sleep_flag); + RESET_WAIT_QUEUE (midi_sleeper, midi_sleep_flag); + output_treshold = SEQ_MAX_QUEUE / 2; - for (i = 0; i < num_synths; i++) - if (pmgr_present[i]) - pmgr_inform (i, PM_E_OPENED, 0, 0, 0, 0); + for (i = 0; i < num_synths; i++) + if (pmgr_present[i]) + pmgr_inform (i, PM_E_OPENED, 0, 0, 0, 0); - return 0; - } + return 0; +} void seq_drain_midi_queues (void) @@ -553,7 +563,7 @@ seq_drain_midi_queues (void) n = 1; - while (!PROCESS_ABORTING && n) + while (!PROCESS_ABORTING (midi_sleeper, midi_sleep_flag) && n) { n = 0; @@ -568,70 +578,70 @@ seq_drain_midi_queues (void) */ if (n) { - REQUEST_TIMEOUT (HZ / 10, seq_sleeper); - INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag); + DO_SLEEP (seq_sleeper, seq_sleep_flag, HZ / 10); } } } void sequencer_release (int dev, struct fileinfo *file) - { - int i; - int mode = file->mode & O_ACCMODE; +{ + int i; + int mode = file->mode & O_ACCMODE; - dev = dev >> 4; + dev = dev >> 4; - DEB (printk ("sequencer_release(dev=%d)\n", dev)); + DEB (printk ("sequencer_release(dev=%d)\n", dev)); - if (dev) /* Patch manager device */ - { - dev--; - pmgr_release (dev); - pmgr_present[dev] = 0; - return; - } + if (dev) /* Patch manager device */ + { + dev--; + pmgr_release (dev); + pmgr_present[dev] = 0; + return; + } - /* + /* * Wait until the queue is empty - */ - while (!PROCESS_ABORTING && qlen) - { - seq_sync (); - } + */ - if (mode != OPEN_READ) - seq_drain_midi_queues (); /* Ensure the output queues are empty */ - seq_reset (); - if (mode != OPEN_READ) - seq_drain_midi_queues (); /* Flush the all notes off messages */ + while (!PROCESS_ABORTING (seq_sleeper, seq_sleep_flag) && qlen) + { + seq_sync (); + } - for (i = 0; i < num_midis; i++) - if (midi_opened[i]) - midi_devs[i]->close (i); + if (mode != OPEN_READ) + seq_drain_midi_queues (); /* Ensure the output queues are empty */ + seq_reset (); + if (mode != OPEN_READ) + seq_drain_midi_queues (); /* Flush the all notes off messages */ - if (mode == OPEN_WRITE || mode == OPEN_READWRITE) - for (i = 0; i < num_synths; i++) - if (synth_open_mask & (1 << i)) /* Actually opened */ - if (synth_devs[i]) - synth_devs[i]->close (i); + for (i = 0; i < num_midis; i++) + if (midi_opened[i]) + midi_devs[i]->close (i); + if (mode == OPEN_WRITE || mode == OPEN_READWRITE) for (i = 0; i < num_synths; i++) - if (pmgr_present[i]) - pmgr_inform (i, PM_E_CLOSED, 0, 0, 0, 0); + if (synth_open_mask & (1 << i)) /* Actually opened */ + if (synth_devs[i]) + synth_devs[i]->close (i); - sequencer_busy = 0; - } + for (i = 0; i < num_synths; i++) + if (pmgr_present[i]) + pmgr_inform (i, PM_E_CLOSED, 0, 0, 0, 0); + + sequencer_busy = 0; +} static int seq_sync (void) { - if (qlen && !seq_playing && !PROCESS_ABORTING) + if (qlen && !seq_playing && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag)) seq_startplay (); - if (qlen && !seq_sleep_flag) /* Queue not empty */ + if (qlen && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) /* Queue not empty */ { - INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag); + DO_SLEEP (seq_sleeper, seq_sleep_flag, 0); } return qlen; @@ -654,8 +664,7 @@ midi_outc (int dev, unsigned char data) while (n && !midi_devs[dev]->putc (dev, data)) { - REQUEST_TIMEOUT (1, seq_sleeper); - INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag); + DO_SLEEP (seq_sleeper, seq_sleep_flag, 4); n--; } } @@ -684,7 +693,8 @@ seq_reset (void) { for (chn = 0; chn < 16; chn++) { - midi_outc (i, 0xb0 + chn); /* Channel message */ + midi_outc (i, + (unsigned char) (0xb0 + (chn & 0xff))); /* Channel msg */ midi_outc (i, 0x7b);/* All notes off */ midi_outc (i, 0); /* Dummy parameter */ } @@ -697,7 +707,7 @@ seq_reset (void) seq_playing = 0; - if (seq_sleep_flag) + if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) printk ("Sequencer Warning: Unexpected sleeping process\n"); } @@ -720,7 +730,7 @@ sequencer_ioctl (int dev, struct fileinfo *file, if (mode == OPEN_READ) return 0; - while (qlen && !PROCESS_ABORTING) + while (qlen && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag)) seq_sync (); return 0; break; @@ -979,7 +989,6 @@ sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * w case SEL_IN: if (!iqlen) { - midi_sleep_flag = 1; select_wait (&midi_sleeper, wait); return 0; } @@ -990,7 +999,6 @@ sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * w case SEL_OUT: if (qlen >= SEQ_MAX_QUEUE) { - seq_sleep_flag = 1; select_wait (&seq_sleeper, wait); return 0; } @@ -1039,7 +1047,7 @@ note_to_freq (int note_num) else if (octave > BASE_OCTAVE) note_freq <<= (octave - BASE_OCTAVE); - /* note_freq >>= 1; */ + /* note_freq >>= 1; */ return note_freq; } @@ -1048,7 +1056,7 @@ unsigned long compute_finetune (unsigned long base_freq, int bend, int range) { unsigned long amount; - int negative, semitones, cents; + int negative, semitones, cents, multiplier = 1; if (!bend) return base_freq; @@ -1072,13 +1080,20 @@ compute_finetune (unsigned long base_freq, int bend, int range) if (bend > range) bend = range; - if (bend > 2399) - bend = 2399; + /* + if (bend > 2399) + bend = 2399; + */ + while (bend > 2399) + { + multiplier *= 4; + bend -= 2400; + } semitones = bend / 100; cents = bend % 100; - amount = semitone_tuning[semitones] * cent_tuning[cents] / 10000; + amount = semitone_tuning[semitones] * multiplier * cent_tuning[cents] / 10000; if (negative) return (base_freq * 10000) / amount; /* Bend down */ @@ -1092,6 +1107,9 @@ sequencer_init (long mem_start) { sequencer_ok = 1; + PERMANENT_MALLOC (unsigned char *, queue, SEQ_MAX_QUEUE * EV_SZ, mem_start); + PERMANENT_MALLOC (unsigned char *, iqueue, SEQ_MAX_QUEUE * IEV_SZ, mem_start); + return mem_start; } @@ -1111,14 +1129,14 @@ sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) int sequencer_open (int dev, struct fileinfo *file) - { - return RET_ERROR (ENXIO); - } +{ + return RET_ERROR (ENXIO); +} void sequencer_release (int dev, struct fileinfo *file) - { - } +{ +} int sequencer_ioctl (int dev, struct fileinfo *file, unsigned int cmd, unsigned int arg) @@ -1138,11 +1156,14 @@ sequencer_init (long mem_start) return mem_start; } +#ifdef ALLOW_SELECT + int sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait) { return RET_ERROR (EIO); } +#endif /* ALLOW_SELECT */ #endif diff --git a/sys/i386/isa/sound/sound_calls.h b/sys/i386/isa/sound/sound_calls.h index 89bd79604359..abc82001b776 100644 --- a/sys/i386/isa/sound/sound_calls.h +++ b/sys/i386/isa/sound/sound_calls.h @@ -16,23 +16,10 @@ int DMAbuf_open_dma (int chan); void DMAbuf_close_dma (int chan); void DMAbuf_reset_dma (int chan); void DMAbuf_inputintr(int dev); -void DMAbuf_outputintr(int dev); +void DMAbuf_outputintr(int dev, int underflow_flag); /* - * System calls for the /dev/dsp - */ - -int dsp_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); -int dsp_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); -int dsp_open (int dev, struct fileinfo *file, int bits); -void dsp_release (int dev, struct fileinfo *file); -int dsp_ioctl (int dev, struct fileinfo *file, - unsigned int cmd, unsigned int arg); -int dsp_lseek (int dev, struct fileinfo *file, off_t offset, int orig); -long dsp_init (long mem_start); - -/* - * System calls for the /dev/audio + * System calls for /dev/dsp and /dev/audio */ int audio_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); @@ -108,11 +95,47 @@ void tenmicrosec(void); void request_sound_timer (int count); void sound_stop_timer(void); int snd_ioctl_return(int *addr, int value); +int snd_set_irq_handler (int interrupt_level, void(*hndlr)(int)); +void snd_release_irq(int vect); +void sound_dma_malloc(int dev); +void sound_dma_free(int dev); + +/* From sound_switch.c */ +int sound_read_sw (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int sound_write_sw (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int sound_open_sw (int dev, struct fileinfo *file); +void sound_release_sw (int dev, struct fileinfo *file); +int sound_ioctl_sw (int dev, struct fileinfo *file, + unsigned int cmd, unsigned long arg); /* From sb_dsp.c */ int sb_dsp_detect (struct address_info *hw_config); long sb_dsp_init (long mem_start, struct address_info *hw_config); void sb_dsp_disable_midi(void); +int sb_get_irq(void); +void sb_free_irq(void); +int sb_dsp_command (unsigned char val); +int sb_reset_dsp (void); + +/* From sb16_dsp.c */ +void sb16_dsp_interrupt (int unused); +long sb16_dsp_init(long mem_start, struct address_info *hw_config); +int sb16_dsp_detect(struct address_info *hw_config); + +/* From sb16_midi.c */ +void sb16midiintr (int unit); +long attach_sb16midi(long mem_start, struct address_info * hw_config); +int probe_sb16midi(struct address_info *hw_config); + +/* From sb_midi.c */ +void sb_midi_init(int model); +void sb_midi_interrupt(int dummy); + +/* From sb_mixer.c */ +void sb_setmixer (unsigned int port, unsigned int value); +int sb_getmixer (unsigned int port); +void sb_mixer_set_stereo(int mode); +int sb_mixer_init(int major_model); /* From opl3.c */ int opl3_detect (int ioaddr); @@ -156,9 +179,10 @@ int gus_wave_detect(int baseaddr); long gus_wave_init(long mem_start, int irq, int dma); void gus_voice_irq(void); unsigned char gus_read8 (int reg); -void gus_write8(int reg, unsigned char data); +void gus_write8(int reg, unsigned int data); void guswave_dma_irq(void); void gus_delay(void); +int gus_default_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg); /* From gus_midi.c */ long gus_midi_init(long mem_start); @@ -179,3 +203,6 @@ int pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count); int pmgr_access(int dev, struct patmgr_info *rec); int pmgr_inform(int dev, int event, unsigned long parm1, unsigned long parm2, unsigned long parm3, unsigned long parm4); + +/* From ics2101.c */ +long ics2101_mixer_init(long mem_start); diff --git a/sys/i386/isa/sound/sound_config.h b/sys/i386/isa/sound/sound_config.h index 9cbd8103b8f5..0d30c12aadc4 100644 --- a/sys/i386/isa/sound/sound_config.h +++ b/sys/i386/isa/sound/sound_config.h @@ -30,6 +30,16 @@ #include "local.h" + +#undef CONFIGURE_SOUNDCARD +#undef DYNAMIC_BUFFER + +#ifdef KERNEL_SOUNDCARD +#define CONFIGURE_SOUNDCARD +#define DYNAMIC_BUFFER +#undef LOADABLE_SOUNDCARD +#endif + #ifdef EXCLUDE_SEQUENCER #ifndef EXCLUDE_MIDI #define EXCLUDE_MIDI @@ -42,6 +52,10 @@ #endif #endif +#ifndef SND_DEFAULT_ENABLE +#define SND_DEFAULT_ENABLE 1 +#endif + /** UWM - new MIDI stuff **/ #ifdef EXCLUDE_CHIP_MIDI @@ -75,6 +89,14 @@ If your card has nonstandard I/O address or IRQ number, change defines #define SBC_DMA 1 #endif +#ifndef SB16_DMA +#define SB16_DMA 6 +#endif + +#ifndef SB16MIDI_BASE +#define SB16MIDI_BASE 0x300 +#endif + #ifndef PAS_BASE #define PAS_BASE 0x388 #endif @@ -111,6 +133,10 @@ If your card has nonstandard I/O address or IRQ number, change defines #define MPU_IRQ 6 #endif +#ifndef MAX_REALTIME_FACTOR +#define MAX_REALTIME_FACTOR 4 +#endif + /************* PCM DMA buffer sizes *******************/ /* If you are using high playback or recording speeds, the default buffersize @@ -132,8 +158,6 @@ If your card has nonstandard I/O address or IRQ number, change defines #define DMA_AUTOINIT 0x10 -#define SND_MAJOR 14 - #define FM_MONO 0x388 /* This is the I/O address used by AdLib */ /* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the @@ -178,10 +202,10 @@ If your card has nonstandard I/O address or IRQ number, change defines #define ON 1 #define OFF 0 -#define MAX_DSP_DEV 3 -#define MAX_MIXER_DEV 1 +#define MAX_DSP_DEV 4 +#define MAX_MIXER_DEV 2 #define MAX_SYNTH_DEV 3 -#define MAX_MIDI_DEV 3 +#define MAX_MIDI_DEV 4 struct fileinfo { int mode; /* Open mode */ @@ -193,6 +217,15 @@ struct address_info { int dma; }; +/* + * Process wakeup reasons + */ +#define WK_NONE 0x00 +#define WK_WAKEUP 0x01 +#define WK_TIMEOUT 0x02 +#define WK_SIGNAL 0x04 +#define WK_SLEEP 0x08 + #define OPEN_READ 1 #define OPEN_WRITE 2 #define OPEN_READWRITE 3 diff --git a/sys/i386/isa/sound/sound_switch.c b/sys/i386/isa/sound/sound_switch.c new file mode 100644 index 000000000000..68c757586083 --- /dev/null +++ b/sys/i386/isa/sound/sound_switch.c @@ -0,0 +1,445 @@ +/* + * sound/sound_switch.c + * + * The system call switch + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +struct sbc_device + { + int usecount; + }; + +static struct sbc_device sbc_devices[SND_NDEVS] = +{ + {0}}; + +static int in_use = 0; /* Total # of open device files (excluding + + * minor 0) */ + +/* + * /dev/sndstatus -device + */ +static char *status_buf = NULL; +static int status_len, status_ptr; +static int status_busy = 0; + +static int +put_status (char *s) +{ + int l; + + for (l = 0; l < 256, s[l]; l++); /* l=strlen(s); */ + + if (status_len + l >= 4000) + return 0; + + memcpy (&status_buf[status_len], s, l); + status_len += l; + + return 1; +} + +static int +put_status_int (unsigned int val, int radix) +{ + int l, v; + + static char hx[] = "0123456789abcdef"; + char buf[11]; + + if (!val) + return put_status ("0"); + + l = 0; + buf[10] = 0; + + while (val) + { + v = val % radix; + val = val / radix; + + buf[9 - l] = hx[v]; + l++; + } + + if (status_len + l >= 4000) + return 0; + + memcpy (&status_buf[status_len], &buf[10 - l], l); + status_len += l; + + return 1; +} + +static void +init_status (void) +{ + /* + * Write the status information to the status_buf and update status_len. + * There is a limit of 4000 bytes for the data. + */ + + int i; + + status_ptr = 0; + + put_status ("Sound Driver:" SOUND_VERSION_STRING + " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@" + SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")" + "\n"); + + if (!put_status ("Config options: ")) + return; + if (!put_status_int (SELECTED_SOUND_OPTIONS, 16)) + return; + + if (!put_status ("\n\nHW config: \n")) + return; + + for (i = 0; i < (num_sound_drivers - 1); i++) + { + if (!supported_drivers[i].enabled) + if (!put_status ("(")) + return; + + if (!put_status ("Type ")) + return; + if (!put_status_int (supported_drivers[i].card_type, 10)) + return; + if (!put_status (": ")) + return; + if (!put_status (supported_drivers[i].name)) + return; + if (!put_status (" at 0x")) + return; + if (!put_status_int (supported_drivers[i].config.io_base, 16)) + return; + if (!put_status (" irq ")) + return; + if (!put_status_int (supported_drivers[i].config.irq, 10)) + return; + if (!put_status (" drq ")) + return; + if (!put_status_int (supported_drivers[i].config.dma, 10)) + return; + + if (!supported_drivers[i].enabled) + if (!put_status (")")) + return; + + if (!put_status ("\n")) + return; + } + + if (!put_status ("\nPCM devices:\n")) + return; + + for (i = 0; i < num_dspdevs; i++) + { + if (!put_status_int (i, 10)) + return; + if (!put_status (": ")) + return; + if (!put_status (dsp_devs[i]->name)) + return; + if (!put_status ("\n")) + return; + } + + if (!put_status ("\nSynth devices:\n")) + return; + + for (i = 0; i < num_synths; i++) + { + if (!put_status_int (i, 10)) + return; + if (!put_status (": ")) + return; + if (!put_status (synth_devs[i]->info->name)) + return; + if (!put_status ("\n")) + return; + } + + if (!put_status ("\nMidi devices:\n")) + return; + + for (i = 0; i < num_midis; i++) + { + if (!put_status_int (i, 10)) + return; + if (!put_status (": ")) + return; + if (!put_status (midi_devs[i]->info.name)) + return; + if (!put_status ("\n")) + return; + } + + if (num_mixers) + { + if (!put_status ("\nMixer(s) installed\n")) + return; + } + else + { + if (!put_status ("\nNo mixers installed\n")) + return; + } +} + +static int +read_status (snd_rw_buf * buf, int count) +{ + /* + * Return at most 'count' bytes from the status_buf. + */ + int l, c; + + l = count; + c = status_len - status_ptr; + + if (l > c) + l = c; + if (l <= 0) + return 0; + + COPY_TO_USER (buf, 0, &status_buf[status_ptr], l); + status_ptr += l; + + return l; +} + +int +sound_read_sw (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + DEB (printk ("sound_read_sw(dev=%d, count=%d)\n", dev, count)); + + switch (dev & 0x0f) + { + case SND_DEV_STATUS: + return read_status (buf, count); + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return audio_read (dev, file, buf, count); + break; + + case SND_DEV_SEQ: + return sequencer_read (dev, file, buf, count); + break; + +#ifndef EXCLUDE_MPU401 + case SND_DEV_MIDIN: + return MIDIbuf_read (dev, file, buf, count); +#endif + + default: + printk ("Sound: Undefined minor device %d\n", dev); + } + + return RET_ERROR (EPERM); +} + +int +sound_write_sw (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + + DEB (printk ("sound_write_sw(dev=%d, count=%d)\n", dev, count)); + + switch (dev & 0x0f) + { + + case SND_DEV_SEQ: + return sequencer_write (dev, file, buf, count); + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return audio_write (dev, file, buf, count); + break; + + default: + return RET_ERROR (EPERM); + } + + return count; +} + +int +sound_open_sw (int dev, struct fileinfo *file) +{ + int retval; + + DEB (printk ("sound_open_sw(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount)); + + if ((dev >= SND_NDEVS) || (dev < 0)) + { + printk ("Invalid minor device %d\n", dev); + return RET_ERROR (ENXIO); + } + + switch (dev & 0x0f) + { + case SND_DEV_STATUS: + if (status_busy) + return RET_ERROR (EBUSY); + status_busy = 1; + if ((status_buf = (char *) KERNEL_MALLOC (4000)) == NULL) + return RET_ERROR (EIO); + status_len = status_ptr = 0; + init_status (); + break; + + case SND_DEV_CTL: + return 0; + break; + + case SND_DEV_SEQ: + if ((retval = sequencer_open (dev, file)) < 0) + return retval; + break; + +#ifndef EXCLUDE_MPU401 + case SND_DEV_MIDIN: + if ((retval = MIDIbuf_open (dev, file)) < 0) + return retval; + break; +#endif + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + if ((retval = audio_open (dev, file)) < 0) + return retval; + break; + + default: + printk ("Invalid minor device %d\n", dev); + return RET_ERROR (ENXIO); + } + + sbc_devices[dev].usecount++; + in_use++; + + return 0; +} + +void +sound_release_sw (int dev, struct fileinfo *file) +{ + + DEB (printk ("sound_release_sw(dev=%d)\n", dev)); + + switch (dev & 0x0f) + { + case SND_DEV_STATUS: + if (status_buf) + KERNEL_FREE (status_buf); + status_buf = NULL; + status_busy = 0; + break; + + case SND_DEV_CTL: + break; + + case SND_DEV_SEQ: + sequencer_release (dev, file); + break; + +#ifndef EXCLUDE_MPU401 + case SND_DEV_MIDIN: + MIDIbuf_release (dev, file); + break; +#endif + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + audio_release (dev, file); + break; + + default: + printk ("Sound error: Releasing unknown device 0x%02x\n", dev); + } + + sbc_devices[dev].usecount--; + in_use--; +} + +int +sound_ioctl_sw (int dev, struct fileinfo *file, + unsigned int cmd, unsigned long arg) +{ + DEB (printk ("sound_ioctl_sw(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); + + switch (dev & 0x0f) + { + + case SND_DEV_CTL: + + if (!num_mixers) + return RET_ERROR (ENXIO); + + if ((dev >> 4) >= num_mixers) + return RET_ERROR (ENXIO); + + return mixer_devs[dev >> 4]->ioctl (dev >> 4, cmd, arg); + break; + + case SND_DEV_SEQ: + return sequencer_ioctl (dev, file, cmd, arg); + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return audio_ioctl (dev, file, cmd, arg); + break; + +#ifndef EXCLUDE_MPU401 + case SND_DEV_MIDIN: + return MIDIbuf_ioctl (dev, file, cmd, arg); + break; +#endif + + default: + return RET_ERROR (EPERM); + break; + } + + return RET_ERROR (EPERM); +} + +#endif diff --git a/sys/i386/isa/sound/soundcard.c b/sys/i386/isa/sound/soundcard.c index 79c9b09298d1..9691e4f07bec 100644 --- a/sys/i386/isa/sound/soundcard.c +++ b/sys/i386/isa/sound/soundcard.c @@ -34,28 +34,20 @@ #include "dev_table.h" -int __timeout_val = 0; -int __process_aborting = 0; - -u_int snd1mask; -u_int snd2mask; -u_int snd3mask; -u_int snd4mask; -u_int snd5mask; - -struct sbc_device -{ - int usecount; -}; +u_int snd1_imask; +u_int snd2_imask; +u_int snd3_imask; +u_int snd4_imask; +u_int snd5_imask; +u_int snd6_imask; +u_int snd7_imask; +u_int snd8_imask; +u_int snd9_imask; #define FIX_RETURN(ret) {if ((ret)<0) return -(ret); else return 0;} -static struct sbc_device sbc_devices[SND_NDEVS]; static int timer_running = 0; -static int in_use = 0; /* Total # of open device files (excluding - * minor 0) */ - static int soundcards_installed = 0; /* Number of installed * soundcards */ static int soundcard_configured = 0; @@ -75,12 +67,19 @@ int sndwrite (int dev, struct uio *uio); int sndselect (int dev, int rw); static void sound_mem_init(void); +unsigned long -get_time() +get_time(void) { extern struct timeval time; - - return(time.tv_usec + (time.tv_sec*1000000)); +struct timeval timecopy; +int x; + + x = splclock(); + timecopy = time; + splx(x); + return timecopy.tv_usec/(1000000/HZ) + + (unsigned long)timecopy.tv_sec*HZ; } @@ -91,41 +90,7 @@ sndread (int dev, struct uio *buf) dev = minor (dev); - DEB (printk ("sound_read(dev=%d, count=%d)\n", dev, count)); - - switch (dev & 0x0f) /* It really has to be 0x0f */ - { - case SND_DEV_AUDIO: - FIX_RETURN (audio_read (dev, &files[dev], buf, count)); - break; - - case SND_DEV_DSP: - case SND_DEV_DSP16: - FIX_RETURN (dsp_read (dev, &files[dev], buf, count)); - break; - - case SND_DEV_SEQ: - FIX_RETURN (sequencer_read (dev, &files[dev], buf, count)); - break; - -#ifndef EXCLUDE_CHIP_MIDI - case CMIDI_DEV_PRO: - FIX_RETURN (CMIDI_read (dev, &files[dev], buf, count)); - - break; -#endif - - -#ifndef EXCLUDE_MPU401 - case SND_DEV_MIDIN: - FIX_RETURN (MIDIbuf_read (dev, &files[dev], buf, count)); -#endif - - default: - ; - } - - FIX_RETURN (-EPERM); + FIX_RETURN (sound_read_sw (dev, &files[dev], buf, count)); } int @@ -133,37 +98,9 @@ sndwrite (int dev, struct uio *buf) { int count = buf->uio_resid; - DEB (printk ("sound_write(dev=%d, count=%d)\n", dev, count)); - dev = minor (dev); - switch (dev & 0x0f) /* It really has to be 0x0f */ - { - - case SND_DEV_SEQ: - FIX_RETURN (sequencer_write (dev, &files[dev], buf, count)); - break; - - case SND_DEV_AUDIO: - FIX_RETURN (audio_write (dev, &files[dev], buf, count)); - break; - - case SND_DEV_DSP: - case SND_DEV_DSP16: - FIX_RETURN (dsp_write (dev, &files[dev], buf, count)); - break; - -#ifndef EXCLUDE_CHIP_MIDI - case CMIDI_DEV_PRO: - FIX_RETURN (CMIDI_write (dev, &files[dev], buf, count)); - break; -#endif - - default: - FIX_RETURN (-EPERM); - } - - FIX_RETURN (count); + FIX_RETURN (sound_write_sw (dev, &files[dev], buf, count)); } int @@ -173,16 +110,6 @@ sndopen (dev_t dev, int flags) dev = minor (dev); - /* printf("SND: Minor number is now : %ld\n",dev); */ - - DEB (printk ("sound_open(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount)); - - if ((dev >= SND_NDEVS) || (dev < 0)) - { - printk ("Invalid minor device %d\n", dev); - FIX_RETURN (-ENODEV); - } - if (!soundcard_configured && dev) { printk ("SoundCard Error: The soundcard system has not been configured\n"); @@ -198,62 +125,7 @@ sndopen (dev_t dev, int flags) else if (flags & FWRITE) files[dev].mode = OPEN_WRITE; - switch (dev & 0x0f) /* It has to be 0x0f. Trust me */ - { - case SND_DEV_CTL: - if (!soundcards_installed) - if (soundcard_configured) - { - printk ("Soundcard not installed\n"); - FIX_RETURN (-ENODEV); - } - break; - - case SND_DEV_SEQ: - if ((retval = sequencer_open (dev, &files[dev])) < 0) - FIX_RETURN (retval); - break; - -/** UWM stuff **/ - -#ifndef EXCLUDE_CHIP_MIDI - case CMIDI_DEV_PRO: - FIX_RETURN ( CMIDI_open (dev, &files[dev]) ); - break; -#endif - - -#ifndef EXCLUDE_MPU401 - case SND_DEV_MIDIN: - if ((retval = MIDIbuf_open (dev, &files[dev])) < 0) - FIX_RETURN (retval); - break; -#endif - - case SND_DEV_AUDIO: - if ((retval = audio_open (dev, &files[dev])) < 0) - FIX_RETURN (retval); - break; - - case SND_DEV_DSP: - if ((retval = dsp_open (dev, &files[dev], 8)) < 0) - FIX_RETURN (retval); - break; - - case SND_DEV_DSP16: - if ((retval = dsp_open (dev, &files[dev], 16)) < 0) - FIX_RETURN (retval); - break; - - default: - printk ("Invalid minor device %d\n", dev); - FIX_RETURN (-ENODEV); - } - - sbc_devices[dev].usecount++; - in_use++; - - FIX_RETURN (0); + FIX_RETURN(sound_open_sw (dev, &files[dev])); } int @@ -262,41 +134,7 @@ sndclose (dev_t dev, int flags) dev = minor (dev); - DEB (printk ("sound_release(dev=%d)\n", dev)); - - switch (dev & 0x0f) /* Has to be 0x0f */ - { - case SND_DEV_SEQ: - sequencer_release (dev, &files[dev]); - break; - -#ifndef EXCLUDE_CHIP_MIDI - case CMIDI_DEV_PRO: - CMIDI_close (dev, &files[dev]); - break; -#endif - -#ifndef EXCLUDE_MPU401 - case SND_DEV_MIDIN: - MIDIbuf_release (dev, &files[dev]); - break; -#endif - - case SND_DEV_AUDIO: - audio_release (dev, &files[dev]); - break; - - case SND_DEV_DSP: - case SND_DEV_DSP16: - dsp_release (dev, &files[dev]); - break; - - default:; - } - - sbc_devices[dev].usecount--; - in_use--; /* If not control port */ - + sound_release_sw(dev, &files[dev]); FIX_RETURN (0); } @@ -305,46 +143,7 @@ sndioctl (dev_t dev, int cmd, caddr_t arg, int mode) { dev = minor (dev); - DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); - - switch (dev & 0x0f) - { - - case SND_DEV_CTL: - if (!num_mixers) - FIX_RETURN (-ENODEV); - - if (dev >= num_mixers) - FIX_RETURN (-ENODEV); - - FIX_RETURN (mixer_devs[dev]->ioctl (dev, cmd, (unsigned int) arg)); - break; - - case SND_DEV_SEQ: - FIX_RETURN (sequencer_ioctl (dev, &files[dev], cmd, (unsigned int) arg)); - break; - - case SND_DEV_AUDIO: - FIX_RETURN (audio_ioctl (dev, &files[dev], cmd, (unsigned int) arg)); - break; - - case SND_DEV_DSP: - case SND_DEV_DSP16: - FIX_RETURN (dsp_ioctl (dev, &files[dev], cmd, (unsigned int) arg)); - break; - -#ifndef EXCLUDE_MPU401 - case SND_DEV_MIDIN: - FIX_RETURN (MIDIbuf_ioctl (dev, &files[dev], cmd, (unsigned int) arg)); - break; -#endif - - default: - FIX_RETURN (-EPERM); - break; - } - - FIX_RETURN (-EPERM); + FIX_RETURN (sound_ioctl_sw (dev, &files[dev], cmd, (unsigned int) arg)); } int @@ -380,7 +179,6 @@ sndprobe (struct isa_device *dev) hw_config.io_base = dev->id_iobase; hw_config.irq = ipri_to_irq (dev->id_irq); hw_config.dma = dev->id_drq; - return sndtable_probe (dev->id_unit, &hw_config); } @@ -430,7 +228,6 @@ sndattach (struct isa_device *dev) dsp_initialized = 1; mem_start = DMAbuf_init (mem_start); mem_start = audio_init (mem_start); - mem_start = dsp_init (mem_start); } /** UWM stuff **/ @@ -459,11 +256,6 @@ sndattach (struct isa_device *dev) mem_start = sequencer_init (mem_start); } - for (i = 0; i < SND_NDEVS; i++) - { - sbc_devices[i].usecount = 0; - } - return TRUE; } @@ -514,7 +306,7 @@ void sound_stop_timer (void) { if (timer_running) - untimeout ((timeout_func_t)sequencer_timer, 0); /* XXX should fix */ + untimeout ((timeout_func_t)sequencer_timer, 0); timer_running = 0; } @@ -533,26 +325,12 @@ sound_mem_init (void) dsp_init_mask |= (1 << dev); if (sound_dma_automode[dev]) - { - sound_dma_automode[dev] = 0; /* Not possible with FreeBSD */ - } - - if (sound_buffcounts[dev] == 1) - { - sound_buffcounts[dev] = 2; - sound_buffsizes[dev] /= 2; - } - - if (sound_buffsizes[dev] > 65536) /* Larger is not possible (yet) */ - sound_buffsizes[dev] = 65536; + sound_buffcounts[dev] = 1; -#if 0 if (sound_dsp_dmachan[dev] > 3 && sound_buffsizes[dev] > 65536) dma_pagesize = 131072; /* 128k */ else dma_pagesize = 65536; -#endif - dma_pagesize = 4096; /* use bounce buffer */ /* More sanity checks */ @@ -566,31 +344,17 @@ sound_mem_init (void) for (snd_raw_count[dev] = 0; snd_raw_count[dev] < sound_buffcounts[dev]; snd_raw_count[dev]++) { - /* - * The DMA buffer allocation algorithm hogs memory. We allocate - * a memory area which is two times the requires size. This - * guarantees that it contains at least one valid DMA buffer. - * - * This really needs some kind of finetuning. - */ - char *tmpbuf = malloc (2*sound_buffsizes[dev], M_DEVBUF, M_NOWAIT); - unsigned long addr, rounded; + char *tmpbuf = contigmalloc (sound_buffsizes[dev], M_DEVBUF, M_NOWAIT, + 0xFFFFFFul, 0ul, dma_pagesize - 1); if (tmpbuf == NULL) { printk ("snd: Unable to allocate %d bytes of buffer\n", - 2 * sound_buffsizes[dev]); + sound_buffsizes[dev]); return; } - addr = kvtop (tmpbuf); - /* - * Align the start address - */ - rounded = (addr & ~(dma_pagesize - 1)) + dma_pagesize; - - snd_raw_buf[dev][snd_raw_count[dev]] = - &tmpbuf[rounded - addr]; /* Compute offset */ + snd_raw_buf[dev][snd_raw_count[dev]] = tmpbuf; /* * Use virtual address as the physical address, since * isa_dmastart performs the phys address computation. @@ -616,4 +380,15 @@ snd_ioctl_return (int *addr, int value) return 0; } +int +snd_set_irq_handler (int interrupt_level, void(*hndlr)(int)) +{ + return 1; +} + +void +snd_release_irq(int vect) +{ +} + #endif diff --git a/sys/i386/isa/spkr.c b/sys/i386/isa/spkr.c index 01c81f4b02a3..77bf59630243 100644 --- a/sys/i386/isa/spkr.c +++ b/sys/i386/isa/spkr.c @@ -4,7 +4,7 @@ * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993 * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su> * - * $Id: spkr.c,v 1.7 1994/01/25 23:04:27 ache Exp $ + * $Id: spkr.c,v 1.8 1994/04/21 14:21:50 sos Exp $ */ #include "speaker.h" @@ -17,7 +17,8 @@ #include "errno.h" #include "buf.h" #include "uio.h" - +#include "i386/isa/isa.h" +#include "i386/isa/timerreg.h" #include "machine/speaker.h" /**************** MACHINE DEPENDENT PART STARTS HERE ************************* @@ -48,10 +49,7 @@ * Channel 2 LSB first, (Square Wave) Encoding * MSB second */ -#define PPI 0x61 /* port of Programmable Peripheral Interface */ #define PPI_SPKR 0x03 /* turn these PPI bits on to pass sound */ -#define PIT_CTRL 0x43 /* PIT control address */ -#define PIT_COUNT 0x42 /* PIT count address */ #define PIT_MODE 0xB6 /* set timer mode for sound generation */ /* @@ -75,13 +73,17 @@ unsigned int thz, ticks; /* set timer to generate clicks at given frequency in Hertz */ sps = spltty(); - outb(PIT_CTRL, PIT_MODE); /* prepare timer */ - outb(PIT_COUNT, (divisor & 0xff)); /* send lo byte */ - outb(PIT_COUNT, (divisor >> 8)); /* send hi byte */ + + if (acquire_timer2(PIT_MODE)) { + /* enter list of waiting procs ??? */ + return; + } + outb(TIMER_CNTR2, (divisor & 0xff)); /* send lo byte */ + outb(TIMER_CNTR2, (divisor >> 8)); /* send hi byte */ splx(sps); /* turn the speaker on */ - outb(PPI, inb(PPI) | PPI_SPKR); + outb(IO_PPI, inb(IO_PPI) | PPI_SPKR); /* * Set timeout to endtone function, then give up the timeslice. @@ -89,7 +91,8 @@ unsigned int thz, ticks; * emitted. */ (void) tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks); - outb(PPI, inb(PPI) & ~PPI_SPKR); + outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR); + release_timer2(); } static void rest(ticks) diff --git a/sys/i386/isa/syscons.c b/sys/i386/isa/syscons.c index afd5f468fc54..21558dbaa872 100644 --- a/sys/i386/isa/syscons.c +++ b/sys/i386/isa/syscons.c @@ -35,7 +35,7 @@ * SUCH DAMAGE. * * from:@(#)syscons.c 1.3 940129 - * $Id: syscons.c,v 1.35.2.2 1994/05/03 07:01:43 rgrimes Exp $ + * $Id: syscons.c,v 1.50 1994/05/30 03:16:02 ache Exp $ * */ @@ -64,7 +64,6 @@ #include "machine/psl.h" #include "machine/frame.h" #include "machine/pc/display.h" -#include "iso8859.font" #include "kbdtables.h" #include "sc.h" @@ -74,6 +73,10 @@ #define NCONS 12 #endif +#if !defined(NO_HARDFONTS) +#include "i386/isa/iso8859.font" +#endif + /* status flags */ #define LOCK_KEY_MASK 0x0000F #define LED_MASK 0x00007 @@ -86,15 +89,21 @@ #define VIDEOMEM 0x000A0000 /* misc defines */ -#define MAX_ESC_PAR 3 +#define MAX_ESC_PAR 5 #define TEXT80x25 1 #define TEXT80x50 2 +#define LOAD 1 +#define SAVE 0 #define COL 80 #define ROW 25 #define BELL_DURATION 5 #define BELL_PITCH 800 #define TIMER_FREQ 1193182 /* should be in isa.h */ +#define CONSOLE_BUFSIZE 1024 #define PCBURST 128 +#define FONT_8_LOADED 0x001 +#define FONT_14_LOADED 0x002 +#define FONT_16_LOADED 0x004 /* defines related to hardware addresses */ #define MONO_BASE 0x3B4 /* crt controller base mono */ @@ -163,33 +172,37 @@ static default_attr kernel_default = { (FG_BLACK | BG_LIGHTGREY) << 8 }; -#define CONSOLE_BUFFER_SIZE 1024 -int console_buffer_count; -char console_buffer[CONSOLE_BUFFER_SIZE]; - static scr_stat console[NCONS]; static scr_stat *cur_console = &console[0]; static scr_stat *new_scp, *old_scp; static term_stat kernel_console; static default_attr *current_default; +static int console_buffer_count; +static char console_buffer[CONSOLE_BUFSIZE]; static int switch_in_progress = 0; static u_short *crtat = 0; static u_int crtc_addr = MONO_BASE; static char crtc_vga = 0; static u_char shfts = 0, ctls = 0, alts = 0, agrs = 0, metas = 0; static u_char nlkcnt = 0, clkcnt = 0, slkcnt = 0, alkcnt = 0; +static char *font_8 = NULL, *font_14 = NULL, *font_16 = NULL; +static int fonts_loaded = 0; static char palette[3*256]; static const u_int n_fkey_tab = sizeof(fkey_tab) / sizeof(*fkey_tab); static int cur_cursor_pos = -1; static char in_putc = 0; static char polling = 0; +#if ASYNCH +static u_char kbd_reply = 0; +#endif static int delayed_next_scr; static char saved_console = -1; /* saved console number */ -static long scrn_blank_time = 0; /* screen saver timout value */ +static long scrn_blank_time = 0; /* screen saver timeout value */ static int scrn_blanked = 0; /* screen saver active flag */ static int scrn_saver = 0; /* screen saver routine */ static long scrn_time_stamp; static u_char scr_map[256]; + extern int hz; extern struct timeval time; @@ -233,18 +246,14 @@ static u_char *get_fstr(u_int c, u_int *len); static void update_leds(int which); static void kbd_wait(void); static void kbd_cmd(u_char command); -static void kbd_cmd2(u_char command, u_char arg); -static int kbd_reply(void); static void set_mode(scr_stat *scp); static void set_border(int color); -static void load_font(int segment, int size, char* font); +static void copy_font(int direction, int segment, int size, char* font); static void save_palette(void); static void load_palette(void); static void change_winsize(struct tty *tp, int x, int y); - /* available screen savers */ - static void none_saver(int test); static void blank_saver(int test); static void fade_saver(int test); @@ -269,42 +278,26 @@ static const struct { #if defined(NetBSD) #define VIRTUAL_TTY(x) pc_tty[x] ? (pc_tty[x]) : (pc_tty[x] = ttymalloc()) #define CONSOLE_TTY pc_tty[NCONS] ? (pc_tty[NCONS]) : (pc_tty[NCONS] = ttymalloc()) -#define frametype struct trapframe -#define eflags tf_eflags extern u_short *Crtat; struct tty *pc_tty[NCONS+1]; int ttrstrt(); #endif #if defined(__FreeBSD__) -#define frametype struct trapframe -#define eflags tf_eflags +#define VIRTUAL_TTY(x) (pccons[x] = ttymalloc(pccons[x])) +#define CONSOLE_TTY (pccons[NCONS] = ttymalloc(pccons[NCONS])) #define timeout_t timeout_func_t #define MONO_BUF (KERNBASE+0xB0000) #define CGA_BUF (KERNBASE+0xB8000) -#endif - -#if defined(__386BSD__) && !defined(__FreeBSD__) -#define frametype struct syscframe -#define eflags sf_eflags -#define timeout_t caddr_t -#define MONO_BUF (0xFE0B0000) -#define CGA_BUF (0xFE0B8000) -#endif - -#if defined(__386BSD__) || defined(__FreeBSD__) -#define VIRTUAL_TTY(x) &pccons[x] -#define CONSOLE_TTY &pccons[NCONS] -u_short *Crtat = (u_short *)MONO_BUF; -struct tty pccons[NCONS+1]; -void consinit(void) {scinit();} #include "ddb.h" #if NDDB > 0 #define DDB 1 #endif +struct tty *pccons[NCONS+1]; +u_short *Crtat = (u_short *)MONO_BUF; +void consinit(void) {scinit();} #endif - struct isa_driver scdriver = { pcprobe, pcattach, "sc", }; @@ -312,18 +305,47 @@ struct isa_driver scdriver = { int pcprobe(struct isa_device *dev) { + int i, retries = 5; + unsigned char val; + /* Enable interrupts and keyboard controller */ kbd_wait(); outb(KB_STAT, KB_WRITE); - kbd_cmd(0x4D); + kbd_wait(); + outb(KB_DATA, KB_MODE); - /* Start keyboard stuff RESET */ - for (;;) { - kbd_cmd(KB_RESET); - if (kbd_reply() == KB_ACK && /* command accepted */ - kbd_reply() == 0xaa) /* self test passed */ - break; - printf("Keyboard reset failed\n"); + /* flush any noise in the buffer */ + while (inb(KB_STAT) & KB_BUF_FULL) { + DELAY(10); + (void) inb(KB_DATA); + } + + /* Reset keyboard hardware */ + while (retries--) { + kbd_wait(); + outb(KB_DATA, KB_RESET); + for (i=0; i<100000; i++) { + DELAY(10); + val = inb(KB_DATA); + if (val == KB_ACK || val == KB_ECHO) + goto gotres; + if (val == KB_RESEND) + break; + } + } +gotres: + if (!retries) + printf("scprobe: keyboard won't accept RESET command\n"); + else { +gotack: + DELAY(10); + while ((inb(KB_STAT) & KB_BUF_FULL) == 0) DELAY(10); + DELAY(10); + val = inb(KB_DATA); + if (val == KB_ACK) + goto gotack; + if (val != KB_RESET_DONE) + printf("scprobe: keyboard RESET failed %02x\n", val); } return (IO_KBDSIZE); } @@ -358,15 +380,28 @@ int pcattach(struct isa_device *dev) if (crtc_vga) { get_cursor_shape(&start, &end); #endif +#if defined(HARDFONTS) + font_8 = font_8x8; + font_14 = font_8x14; + font_16 = font_8x16; + fonts_loaded = FONT_8_LOADED|FONT_14_LOADED|FONT_16_LOADED; + copy_font(LOAD, 1, 8, font_8); + copy_font(LOAD, 2, 14, font_14); + copy_font(LOAD, 0, 16, font_16); +#else + font_8 = (char *)malloc(8*256, M_DEVBUF, M_NOWAIT); + font_14 = (char *)malloc(14*256, M_DEVBUF, M_NOWAIT); + font_16 = (char *)malloc(16*256, M_DEVBUF, M_NOWAIT); + copy_font(SAVE, 0, 16, font_16); + fonts_loaded = FONT_16_LOADED; +#endif save_palette(); - load_font(0, 16, font_8x16); - load_font(1, 8, font_8x8); - load_font(2, 14, font_8x14); } current_default = &user_default; for (i = 0; i < NCONS; i++) { scp = &console[i]; - scp->scr_buf = (u_short *)malloc(COL * ROW * 2, M_DEVBUF, M_NOWAIT); + scp->scr_buf = (u_short *)malloc(COL * ROW * 2, + M_DEVBUF, M_NOWAIT); scp->mode = TEXT80x25; scp->term.esc = 0; scp->term.std_attr = current_default->std_attr; @@ -379,13 +414,14 @@ int pcattach(struct isa_device *dev) scp->ysize = ROW; scp->bell_pitch = BELL_PITCH; scp->bell_duration = BELL_DURATION; - scp->status = 0; + scp->status = NLKED; scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; if (i > 0) { scp->crt_base = scp->crtat = scp->scr_buf; - fillw(scp->term.cur_attr|scr_map[0x20], scp->scr_buf, COL*ROW); + fillw(scp->term.cur_attr|scr_map[0x20], + scp->scr_buf, COL*ROW); } } /* get cursor going */ @@ -394,6 +430,7 @@ int pcattach(struct isa_device *dev) console[0].cursor_end); #endif cursor_pos(1); + update_leds(console[0].status); return 0; } @@ -430,6 +467,7 @@ static int get_scr_num() return i < NCONS ? i : 0; } + int pcopen(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = get_tty_ptr(dev); @@ -441,7 +479,6 @@ int pcopen(dev_t dev, int flag, int mode, struct proc *p) tp->t_param = pcparam; tp->t_dev = dev; if (!(tp->t_state & TS_ISOPEN)) { - tp->t_state |= TS_WOPEN; ttychars(tp); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; @@ -567,7 +604,7 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { int i, error; struct tty *tp; - frametype *fp; + struct trapframe *fp; scr_stat *scp; tp = get_tty_ptr(dev); @@ -619,25 +656,32 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) case CONS_80x50TEXT: /* set 80x50 text mode */ if (!crtc_vga) return ENXIO; - scp->mode = TEXT80x50; - scp->ysize = 50; - free(scp->scr_buf, M_DEVBUF); - scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*2, - M_DEVBUF, M_NOWAIT); - if (scp != cur_console) - scp->crt_base = scp->scr_buf; - set_mode(scp); - clear_screen(scp); - change_winsize(tp, scp->xsize, scp->ysize); - return 0; + /* is there a 8x8 font loaded ? */ + if (fonts_loaded & FONT_8_LOADED) { + scp->mode = TEXT80x50; + scp->ysize = 50; + free(scp->scr_buf, M_DEVBUF); + scp->scr_buf = + (u_short *)malloc(scp->xsize * scp->ysize * 2, + M_DEVBUF, M_NOWAIT); + if (scp != cur_console) + scp->crt_base = scp->scr_buf; + set_mode(scp); + clear_screen(scp); + change_winsize(tp, scp->xsize, scp->ysize); + return 0; + } + else + return EINVAL; case CONS_GETVERS: /* get version number */ *(int*)data = 0x103; /* version 1.3 */ return 0; case CONS_GETINFO: /* get current (virtual) console info */ - if (*data == sizeof(struct vid_info)) { + { vid_info_t *ptr = (vid_info_t*)data; + if (ptr->size == sizeof(struct vid_info)) { ptr->m_num = get_scr_num(); ptr->mv_col = scp->xpos; ptr->mv_row = scp->ypos; @@ -654,6 +698,7 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) return 0; } return EINVAL; + } case VT_SETMODE: /* set screen switcher mode */ bcopy(data, &scp->smode, sizeof(struct vt_mode)); @@ -740,13 +785,13 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) return 0; case KDENABIO: /* allow io operations */ - fp = (frametype *)p->p_regs; - fp->eflags |= PSL_IOPL; + fp = (struct trapframe *)p->p_regs; + fp->tf_eflags |= PSL_IOPL; return 0; case KDDISABIO: /* disallow io operations (default) */ - fp = (frametype *)p->p_regs; - fp->eflags &= ~PSL_IOPL; + fp = (struct trapframe *)p->p_regs; + fp->tf_eflags &= ~PSL_IOPL; return 0; case KDSETMODE: /* set current mode of this (virtual) console */ @@ -754,9 +799,9 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) case KD_TEXT: /* switch to TEXT (known) mode */ /* restore fonts & palette ! */ if (crtc_vga) { - load_font(0, 16, font_8x16); - load_font(1, 8, font_8x8); - load_font(2, 14, font_8x14); + copy_font(LOAD, 0, 16, font_16); + copy_font(LOAD, 1, 8, font_8); + copy_font(LOAD, 2, 14, font_14); load_palette(); } /* FALL THROUGH */ @@ -793,7 +838,7 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) scp->status &= ~LOCK_KEY_MASK; scp->status |= *data; if (scp == cur_console) - update_leds(scp->status & LED_MASK); + update_leds(scp->status); return 0; } return EINVAL; @@ -805,7 +850,10 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) case KDSETRAD: /* set keyboard repeat & delay rates */ if (*data & 0x80) return EINVAL; - kbd_cmd2(KB_SETRAD, *data); + i = spltty(); + kbd_cmd(KB_SETRAD); + kbd_cmd(*data); + splx(i); return 0; case KDSKBMODE: /* set keyboard mode */ @@ -837,18 +885,20 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) if (scp == cur_console) { if (*(int*)data) { int pitch = TIMER_FREQ/(*(int*)data); - /* enable counter 2 */ - outb(0x61, inb(0x61) | 3); /* set command for counter 2, 2 byte write */ - outb(TIMER_MODE, - TIMER_SEL2|TIMER_16BIT|TIMER_SQWAVE); + if (acquire_timer2(TIMER_16BIT|TIMER_SQWAVE)) { + return EBUSY; + } /* set pitch */ outb(TIMER_CNTR2, pitch); outb(TIMER_CNTR2, (pitch>>8)); + /* enable counter 2 output to speaker */ + outb(IO_PPI, inb(IO_PPI) | 3); } else { - /* disable counter 2 */ - outb(0x61, inb(0x61) & 0xFC); + /* disable counter 2 output to speaker */ + outb(IO_PPI, inb(IO_PPI) & 0xFC); + release_timer2(); } } return 0; @@ -862,7 +912,7 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) scp->status &= ~LED_MASK; scp->status |= *data; if (scp == cur_console) - update_leds(scp->status & LED_MASK); + update_leds(scp->status); return 0; } return EINVAL; @@ -914,48 +964,63 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) case PIO_FONT8x8: /* set 8x8 dot font */ if (!crtc_vga) return ENXIO; - bcopy(data, &font_8x8, sizeof(font_8x8)); - load_font(1, 8, font_8x8); + bcopy(data, font_8, 8*256); + fonts_loaded |= FONT_8_LOADED; + copy_font(LOAD, 1, 8, font_8); return 0; case GIO_FONT8x8: /* get 8x8 dot font */ if (!crtc_vga) return ENXIO; - bcopy(&font_8x8, data, sizeof(font_8x8)); - return 0; + if (fonts_loaded & FONT_8_LOADED) { + bcopy(font_8, data, 8*256); + return 0; + } + else + return ENXIO; case PIO_FONT8x14: /* set 8x14 dot font */ if (!crtc_vga) return ENXIO; - bcopy(data, &font_8x14, sizeof(font_8x14)); - load_font(2, 14, font_8x14); + bcopy(data, font_14, 14*256); + fonts_loaded |= FONT_14_LOADED; + copy_font(LOAD, 2, 14, font_14); return 0; case GIO_FONT8x14: /* get 8x14 dot font */ if (!crtc_vga) return ENXIO; - bcopy(&font_8x14, data, sizeof(font_8x14)); - return 0; + if (fonts_loaded & FONT_14_LOADED) { + bcopy(font_14, data, 14*256); + return 0; + } + else + return ENXIO; case PIO_FONT8x16: /* set 8x16 dot font */ if (!crtc_vga) return ENXIO; - bcopy(data, &font_8x16, sizeof(font_8x16)); - load_font(0, 16, font_8x16); + bcopy(data, font_16, 16*256); + fonts_loaded |= FONT_16_LOADED; + copy_font(LOAD, 0, 16, font_16); return 0; case GIO_FONT8x16: /* get 8x16 dot font */ if (!crtc_vga) return ENXIO; - bcopy(&font_8x16, data, sizeof(font_8x16)); - return 0; + if (fonts_loaded & FONT_16_LOADED) { + bcopy(font_16, data, 16*256); + return 0; + } + else + return ENXIO; case CONSOLE_X_MODE_ON: /* just to be compatible */ if (saved_console < 0) { saved_console = get_scr_num(); switch_scr(minor(dev)); - fp = (frametype *)p->p_regs; - fp->eflags |= PSL_IOPL; + fp = (struct trapframe *)p->p_regs; + fp->tf_eflags |= PSL_IOPL; scp->status |= UNKNOWN_MODE; scp->status |= KBD_RAW_MODE; return 0; @@ -963,12 +1028,12 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) return EAGAIN; case CONSOLE_X_MODE_OFF:/* just to be compatible */ - fp = (frametype *)p->p_regs; - fp->eflags &= ~PSL_IOPL; + fp = (struct trapframe *)p->p_regs; + fp->tf_eflags &= ~PSL_IOPL; if (crtc_vga) { - load_font(0, 16, font_8x16); - load_font(1, 8, font_8x8); - load_font(2, 14, font_8x14); + copy_font(LOAD, 0, 16, font_16); + copy_font(LOAD, 1, 8, font_8); + copy_font(LOAD, 2, 14, font_14); load_palette(); } scp->status &= ~UNKNOWN_MODE; @@ -987,7 +1052,7 @@ int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) */ if (data) sysbeep(TIMER_FREQ/((int*)data)[0], - ((int*)data)[1]*hz/3000); + ((int*)data)[1]*hz/1000); else sysbeep(scp->bell_pitch, scp->bell_duration); return 0; @@ -1054,7 +1119,7 @@ void pcstart(struct tty *tp) } splx(s); -#else /* __FreeBSD__ & __386BSD__ */ +#else /* __FreeBSD__ */ int c, s, len, i; scr_stat *scp = get_scr_stat(tp->t_dev); @@ -1065,28 +1130,18 @@ void pcstart(struct tty *tp) s = spltty(); if (!(tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) { for (;;) { - if (RB_LEN(&tp->t_out) <= tp->t_lowat) { - if (tp->t_state & TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_out); - } - if (tp->t_wsel) { - selwakeup(tp->t_wsel, - tp->t_state & TS_WCOLL); - tp->t_wsel = 0; - tp->t_state &= ~TS_WCOLL; - } - } - if (RB_LEN(&tp->t_out) == 0) + if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) + || tp->t_wsel) + ttwwakeup(tp); + if (RB_LEN(tp->t_out) == 0) break; if (scp->status & SLKED) break; len = 0; while( len < PCBURST) { - c = getc(&tp->t_out); - if (c == -1) + buf[len++] = getc(tp->t_out); + if( RB_LEN(tp->t_out) == 0) break; - buf[len++] = c; } tp->t_state |= TS_BUSY; splx(s); @@ -1122,9 +1177,6 @@ void pccnprobe(struct consdev *cp) /* initialize required fields */ cp->cn_dev = makedev(maj, NCONS); cp->cn_pri = CN_INTERNAL; -#if defined(__FreeBSD__) || defined(__386BSD__) - cp->cn_tp = CONSOLE_TTY; -#endif } @@ -1161,10 +1213,12 @@ int pccngetc(dev_t dev) return(c); } + static void none_saver(int test) { } + static void fade_saver(int test) { static int count = 0; @@ -1195,6 +1249,7 @@ static void fade_saver(int test) } } + static void blank_saver(int test) { u_char val; @@ -1212,18 +1267,18 @@ static void blank_saver(int test) static u_long rand_next = 1; + static int rand() { return ((rand_next = rand_next * 1103515245 + 12345) & 0x7FFFFFFF); } +#define NUM_STARS 50 + /* * Alternate saver that got its inspiration from a well known utility * package for an unfamous OS. */ - -#define NUM_STARS 50 - static void star_saver(int test) { scr_stat *scp = cur_console; @@ -1331,6 +1386,7 @@ static void snake_saver(int test) } } + static void cursor_shape(int start, int end) { outb(crtc_addr, 10); @@ -1381,10 +1437,6 @@ static void clear_screen(scr_stat *scp) static int switch_scr(u_int next_scr) { - if (in_putc) { /* delay switch if in putc */ - delayed_next_scr = next_scr+1; - return 0; - } if (switch_in_progress && (cur_console->proc != pfind(cur_console->pid))) switch_in_progress = 0; @@ -1402,7 +1454,10 @@ static int switch_scr(u_int next_scr) return EINVAL; } } - + if (in_putc) { /* delay switch if in putc */ + delayed_next_scr = next_scr+1; + return 0; + } switch_in_progress = 1; old_scp = cur_console; new_scp = &console[next_scr]; @@ -1448,7 +1503,13 @@ static void exchange_scr(void) new_scp->crt_base = Crtat; move_crsr(new_scp, new_scp->xpos, new_scp->ypos); bcopy(new_scp->scr_buf, Crtat, new_scp->xsize * new_scp->ysize * 2); - update_leds(new_scp->status & LED_MASK); + update_leds(new_scp->status); + if ((old_scp->status & UNKNOWN_MODE) && crtc_vga) { + copy_font(LOAD, 0, 16, font_16); + copy_font(LOAD, 1, 8, font_8); + copy_font(LOAD, 2, 14, font_14); + load_palette(); + } if (old_scp->status & KBD_RAW_MODE || new_scp->status & KBD_RAW_MODE) shfts = ctls = alts = agrs = metas = 0; delayed_next_scr = 0; @@ -1464,6 +1525,7 @@ static void move_crsr(scr_stat *scp, int x, int y) scp->crtat = scp->crt_base + scp->ypos * scp->xsize + scp->xpos; } + static void move_up(u_short *s, u_short *d, u_int len) { s += len; @@ -1472,12 +1534,14 @@ static void move_up(u_short *s, u_short *d, u_int len) *--d = *--s; } + static void move_down(u_short *s, u_short *d, u_int len) { while (len-- > 0) *d++ = *s++; } + static void scan_esc(scr_stat *scp, u_char c) { static u_char ansi_col[16] = @@ -1746,39 +1810,45 @@ static void scan_esc(scr_stat *scp, u_char c) break; case 'm': /* change attribute */ - if (scp->term.num_param == 0) - n = 0; - else - n = scp->term.param[0]; - switch (n) { - case 0: /* back to normal */ + if (scp->term.num_param == 0) { scp->term.cur_attr = scp->term.std_attr; break; - case 1: /* highlight (bold) */ - scp->term.cur_attr &= 0xFF00; - scp->term.cur_attr |= 0x0800; - break; - case 4: /* highlight (underline) */ - scp->term.cur_attr &= 0x0F00; - scp->term.cur_attr |= 0x0800; - break; - case 5: /* blink */ - scp->term.cur_attr &= 0xFF00; - scp->term.cur_attr |= 0x8000; - break; - case 7: /* reverse video */ - scp->term.cur_attr = scp->term.rev_attr; - break; - case 30: case 31: case 32: case 33: /* set fg color */ - case 34: case 35: case 36: case 37: - scp->term.cur_attr = (scp->term.cur_attr & 0xF0FF) - | (ansi_col[(n - 30) & 7] << 8); - break; - case 40: case 41: case 42: case 43: /* set bg color */ - case 44: case 45: case 46: case 47: - scp->term.cur_attr = (scp->term.cur_attr & 0x0FFF) - | (ansi_col[(n - 40) & 7] << 12); - break; + } + for (i = 0; i < scp->term.num_param; i++) { + switch (n = scp->term.param[i]) { + case 0: /* back to normal */ + scp->term.cur_attr = scp->term.std_attr; + break; + case 1: /* highlight (bold) */ + scp->term.cur_attr &= 0xFF00; + scp->term.cur_attr |= 0x0800; + break; + case 4: /* highlight (underline) */ + scp->term.cur_attr &= 0xFF00; + scp->term.cur_attr |= 0x0800; + break; + case 5: /* blink */ + scp->term.cur_attr &= 0xFF00; + scp->term.cur_attr |= 0x8000; + break; + case 7: /* reverse video */ + scp->term.cur_attr = scp->term.rev_attr; + break; + case 30: case 31: /* set fg color */ + case 32: case 33: case 34: + case 35: case 36: case 37: + scp->term.cur_attr = + (scp->term.cur_attr & 0xF8FF) + | (ansi_col[(n-30) & 7] << 8); + break; + case 40: case 41: /* set bg color */ + case 42: case 43: case 44: + case 45: case 46: case 47: + scp->term.cur_attr = + (scp->term.cur_attr & 0x8FFF) + | (ansi_col[(n-40) & 7] << 12); + break; + } } break; @@ -1979,6 +2049,7 @@ static void ansi_put(scr_stat *scp, u_char c) switch_scr(delayed_next_scr - 1); } + static void scinit(void) { u_short volatile *cp = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short), was; @@ -2030,7 +2101,7 @@ static void scinit(void) console[0].border = BG_BLACK;; console[0].xsize = COL; console[0].ysize = ROW; - console[0].status = 0; + console[0].status = NLKED; console[0].pid = 0; console[0].proc = NULL; console[0].smode.mode = VT_AUTO; @@ -2065,7 +2136,7 @@ static void scput(u_char c) scp->term = save; --in_putc; } else { - if( console_buffer_count < CONSOLE_BUFFER_SIZE) + if( console_buffer_count < CONSOLE_BUFSIZE) console_buffer[console_buffer_count++] = c; } } @@ -2087,9 +2158,20 @@ static u_char *get_fstr(u_int c, u_int *len) static void update_leds(int which) { + int s; static u_char xlate_leds[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; - kbd_cmd2(KB_SETLEDS, xlate_leds[which & LED_MASK]); + /* replace CAPS led with ALTGR led for ALTGR keyboards */ + if (key_map.n_keys > ALTGR_OFFSET) { + if (which & ALKED) + which |= CLKED; + else + which &= ~CLKED; + } + s = spltty(); + kbd_cmd(KB_SETLEDS); + kbd_cmd(xlate_leds[which & LED_MASK]); + splx(s); } @@ -2099,7 +2181,7 @@ static void update_leds(int which) */ u_int scgetc(int noblock) { - u_char val, code, release; + u_char scancode, keycode; u_int state, action; struct key_t *key; static u_char esc_flag = 0, compose = 0; @@ -2109,132 +2191,136 @@ next_code: kbd_wait(); /* First see if there is something in the keyboard port */ if (inb(KB_STAT) & KB_BUF_FULL) - val = inb(KB_DATA); + scancode = inb(KB_DATA); else if (noblock) return(NOKEY); else goto next_code; if (cur_console->status & KBD_RAW_MODE) - return val; - - code = val & 0x7F; - release = val & 0x80; - + return scancode; +#if ASYNCH + if (scancode == KB_ACK || scancode == KB_RESEND) { + kbd_reply = scancode; + if (noblock) + return(NOKEY); + goto next_code; + } +#endif + keycode = scancode & 0x7F; switch (esc_flag) { case 0x00: /* normal scancode */ - switch(code) { - case 0x38: /* left alt (compose key) */ - if (release && compose) { + switch(scancode) { + case 0xB8: /* left alt (compose key) */ + if (compose) { compose = 0; if (chr > 255) { sysbeep(BELL_PITCH, BELL_DURATION); chr = 0; } } - else { - if (!compose) { - compose = 1; - chr = 0; - } + break; + case 0x38: + if (!compose) { + compose = 1; + chr = 0; } break; - case 0x60: - case 0x61: - esc_flag = code; + case 0xE0: + case 0xE1: + esc_flag = scancode; goto next_code; } break; - case 0x60: /* 0xE0 prefix */ + case 0xE0: /* 0xE0 prefix */ esc_flag = 0; - switch (code) { - case 0x1c: /* right enter key */ - code = 0x59; + switch (keycode) { + case 0x1C: /* right enter key */ + keycode = 0x59; break; - case 0x1d: /* right ctrl key */ - code = 0x5a; + case 0x1D: /* right ctrl key */ + keycode = 0x5A; break; case 0x35: /* keypad divide key */ - code = 0x5b; + keycode = 0x5B; break; case 0x37: /* print scrn key */ - code = 0x5c; + keycode = 0x5C; break; case 0x38: /* right alt key (alt gr) */ - code = 0x5d; + keycode = 0x5D; break; case 0x47: /* grey home key */ - code = 0x5e; + keycode = 0x5E; break; case 0x48: /* grey up arrow key */ - code = 0x5f; + keycode = 0x5F; break; case 0x49: /* grey page up key */ - code = 0x60; + keycode = 0x60; break; - case 0x4b: /* grey left arrow key */ - code = 0x61; + case 0x4B: /* grey left arrow key */ + keycode = 0x61; break; - case 0x4d: /* grey right arrow key */ - code = 0x62; + case 0x4D: /* grey right arrow key */ + keycode = 0x62; break; - case 0x4f: /* grey end key */ - code = 0x63; + case 0x4F: /* grey end key */ + keycode = 0x63; break; case 0x50: /* grey down arrow key */ - code = 0x64; + keycode = 0x64; break; case 0x51: /* grey page down key */ - code = 0x65; + keycode = 0x65; break; case 0x52: /* grey insert key */ - code = 0x66; + keycode = 0x66; break; case 0x53: /* grey delete key */ - code = 0x67; + keycode = 0x67; break; default: /* ignore everything else */ goto next_code; } break; - case 0x61: /* 0xE1 prefix */ + case 0xE1: /* 0xE1 prefix */ esc_flag = 0; - if (code == 0x1D) + if (keycode == 0x1D) esc_flag = 0x1D; goto next_code; /* NOT REACHED */ case 0x1D: /* pause / break */ esc_flag = 0; - if (code != 0x45) + if (keycode != 0x45) goto next_code; - code = 0x68; + keycode = 0x68; break; } if (compose) { - switch (code) { - case 0x47: - case 0x48: /* keypad 7,8,9 */ - case 0x49: - if (!release) - chr = (code - 0x40) + chr*10; + switch (scancode) { + /* key pressed process it */ + case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ + chr = (scancode - 0x40) + chr*10; goto next_code; - case 0x4b: - case 0x4c: /* keypad 4,5,6 */ - case 0x4d: - if (!release) - chr = (code - 0x47) + chr*10; + case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ + chr = (scancode - 0x47) + chr*10; goto next_code; - case 0x4f: - case 0x50: /* keypad 1,2,3 */ - case 0x51: - if (!release) - chr = (code - 0x4e) + chr*10; + case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ + chr = (scancode - 0x4E) + chr*10; goto next_code; case 0x52: /* keypad 0 */ - if (!release) - chr *= 10; + chr *= 10; + goto next_code; + + /* key release, no interest here */ + case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */ + case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */ + case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */ + case 0xD2: /* keypad 0 */ goto next_code; + case 0x38: /* left alt key */ break; default: @@ -2250,15 +2336,15 @@ next_code: state = (shfts ? 1 : 0 ) | (2 * (ctls ? 1 : 0)) | (4 * (alts ? 1 : 0)); if ((!agrs && (cur_console->status & ALKED)) || (agrs && !(cur_console->status & ALKED))) - code += ALTGR_OFFSET; - key = &key_map.key[code]; + keycode += ALTGR_OFFSET; + key = &key_map.key[keycode]; if ( ((key->flgs & FLAG_LOCK_C) && (cur_console->status & CLKED)) || ((key->flgs & FLAG_LOCK_N) && (cur_console->status & NLKED)) ) state ^= 1; /* Check for make/break */ action = key->map[state]; - if (release) { /* key released */ + if (scancode & 0x80) { /* key released */ if (key->spcl & 0x80) { switch (action) { case LSH: @@ -2316,7 +2402,7 @@ next_code: cur_console->status &= ~NLKED; else cur_console->status |= NLKED; - update_leds(cur_console->status & LED_MASK); + update_leds(cur_console->status); } break; case CLK: @@ -2326,7 +2412,7 @@ next_code: cur_console->status &= ~CLKED; else cur_console->status |= CLKED; - update_leds(cur_console->status & LED_MASK); + update_leds(cur_console->status); } break; case SLK: @@ -2338,7 +2424,7 @@ next_code: } else cur_console->status |= SLKED; - update_leds(cur_console->status & LED_MASK); + update_leds(cur_console->status); } break; case ALK: @@ -2348,6 +2434,7 @@ next_code: cur_console->status &= ~ALKED; else cur_console->status |= ALKED; + update_leds(cur_console->status); } break; @@ -2455,6 +2542,7 @@ u_int sgetc(int noblock) return (scgetc(noblock) & 0xff); } + int pcmmap(dev_t dev, int offset, int nprot) { if (offset > 0x20000) @@ -2465,9 +2553,9 @@ int pcmmap(dev_t dev, int offset, int nprot) static void kbd_wait(void) { - int i; + int i = 1000; - for (i=0; i<1000; i++) { /* up to 10 msec */ + while (i--) { if ((inb(KB_STAT) & KB_READY) == 0) break; DELAY (10); @@ -2477,37 +2565,35 @@ static void kbd_wait(void) static void kbd_cmd(u_char command) { - kbd_wait(); - outb(KB_DATA, command); -} - - -static void kbd_cmd2(u_char command, u_char arg) -{ - int r, s = spltty(); + int retry = 5; do { - kbd_cmd(command); - r = kbd_reply(); - if (r == KB_ACK) { - kbd_cmd(arg & 0x7f); - r = kbd_reply(); + int i = 100000; + + kbd_wait(); +#if ASYNCH + kbd_reply = 0; + outb(KB_DATA, command); + while (i--) { + if (kbd_reply == KB_ACK) + return; + if (kbd_reply == KB_RESEND) + break; } - } while (r != KB_ACK); - splx(s); -} - - -static int kbd_reply() -{ - int i; - - kbd_wait(); - for (i=0; i<60000; i++) { /* at least 300 msec, 600 msec enough */ - if (inb(KB_STAT) & KB_BUF_FULL) - return ((u_char) inb(KB_DATA)); - DELAY (10); - } - return(-1); +#else + outb(KB_DATA, command); + while (i--) { + if (inb(KB_STAT) & KB_BUF_FULL) { + int val; + DELAY(10); + val = inb(KB_DATA); + if (val == KB_ACK) + return; + if (val == KB_RESEND) + break; + } + } +#endif + } while (retry--); } @@ -2560,10 +2646,11 @@ static void set_border(int color) inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x11); outb(ATC, color); inb(crtc_addr+6); /* reset flip-flop */ - outb(ATC, 0x20); /* enable Palette */ + outb(ATC, 0x20); /* enable Palette */ } -static void load_font(int segment, int size, char* font) + +static void copy_font(int direction, int segment, int size, char* font) { int ch, line, s; u_char val; @@ -2582,9 +2669,14 @@ static void load_font(int segment, int size, char* font) outb(GDCIDX, 0x06); outb(GDCREG, 0x05); /* addr = a0000, 64kb */ splx(s); for (ch=0; ch < 256; ch++) - for (line=0; line < size; line++) - *((char *)atdevbase+(segment*0x4000)+(ch*32)+line) = - font[(ch*size)+line]; + for (line=0; line < size; line++) + if (direction) + *((char *)atdevbase+(segment*0x4000)+(ch*32)+line) = + font[(ch*size)+line]; + else + font[(ch*size)+line] = + *((char *)atdevbase+(segment*0x4000)+(ch*32)+line); + /* setup vga for text mode again */ s = splhigh(); inb(crtc_addr+6); /* reset flip/flop */ @@ -2617,6 +2709,7 @@ static void load_palette(void) outb(ATC, 0x20); /* enable palette */ } + static void save_palette(void) { int i; diff --git a/sys/i386/isa/tw.c b/sys/i386/isa/tw.c new file mode 100644 index 000000000000..0a90aa3ace1c --- /dev/null +++ b/sys/i386/isa/tw.c @@ -0,0 +1,997 @@ +/*- + * Copyright (c) 1992, 1993 Eugene W. Stark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Eugene W. Stark. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "tw.h" +#if NTW > 0 + +/* + * Driver configuration parameters + */ + +/* + * Time for 1/2 of a power line cycle, in microseconds. + * Change this to 10000 for 50Hz power. Phil Sampson + * (vk2jnt@gw.vk2jnt.ampr.org OR sampson@gidday.enet.dec.com) + * reports that this works (at least in Australia) using a + * TW7223 module (a local version of the TW523). + */ +#define HALFCYCLE 8333 /* 1/2 cycle = 8333us at 60Hz */ + +/* + * Undefine the following if you don't have the high-resolution "microtime" + * routines (leave defined for FreeBSD, which has them). + */ +#define HIRESTIME + +/* + * End of driver configuration parameters + */ + +/* + * 386BSD Device Driver for X-10 POWERHOUSE (tm) + * Two-Way Power Line Interface, Model #TW523 + * + * written by Eugene W. Stark (stark@cs.sunysb.edu) + * December 2, 1992 + * + * REVISION HISTORY + * + * Date Who What + * 12/2/92 stark Initial Release + * 2/7/93 stark Minor upgrades to timing code + * 3/4/93 stark Updated to use high-res. "microtime" routines + * 5/9/93 stark Minor changes to console error reporting + * 10/30/93 stark Clean-up for FreeBSD release + * 3/14/93 stark Clean-up for FreeBSD-1.1 release + * + * NOTES: + * + * The TW523 is a carrier-current modem for home control/automation purposes. + * It is made by: + * + * X-10 Inc. + * 185A LeGrand Ave. + * Northvale, NJ 07647 + * USA + * (201) 784-9700 or 1-800-526-0027 + * + * X-10 Home Controls Inc. + * 1200 Aerowood Drive, Unit 20 + * Mississauga, Ontario + * (416) 624-4446 or 1-800-387-3346 + * + * The TW523 is designed for communications using the X-10 protocol, + * which is compatible with a number of home control systems, including + * Radio Shack "Plug 'n Power(tm)" and Stanley "Lightmaker(tm)." + * I bought my TW523 from: + * + * Home Control Concepts + * 9353-C Activity Road + * San Diego, CA 92126 + * (619) 693-8887 + * + * They supplied me with the TW523 (which has an RJ-11 four-wire modular + * telephone connector), a modular cable, an RJ-11 to DB-25 connector with + * internal wiring, documentation from X-10 on the TW523 (very good), + * an instruction manual by Home Control Concepts (not very informative), + * and a floppy disk containing binary object code of some demonstration/test + * programs and of a C function library suitable for controlling the TW523 + * by an IBM PC under MS-DOS (not useful to me other than to verify that + * the unit worked). I suggest saving money and buying the bare TW523 + * rather than the TW523 development kit (what I bought), because if you + * are running 386BSD you don't really care about the DOS binaries. + * + * The interface to the TW-523 consists of four wires on the RJ-11 connector, + * which are jumpered to somewhat more wires on the DB-25 connector, which + * in turn is intended to plug into the PC parallel printer port. I dismantled + * the DB-25 connector to find out what they had done: + * + * Signal RJ-11 pin DB-25 pin(s) Parallel Port + * Transmit TX 4 (Y) 2, 4, 6, 8 Data out + * Receive RX 3 (G) 10, 14 -ACK, -AutoFeed + * Common 2 (R) 25 Common + * Zero crossing 1 (B) 17 -Select Input + * + * The zero crossing signal is used to synchronize transmission to the + * zero crossings of the AC line, as detailed in the X-10 documentation. + * It would be nice if one could generate interrupts with this signal, + * however one needs interrupts on both the rising and falling edges, + * and the -ACK signal to the parallel port interrupts only on the falling + * edge, so it can't be done without additional hardware. + * + * In this driver, the transmit function is performed in a non-interrupt-driven + * fashion, by polling the zero crossing signal to determine when a transition + * has occurred. This wastes CPU time during transmission, but it seems like + * the best that can be done without additional hardware. One problem with + * the scheme is that preemption of the CPU during transmission can cause loss + * of sync. The driver tries to catch this, by noticing that a long delay + * loop has somehow become foreshortened, and the transmission is aborted with + * an error return. It is up to the user level software to handle this + * situation (most likely by retrying the transmission). + */ + +#include "param.h" +#include "systm.h" +#include "proc.h" +#include "user.h" +#include "buf.h" +#include "kernel.h" +#include "ioctl.h" +#include "tty.h" +#include "uio.h" +#include "syslog.h" + +#ifdef HIRESTIME +#include "time.h" +#endif /* HIRESTIME */ + +#include "i386/isa/isa_device.h" + +void twdelay25(); +void twdelayn(int n); +void twsetuptimes(int *a); +int twprobe(); +int twattach(); +void twintr(int unit); + +/* + * Transmission is done by calling write() to send three byte packets of data. + * The first byte contains a four bit house code (0=A to 15=P). + * The second byte contains five bit unit/key code (0=unit 1 to 15=unit 16, + * 16=All Units Off to 31 = Status Request). The third byte specifies + * the number of times the packet is to be transmitted without any + * gaps between successive transmissions. Normally this is 2, as per + * the X-10 documentation, but sometimes (e.g. for bright and dim codes) + * it can be another value. Each call to write can specify an arbitrary + * number of data bytes. An incomplete packet is buffered until a subsequent + * call to write() provides data to complete it. At most one packet will + * actually be processed in any call to write(). Successive calls to write() + * leave a three-cycle gap between transmissions, per the X-10 documentation. + * + * Reception is done using read(). + * The driver produces a series of three-character packets. + * In each packet, the first character consists of flags, + * the second character is a four bit house code (0-15), + * and the third character is a five bit key/function code (0-31). + * The flags are the following: + */ + +#define TW_RCV_LOCAL 1 /* The packet arrived during a local transmission */ +#define TW_RCV_ERROR 2 /* An invalid/corrupted packet was received */ + +/* + * IBM PC parallel port definitions relevant to TW523 + */ + +#define tw_data 0 /* Data to tw523 (R/W) */ + +#define tw_status 1 /* Status of tw523 (R) */ +#define TWS_RDATA 0x40 /* tw523 receive data */ + +#define tw_control 2 /* Control tw523 (R/W) */ +#define TWC_SYNC 0x08 /* tw523 sync (pin 17) */ +#define TWC_ENA 0x10 /* tw523 interrupt enable */ + +/* + * Miscellaneous defines + */ + +#define TWUNIT(dev) (minor(dev)) /* Extract unit number from device */ +#define TWPRI (PZERO+8) /* I don't know any better, so let's */ + /* use the same as the line printer */ + +struct isa_driver twdriver = { + twprobe, twattach, "tw" +}; + +/* + * Software control structure for TW523 + */ + +#define TWS_XMITTING 1 /* Transmission in progress */ +#define TWS_RCVING 2 /* Reception in progress */ +#define TWS_WANT 4 /* A process wants received data */ +#define TWS_OPEN 8 /* Is it currently open? */ +#define TWS_COLL 16 /* Collision in select */ + +#define TW_SIZE 3*60 /* Enough for about 10 sec. of input */ + +struct tw_sc { + u_int sc_port; /* I/O Port */ + u_int sc_state; /* Current software control state */ + pid_t sc_selp; /* Process sleeping on select */ + u_char sc_xphase; /* Current state of sync (for transmitter) */ + u_char sc_rphase; /* Current state of sync (for receiver) */ + u_char sc_flags; /* Flags for current reception */ + short sc_rcount; /* Number of bits received so far */ + int sc_bits; /* Bits received so far */ + u_char sc_pkt[3]; /* Packet not yet transmitted */ + short sc_pktsize; /* How many bytes in the packet? */ + u_char sc_buf[TW_SIZE]; /* We buffer our own input */ + int sc_nextin; /* Next free slot in circular buffer */ + int sc_nextout; /* First used slot in circular buffer */ +#ifdef HIRESTIME + int sc_xtimes[22]; /* Times for bits in current xmit packet */ + int sc_rtimes[22]; /* Times for bits in current rcv packet */ +#endif /* HIRESTIME */ +} tw_sc[NTW]; + +/* + * Counter value for delay loop. + * It is adjusted by twprobe so that the delay loop takes about 25us. + */ + +#define TWDELAYCOUNT 161 /* Works on my 486DX/33 */ +int twdelaycount; + +/* + * Twdelay25 is used for very short delays of about 25us. + * It is implemented with a calibrated delay loop, and should be + * fairly accurate ... unless we are preempted by an interrupt. + * + * We use this to wait for zero crossings because the X-10 specs say we + * are supposed to assert carrier within 25us when one happens. + * I don't really believe we can do this, but the X-10 devices seem to be + * fairly forgiving. + */ + +void twdelay25() +{ + int cnt; + for(cnt = twdelaycount; cnt; cnt--); /* Should take about 25us */ +} + +/* + * Twdelayn is used to time the length of the 1ms carrier pulse. + * This is not very critical, but if we have high-resolution time-of-day + * we check it every apparent 200us to make sure we don't get too far off + * if we happen to be interrupted during the delay. + */ + +void twdelayn(int n) +{ +#ifdef HIRESTIME + int t, d; + struct timeval tv; + microtime(&tv); + t = tv.tv_usec; + t += n; +#endif /* HIRESTIME */ + while(n > 0) { + twdelay25(); + n -= 25; +#ifdef HIRESTIME + if((n & 0x7) == 0) { + microtime(&tv); + d = tv.tv_usec - t; + if(d >= 0 && d < 1000000) return; + } +#endif /* HIRESTIME */ + } +} + +int twprobe(idp) + struct isa_device *idp; +{ + struct tw_sc sc; + int d; + int tries; + + sc.sc_port = idp->id_iobase; + /* + * Iteratively check the timing of a few sync transitions, and adjust + * the loop delay counter, if necessary, to bring the timing reported + * by wait_for_zero() close to HALFCYCLE. Give up if anything + * ridiculous happens. + */ + if(twdelaycount == 0) { /* Only adjust timing for first unit */ + twdelaycount = TWDELAYCOUNT; + for(tries = 0; tries < 10; tries++) { + sc.sc_xphase = inb(idp->id_iobase + tw_control) & TWC_SYNC; + if(wait_for_zero(&sc) >= 0) { + d = wait_for_zero(&sc); + if(d <= HALFCYCLE/100 || d >= HALFCYCLE*100) { + twdelaycount = 0; + return(0); + } + twdelaycount = (twdelaycount * d)/HALFCYCLE; + } + } + } + /* + * Now do a final check, just to make sure + */ + sc.sc_xphase = inb(idp->id_iobase + tw_control) & TWC_SYNC; + if(wait_for_zero(&sc) >= 0) { + d = wait_for_zero(&sc); + if(d <= (HALFCYCLE * 110)/100 && d >= (HALFCYCLE * 90)/100) return(1); + } + return(0); +} + +int twattach(idp) + struct isa_device *idp; +{ + struct tw_sc *sc; + + sc = &tw_sc[idp->id_unit]; + sc->sc_port = idp->id_iobase; + sc->sc_state = 0; + return (1); +} + +int twopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct tw_sc *sc = &tw_sc[TWUNIT(dev)]; + int s; + int port; + + s = spltty(); + if(sc->sc_state == 0) { + sc->sc_state = TWS_OPEN; + sc->sc_nextin = sc->sc_nextout = 0; + sc->sc_pktsize = 0; + sc->sc_selp = 0; + outb(sc->sc_port+tw_control, TWC_ENA); + } + splx(s); + return(0); +} + +int twclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct tw_sc *sc = &tw_sc[TWUNIT(dev)]; + int s; + int port = sc->sc_port; + + s = spltty(); + sc->sc_state = 0; + outb(sc->sc_port+tw_control, 0); + splx(s); + return(0); +} + +int twread(dev, uio) + dev_t dev; + struct uio *uio; +{ + u_char buf[3]; + struct tw_sc *sc = &tw_sc[TWUNIT(dev)]; + int error, cnt, s; + + s = spltty(); + cnt = MIN(uio->uio_resid, 3); + if((error = twgetbytes(sc, buf, cnt)) == 0) { + error = uiomove(buf, cnt, uio); + } + splx(s); + return(error); +} + +int twwrite(dev, uio) + dev_t dev; + struct uio *uio; +{ + struct tw_sc *sc; + int house, key, reps; + int s, error; + int cnt; + + sc = &tw_sc[TWUNIT(dev)]; + /* + * Note: Although I had intended to allow concurrent transmitters, + * there is a potential problem here if two processes both write + * into the sc_pkt buffer at the same time. The following code + * is an additional critical section that needs to be synchronized. + */ + s = spltty(); + cnt = MIN(3 - sc->sc_pktsize, uio->uio_resid); + if(error = uiomove(&(sc->sc_pkt[sc->sc_pktsize]), cnt, uio)) { + splx(s); + return(error); + } + sc->sc_pktsize += cnt; + if(sc->sc_pktsize < 3) { /* Only transmit 3-byte packets */ + splx(s); + return(0); + } + sc->sc_pktsize = 0; + /* + * Collect house code, key code, and rep count, and check for sanity. + */ + house = sc->sc_pkt[0]; + key = sc->sc_pkt[1]; + reps = sc->sc_pkt[2]; + if(house >= 16 || key >= 32) { + splx(s); + return(ENODEV); + } + /* + * Synchronize with the receiver operating in the bottom half, and + * also with concurrent transmitters. + * We don't want to interfere with a packet currently being received, + * and we would like the receiver to recognize when a packet has + * originated locally. + */ + while(sc->sc_state & (TWS_RCVING | TWS_XMITTING)) { + if(error = tsleep((caddr_t)sc, TWPRI|PCATCH, "twwrite", 0)) { + splx(s); + return(error); + } + } + sc->sc_state |= TWS_XMITTING; + /* + * Everything looks OK, let's do the transmission. + */ + splx(s); /* Enable interrupts because this takes a LONG time */ + error = twsend(sc, house, key, reps); + s = spltty(); + sc->sc_state &= ~TWS_XMITTING; + wakeup((caddr_t)sc); + splx(s); + if(error) return(EIO); + else return(0); +} + +/* + * Determine if there is data available for reading + */ + +int twselect(dev, rw, p) + dev_t dev; + int rw; + struct proc *p; +{ + struct tw_sc *sc; + struct proc *pp; + int s, i; + + sc = &tw_sc[TWUNIT(dev)]; + s = spltty(); + if(sc->sc_nextin != sc->sc_nextout) { + splx(s); + return(1); + } + if(sc->sc_selp && (pp = pfind(sc->sc_selp)) + && pp->p_wchan == (caddr_t)&selwait) { + sc->sc_state |= TWS_COLL; + } else { + sc->sc_selp = p->p_pid; + } + splx(s); + return(0); +} + +/* + * X-10 Protocol + */ + +#define X10_START_LENGTH 4 +char X10_START[] = { 1, 1, 1, 0 }; + +/* + * Each bit of the 4-bit house code and 5-bit key code + * is transmitted twice, once in true form, and then in + * complemented form. This is already taken into account + * in the following tables. + */ + +#define X10_HOUSE_LENGTH 8 +char X10_HOUSE[16][8] = { + 0, 1, 1, 0, 1, 0, 0, 1, /* A = 0110 */ + 1, 0, 1, 0, 1, 0, 0, 1, /* B = 1110 */ + 0, 1, 0, 1, 1, 0, 0, 1, /* C = 0010 */ + 1, 0, 0, 1, 1, 0, 0, 1, /* D = 1010 */ + 0, 1, 0, 1, 0, 1, 1, 0, /* E = 0001 */ + 1, 0, 0, 1, 0, 1, 1, 0, /* F = 1001 */ + 0, 1, 1, 0, 0, 1, 1, 0, /* G = 0101 */ + 1, 0, 1, 0, 0, 1, 1, 0, /* H = 1101 */ + 0, 1, 1, 0, 1, 0, 1, 0, /* I = 0111 */ + 1, 0, 1, 0, 1, 0, 1, 0, /* J = 1111 */ + 0, 1, 0, 1, 1, 0, 1, 0, /* K = 0011 */ + 1, 0, 0, 1, 1, 0, 1, 0, /* L = 1011 */ + 0, 1, 0, 1, 0, 1, 0, 1, /* M = 0000 */ + 1, 0, 0, 1, 0, 1, 0, 1, /* N = 1000 */ + 0, 1, 1, 0, 0, 1, 0, 1, /* O = 0100 */ + 1, 0, 1, 0, 0, 1, 0, 1 /* P = 1100 */ +}; + +#define X10_KEY_LENGTH 10 +char X10_KEY[32][10] = { + 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, /* 01100 => 1 */ + 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, /* 11100 => 2 */ + 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, /* 00100 => 3 */ + 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, /* 10100 => 4 */ + 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, /* 00010 => 5 */ + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, /* 10010 => 6 */ + 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, /* 01010 => 7 */ + 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, /* 11010 => 8 */ + 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, /* 01110 => 9 */ + 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, /* 11110 => 10 */ + 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, /* 00110 => 11 */ + 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 10110 => 12 */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, /* 00000 => 13 */ + 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 10000 => 14 */ + 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, /* 01000 => 15 */ + 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, /* 11000 => 16 */ + 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, /* 00001 => All Units Off */ + 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, /* 00011 => All Units On */ + 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, /* 00101 => On */ + 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, /* 00111 => Off */ + 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 01001 => Dim */ + 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, /* 01011 => Bright */ + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, /* 01101 => All LIGHTS Off */ + 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, /* 01111 => Extended Code */ + 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, /* 10001 => Hail Request */ + 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, /* 10011 => Hail Acknowledge */ + 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, /* 10101 => Preset Dim 0 */ + 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, /* 10111 => Preset Dim 1 */ + 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, /* 11000 => Extended Data (analog) */ + 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, /* 11011 => Status = on */ + 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, /* 11101 => Status = off */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 /* 11111 => Status request */ +}; + +/* + * Tables for mapping received X-10 code back to house/key number. + */ + +short X10_HOUSE_INV[16] = { 12, 4, 2, 10, 14, 6, 0, 8, + 13, 5, 3, 11, 15, 7, 1, 9 }; + +short X10_KEY_INV[32] = { 12, 16, 4, 17, 2, 18, 10, 19, + 14, 20, 6, 21, 0, 22, 8, 23, + 13, 24, 5, 25, 3, 26, 11, 27, + 15, 28, 7, 29, 1, 30, 9, 31 }; + +/* + * Transmit a packet containing house code h and key code k + */ + +#define TWRETRY 10 /* Try 10 times to sync with AC line */ + +int twsend(sc, h, k, cnt) +struct tw_sc *sc; +int h, k, cnt; +{ + int i; + int port = sc->sc_port; + + /* + * Make sure we get a reliable sync with a power line zero crossing + */ + for(i = 0; i < TWRETRY; i++) { + if(wait_for_zero(sc) > 100) goto insync; + } + log(LOG_ERR, "TWXMIT: failed to sync.\n"); + return(-1); + + insync: + /* + * Be sure to leave 3 cycles space between transmissions + */ + for(i = 6; i > 0; i--) + if(next_zero(sc) < 0) return(-1); + /* + * The packet is transmitted cnt times, with no gaps. + */ + while(cnt--) { + /* + * Transmit the start code + */ + for(i = 0; i < X10_START_LENGTH; i++) { + outb(port+tw_data, X10_START[i] ? 0xff : 0x00); /* Waste no time! */ +#ifdef HIRESTIME + if(i == 0) twsetuptimes(sc->sc_xtimes); + if(twchecktime(sc->sc_xtimes[i], HALFCYCLE/20) == 0) { + outb(port+tw_data, 0); + return(-1); + } +#endif /* HIRESTIME */ + twdelayn(1000); /* 1ms pulse width */ + outb(port+tw_data, 0); + if(next_zero(sc) < 0) return(-1); + } + /* + * Transmit the house code + */ + for(i = 0; i < X10_HOUSE_LENGTH; i++) { + outb(port+tw_data, X10_HOUSE[h][i] ? 0xff : 0x00); /* Waste no time! */ +#ifdef HIRESTIME + if(twchecktime(sc->sc_xtimes[i+X10_START_LENGTH], HALFCYCLE/20) == 0) { + outb(port+tw_data, 0); + return(-1); + } +#endif /* HIRESTIME */ + twdelayn(1000); /* 1ms pulse width */ + outb(port+tw_data, 0); + if(next_zero(sc) < 0) return(-1); + } + /* + * Transmit the unit/key code + */ + for(i = 0; i < X10_KEY_LENGTH; i++) { + outb(port+tw_data, X10_KEY[k][i] ? 0xff : 0x00); +#ifdef HIRESTIME + if(twchecktime(sc->sc_xtimes[i+X10_START_LENGTH+X10_HOUSE_LENGTH], + HALFCYCLE/20) == 0) { + outb(port+tw_data, 0); + return(-1); + } +#endif /* HIRESTIME */ + twdelayn(1000); /* 1ms pulse width */ + outb(port+tw_data, 0); + if(next_zero(sc) < 0) return(-1); + } + } + return(0); +} + +/* + * Waste CPU cycles to get in sync with a power line zero crossing. + * The value returned is roughly how many microseconds we wasted before + * seeing the transition. To avoid wasting time forever, we give up after + * waiting patiently for 1/4 sec (15 power line cycles at 60 Hz), + * which is more than the 11 cycles it takes to transmit a full + * X-10 packet. + */ + +int wait_for_zero(sc) +struct tw_sc *sc; +{ + int i, old, new, max, cnt; + int port = sc->sc_port + tw_control; + + old = sc->sc_xphase; + max = 10000; /* 10000 * 25us = 0.25 sec */ + i = 0; + while(max--) { + new = inb(port) & TWC_SYNC; + if(new != old) { + sc->sc_xphase = new; + return(i*25); + } + i++; + twdelay25(); + } + return(-1); +} + +/* + * Wait for the next zero crossing transition, and if we don't have + * high-resolution time-of-day, check to see that the zero crossing + * appears to be arriving on schedule. + * We expect to be waiting almost a full half-cycle (8.333ms-1ms = 7.333ms). + * If we don't seem to wait very long, something is wrong (like we got + * preempted!) and we should abort the transmission because + * there's no telling how long it's really been since the + * last bit was transmitted. + */ + +int next_zero(sc) +struct tw_sc *sc; +{ + int d; +#ifdef HIRESTIME + if((d = wait_for_zero(sc)) < 0) { +#else + if((d = wait_for_zero(sc)) < 6000 || d > 8500) { + /* No less than 6.0ms, no more than 8.5ms */ +#endif /* HIRESTIME */ + log(LOG_ERR, "TWXMIT framing error: %d\n", d); + return(-1); + } + return(0); +} + +/* + * Put a three-byte packet into the circular buffer + * Should be called at priority spltty() + */ + +int twputpkt(sc, p) +struct tw_sc *sc; +u_char *p; +{ + int i, next; + + for(i = 0; i < 3; i++) { + next = sc->sc_nextin+1; + if(next >= TW_SIZE) next = 0; + if(next == sc->sc_nextout) { /* Buffer full */ +/* + log(LOG_ERR, "TWRCV: Buffer overrun\n"); + */ + return(1); + } + sc->sc_buf[sc->sc_nextin] = *p++; + sc->sc_nextin = next; + } + if(sc->sc_state & TWS_WANT) { + sc->sc_state &= ~TWS_WANT; + wakeup((caddr_t)(&sc->sc_buf)); + } + if(sc->sc_selp) { + selwakeup(sc->sc_selp, sc->sc_state & TWS_COLL); + sc->sc_selp = 0; + sc->sc_state &= ~TWS_COLL; + } + return(0); +} + +/* + * Get bytes from the circular buffer + * Should be called at priority spltty() + */ + +int twgetbytes(sc, p, cnt) +struct tw_sc *sc; +u_char *p; +int cnt; +{ + int error; + + while(cnt--) { + while(sc->sc_nextin == sc->sc_nextout) { /* Buffer empty */ + sc->sc_state |= TWS_WANT; + if(error = tsleep((caddr_t)(&sc->sc_buf), TWPRI|PCATCH, "twread", 0)) { + return(error); + } + } + *p++ = sc->sc_buf[sc->sc_nextout++]; + if(sc->sc_nextout >= TW_SIZE) sc->sc_nextout = 0; + } + return(0); +} + +/* + * Abort reception that has failed to complete in the required time. + */ + +void twabortrcv(sc) +struct tw_sc *sc; +{ + int s; + u_char pkt[3]; + + s = spltty(); + sc->sc_state &= ~TWS_RCVING; + sc->sc_flags |= TW_RCV_ERROR; + pkt[0] = sc->sc_flags; + pkt[1] = pkt[2] = 0; + twputpkt(sc, pkt); + log(LOG_ERR, "TWRCV: aborting (%x, %d)\n", sc->sc_bits, sc->sc_rcount); + wakeup((caddr_t)sc); + splx(s); +} + +/* + * This routine handles interrupts that occur when there is a falling + * transition on the RX input. There isn't going to be a transition + * on every bit (some are zero), but if we are smart and keep track of + * how long it's been since the last interrupt (via the zero crossing + * detect line and/or high-resolution time-of-day routine), we can + * reconstruct the transmission without having to poll. + */ + +void twintr(unit) +int unit; +{ + struct tw_sc *sc = &tw_sc[unit]; + int port; + int newphase; + u_char pkt[3]; + + port = sc->sc_port; + /* + * Ignore any interrupts that occur if the device is not open. + */ + if(sc->sc_state == 0) return; + newphase = inb(port + tw_control) & TWC_SYNC; + /* + * NEW PACKET: + * If we aren't currently receiving a packet, set up a new packet + * and put in the first "1" bit that has just arrived. + * Arrange for the reception to be aborted if too much time goes by. + */ + if((sc->sc_state & TWS_RCVING) == 0) { +#ifdef HIRESTIME + twsetuptimes(sc->sc_rtimes); +#endif /* HIRESTIME */ + sc->sc_state |= TWS_RCVING; + sc->sc_rcount = 1; + if(sc->sc_state & TWS_XMITTING) sc->sc_flags = TW_RCV_LOCAL; + else sc->sc_flags = 0; + sc->sc_bits = 0; + sc->sc_rphase = newphase; + timeout((timeout_func_t)twabortrcv, (caddr_t)sc, hz/4); + return; + } + /* + * START CODE: + * The second and third bits are a special case. + */ + if(sc->sc_rcount < 3) { +#ifdef HIRESTIME + if(twchecktime(sc->sc_rtimes[sc->sc_rcount], HALFCYCLE/3) + && newphase != sc->sc_rphase) { +#else + if(newphase != sc->sc_rphase) { +#endif + sc->sc_rcount++; + } else { + /* + * Invalid start code -- abort reception. + */ + sc->sc_state &= ~TWS_RCVING; + sc->sc_flags |= TW_RCV_ERROR; +/* + pkt[0] = sc->sc_flags; + pkt[1] = pkt[2] = 0; + twputpkt(sc, pkt); + wakeup((caddr_t)sc); + */ + untimeout((timeout_func_t)twabortrcv, (caddr_t)sc); + log(LOG_ERR, "TWRCV: Invalid start code\n"); + return; + } + if(sc->sc_rcount == 3) { + /* + * We've gotten three "1" bits in a row. The start code + * is really 1110, but this might be followed by a zero + * bit from the house code, so if we wait any longer we + * might be confused about the first house code bit. + * So, we guess that the start code is correct and insert + * the trailing zero without actually having seen it. + * We don't change sc_rphase in this case, because two + * bit arrivals in a row preserve parity. + */ + sc->sc_rcount++; + return; + } + /* + * Update sc_rphase to the current phase before returning. + */ + sc->sc_rphase = newphase; + return; + } + /* + * GENERAL CASE: + * Now figure out what the current bit is that just arrived. + * The X-10 protocol transmits each data bit twice: once in + * true form and once in complemented form on the next half + * cycle. So, there will be at least one interrupt per bit. + * By comparing the phase we see at the time of the interrupt + * with the saved sc_rphase, we can tell on which half cycle + * the interrupt occrred. This assumes, of course, that the + * packet is well-formed. We do the best we can at trying to + * catch errors by aborting if too much time has gone by, and + * by tossing out a packet if too many bits arrive, but the + * whole scheme is probably not as robust as if we had a nice + * interrupt on every half cycle of the power line. + * If we have high-resolution time-of-day routines, then we + * can do a bit more sanity checking. + */ + + /* + * A complete packet is 22 half cycles. + */ + if(sc->sc_rcount <= 20) { +#ifdef HIRESTIME + if((newphase == sc->sc_rphase && + twchecktime(sc->sc_rtimes[sc->sc_rcount+1], HALFCYCLE/3) == 0) + || (newphase != sc->sc_rphase && + twchecktime(sc->sc_rtimes[sc->sc_rcount], HALFCYCLE/3) == 0)) { + sc->sc_flags |= TW_RCV_ERROR; + } else { +#endif /* HIRESTIME */ + sc->sc_bits = (sc->sc_bits << 1) + | ((newphase == sc->sc_rphase) ? 0x0 : 0x1); + sc->sc_rcount += 2; +#ifdef HIRESTIME + } +#endif /* HIRESTIME */ + } + if(sc->sc_rcount >= 22 || sc->sc_flags & TW_RCV_ERROR) { + if(sc->sc_rcount != 22) { + sc->sc_flags |= TW_RCV_ERROR; + pkt[0] = sc->sc_flags; + pkt[1] = pkt[2] = 0; + } else { + pkt[0] = sc->sc_flags; + pkt[1] = X10_HOUSE_INV[(sc->sc_bits & 0x1e0) >> 5]; + pkt[2] = X10_KEY_INV[sc->sc_bits & 0x1f]; + } + sc->sc_state &= ~TWS_RCVING; + twputpkt(sc, pkt); + untimeout((timeout_func_t)twabortrcv, (caddr_t)sc); + if(sc->sc_flags & TW_RCV_ERROR) + log(LOG_ERR, "TWRCV: invalid packet: (%d, %x)\n", + sc->sc_rcount, sc->sc_bits); + wakeup((caddr_t)sc); + } +} + +#ifdef HIRESTIME +/* + * Initialize an array of 22 times, starting from the current + * microtime and continuing for the next 21 half cycles. + * We use the times as a reference to make sure transmission + * or reception is on schedule. + */ + +void twsetuptimes(int *a) +{ + struct timeval tv; + int i, t; + + microtime(&tv); + t = tv.tv_usec; + for(i = 0; i < 22; i++) { + *a++ = t; + t += HALFCYCLE; + if(t >= 1000000) t -= 1000000; + } +} + +/* + * Check the current time against a slot in a previously set up + * timing array, and make sure that it looks like we are still + * on schedule. + */ + +int twchecktime(int target, int tol) +{ + struct timeval tv; + int t, d; + + microtime(&tv); + t = tv.tv_usec; + d = (target - t) >= 0 ? (target - t) : (t - target); + if(d > 500000) d = 1000000-d; + if(d <= tol && d >= -tol) { + return(1); + } else { + log(LOG_ERR, "TWCHK: timing off by %dus (>= %dus)\n", d, tol); + return(0); + } +} +#endif /* HIRESTIME */ + +#endif NTW diff --git a/sys/i386/isa/ultra14f.c b/sys/i386/isa/ultra14f.c index 230868b33746..d2bc4c39b171 100644 --- a/sys/i386/isa/ultra14f.c +++ b/sys/i386/isa/ultra14f.c @@ -19,7 +19,7 @@ * commenced: Sun Sep 27 18:14:01 PDT 1992 * slight mod to make work with 34F as well: Wed Jun 2 18:05:48 WST 1993 * - * $Id: ultra14f.c,v 1.15 1994/01/29 10:29:14 rgrimes Exp $ + * $Id: ultra14f.c,v 1.17 1994/03/24 02:23:00 davidg Exp $ */ #include <sys/types.h> @@ -465,6 +465,7 @@ uha_attach(dev) uha->sc_link.adapter_targ = uha->our_id; uha->sc_link.adapter = &uha_switch; uha->sc_link.device = &uha_dev; + uha->sc_link.flags = SDEV_BOUNCE; /* * ask the adapter what subunits are present @@ -675,7 +676,8 @@ uha_get_mscp(unit, flags) goto gottit; } else { if (!(flags & SCSI_NOSLEEP)) { - sleep(&uha->free_mscp, PRIBIO); + tsleep((caddr_t)&uha->free_mscp, PRIBIO, + "uhamscp", 0); } } } diff --git a/sys/i386/isa/vector.s b/sys/i386/isa/vector.s index f6bb78fce97a..835da9cdf320 100644 --- a/sys/i386/isa/vector.s +++ b/sys/i386/isa/vector.s @@ -1,6 +1,6 @@ /* * from: vector.s, 386BSD 0.1 unknown origin - * $Id: vector.s,v 1.6 1994/01/10 23:15:09 ache Exp $ + * $Id: vector.s,v 1.7 1994/04/02 07:00:50 davidg Exp $ */ #include "i386/isa/icu.h" @@ -12,24 +12,44 @@ #define IRQ_BIT(irq_num) (1 << ((irq_num) % 8)) #define IRQ_BYTE(irq_num) ((irq_num) / 8) +#ifdef AUTO_EOI_1 +#define ENABLE_ICU1 /* use auto-EOI to reduce i/o */ +#else #define ENABLE_ICU1 \ movb $ICU_EOI,%al ; /* as soon as possible send EOI ... */ \ FASTER_NOP ; /* ... ASAP ... */ \ outb %al,$IO_ICU1 /* ... to clear in service bit */ -#ifdef AUTO_EOI_1 -#undef ENABLE_ICU1 /* we now use auto-EOI to reduce i/o */ -#define ENABLE_ICU1 #endif +#ifdef AUTO_EOI_2 +/* + * The data sheet says no auto-EOI on slave, but it sometimes works. + */ +#define ENABLE_ICU1_AND_2 ENABLE_ICU1 +#else #define ENABLE_ICU1_AND_2 \ movb $ICU_EOI,%al ; /* as above */ \ FASTER_NOP ; \ outb %al,$IO_ICU2 ; /* but do second icu first */ \ FASTER_NOP ; \ outb %al,$IO_ICU1 /* then first icu */ -#ifdef AUTO_EOI_2 -#undef ENABLE_ICU1_AND_2 /* data sheet says no auto-EOI on slave ... */ -#define ENABLE_ICU1_AND_2 /* ... but it works */ +#endif + +#ifdef FAST_INTR_HANDLER_USES_ES +#define ACTUALLY_PUSHED 1 +#define MAYBE_MOVW_AX_ES movl %ax,%es +#define MAYBE_POPL_ES popl %es +#define MAYBE_PUSHL_ES pushl %es +#else +/* + * We can usually skip loading %es for fastintr handlers. %es should + * only be used for string instructions, and fastintr handlers shouldn't + * do anything slow enough to justify using a string instruction. + */ +#define ACTUALLY_PUSHED 0 +#define MAYBE_MOVW_AX_ES +#define MAYBE_POPL_ES +#define MAYBE_PUSHL_ES #endif /* @@ -82,39 +102,63 @@ pushl %ecx ; \ pushl %edx ; \ pushl %ds ; \ - /* pushl %es ; know compiler doesn't do string insns */ \ + MAYBE_PUSHL_ES ; \ movl $KDSEL,%eax ; \ movl %ax,%ds ; \ - /* movl %ax,%es ; */ \ - SHOW_CLI ; /* although it interferes with "ASAP" */ \ + MAYBE_MOVW_AX_ES ; \ + FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \ pushl $unit ; \ call handler ; /* do the work ASAP */ \ enable_icus ; /* (re)enable ASAP (helps edge trigger?) */ \ addl $4,%esp ; \ incl _cnt+V_INTR ; /* book-keeping can wait */ \ - COUNT_EVENT(_intrcnt_actv, id_num) ; \ - SHOW_STI ; \ - /* popl %es ; */ \ + incl _intrcnt_actv + (id_num) * 4 ; \ + movl _cpl,%eax ; /* are we unmasking pending HWIs or SWIs? */ \ + notl %eax ; \ + andl _ipending,%eax ; \ + jne 1f ; /* yes, handle them */ \ + MEXITCOUNT ; \ + MAYBE_POPL_ES ; \ popl %ds ; \ - popl %edx; \ - popl %ecx; \ - popl %eax; \ - iret + popl %edx ; \ + popl %ecx ; \ + popl %eax ; \ + iret ; \ +; \ + ALIGN_TEXT ; \ +1: ; \ + movl _cpl,%eax ; \ + movl $HWI_MASK|SWI_MASK,_cpl ; /* limit nesting ... */ \ + sti ; /* ... to do this as early as possible */ \ + MAYBE_POPL_ES ; /* discard most of thin frame ... */ \ + popl %ecx ; /* ... original %ds ... */ \ + popl %edx ; \ + xchgl %eax,(1+ACTUALLY_PUSHED)*4(%esp) ; /* orig %eax; save cpl */ \ + pushal ; /* build fat frame (grrr) ... */ \ + pushl %ecx ; /* ... actually %ds ... */ \ + pushl %es ; \ + movl $KDSEL,%eax ; \ + movl %ax,%es ; \ + movl (2+8+0)*4(%esp),%ecx ; /* ... %ecx from thin frame ... */ \ + movl %ecx,(2+6)*4(%esp) ; /* ... to fat frame ... */ \ + movl (2+8+1)*4(%esp),%eax ; /* ... cpl from thin frame */ \ + pushl %eax ; \ + subl $4,%esp ; /* junk for unit number */ \ + MEXITCOUNT ; \ + jmp _doreti #define INTR(unit, irq_num, id_num, mask, handler, icu, enable_icus, reg, stray) \ - pushl $0 ; /* dummy error code */ \ - pushl $T_ASTFLT ; \ + pushl $0 ; /* dumby error code */ \ + pushl $0 ; /* dumby trap type */ \ pushal ; \ - pushl %ds ; /* save our data and extra segments ... */ \ + pushl %ds ; /* save our data and extra segments ... */ \ pushl %es ; \ movl $KDSEL,%eax ; /* ... and reload with kernel's own ... */ \ - movl %ax,%ds ; /* ... early in case SHOW_A_LOT is on */ \ + movl %ax,%ds ; /* ... early for obsolete reasons */ \ movl %ax,%es ; \ - SHOW_CLI ; /* interrupt did an implicit cli */ \ movb _imen + IRQ_BYTE(irq_num),%al ; \ orb $IRQ_BIT(irq_num),%al ; \ movb %al,_imen + IRQ_BYTE(irq_num) ; \ - SHOW_IMEN ; \ FASTER_NOP ; \ outb %al,$icu+1 ; \ enable_icus ; \ @@ -123,32 +167,32 @@ testb $IRQ_BIT(irq_num),%reg ; \ jne 2f ; \ 1: ; \ - COUNT_EVENT(_intrcnt_actv, id_num) ; \ + FAKE_MCOUNT(12*4(%esp)) ; /* XXX late to avoid double count */ \ + incl _intrcnt_actv + (id_num) * 4 ; \ movl _cpl,%eax ; \ pushl %eax ; \ pushl $unit ; \ orl mask,%eax ; \ movl %eax,_cpl ; \ - SHOW_CPL ; \ - SHOW_STI ; \ sti ; \ call handler ; \ movb _imen + IRQ_BYTE(irq_num),%al ; \ andb $~IRQ_BIT(irq_num),%al ; \ movb %al,_imen + IRQ_BYTE(irq_num) ; \ - SHOW_IMEN ; \ FASTER_NOP ; \ outb %al,$icu+1 ; \ - jmp doreti ; \ + MEXITCOUNT ; \ + /* We could usually avoid the following jmp by inlining some of */ \ + /* _doreti, but it's probably better to use less cache. */ \ + jmp _doreti ; \ ; \ ALIGN_TEXT ; \ 2: ; \ - COUNT_EVENT(_intrcnt_pend, id_num) ; \ + /* XXX skip mcounting here to avoid double count */ \ movl $1b,%eax ; /* register resume address */ \ /* XXX - someday do it at attach time */ \ - movl %eax,Vresume + (irq_num) * 4 ; \ + movl %eax,ihandlers + (irq_num) * 4 ; \ orb $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num) ; \ - SHOW_IPENDING ; \ popl %es ; \ popl %ds ; \ popal ; \ @@ -191,7 +235,7 @@ .globl _V/**/name ; \ SUPERALIGN_TEXT ; \ _V/**/name: ; \ - FAST_INTR(unit, irq_num, id_num, handler, ENABLE_ICU/**/icu_enables) + FAST_INTR(unit, irq_num,id_num, handler, ENABLE_ICU/**/icu_enables) #undef BUILD_VECTOR #define BUILD_VECTOR(name, unit, irq_num, id_num, mask, handler, \ @@ -201,9 +245,10 @@ _V/**/name: ; \ .globl _V/**/name ; \ SUPERALIGN_TEXT ; \ _V/**/name: ; \ - INTR(unit,irq_num,id_num, mask, handler, IO_ICU/**/icu_num, \ + INTR(unit,irq_num, id_num, mask, handler, IO_ICU/**/icu_num, \ ENABLE_ICU/**/icu_enables, reg,) +MCOUNT_LABEL(bintr) BUILD_VECTORS /* hardware interrupt catcher (IDT 32 - 47) */ @@ -211,7 +256,7 @@ _V/**/name: ; \ #define STRAYINTR(irq_num, icu_num, icu_enables, reg) \ IDTVEC(intr/**/irq_num) ; \ - INTR(irq_num,irq_num,irq_num, _highmask, _isa_strayintr, \ + INTR(irq_num,irq_num,irq_num, _high_imask, _isa_strayintr, \ IO_ICU/**/icu_num, ENABLE_ICU/**/icu_enables, reg,stray) /* @@ -241,6 +286,7 @@ IDTVEC(intr/**/irq_num) ; \ STRAYINTR(4,1,1, al) STRAYINTR(5,1,1, al) STRAYINTR(6,1,1, al) + STRAYINTR(7,1,1, al) STRAYINTR(8,2,1_AND_2, ah) STRAYINTR(9,2,1_AND_2, ah) STRAYINTR(10,2,1_AND_2, ah) @@ -249,11 +295,11 @@ IDTVEC(intr/**/irq_num) ; \ STRAYINTR(13,2,1_AND_2, ah) STRAYINTR(14,2,1_AND_2, ah) STRAYINTR(15,2,1_AND_2, ah) -IDTVEC(intrdefault) - STRAYINTR(7,1,1, al) /* XXX */ #if 0 INTRSTRAY(255, _highmask, 255) ; call _isa_strayintr ; INTREXIT2 #endif +MCOUNT_LABEL(eintr) + /* * These are the interrupt counters, I moved them here from icu.s so that * they are with the name table. rgrimes @@ -263,7 +309,15 @@ IDTVEC(intrdefault) * work with vmstat. */ .data -Vresume: .space 32 * 4 /* where to resume intr handler after unpend */ +ihandlers: /* addresses of interrupt handlers */ + .space NHWI*4 /* actually resumption addresses for HWI's */ + .long swi_tty, swi_net, 0, 0, 0, 0, 0, 0 + .long 0, 0, 0, 0, 0, 0, swi_clock, swi_ast +imasks: /* masks for interrupt handlers */ + .space NHWI*4 /* padding; HWI masks are elsewhere */ + .long SWI_TTY_MASK, SWI_NET_MASK, 0, 0, 0, 0, 0, 0 + .long 0, 0, 0, 0, 0, 0, SWI_CLOCK_MASK, SWI_AST_MASK + .globl _intrcnt _intrcnt: /* used by vmstat to calc size of table */ .globl _intrcnt_bad7 @@ -274,14 +328,8 @@ _intrcnt_bad15: .space 4 /* glitches on irq 15 */ _intrcnt_stray: .space 4 /* total count of stray interrupts */ .globl _intrcnt_actv _intrcnt_actv: .space NR_REAL_INT_HANDLERS * 4 /* active interrupts */ - .globl _intrcnt_pend -_intrcnt_pend: .space NR_REAL_INT_HANDLERS * 4 /* pending interrupts */ .globl _eintrcnt _eintrcnt: /* used by vmstat to calc size of table */ - .globl _intrcnt_spl -_intrcnt_spl: .space 32 * 4 /* XXX 32 should not be hard coded ? */ - .globl _intrcnt_show -_intrcnt_show: .space 8 * 4 /* XXX 16 should not be hard coded ? */ /* * Build the interrupt name table for vmstat @@ -296,8 +344,9 @@ _intrcnt_show: .space 8 * 4 /* XXX 16 should not be hard coded ? */ .ascii "name irq" ; \ .asciz "irq_num" /* - * XXX - use the STRING and CONCAT macros from <sys/cdefs.h> to stringize - * and concatenate names above and elsewhere. + * XXX - use the __STRING and __CONCAT macros from <sys/cdefs.h> to stringize + * and concatenate names above and elsewhere. Note that __CONCAT doesn't + * work when nested. */ .text @@ -308,61 +357,4 @@ _intrnames: BUILD_VECTOR(stray,,,,,,,,) BUILD_VECTORS -#undef BUILD_FAST_VECTOR -#define BUILD_FAST_VECTOR BUILD_VECTOR - -#undef BUILD_VECTOR -#define BUILD_VECTOR(name, unit, irq_num, id_num, mask, handler, \ - icu_num, icu_enables, reg) \ - .asciz "name pend" - - BUILD_VECTORS _eintrnames: - -/* - * now the spl names - */ - .asciz "unpend_v" - .asciz "doreti" - .asciz "p0!ni" - .asciz "!p0!ni" - .asciz "p0ni" - .asciz "netisr_raw" - .asciz "netisr_ip" - .asciz "netisr_imp" - .asciz "netisr_ns" - .asciz "netisr_iso" - .asciz "softclock" /* 10 */ - .asciz "trap" - .asciz "doreti_exit2" - .asciz "splbio" - .asciz "splclock" - .asciz "splhigh" - .asciz "splimp" - .asciz "splnet" - .asciz "splsoftclock" - .asciz "spltty" - .asciz "spl0" /* 20 */ - .asciz "netisr_raw2" - .asciz "netisr_ip2" - .asciz "netisr_imp2" - .asciz "netisr_ns2" - .asciz "netisr_iso2" - .asciz "splx" - .asciz "splx!0" - .asciz "unpend_V" - .asciz "netisr_x25" - .asciz "netisr_hdlc" - .asciz "spl31" -/* - * now the mask names - */ - .asciz "cli" - .asciz "cpl" - .asciz "imen" - .asciz "ipending" - .asciz "sti" - .asciz "mask5" /* mask5-mask7 are spares */ - .asciz "mask6" - .asciz "mask7" - diff --git a/sys/i386/isa/wd.c b/sys/i386/isa/wd.c index cb22340349e5..d141f040f0e0 100644 --- a/sys/i386/isa/wd.c +++ b/sys/i386/isa/wd.c @@ -37,7 +37,7 @@ static int wdtest = 0; * SUCH DAMAGE. * * from: @(#)wd.c 7.2 (Berkeley) 5/9/91 - * $Id: wd.c,v 1.33.2.1 1994/03/07 01:42:55 rgrimes Exp $ + * $Id: wd.c,v 1.40 1994/06/17 16:57:03 pst Exp $ */ /* TODO: @@ -83,11 +83,11 @@ static int wdtest = 0; #include "syslog.h" #include "vm/vm.h" -#define TIMEOUT 10000 /* XXX? WDCC_DIAGNOSE can take > 1.1 sec */ - +#define TIMEOUT 10000 #define RETRIES 5 /* number of retries before giving up */ #define RECOVERYTIME 500000 /* usec for controller to recover after err */ #define MAXTRANSFER 256 /* max size of transfer in sectors */ +#define BAD144_NO_CYL 0xffff /* XXX should be in dkbad.h; bad144.c uses -1 */ #ifdef notyet #define wdnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */ @@ -149,6 +149,7 @@ struct disk { struct dos_partition dk_dospartitions[NDOSPART]; /* DOS view of disk */ struct dkbad dk_bad; /* bad sector table */ + long dk_badsect[127]; /* 126 plus trailing -1 marker */ }; static struct disk *wddrives[NWD]; /* table of units */ @@ -159,6 +160,8 @@ static struct buf rwdbuf[NWD]; /* buffers for raw IO */ #endif static long wdxfer[NWD]; /* count of transfers */ + +static void bad144intern(struct disk *); static int wdprobe(struct isa_device *dvp); static int wdattach(struct isa_device *dvp); static void wdustart(struct disk *du); @@ -209,9 +212,46 @@ wdprobe(struct isa_device *dvp) /* execute a controller only command */ if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0 - || wdwait(du, 0, TIMEOUT) != 0) + || wdwait(du, 0, TIMEOUT) < 0) goto nodevice; + /* + * drive(s) did not time out during diagnostic : + * Get error status and check that both drives are OK. + * Table 9-2 of ATA specs suggests that we must check for + * a value of 0x01 + * + * Strangely, some controllers will return a status of + * 0x81 (drive 0 OK, drive 1 failure), and then when + * the DRV bit is set, return status of 0x01 (OK) for + * drive 2. (This seems to contradict the ATA spec.) + */ + du->dk_error = inb(du->dk_port + wd_error); + /* printf("Error : %x\n", du->dk_error); */ + if(du->dk_error != 0x01) { + if(du->dk_error & 0x80) { /* drive 1 failure */ + + /* first set the DRV bit */ + u_int sdh; + sdh = inb(du->dk_port+ wd_sdh); + sdh = sdh | 0x10; + outb(du->dk_port+ wd_sdh, sdh); + + /* Wait, to make sure drv 1 has completed diags */ + if ( wdwait(du, 0, TIMEOUT) < 0) + goto nodevice; + + /* Get status for drive 1 */ + du->dk_error = inb(du->dk_port + wd_error); + /* printf("Error (drv 1) : %x\n", du->dk_error); */ + + if(du->dk_error != 0x01) + goto nodevice; + } else /* drive 0 fail */ + goto nodevice; + } + + free(du, M_TEMP); return (IO_WDCSIZE); @@ -318,13 +358,14 @@ wdstrategy(register struct buf *bp) goto done; } +#if !defined(DISKLABEL_UNPROTECTED) /* "soft" write protect check */ if ((du->dk_flags & DKFL_WRITEPROT) && (bp->b_flags & B_READ) == 0) { bp->b_error = EROFS; bp->b_flags |= B_ERROR; goto done; } - +#endif /* !defined(DISKLABEL_UNPROTECTED) */ /* * Do bounds checking, adjust transfer, and set b_cylin. */ @@ -333,10 +374,32 @@ wdstrategy(register struct buf *bp) du->dk_wlabel) <= 0) goto done; + /* + * Check for *any* block on this transfer being on the bad block list + * if it is, then flag the block as a transfer that requires + * bad block handling. Also, used as a hint for low level disksort + * clustering code to keep from coalescing a bad transfer into + * a normal transfer. Single block transfers for a large number of + * blocks associated with a cluster I/O are undersirable. + */ + if( du->dk_flags & DKFL_BADSECT) { + int i; + int nsecs = howmany(bp->b_bcount, DEV_BSIZE); + int blkend = bp->b_pblkno + nsecs; + for(i=0;du->dk_badsect[i] != -1 && du->dk_badsect[i] < blkend;i++) { + if( du->dk_badsect[i] >= bp->b_pblkno) { + bp->b_flags |= B_BAD; + break; + } + } + } + /* queue transfer on drive, activate drive and controller if idle */ dp = &wdutab[lunit]; s = splbio(); - disksort(dp, bp); + + cldisksort(dp, bp, 254*DEV_BSIZE); + if (dp->b_active == 0) wdustart(du); /* start drive */ @@ -352,8 +415,10 @@ wdstrategy(register struct buf *bp) return; done: + s = splbio(); /* toss transfer, we're done early */ biodone(bp); + splx(s); } /* @@ -440,7 +505,7 @@ loop: } /* calculate transfer details */ - blknum = bp->b_blkno + du->dk_skip; + blknum = bp->b_pblkno + du->dk_skip; #ifdef WDDEBUG if (du->dk_skip == 0) printf("wd%d: wdstart: %s %d@%d; map ", lunit, @@ -449,67 +514,49 @@ loop: else printf(" %d)%x", du->dk_skip, inb(du->dk_port + wd_altsts)); #endif - if (du->dk_skip == 0) - du->dk_bc = bp->b_bcount; lp = &du->dk_dd; secpertrk = lp->d_nsectors; secpercyl = lp->d_secpercyl; - if (wddospart(bp->b_dev)) - blknum += du->dk_dd2.d_partitions[wdpart(bp->b_dev)].p_offset; - else - blknum += lp->d_partitions[wdpart(bp->b_dev)].p_offset; - cylin = blknum / secpercyl; - head = (blknum % secpercyl) / secpertrk; - sector = blknum % secpertrk; - /* - * See if the current block is in the bad block list. - * (If we have one, and not formatting.) - */ - if ((du->dk_flags & (DKFL_SINGLE | DKFL_BADSECT)) - == (DKFL_SINGLE | DKFL_BADSECT)) -#define BAD144_NO_CYL 0xffff /* XXX should be in dkbad.h; bad144.c uses -1 */ - for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != BAD144_NO_CYL; - bt_ptr++) { - if (bt_ptr->bt_cyl > cylin) - /* Sorted list, and we passed our cylinder. quit. */ - break; - if (bt_ptr->bt_cyl == cylin && - bt_ptr->bt_trksec == (head << 8) + sector) { - /* - * Found bad block. Calculate new block number. - * This starts at the end of the disk (skip the - * last track which is used for the bad block list), - * and works backwards to the front of the disk. - */ -#ifdef WDDEBUG - printf("--- badblock code -> Old = %ld; ", blknum); -#endif + if (du->dk_skip == 0) { + du->dk_bc = bp->b_bcount; + if (bp->b_flags & B_BAD) { + du->dk_flags |= DKFL_SINGLE; + } + } + + if ((du->dk_flags & (DKFL_SINGLE|DKFL_BADSECT)) /* 19 Aug 92*/ + == (DKFL_SINGLE|DKFL_BADSECT)) { + int i; + for(i=0; + du->dk_badsect[i] != -1 && du->dk_badsect[i] <= blknum; + i++) { + + if( du->dk_badsect[i] == blknum) { /* * XXX the offset of the bad sector table ought * to be stored in the in-core copy of the table. */ #define BAD144_PART 2 /* XXX scattered magic numbers */ #define BSD_PART 0 /* XXX should be 2 but bad144.c uses 0 */ - if (lp->d_partitions[BSD_PART].p_offset != 0) - blknum = lp->d_partitions[BAD144_PART].p_offset - + lp->d_partitions[BAD144_PART].p_size; - else - blknum = lp->d_secperunit; - blknum -= lp->d_nsectors + (bt_ptr - du->dk_bad.bt_bad) - + 1; - - cylin = blknum / secpercyl; - head = (blknum % secpercyl) / secpertrk; - sector = blknum % secpertrk; -#ifdef WDDEBUG - printf("new = %ld\n", blknum); -#endif - break; + if (lp->d_partitions[BSD_PART].p_offset != 0) + blknum = lp->d_partitions[BAD144_PART].p_offset + + lp->d_partitions[BAD144_PART].p_size; + else + blknum = lp->d_secperunit; + blknum -= lp->d_nsectors + i + 1; + + break; + } } } + + + cylin = blknum / secpercyl; + head = (blknum % secpercyl) / secpertrk; + sector = blknum % secpertrk; wdtab[ctrlr].b_active = 1; /* mark controller active */ @@ -643,7 +690,7 @@ wdintr(int unit) return; case 1: wdstart(unit); - return; + return; case 2: goto done; } @@ -690,6 +737,9 @@ oops: chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short)); /* ready to receive data? */ + if ((du->dk_status & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)) + != (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)) + wderror(bp, du, "wdintr: read intr arrived early"); if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) { wderror(bp, du, "wdintr: read error detected late"); goto oops; @@ -840,8 +890,10 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p) du->dk_flags |= DKFL_BSDLABEL; du->dk_flags &= ~DKFL_WRITEPROT; - if (du->dk_dd.d_flags & D_BADSECT) + if (du->dk_dd.d_flags & D_BADSECT) { du->dk_flags |= DKFL_BADSECT; + bad144intern(du); + } /* * Force WDRAW partition to be the whole disk. @@ -1008,6 +1060,7 @@ wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector, static int wdsetctlr(struct disk *du) { + int error = 0; #ifdef WDDEBUG printf("wd(%d,%d): wdsetctlr: C %lu H %lu S %lu\n", du->dk_ctrlr, du->dk_unit, @@ -1028,8 +1081,18 @@ wdsetctlr(struct disk *du) } else { printf("(truncating to 16)\n"); - du->dk_dd.d_ntracks = 16; + du->dk_dd.d_ntracks = 16; + } } + + if (du->dk_dd.d_nsectors == 0 || du->dk_dd.d_nsectors > 255) { + printf("wd%d: cannot handle %lu sectors (max 255)\n", + du->dk_lunit, du->dk_dd.d_nsectors); + error = 1; + } + if (error) { + wdtab[du->dk_ctrlr].b_errcnt += RETRIES; + return (1); } if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0, du->dk_dd.d_nsectors, WDCC_IDC) != 0 @@ -1215,8 +1278,10 @@ wdioctl(dev_t dev, int cmd, caddr_t addr, int flag) case DIOCSBAD: if ((flag & FWRITE) == 0) error = EBADF; - else + else { du->dk_bad = *(struct dkbad *)addr; + bad144intern(du); + } break; case DIOCGDINFO: @@ -1735,4 +1800,27 @@ wdwait(struct disk *du, u_char bits_wanted, int timeout) return (-1); } +/* + * Internalize the bad sector table. + */ +void bad144intern(struct disk *du) { + int i; + if (du->dk_flags & DKFL_BADSECT) { + for (i = 0; i < 127; i++) { + du->dk_badsect[i] = -1; + } + for (i = 0; i < 126; i++) { + if (du->dk_bad.bt_bad[i].bt_cyl == 0xffff) { + break; + } else { + du->dk_badsect[i] = + du->dk_bad.bt_bad[i].bt_cyl * du->dk_dd.d_secpercyl + + (du->dk_bad.bt_bad[i].bt_trksec >> 8) * du->dk_dd.d_nsectors ++ + (du->dk_bad.bt_bad[i].bt_trksec & 0x00ff); + } + } + } +} + #endif /* NWDC > 0 */ diff --git a/sys/i386/isa/wt.c b/sys/i386/isa/wt.c index 307c5062b2be..783df59433bd 100644 --- a/sys/i386/isa/wt.c +++ b/sys/i386/isa/wt.c @@ -19,7 +19,7 @@ * the original CMU copyright notice. * * Version 1.3, Thu Nov 11 12:09:13 MSK 1993 - * $Id: wt.c,v 1.5 1993/12/19 00:50:50 wollman Exp $ + * $Id: wt.c,v 1.6 1994/06/29 06:11:20 jkh Exp $ * */ @@ -895,6 +895,7 @@ static int wtstatus (wtinfo_t *t) outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */ wtpoll (t, t->BUSY, 0); /* wait for not ready */ + DELAY(30); outb (t->CTLPORT, t->ONLINE); /* unset request */ } return (1); diff --git a/sys/i386/isa/wt.c.orig b/sys/i386/isa/wt.c.orig new file mode 100644 index 000000000000..572802a6fa9b --- /dev/null +++ b/sys/i386/isa/wt.c.orig @@ -0,0 +1,902 @@ +/* + * Streamer tape driver for 386bsd and FreeBSD. + * Supports Archive and Wangtek compatible QIC-02/QIC-36 boards. + * + * Copyright (C) 1993 by: + * Sergey Ryzhkov <sir@kiae.su> + * Serge Vakulenko <vak@zebub.msk.su> + * + * This software is distributed with NO WARRANTIES, not even the implied + * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Authors grant any other persons or organisations permission to use + * or modify this software as long as this message is kept with the software, + * all derivative works or modified versions. + * + * This driver is derived from the old 386bsd Wangtek streamer tape driver, + * made by Robert Baron at CMU, based on Intel sources. + * Authors thank Robert Baron, CMU and Intel and retain here + * the original CMU copyright notice. + * + * Version 1.3, Thu Nov 11 12:09:13 MSK 1993 + * $Id: wt.c,v 1.6 1994/06/29 06:11:20 jkh Exp $ + * + */ + +/* + * Copyright (c) 1989 Carnegie-Mellon University. + * All rights reserved. + * + * Authors: Robert Baron + * + * Permission to use, copy, modify and distribute this software and + * its documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#include "wt.h" +#if NWT > 0 + +#include "sys/param.h" +#include "systm.h" +#include "kernel.h" +#include "sys/buf.h" +#include "sys/fcntl.h" +#include "sys/malloc.h" +#include "sys/ioctl.h" +#include "sys/mtio.h" +#include "vm/vm_param.h" +#include "i386/include/pio.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/wtreg.h" + +/* + * Uncomment this to enable internal device tracing. + */ +#define DEBUG(s) /* printf s */ + +#define WTPRI (PZERO+10) /* sleep priority */ + +/* + * Wangtek controller ports + */ +#define WT_CTLPORT(base) ((base)+0) /* control, write only */ +#define WT_STATPORT(base) ((base)+0) /* status, read only */ +#define WT_CMDPORT(base) ((base)+1) /* command, write only */ +#define WT_DATAPORT(base) ((base)+1) /* data, read only */ +#define WT_NPORT 2 /* 2 i/o ports */ + +/* status port bits */ +#define WT_BUSY 0x01 /* not ready bit define */ +#define WT_NOEXCEP 0x02 /* no exception bit define */ +#define WT_RESETMASK 0x07 /* to check after reset */ +#define WT_RESETVAL 0x05 /* state after reset */ + +/* control port bits */ +#define WT_ONLINE 0x01 /* device selected */ +#define WT_RESET 0x02 /* reset command */ +#define WT_REQUEST 0x04 /* request command */ +#define WT_IEN 0x08 /* enable dma */ + +/* + * Archive controller ports + */ +#define AV_DATAPORT(base) ((base)+0) /* data, read only */ +#define AV_CMDPORT(base) ((base)+0) /* command, write only */ +#define AV_STATPORT(base) ((base)+1) /* status, read only */ +#define AV_CTLPORT(base) ((base)+1) /* control, write only */ +#define AV_SDMAPORT(base) ((base)+2) /* start dma */ +#define AV_RDMAPORT(base) ((base)+3) /* reset dma */ +#define AV_NPORT 4 /* 4 i/o ports */ + +/* status port bits */ +#define AV_BUSY 0x40 /* not ready bit define */ +#define AV_NOEXCEP 0x20 /* no exception bit define */ +#define AV_RESETMASK 0xf8 /* to check after reset */ +#define AV_RESETVAL 0x50 /* state after reset */ + +/* control port bits */ +#define AV_RESET 0x80 /* reset command */ +#define AV_REQUEST 0x40 /* request command */ +#define AV_IEN 0x20 /* enable interrupts */ + +enum wttype { + UNKNOWN = 0, /* unknown type, driver disabled */ + ARCHIVE, /* Archive Viper SC499, SC402 etc */ + WANGTEK, /* Wangtek */ +}; + +typedef struct { + unsigned short err; /* code for error encountered */ + unsigned short ercnt; /* number of error blocks */ + unsigned short urcnt; /* number of underruns */ +} wtstatus_t; + +typedef struct { + enum wttype type; /* type of controller */ + unsigned unit; /* unit number */ + unsigned port; /* base i/o port */ + unsigned chan; /* dma channel number, 1..3 */ + unsigned flags; /* state of tape drive */ + unsigned dens; /* tape density */ + int bsize; /* tape block size */ + void *buf; /* internal i/o buffer */ + + void *dmavaddr; /* virtual address of dma i/o buffer */ + unsigned dmatotal; /* size of i/o buffer */ + unsigned dmaflags; /* i/o direction, B_READ or B_WRITE */ + unsigned dmacount; /* resulting length of dma i/o */ + + wtstatus_t error; /* status of controller */ + + unsigned short DATAPORT, CMDPORT, STATPORT, CTLPORT, SDMAPORT, RDMAPORT; + unsigned char BUSY, NOEXCEP, RESETMASK, RESETVAL; + unsigned char ONLINE, RESET, REQUEST, IEN; +} wtinfo_t; + +wtinfo_t wttab[NWT]; /* tape info by unit number */ + +static int wtwait (wtinfo_t *t, int catch, char *msg); +static int wtcmd (wtinfo_t *t, int cmd); +static int wtstart (wtinfo_t *t, unsigned mode, void *vaddr, unsigned len); +static void wtdma (wtinfo_t *t); +static void wtimer (caddr_t, int); +static void wtclock (wtinfo_t *t); +static int wtreset (wtinfo_t *t); +static int wtsense (wtinfo_t *t, int verb, int ignor); +static int wtstatus (wtinfo_t *t); +static void wtrewind (wtinfo_t *t); +static int wtreadfm (wtinfo_t *t); +static int wtwritefm (wtinfo_t *t); +static int wtpoll (wtinfo_t *t, int mask, int bits); + +/* XXX */ +extern void DELAY (int usec); + +/* + * Probe for the presence of the device. + */ +int wtprobe (struct isa_device *id) +{ + wtinfo_t *t = wttab + id->id_unit; + + t->unit = id->id_unit; + t->chan = id->id_drq; + t->port = id->id_iobase; + if (t->chan<1 || t->chan>3) { + printf ("wt%d: Bad drq=%d, should be 1..3\n", t->unit, t->chan); + return (0); + } + + /* Try Wangtek. */ + t->type = WANGTEK; + t->CTLPORT = WT_CTLPORT (t->port); t->STATPORT = WT_STATPORT (t->port); + t->CMDPORT = WT_CMDPORT (t->port); t->DATAPORT = WT_DATAPORT (t->port); + t->SDMAPORT = 0; t->RDMAPORT = 0; + t->BUSY = WT_BUSY; t->NOEXCEP = WT_NOEXCEP; + t->RESETMASK = WT_RESETMASK; t->RESETVAL = WT_RESETVAL; + t->ONLINE = WT_ONLINE; t->RESET = WT_RESET; + t->REQUEST = WT_REQUEST; t->IEN = WT_IEN; + if (wtreset (t)) + return (WT_NPORT); + + /* Try Archive. */ + t->type = ARCHIVE; + t->CTLPORT = AV_CTLPORT (t->port); t->STATPORT = AV_STATPORT (t->port); + t->CMDPORT = AV_CMDPORT (t->port); t->DATAPORT = AV_DATAPORT (t->port); + t->SDMAPORT = AV_SDMAPORT (t->port); t->RDMAPORT = AV_RDMAPORT (t->port); + t->BUSY = AV_BUSY; t->NOEXCEP = AV_NOEXCEP; + t->RESETMASK = AV_RESETMASK; t->RESETVAL = AV_RESETVAL; + t->ONLINE = 0; t->RESET = AV_RESET; + t->REQUEST = AV_REQUEST; t->IEN = AV_IEN; + if (wtreset (t)) + return (AV_NPORT); + + /* Tape controller not found. */ + t->type = UNKNOWN; + return (0); +} + +/* + * Device is found, configure it. + */ +int wtattach (struct isa_device *id) +{ + wtinfo_t *t = wttab + id->id_unit; + + if (t->type == ARCHIVE) { + printf ("wt%d: type <Archive>\n", t->unit); + outb (t->RDMAPORT, 0); /* reset dma */ + } else + printf ("wt%d: type <Wangtek>\n", t->unit); + t->flags = TPSTART; /* tape is rewound */ + t->dens = -1; /* unknown density */ + return (1); +} + +struct isa_driver wtdriver = { wtprobe, wtattach, "wt", }; + +int wtdump (int dev) +{ + /* Not implemented */ + return (EINVAL); +} + +int wtsize (int dev) +{ + /* Not implemented */ + return (-1); +} + +/* + * Open routine, called on every device open. + */ +int wtopen (int dev, int flag) +{ + int u = minor (dev) & T_UNIT; + wtinfo_t *t = wttab + u; + int error; + + if (u >= NWT || t->type == UNKNOWN) + return (ENXIO); + + /* Check that device is not in use */ + if (t->flags & TPINUSE) + return (EBUSY); + + /* If the tape is in rewound state, check the status and set density. */ + if (t->flags & TPSTART) { + /* If rewind is going on, wait */ + if (error = wtwait (t, PCATCH, "wtrew")) + return (error); + + /* Check the controller status */ + if (! wtsense (t, 0, (flag & FWRITE) ? 0 : TP_WRP)) { + /* Bad status, reset the controller */ + if (! wtreset (t)) + return (EIO); + if (! wtsense (t, 1, (flag & FWRITE) ? 0 : TP_WRP)) + return (EIO); + } + + /* Set up tape density. */ + if (t->dens != (minor (dev) & WT_DENSEL)) { + int d = 0; + + switch (minor (dev) & WT_DENSEL) { + case WT_DENSDFLT: default: break; /* default density */ + case WT_QIC11: d = QIC_FMT11; break; /* minor 010 */ + case WT_QIC24: d = QIC_FMT24; break; /* minor 020 */ + case WT_QIC120: d = QIC_FMT120; break; /* minor 030 */ + case WT_QIC150: d = QIC_FMT150; break; /* minor 040 */ + case WT_QIC300: d = QIC_FMT300; break; /* minor 050 */ + case WT_QIC600: d = QIC_FMT600; break; /* minor 060 */ + } + if (d) { + /* Change tape density. */ + if (! wtcmd (t, d)) + return (EIO); + if (! wtsense (t, 1, TP_WRP | TP_ILL)) + return (EIO); + + /* Check the status of the controller. */ + if (t->error.err & TP_ILL) { + printf ("wt%d: invalid tape density\n", t->unit); + return (ENODEV); + } + } + t->dens = minor (dev) & WT_DENSEL; + } + t->flags &= ~TPSTART; + } else if (t->dens != (minor (dev) & WT_DENSEL)) + return (ENXIO); + + t->bsize = (minor (dev) & WT_BSIZE) ? 1024 : 512; + t->buf = malloc (t->bsize, M_TEMP, M_WAITOK); + if (! t->buf) + return (EAGAIN); + + t->flags = TPINUSE; + if (flag & FREAD) + t->flags |= TPREAD; + if (flag & FWRITE) + t->flags |= TPWRITE; + return (0); +} + +/* + * Close routine, called on last device close. + */ +int wtclose (int dev) +{ + int u = minor (dev) & T_UNIT; + wtinfo_t *t = wttab + u; + + if (u >= NWT || t->type == UNKNOWN) + return (ENXIO); + + /* If rewind is pending, do nothing */ + if (t->flags & TPREW) + goto done; + + /* If seek forward is pending and no rewind on close, do nothing */ + if (t->flags & TPRMARK) { + if (minor (dev) & T_NOREWIND) + goto done; + + /* If read file mark is going on, wait */ + wtwait (t, 0, "wtrfm"); + } + + if (t->flags & TPWANY) + /* Tape was written. Write file mark. */ + wtwritefm (t); + + if (! (minor (dev) & T_NOREWIND)) { + /* Rewind tape to beginning of tape. */ + /* Don't wait until rewind, though. */ + wtrewind (t); + goto done; + } + if ((t->flags & TPRANY) && ! (t->flags & (TPVOL | TPWANY))) + /* Space forward to after next file mark if no writing done. */ + /* Don't wait for completion. */ + wtreadfm (t); +done: + t->flags &= TPREW | TPRMARK | TPSTART | TPTIMER; + free (t->buf, M_TEMP); + return (0); +} + +/* + * Ioctl routine. Compatible with BSD ioctls. + * Direct QIC-02 commands ERASE and RETENSION added. + * There are three possible ioctls: + * ioctl (int fd, MTIOCGET, struct mtget *buf) -- get status + * ioctl (int fd, MTIOCTOP, struct mtop *buf) -- do BSD-like op + * ioctl (int fd, WTQICMD, int qicop) -- do QIC op + */ +int wtioctl (int dev, int cmd, void *arg, int mode) +{ + int u = minor (dev) & T_UNIT; + wtinfo_t *t = wttab + u; + int error, count, op; + + if (u >= NWT || t->type == UNKNOWN) + return (ENXIO); + + switch (cmd) { + default: + return (EINVAL); + case WTQICMD: /* direct QIC command */ + op = (int) *(void**)arg; + switch (op) { + default: + return (EINVAL); + case QIC_ERASE: /* erase the whole tape */ + if (! (t->flags & TPWRITE) || (t->flags & TPWP)) + return (EACCES); + if (error = wtwait (t, PCATCH, "wterase")) + return (error); + break; + case QIC_RETENS: /* retension the tape */ + if (error = wtwait (t, PCATCH, "wtretens")) + return (error); + break; + } + /* Both ERASE and RETENS operations work like REWIND. */ + /* Simulate the rewind operation here. */ + t->flags &= ~(TPRO | TPWO | TPVOL); + if (! wtcmd (t, op)) + return (EIO); + t->flags |= TPSTART | TPREW; + if (op == QIC_ERASE) + t->flags |= TPWANY; + wtclock (t); + return (0); + case MTIOCIEOT: /* ignore EOT errors */ + case MTIOCEEOT: /* enable EOT errors */ + return (0); + case MTIOCGET: + ((struct mtget*)arg)->mt_type = + t->type == ARCHIVE ? MT_ISVIPER1 : 0x11; + ((struct mtget*)arg)->mt_dsreg = t->flags; /* status */ + ((struct mtget*)arg)->mt_erreg = t->error.err; /* errors */ + ((struct mtget*)arg)->mt_resid = 0; + ((struct mtget*)arg)->mt_fileno = 0; /* file */ + ((struct mtget*)arg)->mt_blkno = 0; /* block */ + return (0); + case MTIOCTOP: + break; + } + switch ((short) ((struct mtop*)arg)->mt_op) { + default: + case MTFSR: /* forward space record */ + case MTBSR: /* backward space record */ + case MTBSF: /* backward space file */ + break; + case MTNOP: /* no operation, sets status only */ + case MTCACHE: /* enable controller cache */ + case MTNOCACHE: /* disable controller cache */ + return (0); + case MTREW: /* rewind */ + case MTOFFL: /* rewind and put the drive offline */ + if (t->flags & TPREW) /* rewind is running */ + return (0); + if (error = wtwait (t, PCATCH, "wtorew")) + return (error); + wtrewind (t); + return (0); + case MTFSF: /* forward space file */ + for (count=((struct mtop*)arg)->mt_count; count>0; --count) { + if (error = wtwait (t, PCATCH, "wtorfm")) + return (error); + if (error = wtreadfm (t)) + return (error); + } + return (0); + case MTWEOF: /* write an end-of-file record */ + if (! (t->flags & TPWRITE) || (t->flags & TPWP)) + return (EACCES); + if (error = wtwait (t, PCATCH, "wtowfm")) + return (error); + if (error = wtwritefm (t)) + return (error); + return (0); + } + return (EINVAL); +} + +/* + * Strategy routine. + */ +void wtstrategy (struct buf *bp) +{ + int u = minor (bp->b_dev) & T_UNIT; + wtinfo_t *t = wttab + u; + int s; + + bp->b_resid = bp->b_bcount; + if (u >= NWT || t->type == UNKNOWN) + goto errxit; + + /* at file marks and end of tape, we just return '0 bytes available' */ + if (t->flags & TPVOL) + goto xit; + + if (bp->b_flags & B_READ) { + /* Check read access and no previous write to this tape. */ + if (! (t->flags & TPREAD) || (t->flags & TPWANY)) + goto errxit; + + /* For now, we assume that all data will be copied out */ + /* If read command outstanding, just skip down */ + if (! (t->flags & TPRO)) { + if (! wtsense (t, 1, TP_WRP)) /* clear status */ + goto errxit; + if (! wtcmd (t, QIC_RDDATA)) { /* sed read mode */ + wtsense (t, 1, TP_WRP); + goto errxit; + } + t->flags |= TPRO | TPRANY; + } + } else { + /* Check write access and write protection. */ + /* No previous read from this tape allowed. */ + if (! (t->flags & TPWRITE) || (t->flags & (TPWP | TPRANY))) + goto errxit; + + /* If write command outstanding, just skip down */ + if (! (t->flags & TPWO)) { + if (! wtsense (t, 1, 0)) /* clear status */ + goto errxit; + if (! wtcmd (t, QIC_WRTDATA)) { /* set write mode */ + wtsense (t, 1, 0); + goto errxit; + } + t->flags |= TPWO | TPWANY; + } + } + + if (! bp->b_bcount) + goto xit; + + t->flags &= ~TPEXCEP; + s = splbio (); + if (wtstart (t, bp->b_flags, bp->b_un.b_addr, bp->b_bcount)) { + wtwait (t, 0, (bp->b_flags & B_READ) ? "wtread" : "wtwrite"); + bp->b_resid -= t->dmacount; + } + splx (s); + + if (t->flags & TPEXCEP) { +errxit: bp->b_flags |= B_ERROR; + bp->b_error = EIO; + } +xit: biodone (bp); + return; +} + +/* + * Interrupt routine. + */ +void wtintr (int u) +{ + wtinfo_t *t = wttab + u; + unsigned char s; + + if (u >= NWT || t->type == UNKNOWN) { + DEBUG (("wtintr() -- device not configured\n")); + return; + } + + s = inb (t->STATPORT); /* get status */ + DEBUG (("wtintr() status=0x%x -- ", s)); + if ((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP)) { + DEBUG (("busy\n")); + return; /* device is busy */ + } + + /* + * Check if rewind finished. + */ + if (t->flags & TPREW) { + DEBUG (((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP) ? + "rewind busy?\n" : "rewind finished\n")); + t->flags &= ~TPREW; /* Rewind finished. */ + wtsense (t, 1, TP_WRP); + wakeup ((caddr_t)t); + return; + } + + /* + * Check if writing/reading of file mark finished. + */ + if (t->flags & (TPRMARK | TPWMARK)) { + DEBUG (((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP) ? + "marker r/w busy?\n" : "marker r/w finished\n")); + if (! (s & t->NOEXCEP)) /* operation failed */ + wtsense (t, 1, (t->flags & TPRMARK) ? TP_WRP : 0); + t->flags &= ~(TPRMARK | TPWMARK); /* operation finished */ + wakeup ((caddr_t)t); + return; + } + + /* + * Do we started any i/o? If no, just return. + */ + if (! (t->flags & TPACTIVE)) { + DEBUG (("unexpected interrupt\n")); + return; + } + t->flags &= ~TPACTIVE; + t->dmacount += t->bsize; /* increment counter */ + + /* + * Clean up dma. + */ + if ((t->dmaflags & B_READ) && (t->dmatotal - t->dmacount) < t->bsize) { + /* If reading short block, copy the internal buffer + * to the user memory. */ + isa_dmadone (t->dmaflags, t->buf, t->bsize, t->chan); + bcopy (t->buf, t->dmavaddr, t->dmatotal - t->dmacount); + } else + isa_dmadone (t->dmaflags, t->dmavaddr, t->bsize, t->chan); + + /* + * On exception, check for end of file and end of volume. + */ + if (! (s & t->NOEXCEP)) { + DEBUG (("i/o exception\n")); + wtsense (t, 1, (t->dmaflags & B_READ) ? TP_WRP : 0); + if (t->error.err & (TP_EOM | TP_FIL)) + t->flags |= TPVOL; /* end of file */ + else + t->flags |= TPEXCEP; /* i/o error */ + wakeup ((caddr_t)t); + return; + } + + if (t->dmacount < t->dmatotal) { /* continue i/o */ + t->dmavaddr += t->bsize; + wtdma (t); + DEBUG (("continue i/o, %d\n", t->dmacount)); + return; + } + if (t->dmacount > t->dmatotal) /* short last block */ + t->dmacount = t->dmatotal; + wakeup ((caddr_t)t); /* wake up user level */ + DEBUG (("i/o finished, %d\n", t->dmacount)); +} + +/* start the rewind operation */ +static void wtrewind (wtinfo_t *t) +{ + int rwmode = (t->flags & (TPRO | TPWO)); + + t->flags &= ~(TPRO | TPWO | TPVOL); + /* + * Wangtek strictly follows QIC-02 standard: + * clearing ONLINE in read/write modes causes rewind. + * REWIND command is not allowed in read/write mode + * and gives `illegal command' error. + */ + if (t->type==WANGTEK && rwmode) { + outb (t->CTLPORT, 0); + } else if (! wtcmd (t, QIC_REWIND)) + return; + t->flags |= TPSTART | TPREW; + wtclock (t); +} + +/* start the `read marker' operation */ +static int wtreadfm (wtinfo_t *t) +{ + t->flags &= ~(TPRO | TPWO | TPVOL); + if (! wtcmd (t, QIC_READFM)) { + wtsense (t, 1, TP_WRP); + return (EIO); + } + t->flags |= TPRMARK | TPRANY; + wtclock (t); + /* Don't wait for completion here. */ + return (0); +} + +/* write marker to the tape */ +static int wtwritefm (wtinfo_t *t) +{ + tsleep ((caddr_t)wtwritefm, WTPRI, "wtwfm", hz); /* timeout: 1 second */ + t->flags &= ~(TPRO | TPWO); + if (! wtcmd (t, QIC_WRITEFM)) { + wtsense (t, 1, 0); + return (EIO); + } + t->flags |= TPWMARK | TPWANY; + wtclock (t); + return (wtwait (t, 0, "wtwfm")); +} + +/* while controller status & mask == bits continue waiting */ +static int wtpoll (wtinfo_t *t, int mask, int bits) +{ + int s, i; + + /* Poll status port, waiting for specified bits. */ + for (i=0; i<1000; ++i) { /* up to 1 msec */ + s = inb (t->STATPORT); + if ((s & mask) != bits) + return (s); + DELAY (1); + } + for (i=0; i<100; ++i) { /* up to 10 msec */ + s = inb (t->STATPORT); + if ((s & mask) != bits) + return (s); + DELAY (100); + } + for (;;) { /* forever */ + s = inb (t->STATPORT); + if ((s & mask) != bits) + return (s); + tsleep ((caddr_t)wtpoll, WTPRI, "wtpoll", 1); /* timeout: 1 tick */ + } +} + +/* execute QIC command */ +static int wtcmd (wtinfo_t *t, int cmd) +{ + int s; + + DEBUG (("wtcmd() cmd=0x%x\n", cmd)); + s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */ + if (! (s & t->NOEXCEP)) /* error */ + return (0); + + outb (t->CMDPORT, cmd); /* output the command */ + + outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */ + wtpoll (t, t->BUSY, t->BUSY); /* wait for ready */ + outb (t->CTLPORT, t->IEN | t->ONLINE); /* reset request */ + wtpoll (t, t->BUSY, 0); /* wait for not ready */ + return (1); +} + +/* wait for the end of i/o, seeking marker or rewind operation */ +static int wtwait (wtinfo_t *t, int catch, char *msg) +{ + int error; + + DEBUG (("wtwait() `%s'\n", msg)); + while (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK)) + if (error = tsleep ((caddr_t)t, WTPRI | catch, msg, 0)) + return (error); + return (0); +} + +/* initialize dma for the i/o operation */ +static void wtdma (wtinfo_t *t) +{ + t->flags |= TPACTIVE; + wtclock (t); + + if (t->type == ARCHIVE) + outb (t->SDMAPORT, 0); /* set dma */ + + if ((t->dmaflags & B_READ) && (t->dmatotal - t->dmacount) < t->bsize) + /* Reading short block. Do it through the internal buffer. */ + isa_dmastart (t->dmaflags, t->buf, t->bsize, t->chan); + else + isa_dmastart (t->dmaflags, t->dmavaddr, t->bsize, t->chan); +} + +/* start i/o operation */ +static int wtstart (wtinfo_t *t, unsigned flags, void *vaddr, unsigned len) +{ + int s; + + DEBUG (("wtstart()\n")); + s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */ + if (! (s & t->NOEXCEP)) { + t->flags |= TPEXCEP; /* error */ + return (0); + } + t->flags &= ~TPEXCEP; /* clear exception flag */ + t->dmavaddr = vaddr; + t->dmatotal = len; + t->dmacount = 0; + t->dmaflags = flags; + wtdma (t); + return (1); +} + +/* start timer */ +static void wtclock (wtinfo_t *t) +{ + if (! (t->flags & TPTIMER)) { + t->flags |= TPTIMER; + /* Some controllers seem to lose dma interrupts too often. + * To make the tape stream we need 1 tick timeout. */ + timeout (wtimer, (caddr_t)t, (t->flags & TPACTIVE) ? 1 : hz); + } +} + +/* + * Simulate an interrupt periodically while i/o is going. + * This is necessary in case interrupts get eaten due to + * multiple devices on a single IRQ line. + */ +static void wtimer (caddr_t xt, int dummy) +{ + wtinfo_t *t = (wtinfo_t *)xt; + int s; + + t->flags &= ~TPTIMER; + if (! (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK))) + return; + + /* If i/o going, simulate interrupt. */ + s = splbio (); + if ((inb (t->STATPORT) & (t->BUSY | t->NOEXCEP)) != (t->BUSY | t->NOEXCEP)) { + DEBUG (("wtimer() -- ")); + wtintr (t->unit); + } + splx (s); + + /* Restart timer if i/o pending. */ + if (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK)) + wtclock (t); +} + +/* reset the controller */ +static int wtreset (wtinfo_t *t) +{ + /* Perform QIC-02 and QIC-36 compatible reset sequence. */ + /* Thanks to Mikael Hybsch <micke@dynas.se>. */ + int s, i; + + outb (t->CTLPORT, t->RESET | t->ONLINE); /* send reset */ + DELAY (30); + outb (t->CTLPORT, t->ONLINE); /* turn off reset */ + DELAY (30); + + /* Read the controller status. */ + s = inb (t->STATPORT); + if (s == 0xff) /* no port at this address? */ + return (0); + + /* Wait 3 sec for reset to complete. Needed for QIC-36 boards? */ + for (i=0; i<3000; ++i) { + if (! (s & t->BUSY) || ! (s & t->NOEXCEP)) + break; + DELAY (1000); + s = inb (t->STATPORT); + } + return ((s & t->RESETMASK) == t->RESETVAL); +} + +/* get controller status information */ +/* return 0 if user i/o request should receive an i/o error code */ +static int wtsense (wtinfo_t *t, int verb, int ignor) +{ + char *msg = 0; + int err; + + DEBUG (("wtsense() ignor=0x%x\n", ignor)); + t->flags &= ~(TPRO | TPWO); + if (! wtstatus (t)) + return (0); + if (! (t->error.err & TP_ST0)) + t->error.err &= ~TP_ST0MASK; + if (! (t->error.err & TP_ST1)) + t->error.err &= ~TP_ST1MASK; + t->error.err &= ~ignor; /* ignore certain errors */ + err = t->error.err & (TP_FIL | TP_BNL | TP_UDA | TP_EOM | TP_WRP | + TP_USL | TP_CNI | TP_MBD | TP_NDT | TP_ILL); + if (! err) + return (1); + if (! verb) + return (0); + + /* lifted from tdriver.c from Wangtek */ + if (err & TP_USL) msg = "Drive not online"; + else if (err & TP_CNI) msg = "No cartridge"; + else if ((err & TP_WRP) && !(t->flags & TPWP)) { + msg = "Tape is write protected"; + t->flags |= TPWP; + } + else if (err & TP_FIL) msg = 0 /*"Filemark detected"*/; + else if (err & TP_EOM) msg = 0 /*"End of tape"*/; + else if (err & TP_BNL) msg = "Block not located"; + else if (err & TP_UDA) msg = "Unrecoverable data error"; + else if (err & TP_NDT) msg = "No data detected"; + else if (err & TP_ILL) msg = "Illegal command"; + if (msg) + printf ("wt%d: %s\n", t->unit, msg); + return (0); +} + +/* get controller status information */ +static int wtstatus (wtinfo_t *t) +{ + char *p; + + wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */ + outb (t->CMDPORT, QIC_RDSTAT); /* send `read status' command */ + + outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */ + wtpoll (t, t->BUSY, t->BUSY); /* wait for ready */ + outb (t->CTLPORT, t->ONLINE); /* reset request */ + wtpoll (t, t->BUSY, 0); /* wait for not ready */ + + p = (char*) &t->error; + while (p < (char*)&t->error + 6) { + int s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); + if (! (s & t->NOEXCEP)) /* error */ + return (0); + + *p++ = inb (t->DATAPORT); /* read status byte */ + + outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */ + wtpoll (t, t->BUSY, 0); /* wait for not ready */ + outb (t->CTLPORT, t->ONLINE); /* unset request */ + } + return (1); +} +#endif /* NWT */ diff --git a/sys/i386/netboot/Makefile b/sys/i386/netboot/Makefile index 550cd7be44cd..b7b2b442e557 100644 --- a/sys/i386/netboot/Makefile +++ b/sys/i386/netboot/Makefile @@ -5,13 +5,18 @@ # -DSMALL_ROM - Compile for 8K ROMS # -DROMSIZE - Size of EPROM - Must be set (even for .COM files) # -DRELOC - Relocation address (usually 0x90000) +# -DINCLUDE_WD - Include Western Digital/SMC support +# -DINCLUDE_NE - Include NE1000/NE2000 support +# -DNE_BASE - Base I/O address for NE1000/NE2000 +# -DWD_DEFAULT_MEM- Default memory location for WD/SMC cards # ROMSIZE=16384 RELOCADDR=0x90000 -CFLAGS=-O2 -DNFS -DROMSIZE=$(ROMSIZE) -DRELOC=$(RELOCADDR) +CFLAGS= -O2 -DNFS -DINCLUDE_WD -DINCLUDE_NE -DROMSIZE=$(ROMSIZE) \ + -DRELOC=$(RELOCADDR) -DNE_BASE=0x320 -DWD_DEFAULT_MEM=0xD0000 HDRS=netboot.h -COBJS=main.o misc.o wd80x3.o bootmenu.o +COBJS=main.o misc.o ether.o bootmenu.o SSRCS=start2.S SOBJS=start2.o diff --git a/sys/i386/netboot/bootmenu.c b/sys/i386/netboot/bootmenu.c index c6b7e79d99b4..d77e7efc773e 100644 --- a/sys/i386/netboot/bootmenu.c +++ b/sys/i386/netboot/bootmenu.c @@ -86,7 +86,7 @@ CMD_BOOTFILE - set boot filename cmd_bootfile(p) char *p; { - char *q = bootname; + char *q = bootfile = bootname; if (*p) { while(*p) *(q++) = *(p++); diff --git a/sys/i386/netboot/ether.c b/sys/i386/netboot/ether.c new file mode 100644 index 000000000000..69448026c4e4 --- /dev/null +++ b/sys/i386/netboot/ether.c @@ -0,0 +1,479 @@ + +/************************************************************************** +NETBOOT - BOOTP/TFTP Bootstrap Program + +Author: Martin Renters + Date: May/94 + + This code is based heavily on David Greenman's if_ed.c driver + + Copyright (C) 1993-1994, David Greenman, Martin Renters. + This software may be used, modified, copied, distributed, and sold, in + both source and binary form provided that the above copyright and these + terms are retained. Under no circumstances are the authors responsible for + the proper functioning of this software, nor do the authors assume any + responsibility for damages incurred with its use. + + +**************************************************************************/ + +#include "netboot.h" +#include "ether.h" + +unsigned short eth_nic_base; +unsigned short eth_asic_base; +unsigned char eth_tx_start; +unsigned char eth_laar; +unsigned char eth_flags; +unsigned char eth_vendor; +unsigned char eth_memsize; +unsigned char *eth_bmem; +unsigned char *eth_node_addr; + +/************************************************************************** +The following two variables are used externally +**************************************************************************/ +char packet[ETH_MAX_PACKET]; +int packetlen; + +/************************************************************************** +ETH_PROBE - Look for an adapter +**************************************************************************/ +eth_probe() +{ + int i; + struct wd_board *brd; + char *name; + unsigned short chksum; + unsigned char c; + + eth_vendor = VENDOR_NONE; + +#ifdef INCLUDE_WD + /****************************************************************** + Search for WD/SMC cards + ******************************************************************/ + for (eth_asic_base = WD_LOW_BASE; eth_asic_base <= WD_HIGH_BASE; + eth_asic_base += 0x20) { + chksum = 0; + for (i=8; i<16; i++) + chksum += inb(i+eth_asic_base); + if ((chksum & 0x00FF) == 0x00FF) + break; + } + if (eth_asic_base <= WD_HIGH_BASE) { /* We've found a board */ + eth_vendor = VENDOR_WD; + eth_nic_base = eth_asic_base + WD_NIC_ADDR; + c = inb(eth_asic_base+WD_BID); /* Get board id */ + for (brd = wd_boards; brd->name; brd++) + if (brd->id == c) break; + if (!brd->name) { + printf("\r\nUnknown Ethernet type %x\r\n", c); + return(0); /* Unknown type */ + } + eth_flags = brd->flags; + eth_memsize = brd->memsize; + eth_tx_start = 0; + if ((c == TYPE_WD8013EP) && + (inb(eth_asic_base + WD_ICR) & WD_ICR_16BIT)) { + eth_flags = FLAG_16BIT; + eth_memsize = MEM_16384; + } + if ((c & WD_SOFTCONFIG) && (!(eth_flags & FLAG_790))) { + eth_bmem = (char *)(0x80000 | + ((inb(eth_asic_base + WD_MSR) & 0x3F) << 13)); + } else + eth_bmem = (char *)WD_DEFAULT_MEM; + outb(eth_asic_base + WD_MSR, 0x80); /* Reset */ + printf("\r\n%s base 0x%x, memory 0x%X, addr ", + brd->name, eth_asic_base, eth_bmem); + for (i=0; i<6; i++) + printhb((int)(arptable[ARP_CLIENT].node[i] = + inb(i+eth_asic_base+WD_LAR))); + if (eth_flags & FLAG_790) { + outb(eth_asic_base+WD_MSR, WD_MSR_MENB); + outb(eth_asic_base+0x04, (inb(eth_asic_base+0x04) | + 0x80)); + outb(eth_asic_base+0x0B, + (((unsigned)eth_bmem >> 13) & 0x0F) | + (((unsigned)eth_bmem >> 11) & 0x40) | + (inb(eth_asic_base+0x0B) & 0x0B)); + outb(eth_asic_base+0x04, (inb(eth_asic_base+0x04) & + ~0x80)); + } else { + outb(eth_asic_base+WD_MSR, + (((unsigned)eth_bmem >> 13) & 0x3F) | 0x40); + } + if (eth_flags & FLAG_16BIT) { + if (eth_flags & FLAG_790) { + eth_laar = inb(eth_asic_base + WD_LAAR); + outb(eth_asic_base + WD_LAAR, WD_LAAR_M16EN); + inb(0x84); + } else { + outb(eth_asic_base + WD_LAAR, (eth_laar = + WD_LAAR_M16EN | WD_LAAR_L16EN | 1)); + } + } + printf("\r\n"); + + } +#endif +#ifdef INCLUDE_NE + /****************************************************************** + Search for NE1000/2000 if no WD/SMC cards + ******************************************************************/ + if (eth_vendor != VENDOR_WD) { + char romdata[16], testbuf[32]; + char test[] = "NE1000/2000 memory"; + eth_bmem = (char *)0; /* No shared memory */ + eth_asic_base = NE_BASE + NE_ASIC_OFFSET; + eth_nic_base = NE_BASE; + eth_vendor = VENDOR_NOVELL; + eth_flags = FLAG_PIO; + eth_memsize = MEM_16384; + eth_tx_start = 32; + c = inb(eth_asic_base + NE_RESET); + outb(eth_asic_base + NE_RESET, c); + inb(0x84); + outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_STP | + D8390_COMMAND_RD2); + outb(eth_nic_base + D8390_P0_RCR, D8390_RCR_MON); + outb(eth_nic_base + D8390_P0_DCR, D8390_DCR_FT1 | D8390_DCR_LS); + outb(eth_nic_base + D8390_P0_PSTART, MEM_8192); + outb(eth_nic_base + D8390_P0_PSTOP, MEM_16384); + eth_pio_write(test, 8192, sizeof(test)); + eth_pio_read(8192, testbuf, sizeof(test)); + if (!bcompare(test, testbuf, sizeof(test))) { + eth_flags |= FLAG_16BIT; + eth_memsize = MEM_32768; + eth_tx_start = 64; + outb(eth_nic_base + D8390_P0_DCR, D8390_DCR_WTS | + D8390_DCR_FT1 | D8390_DCR_LS); + outb(eth_nic_base + D8390_P0_PSTART, MEM_16384); + outb(eth_nic_base + D8390_P0_PSTOP, MEM_32768); + eth_pio_write(test, 16384, sizeof(test)); + eth_pio_read(16384, testbuf, sizeof(test)); + if (!bcompare(testbuf, test, sizeof(test))) return(0); + } + eth_pio_read(0, romdata, 16); + printf("\r\nNE1000/NE2000 base 0x%x, addr ", eth_nic_base); + for (i=0; i<6; i++) + printhb((int)(arptable[ARP_CLIENT].node[i] = romdata[i + + ((eth_flags & FLAG_16BIT) ? i : 0)])); + printf("\r\n"); + } +#endif + if (eth_vendor == VENDOR_NONE) return(0); + + eth_node_addr = arptable[ARP_CLIENT].node; + eth_reset(); + return(eth_vendor); +} + +/************************************************************************** +ETH_RESET - Reset adapter +**************************************************************************/ +eth_reset() +{ + int i; + if (eth_flags & FLAG_790) + outb(eth_nic_base+D8390_P0_COMMAND, + D8390_COMMAND_PS0 | D8390_COMMAND_STP); + else + outb(eth_nic_base+D8390_P0_COMMAND, + D8390_COMMAND_PS0 | D8390_COMMAND_RD2 | + D8390_COMMAND_STP); + if (eth_flags & FLAG_16BIT) + outb(eth_nic_base+D8390_P0_DCR, 0x49); + else + outb(eth_nic_base+D8390_P0_DCR, 0x48); + outb(eth_nic_base+D8390_P0_RBCR0, 0); + outb(eth_nic_base+D8390_P0_RBCR1, 0); + outb(eth_nic_base+D8390_P0_RCR, 4); /* allow broadcast frames */ + outb(eth_nic_base+D8390_P0_TCR, 2); + outb(eth_nic_base+D8390_P0_TPSR, eth_tx_start); + outb(eth_nic_base+D8390_P0_PSTART, eth_tx_start + D8390_TXBUF_SIZE); + if (eth_flags & FLAG_790) outb(eth_nic_base + 0x09, 0); + outb(eth_nic_base+D8390_P0_PSTOP, eth_memsize); + outb(eth_nic_base+D8390_P0_BOUND, eth_tx_start + D8390_TXBUF_SIZE); + outb(eth_nic_base+D8390_P0_ISR, 0xFF); + outb(eth_nic_base+D8390_P0_IMR, 0); + if (eth_flags & FLAG_790) + outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS1 | + D8390_COMMAND_STP); + else + outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS1 | + D8390_COMMAND_RD2 | D8390_COMMAND_STP); + for (i=0; i<6; i++) + outb(eth_nic_base+D8390_P1_PAR0+i, eth_node_addr[i]); + for (i=0; i<6; i++) + outb(eth_nic_base+D8390_P1_MAR0+i, 0xFF); + outb(eth_nic_base+D8390_P1_CURR, eth_tx_start + D8390_TXBUF_SIZE+1); + if (eth_flags & FLAG_790) + outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 | + D8390_COMMAND_STA); + else + outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 | + D8390_COMMAND_RD2 | D8390_COMMAND_STA); + outb(eth_nic_base+D8390_P0_ISR, 0xFF); + outb(eth_nic_base+D8390_P0_TCR, 0); + return(1); +} + +/************************************************************************** +ETH_TRANSMIT - Transmit a frame +**************************************************************************/ +eth_transmit(d,t,s,p) + char *d; /* Destination */ + unsigned short t; /* Type */ + unsigned short s; /* size */ + char *p; /* Packet */ +{ + unsigned char c; +#ifdef INCLUDE_WD + if (eth_vendor == VENDOR_WD) { /* Memory interface */ + if (eth_flags & FLAG_16BIT) { + outb(eth_asic_base + WD_LAAR, eth_laar | WD_LAAR_M16EN); + inb(0x84); + } + if (eth_flags & FLAG_790) { + outb(eth_asic_base + WD_MSR, WD_MSR_MENB); + inb(0x84); + } + inb(0x84); + bcopy(d, eth_bmem, 6); /* dst */ + bcopy(eth_node_addr, eth_bmem+6, ETHER_ADDR_SIZE); /* src */ + *(eth_bmem+12) = t>>8; /* type */ + *(eth_bmem+13) = t; + bcopy(p, eth_bmem+14, s); + s += 14; + while (s < ETH_MIN_PACKET) *(eth_bmem+(s++)) = 0; + if (eth_flags & FLAG_790) { + outb(eth_asic_base + WD_MSR, 0); + inb(0x84); + } + if (eth_flags & FLAG_16BIT) { + outb(eth_asic_base + WD_LAAR, eth_laar & ~WD_LAAR_M16EN); + inb(0x84); + } + } +#endif +#ifdef INCLUDE_NE + if (eth_vendor == VENDOR_NOVELL) { /* Programmed I/O */ + unsigned short type; + type = (t >> 8) | (t << 8); + eth_pio_write(d, eth_tx_start<<8, 6); + eth_pio_write(eth_node_addr, (eth_tx_start<<8)+6, 6); + eth_pio_write(&type, (eth_tx_start<<8)+12, 2); + eth_pio_write(p, (eth_tx_start<<8)+14, s); + s += 14; + if (s < ETH_MIN_PACKET) s = ETH_MIN_PACKET; + } +#endif + twiddle(); + if (eth_flags & FLAG_790) + outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 | + D8390_COMMAND_STA); + else + outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 | + D8390_COMMAND_RD2 | D8390_COMMAND_STA); + outb(eth_nic_base+D8390_P0_TPSR, eth_tx_start); + outb(eth_nic_base+D8390_P0_TBCR0, s); + outb(eth_nic_base+D8390_P0_TBCR1, s>>8); + if (eth_flags & FLAG_790) + outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 | + D8390_COMMAND_TXP | D8390_COMMAND_STA); + else + outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 | + D8390_COMMAND_TXP | D8390_COMMAND_RD2 | + D8390_COMMAND_STA); + return(0); +} + +/************************************************************************** +ETH_POLL - Wait for a frame +**************************************************************************/ +eth_poll() +{ + int ret = 0; + unsigned short type = 0; + unsigned char bound,curr,rstat; + unsigned short len; + unsigned short pktoff; + unsigned char *p; + struct ringbuffer pkthdr; + rstat = inb(eth_nic_base+D8390_P0_RSR); + if (rstat & D8390_RSTAT_OVER) { + eth_reset(); + return(0); + } + if (!(rstat & D8390_RSTAT_PRX)) return(0); + bound = inb(eth_nic_base+D8390_P0_BOUND)+1; + if (bound == eth_memsize) bound = eth_tx_start + D8390_TXBUF_SIZE; + outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS1); + curr = inb(eth_nic_base+D8390_P1_CURR); + outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0); + if (curr == eth_memsize) curr=eth_tx_start + D8390_TXBUF_SIZE; + if (curr == bound) return(0); + if (eth_vendor == VENDOR_WD) { + if (eth_flags & FLAG_16BIT) { + outb(eth_asic_base + WD_LAAR, eth_laar | WD_LAAR_M16EN); + inb(0x84); + } + if (eth_flags & FLAG_790) { + outb(eth_asic_base + WD_MSR, WD_MSR_MENB); + inb(0x84); + } + inb(0x84); + } + pktoff = (bound << 8); + if (eth_flags & FLAG_PIO) + eth_pio_read(pktoff, &pkthdr, 4); + else + bcopy(eth_bmem + pktoff, &pkthdr, 4); + len = pkthdr.len - 4; /* sub CRC */ + pktoff += 4; + if (len > 1514) len = 1514; + bound = pkthdr.bound; /* New bound ptr */ + if ( (pkthdr.status & D8390_RSTAT_PRX) && (len > 14) && (len < 1518)) { + p = packet; + packetlen = len; + len = (eth_memsize << 8) - pktoff; + if (packetlen > len) { /* We have a wrap-around */ + if (eth_flags & FLAG_PIO) + eth_pio_read(pktoff, p, len); + else + bcopy(eth_bmem + pktoff, p, len); + pktoff = (eth_tx_start + D8390_TXBUF_SIZE) << 8; + p += len; + packetlen -= len; + } + if (eth_flags & FLAG_PIO) + eth_pio_read(pktoff, p, packetlen); + else + bcopy(eth_bmem + pktoff, p, packetlen); + + type = (packet[12]<<8) | packet[13]; + ret = 1; + } + if (eth_vendor == VENDOR_WD) { + if (eth_flags & FLAG_790) { + outb(eth_asic_base + WD_MSR, 0); + inb(0x84); + } + if (eth_flags & FLAG_16BIT) { + outb(eth_asic_base + WD_LAAR, eth_laar & + ~WD_LAAR_M16EN); + inb(0x84); + } + inb(0x84); + } + if (bound == (eth_tx_start + D8390_TXBUF_SIZE)) + bound = eth_memsize; + outb(eth_nic_base+D8390_P0_BOUND, bound-1); + if (ret && (type == ARP)) { + struct arprequest *arpreq; + unsigned long reqip; + arpreq = (struct arprequest *)&packet[ETHER_HDR_SIZE]; + convert_ipaddr(&reqip, arpreq->tipaddr); + if ((ntohs(arpreq->opcode) == ARP_REQUEST) && + (reqip == arptable[ARP_CLIENT].ipaddr)) { + arpreq->opcode = htons(ARP_REPLY); + bcopy(arpreq->sipaddr, arpreq->tipaddr, 4); + bcopy(arpreq->shwaddr, arpreq->thwaddr, 6); + bcopy(arptable[ARP_CLIENT].node, arpreq->shwaddr, 6); + convert_ipaddr(arpreq->sipaddr, &reqip); + eth_transmit(arpreq->thwaddr, ARP, sizeof(struct arprequest), + arpreq); + return(0); + } + } + return(ret); +} + +#ifdef INCLUDE_NE +/************************************************************************** +NE1000/NE2000 Support Routines +**************************************************************************/ +static inline unsigned short inw(unsigned short a) +{ + unsigned short d; + asm volatile( "inw %1, %0" : "=a" (d) : "d" (a)); + return d; +} + +static inline void outw(unsigned short a, unsigned short d) +{ + asm volatile( "outw %0, %1" : : "a" (d), "d" (a)); +} + +/************************************************************************** +ETH_PIO_READ - Read a frame via Programmed I/O +**************************************************************************/ +eth_pio_read(src, dst, cnt, init) + unsigned short src; + unsigned char *dst; + unsigned short cnt; + int init; +{ + if (cnt & 1) cnt++; + outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD2 | + D8390_COMMAND_STA); + outb(eth_nic_base + D8390_P0_RBCR0, cnt); + outb(eth_nic_base + D8390_P0_RBCR1, cnt>>8); + outb(eth_nic_base + D8390_P0_RSAR0, src); + outb(eth_nic_base + D8390_P0_RSAR1, src>>8); + outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD0 | + D8390_COMMAND_STA); + if (eth_flags & FLAG_16BIT) { + while (cnt) { + *((unsigned short *)dst) = inw(eth_asic_base + NE_DATA); + dst += 2; + cnt -= 2; + } + } + else { + while (cnt--) + *(dst++) = inb(eth_asic_base + NE_DATA); + } +} + +/************************************************************************** +ETH_PIO_WRITE - Write a frame via Programmed I/O +**************************************************************************/ +eth_pio_write(src, dst, cnt, init) + unsigned char *src; + unsigned short dst; + unsigned short cnt; + int init; +{ + outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD2 | + D8390_COMMAND_STA); + outb(eth_nic_base + D8390_P0_ISR, D8390_ISR_RDC); + outb(eth_nic_base + D8390_P0_RBCR0, cnt); + outb(eth_nic_base + D8390_P0_RBCR1, cnt>>8); + outb(eth_nic_base + D8390_P0_RSAR0, dst); + outb(eth_nic_base + D8390_P0_RSAR1, dst>>8); + outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD1 | + D8390_COMMAND_STA); + if (eth_flags & FLAG_16BIT) { + if (cnt & 1) cnt++; /* Round up */ + while (cnt) { + outw(eth_asic_base + NE_DATA, *((unsigned short *)src)); + src += 2; + cnt -= 2; + } + } + else { + while (cnt--) + outb(eth_asic_base + NE_DATA, *(src++)); + } + while((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC) + != D8390_ISR_RDC); +} +#else +/************************************************************************** +ETH_PIO_READ - Dummy routine when NE2000 not compiled in +**************************************************************************/ +eth_pio_read() {} +#endif diff --git a/sys/i386/netboot/ether.h b/sys/i386/netboot/ether.h new file mode 100644 index 000000000000..586a35cb963b --- /dev/null +++ b/sys/i386/netboot/ether.h @@ -0,0 +1,178 @@ +/************************************************************************** +NETBOOT - BOOTP/TFTP Bootstrap Program + +Author: Martin Renters + Date: Jun/94 + +**************************************************************************/ + +#define TRUE 1 +#define FALSE 0 + +#define ETH_MIN_PACKET 64 +#define ETH_MAX_PACKET 1518 + +#define VENDOR_NONE 0 +#define VENDOR_WD 1 +#define VENDOR_NOVELL 2 + +#define FLAG_PIO 0x01 +#define FLAG_16BIT 0x02 +#define FLAG_790 0x04 + +#define MEM_8192 32 +#define MEM_16384 64 +#define MEM_32768 128 + +/************************************************************************** +Western Digital/SMC Board Definitions +**************************************************************************/ +#define WD_LOW_BASE 0x200 +#define WD_HIGH_BASE 0x3e0 +#ifndef WD_DEFAULT_MEM +#define WD_DEFAULT_MEM 0xD0000 +#endif +#define WD_NIC_ADDR 0x10 + +/************************************************************************** +Western Digital/SMC ASIC Addresses +**************************************************************************/ +#define WD_MSR 0x00 +#define WD_ICR 0x01 +#define WD_IAR 0x02 +#define WD_BIO 0x03 +#define WD_IRR 0x04 +#define WD_LAAR 0x05 +#define WD_IJR 0x06 +#define WD_GP2 0x07 +#define WD_LAR 0x08 +#define WD_BID 0x0E + +#define WD_ICR_16BIT 0x01 + +#define WD_MSR_MENB 0x40 + +#define WD_LAAR_L16EN 0x40 +#define WD_LAAR_M16EN 0x80 + +#define WD_SOFTCONFIG 0x20 + +/************************************************************************** +Western Digital/SMC Board Types +**************************************************************************/ +#define TYPE_WD8003S 0x02 +#define TYPE_WD8003E 0x03 +#define TYPE_WD8013EBT 0x05 +#define TYPE_WD8003W 0x24 +#define TYPE_WD8003EB 0x25 +#define TYPE_WD8013W 0x26 +#define TYPE_WD8013EP 0x27 +#define TYPE_WD8013WC 0x28 +#define TYPE_WD8013EPC 0x29 +#define TYPE_SMC8216T 0x2a +#define TYPE_SMC8216C 0x2b +#define TYPE_SMC8013EBP 0x2c + +#ifdef INCLUDE_WD +struct wd_board { + char *name; + char id; + char flags; + char memsize; +} wd_boards[] = { + {"WD8003S", TYPE_WD8003S, 0, MEM_8192}, + {"WD8003E", TYPE_WD8003E, 0, MEM_8192}, + {"WD8013EBT", TYPE_WD8013EBT, FLAG_16BIT, MEM_16384}, + {"WD8003W", TYPE_WD8003W, 0, MEM_8192}, + {"WD8003EB", TYPE_WD8003EB, 0, MEM_8192}, + {"WD8013W", TYPE_WD8013W, FLAG_16BIT, MEM_16384}, + {"WD8003EP/WD8013EP", + TYPE_WD8013EP, 0, MEM_8192}, + {"WD8013WC", TYPE_WD8013WC, FLAG_16BIT, MEM_16384}, + {"WD8013EPC", TYPE_WD8013EPC, FLAG_16BIT, MEM_16384}, + {"SMC8216T", TYPE_SMC8216T, FLAG_16BIT | FLAG_790, MEM_16384}, + {"SMC8216C", TYPE_SMC8216C, FLAG_16BIT | FLAG_790, MEM_16384}, + {"SMC8013EBP", TYPE_SMC8013EBP,FLAG_16BIT, MEM_16384}, + {NULL, 0, 0} +}; +#endif + +/************************************************************************** +NE1000/2000 definitions +**************************************************************************/ +#ifndef NE_BASE +#define NE_BASE 0x320 +#endif +#define NE_ASIC_OFFSET 0x10 +#define NE_RESET 0x0F /* Used to reset card */ +#define NE_DATA 0x00 /* Used to read/write NIC mem */ + +/************************************************************************** +8390 Register Definitions +**************************************************************************/ +#define D8390_P0_COMMAND 0x00 +#define D8390_P0_PSTART 0x01 +#define D8390_P0_PSTOP 0x02 +#define D8390_P0_BOUND 0x03 +#define D8390_P0_TSR 0x04 +#define D8390_P0_TPSR 0x04 +#define D8390_P0_TBCR0 0x05 +#define D8390_P0_TBCR1 0x06 +#define D8390_P0_ISR 0x07 +#define D8390_P0_RSAR0 0x08 +#define D8390_P0_RSAR1 0x09 +#define D8390_P0_RBCR0 0x0A +#define D8390_P0_RBCR1 0x0B +#define D8390_P0_RSR 0x0C +#define D8390_P0_RCR 0x0C +#define D8390_P0_TCR 0x0D +#define D8390_P0_DCR 0x0E +#define D8390_P0_IMR 0x0F +#define D8390_P1_COMMAND 0x00 +#define D8390_P1_PAR0 0x01 +#define D8390_P1_PAR1 0x02 +#define D8390_P1_PAR2 0x03 +#define D8390_P1_PAR3 0x04 +#define D8390_P1_PAR4 0x05 +#define D8390_P1_PAR5 0x06 +#define D8390_P1_CURR 0x07 +#define D8390_P1_MAR0 0x08 + +#define D8390_COMMAND_PS0 0x0 /* Page 0 select */ +#define D8390_COMMAND_PS1 0x40 /* Page 1 select */ +#define D8390_COMMAND_PS2 0x80 /* Page 2 select */ +#define D8390_COMMAND_RD2 0x20 /* Remote DMA control */ +#define D8390_COMMAND_RD1 0x10 +#define D8390_COMMAND_RD0 0x08 +#define D8390_COMMAND_TXP 0x04 /* transmit packet */ +#define D8390_COMMAND_STA 0x02 /* start */ +#define D8390_COMMAND_STP 0x01 /* stop */ + +#define D8390_RCR_MON 0x20 /* monitor mode */ + +#define D8390_DCR_FT1 0x40 +#define D8390_DCR_LS 0x08 /* Loopback select */ +#define D8390_DCR_WTS 0x01 /* Word transfer select */ + +#define D8390_ISR_PRX 0x01 /* successful recv */ +#define D8390_ISR_PTX 0x02 /* successful xmit */ +#define D8390_ISR_RXE 0x04 /* receive error */ +#define D8390_ISR_TXE 0x08 /* transmit error */ +#define D8390_ISR_OVW 0x10 /* Overflow */ +#define D8390_ISR_CNT 0x20 /* Counter overflow */ +#define D8390_ISR_RDC 0x40 /* Remote DMA complete */ +#define D8390_ISR_RST 0x80 /* reset */ + +#define D8390_RSTAT_PRX 0x01 /* successful recv */ +#define D8390_RSTAT_CRC 0x02 /* CRC error */ +#define D8390_RSTAT_FAE 0x04 /* Frame alignment error */ +#define D8390_RSTAT_OVER 0x08 /* overflow */ + +#define D8390_TXBUF_SIZE 6 +#define D8390_RXBUF_END 32 + +struct ringbuffer { + unsigned char status; + unsigned char bound; + unsigned short len; +}; diff --git a/sys/i386/netboot/wd80x3.c b/sys/i386/netboot/wd80x3.c deleted file mode 100644 index a68dc44c1f18..000000000000 --- a/sys/i386/netboot/wd80x3.c +++ /dev/null @@ -1,240 +0,0 @@ - -/************************************************************************** -NETBOOT - BOOTP/TFTP Bootstrap Program - -Author: Martin Renters - Date: Dec/93 - -**************************************************************************/ - -#include "netboot.h" - -unsigned short we_base; -unsigned char *we_bmem; - -#define WE_LOW_BASE 0x200 -#define WE_HIGH_BASE 0x3e0 -#define WE_DEFAULT_MEM 0xD0000 -#define WE_MIN_PACKET 64 -#define WE_TXBUF_SIZE 6 -#define WE_RXBUF_END 32 - -#define WE_MSR 0x00 -#define WE_ICR 0x01 -#define WE_IAR 0x02 -#define WE_BIO 0x03 -#define WE_IRR 0x04 -#define WE_LAAR 0x05 -#define WE_IJR 0x06 -#define WE_GP2 0x07 -#define WE_LAR 0x08 -#define WE_BID 0x0E -#define WE_P0_COMMAND 0x10 -#define WE_P0_PSTART 0x11 -#define WE_P0_PSTOP 0x12 -#define WE_P0_BOUND 0x13 -#define WE_P0_TSR 0x14 -#define WE_P0_TPSR 0x14 -#define WE_P0_TBCR0 0x15 -#define WE_P0_TBCR1 0x16 -#define WE_P0_ISR 0x17 -#define WE_P0_RBCR0 0x1A -#define WE_P0_RBCR1 0x1B -#define WE_P0_RSR 0x1C -#define WE_P0_RCR 0x1C -#define WE_P0_TCR 0x1D -#define WE_P0_DCR 0x1E -#define WE_P0_IMR 0x1F -#define WE_P1_COMMAND 0x10 -#define WE_P1_PAR0 0x11 -#define WE_P1_PAR1 0x12 -#define WE_P1_PAR2 0x13 -#define WE_P1_PAR3 0x14 -#define WE_P1_PAR4 0x15 -#define WE_P1_PAR5 0x16 -#define WE_P1_CURR 0x17 -#define WE_P1_MAR0 0x18 - -#define WE_COMMAND_PS0 0x0 /* Page 0 select */ -#define WE_COMMAND_PS1 0x40 /* Page 1 select */ -#define WE_COMMAND_PS2 0x80 /* Page 2 select */ -#define WE_COMMAND_TXP 0x04 /* transmit packet */ -#define WE_COMMAND_STA 0x02 /* start */ -#define WE_COMMAND_STP 0x01 /* stop */ - -#define WE_ISR_PRX 0x01 /* successful recv */ -#define WE_ISR_PTX 0x02 /* successful xmit */ -#define WE_ISR_RXE 0x04 /* receive error */ -#define WE_ISR_TXE 0x08 /* transmit error */ -#define WE_ISR_OVW 0x10 /* Overflow */ -#define WE_ISR_CNT 0x20 /* Counter overflow */ -#define WE_ISR_RST 0x80 /* reset */ - -#define WE_RSTAT_PRX 0x01 /* successful recv */ -#define WE_RSTAT_CRC 0x02 /* CRC error */ -#define WE_RSTAT_FAE 0x04 /* Frame alignment error */ -#define WE_RSTAT_OVER 0x08 /* overflow */ - -char packet[1600]; -int packetlen; -int bit16; - -/************************************************************************** -ETH_PROBE - Look for an adapter -**************************************************************************/ -eth_probe() -{ - unsigned short base; - unsigned short chksum; - unsigned char c; - int i; - for (we_base = WE_LOW_BASE; we_base <= WE_HIGH_BASE; we_base += 0x20) { - chksum = 0; - for (i=8; i<16; i++) - chksum += inb(i+we_base); - if ((chksum & 0x00FF) == 0x00FF) - break; - } - if (we_base > WE_HIGH_BASE) return(0); /* No adapter found */ - - for (i = 1; i<6; i++) /* Look for aliased registers */ - if (inb(we_base+i) != inb(we_base+i+WE_LAR)) break; - if (i == 6) { /* Aliased */ - we_bmem = (char *)WE_DEFAULT_MEM; - bit16 = 0; - } else { - we_bmem = (char *)(0x80000 | ((inb(we_base+WE_MSR) & 0x3F) << 13)); - bit16 = 1; - } - outb(we_base+WE_MSR, 0x80); /* Reset */ - printf("\r\nSMC80x3 base 0x%x, memory 0x%X, etheraddr ",we_base, we_bmem); - for (i=0; i<6; i++) - printhb((int)(arptable[ARP_CLIENT].node[i] = inb(i+we_base+WE_LAR))); - printf("\r\n"); - outb(we_base+WE_MSR,(((unsigned)we_bmem >> 13) & 0x3F) | 0x40); - iskey(); /* Kill some time while device resets */ - eth_reset(); -} - -/************************************************************************** -ETH_RESET - Reset adapter -**************************************************************************/ -eth_reset() -{ - int i; - outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0 | WE_COMMAND_STP); - outb(we_base+WE_P0_DCR, 0x48); - outb(we_base+WE_P0_RBCR0, 0); - outb(we_base+WE_P0_RBCR1, 0); - outb(we_base+WE_P0_RCR, 4); /* allow broadcast frames */ - outb(we_base+WE_P0_TCR, 0); - outb(we_base+WE_P0_TPSR, 0); - outb(we_base+WE_P0_PSTART, WE_TXBUF_SIZE); - outb(we_base+WE_P0_PSTOP, WE_RXBUF_END); /* All cards have 8K */ - outb(we_base+WE_P0_BOUND, WE_TXBUF_SIZE); - outb(we_base+WE_P0_IMR, 0); - outb(we_base+WE_P0_ISR, 0xFF); - if (bit16) outb(we_base+WE_LAAR, 1); /* Turn off 16bit mode */ - outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS1); - for (i=0; i<6; i++) - outb(we_base+WE_P1_PAR0+i, inb(we_base+WE_LAR+i)); - for (i=0; i<6; i++) - outb(we_base+WE_P1_MAR0+i, 0xFF); - outb(we_base+WE_P1_CURR, WE_TXBUF_SIZE+1); - outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0 | WE_COMMAND_STA); - return(1); -} - -/************************************************************************** -ETH_TRANSMIT - Transmit a frame -**************************************************************************/ -eth_transmit(d,t,s,p) - char *d; /* Destination */ - unsigned short t; /* Type */ - unsigned short s; /* size */ - char *p; /* Packet */ -{ - unsigned char c; - bcopy(d, we_bmem, 6); /* Copy destination */ - bcopy(arptable[ARP_CLIENT].node, we_bmem+6, ETHER_ADDR_SIZE); /* My ether addr */ - *(we_bmem+12) = t>>8; /* Type field */ - *(we_bmem+13) = t; - bcopy(p, we_bmem+14, s); - s += 14; - while (s < WE_MIN_PACKET) *(we_bmem+(s++)) = 0; - twiddle(); - outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0); - outb(we_base+WE_P0_TPSR, 0); - outb(we_base+WE_P0_TBCR0, s); - outb(we_base+WE_P0_TBCR1, s>>8); - outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0 | WE_COMMAND_TXP); - return(0); -} - -/************************************************************************** -ETH_POLL - Wait for a frame -**************************************************************************/ -eth_poll() -{ - int ret = 0; - unsigned short type = 0; - unsigned char bound,curr,rstat; - unsigned short len; - unsigned char *pkt, *p; - rstat = inb(we_base+WE_P0_RSR); - if (rstat & WE_RSTAT_OVER) { - eth_reset(); - return(0); - } - if (!(rstat & WE_RSTAT_PRX)) return(0); - bound = inb(we_base+WE_P0_BOUND)+1; - if (bound == WE_RXBUF_END) bound = WE_TXBUF_SIZE; - outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS1); - curr = inb(we_base+WE_P1_CURR); - outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0); - if (curr == WE_RXBUF_END) curr=WE_TXBUF_SIZE; - if (curr == bound) return(0); - pkt = we_bmem + (bound << 8); - len = *((unsigned short *)(pkt+2)) - 4; /* sub CRC */ - if (len > 1514) len = 1514; -#ifdef DEBUG -printf("[R%dS%dC%dB%dN%d]",len, rstat, curr,bound,*(pkt+1)); -#endif - bound = *(pkt+1); /* New bound ptr */ - p = packet; - if ( (*pkt & WE_RSTAT_PRX) && (len > 14) && (len < 1518)) { - pkt += 4; - packetlen = len; - while (len) { - while (len && (pkt < (we_bmem + 8192))) { - *(p++) = *(pkt++); - len--; - } - pkt = we_bmem + (WE_TXBUF_SIZE << 8); - } - type = (packet[12]<<8) | packet[13]; - ret = 1; - } - if (bound == WE_TXBUF_SIZE) - bound = WE_RXBUF_END; - outb(we_base+WE_P0_BOUND, bound-1); - if (ret && (type == ARP)) { - struct arprequest *arpreq; - unsigned long reqip; - arpreq = (struct arprequest *)&packet[ETHER_HDR_SIZE]; - convert_ipaddr(&reqip, arpreq->tipaddr); - if ((ntohs(arpreq->opcode) == ARP_REQUEST) && - (reqip == arptable[ARP_CLIENT].ipaddr)) { - arpreq->opcode = htons(ARP_REPLY); - bcopy(arpreq->sipaddr, arpreq->tipaddr, 4); - bcopy(arpreq->shwaddr, arpreq->thwaddr, 6); - bcopy(arptable[ARP_CLIENT].node, arpreq->shwaddr, 6); - convert_ipaddr(arpreq->sipaddr, &reqip); - eth_transmit(arpreq->thwaddr, ARP, sizeof(struct arprequest), - arpreq); - return(0); - } - } - return(ret); -} - diff --git a/sys/i386/pci/ncr.c b/sys/i386/pci/ncr.c new file mode 100644 index 000000000000..0c094afcf609 --- /dev/null +++ b/sys/i386/pci/ncr.c @@ -0,0 +1,5507 @@ +/************************************************************************** +** +** $Id: ncr.c,v 2.0.0.3 94/07/24 09:02:42 wolf Exp $ +** +** Device driver for the NCR 53C810 PCI-SCSI-Controller. +** +** 386bsd / FreeBSD +** +**------------------------------------------------------------------------- +** +** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany +** <wolf@dentaro.GUN.de> +** +** This is a beta version - use with care. +** +**------------------------------------------------------------------------- +** +** $Log: ncr.c,v $ +** Revision 2.0.0.3 94/07/24 09:02:42 wolf +** sstat0 used to calculate residue in int_ma. +** log messages extended. +** +** Revision 2.0.0.2 94/07/22 19:04:26 wolf +** ncr_int_ma: byte count corrected with dfifo. +** script: dispatch and no_data changed. +** +** Revision 2.0.0.1 94/07/19 21:42:02 wolf +** New debug value: DEBUG_FREEZE +** M_REJECT log entry includes rejected message. +** Phase change in command/status/msg phase logged. +** Timeout exception handler locked. +** +** Revision 2.0 94/07/10 15:53:22 wolf +** FreeBSD release. +** +** Revision 1.0 94/06/07 20:02:16 wolf +** Beta release. +** +*************************************************************************** +*/ + +#ifdef KERNEL +#include <ncr.h> +#else /* KERNEL */ +#define NNCR 1 +#endif /* KERNEL */ + +#if NNCR > 0 + +#define NCR_VERSION (2) + + +/*========================================================== +** +** Configuration and Debugging +** +** May be overwritten in <i386/conf/XXXXX> +** +**========================================================== +*/ + +/* +** Enable/Disable debug messages. +** Can be changed at runtime too. +*/ + +#ifndef SCSI_NCR_DEBUG +#define SCSI_NCR_DEBUG (0) +#endif /* SCSI_NCR_DEBUG */ + +/* +** SCSI address of this device. +** The boot routines should have set it. +** If not, use this. +*/ + +#ifndef SCSI_NCR_MYADDR +#define SCSI_NCR_MYADDR (7) +#endif /* SCSI_NCR_MYADDR */ + +/* +** The maximal synchronous frequency in kHz. +** (0=asynchronous) +*/ + +#ifndef SCSI_NCR_MAX_SYNC +#define SCSI_NCR_MAX_SYNC (0) +#endif /* SCSI_NCR_MAX_SYNC */ + +/* +** The maximum number of tags per logic unit. +** Used only for disk devices that support tags. +*/ + +#ifndef SCSI_NCR_MAX_TAGS +#define SCSI_NCR_MAX_TAGS (8) +#endif /* SCSI_NCR_MAX_TAGS */ + +/*========================================================== +** +** Configuration and Debugging +** +**========================================================== +*/ + +/* +** Number of targets supported by the driver. +** n permits target numbers 0..n-1. +** Default is 7, meaning targets #0..#6. +** #7 .. is myself. +*/ + +#define MAX_TARGET (7) + +/* +** Number of logic units supported by the driver. +** n enables logic unit numbers 0..n-1. +** The common SCSI devices require only +** one lun, so take 1 as the default. +*/ + +#define MAX_LUN (1) + +/* +** The maximum number of jobs scheduled for starting. +** There should be one slot per target, and one slot +** for each tag of each target. +*/ + +#define MAX_START (20) + +/* +** The maximum number of segments a transfer is split into. +*/ + +#define MAX_SCATTER (33) + +/* +** The maximum transfer length (should be >= 64k). +** MUST NOT be greater than (MAX_SCATTER-1) * NBPG. +*/ + +#define MAX_SIZE ((MAX_SCATTER-1) * NBPG) + +/* +** Enable some processor/os dependent functions. +*/ + +#define DIRTY 1 + +/* +** Write disk status information to dkstat ? +*/ + +#define DK 1 + +/*========================================================== +** +** Include files +** +**========================================================== +*/ + +#ifdef KERNEL +#include <types.h> +#include <param.h> +#include <time.h> +#include <systm.h> +#include <malloc.h> +#include <buf.h> +#include <kernel.h> +#include <vm/vm.h> + +#ifdef DK +#include <dkstat.h> +#endif /* DK */ + +#else /* KERNEL */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#endif /* KERNEL */ + +#include <i386/pci/ncr_reg.h> +#include <i386/pci/pci.h> +#include <i386/pci/pci_device.h> + +#ifdef __NetBSD__ +#include <sys/device.h> +#include <i386/isa/isavar.h> +#endif + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + + +/*========================================================== +** +** Debugging tags +** +**========================================================== +*/ + +#ifdef SCSI_NCR_DEBUG + +#define DEBUG_ALLOC (0x0001) +#define DEBUG_PHASE (0x0002) +#define DEBUG_POLL (0x0004) +#define DEBUG_QUEUE (0x0008) +#define DEBUG_RESULT (0x0010) +#define DEBUG_SCATTER (0x0020) +#define DEBUG_SCRIPT (0x0040) +#define DEBUG_TINY (0x0080) +#define DEBUG_TIMING (0x0100) +#define DEBUG_SDTR (0x0200) +#define DEBUG_TAGS (0x0400) +#define DEBUG_FREEZE (0x0800) +#define DEBUG_NODUMP (0x1000) + +int ncr_debug = SCSI_NCR_DEBUG; + +#else /* SCSI_NCR_DEBUG */ +int ncr_debug = 0; +#endif /* SCSI_NCR_DEBUG */ + + +/*========================================================== +** +** assert () +** +**========================================================== +** +** modified copy from 386bsd:/usr/include/sys/assert.h +** +**---------------------------------------------------------- +*/ + +#define assert(expression) { \ + if (!(expression)) { \ + (void)printf(\ + "assertion \"%s\" failed: file \"%s\", line %d\n", \ + #expression, \ + __FILE__, __LINE__); \ + } \ +} + +/*========================================================== +** +** Access to the controller chip. +** +**========================================================== +*/ + +#define INB(r) (np->reg->r) +#define INW(r) (np->reg->r) +#define INL(r) (np->reg->r) + +#define OUTB(r, val) np->reg->r = val +#define OUTW(r, val) np->reg->r = val +#define OUTL(r, val) np->reg->r = val + +/*========================================================== +** +** Command control block states. +** +**========================================================== +*/ + +#define HS_IDLE (0) +#define HS_BUSY (1) +#define HS_NEGOTIATE (2) /* sync. data transfer */ +#define HS_DISCONNECT (3) /* Disconnected by target */ + +#define HS_COMPLETE (4) +#define HS_SEL_TIMEOUT (5) /* Selection timeout */ +#define HS_RESET (6) /* SCSI reset */ +#define HS_ABORTED (7) /* Transfer aborted */ +#define HS_TIMEOUT (8) /* Software timeout */ +#define HS_FAIL (9) /* SCSI or PCI bus errors */ +#define HS_UNEXPECTED (10) /* Unexpected disconnect */ + +/*========================================================== +** +** Misc. +** +**========================================================== +*/ + +#define ILLEGAL_ADDR (0xefffffff) +#define CCB_MAGIC (0xf2691ad2) + +/*========================================================== +** +** Capability bits in Inquire response byte #7. +** +**========================================================== +*/ + +#define INQ7_SYNC (0x10) +#define INQ7_QUEUE (0x02) + +/*========================================================== +** +** OS dependencies. +** +**========================================================== +*/ + +#ifndef __FreeBSD__ +#ifndef __NetBSD__ + #define ANCIENT +#endif /*__NetBSD__*/ +#endif /*__FreeBSD__*/ + +#ifdef ANCIENT + #define LUN lu + #define TARGET targ + #define INT32 int + #define U_INT32 long + #define TIMEOUT +#else /* ANCIENT */ + #define LUN sc_link->lun + #define TARGET sc_link->target +#ifdef __NetBSD__ + #define INT32 int + #define U_INT32 u_int + #define TIMEOUT (void*) +#else /*__NetBSD__*/ + #define INT32 int32 + #define U_INT32 u_int32 + #define TIMEOUT (timeout_func_t) +#endif /*__NetBSD__*/ +#endif /* ANCIENT */ + +/*========================================================== +** +** Declaration of structs. +** +**========================================================== +*/ + +struct tcb; +struct lcb; +struct ccb; +struct ncb; +struct script; + +typedef struct ncb * ncb_p; +typedef struct tcb * tcb_p; +typedef struct lcb * lcb_p; +typedef struct ccb * ccb_p; + +struct link { + u_long l_cmd; + u_long l_paddr; +}; + +struct usrcmd { + u_long target; + u_long lun; + u_long data; + u_long cmd; +}; + +#define UC_SETSYNC 10 +#define UC_SETTAGS 11 +#define UC_SETDEBUG 12 +#define UC_SETORDER 13 + +/*========================================================== +** +** Access to fields of structs. +** +**========================================================== +*/ + +#define offsetof(type, member) ((size_t)(&((type *)0)->member)) + +/*--------------------------------------- +** +** Timestamps for profiling +** +**--------------------------------------- +*/ + +struct tstamp { + struct timeval start; + struct timeval end; + struct timeval select; + struct timeval command; + struct timeval data; + struct timeval status; + struct timeval disconnect; + struct timeval reselect; +}; + +/* +** profiling data (per device) +*/ + +struct profile { + u_long num_trans; + u_long num_bytes; + u_long num_disc; + u_long num_break; + u_long num_int; + u_long num_fly; + u_long ms_setup; + u_long ms_data; + u_long ms_disc; + u_long ms_post; +}; + +/*========================================================== +** +** Declaration of structs: TARGET control block +** +**========================================================== +*/ + +struct tcb { + /* + ** during reselection the ncr jumps to this point + ** with SFBR set to the encoded TARGET number + ** with bit 7 set. + ** if it's not this target, jump to the next. + ** + ** JUMP IF (SFBR != #TARGET#) + ** @(next tcb) + */ + + struct link jump_tcb; + + /* + ** load the actual synchronous mode + ** for this target to the sxfer register + ** + ** SCR_COPY (1); + ** @(sval field of this tcb) + ** @(sxfer register) + */ + + ncrcmd getscr[3]; + + /* + ** if next message is "identify" + ** then load the message to SFBR, + ** else load 0 to SFBR. + ** + ** CALL + ** <RESEL_LUN> + */ + + struct link call_lun; + + /* + ** now look for the right lun. + ** + ** JUMP + ** @(first ccb of this lun) + */ + + struct link jump_lcb; + + /* + ** pointer to interrupted getcc ccb + */ + + ccb_p hold_cp; + + /* + ** statistical data + */ + + u_long transfers; + u_long bytes; + + /* + ** user settable limits for sync transfer + ** and tagged commands. + */ + + u_char usrsync; + u_char usrtags; + + /* + ** negotiation of synch transfer and tagged commands + */ + + u_short period; + u_char _1; + u_char sval; + u_char minsync; + u_char maxoffs; + + /* + ** inquire data + */ +#define MAX_INQUIRE 36 + u_char inqdata[MAX_INQUIRE]; + + /* + ** the lcb's of this tcb + */ + + lcb_p lp[MAX_LUN]; +}; + +/*========================================================== +** +** Declaration of structs: LUN control block +** +**========================================================== +*/ + +struct lcb { + /* + ** during reselection the ncr jumps to this point + ** with SFBR set to the "Identify" message. + ** if it's not this lun, jump to the next. + ** + ** JUMP IF (SFBR == #LUN#) + ** @(next lcb of this target) + */ + + struct link jump_lcb; + + /* + ** if next message is "simple tag", + ** then load the tag to SFBR, + ** else load 0 to SFBR. + ** + ** CALL + ** <RESEL_TAG> + */ + + struct link call_tag; + + /* + ** now look for the right ccb. + ** + ** JUMP + ** @(first ccb of this lun) + */ + + struct link jump_ccb; + + /* + ** start of the ccb chain + */ + + ccb_p next_ccb; + + /* + ** Control of tagged queueing + */ + + u_char reqccbs; + u_char actccbs; + u_char reqlink; + u_char actlink; + u_char usetags; + u_char lasttag; +}; + +/*========================================================== +** +** Declaration of structs: COMMAND control block +** +**========================================================== +** +** This substructure is copied from the ccb to a +** global address after selection (or reselection) +** and copied back before disconnect. +** +** These fields are accessible to the script processor. +** +**---------------------------------------------------------- +*/ + +struct head { + /* + ** Execution of a ccb starts at this point. + ** It's a jump to the "SELECT" label + ** of the script. + ** + ** After successful selection the script + ** processor overwrites it with a jump to + ** the IDLE label of the script. + */ + + struct link launch; + + /* + ** Saved data pointer. + ** Points to the position in the script + ** responsible for the actual transfer + ** of data. + ** It's written after reception of a + ** "SAVE_DATA_POINTER" message. + */ + + u_long savep; + + /* + ** The virtual address of the ccb + ** containing this header. + */ + + ccb_p cp; + + /* + ** space for some timestamps to gather + ** profiling data about devices and this driver. + */ + + struct tstamp stamp; + + /* + ** status fields. + */ + + u_char status[8]; + +#define host_status phys.header.status[0] +#define scsi_status phys.header.status[1] +#define scs2_status phys.header.status[2] +#define sync_status phys.header.status[3] +#define parity_errs phys.header.status[4] +}; + +/*========================================================== +** +** Declaration of structs: Data structure block +** +**========================================================== +** +** During execution of a ccb by the script processor, +** the DSA (data structure address) register points +** to this substructure of the ccb. +** This substructure contains the header with +** the script-processor-changable data and +** data blocks for the indirect move commands. +** +**---------------------------------------------------------- +*/ + +struct dsb { + + /* + ** Header. + ** Has to be the first entry, + ** because it's jumped to by the + ** script processor + */ + + struct head header; + + /* + ** Table data for Script + */ + + struct scr_tblsel select; + struct scr_tblmove smsg ; + struct scr_tblmove smsg2 ; + struct scr_tblmove cmd ; + struct scr_tblmove sense ; + struct scr_tblmove data [MAX_SCATTER]; +}; + +/*========================================================== +** +** Declaration of structs: Command control block. +** +**========================================================== +** +** During execution of a ccb by the script processor, +** the DSA (data structure address) register points +** to this substructure of the ccb. +** This substructure contains the header with +** the script-processor-changable data and then +** data blocks for the indirect move commands. +** +**---------------------------------------------------------- +*/ + + +struct ccb { + /* + ** during reselection the ncr jumps to this point. + ** If a "SIMPLE_TAG" message was received, + ** then SFBR is set to the tag. + ** else SFBR is set to 0 + ** If looking for another tag, jump to the next ccb. + ** + ** JUMP IF (SFBR != #TAG#) + ** @(next ccb of this lun) + */ + + struct link jump_ccb; + + /* + ** After execution of this call, the return address + ** (in the TEMP register) points to the following + ** data structure block. + ** So copy it to the DSA register, and start + ** processing of this data structure. + ** + ** CALL + ** <RESEL_TMP> + */ + + struct link call_tmp; + + /* + ** This is the data structure which is + ** to be executed by the script processor. + */ + + struct dsb phys; + + /* + ** If a data transfer phase is terminated too early + ** (after reception of a message (i.e. DISCONNECT)), + ** we have to prepare a mini script to transfer + ** the rest of the data. + */ + + u_long patch[8]; + + /* + ** The general SCSI driver provides a + ** pointer to a control block. + */ + + struct scsi_xfer *xfer; + +#ifdef ANCIENT + /* + ** We copy the SCSI command, because it + ** may be volatile (on the stack). + ** + */ + struct scsi_generic cmd; +#endif /* ANCIENT */ + + /* + ** We prepare a message to be sent after selection, + ** and a second one to be sent after getcc selection. + ** Contents are IDENTIFY and SIMPLE_TAG. + ** And sdtr .. (if negotiating sync transfers) + */ + + u_char scsi_smsg [8]; + u_char scsi_smsg2[8]; + + /* + ** Lock this ccb. + ** Flag is used while looking for a free ccb. + */ + + u_long magic; + u_long tlimit; + + /* + ** All ccbs of one hostadapter are linked. + */ + + ccb_p link_ccb; + + /* + ** All ccbs of one target/lun are linked. + */ + + ccb_p next_ccb; + + /* + ** Tag for this transfer. + ** It's patched into jump_ccb. + ** If it's not zero, a SIMPLE_TAG + ** message is included in smsg. + */ + + u_char tag; +}; + +/*========================================================== +** +** Declaration of structs: NCR device descriptor +** +**========================================================== +*/ + +struct ncb { + /*----------------------------------------------- + ** Scripts .. + **----------------------------------------------- + ** + ** During reselection the ncr jumps to this point. + ** The SFBR register is loaded with the encoded target id. + ** + ** Jump to the first target. + ** + ** JUMP + ** @(next tcb) + */ + struct link jump_tcb; + + /*----------------------------------------------- + ** Configuration .. + **----------------------------------------------- + ** + ** virtual and physical addresses + ** of the 53c810 chip. + */ + vm_offset_t vaddr; + vm_offset_t paddr; + + /* + ** pointer to the chip's registers. + */ + volatile + struct ncr_reg* reg; + + /* + ** virtual and physical addresses + ** of the script. + */ + struct script* v_script; + u_long p_script; + + /* + ** The SCSI address of the host adapter. + */ + u_char myaddr; + + /* + ** timing parameters + */ + u_char ns_async; + u_char ns_sync; + u_char rv_scntl3; + +#ifndef ANCIENT + /*----------------------------------------------- + ** Link to the generic SCSI driver + **----------------------------------------------- + */ + + struct scsi_link sc_link; +#endif /* ANCIENT */ + + /*----------------------------------------------- + ** Job control + **----------------------------------------------- + ** + ** Commands from user + */ + struct usrcmd user; + u_char order; + + /* + ** Target data + */ + struct tcb target[MAX_TARGET]; + + /* + ** Start queue. + */ + u_long squeue [MAX_START]; + u_short squeueput; + u_short actccbs; + + /* + ** Timeout handler + */ + u_long heartbeat; + u_short ticks; + u_short latetime; + u_long lasttime; + u_short imask; + u_short mcount; + + /*----------------------------------------------- + ** Debug and profiling + **----------------------------------------------- + ** + ** register dump + */ + struct ncr_reg regdump; + struct timeval regtime; + + /* + ** Profiling data + */ + struct profile profile; + u_long disc_phys; + u_long disc_ref; + + /*----------------------------------------------- + ** Working areas + **----------------------------------------------- + ** + ** The global header. + ** Accessible to both the host and the + ** script-processor. + */ + + struct head header; + + /* + ** The global control block. + ** It's used only during the configuration phase. + ** A target control block will be created + ** after the first successful transfer. + */ + + struct ccb ccb; + + /* + ** message buffers. + ** Should be longword aligned, + ** because they're written with a + ** COPY script command. + */ + + u_char msgout[8]; + u_char msgin [8]; + + /* + ** Buffer for STATUS_IN phase. + */ + + u_char scratch; + u_char lock; /* @DEBUG@ */ + +#ifdef __NetBSD__ + /*----------------------------------------------- + ** Interrupt handler. + **----------------------------------------------- + ** + ** Only for NetBSD + */ + + struct intrhand ihand; +#endif + +}; + +/*========================================================== +** +** +** Script for NCR-Processor. +** +** Use ncr_script_fill() to create the variable parts. +** Use ncr_script_bind() to bind to physical addresses. +** +** +**========================================================== +** +** We have to know the offsets of all labels before +** we reach them (for forward jumps). +** Therefore we declare a struct here. +** If you make changes inside the script, +** DONT FORGET TO CHANGE THE LENGTHS HERE! +** +**---------------------------------------------------------- +*/ + +struct script { + ncrcmd start [ 2]; + ncrcmd start1 [ 10]; + ncrcmd startpos [ 1]; + ncrcmd tryloop [MAX_START*5+2]; + ncrcmd trysel [ 8]; + ncrcmd skip [ 8]; + ncrcmd skip2 [ 3]; + ncrcmd idle [ 2]; + ncrcmd select [ 22]; + ncrcmd prepare [ 4]; + ncrcmd loadpos [ 24]; + ncrcmd prepare2 [ 20]; + ncrcmd setmsg [ 5]; + ncrcmd clrack [ 6]; + ncrcmd dispatch [ 22]; + ncrcmd no_data [ 19]; + ncrcmd checkatn [ 16]; + ncrcmd command [ 15]; + ncrcmd status [ 25]; + ncrcmd msg_in [ 22]; + ncrcmd msg_bad [ 6]; + ncrcmd msg_parity [ 12]; + ncrcmd msg_reject [ 6]; + ncrcmd msg_extended [ 34]; + ncrcmd msg_sdtr [ 39]; + ncrcmd complete [ 6]; + ncrcmd cleanup [ 12]; + ncrcmd savepos [ 11]; + ncrcmd signal [ 10]; + ncrcmd save_dp [ 5]; + ncrcmd restore_dp [ 5]; + ncrcmd disconnect [ 21]; + ncrcmd msg_out [ 9]; + ncrcmd msg_out_done [ 7]; + ncrcmd msg_out_abort [ 10]; + ncrcmd getcc [ 4]; + ncrcmd getcc1 [ 5]; + ncrcmd getcc2 [ 33]; + ncrcmd badgetcc [ 2]; + ncrcmd reselect [ 12]; + ncrcmd reselect2 [ 6]; + ncrcmd resel_tmp [ 5]; + ncrcmd resel_lun [ 18]; + ncrcmd resel_tag [ 24]; + ncrcmd data_in [MAX_SCATTER * 4 + 7]; + ncrcmd data_out [MAX_SCATTER * 4 + 7]; + ncrcmd aborttag [ 4]; + ncrcmd abort [ 20]; +}; + +/*========================================================== +** +** +** Function Headers. +** +** +**========================================================== +*/ + +#ifdef KERNEL +#ifdef ANCIENT +extern int splbio(void); +extern void splx(int level); +extern int wakeup(void* channel); +extern int tsleep(); +extern int DELAY(); +extern int scsi_attachdevs(); +extern void timeout(); +extern void untimeout(); +#endif /* ANCIENT */ + +static void ncr_alloc_ccb (ncb_p np, struct scsi_xfer * xp); +static int ncr_attach (pcici_t config_id); +static void ncr_complete (ncb_p np, ccb_p cp); +static int ncr_delta (struct timeval * from, struct timeval * to); +static void ncr_exception (ncb_p np); +static void ncr_free_ccb (ncb_p np, ccb_p cp, int flags); +static void ncr_getclock (ncb_p np); +static ccb_p ncr_get_ccb (ncb_p np, u_long flags, u_long t,u_long l); +static U_INT32 ncr_info (int unit); +static void ncr_init (ncb_p np, char * msg, u_long code); +static void ncr_int_ma (ncb_p np); +static void ncr_int_sir (ncb_p np); +static void ncr_int_sto (ncb_p np); +static int ncr_intr (int dev); +static void ncr_min_phys (struct buf *bp); +static void ncr_opennings (ncb_p np, lcb_p lp, struct scsi_xfer * xp); +static int ncb_probe (pcici_t config_id); +static void ncb_profile (ncb_p np, ccb_p cp); +static void ncr_script_bind (ncb_p np); +static void ncr_script_copy (ncb_p oldnp, ncb_p np); +static void ncr_script_fill (struct script * scr); +static u_long ncr_scatter (struct dsb* phys,u_long vaddr,u_long datalen); +static void ncr_setmaxtags (tcb_p tp, u_long usrtags); +static void ncr_setsync (ncb_p np, ccb_p xxxcp, u_char sxfer); +static void ncr_settags (tcb_p tp, lcb_p lp); +static INT32 ncr_start (struct scsi_xfer *xp); +static void ncr_timeout (ncb_p np); +static void ncr_usercmd (ncb_p np); +static void ncr_wakeup (ncb_p np, u_long code); + +/*========================================================== +** +** +** Access to processor ports. +** +** +**========================================================== +*/ + +#ifdef DIRTY +#include <i386/isa/isa.h> +#ifdef ANCIENT +/* +** Doch das ist alles nur geklaut .. +** aus: 386bsd:/sys/i386/include/pio.h +** +** Mach Operating System +** Copyright (c) 1990 Carnegie-Mellon University +** All rights reserved. The CMU software License Agreement specifies +** the terms and conditions for use and redistribution. +*/ + +#undef inb +#define inb(port) \ +({ unsigned char data; \ + __asm __volatile("inb %1, %0": "=a" (data): "d" ((u_short)(port))); \ + data; }) + +#undef outb +#define outb(port, data) \ +{__asm __volatile("outb %0, %1"::"a" ((u_char)(data)), "d" ((u_short)(port)));} + +#define disable_intr() \ +{__asm __volatile("cli");} + +#define enable_intr() \ +{__asm __volatile("sti");} +#endif /* ANCIENT */ + +/*------------------------------------------------------------------ +** +** getirr: get a bit vector of the pending interrupts. +** +** NOTE: this is HIGHLY hardware dependant :-( +** +**------------------------------------------------------------------ +*/ + + +static u_long getirr (void) +{ + u_long mask; + + disable_intr(); + + outb (IO_ICU2, 0x0a); + mask = inb (IO_ICU2); + outb (IO_ICU2, 0x0b); + + mask <<= 8; + + outb (IO_ICU1, 0x0a); + mask|= inb (IO_ICU1); + outb (IO_ICU1, 0x0b); + + enable_intr(); + + return (mask); +} + +#else /* DIRTY */ + #define getirr() (0) +#endif /* DIRTY */ +#endif /* KERNEL */ + +/*========================================================== +** +** +** Global static data. +** +** +**========================================================== +*/ + + +static char ident[] = + "\n$Id: ncr.c,v 2.0.0.3 94/07/24 09:02:42 wolf Exp $\n" + "Copyright (c) 1994, Wolfgang Stanglmeier\n"; + + +int ncr_version = NCR_VERSION + + sizeof (struct ncb) * sizeof (struct ccb) + * sizeof (struct lcb) * sizeof (struct tcb); + +#ifdef KERNEL + +u_long ncr_units; +u_long nncr=NNCR; +struct ncb ncr0; +ncb_p (ncrp [NNCR]) = {&ncr0}; +u_long ncr_msgout; /* @DEBUG@ */ + +/* +** SCSI cmd to get the SCSI sense data +*/ + +static u_char rs_cmd [6] = + { 0x03, 0, 0, 0, sizeof (struct scsi_sense_data), 0 }; + +/*========================================================== +** +** +** Global static data: auto configure +** +** +**========================================================== +*/ + +struct pci_driver ncrdevice = { + ncb_probe, + ncr_attach, + 0x00011000ul, + "ncr", + "ncr 53c810 scsi", + ncr_intr +}; + + +#ifndef ANCIENT +struct scsi_adapter ncr_switch = +{ + ncr_start, + ncr_min_phys, + 0, + 0, + ncr_info, + "ncr", +}; + +struct scsi_device ncr_dev = +{ + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ + "ncr", +}; +#else /* ANCIENT */ +struct scsi_switch ncr_switch = +{ + ncr_start, + ncr_min_phys, + 0, + 0, + ncr_info, + 0,0,0 +}; +#endif /* ANCIENT */ + +/*========================================================== +** +** +** Scripts for NCR-Processor. +** +** Use ncr_script_bind for binding to physical addresses. +** +** +**========================================================== +** +** +** +** PADDR generates a reference to another part of the script. +** REG generates a reference to a script processor register. +** +** +**---------------------------------------------------------- +*/ + +#define PADDR(label) ((ncrcmd) &script0.label) + +static struct script script0 = { +/*--------------------------< START >-----------------------*/ { + /* + ** Hook for interrupted GetConditionCode. + ** Will be patched to ... IFTRUE by + ** the interrupt handler. + */ + SCR_INT ^ IFFALSE (0), + 1, +}/*-------------------------< START1 >----------------------*/,{ + /* + ** Hook for stalled start queue. + ** Will be patched to IFTRUE by the interrupt handler. + */ + SCR_INT ^ IFFALSE (0), + 7, + /* + ** Claim to be still alive ... + */ + SCR_COPY (sizeof (ncr0.heartbeat)), + (ncrcmd) &time.tv_sec, + (ncrcmd) &ncr0.heartbeat, + /* + ** Make data structure address invalid. + ** clear SIGP. + */ + SCR_LOAD_REG (dsa, 0xff), + 0, + SCR_FROM_REG (ctest2), + 0, + /* + ** Then jump to a certain point in tryloop. + ** Due to the lack of indirect addressing the code + ** is self modifying here. + */ + SCR_JUMP, +}/*-------------------------< STARTPOS >--------------------*/,{ + PADDR(tryloop), + +}/*-------------------------< TRYLOOP >---------------------*/,{ +/* +** Load an entry of the start queue into dsa +** and try to start it by jumping to TRYSEL. +** +** Because the size depends on the +** #define MAX_START parameter, it is filled +** in at runtime. +** +**----------------------------------------------------------- +** +** ##===========< I=0; i<MAX_START >=========== +** || SCR_COPY (4), +** || (ncrcmd) &ncr0.squeue[i], +** || REG (dsa), +** || SCR_CALL, +** || PADDR (trysel), +** ##========================================== +** +** SCR_JUMP, +** PADDR(tryloop), +** +**----------------------------------------------------------- +*/ +0 + +}/*-------------------------< TRYSEL >----------------------*/,{ + /* + ** Now: + ** DSA: Address of a Data Structure + ** or Address of the IDLE-Label. + ** + ** TEMP: Address of a script, which tries to + ** start the NEXT entry. + ** + ** Save the TEMP register into the SCRATCHA register. + ** Then copy the DSA to TEMP and RETURN. + ** This is kind of an indirect jump. + ** (The script processor has NO stack, so the + ** CALL is actually a jump and link, and the + ** RETURN is an indirect jump.) + ** + ** If the slot was empty, DSA contains the address + ** of the IDLE part of this script. The processor + ** jumps to IDLE and waits for a reselect. + ** It will wake up and try the same slot again + ** after the SIGP bit becomes set by the host. + ** + ** If the slot was not empty, DSA contains + ** the address of the phys-part of a ccb. + ** The processor jumps to this address. + ** phys starts with head, + ** head starts with launch, + ** so actually the processor jumps to + ** the lauch part. + ** If the entry is scheduled to be executed, + ** then launch contains a jump to SELECT. + ** If it's not scheduled, it contains a jump to IDLE. + */ + SCR_COPY (4), + REG (temp), + REG (scratcha), + SCR_COPY (4), + REG (dsa), + REG (temp), + SCR_RETURN, + 0 + +}/*-------------------------< SKIP >------------------------*/,{ + /* + ** This entry has been canceled. + ** Next time use the next slot. + */ + SCR_COPY (4), + REG (scratcha), + PADDR (startpos), + /* + ** patch the launch field. + ** should look like an idle process. + */ + SCR_COPY (4), + REG (dsa), + PADDR (skip2), + SCR_COPY (8), + PADDR (idle), +}/*-------------------------< SKIP2 >-----------------------*/,{ + 0, + SCR_JUMP, + PADDR(start), +}/*-------------------------< IDLE >------------------------*/,{ + /* + ** Nothing to do? + ** Wait for reselect. + */ + SCR_JUMP, + PADDR(reselect), + +}/*-------------------------< SELECT >----------------------*/,{ + /* + ** DSA contains the address of a scheduled + ** data structure. + ** + ** SCRATCHA contains the address of the script, + ** which starts the next entry. + ** + ** Set Initiator mode. + ** + ** (Target mode is left as an exercise for the student) + */ + + SCR_CLR (SCR_TRG), + 0, + SCR_LOAD_REG (scr0, 0xff), + 0, + + /* + ** And try to select this target. + */ + SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select), + PADDR (reselect), + + /* + ** Now there are 4 possibilities: + ** + ** (1) The ncr looses arbitration. + ** This is ok, because it will try again, + ** when the bus becomes idle. + ** (But beware of the timeout function!) + ** + ** (2) The ncr is reselected. + ** Then the script processor takes the jump + ** to the RESELECT label. + ** + ** (3) The ncr completes the selection. + ** Then it will execute the next statement. + ** + ** (4) There is a selection timeout. + ** Then the ncr should interrupt the host and stop. + ** Unfortunately, it seems to continue execution + ** of the script. But it will fail with an + ** IID-interrupt on the next WHEN. + */ + + SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)), + 0, + + /* + ** Save target id to ctest0 register + */ + + SCR_FROM_REG (sdid), + 0, + SCR_TO_REG (ctest0), + 0, + /* + ** Send the IDENTIFY and SIMPLE_TAG messages + ** (and the M_X_SDTR message) + */ + SCR_MOVE_TBL ^ SCR_MSG_OUT, + offsetof (struct dsb, smsg), + SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_OUT)), + -16, + SCR_COPY (1), + REG (sfbr), + (ncrcmd) &ncr_msgout, + /* + ** Selection complete. + ** Next time use the next slot. + */ + SCR_COPY (4), + REG (scratcha), + PADDR (startpos), +}/*-------------------------< PREPARE >----------------------*/,{ + /* + ** The ncr doesn't have an indirect load + ** or store command. So we have to + ** copy part of the control block to a + ** fixed place, where we can access it. + ** + ** We patch the address part of a + ** COPY command with the DSA-register. + */ + SCR_COPY (4), + REG (dsa), + PADDR (loadpos), + /* + ** then we do the actual copy. + */ + SCR_COPY (sizeof (struct head)), + /* + ** continued after the next label ... + */ + +}/*-------------------------< LOADPOS >---------------------*/,{ + 0, + (ncrcmd) &ncr0.header, + /* + ** Mark this ccb as not scheduled. + */ + SCR_COPY (8), + PADDR (idle), + (ncrcmd) &ncr0.header.launch, + /* + ** Set a time stamp for this selection + */ + SCR_COPY (sizeof (struct timeval)), + (ncrcmd) &time, + (ncrcmd) &ncr0.header.stamp.select, + /* + ** load the savep (saved pointer) into + ** the TEMP register (actual pointer) + */ + SCR_COPY (4), + (ncrcmd) &ncr0.header.savep, + REG (temp), + /* + ** Initialize the status registers + */ + SCR_COPY (4), + (ncrcmd) &ncr0.header.status, + REG (scr0), + /* + ** Set carry according to host_status + */ + SCR_CLR (SCR_CARRY), + 0, + SCR_FROM_REG (scr0), + 0, + SCR_JUMPR ^ IFFALSE (DATA (HS_NEGOTIATE)), + 16, + SCR_LOAD_REG (scr0, HS_BUSY), + 0, + SCR_SET (SCR_CARRY), + 0, + +}/*-------------------------< PREPARE2 >---------------------*/,{ + /* + ** <Carry set iff SDTM message sent> + ** + ** Load the synchronous mode register + */ + SCR_FROM_REG (scr3), + 0, + SCR_TO_REG (sxfer), + 0, + /* + ** Initialize the msgout buffer with a NOOP message. + */ + SCR_LOAD_REG (scratcha, M_NOOP), + 0, + SCR_COPY (1), + REG (scratcha), + (ncrcmd) &ncr0.msgout, + SCR_COPY (1), + REG (scratcha), + (ncrcmd) &ncr0.msgin, + /* + ** If M_X_SDTR sent, but no MSG_IN phase, ... + */ + SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)), + PADDR (msg_in), + SCR_JUMP ^ IFFALSE (CARRYSET), + PADDR (dispatch), + /* + ** no answer is an answer, too. + */ + SCR_INT, + 3, + SCR_JUMP, + PADDR (dispatch), + +}/*-------------------------< SETMSG >----------------------*/,{ + SCR_COPY (1), + REG (scratcha), + (ncrcmd) &ncr0.msgout, + SCR_SET (SCR_ATN), + 0, +}/*-------------------------< CLRACK >----------------------*/,{ + /* + ** Terminate sdtr mode. + ** Terminate possible pending message phase. + */ + SCR_FROM_REG (scr0), + 0, + SCR_LOAD_REG (scr0, HS_BUSY), + 0, + SCR_CLR (SCR_ACK | SCR_CARRY), + 0, + +}/*-----------------------< DISPATCH >----------------------*/,{ + SCR_RETURN ^ IFTRUE (WHEN (SCR_DATA_OUT)), + 0, + SCR_RETURN ^ IFTRUE (IF (SCR_DATA_IN)), + 0, + SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)), + PADDR (msg_out), + SCR_JUMP ^ IFTRUE (IF (SCR_MSG_IN)), + PADDR (msg_in), + SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)), + PADDR (command), + SCR_JUMP ^ IFTRUE (IF (SCR_STATUS)), + PADDR (status), + /* + ** Discard one illegal phase byte, if required. + */ + SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_OUT)), + 8, + SCR_MOVE_ABS (1) ^ SCR_ILG_OUT, + (ncrcmd) &ncr0.scratch, + SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_IN)), + 8, + SCR_MOVE_ABS (1) ^ SCR_ILG_IN, + (ncrcmd) &ncr0.scratch, + SCR_JUMP, + PADDR (dispatch), +}/*-------------------------< NO_DATA >--------------------*/,{ + /* + ** The target wants to tranfer too much data + ** or in the wrong direction. + ** Prepare an abort message and set ATN. + */ + SCR_LOAD_REG (scratcha, M_ABORT), + 0, + SCR_COPY (1), + REG (scratcha), + (ncrcmd) &ncr0.msgout, + SCR_SET (SCR_ATN), + 0, + /* + ** Discard one data byte, if required. + */ + SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_OUT)), + 8, + SCR_MOVE_ABS (1) ^ SCR_DATA_OUT, + (ncrcmd) &ncr0.scratch, + SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)), + 8, + SCR_MOVE_ABS (1) ^ SCR_DATA_IN, + (ncrcmd) &ncr0.scratch, + /* + ** .. and repeat as required. + */ + SCR_CALL, + PADDR (dispatch), + SCR_JUMP, + PADDR (no_data), + +}/*-------------------------< CHECKATN >--------------------*/,{ + /* + ** If AAP (bit 1 of scntl0 register) is set + ** and a parity error is detected, + ** the script processor asserts ATN. + ** + ** The target should switch to a MSG_OUT phase + ** to get the message. + */ + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFFALSE (MASK (CATN, CATN)), + PADDR (dispatch), + /* + ** count it + */ + SCR_COPY (1), + (ncrcmd) &ncr0.header.status[4], + REG (scratcha), + SCR_REG_REG (scratcha, SCR_ADD, 0x01), + 0, + SCR_COPY (1), + REG (scratcha), + (ncrcmd) &ncr0.header.status[4], + /* + ** Prepare a M_ID_ERROR message + ** (initiator detected error). + ** The target should retry the transfer. + */ + SCR_LOAD_REG (scratcha, M_ID_ERROR), + 0, + SCR_JUMP, + PADDR (setmsg), + +}/*-------------------------< COMMAND >--------------------*/,{ + /* + ** If this is not a GETCC transfer ... + */ + SCR_FROM_REG (scr1), + 0, +/*<<<*/ SCR_JUMPR ^ IFTRUE (DATA (S_CHECK_COND)), + 28, + /* + ** ... set a timestamp ... + */ + SCR_COPY (sizeof (struct timeval)), + (ncrcmd) &time, + (ncrcmd) &ncr0.header.stamp.command, + /* + ** ... and send the command + */ + SCR_MOVE_TBL ^ SCR_COMMAND, + offsetof (struct dsb, cmd), + SCR_JUMP, + PADDR (dispatch), + /* + ** Send the GETCC command + */ +/*>>>*/ SCR_MOVE_ABS (6) ^ SCR_COMMAND, + (ncrcmd) &rs_cmd, + SCR_JUMP, + PADDR (dispatch), + +}/*-------------------------< STATUS >--------------------*/,{ + /* + ** set the timestamp. + */ + SCR_COPY (sizeof (struct timeval)), + (ncrcmd) &time, + (ncrcmd) &ncr0.header.stamp.status, + /* + ** If this is a GETCC transfer, + */ + SCR_FROM_REG (scr1), + 0, +/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (S_CHECK_COND)), + 32, + /* + ** get the status + */ + SCR_MOVE_ABS (1) ^ SCR_STATUS, + (ncrcmd) &ncr0.scratch, + /* + ** Save status to scs2_status. + ** Mark as complete. + ** And wait for disconnect. + */ + SCR_TO_REG (scr2), + 0, + SCR_LOAD_REG (scr0, HS_COMPLETE), + 0, + SCR_JUMP, + PADDR (checkatn), + /* + ** If it was no GETCC transfer, + ** save the status to scsi_status. + */ +/*>>>*/ SCR_MOVE_ABS (1) ^ SCR_STATUS, + (ncrcmd) &ncr0.scratch, + SCR_TO_REG (scr1), + 0, + /* + ** if it was no check condition ... + */ + SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)), + PADDR (checkatn), + /* + ** ... mark as complete. + */ + SCR_LOAD_REG (scr0, HS_COMPLETE), + 0, + SCR_JUMP, + PADDR (checkatn), + +}/*-------------------------< MSG_IN >--------------------*/,{ + /* + ** Get the first byte of the message + ** and save it to SCRATCHA. + ** + ** The script processor doesn't negate the + ** ACK signal after this transfer. + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + (ncrcmd) &ncr0.msgin[0], + /* + ** Check for message parity error. + */ + SCR_TO_REG (scratcha), + 0, + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDR (msg_parity), + SCR_FROM_REG (scratcha), + 0, + /* + ** Parity was ok, handle this message. + */ + SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE)), + PADDR (complete), + SCR_JUMP ^ IFTRUE (DATA (M_SAVE_DP)), + PADDR (save_dp), + SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP)), + PADDR (restore_dp), + SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)), + PADDR (disconnect), + SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)), + PADDR (msg_extended), + SCR_JUMP ^ IFTRUE (DATA (M_REJECT)), + PADDR (msg_reject), + /* + ** Rest of the messages left as + ** an exercise ... + ** + ** Unimplemented messages: + ** fall through to MSG_BAD. + */ +}/*-------------------------< MSG_BAD >------------------*/,{ + /* + ** unimplemented message - reject it. + */ + SCR_INT, + 6, + SCR_LOAD_REG (scratcha, M_REJECT), + 0, + SCR_JUMP, + PADDR (setmsg), + +}/*-------------------------< MSG_PARITY >---------------*/,{ + /* + ** count it + */ + SCR_COPY (1), + (ncrcmd) &ncr0.header.status[4], + REG (scratcha), + SCR_REG_REG (scratcha, SCR_ADD, 0x01), + 0, + SCR_COPY (1), + REG (scratcha), + (ncrcmd) &ncr0.header.status[4], + /* + ** send a "message parity error" message. + */ + SCR_LOAD_REG (scratcha, M_PARITY), + 0, + SCR_JUMP, + PADDR (setmsg), +}/*-------------------------< MSG_REJECT >---------------*/,{ + /* + ** If a M_X_SDTR message was sent, + ** negotiate synchronous mode. + */ + SCR_INT ^ IFTRUE (CARRYSET), + 3, + /* + ** make host log this message + */ + SCR_INT ^ IFFALSE (CARRYSET), + 5, + SCR_JUMP, + PADDR (clrack), + +}/*-------------------------< MSG_EXTENDED >-------------*/,{ + /* + ** Terminate cycle + */ + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get length. + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + (ncrcmd) &ncr0.msgin[1], + /* + ** Check for message parity error. + */ + SCR_TO_REG (scratcha), + 0, + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDR (msg_parity), + SCR_FROM_REG (scratcha), + 0, + /* + */ + SCR_JUMP ^ IFFALSE (DATA (3)), + PADDR (msg_bad), + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get extended message code. + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + (ncrcmd) &ncr0.msgin[2], + /* + ** Check for message parity error. + */ + SCR_TO_REG (scratcha), + 0, + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDR (msg_parity), + SCR_FROM_REG (scratcha), + 0, + SCR_JUMP ^ IFTRUE (DATA (M_X_SDTR)), + PADDR (msg_sdtr), + /* + ** unknown extended message + */ + SCR_JUMP, + PADDR (msg_bad) + +}/*-------------------------< MSG_SDTR >-----------------*/,{ + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get period and offset + */ + SCR_MOVE_ABS (2) ^ SCR_MSG_IN, + (ncrcmd) &ncr0.msgin[3], + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDR (msg_parity), + /* + ** let the host do the real work. + */ + SCR_INT ^ IFTRUE (CARRYSET), + 3, + SCR_INT ^ IFFALSE (CARRYSET), + 4, + /* + ** let the target fetch our answer. + */ + SCR_SET (SCR_ATN), + 0, + SCR_CLR (SCR_ACK), + 0, + +/*<<<*/ SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_OUT)), + 16, + SCR_INT, + 4, + SCR_JUMP, + PADDR (dispatch), + /* + ** Sent the M_X_SDTR + */ +/*>>>*/ SCR_MOVE_ABS (5) ^ SCR_MSG_OUT, + (ncrcmd) &ncr0.msgout, + SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_OUT)), + -16, + SCR_COPY (1), + REG (sfbr), + (ncrcmd) &ncr_msgout, + /* + ** If rejected, cancel sync transfer. + */ + SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)), + PADDR (msg_out_done), + SCR_FROM_REG (sbdl), + 0, + SCR_INT ^ IFTRUE (DATA (M_REJECT)), + 4, + SCR_JUMP, + PADDR (msg_out_done), + +}/*-------------------------< COMPLETE >-----------------*/,{ + /* + ** Complete message. + ** + ** When we terminate the cycle by clearing ACK, + ** the target may disconnect immediately. + ** + ** We don't want to be told of an + ** "unexpected disconnect", + ** so we disable this feature. + */ + SCR_REG_REG (scntl2, SCR_AND, 0x7f), + 0, + /* + ** Terminate cycle ... + */ + SCR_CLR (SCR_ACK), + 0, + /* + ** ... and wait for the disconnect. + */ + SCR_WAIT_DISC, + 0, +}/*-------------------------< CLEANUP >-------------------*/,{ + /* + ** dsa: Pointer to ccb + ** or xxxxxxFF (no ccb) + ** + ** scr0: Host-Status (<>0!) + */ + SCR_FROM_REG (dsa), + 0, + SCR_JUMP ^ IFTRUE (DATA (0xff)), + PADDR (signal), + /* + ** dsa is valid. + ** save the status registers + */ + SCR_COPY (4), + REG (scr0), + (ncrcmd) &ncr0.header.status, + /* + ** and copy back the header to the ccb. + */ + SCR_COPY (4), + REG (dsa), + PADDR (savepos), + SCR_COPY (sizeof (struct head)), + (ncrcmd) &ncr0.header, +}/*-------------------------< SAVEPOS >---------------------*/,{ + 0, + + /* + ** If command resulted in "check condition" + ** status and is not yet completed, + ** try to get the condition code. + */ + SCR_FROM_REG (scr0), + 0, +/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (0x00, 0xfc)), + 16, + SCR_FROM_REG (scr1), + 0, + SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)), + PADDR(getcc2), + /* + ** And make the DSA register invalid. + */ +/*>>>*/ SCR_LOAD_REG (dsa, 0xff), /* invalid */ + 0, +}/*-------------------------< SIGNAL >----------------------*/,{ + /* + ** if status = queue full, + ** reinsert in startqueue and stall queue. + */ + SCR_FROM_REG (scr1), + 0, + SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)), + 8, + /* + ** if job completed ... + */ + SCR_FROM_REG (scr0), + 0, + /* + ** ... signal completion to the host + */ + SCR_INT_FLY ^ IFFALSE (MASK (0x00, 0xfc)), + 0, + /* + ** Auf zu neuen Schandtaten! + */ + SCR_JUMP, + PADDR(start), + +}/*-------------------------< SAVE_DP >------------------*/,{ + /* + ** SAVE_DP message: + ** Copy TEMP register to SAVEP in header. + */ + SCR_COPY (4), + REG (temp), + (ncrcmd) &ncr0.header.savep, + SCR_JUMP, + PADDR (clrack), +}/*-------------------------< RESTORE_DP >---------------*/,{ + /* + ** RESTORE_DP message: + ** Copy SAVEP in header to TEMP register. + */ + SCR_COPY (4), + (ncrcmd) &ncr0.header.savep, + REG (temp), + SCR_JUMP, + PADDR (clrack), +}/*-------------------------< DISCONNECT >---------------*/,{ + /* + ** Disable the "unexpected disconnect" feature. + */ + SCR_REG_REG (scntl2, SCR_AND, 0x7f), + 0, + SCR_CLR (SCR_ACK), + 0, + /* + ** Wait for the disconnect. + */ + SCR_WAIT_DISC, + 0, + /* + ** Profiling: + ** Set a time stamp, + ** and count the disconnects. + */ + SCR_COPY (sizeof (struct timeval)), + (ncrcmd) &time, + (ncrcmd) &ncr0.header.stamp.disconnect, + SCR_COPY (4), + (ncrcmd) &ncr0.disc_phys, + REG (temp), + SCR_REG_REG (temp, SCR_ADD, 0x01), + 0, + SCR_COPY (4), + REG (temp), + (ncrcmd) &ncr0.disc_phys, + /* + ** Status is: DISCONNECTED. + */ + SCR_LOAD_REG (scr0, HS_DISCONNECT), + 0, + SCR_JUMP, + PADDR (cleanup), + +}/*-------------------------< MSG_OUT >-------------------*/,{ + /* + ** The target requests a message. + ** First remove ATN so the target will + ** not continue fetching messages. + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_OUT, + (ncrcmd) &ncr0.msgout, + SCR_COPY (1), + REG (sfbr), + (ncrcmd) &ncr_msgout, + /* + ** If it was no ABORT message ... + */ + SCR_JUMP ^ IFTRUE (DATA (M_ABORT)), + PADDR (msg_out_abort), + /* + ** ... wait for the next phase + ** if it's a message out, send it again, ... + */ + SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)), + PADDR (msg_out), +}/*-------------------------< MSG_OUT_DONE >--------------*/,{ + /* + ** ... else clear the message ... + */ + SCR_LOAD_REG (scratcha, M_NOOP), + 0, + SCR_COPY (4), + REG (scratcha), + (ncrcmd) &ncr0.msgout, + /* + ** ... and process the next phase + */ + SCR_JUMP, + PADDR (dispatch), +}/*-------------------------< MSG_OUT_ABORT >-------------*/,{ + /* + ** After ABORT message, + ** + ** expect an immediate disconnect, ... + */ + SCR_REG_REG (scntl2, SCR_AND, 0x7f), + 0, + SCR_CLR (SCR_ACK), + 0, + SCR_WAIT_DISC, + 0, + /* + ** ... and set the status to "ABORTED" + */ + SCR_LOAD_REG (scr0, HS_ABORTED), + 0, + SCR_JUMP, + PADDR (cleanup), + +}/*-------------------------< GETCC >-----------------------*/,{ + /* + ** The ncr doesn't have an indirect load + ** or store command. So we have to + ** copy part of the control block to a + ** fixed place, where we can modify it. + ** + ** We patch the address part of a COPY command + ** with the address of the dsa register ... + */ + SCR_COPY (4), + REG (dsa), + PADDR (getcc1), + /* + ** ... then we do the actual copy. + */ + SCR_COPY (sizeof (struct head)), +}/*-------------------------< GETCC1 >----------------------*/,{ + 0, + (ncrcmd) &ncr0.header, + /* + ** Initialize the status registers + */ + SCR_COPY (4), + (ncrcmd) &ncr0.header.status, + REG (scr0), +}/*-------------------------< GETCC2 >----------------------*/,{ + /* + ** Get the condition code from a target. + ** + ** DSA points to a data structure. + ** Set TEMP to the script location + ** that receives the condition code. + ** + ** Because there is no script command + ** to load a longword into a register, + ** we use a CALL command. + */ +/*<<<*/ SCR_CALLR, + 24, + /* + ** Get the condition code. + */ + SCR_MOVE_TBL ^ SCR_DATA_IN, + offsetof (struct dsb, sense), + /* + ** No data phase may follow! + */ + SCR_CALL, + PADDR (checkatn), + SCR_JUMP, + PADDR (no_data), +/*>>>*/ + + /* + ** The CALL jumps to this point. + ** Prepare for a RESTORE_POINTER message. + ** Save the TEMP register into the saved pointer. + */ + SCR_COPY (4), + REG (temp), + (ncrcmd) &ncr0.header.savep, + /* + ** Load scratcha, because in case of a selection timeout, + ** the host will expect a new value for startpos in + ** the scratcha register. + */ + SCR_COPY (4), + PADDR (startpos), + REG (scratcha), + /* + ** Then try to connect to the target. + ** If we are reselected, special treatment + ** of the current job is required before + ** accepting the reselection. + */ + SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select), + PADDR(badgetcc), + SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)), + 0, + SCR_FROM_REG (sdid), + 0, + SCR_TO_REG (ctest0), + 0, + /* + ** and send the IDENTIFY and a SDTM message. + */ + SCR_MOVE_TBL ^ SCR_MSG_OUT, + offsetof (struct dsb, smsg2), + SCR_JUMPR ^ IFTRUE ( WHEN (SCR_MSG_OUT) ), + -16, + SCR_COPY (1), + REG (sfbr), + (ncrcmd) &ncr_msgout, + /* + ** Handle synch negotiation. + */ + SCR_SET (SCR_CARRY), + 0, + SCR_JUMP, + PADDR (prepare2), + +}/*------------------------< BADGETCC >---------------------*/,{ + SCR_INT, + 2, +}/*-------------------------< RESELECT >--------------------*/,{ + /* + ** make the DSA invalid. + */ + SCR_LOAD_REG (dsa, 0xff), + 0, + SCR_CLR (SCR_TRG), + 0, + /* + ** Sleep waiting for a reselection. + ** If SIGP is set, special treatment. + ** + ** Zu allem bereit .. + */ + SCR_WAIT_RESEL, + PADDR(reselect2), + /* + ** ... zu nichts zu gebrauchen ? + ** + ** load the target id into the SFBR + ** and jump to the control block. + ** + ** Look at the declarations of + ** - struct ncb + ** - struct tcb + ** - struct lcb + ** - struct ccb + ** to understand what's going on. + */ + SCR_REG_SFBR (ssid, SCR_AND, 0x87), + 0, + SCR_TO_REG (ctest0), + 0, + SCR_JUMP, + (ncrcmd) &ncr0.jump_tcb, +}/*-------------------------< RESELECT2 >-------------------*/,{ + /* + ** If it's not connected :( + ** -> interrupted by SIGP bit. + ** Jump to start. + */ + SCR_FROM_REG (ctest2), + 0, + SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)), + PADDR (start), + SCR_JUMP, + PADDR (reselect), + +}/*-------------------------< RESEL_TMP >-------------------*/,{ + /* + ** The return address in TEMP + ** is in fact the data structure address, + ** so copy it to the DSA register. + */ + SCR_COPY (4), + REG (temp), + REG (dsa), + SCR_JUMP, + PADDR (prepare), + +}/*-------------------------< RESEL_LUN >-------------------*/,{ + /* + ** come back to this point + ** to get an IDENTIFY message + ** Wait for a msg_in phase. + */ +/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)), + 48, + /* + ** message phase + ** It's not a sony, it's a trick: + ** read the data without acknowledging it. + */ + SCR_FROM_REG (sbdl), + 0, +/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (M_IDENTIFY, 0x98)), + 32, + /* + ** It WAS an Identify message. + ** get it and ack it! + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + (ncrcmd) &ncr0.msgin, + SCR_CLR (SCR_ACK), + 0, + /* + ** Mask out the LUN. + */ + SCR_REG_REG (sfbr, SCR_AND, 0x07), + 0, + SCR_RETURN, + 0, + /* + ** No message phase or no IDENTIFY message: + ** return 0. + */ +/*>>>*/ SCR_LOAD_SFBR (0), + 0, + SCR_RETURN, + 0, + +}/*-------------------------< RESEL_TAG >-------------------*/,{ + /* + ** come back to this point + ** to get a SIMPLE_TAG message + ** Wait for a MSG_IN phase. + */ +/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)), + 64, + /* + ** message phase + ** It's a trick - read the data + ** without acknowledging it. + */ + SCR_FROM_REG (sbdl), + 0, +/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (M_SIMPLE_TAG)), + 48, + /* + ** It WAS a SIMPLE_TAG message. + ** get it and ack it! + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + (ncrcmd) &ncr0.msgin, + SCR_CLR (SCR_ACK), + 0, + /* + ** Wait for the second byte (the tag) + */ +/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)), + 24, + /* + ** Get it and ack it! + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + (ncrcmd) &ncr0.msgin, + SCR_CLR (SCR_ACK|SCR_CARRY), + 0, + SCR_RETURN, + 0, + /* + ** No message phase or no SIMPLE_TAG message + ** or no second byte: return 0. + */ +/*>>>*/ SCR_LOAD_SFBR (0), + 0, + SCR_SET (SCR_CARRY), + 0, + SCR_RETURN, + 0, + +}/*-------------------------< DATA_IN >--------------------*/,{ +/* +** Because the size depends on the +** #define MAX_SCATTER parameter, +** it is filled in at runtime. +** +** SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)), +** PADDR (no_data), +** SCR_COPY (sizeof (struct timeval)), +** (ncrcmd) &time, +** (ncrcmd) &ncr0.header.stamp.data, +** SCR_MOVE_TBL ^ SCR_DATA_IN, +** offsetof (struct dsb, data[ 0]), +** +** ##===========< i=1; i<MAX_SCATTER >========= +** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)), +** || PADDR (checkatn), +** || SCR_MOVE_TBL ^ SCR_DATA_IN, +** || offsetof (struct dsb, data[ i]), +** ##========================================== +** +** SCR_CALL, +** PADDR (checkatn), +** SCR_JUMP, +** PADDR (no_data), +*/ +0 +}/*-------------------------< DATA_OUT >-------------------*/,{ +/* +** Because the size depends on the +** #define MAX_SCATTER parameter, +** it is filled in at runtime. +** +** SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)), +** PADDR (no_data), +** SCR_COPY (sizeof (struct timeval)), +** (ncrcmd) &time, +** (ncrcmd) &ncr0.header.stamp.data, +** SCR_MOVE_TBL ^ SCR_DATA_OUT, +** offsetof (struct dsb, data[ 0]), +** +** ##===========< i=1; i<MAX_SCATTER >========= +** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)), +** || PADDR (dispatch), +** || SCR_MOVE_TBL ^ SCR_DATA_OUT, +** || offsetof (struct dsb, data[ i]), +** ##========================================== +** +** SCR_CALL, +** PADDR (dispatch), +** SCR_JUMP, +** PADDR (no_data), +** +**--------------------------------------------------------- +*/ +(u_long)&ident + +}/*-------------------------< ABORTTAG >-------------------*/,{ + /* + ** Abort a bad reselection. + ** Set the message to ABORT vs. ABORT_TAG + */ + SCR_LOAD_REG (scratcha, M_ABORT_TAG), + 0, + SCR_JUMPR ^ IFFALSE (CARRYSET), + 8, +}/*-------------------------< ABORT >----------------------*/,{ + SCR_LOAD_REG (scratcha, M_ABORT), + 0, + SCR_COPY (1), + REG (scratcha), + (ncrcmd) &ncr0.msgout, + SCR_SET (SCR_ATN), + 0, + SCR_CLR (SCR_ACK), + 0, + /* + ** and send it. + ** we expect an immediate disconnect + */ + SCR_REG_REG (scntl2, SCR_AND, 0x7f), + 0, + SCR_MOVE_ABS (1) ^ SCR_MSG_OUT, + (ncrcmd) &ncr0.msgout, + SCR_COPY (1), + REG (sfbr), + (ncrcmd) &ncr_msgout, + SCR_WAIT_DISC, + 0, + SCR_JUMP, + PADDR (start), +}/*--------------------------------------------------------*/ +}; + +/*========================================================== +** +** +** Fill in #define dependent parts of the script +** +** +**========================================================== +*/ + +void ncr_script_fill (struct script * scr) +{ + int i; + ncrcmd *p; + + p = scr->tryloop; + for (i=0; i<MAX_START; i++) { + *p++ =SCR_COPY (4); + *p++ =(ncrcmd) &ncr0.squeue[i]; + *p++ =REG (dsa); + *p++ =SCR_CALL; + *p++ =PADDR (trysel); + }; + *p++ =SCR_JUMP; + *p++ =PADDR(tryloop); + + assert ((u_long)p == (u_long)&scr->tryloop + sizeof (scr->tryloop)); + + p = scr->data_in; + + *p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)); + *p++ =PADDR (no_data); + *p++ =SCR_COPY (sizeof (struct timeval)); + *p++ =(ncrcmd) &time; + *p++ =(ncrcmd) &ncr0.header.stamp.data; + *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN; + *p++ =offsetof (struct dsb, data[ 0]); + + for (i=1; i<MAX_SCATTER; i++) { + *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)); + *p++ =PADDR (checkatn); + *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN; + *p++ =offsetof (struct dsb, data[i]); + }; + + *p++ =SCR_CALL; + *p++ =PADDR (checkatn); + *p++ =SCR_JUMP; + *p++ =PADDR (no_data); + + assert ((u_long)p == (u_long)&scr->data_in + sizeof (scr->data_in)); + + p = scr->data_out; + + *p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_OUT)); + *p++ =PADDR (no_data); + *p++ =SCR_COPY (sizeof (struct timeval)); + *p++ =(ncrcmd) &time; + *p++ =(ncrcmd) &ncr0.header.stamp.data; + *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT; + *p++ =offsetof (struct dsb, data[ 0]); + + for (i=1; i<MAX_SCATTER; i++) { + *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)); + *p++ =PADDR (dispatch); + *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT; + *p++ =offsetof (struct dsb, data[i]); + }; + + *p++ =SCR_CALL; + *p++ =PADDR (dispatch); + *p++ =SCR_JUMP; + *p++ =PADDR (no_data); + + assert ((u_long)p == (u_long)&scr->data_out + sizeof (scr->data_out)); +} + +/*========================================================== +** +** +** Bind the script to its physical address. +** +** +**========================================================== +*/ + +static int ncr_unit (ncb_p np) +{ + int idx; + for (idx=0;idx<NNCR; idx++) + if (ncrp[idx]==np) + return (idx); + return (-1); +} + +static void ncr_script_bind (ncb_p np) +{ + ncrcmd *p = (ncrcmd*) np->v_script; + ncrcmd opcode, arg, a1; + ncrcmd *end, *start; + + start = p; + for (end=p+(sizeof(struct script)/4);p<end;) { + + /* + ** If we forget to change the length + ** in struct script, a field will be + ** padded with 0. This is an illegal + ** command. + */ + + if (!(opcode=*p++)) + printf ("ncr%d: ERROR0 IN SCRIPT at %d.\n", + ncr_unit(np), p-start-1); + arg=*p; +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_SCRIPT) + printf ("%x: <%x>\n", + (u_long)(p-1), opcode); +#endif /* SCSI_NCR_DEBUG */ + /* + ** We don't have to decode ALL commands + */ + switch (opcode >> 28) { + + case 0xc: + /* + ** COPY has TWO arguments. + */ + a1 = arg; + if (a1 >= 256) *p = vtophys (a1); + else *p = np->paddr + a1; + arg = *++p; +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_SCRIPT) + printf ("%x: <%x>\n", (u_long)p, *p); +#endif /* SCSI_NCR_DEBUG */ + if ((a1 ^ arg) & 3) + printf ("ncr%d: script: ERROR1 IN SCRIPT at %d.\n", + ncr_unit (np), p-start-1); + /* pass */ + case 0x0: + /* + ** MOVE (absolute address) + */ + if (arg >= 256) *p = vtophys (arg); + else *p = np->paddr + arg; + break; + case 0x8: + /* + ** JUMP / CALL + ** dont't relocate if relative :-) + */ + if (opcode & 0x00800000) break; + *p = vtophys (arg); + break; + /* + ** don't relocate 0 arguments. + */ + case 0x4: + case 0x5: + case 0x6: + case 0x7: + if (arg) *p = vtophys (arg); + break; + default: + break; + }; +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_SCRIPT) + printf ("%x: <%x>\n", (u_long)p, *p); +#endif /* SCSI_NCR_DEBUG */ + p++; + }; +} + +/*========================================================== +** +** +** Copy and rebind a script. +** +** +**========================================================== +*/ + +static void ncr_script_copy (ncb_p oldnp, ncb_p np) +{ + ncrcmd *src = (ncrcmd*) oldnp->v_script; + ncrcmd *end = (ncrcmd*) oldnp->v_script + sizeof (struct script)/4; + ncrcmd *dst = (ncrcmd*) np->v_script; + + u_long oldbase = vtophys (oldnp); + u_long newbase = vtophys (np); + + ncrcmd opcode, old, new; + u_long d; + + printf ("ncr%d: script_copy from ncr%d..\n", + ncr_unit (np), ncr_unit (oldnp)); + while (src < end) { + + *dst++ = opcode = *src++; + + new=old=*src++; + + /* + ** We don't have to decode ALL commands + */ + switch (opcode >> 28) { + + case 0xc: + /* + ** COPY has TWO arguments. + */ + + d = old - oldnp->paddr; + if (d<sizeof (struct ncr_reg)) + new = np->paddr + d; + + d = old - oldnp->p_script; + if (d<sizeof (struct script)) + new = np->p_script + d; + + d = old - oldbase; + if (d<sizeof (struct ncb)) + new = newbase + d; + + *dst++ = new; + new=old=*src++; + + /* + ** fall through + */ + + case 0x0: + /* + ** MOVE (absolute address) + */ + d = old - oldnp->paddr; + if (d<sizeof (struct ncr_reg)) + new = np->paddr + d; + + d = old - oldnp->p_script; + if (d<sizeof (struct script)) + new = np->p_script + d; + + d = old - oldbase; + if (d<sizeof (struct ncb)) + new = newbase + d; + + break; + case 0x8: + /* + ** JUMP / CALL + ** dont't relocate if relative :-) + */ + if (opcode & 0x00800000) break; + /* + ** fall through + */ + case 0x4: + case 0x5: + case 0x6: + case 0x7: + d = old - oldnp->paddr; + if (d<sizeof (struct ncr_reg)) + new = np->paddr + d; + + d = old - oldnp->p_script; + if (d<sizeof (struct script)) + new = np->p_script + d; + + d = old - oldbase; + if (d<sizeof (struct ncb)) + new = newbase + d; + + default: + break; + }; + *dst++ = new; + }; + assert ((char*)src == ((char*)oldnp->v_script)+sizeof(struct script)); + assert ((char*)dst == ((char*) np->v_script)+sizeof(struct script)); +} + +/*========================================================== +** +** +** Auto configuration. +** +** +**========================================================== +*/ + +/*---------------------------------------------------------- +** +** Reduce the transfer length to the max value +** we can transfer safely. +** +** Reading a block greater then MAX_SIZE from the +** raw (character) device exercises a memory leak +** in the vm subsystem. This is common to ALL devices. +** We have submitted a description of this bug to +** <FreeBSD-bugs@freefall.cdrom.com>. +** It should be fixed in the current release. +** +**---------------------------------------------------------- +*/ + +void ncr_min_phys (struct buf *bp) +{ + if (bp->b_bcount > MAX_SIZE) bp->b_bcount = MAX_SIZE; +} + +/*---------------------------------------------------------- +** +** Maximal number of outstanding requests per target. +** +**---------------------------------------------------------- +*/ + +U_INT32 ncr_info (int unit) +{ + return (1); /* may be changed later */ +} + +/*---------------------------------------------------------- +** +** Probe the hostadapter. +** +**---------------------------------------------------------- +*/ + +static int ncb_probe(pcici_t config_id) +{ + if (ncr_units >= NNCR) return (-1); + return (ncr_units); +} + +/*========================================================== +** +** +** Auto configuration: attach and init a host adapter. +** +** +**========================================================== +*/ + +#define MIN_ASYNC_PD 40 +#define MIN_SYNC_PD 20 + +static int ncr_attach (pcici_t config_id) +{ + int retval; + ncb_p np = ncrp[ncr_units]; + + /* + ** allocate structure + */ + + if (!np) { + np = (ncb_p) malloc (sizeof (struct ncb), + M_DEVBUF, M_NOWAIT); + if (!np) return (0); + ncrp[ncr_units]=np; + } + + /* + ** initialize structure. + */ + + bzero (np, sizeof (*np)); + + /* + ** Try to map the controller chip to + ** virtual and physical memory. + */ + + retval = pci_map_mem (config_id, 0x14, &np->vaddr, &np->paddr); + + if (retval) { + printf ("ncr%d: pci_map_mem failed.\n",ncr_unit (np)); + return (retval); + }; + + /* + ** Patch script to physical addresses + */ + + if (np==&ncr0) { + np->v_script = &script0; + np->p_script = vtophys (np->v_script); + ncr_script_fill (&script0); + ncr_script_bind (np); + } else { + /* + ** allocate space for script + */ + np->v_script = (struct script *) + malloc (sizeof (struct ncb), M_DEVBUF, M_NOWAIT); + if (!np->v_script) return (0); + np->p_script = vtophys (np->v_script); + ncr_script_copy (&ncr0, np); + }; + + /* + ** init data structure + */ + + np -> jump_tcb.l_cmd = SCR_JUMP ; + np -> jump_tcb.l_paddr = vtophys (&np->v_script->abort); + + /* + ** Make the controller's registers available. + ** Now the INB INW INL OUTB OUTW OUTL macros + ** can be used safely. + */ + + np->reg = (struct ncr_reg*) np->vaddr; + + /* + ** Get SCSI addr of host adapter (set by bios?). + */ + + np->myaddr = INB(nc_scid) & 0x07; + if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR; + + /* + ** Get the value of the chip's clock. + ** Find the right value for scntl3. + */ + + ncr_getclock (np); + + /* + ** Reset chip. + */ + + OUTB (nc_istat, SRST); + OUTB (nc_istat, 0 ); + + /* + ** After SCSI devices have been opened, we cannot + ** reset the bus safely, so we do it here. + ** Interrupt handler does the real work. + */ + + OUTB (nc_scntl1, CRST); + + /* + ** process the reset exception, + ** if interrupts are not enabled yet. + */ + ncr_exception (np); + +#ifdef ANCIENT + printf ("ncr%d waiting for scsi devices to settle\n", + ncr_unit (np)); + DELAY (1000000); +#endif + + /* + ** Now let the generic SCSI driver + ** look for the SCSI devices on the bus .. + */ + +#ifndef ANCIENT +#ifdef __NetBSD__ + np->sc_link.adapter_softc = np; +#else/*__NetBSD__*/ + np->sc_link.adapter_unit = ncr_units; +#endif /*__NetBSD__*/ + np->sc_link.adapter_targ = np->myaddr; + np->sc_link.adapter = &ncr_switch; + np->sc_link.device = &ncr_dev; + + scsi_attachdevs (&np->sc_link); +#else /* ANCIENT */ + scsi_attachdevs (ncr_units, np->myaddr, &ncr_switch); +#endif /* ANCIENT */ + + /* + ** start the timeout daemon + */ + ncr_timeout (np); + np->lasttime=0; + + /* + ** Done. + */ + + ncr_units++; + + return(1); +} + +/*========================================================== +** +** +** Process pending device interrupts. +** +** +**========================================================== +*/ + +static int ncr_intr (int dev) +{ + ncb_p np; + int n=0; + + /* + ** Sanity check + */ + + if (dev >= ncr_units) return (0); + +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_TINY) printf ("["); +#endif /* SCSI_NCR_DEBUG */ + + assert (dev<NNCR); + + /* + ** Repeat until no outstanding ints + */ + + np = ncrp[dev]; + while (INB(nc_istat) & (INTF|SIP|DIP)) { + ncr_exception (np); + n=1; + }; + + /* + ** Switch timeout function to slow. + */ + + if (n) np->ticks = 100; + +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_TINY) printf ("]\n"); +#endif /* SCSI_NCR_DEBUG */ + + return (n); +} + +/*========================================================== +** +** +** Start execution of a SCSI command. +** This is called from the generic SCSI driver. +** +** +**========================================================== +*/ + +static INT32 ncr_start (struct scsi_xfer * xp) +{ +#ifndef ANCIENT +#ifdef __NetBSD__ + ncb_p np = xp->sc_link->adapter_softc; +#else /*__NetBSD__*/ + ncb_p np = ncrp[xp->sc_link->adapter_unit]; +#endif/*__NetBSD__*/ +#else /* ANCIENT */ + ncb_p np = ncrp[xp->adapter]; +#endif /* ANCIENT */ + + struct scsi_generic * cmd = xp->cmd; + ccb_p cp; + lcb_p lp; + tcb_p tp; + + int i, oldspl, flags = xp->flags; + u_char ptr, startcode, idmsg; + u_long msglen, msglen2; + + /*--------------------------------------------- + ** + ** Reset SCSI bus + ** + ** Interrupt handler does the real work. + ** + **--------------------------------------------- + */ + + if (flags & SCSI_RESET) { + OUTB (nc_scntl1, CRST); + return(COMPLETE); + }; + + /*--------------------------------------------- + ** + ** Some shortcuts ... + ** + **--------------------------------------------- + */ + + if ((xp->TARGET == np->myaddr ) || + (xp->TARGET >= MAX_TARGET) || + (xp->LUN >= MAX_LUN ) || + (flags & SCSI_DATA_UIO)) { + xp->error = XS_DRIVER_STUFFUP; + return(HAD_ERROR); + }; + +#ifdef ANCIENT + /*--------------------------------------------- + ** Ancient version of <sys/scsi/sd.c> + ** doesn't set the DATA_IN/DATA_OUT bits. + ** So we have to fix it .. + **--------------------------------------------- + */ + + switch (cmd->opcode) { + case 0x1a: /* MODE_SENSE */ + case 0x25: /* READ_CAPACITY */ + case 0x28: /* READ_BIG (10) */ + xp->flags |= SCSI_DATA_IN; + break; + case 0x2a: /* WRITE_BIG(10) */ + xp->flags |= SCSI_DATA_OUT; + break; + }; +#endif /* ANCIENT */ + +#ifdef SCSI_NCR_DEBUG + if(ncr_debug & DEBUG_TINY) + printf("ncr%d targ %d: CMD=%x F=%x L=%x ", + ncr_unit (np), xp->TARGET, cmd->opcode, + xp->flags, xp->datalen); +#endif /* SCSI_NCR_DEBUG */ + + /*-------------------------------------------- + ** + ** Sanity checks ... + ** copied from Elischer's Adaptec driver. + ** + **-------------------------------------------- + */ + + flags = xp->flags; + if (!(flags & INUSE)) { + printf("ncr%d: ?INUSE?\n", ncr_unit (np)); + xp->flags |= INUSE; + }; + + if(flags & ITSDONE) { + printf("ncr%d: ?ITSDONE?\n", ncr_unit (np)); + xp->flags &= ~ITSDONE; + }; + + if (xp->bp) + flags |= (SCSI_NOSLEEP); /* just to be sure */ + + /*--------------------------------------------------- + ** + ** Assign a ccb + ** + **---------------------------------------------------- + */ + + if (!(cp=ncr_get_ccb (np, flags, xp->TARGET, xp->LUN))) { + printf ("ncr%d: no ccb.\n", ncr_unit (np)); + xp->error = XS_DRIVER_STUFFUP; + return(TRY_AGAIN_LATER); + }; + + /*--------------------------------------------------- + ** + ** timestamp + ** + **---------------------------------------------------- + */ + + bzero (&cp->phys.header.stamp, sizeof (struct tstamp)); + cp->phys.header.stamp.start = time; + + /*--------------------------------------------------- + ** + ** sync negotiation required? + ** + **---------------------------------------------------- + */ + + tp = &np->target[xp->TARGET]; + + if ((cmd->opcode!=0x12) && (tp->period)) { + + startcode = HS_BUSY; + + } else if (!(tp->inqdata[7] & INQ7_SYNC)) { + + tp->minsync = 255; + tp->maxoffs = 8 ; + tp->period =0xffff; + startcode = HS_BUSY; + + } else { + /* + ** minsync unit is 4ns ! + */ + + u_long minsync = tp->usrsync; + + if (minsync < 25) minsync=25; + + /* + ** if not scsi 2 + ** don't believe FAST! + */ + + if ((minsync < 50) && (tp->inqdata[2] & 0x0f) < 2) + minsync=50; + + /* + ** our limit .. + */ + + if (minsync < np->ns_sync) + minsync = np->ns_sync; + + /* + ** divider limit + */ + + if (minsync > (np->ns_sync * 11) / 4) + minsync = 255; + + tp->minsync = minsync; + tp->maxoffs = (minsync<255 ? 8 : 0); + + startcode = HS_NEGOTIATE; + }; + + /*--------------------------------------------------- + ** + ** choose a new tag ... + ** + **---------------------------------------------------- + */ + + if ((lp = tp->lp[xp->LUN]) && (lp->usetags)) { + /* + ** assign a tag to this ccb! + */ + while (!cp->tag) { + ccb_p cp2 = lp->next_ccb; + lp->lasttag = lp->lasttag % 255 + 1; + while (cp2 && cp2->tag != lp->lasttag) + cp2 = cp2->next_ccb; + if (cp2) continue; + cp->tag=lp->lasttag; + printf ("ncr%d targ %d lun %d: using tag #%d.\n", + ncr_unit (np), xp->TARGET, xp->LUN, cp->tag); + }; + } else cp->tag=0; + + + /*---------------------------------------------------- + ** + ** Build the identify / tag / sdtr message + ** + **---------------------------------------------------- + */ + + idmsg = (cp==&np->ccb ? 0x80 : 0xc0) | xp->LUN; + + cp -> scsi_smsg [0] = idmsg; + msglen=1; + + if (cp->tag) { + + /* + ** Ordered write ops, unordered read ops. + */ + switch (cmd->opcode) { + case 0x08: /* READ_SMALL (6) */ + case 0x28: /* READ_BIG (10) */ + case 0xa8: /* READ_HUGE (12) */ + cp -> scsi_smsg [msglen] = M_SIMPLE_TAG; + break; + default: + cp -> scsi_smsg [msglen] = M_ORDERED_TAG; + } + + /* + ** can be overwritten by ncrstat + */ + switch (np->order) { + + case M_SIMPLE_TAG: + cp -> scsi_smsg [msglen] = M_SIMPLE_TAG; + break; + + case M_ORDERED_TAG: + cp -> scsi_smsg [msglen] = M_ORDERED_TAG; + break; + }; + msglen++; + + cp -> scsi_smsg [msglen++] = cp -> tag; + } + if (startcode==HS_NEGOTIATE) { + cp -> scsi_smsg [msglen++] = M_EXTENDED; + cp -> scsi_smsg [msglen++] = 3; + cp -> scsi_smsg [msglen++] = M_X_SDTR; + cp -> scsi_smsg [msglen++] = np->target[xp->TARGET].minsync; + cp -> scsi_smsg [msglen++] = np->target[xp->TARGET].maxoffs; + }; + + /*---------------------------------------------------- + ** + ** Build the identify / sdtr message for getcc + ** + **---------------------------------------------------- + */ + + cp -> scsi_smsg2 [0] = idmsg; + msglen2 = 1; + if (np->target[xp->TARGET].inqdata[7]&INQ7_SYNC) { + cp -> scsi_smsg2 [1] = M_EXTENDED; + cp -> scsi_smsg2 [2] = 3; + cp -> scsi_smsg2 [3] = M_X_SDTR; + cp -> scsi_smsg2 [4] = np->target[xp->TARGET].minsync; + cp -> scsi_smsg2 [5] = np->target[xp->TARGET].maxoffs; + msglen2 = 6; + }; + + /*---------------------------------------------------- + ** + ** Build the data descriptors + ** + **---------------------------------------------------- + */ + + if (ncr_scatter (&cp->phys, + (vm_offset_t) xp->data, + (vm_size_t) xp->datalen)) { + xp->error = XS_DRIVER_STUFFUP; + ncr_free_ccb(np, cp, flags); + return(HAD_ERROR); + }; + + /*---------------------------------------------------- + ** + ** Set the SAVED_POINTER. + ** + **---------------------------------------------------- + */ + + if (flags & SCSI_DATA_IN) { + cp->phys.header.savep = vtophys (&np->v_script->data_in); + } else if (flags & SCSI_DATA_OUT) { + cp->phys.header.savep = vtophys (&np->v_script->data_out); + } else { + cp->phys.header.savep = vtophys (&np->v_script->no_data); + }; + + /*---------------------------------------------------- + ** + ** fill ccb + ** + **---------------------------------------------------- + */ + + /* + ** physical -> virtual translation + */ + cp->phys.header.cp = cp; + /* + ** Generic SCSI command + */ + cp->xfer = xp; + /* + ** Startqueue + */ + cp->phys.header.launch.l_paddr = vtophys (&np->v_script->select); + cp->phys.header.launch.l_cmd = SCR_JUMP; + /* + ** select + */ + cp->phys.select.sel_id = xp->TARGET; + cp->phys.select.sel_scntl3 = np->rv_scntl3; + cp->phys.select.sel_sxfer = np->target[xp->TARGET].sval; + /* + ** message + */ + cp->phys.smsg.addr = vtophys (&cp->scsi_smsg ); + cp->phys.smsg.size = msglen; + cp->phys.smsg2.addr = vtophys (&cp->scsi_smsg2); + cp->phys.smsg2.size = msglen2; + /* + ** command + */ +#ifdef ANCIENT + bcopy (cmd, &cp->cmd, sizeof (cp->cmd)); + cp->phys.cmd.addr = vtophys (&cp->cmd); +#else /* ANCIENT */ + cp->phys.cmd.addr = vtophys (cmd); +#endif /* ANCIENT */ + cp->phys.cmd.size = xp->cmdlen; + /* + ** sense data + */ + cp->phys.sense.addr = vtophys (&cp->xfer->sense); + cp->phys.sense.size = sizeof(struct scsi_sense_data); + /* + ** status + */ + cp->scs2_status = S_ILLEGAL; + cp->scsi_status = S_ILLEGAL; + cp->sync_status = np->target[xp->TARGET].sval; + cp->host_status = startcode; + cp->parity_errs = 0; + + /*---------------------------------------------------- + ** + ** Critical region: starting this job. + ** + **---------------------------------------------------- + */ + + oldspl = 0; /* for the sake of gcc */ + if (!(flags & SCSI_NOMASK)) oldspl = splbio(); + np->lock++; + + /* + ** reselect pattern and activate this job. + */ + + cp->jump_ccb.l_cmd = (SCR_JUMP ^ IFFALSE (DATA (cp->tag))); + cp->tlimit = time.tv_sec + xp->timeout / 1000 + 2; + cp->magic = CCB_MAGIC; + + /* + ** insert into startqueue. + */ + + ptr = np->squeueput + 1; + if (ptr >= MAX_START) ptr=0; + np->squeue [ptr ] = vtophys(&np->v_script->idle); + np->squeue [np->squeueput] = vtophys(&cp->phys); + np->squeueput = ptr; + +#ifdef SCSI_NCR_DEBUG + if(ncr_debug & DEBUG_QUEUE) + printf ("ncr%d: queuepos=%d tryoffset=%d.\n", ncr_unit (np), + np->squeueput, np->v_script->startpos[0]-(vtophys(&np->v_script->tryloop))); +#endif /* SCSI_NCR_DEBUG */ + + /* + ** Script processor may be waiting for reconnect. + ** Wake it up. + */ + OUTB (nc_istat, SIGP); + + /* + ** If interrupts are enabled, return now. + ** Command is successfully queued. + */ + + np->lock--; + if (!(flags & SCSI_NOMASK)) { + splx (oldspl); + if (np->lasttime) { +#ifdef SCSI_NCR_DEBUG + if(ncr_debug & DEBUG_TINY) printf ("Q"); +#endif /* SCSI_NCR_DEBUG */ + return(SUCCESSFULLY_QUEUED); + }; + }; + + /*---------------------------------------------------- + ** + ** Interrupts not yet enabled - have to poll. + ** + **---------------------------------------------------- + */ + +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_POLL) printf("P"); +#endif /* SCSI_NCR_DEBUG */ + + for (i=xp->timeout; i && !(xp->flags & ITSDONE);i--) { +#ifdef SCSI_NCR_DEBUG + if ((ncr_debug & DEBUG_POLL) && (cp->host_status)) + printf ("%c", (cp->host_status & 0xf) + '0'); +#endif /* SCSI_NCR_DEBUG */ + DELAY (1000); + ncr_exception (np); + }; + + /* + ** Abort if command not done. + */ + if (!(xp->flags & ITSDONE)) { + printf ("ncr%d: aborting job ...\n", ncr_unit (np)); + OUTB (nc_istat, CABRT); + DELAY (100000); + OUTB (nc_istat, SIGP); + ncr_exception (np); + }; + + if (!(xp->flags & ITSDONE)) { + printf ("ncr%d: abortion failed at %x.\n", + ncr_unit (np), INL(nc_dsp)); + ncr_init (np, "timeout", HS_TIMEOUT); + }; + + if (!(xp->flags & ITSDONE)) { + cp-> host_status = HS_SEL_TIMEOUT; + ncr_complete (np, cp); + }; + +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_RESULT) { + printf ("ncr%d: result: %x %x %x.\n", + ncr_unit (np), cp->host_status, + cp->scsi_status, cp->scs2_status); + }; +#endif /* SCSI_NCR_DEBUG */ + if (!(flags & SCSI_NOMASK)) + return (SUCCESSFULLY_QUEUED); + switch (xp->error) { + case 0 : return (COMPLETE); + case XS_BUSY: return (TRY_AGAIN_LATER); + }; + return (HAD_ERROR); +} + +/*========================================================== +** +** +** Complete execution of a SCSI command. +** Signal completion to the generic SCSI driver. +** +** +**========================================================== +*/ + +void ncr_complete (ncb_p np, ccb_p cp) +{ + struct scsi_xfer * xp; + tcb_p tp; + lcb_p lp; + + /* + ** Sanity check + */ + + if (!cp || !cp->magic || !cp->xfer) return; + cp->magic = 1; + cp->tlimit= 0; + + /* + ** No Reselect anymore. + */ + cp->jump_ccb.l_cmd = (SCR_JUMP); + + /* + ** No starting. + */ + cp->phys.header.launch.l_paddr= vtophys (&np->v_script->idle); + + /* + ** timestamp + */ + ncb_profile (np, cp); + +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_TINY) + printf ("CCB=%x STAT=%x/%x/%x\n", (u_long)cp & 0xfff, + cp->host_status,cp->scsi_status,cp->scs2_status); +#endif /* SCSI_NCR_DEBUG */ + + xp = cp->xfer; + cp->xfer = NULL; + tp = &np->target[xp->TARGET]; + lp = tp->lp[xp->LUN]; + + /* + ** @PARITY@ + ** Check for parity errors. + */ + + if (cp->parity_errs) { + printf ("ncr%d targ %d: %d parity error(s), fallback.\n", + ncr_unit (np), xp->TARGET, cp->parity_errs); + /* + ** fallback to asynch transfer. + */ + tp->usrsync=255; + tp->period = 0; + }; + + /* + ** Check the status. + */ + if ( (cp->host_status == HS_COMPLETE) + && (cp->scsi_status == S_GOOD)) { + + /* + ** All went well. + */ + xp->resid = 0; + + /* + ** Try to assign a ccb to this nexus + */ + ncr_alloc_ccb (np, xp); + + /* + ** On inquire cmd (0x12) save some data. + */ +#ifdef ANCIENT + if (cp->cmd.opcode == 0x12) { +#else /* ANCIENT */ + if (xp->cmd->opcode == 0x12) { +#endif /* ANCIENT */ + bcopy ( xp->data, + &tp->inqdata, + sizeof (tp->inqdata)); + ncr_setmaxtags (tp, tp->usrtags); + tp->period=0; + }; + + if (!tp->sval) { + printf ("ncr%d targ %d: asynchronous.\n", + ncr_unit (np), xp->TARGET); + tp->sval = 0xe0; + }; + + /* + ** Announce changes to the generic driver + */ + if (lp) { + ncr_settags (tp, lp); + if (lp->reqlink != lp->actlink) + ncr_opennings (np, lp, xp); + }; + +#ifdef DK + dk_xfer[DK] ++; + dk_wds [DK] += xp->datalen/64; + dk_wpms[DK] = 1000000; +#endif /* DK */ + + tp->bytes += xp->datalen; + tp->transfers ++; + + } else if (xp->flags & SCSI_ERR_OK) { + + /* + ** Not correct, but errors expected. + */ + xp->resid = 0; + + } else if ((cp->host_status == HS_COMPLETE) + && (cp->scsi_status == S_CHECK_COND) + && (cp->scs2_status == S_GOOD)) { + + /* + ** Check condition code + */ + xp->error = XS_SENSE; + +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & (DEBUG_RESULT|DEBUG_TINY)) { + u_char * p = (u_char*) & xp->sense; + int i; + printf ("\nncr%d: sense data:", ncr_unit (np)); + for (i=0; i<14; i++) printf (" %x", *p++); + printf (".\n"); + }; +#endif /* SCSI_NCR_DEBUG */ + + } else if ((cp->host_status == HS_COMPLETE) + && (cp->scsi_status == S_BUSY)) { + + /* + ** Target is busy. + */ + xp->error = XS_BUSY; + + } else if ((cp->host_status == HS_SEL_TIMEOUT) + || (cp->host_status == HS_TIMEOUT)) { + + /* + ** No response + */ + xp->error = XS_TIMEOUT; + + } else { + + /* + ** Other protocol messes + */ + printf ("ncr%d targ %d: COMMAND FAILED (%x %x %x) @%x.\n", + ncr_unit (np), xp->TARGET, + cp->host_status, cp->scsi_status, cp->scs2_status, + cp); + + xp->error = XS_DRIVER_STUFFUP; + } + + xp->flags |= ITSDONE; + + /* + ** Free this ccb + */ + ncr_free_ccb (np, cp, xp->flags); + + /* + ** signal completion to generic driver. + */ +#ifdef ANCIENT + if (xp->when_done) + (*(xp->when_done))(xp->done_arg,xp->done_arg2); +#else /* ANCIENT */ + scsi_done (xp); +#endif /* ANCIENT */ +} + +/*========================================================== +** +** +** Signal all (or one) control block done. +** +** +**========================================================== +*/ + +void ncr_wakeup (ncb_p np, u_long code) +{ + /* + ** Starting at the default ccb and following + ** the links, complete all jobs with a + ** host_status greater than "disconnect". + ** + ** If the "code" parameter is not zero, + ** complete all jobs that are not IDLE. + */ + + ccb_p cp = &np->ccb; + while (cp) { + switch (cp->host_status) { + + case HS_IDLE: + break; + + case HS_DISCONNECT: +#ifdef SCSI_NCR_DEBUG + if(ncr_debug & DEBUG_TINY) printf ("D"); +#endif /* SCSI_NCR_DEBUG */ + /* fall through */ + + case HS_BUSY: + case HS_NEGOTIATE: + if (!code) break; + cp->host_status = code; + + /* fall through */ + + default: + ncr_complete (np, cp); + break; + }; + cp = cp -> link_ccb; + }; +} + +/*========================================================== +** +** +** Start NCR chip. +** +** +**========================================================== +*/ + +void ncr_init (ncb_p np, char * msg, u_long code) +{ + int i; + u_long usrsync; + + /* + ** Reset chip. + */ + + OUTB (nc_istat, SRST ); + + /* + ** Message. + */ + + if (msg) printf ("ncr%d: restart (%s).\n", ncr_unit (np), msg); + + /* + ** Clear Start Queue + */ + + for (i=0;i<MAX_START;i++) + np -> squeue [i] = vtophys (&np->v_script->idle); + + /* + ** Start at first entry. + */ + + np->squeueput = 0; + np->v_script->startpos[0] = vtophys (&np->v_script->tryloop); + np->v_script->start [0] = SCR_INT ^ IFFALSE (0); + + /* + ** Wakeup all pending jobs. + */ + + ncr_wakeup (np, code); + + /* + ** Init chip. + */ + + OUTB (nc_istat, 0 ); /* Remove Reset, abort ... */ + OUTB (nc_scntl0, 0xca ); /* full arb., ena parity, par->ATN */ + OUTB (nc_scntl1, 0x00 ); /* odd parity, and remove CRST!! */ + OUTB (nc_scntl3, np->rv_scntl3);/* timing prescaler */ + OUTB (nc_scid , 0x40|np->myaddr); /* host adapter SCSI address */ + OUTB (nc_respid, 1<<np->myaddr);/* id to respond to */ + OUTB (nc_istat , SIGP ); /* Signal Process */ + OUTB (nc_dmode , 0xc ); /* Burst length = 16 transfer */ + OUTB (nc_dcntl , NOCOM ); /* no single step mode, protect SFBR*/ + OUTB (nc_ctest4, 0x08 ); /* enable master parity checking */ + OUTB (nc_stest3, TE ); /* TolerANT enable */ + OUTB (nc_stime0, 0xfb ); /* HTH = 1.6sec STO = 0.1 sec. */ + + /* + ** Reinitialize usrsync. + ** Have to renegotiate synch mode. + */ + + usrsync = 255; + if (SCSI_NCR_MAX_SYNC) { + u_long period; + period =1000000/SCSI_NCR_MAX_SYNC; /* ns = 10e6 / kHz */ + if (period <= 11 * np->ns_sync) { + if (period < 4 * np->ns_sync) + usrsync = np->ns_sync; + else + usrsync = period / 4; + }; + }; + + for (i=0;i<MAX_TARGET;i++) { + tcb_p tp = &np->target[i]; + tp->period = 0; + tp->sval = 0; + tp->usrsync = usrsync; + } + + /* + ** enable ints + */ + + OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST); + OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID); + + /* + ** Start script processor. + */ + + OUTL (nc_dsp, vtophys (&np->v_script->start)); +} + +/*========================================================== +** +** Switch sync mode for current job and it's target +** @CHECKOUT@ xxxcp only for assert - may delete it. +** +**========================================================== +*/ + +static void ncr_setsync (ncb_p np, ccb_p xxxcp, u_char sxfer) +{ + u_short target = INB (nc_ctest0)&7; + u_short period; + tcb_p tp; + ccb_p cp; + + assert (xxxcp); + if (!xxxcp) return; + assert (xxxcp->xfer); + if (!xxxcp->xfer) return; + assert (target==xxxcp->xfer->TARGET & 7); + + tp = &np->target[target]; + tp->period= sxfer&0xf ? ((sxfer>>5)+4) * np->ns_sync : 0xffff; + if (tp->sval == sxfer) return; + tp->sval = sxfer; + + /* + ** Bells and whistles ;-) + */ + if (sxfer & 0x0f) { + period = np->ns_sync * ((sxfer>>5)+4); + printf ("ncr%d targ %d: %s%dns (%d Mb/sec) offset %d.\n", + ncr_unit (np), target, period<200 ? "FAST SCSI-2 ":"", + period, (1000+period/2)/period, sxfer & 0x0f); + } else{ + printf ("ncr%d targ %d: asynchronous.\n", + ncr_unit (np), target); + } + + /* + ** set actual value and sync_status + */ + OUTB (nc_scr3 , sxfer); + OUTB (nc_sxfer, sxfer); + + /* + ** patch ALL ccbs of this target. + */ + for (cp = &np->ccb; cp; cp = cp -> link_ccb) { + if (!cp->xfer) continue; + if (cp->xfer->TARGET != target) continue; + cp->sync_status = sxfer; + }; +} + +/*========================================================== +** +** Switch tagged mode for a target. +** +**========================================================== +*/ + +static void ncr_setmaxtags (tcb_p tp, u_long usrtags) +{ + int l; + tp->usrtags = usrtags; + for (l=0; l<MAX_LUN; l++) { + lcb_p lp; + if (!tp) break; + lp=tp->lp[l]; + if (!lp) continue; + ncr_settags (tp, lp); + }; +} + +static void ncr_settags (tcb_p tp, lcb_p lp) +{ + u_char reqtags, tmp; + + if ((!tp) || (!lp)) return; + + /* + ** only devices capable of tagges commands + ** only disk devices + ** only if enabled by user .. + */ + if (( tp->inqdata[7] & INQ7_QUEUE) && ((tp->inqdata[0] & 0x1f)==0x00) + && tp->usrtags) { + reqtags = tp->usrtags; + if (lp->actlink <= 1) + lp->usetags=reqtags; + } else { + reqtags = 1; + if (lp->actlink <= 1) + lp->usetags=0; + }; + + /* + ** don't announce more than available. + */ + tmp = lp->actccbs; + if (tmp > reqtags) tmp = reqtags; + lp->reqlink = tmp; + + /* + ** don't discard if announced. + */ + tmp = lp->actlink; + if (tmp < reqtags) tmp = reqtags; + lp->reqccbs = tmp; +} + +/*---------------------------------------------------- +** +** handle user commands +** +**---------------------------------------------------- +*/ + +static void ncr_usercmd (ncb_p np) +{ + u_char t; + tcb_p tp; + + switch (np->user.cmd) { + + case 0: return; + + case UC_SETSYNC: + for (t=0; t<MAX_TARGET; t++) { + if (!((np->user.target>>t)&1)) continue; + tp = &np->target[t]; + tp->usrsync = np->user.data; + tp->period = 0; + }; + break; + + case UC_SETTAGS: + if (np->user.data > SCSI_NCR_MAX_TAGS) + break; + for (t=0; t<MAX_TARGET; t++) { + if (!((np->user.target>>t)&1)) continue; + ncr_setmaxtags (&np->target[t], np->user.data); + }; + break; + + case UC_SETDEBUG: + ncr_debug = np->user.data; + break; + + case UC_SETORDER: + np->order = np->user.data; + break; + + } + np->user.cmd=0; +} + +/*========================================================== +** +** +** ncr timeout handler. +** +** +**========================================================== +** +** Misused to keep the driver running when +** interrupts are not configured correctly. +** +**---------------------------------------------------------- +*/ + +static void ncr_timeout (ncb_p np) +{ + u_long thistime = time.tv_sec; + u_long step = np->ticks; + u_long count = 0; + long signed t; + ccb_p cp; + + if (np->lasttime != thistime) { + np->lasttime = thistime; + + ncr_usercmd (np); + + /*---------------------------------------------------- + ** + ** handle ncr chip timeouts + ** + ** Assumption: + ** We have a chance to arbitrate for the + ** SCSI bus at least every 10 seconds. + ** + **---------------------------------------------------- + */ + + t = thistime - np->heartbeat; + + if (t<2) np->latetime=0; else np->latetime++; + + if (np->latetime>2) { + /* + ** If there are no requests, the script + ** processor will sleep on SEL_WAIT_RESEL. + ** But we have to check whether it died. + ** Let's wake it up. + */ + OUTB (nc_istat, SIGP); + }; + + if (np->latetime>10) { + /* + ** Although we tried to wakeup it, + ** the script processor didn't answer. + ** + ** May be a target is hanging, + ** or another initator lets a tape device + ** rewind with disconnect disabled :-( + ** + ** We won't accept that. + */ + printf ("ncr%d: reset by timeout.\n", ncr_unit (np)); + OUTB (nc_istat, SRST); + OUTB (nc_istat, 0); + if (INB (nc_sbcl) & CBSY) + OUTB (nc_scntl1, CRST); + ncr_init (np, NULL, HS_TIMEOUT); + np->heartbeat = thistime; + }; + + /*---------------------------------------------------- + ** + ** handle ccb timeouts + ** + **---------------------------------------------------- + */ + + for (cp=&np->ccb; cp; cp=cp->link_ccb) { + /* + ** look for timed out ccbs. + */ + if (!cp->host_status) continue; + count++; + if (cp->tlimit > thistime) continue; + + /* + ** Disable reselect. + ** Remove it from startqueue. + */ + cp->jump_ccb.l_cmd = (SCR_JUMP); + if (cp->phys.header.launch.l_paddr == + vtophys (&np->v_script->select)) { + printf ("ncr%d: timeout ccb=%x (skip)\n", + ncr_unit (np), cp); + cp->phys.header.launch.l_paddr + = vtophys (&np->v_script->skip); + }; + + switch (cp->host_status) { + + case HS_BUSY: + case HS_NEGOTIATE: + /* + ** still in start queue ? + */ + if (cp->phys.header.launch.l_paddr == + vtophys (&np->v_script->skip)) + continue; + + /* fall through */ + case HS_DISCONNECT: + cp->host_status=HS_TIMEOUT; + }; + cp->tag = 0; + + /* + ** wakeup this ccb. + */ + { + int oldspl = splbio(); + ncr_complete (np, cp); + splx (oldspl); + }; + }; + } + + timeout (TIMEOUT ncr_timeout, (caddr_t) np, step ? step : 1); + + if ((INB(nc_istat) & (INTF|SIP|DIP)) && !np->lock) { + + /* + ** Process pending interrupts. + */ + + int oldspl = splbio (); + u_long imask = getirr(); +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_TINY) printf ("{"); +#endif /* SCSI_NCR_DEBUG */ + ncr_exception (np); +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_TINY) printf ("}"); +#endif /* SCSI_NCR_DEBUG */ + imask &=~getirr(); + splx (oldspl); + + /* + ** automagically find int vector. + */ + if (imask) { + if ((imask != np->imask) && (np->mcount<100)) + np->mcount=0; + np->imask = imask; + np->mcount ++; + }; + + /* + ** a hint to the user :-) + */ + if (np->mcount==100) { + if (np->imask & (np->imask-1)) { + printf ("ncr%d: please configure intr mask %x.\n", + ncr_unit (np), np->imask); + } else { +#ifdef __NetBSD__ + if (!np->ihand.ih_fun) { + np->ihand.ih_fun = ncr_intr; + np->ihand.ih_arg = (void*)ncr_unit (np); + np->ihand.ih_level = IPL_BIO; + intr_establish(ffs (np->imask)-1, + &np->ihand); + } +#else + printf ("ncr%d: please configure intr %d.\n", + ncr_unit (np), ffs (np->imask)-1); +#endif + }; + np->mcount++; + }; + }; +} + +/*========================================================== +** +** +** ncr chip exception handler. +** +** +**========================================================== +** +** @RECOVER@ this function is not yet complete. +** +** there should be better ways to handle +** unexpected exceptions than to restart the +** script processor. +** +**---------------------------------------------------------- +*/ + +void ncr_exception (ncb_p np) +{ + u_char istat, dstat; + u_short sist; + u_long dsp; + + /* + ** interrupt on the fly ? + */ + while ((istat = INB (nc_istat)) & INTF) { +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_TINY) printf ("F"); +#endif /* SCSI_NCR_DEBUG */ + OUTB (nc_istat, INTF); + np->profile.num_fly++; + ncr_wakeup (np, 0); + }; + + if (!(istat & (SIP|DIP))) return; + dstat = INB (nc_dstat); + sist = INW (nc_sist) ; + np->profile.num_int++; + +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_TINY) + printf ("<%d|%x:%x|%x:%x>", + INB(nc_scr0), + dstat,sist, + INL(nc_dsp),INL(nc_dbc)); +#endif /* SCSI_NCR_DEBUG */ + +/*========================================================== +** +** First the normal cases. +** +**========================================================== +*/ + /*------------------------------------------- + ** SCSI reset + **------------------------------------------- + */ + + if (sist & RST) { + ncr_init (np, "scsi reset", HS_RESET); + return; + }; + + /*------------------------------------------- + ** selection timeout + ** + ** IID excluded from dstat mask! + ** (chip bug) + **------------------------------------------- + */ + + if ((sist & STO) && + !(sist & (GEN|HTH|MA|SGE|UDC|RST|PAR)) && + !(dstat & (MDPE|BF|ABRT|SIR))) { + ncr_int_sto (np); + return; + }; + + /*------------------------------------------- + ** Phase mismatch. + **------------------------------------------- + */ + + if ((sist & MA) && + !(sist & (STO|GEN|HTH|SGE|UDC|RST|PAR)) && + !(dstat & (MDPE|BF|ABRT|SIR|IID))) { + ncr_int_ma (np); + return; + }; + + /*------------------------------------------- + ** Programmed interrupt + **------------------------------------------- + */ + + if ((dstat & SIR) && + !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && + !(dstat & (MDPE|BF|ABRT|IID)) && + (INB(nc_dsps) <= 8)) { + ncr_int_sir (np); + return; + }; + + /*======================================== + ** do the register dump + **======================================== + */ + +#ifdef SCSI_NCR_DEBUG + if (!(ncr_debug & DEBUG_NODUMP)) /* @DEBUG@ */ +#endif + if (time.tv_sec - np->regtime.tv_sec>10) { + int i; + np->regtime = time; + for (i=0; i<sizeof(np->regdump); i++) + ((char*)&np->regdump)[i] = ((char*)np->reg)[i]; + np->regdump.nc_dstat = dstat; + np->regdump.nc_sist = sist; + }; + + printf ("ncr%d targ %d?: ERROR (%x:%x:%x) (%x/%x) @ (%x:%x).\n", + ncr_unit (np), INB (nc_ctest0)&7, dstat, sist, + INB (nc_sbcl), + INB (nc_sxfer),INB (nc_scr3), + dsp = INL (nc_dsp), INL (nc_dbc)); + + /*---------------------------------------- + ** clean up the dma fifo + **---------------------------------------- + */ + + if ((INB(nc_sstat0)&(ILF|ORF|OLF)) || + (INB(nc_sstat1)&0xf0) || !(dstat & DFE)) { + printf ("ncr%d: have to clear fifos.\n", ncr_unit (np)); + OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */ + OUTB (nc_ctest3, CLF); /* clear dma fifo */ + } + + /*---------------------------------------- + ** unexpected disconnect + **---------------------------------------- + */ + + if ((sist & UDC) && + !(sist & (STO|GEN|HTH|MA|SGE|RST|PAR)) && + !(dstat & (MDPE|BF|ABRT|SIR|IID))) { + OUTB (nc_scr0, HS_UNEXPECTED); + OUTL (nc_dsp, vtophys(&np->v_script->cleanup)); + return; + }; + + /*---------------------------------------- + ** cannot disconnect + **---------------------------------------- + */ + + if ((dstat & IID) && + !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && + !(dstat & (MDPE|BF|ABRT|SIR)) && + ((INL(nc_dbc) & 0xf8000000) == 0x18000000)) { + /* + ** Data cycles while waiting for disconnect. + ** Force disconnect. + */ + OUTB (nc_scntl1, 0); + /* + ** System may hang, but timeout will handle that. + ** In fact, timeout can handle ALL problems :-) + */ + OUTB (nc_dcntl, (STD|NOCOM)); + return; + }; + + /*---------------------------------------- + ** single step + **---------------------------------------- + */ + + if ((dstat & SSI) && + !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && + !(dstat & (MDPE|BF|ABRT|SIR|IID))) { + OUTB (nc_dcntl, (STD|NOCOM)); + return; + }; + +/* +** @RECOVER@ HTH, SGE, ABRT. +** +** We should try to recover from these interrupts. +** They may occur if there are problems with synch transfers, +** or if targets are powerswitched while the driver is running. +*/ + + if (sist & SGE) { + OUTB (nc_ctest3, CLF); /* clear scsi offsets */ + } + +#ifdef SCSI_NCR_DEBUG + /* + ** Freeze controller to be able to read the messages. + */ + + if (ncr_debug & DEBUG_FREEZE) { + int i; + unsigned char val; + for (i=0; i<0x60; i++) { + switch (i%16) { + + case 0: + printf ("ncr%d: reg[%d0]: ", + ncr_unit(np),i/16); + break; + case 4: + case 8: + case 12: + printf (" "); + break; + }; + val = ((unsigned char*) np->vaddr) [i]; + printf (" %x%x", val/16, val%16); + if (i%16==15) printf (".\n"); + }; + + untimeout (TIMEOUT ncr_timeout, (caddr_t) np); + + printf ("ncr%d: halted!\n", ncr_unit(np)); + /* + ** don't restart controller ... + */ + OUTB (nc_istat, SRST); + return; + }; +#endif /* SCSI_NCR_DEBUG */ + + /* + ** sorry, have to kill ALL jobs ... + */ + + ncr_init (np, "fatal error", HS_FAIL); +} + +/*========================================================== +** +** ncr chip exception handler for selection timeout +** +**========================================================== +** +** There seems to be a bug in the 53c810. +** Although a STO-Interupt is pending, +** it continues executing script commands. +** But it will fail and interrupt (IID) on +** the next instruction where it's looking +** for a valid phase. +** +**---------------------------------------------------------- +*/ + +void ncr_int_sto (ncb_p np) +{ + u_long dsa, scratcha, diff; + ccb_p cp; +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_TINY) printf ("T"); +#endif /* SCSI_NCR_DEBUG */ + + /* + ** look for ccb and set the status. + */ + + dsa= INL (nc_dsa); + cp = &np->ccb; + while (cp && (vtophys(&cp->phys)!=dsa)) cp = cp -> link_ccb; + + if (cp) { + cp-> host_status = HS_SEL_TIMEOUT; + ncr_complete (np, cp); + }; + + /* + ** repair start queue + */ + + scratcha = INL (nc_scratcha); + diff = scratcha-(np->p_script + offsetof (struct script, tryloop)); + + assert ((diff <= MAX_START * 20) && !(diff % 20)); + + if ((diff <= MAX_START * 20) && !(diff % 20)) { + np->v_script->startpos[0] = scratcha; + OUTL (nc_dsp, vtophys (&np->v_script->start)); + return; + }; + ncr_init (np, "selection timeout", HS_FAIL); +} + +/*========================================================== +** +** +** ncr chip exception handler for phase errors. +** +** +**========================================================== +** +** We have to construct a new transfer descriptor, +** to transfer the rest of the current block. +** +**---------------------------------------------------------- +*/ + +static void ncr_int_ma (ncb_p np) +{ + u_long dbc; + u_long rest; + u_long dsa; + u_long dsp; + u_long nxtdsp; + u_long *vdsp; + u_long oadr; + u_long olen; + u_long *tblp; + u_long *newcmd; + u_char cmd; + u_char sbcl; + u_char delta; + u_char ss0; + ccb_p cp,cp2; + + dsp = INL (nc_dsp); + dsa = INL (nc_dsa); + dbc = INL (nc_dbc); + ss0 = INB (nc_sstat0); + sbcl= INB (nc_sbcl); + + cmd = dbc >> 24; + rest= dbc & 0xffffff; + delta=(INB (nc_dfifo) - rest) & 0x7f; + + /* + ** The data in the dma fifo has not been transfered to + ** the target -> add the amount to the rest .. + */ + + if (! (INB(nc_dstat) & DFE)) rest += delta; + if (ss0 & OLF) rest++; + if (ss0 & ORF) rest++; + + /* + ** and clear it. + */ + + OUTB (nc_ctest3, CLF ); /* clear dma fifo */ + OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */ + + cp2 = np->header.cp; + + cp = &np->ccb; + while (cp && (vtophys(&cp->phys)!=dsa)) cp = cp -> link_ccb; + + assert (cp == cp2); + + if (dsp == vtophys (&cp->patch[2])) { + vdsp = &cp->patch[0]; + nxtdsp = vdsp[3]; + } else if (dsp == vtophys (&cp->patch[6])) { + vdsp = &cp->patch[4]; + nxtdsp = vdsp[3]; + } else { + vdsp = (u_long*) ((char*)np->v_script + dsp - np->p_script -8); + nxtdsp = dsp; + }; + +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & (DEBUG_TINY|DEBUG_PHASE)) { + printf ("P%d%d ",cmd&7, sbcl&7); + printf ("RL=%d D=%d SS0=%x ",rest,delta,ss0); + }; + if (ncr_debug & DEBUG_PHASE) { + printf ("\nCP=%x CP2=%x DSP=%x NXT=%x VDSP=%x CMD=%x ", + cp, cp2, dsp, nxtdsp, vdsp, cmd); + }; +#endif /* SCSI_NCR_DEBUG */ + + oadr = vdsp[1]; + + if (cmd & 0x10) { /* Table indirect */ + tblp = (u_long*) ((char*) &cp->phys + oadr); + olen = tblp[0]; + oadr = tblp[1]; + } else { + tblp = (u_long*) 0; + olen = vdsp[0] & 0xffffff; + }; + +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_PHASE) { + printf ("OCMD=%x\nTBLP=%x OLEN=%x OADR=%x\n", + vdsp[0] >> 24, tblp, olen, oadr); + }; +#endif /* SCSI_NCR_DEBUG */ + assert (cmd == (vdsp[0] >> 24)); + + if (cmd & 0x06) { + + printf ("ncr%d: targ %d: phase change %d-%d %d@%x resid=%d.\n", + ncr_unit (np), INB(nc_ctest0) & 7, + cmd&7, sbcl&7, olen, oadr, rest); + + OUTB (nc_dcntl, (STD|NOCOM)); + return; + }; + + newcmd = cp->patch; + if (cp->phys.header.savep == vtophys (newcmd)) newcmd+=4; + + newcmd[0] = ((cmd & 0x0f) << 24) | rest; + newcmd[1] = oadr + olen - rest; + newcmd[2] = SCR_JUMP; + newcmd[3] = nxtdsp; + +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_PHASE) + printf ("ncr%d targ %d: newcmd[%d] %x %x %x %x.\n", + ncr_unit (np), INB(nc_ctest0) & 7, + newcmd - cp->patch, + newcmd[0], newcmd[1], newcmd[2], newcmd[3]); +#endif /* SCSI_NCR_DEBUG */ + np->profile.num_break++; + OUTL (nc_temp, vtophys (newcmd)); + OUTL (nc_dsp, vtophys (&np->v_script->dispatch)); +} + +/*========================================================== +** +** +** ncr chip exception handler for programmed interrupts. +** +** +**========================================================== +*/ + +static void ncr_show_msg (u_char * msg) +{ + u_char i; + printf ("%x",*msg); + if (*msg==M_EXTENDED) { + for (i=1;i<8;i++) { + if (i-1>msg[1]) break; + printf ("-%x",msg[i]); + }; + } else if ((*msg & 0xf0) == 0x20) { + printf ("-%x",msg[1]); + } +} + +void ncr_int_sir (ncb_p np) +{ + u_char chg, ofs, per, fak; + u_char num = INB (nc_dsps); + ccb_p cp; + tcb_p tp; + u_long dsa; + u_char target = INB (nc_ctest0) & 7; + int i; +#ifdef SCSI_NCR_DEBUG + if(ncr_debug & DEBUG_TINY) printf ("I#%d", num); +#endif /* SCSI_NCR_DEBUG */ + + switch (num) { + +/*-------------------------------------------------------------------- +** +** Processing of interrupted getcc selects +** +**-------------------------------------------------------------------- +*/ + + case 1: /* + ** Script processor is idle. + ** Look for interrupted "check cond" + */ + + printf ("ncr%d: int#%d",ncr_unit (np),num); + cp = (ccb_p) 0; + for (i=0; i<MAX_TARGET; i++) { + printf (" t%d", i); + tp = &np->target[i]; + printf ("+"); + cp = tp->hold_cp; + if (!cp) continue; + printf ("+"); + if ((cp->host_status==HS_BUSY) && + (cp->scsi_status==S_CHECK_COND) && + (cp->scs2_status==S_ILLEGAL)) + break; + printf ("- (remove)"); + tp->hold_cp = cp = (ccb_p) 0; + }; + + if (cp) { + printf ("+ restart job ..\n"); + OUTL (nc_dsa, vtophys (&cp->phys)); + OUTL (nc_dsp, vtophys (&np->v_script->getcc)); + return; + }; + + /* + ** no job, resume normal processing + */ + printf (" -- remove trap\n"); + np->v_script->start[0] = SCR_INT ^ IFFALSE (0); + break; + + case 2: /* + ** While trying to reselect for + ** getting the condition code, + ** a target reselected us. + */ + + dsa= INL (nc_dsa); + printf ("ncr%d targ %d: in getcc reselect by t%d (dsa=%x).\n", + ncr_unit (np), target, INB(nc_ssid)&7, dsa); + + /* + ** lookup the ccb + */ + cp = &np->ccb; + while (cp && (vtophys(&cp->phys)!=dsa)) + cp = cp -> link_ccb; + assert (cp==np->header.cp); + + /* + ** can't happen + */ + + assert (cp); + if (!cp) break; + + /* + ** Mark this job + */ + cp->host_status = HS_BUSY; + cp->scsi_status = S_CHECK_COND; + cp->scs2_status = S_ILLEGAL; + np->target[target].hold_cp = cp; + + /* + ** And patch code to restart it. + */ + np->v_script->start[0] = SCR_INT; + break; + +/*-------------------------------------------------------------------- +** +** Negotiation of synch mode. +** +** Possible cases: int msg_in[0] sxfer send goto +** We try try to negotiate: +** -> target doesnt't msgin (3) noop ASYNC - - +** -> target rejected our msg (3) reject ASYNC - - +** -> target answered (ok) (3) sdtr set - clrack +** -> target answered (!ok) (3) sdtr ASYNC REJ--->msg_bad +** -> any other msgin - +** +** Target tries to negotiate: +** -> incoming message (4) sdtr set SDTR - +** We sent our answer: +** -> target doesn't msgout (4) reject* ASYNC - - +** -> target rejected our msg (4) reject ASYNC - - +** -> target negotiates again (4) sdtr set SDTR - +** +**-------------------------------------------------------------------- +*/ + case 3: + case 4: +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_SDTR) { + printf ("ncr%d targ %d: sync msg in: ", + ncr_unit (np), target); + ncr_show_msg (np->msgin); + printf (".\n"); + }; +#endif /* SCSI_NCR_DEBUG */ + /* + ** @CHECKOUT@ + */ + dsa= INL (nc_dsa); + cp = &np->ccb; + while (cp && (vtophys(&cp->phys)!=dsa)) cp = cp -> link_ccb; + assert (cp==np->header.cp); + tp = &np->target[target]; + + /* + ** any error in negotiation: + ** fall back to asynch. + */ + + if ((np->msgin[0]!=M_EXTENDED) || + (np->msgin[1]!=3) || + (np->msgin[2]!=M_X_SDTR)) { + np->msgin [0] = M_NOOP; + ncr_setsync (np, cp, 0xe0); + break; + } + + per = np->msgin[3]; + ofs = np->msgin[4]; + + /* + ** if target sends SDTR message, + ** it CAN transfer synch. + */ + + if (ofs) + tp->inqdata[7] |= INQ7_SYNC; + + /*------------------------------------------------ + ** do actual computation. + **------------------------------------------------ + */ + chg = 0; + + if (ofs==0) per=255; + if (per < np->ns_sync) {chg = 1; per = np->ns_sync;} + if (per < tp->minsync) + {chg = 1; per = tp->minsync;} + if (ofs > tp->maxoffs) + {chg = 1; ofs = 8;} + fak = (4ul * per - 1) / np->ns_sync - 3; + + if (ofs && (fak>7)) {chg = 1; ofs = 0;} + +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_SDTR) + printf ("ncr%d targ %d: sync: per=%d ofs=%d fak=%d chg=%d.\n", + ncr_unit (np), target, per, ofs, fak, chg); +#endif /* SCSI_NCR_DEBUG */ + + /* + ** if the answer had bad values, + ** we will use asynch mode. + */ + + if ((num == 3) && chg) ofs = 0; + if (!ofs) fak=7; + + /* + ** Set synchronous mode now. + */ + ncr_setsync (np, cp, (fak<<5)|ofs); + + if (num == 3) { + if (chg) OUTL (nc_dsp,vtophys (&np->v_script->msg_bad)); + else OUTL (nc_dsp,vtophys (&np->v_script->clrack)); + return; + }; + + /*------------------------------------------------ + ** prepare an answer message + **------------------------------------------------ + */ + + np->msgout[0] = M_EXTENDED; + np->msgout[1] = 3; + np->msgout[2] = M_X_SDTR; + np->msgout[3] = per; + np->msgout[4] = ofs; + + np->msgin [0] = M_NOOP; + +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_SDTR) { + printf ("ncr%d targ %d: sync msgout: ", + ncr_unit (np), target); + ncr_show_msg (np->msgin); + printf (".\n"); + } +#endif /* SCSI_NCR_DEBUG */ + break; + + +/*-------------------------------------------------------------------- +** +** Processing of special messages +** +**-------------------------------------------------------------------- +*/ + + case 5: /* + ** We received a M_REJECT message. + */ + printf ("ncr%d targ %d: M_REJECT received (%x:%x).\n", + ncr_unit (np), target, ncr_msgout, np->msgout[0]); + break; + + case 6: /* + ** We received an unknown message + */ + printf ("ncr%d targ %d: M_REJECT sent for ", + ncr_unit (np), target); + ncr_show_msg (np->msgin); + printf (".\n"); + break; + +/*-------------------------------------------------------------------- +** +** Processing of a "S_QUEUE_FULL" status. +** +** The current command has been rejected. +** We have started too many commands for that target. +** +** If possible, reinsert at head of queue. +** Stall queue until there are no disconnected jobs +** (ncr REALLY idle). Then restart processing. +** +**-------------------------------------------------------------------- +*/ + case 8: /* + ** Stall the start queue. + */ + + printf ("ncr%d targ %d: queue full.\n", + ncr_unit (np), target); + np->v_script->start1[0] = SCR_INT; + + /* + ** Try to disable tagged transfers. + */ + ncr_setmaxtags (&np->target[target], 0); + + /* + ** @QUEUE@ reinsert current job in queue. + */ + + /* fall through */ + + case 7: /* + ** Look for a disconnected job. + */ + + cp = &np->ccb; + while (cp && cp->host_status != HS_DISCONNECT) + cp = cp -> link_ccb; + + /* + ** if there is one, ... + */ + if (cp) { + /* + ** wait for reselection + */ + OUTL (nc_dsp,vtophys (&np->v_script->reselect)); + return; + }; + + /* + ** else remove the interrupt. + */ + + printf ("ncr%d: queue empty.\n", ncr_unit (np)); + np->v_script->start1[0] = SCR_INT ^ IFFALSE (0); + break; + + }; + OUTB (nc_dcntl, (STD|NOCOM)); +} + +/*========================================================== +** +** +** Aquire a control block +** +** +**========================================================== +*/ + +static ccb_p ncr_get_ccb + (ncb_p np, u_long flags, u_long target, u_long lun) +{ + lcb_p lp; + ccb_p cp = (ccb_p ) 0; + + /* + ** Lun structure available ? + */ + + lp = np->target[target].lp[lun]; + if (lp) + cp = lp->next_ccb; + + /* + ** Look for free CCB + */ + + while (cp && cp->magic) cp = cp->next_ccb; + + /* + ** if nothing available, take the default. + */ + + if (!cp) cp = &np->ccb; + + /* + ** Wait until available. + */ + + while (cp->magic) { + if (flags & SCSI_NOSLEEP) break; + if (tsleep ((caddr_t)cp, PZERO|PCATCH, "ncr", 0)) + break; + }; + + if (cp->magic) + return ((ccb_p) 0); + + cp->magic = 1; + return (cp); +} + +/*========================================================== +** +** +** Release one control block +** +** +**========================================================== +*/ + +void ncr_free_ccb (ncb_p np, ccb_p cp, int flags) +{ + /* + ** sanity + */ + + if (!cp) return; + + cp -> host_status = HS_IDLE; + cp -> magic = 0; + if (cp == &np->ccb) + wakeup ((caddr_t) cp); +} + +/*========================================================== +** +** +** Allocation of resources for Targets/Luns/Tags. +** +** +**========================================================== +*/ + +static void ncr_alloc_ccb (ncb_p np, struct scsi_xfer * xp) +{ + tcb_p tp; + lcb_p lp; + ccb_p cp; + + u_long target; + u_long lun; + + if (!np) return; + if (!xp) return; + + target = xp->TARGET; + lun = xp->LUN; + + if (target>=MAX_TARGET) return; + if (lun >=MAX_LUN ) return; + + /* + ** target control block ? + */ + tp=&np->target[target]; + + if (!tp->jump_tcb.l_cmd) { + + /* + ** initialize it. + */ + tp->jump_tcb.l_cmd = (SCR_JUMP^IFFALSE (DATA (0x80 + target))); + tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr; + + tp->getscr[0] = SCR_COPY (1); + tp->getscr[1] = vtophys (&tp->sval); + tp->getscr[2] = np->paddr + offsetof (struct ncr_reg, nc_sxfer); + + assert (( (offsetof(struct ncr_reg, nc_sxfer) ^ + offsetof(struct tcb , sval )) &3) == 0); + + tp->call_lun.l_cmd = (SCR_CALL); + tp->call_lun.l_paddr = vtophys (&np->v_script->resel_lun); + + tp->jump_lcb.l_cmd = (SCR_JUMP); + tp->jump_lcb.l_paddr = vtophys (&np->v_script->abort); + + np->jump_tcb.l_paddr = vtophys (&tp->jump_tcb); + } + + /* + ** Logic unit control block + */ + lp = tp->lp[lun]; + if (!lp) { + /* + ** Allocate a lcb + */ + lp = (lcb_p) malloc (sizeof (struct lcb), M_DEVBUF, M_NOWAIT); + if (!lp) return; + + /* + ** Initialize it + */ + bzero (lp, sizeof (*lp)); + lp->jump_lcb.l_cmd = (SCR_JUMP ^ IFFALSE (DATA (lun))); + lp->jump_lcb.l_paddr = tp->jump_lcb.l_paddr; + + lp->call_tag.l_cmd = (SCR_CALL); + lp->call_tag.l_paddr = vtophys (&np->v_script->resel_tag); + + lp->jump_ccb.l_cmd = (SCR_JUMP); + lp->jump_ccb.l_paddr = vtophys (&np->v_script->aborttag); + + lp->actlink = 1; + /* + ** Link into Lun-Chain + */ + + tp->jump_lcb.l_paddr = vtophys (&lp->jump_lcb); + tp->lp[lun] = lp; + + } + + /* + ** Limit possible number of ccbs. + ** + ** If tagged command queueing is enabled, + ** can use more than one ccb. + */ + + if (np->actccbs >= MAX_START-2) return; + if (lp->actccbs && (lp->actccbs >= lp->reqccbs)) + return; + + /* + ** Allocate a ccb + */ + cp = (ccb_p) malloc (sizeof (struct ccb), M_DEVBUF, M_NOWAIT); + + if (!cp) + return; + +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_ALLOC) + printf ("ncr%d targ %d lun %d: new ccb @%x.\n", + ncr_unit (np), target, lun, cp); +#endif /* SCSI_NCR_DEBUG */ + + /* + ** Count it + */ + lp->actccbs++; + np->actccbs++; + + /* + ** Initialize it. + */ + bzero (cp, sizeof (*cp)); + + /* + ** link in reselect chain. + */ + cp->jump_ccb.l_cmd = SCR_JUMP; + cp->jump_ccb.l_paddr = lp->jump_ccb.l_paddr; + lp->jump_ccb.l_paddr = vtophys(&cp->jump_ccb); + cp->call_tmp.l_cmd = SCR_CALL; + cp->call_tmp.l_paddr = vtophys(&np->v_script->resel_tmp); + + /* + ** link in wakeup chain + */ + cp->link_ccb = np->ccb.link_ccb; + np->ccb.link_ccb = cp; + + /* + ** Link into CCB-Chain + */ + cp->next_ccb = lp->next_ccb; + lp->next_ccb = cp; +} + +/*========================================================== +** +** +** Announce the number of ccbs/tags to the scsi driver. +** +** +**========================================================== +*/ + +static void ncr_opennings (ncb_p np, lcb_p lp, struct scsi_xfer * xp) +{ +#ifndef ANCIENT + /* + ** want to reduce the number ... + */ + if (lp->actlink > lp->reqlink) { + + /* + ** Try to reduce the count. + ** We assumed to run at splbio .. + */ + u_char diff = lp->actlink - lp->reqlink; + + if (!diff) return; + + if (diff > xp->sc_link->opennings) + diff = xp->sc_link->opennings; + + /* + ** reduce it. + */ + + xp->sc_link->opennings -= diff; + lp->actlink -= diff; +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_TAGS) + printf ("ncr%d: actlink: diff=%d, new=%d, req=%d\n", + ncr_unit(np), diff, lp->actlink, lp->reqlink); +#endif /* SCSI_NCR_DEBUG */ + return; + }; + + /* + ** want to increase the number ? + */ + if (lp->reqlink > lp->actlink) { + u_char diff = lp->reqlink - lp->actlink; + + xp->sc_link->opennings += diff; + lp->actlink += diff; + wakeup ((caddr_t) xp->sc_link); +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_TAGS) + printf ("ncr: actlink: diff=%d, new=%d, req=%d\n", + ncr_unit(np), diff, lp->actlink, lp->reqlink); +#endif + }; +#endif +} + +/*========================================================== +** +** +** Build Scatter Gather Block +** +** +**========================================================== +** +** The transfer area may be scattered among +** several non adjacent physical pages. +** +** We may use MAX_SCATTER blocks. +** +**---------------------------------------------------------- +*/ + +static u_long ncr_scatter + (struct dsb* phys, vm_offset_t vaddr, vm_size_t datalen) +{ + u_long paddr, pnext; + + u_short segment = 0; + u_long segsize, segaddr; + u_long size, csize = 0; + u_long chunk = MAX_SIZE; + int free; + + bzero (&phys->data, sizeof (phys->data)); + if (!datalen) return (0); + + paddr = vtophys (vaddr); + + /* + ** insert extra break points at a distance of chunk. + ** We try to reduce the number of interrupts due to + ** unexpected phase changes due to disconnects. + ** A typical harddisk may disconnect before ANY block. + ** If we want to avoid unexpected phase changes at all + ** we have to use a break point every 512 bytes. + ** Of course the number of scatter/gather blocks is + ** limited. + */ + + free = MAX_SCATTER - 1; + + if (vaddr & (NBPG-1)) free -= datalen / NBPG; + + if (free>1) + while ((chunk * free >= 2 * datalen) && (chunk>=1024)) + chunk /= 2; + +#ifdef SCSI_NCR_DEBUG + if(ncr_debug & DEBUG_SCATTER) + printf("ncr?:\tscattering virtual=0x%x size=%d chunk=%d.\n", + (u_long) vaddr, (u_long) datalen, chunk); +#endif /* SCSI_NCR_DEBUG */ + + /* + ** Build data descriptors. + */ + while (datalen && (segment < MAX_SCATTER)) { + + /* + ** this segment is empty + */ + segsize = 0; + segaddr = paddr; + pnext = paddr; + + if (!csize) csize = chunk; + + while ((datalen) && (paddr == pnext) && (csize)) { + + /* + ** continue this segment + */ + pnext = (paddr & (~(NBPG - 1))) + NBPG; + + /* + ** Compute max size + */ + + size = pnext - paddr; /* page size */ + if (size > datalen) size = datalen; /* data size */ + if (size > csize ) size = csize ; /* chunksize */ + + segsize += size; + vaddr += size; + csize -= size; + datalen -= size; + paddr = vtophys (vaddr); + }; + +#ifdef SCSI_NCR_DEBUG + if(ncr_debug & DEBUG_SCATTER) + printf ("\tseg #%d addr=%x size=%d (rest=%d).\n", + segment, segaddr, segsize, datalen); +#endif /* SCSI_NCR_DEBUG */ + + phys->data[segment].addr = segaddr; + phys->data[segment].size = segsize; + segment++; + } + + if (datalen) + printf("ncr?: scatter/gather failed (residue=%d).\n", + datalen); + + return (datalen); +} + +/*========================================================== +** +** +** Profiling the drivers and targets performance. +** +** +**========================================================== +*/ + +/* +** Compute the difference in milliseconds. +**/ + +static int ncr_delta (struct timeval * from, struct timeval * to) +{ + if (!from->tv_sec) return (-1); + if (!to ->tv_sec) return (-2); + return ( (to->tv_sec - from->tv_sec - 2)*1000+ + +(to->tv_usec - from->tv_usec + 2000000)/1000); +} + +#define PROFILE cp->phys.header.stamp +static void ncb_profile (ncb_p np, ccb_p cp) +{ + int co, da, st, en, di, se, post,work,disc; + u_long diff; + + PROFILE.end = time; + + st = ncr_delta (&PROFILE.start,&PROFILE.status); + if (st<0) return; /* status not reached */ + + da = ncr_delta (&PROFILE.start,&PROFILE.data); + if (da<0) return; /* No data transfer phase */ + + co = ncr_delta (&PROFILE.start,&PROFILE.command); + if (co<0) return; /* command not executed */ + + en = ncr_delta (&PROFILE.start,&PROFILE.end), + di = ncr_delta (&PROFILE.start,&PROFILE.disconnect), + se = ncr_delta (&PROFILE.start,&PROFILE.select); + post = en - st; + + /* + ** @PROFILE@ Disconnect time invalid if multiple disconnects + */ + + if (di>=0) disc = se-di; else disc = 0; + + work = (st - co) - disc; + + diff = (np->disc_phys - np->disc_ref) & 0xff; + np->disc_ref += diff; + + np->profile.num_trans += 1; + if (cp->xfer) + np->profile.num_bytes += cp->xfer->datalen; + np->profile.num_disc += diff; + np->profile.ms_setup += co; + np->profile.ms_data += work; + np->profile.ms_disc += disc; + np->profile.ms_post += post; +} +#undef PROFILE + +/*========================================================== +** +** Determine the ncr's clock frequency. +** This is important for the negotiation +** of the synchronous transfer rate. +** +**========================================================== +** +** Note: we have to return the correct value. +** THERE IS NO SAVE DEFAULT VALUE. +** +** We assume that all NCR based boards are delivered +** with a 40Mhz clock. Because we have to divide +** by an integer value greater than 3, only clock +** frequencies of 40Mhz (/4) or 50MHz (/5) permit +** the FAST-SCSI rate of 10MHz. +** +**---------------------------------------------------------- +*/ + +#ifndef NCR_CLOCK +# define NCR_CLOCK 40 +#endif /* NCR_CLOCK */ + + +static void ncr_getclock (ncb_p np) +{ + u_char tbl[5] = {6,2,3,4,6}; + u_char f; + u_char ns_clock = (1000/NCR_CLOCK); + + /* + ** Compute the best value for scntl3. + */ + + f = (2 * MIN_SYNC_PD - 1) / ns_clock; + if (!f ) f=1; + if (f>4) f=4; + np -> ns_sync = (ns_clock * tbl[f]) / 2; + np -> rv_scntl3 = f<<4; + + f = (2 * MIN_ASYNC_PD - 1) / ns_clock; + if (!f ) f=1; + if (f>4) f=4; + np -> ns_async = (ns_clock * tbl[f]) / 2; + np -> rv_scntl3 |= f; +#ifdef SCSI_NCR_DEBUG + if (ncr_debug & DEBUG_TIMING) + printf ("ncr%d: sclk=%d async=%d sync=%d (ns) scntl3=0x%x\n", + ncr_unit (np), ns_clock, np->ns_async, np->ns_sync, np->rv_scntl3); +#endif /* SCSI_NCR_DEBUG */ +} + +/*=========================================================================*/ +#endif /* KERNEL */ +#endif /* NNCR */ diff --git a/sys/i386/pci/ncr_reg.h b/sys/i386/pci/ncr_reg.h new file mode 100644 index 000000000000..917dc14ab097 --- /dev/null +++ b/sys/i386/pci/ncr_reg.h @@ -0,0 +1,489 @@ +/************************************************************************** +** +** $Id: ncr_reg.h,v 2.0.0.3 94/07/24 08:59:19 wolf Exp $ +** +** #define the NCR 53 C 810 Register Structure +** #define the NCR 53 C 810 Scripts Language +** +**------------------------------------------------------------------------- +** +** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany +** <wolf@dentaro.GUN.de> +** +** This is a beta version - use with care. +** +**------------------------------------------------------------------------- +** +** $Log: ncr_reg.h,v $ +** Revision 2.0.0.3 94/07/24 08:59:19 wolf +** bits of sstat0 defined. +** +** Revision 2.0 94/07/10 15:53:27 wolf +** FreeBSD release. +** +** Revision 1.0 94/06/07 20:02:21 wolf +** Beta release. +** +*************************************************************************** +*/ + +#ifndef __NCR_REG_H__ +#define __NCR_REG_H__ + + +/*----------------------------------------------------------------- +** +** The ncr 53c810 register structure. +** +**----------------------------------------------------------------- +*/ + +struct ncr_reg { +/*00*/ u_char nc_scntl0; /* full arb., ena parity, par->ATN */ +/*01*/ u_char nc_scntl1; /* no reset */ + #define ISCON 0x10 /* connected to scsi */ + #define CRST 0x08 /* force reset */ +/*02*/ u_char nc_scntl2; /* no disconnect expected */ +/*03*/ u_char nc_scntl3; /* cnf system clock dependent */ +/*04*/ u_char nc_scid; /* cnf host adapter scsi address */ + #define RRE 0x40 /* r/w:e enable response to resel. */ + #define SRE 0x20 /* r/w:e enable response to select */ +/*05*/ u_char nc_sxfer; /* ### Sync speed and count */ +/*06*/ u_char nc_sdid; /* ### Destination-ID */ +/*07*/ u_char nc_gpreg; /* ??? IO-Pins */ +/*08*/ u_char nc_sfbr; /* ### First byte in phase */ +/*09*/ u_char nc_socl; + #define CREQ 0x80 /* r/w: SCSI-REQ */ + #define CACK 0x40 /* r/w: SCSI-ACK */ + #define CBSY 0x20 /* r/w: SCSI-BSY */ + #define CSEL 0x10 /* r/w: SCSI-SEL */ + #define CATN 0x08 /* r/w: SCSI-ATN */ + #define CMSG 0x04 /* r/w: SCSI-MSG */ + #define CC_D 0x02 /* r/w: SCSI-C_D */ + #define CI_O 0x01 /* r/w: SCSI-I_O */ +/*0a*/ u_char nc_ssid; +/*0b*/ u_char nc_sbcl; +/*0c*/ u_char nc_dstat; + #define DFE 0x80 /* sta: dma fifo empty */ + #define MDPE 0x40 /* int: master data parity error */ + #define BF 0x20 /* int: script: bus fault */ + #define ABRT 0x10 /* int: script: command aborted */ + #define SSI 0x08 /* int: script: single step */ + #define SIR 0x04 /* int: script: interrupt instruct. */ + #define IID 0x01 /* int: script: illegal instruct. */ +/*0d*/ u_char nc_sstat0; + #define ILF 0x80 /* sta: data in SIDL register */ + #define ORF 0x40 /* sta: data in SODR register */ + #define OLF 0x20 /* sta: data in SODL register */ + #define AIP 0x10 /* sta: arbitration in progress */ + #define LOA 0x08 /* sta: arbitration lost */ + #define WOA 0x04 /* sta: arbitration won */ + #define IRST 0x02 /* sta: scsi reset signal */ + #define SDP 0x01 /* sta: scsi parity signal */ +/*0e*/ u_char nc_sstat1; +/*0f*/ u_char nc_sstat2; + +/*10*/ u_long nc_dsa; /* --> Base page */ +/*14*/ u_char nc_istat; /* --> Main Command and status */ + #define CABRT 0x80 /* cmd: abort current operation */ + #define SRST 0x40 /* mod: reset chip */ + #define SIGP 0x20 /* r/w: message from host to ncr */ + #define SEM 0x10 /* r/w: message between host + ncr */ + #define CON 0x08 /* sta: connected to scsi */ + #define INTF 0x04 /* sta: int on the fly (reset by wr)*/ + #define SIP 0x02 /* sta: scsi-interupt */ + #define DIP 0x01 /* sta: host/script interupt */ +/*15*/ u_char nc_15_; +/*16*/ u_char nc_16_; +/*17*/ u_char nc_17_; +/*18*/ u_char nc_ctest0; +/*19*/ u_char nc_ctest1; +/*1a*/ u_char nc_ctest2; + #define CSIGP 0x40 +/*1b*/ u_char nc_ctest3; + #define CLF 0x04 /* clear scsi fifo */ +/*1c*/ u_long nc_temp; /* ### Temporary stack */ +/*20*/ u_char nc_dfifo; +/*21*/ u_char nc_ctest4; +/*22*/ u_char nc_ctest5; +/*23*/ u_char nc_ctest6; +/*24*/ u_long nc_dbc; /* ### Byte count and command */ +/*28*/ u_long nc_dnad; /* ### Next command register */ +/*2c*/ u_long nc_dsp; /* --> Script Pointer */ +/*30*/ u_long nc_dsps; /* --> Script pointer save/opcode#2 */ +/*34*/ u_long nc_scratcha; /* ??? Temporary register a */ +/*38*/ u_char nc_dmode; +/*39*/ u_char nc_dien; +/*3a*/ u_char nc_dwt; +/*3b*/ u_char nc_dcntl; /* --> Script execution control */ + #define SSM 0x10 /* mod: single step mode */ + #define STD 0x04 /* cmd: start dma mode */ + #define NOCOM 0x01 /* cmd: protect sfbr while reselect */ +/*3c*/ u_long nc_adder; + +/*40*/ u_short nc_sien; /* -->: interupt enable */ +/*42*/ u_short nc_sist; /* <--: interupt status */ + #define STO 0x0400/* sta: timeout (select) */ + #define GEN 0x0200/* sta: timeout (general) */ + #define HTH 0x0100/* sta: timeout (handshake) */ + #define MA 0x80 /* sta: phase mismatch */ + #define CMP 0x40 /* sta: arbitration complete */ + #define SEL 0x20 /* sta: selected by another device */ + #define RSL 0x10 /* sta: reselected by another device*/ + #define SGE 0x08 /* sta: gross error (over/underflow)*/ + #define UDC 0x04 /* sta: unexpected disconnect */ + #define RST 0x02 /* sta: scsi bus reset detected */ + #define PAR 0x01 /* sta: scsi parity error */ +/*44*/ u_char nc_slpar; +/*45*/ u_char nc_45_; +/*46*/ u_char nc_macntl; +/*47*/ u_char nc_gpcntl; +/*48*/ u_char nc_stime0; /* cmd: timeout for select&handshake*/ +/*49*/ u_char nc_stime1; /* cmd: timeout user defined */ +/*4a*/ u_char nc_respid; /* sta: Reselect-IDs */ +/*4b*/ u_char nc_4b_; +/*4c*/ u_char nc_stest0; +/*4d*/ u_char nc_stest1; +/*4e*/ u_char nc_stest2; + #define ROF 0x40 /* reset scsi offset (after gross error!) */ + #define EXT 0x02 /* extended filtering */ +/*4f*/ u_char nc_stest3; + #define TE 0x80 /* c: tolerAnt enable */ + #define CSF 0x02 /* c: clear scsi fifo */ +/*50*/ u_char nc_sidl; /* Lowlevel: latched from scsi data */ +/*51*/ u_char nc_51_; +/*52*/ u_char nc_52_; +/*53*/ u_char nc_53_; +/*54*/ u_char nc_sodl; /* Lowlevel: data out to scsi data */ +/*55*/ u_char nc_55_; +/*56*/ u_char nc_56_; +/*57*/ u_char nc_57_; +/*58*/ u_char nc_sbdl; /* Lowlevel: data from scsi data */ +/*59*/ u_char nc_59_; +/*5a*/ u_char nc_5a_; +/*5b*/ u_char nc_5b_; +/*5c*/ u_char nc_scr0; /* Working register B */ +/*5d*/ u_char nc_scr1; /* */ +/*5e*/ u_char nc_scr2; /* */ +/*5f*/ u_char nc_scr3; /* */ +/*60*/ +}; + +/*----------------------------------------------------------- +** +** Utility macros for the script. +** +**----------------------------------------------------------- +*/ + +#define REGJ(p,r) (offsetof(struct ncr_reg, p ## r)) +#define REG(r) REGJ (nc_, r) + +#ifndef TARGET_MODE +#define TARGET_MODE 0 +#endif + +typedef unsigned long ncrcmd; + +/*----------------------------------------------------------- +** +** SCSI phases +** +**----------------------------------------------------------- +*/ + +#define SCR_DATA_OUT 0x00000000 +#define SCR_DATA_IN 0x01000000 +#define SCR_COMMAND 0x02000000 +#define SCR_STATUS 0x03000000 +#define SCR_ILG_OUT 0x04000000 +#define SCR_ILG_IN 0x05000000 +#define SCR_MSG_OUT 0x06000000 +#define SCR_MSG_IN 0x07000000 + +/*----------------------------------------------------------- +** +** Data transfer via SCSI. +** +**----------------------------------------------------------- +** +** MOVE_ABS (LEN) +** <<start address>> +** +** MOVE_IND (LEN) +** <<dnad_offset>> +** +** MOVE_TBL +** <<dnad_offset>> +** +**----------------------------------------------------------- +*/ + +#define SCR_MOVE_ABS(l) ((0x08000000 ^ (TARGET_MODE << 1ul)) | (l)) +#define SCR_MOVE_IND(l) ((0x28000000 ^ (TARGET_MODE << 1ul)) | (l)) +#define SCR_MOVE_TBL (0x18000000 ^ (TARGET_MODE << 1ul)) + +struct scr_tblmove { + u_long size; + u_long addr; +}; + +/*----------------------------------------------------------- +** +** Selection +** +**----------------------------------------------------------- +** +** SEL_ABS | SCR_ID (0..7) [ | REL_JMP] +** <<alternate_address>> +** +** SEL_TBL | << dnad_offset>> [ | REL_JMP] +** <<alternate_address>> +** +**----------------------------------------------------------- +*/ + +#define SCR_SEL_ABS 0x40000000 +#define SCR_SEL_ABS_ATN 0x41000000 +#define SCR_SEL_TBL 0x42000000 +#define SCR_SEL_TBL_ATN 0x43000000 + +struct scr_tblsel { + u_char sel_0; + u_char sel_sxfer; + u_char sel_id; + u_char sel_scntl3; +}; + +#define SCR_JMP_REL 0x04000000 +#define SCR_ID(id) (((u_long)(id)) << 16) + +/*----------------------------------------------------------- +** +** Waiting for Disconnect or Reselect +** +**----------------------------------------------------------- +** +** WAIT_DISC +** dummy: <<alternate_address>> +** +** WAIT_RESEL +** <<alternate_address>> +** +**----------------------------------------------------------- +*/ + +#define SCR_WAIT_DISC 0x48000000 +#define SCR_WAIT_RESEL 0x50000000 + +/*----------------------------------------------------------- +** +** Bit Set / Reset +** +**----------------------------------------------------------- +** +** SET (flags {|.. }) +** +** CLR (flags {|.. }) +** +**----------------------------------------------------------- +*/ + +#define SCR_SET(f) (0x58000000 | (f)) +#define SCR_CLR(f) (0x60000000 | (f)) + +#define SCR_CARRY 0x00000400 +#define SCR_TRG 0x00000200 +#define SCR_ACK 0x00000040 +#define SCR_ATN 0x00000008 + + + + +/*----------------------------------------------------------- +** +** Memory to memory move +** +**----------------------------------------------------------- +** +** COPY (bytecount) +** << source_address >> +** << destination_address >> +** +**----------------------------------------------------------- +*/ + +#define SCR_COPY(n) (0xc0000000 | (n)) + +/*----------------------------------------------------------- +** +** Register move and binary operations +** +**----------------------------------------------------------- +** +** SFBR_REG (reg, op, data) reg = SFBR op data +** << 0 >> +** +** REG_SFBR (reg, op, data) SFBR = reg op data +** << 0 >> +** +** REG_REG (reg, op, data) reg = reg op data +** << 0 >> +** +**----------------------------------------------------------- +*/ + +#define SCR_SFBR_REG(reg,op,data) \ + (0x68000000 | (REG(reg) << 16ul) | (op) | ((data)<<8ul)) + +#define SCR_REG_SFBR(reg,op,data) \ + (0x70000000 | (REG(reg) << 16ul) | (op) | ((data)<<8ul)) + +#define SCR_REG_REG(reg,op,data) \ + (0x78000000 | (REG(reg) << 16ul) | (op) | ((data)<<8ul)) + +#define SCR_LOAD 0x00000000 +#define SCR_SHL 0x01000000 +#define SCR_OR 0x02000000 +#define SCR_XOR 0x03000000 +#define SCR_AND 0x04000000 +#define SCR_SHR 0x05000000 +#define SCR_ADD 0x06000000 +#define SCR_ADDC 0x07000000 + +/*----------------------------------------------------------- +** +** FROM_REG (reg) reg = SFBR +** << 0 >> +** +** TO_REG (reg) SFBR = reg +** << 0 >> +** +** LOAD_REG (reg, data) reg = <data> +** << 0 >> +** +** LOAD_SFBR(data) SFBR = <data> +** << 0 >> +** +**----------------------------------------------------------- +*/ + +#define SCR_FROM_REG(reg) \ + SCR_REG_SFBR(reg,SCR_OR,0) + +#define SCR_TO_REG(reg) \ + SCR_SFBR_REG(reg,SCR_OR,0) + +#define SCR_LOAD_REG(reg,data) \ + SCR_REG_REG(reg,SCR_LOAD,data) + +#define SCR_LOAD_SFBR(data) \ + (SCR_REG_SFBR (gpreg, SCR_LOAD, data)) + +/*----------------------------------------------------------- +** +** Waiting for Disconnect or Reselect +** +**----------------------------------------------------------- +** +** JUMP [ | IFTRUE/IFFALSE ( ... ) ] +** <<address>> +** +** JUMPR [ | IFTRUE/IFFALSE ( ... ) ] +** <<distance>> +** +** CALL [ | IFTRUE/IFFALSE ( ... ) ] +** <<address>> +** +** CALLR [ | IFTRUE/IFFALSE ( ... ) ] +** <<distance>> +** +** RETURN [ | IFTRUE/IFFALSE ( ... ) ] +** <<dummy>> +** +** INT [ | IFTRUE/IFFALSE ( ... ) ] +** <<ident>> +** +** INT_FLY [ | IFTRUE/IFFALSE ( ... ) ] +** <<ident>> +** +** Conditions: +** WHEN (phase) +** IF (phase) +** CARRY +** DATA (data, mask) +** +**----------------------------------------------------------- +*/ + +#define SCR_JUMP 0x80080000 +#define SCR_JUMPR 0x80880000 +#define SCR_CALL 0x88080000 +#define SCR_CALLR 0x88880000 +#define SCR_RETURN 0x90080000 +#define SCR_INT 0x98080000 +#define SCR_INT_FLY 0x98180000 + +#define IFFALSE(arg) (0x00080000 | (arg)) +#define IFTRUE(arg) (0x00000000 | (arg)) + +#define WHEN(phase) (0x00030000 | (phase)) +#define IF(phase) (0x00020000 | (phase)) + +#define DATA(D) (0x00040000 | ((D) & 0xff)) +#define MASK(D,M) (0x00040000 | (((M ^ 0xff) & 0xff) << 8ul)|((D) & 0xff)) + +#define CARRYSET (0x00200000) + +/*----------------------------------------------------------- +** +** SCSI constants. +** +**----------------------------------------------------------- +*/ + +/* +** Messages +*/ + +#define M_COMPLETE (0x00) +#define M_EXTENDED (0x01) +#define M_SAVE_DP (0x02) +#define M_RESTORE_DP (0x03) +#define M_DISCONNECT (0x04) +#define M_ID_ERROR (0x05) +#define M_ABORT (0x06) +#define M_REJECT (0x07) +#define M_NOOP (0x08) +#define M_PARITY (0x09) +#define M_LCOMPLETE (0x0a) +#define M_FCOMPLETE (0x0b) +#define M_RESET (0x0c) +#define M_ABORT_TAG (0x0d) +#define M_CLEAR_QUEUE (0x0e) +#define M_INIT_REC (0x0f) +#define M_REL_REC (0x10) +#define M_TERMINATE (0x11) +#define M_SIMPLE_TAG (0x20) +#define M_HEAD_TAG (0x21) +#define M_ORDERED_TAG (0x22) +#define M_IDENTIFY (0x80) + +#define M_X_SDTR (0x01) + +/* +** Status +*/ + +#define S_GOOD (0x00) +#define S_CHECK_COND (0x02) +#define S_COND_MET (0x04) +#define S_BUSY (0x08) +#define S_INT (0x10) +#define S_INT_COND_MET (0x14) +#define S_CONFLICT (0x18) +#define S_TERMINATED (0x20) +#define S_QUEUE_FULL (0x28) +#define S_ILLEGAL (0xff) + +#endif /*__NCR_REG_H__*/ diff --git a/sys/i386/pci/pci.c b/sys/i386/pci/pci.c new file mode 100644 index 000000000000..878c97c68165 --- /dev/null +++ b/sys/i386/pci/pci.c @@ -0,0 +1,494 @@ +/************************************************************************** +** +** $Id: pci.c,v 2.0.0.1 94/07/19 19:06:44 wolf Exp $ +** +** General subroutines for the PCI bus on 80*86 systems. +** pci_configure () +** +**------------------------------------------------------------------------- +** +** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany +** <wolf@dentaro.GUN.de> +** +** This is a beta version - use with care. +** +**------------------------------------------------------------------------- +** +** $Log: pci.c,v $ +** Revision 2.0.0.1 94/07/19 19:06:44 wolf +** New vendor entry: MATROX +** +** Revision 2.0 94/07/10 15:53:29 wolf +** FreeBSD release. +** +** Revision 1.0 94/06/07 20:02:19 wolf +** Beta release. +** +*************************************************************************** +*/ + +#include <pci.h> +#if NPCI > 0 + +/*======================================================== +** +** Configuration +** +**======================================================== +*/ + +/* +** maximum number of devices which share one interrupt line +*/ + +#define PCI_MAX_DPI 1 + +/* +** there may be up to 255 busses on pci. +** don't probe them all :-) +*/ + +#define FIRST_BUS 0 +#define LAST_BUS 0 + +/* +** there may be up to 32 devices per bus. +** do probe them all ;-) +*/ + +#define FIRST_DEVICE 0 +#define LAST_DEVICE 31 + +/*======================================================== +** +** #includes and declarations +** +**======================================================== +*/ + +#include <types.h> +#include <cdefs.h> +#include <errno.h> +#include <param.h> + +#include <vm/vm.h> +#include <vm/vm_param.h> + +#include <i386/isa/icu.h> +#include <i386/isa/isa.h> +#include <i386/isa/isa_device.h> + +#include <i386/pci/pci.h> +#include <i386/pci/pci_device.h> +#include <i386/pci/pcibios.h> + + +static char ident[] = + "\n$Id: pci.c,v 2.0.0.1 94/07/19 19:06:44 wolf Exp $\n" + "Copyright (c) 1994, Wolfgang Stanglmeier\n"; + +/* +** Function prototypes missing in system headers +*/ + +extern int printf(); +extern int ffs(); +extern pmap_t pmap_kernel(void); + +/* +** function prototypes +*/ + +int pci_map_mem + (pcici_t tag, u_long reg, u_long *va, vm_offset_t *pa); +int pci_map_port (pcici_t tag, u_long reg, u_short* pa); +void pci_configure (void); + +/*======================================================== +** +** Autoconfiguration (of isa bus) (Free/386) +** +**======================================================== +*/ + +/* +** per device (interrupt) data structure. +*/ + +static struct { + u_short number; + u_short isanum; + struct { + int (*proc)(int dev); + dev_t unit; + } vector[PCI_MAX_DPI]; +} pcidata [NPCI]; + +#ifndef __NetBSD__ + +/* +** check device ready +*/ +static int pciprobe (struct isa_device *dev) +{ + if (dev->id_unit >= NPCI) + return (0); + + if (!pci_conf_mode()) + return (0); + + return (1); +} + +/* +** initialize the driver structure +*/ +static int pciattach (struct isa_device *isdp) +{ + pcidata[isdp->id_unit].number = 0; + pcidata[isdp->id_unit].isanum = ffs(isdp->id_irq)-1; + return (1); +} + +/* +** ISA driver structure +*/ + +struct isa_driver pcidriver = { + pciprobe, + pciattach, + "pci" +}; + +/*======================================================== +** +** Interrupt forward from isa to pci devices. +** +**======================================================== +*/ + + +void pciintr (int unit) +{ + u_short i; + if (unit >= NPCI) return; + +#if defined (GENERICAH) || defined (GENERICBT) || defined (GENERICNCR) \ + || defined (GENERICAHA) || defined (GENERICISA) + for (unit=0; unit < NPCI; unit++) +#endif + + for (i=0; i<pcidata[unit].number; i++) { + (void)(*pcidata[unit].vector[i].proc)(pcidata[unit].vector[i].unit); + }; +} + +#endif /* __NetBSD__ */ + +/*======================================================== +** +** Autoconfiguration of pci devices. +** +** This is reverse to the isa configuration. +** (1) find a pci device. +** (2) look for a driver. +** +**======================================================== +*/ + +/*-------------------------------------------------------- +** +** The pci devices can be mapped to any address. +** As default we start at the last gigabyte. +** +**-------------------------------------------------------- +*/ + +#ifndef PCI_PMEM_START +#define PCI_PMEM_START 0xc0000000 +#endif + +static vm_offset_t pci_paddr = PCI_PMEM_START; + +/*--------------------------------------------------------- +** +** pci_configure () +** +**--------------------------------------------------------- +*/ + +void pci_configure() +{ + u_char bus, device, reg; + pcici_t tag; + pcidi_t type; + u_long data; + int unit; + int intpin; + int pci_mode; + + struct pci_driver *drp; + struct pci_device *dvp; + + /* + ** check pci bus present + */ + + pci_mode = pci_conf_mode (); + if (!pci_mode) return; + + /* + ** hello world .. + */ + + printf ("PCI configuration mode %d.\n", pci_mode); + + for (bus=FIRST_BUS;bus<=LAST_BUS; bus++) + for (device=FIRST_DEVICE; device<=LAST_DEVICE; device ++) { + tag = pcitag (bus, device, 0); + type = pci_conf_read (tag, 0); + + if ((!type) || (type==0xfffffffful)) continue; + printf ("on pci%d:%d ", bus, device); + + /* + ** lookup device in ioconfiguration: + */ + + for (dvp = pci_devtab; drp=dvp->pd_driver; dvp++) { + if (drp->device_id == type) break; + }; + + if (!drp) { + + /* + ** not found + */ + + switch (type & 0xffff) { + + case 0x1002: + printf ("ATI TECHNOLOGIES INC"); + break; + case 0x101A: + printf ("NCR"); + break; + case 0x102B: + printf ("MATROX"); + break; + case 0x1045: + printf ("OPTI"); + break; + case 0x8086: + printf ("INTEL CORPORATION"); + break; + + default: + printf ("vendor=%x", type & 0xffff); + }; + + switch (type) { + + case 0x04848086: + printf (" 82378IB pci-isa bridge"); + break; + case 0x04838086: + printf (" 82424ZX cache dram controller"); + break; + case 0x04828086: + printf (" 82375EB pci-eisa bridge"); + break; + case 0x04A38086: + printf (" 82434LX pci cache memory controller"); + break; + default: + printf (", device=%x", type >> 16); + }; + printf (" [not supported]\n"); + + for (reg=0x10; reg<=0x20; reg+=4) { + data = pci_conf_read (tag, reg); + if (!data) continue; + switch (data&7) { + + case 1: + case 5: + printf (" map(%x): io(%x)\n", + reg, data & ~3); + break; + case 0: + printf (" map(%x): mem32(%x)\n", + reg, data & ~7); + break; + case 2: + printf (" map(%x): mem20(%x)\n", + reg, data & ~7); + break; + case 4: + printf (" map(%x): mem64(%x)\n", + reg, data & ~7); + break; + }; + }; + continue; + }; + + /* + ** found it. + ** probe returns the device unit. + */ + + printf ("<%s>", drp -> vendor); + + unit = (*drp->probe) (tag); + + if (unit<0) { + printf (" probe failed.\n"); + continue; + }; + + /* + ** install interrupts + */ + + intpin = (pci_conf_read (tag, 0x3c) >> 8) & 0xff; + if (intpin) { + printf (" irq %c", 0x60+intpin); + intpin--; + if (intpin < NPCI) { + u_short entry = pcidata[intpin].number; + if (entry < PCI_MAX_DPI) { + pcidata[intpin].vector[entry].proc = drp->intr; + pcidata[intpin].vector[entry].unit = unit; + entry++; + }; + printf (" isa=%d [%d]",pcidata[intpin].isanum, entry); + pcidata[intpin].number=entry; + } else printf (" not installed"); + }; + + /* + ** enable memory access + */ + data = pci_conf_read (tag, 0x04) & 0xffff | 0x0002; + pci_conf_write (tag, (u_char) 0x04, data); + + /* + ** attach device + ** may produce additional log messages, + ** i.e. when installing subdevices. + */ + + printf (" as %s%d\n", drp->name,unit); + (void) (*drp->attach) (tag); + + }; + + printf ("pci uses physical addresses from %x to %x\n", + PCI_PMEM_START, pci_paddr); +} + +/*----------------------------------------------------------------------- +** +** Map device into virtual and physical space +** +**----------------------------------------------------------------------- +*/ + +extern vm_map_t kernel_map; + +int pci_map_port (pcici_t tag, u_long reg, u_short* pa) +{ + /* + ** @MAPIO@ not yet implemented. + */ + return (ENOSYS); +} + + +int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa) +{ + u_long data, result; + vm_size_t vsize; + vm_offset_t vaddr; + + /* + ** sanity check + */ + + if (reg <= 0x10 || reg >= 0x20 || (reg & 3)) + return (EINVAL); + + /* + ** get size and type of memory + */ + + pci_conf_write (tag, reg, 0xfffffffful); + data = pci_conf_read (tag, reg); + + switch (data & 0x0f) { + + case 0x0: /* 32 bit non cachable */ + break; + + default: /* unknown */ + return (EINVAL); + }; + + vsize = round_page (-(data & 0xfffffff0)); + + printf (" memory size=0x%x", vsize); + + if (!vsize) return (EINVAL); + + /* + ** try to map device to virtual space + */ + + vaddr = vm_map_min (kernel_map); + + result = vm_map_find (kernel_map, (void*)0, (vm_offset_t) 0, + &vaddr, vsize, TRUE); + + if (result != KERN_SUCCESS) { + printf (" vm_map_find failed(%d)\n", result); + return (ENOMEM); + }; + + /* + ** align physical address to virtual size + */ + + if (data = pci_paddr % vsize) + pci_paddr += vsize - data; + + /* + ** display values. + */ + + printf (" virtual=0x%x physical=0x%x\n", vaddr, pci_paddr); + + *va = vaddr; + *pa = pci_paddr; + + /* + ** set device address + */ + + pci_conf_write (tag, reg, pci_paddr); + + /* + ** map physical + */ + + while (vsize >= NBPG) { + pmap_enter (pmap_kernel(), vaddr, pci_paddr, + VM_PROT_READ|VM_PROT_WRITE, TRUE); + vaddr += NBPG; + pci_paddr += NBPG; + vsize -= NBPG; + }; + + return (0); +} +#endif diff --git a/sys/i386/pci/pci.h b/sys/i386/pci/pci.h new file mode 100644 index 000000000000..703144d1eb8f --- /dev/null +++ b/sys/i386/pci/pci.h @@ -0,0 +1,58 @@ +/************************************************************************** +** +** $Id: pci.h,v 2.0 94/07/10 15:53:30 wolf Rel $ +** +** #define for pci bus device drivers +** +**------------------------------------------------------------------------- +** +** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany +** <wolf@dentaro.GUN.de> +** +** This is a beta version - use with care. +** +**------------------------------------------------------------------------- +** +** $Log: pci.h,v $ +** Revision 2.0 94/07/10 15:53:30 wolf +** FreeBSD release. +** +** Revision 1.0 94/06/07 20:02:21 wolf +** Beta release. +** +*************************************************************************** +*/ + +#ifndef __PCI_H__ +#define __PCI_H__ + +/* +** main pci initialization function. +** called at boot time from autoconf.c +*/ + +void pci_configure(void); + +/* +** pci configuration id +** +** is constructed from: bus, device & function numbers. +*/ + +typedef union { + u_long cfg1; + struct { + u_char enable; + u_char forward; + u_short port; + } cfg2; + } pcici_t; + +/* +** Each pci device has an unique device id. +** It is used to find a matching driver. +*/ + +typedef u_long pcidi_t; + +#endif /*__PCI_H__*/ diff --git a/sys/i386/pci/pci_config.c b/sys/i386/pci/pci_config.c new file mode 100644 index 000000000000..d1e2332191f2 --- /dev/null +++ b/sys/i386/pci/pci_config.c @@ -0,0 +1,45 @@ +/************************************************************************** +** +** $Id: pci_config.c,v 2.0 94/07/10 15:53:30 wolf Rel $ +** +** @PCI@ this should be part of "ioconf.c". +** +** The config-utility should build it! +** When struct pci_driver has become stable +** I'll extend the config utility. +** +**------------------------------------------------------------------------- +** +** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany +** <wolf@dentaro.GUN.de> +** +** This is a beta version - use with care. +** +**------------------------------------------------------------------------- +** +** $Log: pci_config.c,v $ +** Revision 2.0 94/07/10 15:53:30 wolf +** FreeBSD release. +** +** Revision 1.0 94/06/07 20:04:37 wolf +** Beta release. +** +*************************************************************************** +*/ + +#include "types.h" +#include "i386/pci/pci.h" +#include "i386/pci/pci_device.h" + +#include "ncr.h" +#if NNCR>0 +extern struct pci_driver ncrdevice; +#endif + +struct pci_device pci_devtab[] = { + +#if NNCR>0 + {&ncrdevice}, +#endif + {0} +}; diff --git a/sys/i386/pci/pci_device.h b/sys/i386/pci/pci_device.h new file mode 100644 index 000000000000..5fc33216a5a5 --- /dev/null +++ b/sys/i386/pci/pci_device.h @@ -0,0 +1,88 @@ +/************************************************************************** +** +** $Id: pci_device.h,v 2.0 94/07/10 15:53:31 wolf Rel $ +** +** #define for pci based device drivers +** +**------------------------------------------------------------------------- +** +** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany +** <wolf@dentaro.GUN.de> +** +** This is a beta version - use with care. +** +**------------------------------------------------------------------------- +** +** $Log: pci_device.h,v $ +** Revision 2.0 94/07/10 15:53:31 wolf +** FreeBSD release. +** +** Revision 1.0 94/06/07 20:02:22 wolf +** Beta release. +** +*************************************************************************** +*/ + +#ifndef __PCI_DEVICE_H__ +#define __PCI_DEVICE_H__ + +/*------------------------------------------------------------ +** +** Per driver structure. +** +**------------------------------------------------------------ +*/ + +struct pci_driver { + int (*probe )(pcici_t pci_ident); /* test whether device is present */ + int (*attach)(pcici_t pci_ident); /* setup driver for a device */ + pcidi_t device_id; /* device pci id */ + char *name; /* device name */ + char *vendor; /* device long name */ + int (*intr)(int); /* interupt handler */ +}; + +/*----------------------------------------------------------- +** +** Per device structure. +** +** It is initialized by the config utility and should live in +** "ioconf.c". At the moment there is only one field. +** +** This is a first attempt to include the pci bus to 386bsd. +** So this structure may grow .. +** +**----------------------------------------------------------- +*/ + +struct pci_device { + struct pci_driver * pd_driver; +}; + +/*----------------------------------------------------------- +** +** This table should be generated in file "ioconf.c" +** by the config program. +** It is used at boot time by the configuration function +** pci_configure() +** +**----------------------------------------------------------- +*/ + +extern struct pci_device pci_devtab[]; + +/*----------------------------------------------------------- +** +** This functions may be used by drivers to map devices +** to virtual and physical addresses. The va and pa +** addresses are "in/out" parameters. If they are 0 +** on entry, the mapping function assigns an address. +** +**----------------------------------------------------------- +*/ + +int pci_map_mem (pcici_t tag, u_long entry, u_long * va, u_long * pa); + +int pci_map_port(pcici_t tag, u_long entry, u_short * pa); + +#endif /*__PCI_DEVICE_H__*/ diff --git a/sys/i386/pci/pcibios.c b/sys/i386/pci/pcibios.c new file mode 100644 index 000000000000..049d6e77a057 --- /dev/null +++ b/sys/i386/pci/pcibios.c @@ -0,0 +1,261 @@ +/************************************************************************** +** +** $Id: pcibios.c,v 2.0 94/07/10 15:53:31 wolf Rel $ +** +** #define for pci-bus bios functions. +** +**------------------------------------------------------------------------- +** +** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany +** <wolf@dentaro.GUN.de> +** +** This is a beta version - use with care. +** +**------------------------------------------------------------------------- +** +** $Log: pcibios.c,v $ +** Revision 2.0 94/07/10 15:53:31 wolf +** FreeBSD release. +** +** Revision 1.0 94/06/07 20:02:20 wolf +** Beta release. +** +*************************************************************************** +*/ + + +#include "types.h" +#include "i386/isa/isa.h" +#include "i386/pci/pci.h" +#include "i386/pci/pcibios.h" + + +extern int printf(); + +static char pci_mode; + +static char ident[] = + "\n$Id: pcibios.c,v 2.0 94/07/10 15:53:31 wolf Rel $\n" + "Copyright (c) 1994, Wolfgang Stanglmeier\n"; + + +/*-------------------------------------------------------------------- +** +** Port access +** +**-------------------------------------------------------------------- +** +** @FREEBSD@ inl() and outl() functions are not defined +*/ + +#define DIRTY + +#ifdef DIRTY + +#undef inl +#define inl(port) \ +({ u_long data; \ + __asm __volatile("inl %1, %0": "=a" (data): "d" ((u_short)(port))); \ + data; }) + + +#undef outl +#define outl(port, data) \ +{__asm __volatile("outl %0, %1"::"a" ((u_long)(data)), "d" ((u_short)(port)));} + + +#undef inb +#define inb(port) \ +({ u_char data; \ + __asm __volatile("inb %1, %0": "=a" (data): "d" ((u_short)(port))); \ + data; }) + + +#undef outb +#define outb(port, data) \ +{__asm __volatile("outb %0, %1"::"a" ((u_char)(data)), "d" ((u_short)(port)));} + +#endif + +/*-------------------------------------------------------------------- +** +** Determine configuration mode +** +**-------------------------------------------------------------------- +*/ + + +#define CONF1_ENABLE 0x80000000ul +#define CONF1_ADDR_PORT 0x0cf8 +#define CONF1_DATA_PORT 0x0cfc + + +#define CONF2_ENABLE_PORT 0x0cf8 +#define CONF2_FORWARD_PORT 0x0cfa + + +int pci_conf_mode (void) +{ + u_long result, oldval; + + /*--------------------------------------- + ** Configuration mode 1 ? + **--------------------------------------- + */ + + oldval = inl (CONF1_ADDR_PORT); + outl (CONF1_ADDR_PORT, CONF1_ENABLE); + result = inl (CONF1_ADDR_PORT); + outl (CONF1_ADDR_PORT, oldval); + + if (result == CONF1_ENABLE) { + pci_mode = 1; + return (1); + }; + + /*--------------------------------------- + ** Configuration mode 2 ? + **--------------------------------------- + */ + + outb (CONF2_ENABLE_PORT, 0); + outb (CONF2_FORWARD_PORT, 0); + if (!inb (CONF2_ENABLE_PORT) && !inb (CONF2_FORWARD_PORT)) { + pci_mode = 2; + return (2); + }; + + /*--------------------------------------- + ** No PCI bus available. + **--------------------------------------- + */ + return (0); +} + +/*-------------------------------------------------------------------- +** +** Build a pcitag from bus, device and function number +** +**-------------------------------------------------------------------- +*/ + + +pcici_t pcitag (unsigned char bus, + unsigned char device, + unsigned char func) +{ + pcici_t tag; + + tag.cfg1 = 0; + if (device >= 32) return tag; + if (func >= 8) return tag; + + switch (pci_mode) { + + case 1: + tag.cfg1 = CONF1_ENABLE + | (((u_long) bus ) << 16ul) + | (((u_long) device) << 11ul) + | (((u_long) func ) << 8ul); + break; + case 2: + if (device >= 16) break; + tag.cfg2.port = 0xc000 | (device << 8ul); + tag.cfg2.enable = 0xf1 | (func << 1ul); + tag.cfg2.forward = bus; + break; + }; + return tag; +} + +/*-------------------------------------------------------------------- +** +** Read register from configuration space. +** +**-------------------------------------------------------------------- +*/ + + +u_long pci_conf_read (pcici_t tag, u_long reg) +{ + u_long addr, data = 0; + + if (!tag.cfg1) return (0xfffffffful); + + switch (pci_mode) { + + case 1: + addr = tag.cfg1 | reg & 0xfc; +#ifdef PCI_DEBUG + printf ("pci_conf_read(1): addr=%x ", addr); +#endif + outl (CONF1_ADDR_PORT, addr); + data = inl (CONF1_DATA_PORT); + outl (CONF1_ADDR_PORT, 0 ); + break; + + case 2: + addr = tag.cfg2.port | reg & 0xfc; +#ifdef PCI_DEBUG + printf ("pci_conf_read(2): addr=%x ", addr); +#endif + outb (CONF2_ENABLE_PORT , tag.cfg2.enable ); + outb (CONF2_FORWARD_PORT, tag.cfg2.forward); + + data = inl ((u_short) addr); + + outb (CONF2_ENABLE_PORT, 0); + outb (CONF2_FORWARD_PORT, 0); + break; + }; + +#ifdef PCI_DEBUG + printf ("data=%x\n", data); +#endif + + return (data); +} + +/*-------------------------------------------------------------------- +** +** Write register into configuration space. +** +**-------------------------------------------------------------------- +*/ + + +void pci_conf_write (pcici_t tag, u_long reg, u_long data) +{ + u_long addr; + + if (!tag.cfg1) return; + + switch (pci_mode) { + + case 1: + addr = tag.cfg1 | reg & 0xfc; +#ifdef PCI_DEBUG + printf ("pci_conf_write(1): addr=%x data=%x\n", + addr, data); +#endif + outl (CONF1_ADDR_PORT, addr); + outl (CONF1_DATA_PORT, data); + outl (CONF1_ADDR_PORT, 0 ); + break; + + case 2: + addr = tag.cfg2.port | reg & 0xfc; +#ifdef PCI_DEBUG + printf ("pci_conf_write(2): addr=%x data=%x\n", + addr, data); +#endif + outb (CONF2_ENABLE_PORT, tag.cfg2.enable); + outb (CONF2_FORWARD_PORT, tag.cfg2.forward); + + outl ((u_short) addr, data); + + outb (CONF2_ENABLE_PORT, 0); + outb (CONF2_FORWARD_PORT, 0); + break; + }; +} diff --git a/sys/i386/pci/pcibios.h b/sys/i386/pci/pcibios.h new file mode 100644 index 000000000000..5e82d2f27b34 --- /dev/null +++ b/sys/i386/pci/pcibios.h @@ -0,0 +1,53 @@ +/************************************************************************** +** +** $Id: pcibios.h,v 2.0 94/07/10 15:53:32 wolf Rel $ +** +** #define for pci-bus bios functions. +** +**------------------------------------------------------------------------- +** +** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany +** <wolf@dentaro.GUN.de> +** +** This is a beta version - use with care. +** +**------------------------------------------------------------------------- +** +** $Log: pcibios.h,v $ +** Revision 2.0 94/07/10 15:53:32 wolf +** FreeBSD release. +** +** Revision 1.0 94/06/07 20:02:23 wolf +** Beta release. +** +*************************************************************************** +*/ + +#ifndef __PCIBIOS_H__ +#define __PCIBIOS_H__ + +/* +** the availability of a pci bus. +** configuration mode (1 or 2) +** 0 if no pci bus found. +*/ + +int pci_conf_mode (void); + +/* +** get a "ticket" for accessing a pci device +** configuration space. +*/ + +pcici_t pcitag (unsigned char bus, + unsigned char device, + unsigned char func); + +/* +** read or write the configuration space. +*/ + +u_long pci_conf_read (pcici_t tag, u_long reg ); +void pci_conf_write (pcici_t tag, u_long reg, u_long data); + +#endif |
