diff -u --recursive --new-file pl11/linux/Makefile linux/Makefile --- pl11/linux/Makefile Sat Jul 17 18:32:44 1993 +++ linux/Makefile Thu Aug 5 11:59:18 1993 @@ -3,7 +3,7 @@ .EXPORT_ALL_VARIABLES: -BASH = $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ +CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ else if [ -x /bin/bash ]; then echo /bin/bash; \ else echo sh; fi ; fi) @@ -96,13 +96,8 @@ Version: dummy rm -f tools/version.h -lilo: $(CONFIGURE) Image - if [ -f /vmlinux ]; then mv /vmlinux /vmlinux.old; fi - cat Image > /vmlinux - /etc/lilo/install - config: - $(BASH) Configure $(OPTS) < config.in + $(CONFIG_SHELL) Configure $(OPTS) < config.in mv .config~ .config $(MAKE) soundconf @@ -116,7 +111,7 @@ tools/version.h: $(CONFIGURE) Makefile @./makever.sh - @echo \#define UTS_RELEASE \"0.99.11\" > tools/version.h + @echo \#define UTS_RELEASE \"0.99.12\" > tools/version.h @echo \#define UTS_VERSION \"\#`cat .version` `date`\" >> tools/version.h @echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> tools/version.h @echo \#define LINUX_COMPILE_BY \"`whoami`\" >> tools/version.h @@ -123,13 +118,6 @@ @echo \#define LINUX_COMPILE_HOST \"`hostname`\" >> tools/version.h @echo \#define LINUX_COMPILE_DOMAIN \"`domainname`\" >> tools/version.h -Image: $(CONFIGURE) boot/bootsect boot/setup tools/system tools/build - tools/build boot/bootsect boot/setup tools/system $(ROOT_DEV) > Image - sync - -disk: Image - dd bs=8192 if=Image of=/dev/fd0 - tools/build: $(CONFIGURE) tools/build.c $(HOSTCC) $(CFLAGS) \ -o tools/build tools/build.c @@ -246,3 +234,27 @@ dummy: endif + +# +# Leave these dummy entries for now to tell people that they are going away.. +# +lilo: + @echo + @echo Uncompressed kernel images no longer supported. Use + @echo \"make zlilo\" instead. + @echo + @exit 1 + +Image: + @echo + @echo Uncompressed kernel images no longer supported. Use + @echo \"make zImage\" instead. + @echo + @exit 1 + +disk: + @echo + @echo Uncompressed kernel images no longer supported. Use + @echo \"make zdisk\" instead. + @echo + @exit 1 diff -u --recursive --new-file pl11/linux/boot/head.S linux/boot/head.S --- pl11/linux/boot/head.S Sat Jul 3 00:27:47 1993 +++ linux/boot/head.S Sun Aug 1 15:07:07 1993 @@ -35,6 +35,20 @@ mov %ax,%fs mov %ax,%gs lss _stack_start,%esp +/* + * Clear BSS first so that there are no surprises... + */ + xorl %eax,%eax + movl $__edata,%edi + movl $__end,%ecx + subl %edi,%ecx + cld + rep + stosb +/* + * start system 32-bit setup. We need to re-do some of the things done + * in 16-bit mode for the "real" operations. + */ call setup_idt xorl %eax,%eax 1: incl %eax # check that A20 really IS enabled @@ -72,16 +86,6 @@ rep movsb 1: -/* - * Clear BSS - */ - xorl %eax,%eax - movl $__edata,%edi - movl $__end,%ecx - subl %edi,%ecx - cld - rep - stosb /* check if it is 486 or 386. */ /* * XXX - this does a lot of unnecessary setup. Alignment checks don't @@ -90,6 +94,7 @@ */ movl %esp,%edi # save stack pointer andl $0xfffffffc,%esp # align stack to avoid AC fault + movl $3,_x86 pushfl # push EFLAGS popl %eax # get EFLAGS movl %eax,%ecx # save original EFLAGS @@ -100,28 +105,31 @@ popl %eax # put it in eax xorl %ecx,%eax # change in flags andl $0x40000,%eax # check if AC bit changed - jnz 1f # 486 - pushl %ecx # restore original EFLAGS + je is386 + movl $4,_x86 + movl %ecx,%eax + xorl $0x200000,%eax # check ID flag + pushl %eax + popfl # if we are on a 486, + pushfl # we can't change it + popl %eax + xorl %ecx,%eax + andl $0x200000,%eax + je is486 + movl $5,_x86 # 586 setup same as 486 at least for now +is486: pushl %ecx # restore original EFLAGS popfl movl %edi,%esp # restore esp - movl %cr0,%eax # 386 + movl %cr0,%eax # 486 andl $0x80000011,%eax # Save PG,PE,ET - orl $2,%eax # set MP - jmp 2f -/* - * NOTE! 486 should set bit 16, to check for write-protect in supervisor - * mode. Then it would be unnecessary with the "verify_area()"-calls. - * 486 users probably want to set the NE (#5) bit also, so as to use - * int 16 for math errors. - * XXX - the above is out of date. We set all the bits, but don't take - * advantage of WP (26 Dec 92). - */ -1: pushl %ecx # restore original EFLAGS + orl $0x50022,%eax # set AM, WP, NE and MP + jmp 2f +is386: pushl %ecx # restore original EFLAGS popfl movl %edi,%esp # restore esp - movl %cr0,%eax # 486 + movl %cr0,%eax # 386 andl $0x80000011,%eax # Save PG,PE,ET - orl $0x50022,%eax # set AM, WP, NE and MP + orl $2,%eax # set MP 2: movl %eax,%cr0 call check_x87 call setup_paging @@ -134,9 +142,11 @@ mov %ax,%fs mov %ax,%gs lss _stack_start,%esp - pushl $0 # These are the parameters to main :-) - pushl $0 - pushl $0 + xorl %eax,%eax + lldt %ax + pushl %eax # These are the parameters to main :-) + pushl %eax + pushl %eax cld # gcc2 wants the direction flag cleared at all times call _start_kernel L6: @@ -148,6 +158,7 @@ */ check_x87: movl $0,_hard_math + clts fninit fstsw %ax cmpb $0,%al diff -u --recursive --new-file pl11/linux/config.in linux/config.in --- pl11/linux/config.in Sun Jul 18 17:06:53 1993 +++ linux/config.in Sun Aug 15 11:24:56 1993 @@ -5,7 +5,7 @@ * * General setup * -bool 'Kernel math emulation' CONFIG_MATH_EMULATION n +bool 'Kernel math emulation' CONFIG_MATH_EMULATION y bool 'Normal harddisk support' CONFIG_BLK_DEV_HD y bool 'XT harddisk support' CONFIG_BLK_DEV_XD n bool 'TCP/IP networking' CONFIG_INET y @@ -18,7 +18,7 @@ bool 'SCSI support?' CONFIG_SCSI n if [ "$CONFIG_SCSI" = "n" ] : -: Skipping SCSI onfiguration options... +: Skipping SCSI configuration options... : else * @@ -38,6 +38,32 @@ bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST n fi * +* Network device support +* +bool 'Network device support?' CONFIG_ETHERCARDS y +if [ "$CONFIG_ETHERCARDS" = "n" ] +: +: Skipping ethercard configuration options... +: +else +bool 'SLIP (serial line) support' CONFIG_SLIP n +bool 'PLIP (parallel port) support' CONFIG_PLIP n +bool 'NE2000/NE1000 support' CONFIG_NE2000 y +bool 'WD80*3 support' CONFIG_WD80x3 y +#bool '3c501 support' CONFIG_EL1 n +bool '3c503 support' CONFIG_EL2 y +#bool '3c509 support' CONFIG_EL3 n +bool 'HP PCLAN support' CONFIG_HPLAN y +bool 'AT1500 and NE2100 support' CONFIG_AT1500 y +#bool 'DEPCA support' CONFIG_DEPCA n +bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 y +#bool 'AT-LAN-TEC pocket adaptor support' CONFIG_ATP n +#bool 'EtherExpress support' CONFIG_EEXPRESS n +fi +* +bool 'Sony CDU31A CDROM driver support' CONFIG_CDU31A n +bool 'Mitsumi CDROM driver support' CONFIG_MCD n +* * Filesystems * bool 'Standard (minix) fs support' CONFIG_MINIX_FS y @@ -46,12 +72,13 @@ bool 'xiafs filesystem support' CONFIG_XIA_FS n bool 'msdos fs support' CONFIG_MSDOS_FS y bool '/proc filesystem support' CONFIG_PROC_FS y -bool 'NFS filesystem support' CONFIG_NFS_FS n +bool 'NFS filesystem support' CONFIG_NFS_FS y bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n * * character devices * bool 'Keyboard meta-key sends ESC-prefix' CONFIG_KBD_META y +bool 'Keyboard Num Lock on by default' CONFIG_KBD_NUML y bool 'Logitech busmouse support' CONFIG_BUSMOUSE n bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n @@ -67,4 +94,6 @@ * bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n bool 'Kernel profiling support' CONFIG_PROFILE n - +if [ "$CONFIG_SCSI" = "y" ] +bool 'Verbose scsi error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS n +fi diff -u --recursive --new-file pl11/linux/fs/block_dev.c linux/fs/block_dev.c --- pl11/linux/fs/block_dev.c Sat Jun 26 10:28:05 1993 +++ linux/fs/block_dev.c Mon Aug 9 18:02:28 1993 @@ -44,7 +44,7 @@ if (blk_size[MAJOR(dev)]) size = (blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS) >> blocksize_bits; else - size = 0x7fffffff; + size = INT_MAX; while (count>0) { if (block >= size) return written; @@ -106,7 +106,7 @@ if (blk_size[MAJOR(dev)]) size = blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS; else - size = 0x7fffffff; + size = INT_MAX; if (offset > size) left = 0; diff -u --recursive --new-file pl11/linux/fs/buffer.c linux/fs/buffer.c --- pl11/linux/fs/buffer.c Sat Jul 3 01:06:13 1993 +++ linux/fs/buffer.c Sat Aug 14 23:47:21 1993 @@ -6,7 +6,7 @@ /* * 'buffer.c' implements the buffer-cache functions. Race-conditions have - * been avoided by NEVER letting a interrupt change a buffer (except for the + * been avoided by NEVER letting an interrupt change a buffer (except for the * data, of course), but instead letting the caller do it. */ @@ -38,6 +38,12 @@ extern int revalidate_scsidisk(int, int); #endif #endif +#ifdef CONFIG_CDU31A +extern int check_cdu31a_media_change(int, int); +#endif +#ifdef CONFIG_MCD +extern int check_mcd_media_change(int, int); +#endif static struct buffer_head * hash_table[NR_HASH]; static struct buffer_head * free_list = NULL; @@ -233,6 +239,18 @@ break; #endif +#if defined(CONFIG_CDU31A) + case 15: /* Sony CDROM */ + i = check_cdu31a_media_change(dev, 0); + break; +#endif + +#if defined(CONFIG_MCD) + case 23: /* Sony CDROM */ + i = check_mcd_media_change(dev, 0); + break; +#endif + default: return; }; @@ -379,13 +397,15 @@ if (!blksize_size[MAJOR(dev)]) return; - if (size != 512 && size != 1024 && size != 2048 && size != 4096) - panic("Invalid blocksize passed to set_blocksize"); + switch(size) { + default: panic("Invalid blocksize passed to set_blocksize"); + case 512: case 1024: case 2048: case 4096:; + } if (blksize_size[MAJOR(dev)][MINOR(dev)] == 0 && size == BLOCK_SIZE) { blksize_size[MAJOR(dev)][MINOR(dev)] = size; return; - }; + } if (blksize_size[MAJOR(dev)][MINOR(dev)] == size) return; sync_buffers(dev, 2); @@ -437,7 +457,7 @@ grow_size -= size; if (nr_free_pages > min_free_pages && grow_size <= 0) { grow_buffers(size); - grow_size = 4096; + grow_size = PAGE_SIZE; } buffers = nr_buffers; bh = NULL; @@ -588,19 +608,18 @@ static void get_more_buffer_heads(void) { - unsigned long page; + int i; struct buffer_head * bh; if (unused_list) return; - page = get_free_page(GFP_BUFFER); - if (!page) + + if(! (bh = (struct buffer_head*) get_free_page(GFP_BUFFER))) return; - bh = (struct buffer_head *) page; - while ((unsigned long) (bh+1) <= page+4096) { - put_unused_buffer_head(bh); - bh++; - nr_buffer_heads++; + + for (nr_buffer_heads+=i=PAGE_SIZE/sizeof*bh ; i>0; i--) { + bh->b_next_free = unused_list; /* only make link */ + unused_list = bh++; } } @@ -632,8 +651,8 @@ unsigned long offset; head = NULL; - offset = 4096; - while ((offset -= size) < 4096) { + offset = PAGE_SIZE; + while ((offset -= size) < PAGE_SIZE) { bh = get_unused_buffer_head(); if (!bh) goto no_grow; @@ -685,7 +704,7 @@ int nrbuf; page = (unsigned long) first->b_data; - if (page & 0xfff) { + if (page & ~PAGE_MASK) { brelse(first); return 0; } @@ -692,7 +711,7 @@ mem_map[MAP_NR(page)]++; bh[0] = first; nrbuf = 1; - for (offset = size ; offset < 4096 ; offset += size) { + for (offset = size ; offset < PAGE_SIZE ; offset += size) { block = *++b; if (!block) goto no_go; @@ -728,7 +747,7 @@ if (!bh) return 0; p = b; - for (offset = 0 ; offset < 4096 ; offset += size) { + for (offset = 0 ; offset < PAGE_SIZE ; offset += size) { block = *(p++); if (!block) goto not_aligned; @@ -755,7 +774,7 @@ else break; } - buffermem += 4096; + buffermem += PAGE_SIZE; bh->b_this_page = tmp; mem_map[MAP_NR(address)]++; read_buffers(arr,block); @@ -847,12 +866,11 @@ unsigned long page; struct buffer_head *bh, *tmp; - if ((size & 511) || (size > 4096)) { + if ((size & 511) || (size > PAGE_SIZE)) { printk("VFS: grow_buffers: size = %d\n",size); return; } - page = get_free_page(GFP_BUFFER); - if (!page) + if(!(page = __get_free_page(GFP_BUFFER))) return; bh = create_buffers(page, size); if (!bh) { @@ -878,7 +896,7 @@ break; } tmp->b_this_page = bh; - buffermem += 4096; + buffermem += PAGE_SIZE; return; } @@ -893,7 +911,7 @@ *bhp = bh; page = (unsigned long) bh->b_data; - page &= 0xfffff000; + page &= PAGE_MASK; tmp = bh; do { if (!tmp) @@ -912,7 +930,7 @@ remove_from_queues(p); put_unused_buffer_head(p); } while (tmp != bh); - buffermem -= 4096; + buffermem -= PAGE_SIZE; free_page(page); return !mem_map[MAP_NR(page)]; } diff -u --recursive --new-file pl11/linux/fs/exec.c linux/fs/exec.c --- pl11/linux/fs/exec.c Tue Jul 13 22:55:33 1993 +++ linux/fs/exec.c Sun Aug 15 11:33:07 1993 @@ -15,6 +15,12 @@ * * Once more I can proudly say that linux stood up to being changed: it * was less than 2 hours work to get demand-loading completely implemented. + * + * Demand loading changed July 1993 by Eric Youngdale. Use mmap instead, + * current->executable is only used by the procfs. This allows a dispatch + * table to check for several different types of binary formats. We keep + * trying until we recognize the file or we run out of supported binary + * formats. */ #include @@ -31,17 +37,56 @@ #include #include #include +#include + +#include #include +#include + +extern "C" int sys_exit(int exit_code); +extern "C" int sys_close(unsigned fd); +extern "C" int sys_open(const char *, int, int); extern void shm_exit (void); -/* - * MAX_ARG_PAGES defines the number of pages allocated for arguments - * and envelope for the new program. 32 should suffice, this gives - * a maximum env+arg of 128kB ! - */ -#define MAX_ARG_PAGES 32 +static int open_inode(struct inode * inode, int mode) +{ + int error, fd; + struct file *f, **fpp; + + if (!inode->i_op || !inode->i_op->default_file_ops) + return -EINVAL; + f = get_empty_filp(); + if (!f) + return -EMFILE; + fd = 0; + fpp = current->filp; + for (;;) { + if (!*fpp) + break; + if (++fd > NR_OPEN) + return -ENFILE; + fpp++; + } + *fpp = f; + f->f_flags = mode; + f->f_mode = (mode+1) & O_ACCMODE; + f->f_inode = inode; + f->f_pos = 0; + f->f_reada = 0; + f->f_op = inode->i_op->default_file_ops; + if (f->f_op->open) { + error = f->f_op->open(inode,f); + if (error) { + *fpp = NULL; + f->f_count--; + return error; + } + } + inode->i_count++; + return fd; +} /* * These are the only things you should do on a core-file: use only these @@ -78,9 +123,6 @@ return 0; current->dumpable = 0; - if (current->elf_executable) - return 0; - /* See if we have enough room to write the upage. */ if (current->rlim[RLIMIT_CORE].rlim_cur < PAGE_SIZE) return 0; @@ -185,54 +227,27 @@ */ extern "C" int sys_uselib(const char * library) { - int fd, error; + int fd, retval; struct file * file; - struct inode * inode; - struct exec ex; - unsigned int len; - unsigned int bss; + struct linux_binfmt * fmt; fd = sys_open(library, 0, 0); if (fd < 0) return fd; file = current->filp[fd]; - if (!file || !(inode = file->f_inode) || !file->f_op || !file->f_op->read) { - sys_close(fd); - return -ENOEXEC; - } - set_fs(KERNEL_DS); - if (file->f_op->read(inode, file, (char *) &ex, sizeof(ex)) != sizeof(ex)) { - sys_close(fd); - return -EACCES; - } - set_fs(USER_DS); - - /* We come in here for the regular a.out style of shared libraries */ - if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || - ex.a_drsize || ex.a_entry & 0xfff || - inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) { - sys_close(fd); - return -ENOEXEC; - } - if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) && - (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) { - printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n"); - sys_close(fd); - return -ENOEXEC; + retval = -ENOEXEC; + if (file && file->f_inode && file->f_op && file->f_op->read) { + fmt = formats; + do { + int (*fn)(int) = fmt->load_shlib; + if (!fn) + break; + retval = fn(fd); + fmt++; + } while (retval == -ENOEXEC); } - - /* Now use mmap to map the library into memory. */ - error = do_mmap(file, ex.a_entry, ex.a_text + ex.a_data, - PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE, - N_TXTOFF(ex)); sys_close(fd); - if (error != ex.a_entry) - return error; - len = (ex.a_text + ex.a_data + 0xfff) & 0xfffff000; - bss = ex.a_text + ex.a_data + ex.a_bss; - if (bss > len) - zeromap_page_range(ex.a_entry + len, bss-len, PAGE_COPY); - return 0; + return retval; } /* @@ -240,7 +255,7 @@ * memory and creates the pointer tables from them, and puts their * addresses on the "stack", returning the new stack pointer value. */ -static unsigned long * create_tables(char * p,int argc,int envc) +unsigned long * create_tables(char * p,int argc,int envc) { unsigned long *argv,*envp; unsigned long * sp; @@ -351,7 +366,7 @@ return p; } -static unsigned long change_ldt(unsigned long text_size,unsigned long * page) +unsigned long change_ldt(unsigned long text_size,unsigned long * page) { unsigned long code_limit,data_limit,code_base,data_base; int i; @@ -363,8 +378,10 @@ data_base += data_limit; for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) { data_base -= PAGE_SIZE; - if (page[i]) + if (page[i]) { + current->rss++; put_dirty_page(current,page[i],data_base); + } } return data_limit; } @@ -374,7 +391,7 @@ * that aren't on a block boundary, and for files on filesystems * without bmap support. */ -static int read_exec(struct inode *inode, unsigned long offset, +int read_exec(struct inode *inode, unsigned long offset, char * addr, unsigned long count) { struct file file; @@ -413,14 +430,16 @@ * that a new one can be started */ -static void flush_old_exec(struct inode * inode, char * filename, int e_uid, int e_gid) +void flush_old_exec(struct linux_binprm * bprm) { int i; int ch; + char * name; struct vm_area_struct * mpnt, *mpnt1; current->dumpable = 1; - for (i=0; (ch = get_fs_byte(filename++)) != '\0';) { + name = bprm->filename; + for (i=0; (ch = *(name++)) != '\0';) { if (ch == '/') i = 0; else @@ -445,7 +464,22 @@ kfree(mpnt); mpnt = mpnt1; } - if (e_uid != current->euid || e_gid != current->egid || !permission(inode,MAY_READ)) + + /* Flush the old ldt stuff... */ + if (current->ldt) { + free_page((unsigned long) current->ldt); + current->ldt = NULL; + for (i=1 ; ie_uid != current->euid || bprm->e_gid != current->egid || + !permission(bprm->inode,MAY_READ)) current->dumpable = 0; current->signal = 0; for (i=0 ; i<32 ; i++) { @@ -468,48 +502,43 @@ /* * sys_execve() executes a new program. */ -extern "C" int sys_execve(struct pt_regs regs) +static int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs) { - char * filename = (char *) regs.ebx; - char ** argv = (char **) regs.ecx; - char ** envp = (char **) regs.edx; - struct inode * inode; - char buf[128]; + struct linux_binprm bprm; + struct linux_binfmt * fmt; unsigned long old_fs; - struct exec ex; - unsigned long page[MAX_ARG_PAGES]; - int i,argc,envc; - int e_uid, e_gid; + int i; int retval; int sh_bang = 0; - unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4; - if (regs.cs != USER_CS) - panic("VFS: execve called from supervisor mode"); + if (regs->cs != USER_CS) + return -EINVAL; + bprm.p = PAGE_SIZE*MAX_ARG_PAGES-4; for (i=0 ; ii_mode)) { /* must be regular file */ + if (!S_ISREG(bprm.inode->i_mode)) { /* must be regular file */ retval = -EACCES; goto exec_error2; } - if (IS_NOEXEC(inode)) { /* FS mustn't be mounted noexec */ + if (IS_NOEXEC(bprm.inode)) { /* FS mustn't be mounted noexec */ retval = -EPERM; goto exec_error2; } - if (!inode->i_sb) { + if (!bprm.inode->i_sb) { retval = -EACCES; goto exec_error2; } - i = inode->i_mode; - if (IS_NOSUID(inode) && (((i & S_ISUID) && inode->i_uid != current-> - euid) || ((i & S_ISGID) && !in_group_p(inode->i_gid))) && + i = bprm.inode->i_mode; + if (IS_NOSUID(bprm.inode) && (((i & S_ISUID) && bprm.inode->i_uid != current-> + euid) || ((i & S_ISGID) && !in_group_p(bprm.inode->i_gid))) && !suser()) { retval = -EPERM; goto exec_error2; @@ -516,30 +545,29 @@ } /* make sure we don't let suid, sgid files be ptraced. */ if (current->flags & PF_PTRACED) { - e_uid = current->euid; - e_gid = current->egid; + bprm.e_uid = current->euid; + bprm.e_gid = current->egid; } else { - e_uid = (i & S_ISUID) ? inode->i_uid : current->euid; - e_gid = (i & S_ISGID) ? inode->i_gid : current->egid; + bprm.e_uid = (i & S_ISUID) ? bprm.inode->i_uid : current->euid; + bprm.e_gid = (i & S_ISGID) ? bprm.inode->i_gid : current->egid; } - if (current->euid == inode->i_uid) + if (current->euid == bprm.inode->i_uid) i >>= 6; - else if (in_group_p(inode->i_gid)) + else if (in_group_p(bprm.inode->i_gid)) i >>= 3; if (!(i & 1) && - !((inode->i_mode & 0111) && suser())) { + !((bprm.inode->i_mode & 0111) && suser())) { retval = -EACCES; goto exec_error2; } - memset(buf,0,sizeof(buf)); + memset(bprm.buf,0,sizeof(bprm.buf)); old_fs = get_fs(); set_fs(get_ds()); - retval = read_exec(inode,0,buf,128); + retval = read_exec(bprm.inode,0,bprm.buf,128); set_fs(old_fs); if (retval < 0) goto exec_error2; - ex = *((struct exec *) buf); /* exec-header */ - if ((buf[0] == '#') && (buf[1] == '!') && (!sh_bang)) { + if ((bprm.buf[0] == '#') && (bprm.buf[1] == '!') && (!sh_bang)) { /* * This section does the #! interpretation. * Sorta complicated, but hopefully it will work. -TYT @@ -547,12 +575,12 @@ char *cp, *interp, *i_name, *i_arg; - iput(inode); - buf[127] = '\0'; - if ((cp = strchr(buf, '\n')) == NULL) - cp = buf+127; + iput(bprm.inode); + bprm.buf[127] = '\0'; + if ((cp = strchr(bprm.buf, '\n')) == NULL) + cp = bprm.buf+127; *cp = '\0'; - while (cp > buf) { + while (cp > bprm.buf) { cp--; if ((*cp == ' ') || (*cp == '\t')) *cp = '\0'; @@ -559,7 +587,7 @@ else break; } - for (cp = buf+2; (*cp == ' ') || (*cp == '\t'); cp++); + for (cp = bprm.buf+2; (*cp == ' ') || (*cp == '\t'); cp++); if (!cp || *cp == '\0') { retval = -ENOEXEC; /* No interpreter name found */ goto exec_error1; @@ -579,8 +607,8 @@ * (optional) argument. */ if (sh_bang++ == 0) { - p = copy_strings(envc, envp, page, p, 0); - p = copy_strings(--argc, argv+1, page, p, 0); + bprm.p = copy_strings(bprm.envc, envp, bprm.page, bprm.p, 0); + bprm.p = copy_strings(--bprm.argc, argv+1, bprm.page, bprm.p, 0); } /* * Splice in (1) the interpreter's name for argv[0] @@ -590,15 +618,15 @@ * This is done in reverse order, because of how the * user environment and arguments are stored. */ - p = copy_strings(1, &filename, page, p, 1); - argc++; + bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p, 2); + bprm.argc++; if (i_arg) { - p = copy_strings(1, &i_arg, page, p, 2); - argc++; + bprm.p = copy_strings(1, &i_arg, bprm.page, bprm.p, 2); + bprm.argc++; } - p = copy_strings(1, &i_name, page, p, 2); - argc++; - if (!p) { + bprm.p = copy_strings(1, &i_name, bprm.page, bprm.p, 2); + bprm.argc++; + if (!bprm.p) { retval = -E2BIG; goto exec_error1; } @@ -607,71 +635,198 @@ * Note that we use open_namei() as the name is now in kernel * space, and we don't need to copy it. */ - retval = open_namei(interp,0,0,&inode,NULL); + retval = open_namei(interp, 0, 0, &bprm.inode, NULL); if (retval) goto exec_error1; goto restart_interp; } + if (!sh_bang) { + bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p,0); + bprm.p = copy_strings(bprm.argc,argv,bprm.page,bprm.p,0); + if (!bprm.p) { + retval = -E2BIG; + goto exec_error2; + } + } + + fmt = formats; + do { + int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary; + if (!fn) + break; + retval = fn(&bprm, regs); + if (retval == 0) { + iput(bprm.inode); + return 0; + } + fmt++; + } while (retval == -ENOEXEC); +exec_error2: + iput(bprm.inode); +exec_error1: + for (i=0 ; ip; + + ex = *((struct exec *) bprm->buf); /* exec-header */ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC) || - ex.a_trsize || ex.a_drsize || - inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) { - retval = -ENOEXEC; - goto exec_error2; + ex.a_trsize || ex.a_drsize || + bprm->inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) { + return -ENOEXEC; } if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) && - (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) { + (N_TXTOFF(ex) < bprm->inode->i_sb->s_blocksize)) { printk("N_TXTOFF < BLOCK_SIZE. Please convert binary."); - retval = -ENOEXEC; - goto exec_error2; + return -ENOEXEC; } if (N_TXTOFF(ex) != BLOCK_SIZE && N_MAGIC(ex) != OMAGIC) { printk("N_TXTOFF != BLOCK_SIZE. See a.out.h."); - retval = -ENOEXEC; - goto exec_error2; - } - if (!sh_bang) { - p = copy_strings(envc,envp,page,p,0); - p = copy_strings(argc,argv,page,p,0); - if (!p) { - retval = -E2BIG; - goto exec_error2; - } + return -ENOEXEC; } -/* OK, This is the point of no return */ - flush_old_exec(inode, filename, e_uid, e_gid); - p += change_ldt(ex.a_text,page); - p -= MAX_ARG_PAGES*PAGE_SIZE; - p = (unsigned long) create_tables((char *)p,argc,envc); - current->brk = ex.a_bss + + + /* OK, This is the point of no return */ + flush_old_exec(bprm); + current->start_brk = current->brk = ex.a_bss + (current->end_data = ex.a_data + - (current->end_code = ex.a_text)); - current->start_stack = p; - current->rss = (TASK_SIZE - p + PAGE_SIZE-1) / PAGE_SIZE; - current->suid = current->euid = e_uid; + (current->end_code = ex.a_text)); + current->rss = 0; + current->suid = current->euid = bprm->e_uid; current->mmap = NULL; - current->sgid = current->egid = e_gid; + current->executable = NULL; /* for OMAGIC files */ + current->sgid = current->egid = bprm->e_gid; if (N_MAGIC(ex) == OMAGIC) { - read_exec(inode, 32, (char *) 0, ex.a_text+ex.a_data); - iput(inode); - } else if (!inode->i_op || !inode->i_op->bmap) { - read_exec(inode, 1024, (char *) 0, ex.a_text+ex.a_data); - iput(inode); + read_exec(bprm->inode, 32, (char *) 0, ex.a_text+ex.a_data); } else { if (ex.a_text & 0xfff || ex.a_data & 0xfff) printk("%s: executable not page aligned\n", current->comm); - current->executable = inode; + + fd = open_inode(bprm->inode, O_RDONLY); + + if (fd < 0) + return fd; + file = current->filp[fd]; + if (!file->f_op || !file->f_op->mmap) { + sys_close(fd); + read_exec(bprm->inode, 1024, (char *) 0, ex.a_text+ex.a_data); + goto beyond_if; + } + error = do_mmap(file, 0, ex.a_text, + PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_SHARED, N_TXTOFF(ex)); + if (error != 0) { + sys_close(fd); + send_sig(SIGSEGV, current, 0); + return 0; + }; + + error = do_mmap(file, ex.a_text, ex.a_data, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, N_TXTOFF(ex) + ex.a_text); + sys_close(fd); + if (error != ex.a_text) { + send_sig(SIGSEGV, current, 0); + return 0; + }; + current->executable = bprm->inode; + bprm->inode->i_count++; } +beyond_if: zeromap_page_range((ex.a_text + ex.a_data + 0xfff) & 0xfffff000,ex.a_bss, PAGE_COPY); - regs.eip = ex.a_entry; /* eip, magic happens :-) */ - regs.esp = p; /* stack pointer */ + p += change_ldt(ex.a_text,bprm->page); + p -= MAX_ARG_PAGES*PAGE_SIZE; + p = (unsigned long) create_tables((char *)p,bprm->argc,bprm->envc); + current->start_stack = p; + regs->eip = ex.a_entry; /* eip, magic happens :-) */ + regs->esp = p; /* stack pointer */ if (current->flags & PF_PTRACED) send_sig(SIGTRAP, current, 0); return 0; -exec_error2: - iput(inode); -exec_error1: - for (i=0 ; ifilp[fd]; + inode = file->f_inode; + + set_fs(KERNEL_DS); + if (file->f_op->read(inode, file, (char *) &ex, sizeof(ex)) != sizeof(ex)) { + return -EACCES; + } + set_fs(USER_DS); + + /* We come in here for the regular a.out style of shared libraries */ + if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || + ex.a_drsize || ex.a_entry & 0xfff || + inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) { + return -ENOEXEC; + } + if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) && + (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) { + printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n"); + return -ENOEXEC; + } + + /* Now use mmap to map the library into memory. */ + error = do_mmap(file, ex.a_entry, ex.a_text + ex.a_data, + PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE, + N_TXTOFF(ex)); + if (error != ex.a_entry) + return error; + len = (ex.a_text + ex.a_data + 0xfff) & 0xfffff000; + bss = ex.a_text + ex.a_data + ex.a_bss; + if (bss > len) + zeromap_page_range(ex.a_entry + len, bss-len, PAGE_COPY); + return 0; +} diff -u --recursive --new-file pl11/linux/fs/ext2/acl.c linux/fs/ext2/acl.c --- pl11/linux/fs/ext2/acl.c Fri Apr 23 19:57:54 1993 +++ linux/fs/ext2/acl.c Mon Aug 9 18:02:29 1993 @@ -12,6 +12,7 @@ #include #include #include +#include /* * ext2_permission () @@ -30,7 +31,7 @@ mode >>= 6; else if (in_group_p (inode->i_gid)) mode >>= 3; - if (((mode & mask & 0007) == mask)) + if (((mode & mask & S_IRWXO) == mask)) return 1; else return 0; diff -u --recursive --new-file pl11/linux/fs/ext2/balloc.c linux/fs/ext2/balloc.c --- pl11/linux/fs/ext2/balloc.c Sat Jun 26 10:28:05 1993 +++ linux/fs/ext2/balloc.c Thu Aug 5 21:33:22 1993 @@ -35,7 +35,7 @@ "rep\n\t" \ "stosl" \ : \ - :"a" (0), "c" (size/4), "D" ((long) (addr)) \ + :"a" (0), "c" (size / 4), "D" ((long) (addr)) \ :"cx", "di") static inline int find_first_zero_bit (unsigned long * addr, unsigned size) @@ -58,7 +58,7 @@ shll $3,%%edi addl %%edi,%%edx" :"=d" (res) - :"c" ((size+31)>>5), "D" (addr), "b" (addr) + :"c" ((size + 31) >> 5), "D" (addr), "b" (addr) :"ax", "bx", "cx", "di"); return res; } @@ -239,13 +239,12 @@ return; } lock_super (sb); - if (block < sb->u.ext2_sb.s_first_data_block || - block >= sb->u.ext2_sb.s_blocks_count) { + es = sb->u.ext2_sb.s_es; + if (block < es->s_first_data_block || block >= es->s_blocks_count) { printk ("ext2_free_block: block not in datazone\n"); unlock_super (sb); return; } - es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data; #ifdef EXT2FS_DEBUG printk ("ext2_free_block: freeing block %d\n", block); #endif @@ -253,10 +252,9 @@ if (bh) bh->b_dirt = 0; brelse (bh); - block_group = (block - sb->u.ext2_sb.s_first_data_block) / + block_group = (block - es->s_first_data_block) / EXT2_BLOCKS_PER_GROUP(sb); - bit = (block - sb->u.ext2_sb.s_first_data_block) % - EXT2_BLOCKS_PER_GROUP(sb); + bit = (block - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb); bitmap_nr = load_block_bitmap (sb, block_group); bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; if (!bh) { @@ -275,11 +273,11 @@ panic ("ext2_free_block: Group descriptor not loaded"); } gdp = (struct ext2_group_desc *) bh2->b_data; - gdp[desc].bg_free_blocks_count ++; + gdp[desc].bg_free_blocks_count++; bh2->b_dirt = 1; } bh->b_dirt = 1; - es->s_free_blocks_count ++; + es->s_free_blocks_count++; sb->u.ext2_sb.s_sbh->b_dirt = 1; sb->s_dirt = 1; unlock_super (sb); @@ -313,7 +311,7 @@ return 0; } lock_super (sb); - es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data; + es = sb->u.ext2_sb.s_es; if (es->s_free_blocks_count <= es->s_r_blocks_count && !suser()) { unlock_super (sb); return 0; @@ -325,8 +323,7 @@ repeat: /* First, test whether the goal block is free. */ - i = ((goal - sb->u.ext2_sb.s_first_data_block) / - EXT2_BLOCKS_PER_GROUP(sb)); + i = ((goal - es->s_first_data_block) / EXT2_BLOCKS_PER_GROUP(sb)); group_desc = i / EXT2_DESC_PER_BLOCK(sb); desc = i % EXT2_DESC_PER_BLOCK(sb); gdp = (struct ext2_group_desc *) @@ -335,7 +332,7 @@ panic ("ext2_new_block: Descriptor not loaded"); } if (gdp[desc].bg_free_blocks_count > 0) { - j = ((goal - sb->u.ext2_sb.s_first_data_block) % + j = ((goal - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb)); #ifdef EXT2FS_DEBUG if (j) @@ -475,8 +472,8 @@ #ifdef EXT2FS_DEBUG printk ("ext2_new_block: found bit %d\n", j); #endif - j += i * EXT2_BLOCKS_PER_GROUP(sb) + sb->u.ext2_sb.s_first_data_block; - if (j >= sb->u.ext2_sb.s_blocks_count) { + j += i * EXT2_BLOCKS_PER_GROUP(sb) + es->s_first_data_block; + if (j >= es->s_blocks_count) { printk ("block_group = %d,block=%d\n", i, j); printk ("ext2_new_block: block >= blocks count"); unlock_super (sb); @@ -495,9 +492,9 @@ printk("ext2_new_block: allocating block %d. " "Goal hits %d of %d.\n", j, goal_hits, goal_attempts); #endif - gdp[desc].bg_free_blocks_count --; + gdp[desc].bg_free_blocks_count--; sb->u.ext2_sb.s_group_desc[group_desc]->b_dirt = 1; - es->s_free_blocks_count --; + es->s_free_blocks_count--; sb->u.ext2_sb.s_sbh->b_dirt = 1; sb->s_dirt = 1; unlock_super (sb); @@ -506,8 +503,8 @@ unsigned long ext2_count_free_blocks (struct super_block *sb) { - struct ext2_super_block * es; #ifdef EXT2FS_DEBUG + struct ext2_super_block * es; unsigned long desc_count, bitmap_count, x; unsigned long group_desc; unsigned long desc; @@ -516,7 +513,7 @@ int i; lock_super (sb); - es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data; + es = sb->u.ext2_sb.s_es; desc_count = 0; bitmap_count = 0; group_desc = 0; @@ -545,9 +542,9 @@ printk ("group %d: stored = %d, counted = %d\n", i, gdp[desc].bg_free_blocks_count, x); bitmap_count += x; - desc ++; + desc++; if (desc == EXT2_DESC_PER_BLOCK(sb)) { - group_desc ++; + group_desc++; desc = 0; gdp = NULL; } @@ -557,7 +554,6 @@ unlock_super (sb); return bitmap_count; #else - es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data; - return es->s_free_blocks_count; + return sb->u.ext2_sb.s_es->s_free_blocks_count; #endif } diff -u --recursive --new-file pl11/linux/fs/ext2/dcache.c linux/fs/ext2/dcache.c --- pl11/linux/fs/ext2/dcache.c Thu May 27 22:23:46 1993 +++ linux/fs/ext2/dcache.c Thu Aug 5 21:33:22 1993 @@ -25,15 +25,15 @@ unsigned long ino; char name[EXT2_NAME_LEN]; int len; - struct dir_cache_entry *queue_prev; - struct dir_cache_entry *queue_next; - struct dir_cache_entry *prev; - struct dir_cache_entry *next; + struct dir_cache_entry * queue_prev; + struct dir_cache_entry * queue_next; + struct dir_cache_entry * prev; + struct dir_cache_entry * next; }; -static struct dir_cache_entry *first = NULL; -static struct dir_cache_entry *last = NULL; -static struct dir_cache_entry *first_free = NULL; +static struct dir_cache_entry * first = NULL; +static struct dir_cache_entry * last = NULL; +static struct dir_cache_entry * first_free = NULL; static int cache_initialized = 0; #ifdef EXT2FS_DEBUG_CACHE static int hits = 0; @@ -46,8 +46,8 @@ #define HASH_QUEUES 16 -static struct dir_cache_entry *queue_head[HASH_QUEUES]; -static struct dir_cache_entry *queue_tail[HASH_QUEUES]; +static struct dir_cache_entry * queue_head[HASH_QUEUES]; +static struct dir_cache_entry * queue_tail[HASH_QUEUES]; #define hash(dev,dir) ((dev ^ dir) % HASH_QUEUES) @@ -78,14 +78,15 @@ /* * Find a name in the cache */ -static struct dir_cache_entry *find_name (int queue, unsigned short dev, - unsigned long dir, const char *name, - int len) -{ - struct dir_cache_entry *p; - - for (p = queue_head[queue]; p!= NULL && (p->dev != dev || - p->dir != dir || p->len != len || strncmp (name, p->name, p->len)); +static struct dir_cache_entry * find_name (int queue, unsigned short dev, + unsigned long dir, + const char * name, int len) +{ + struct dir_cache_entry * p; + + for (p = queue_head[queue]; p != NULL && (p->dev != dev || + p->dir != dir || p->len != len || + strncmp (name, p->name, p->len) != 0); p = p->queue_next) ; return p; @@ -95,9 +96,9 @@ /* * List the cache entries for debugging */ -static void show_cache (const char *func_name) +static void show_cache (const char * func_name) { - struct dir_cache_entry *p; + struct dir_cache_entry * p; printk ("%s: cache status\n", func_name); for (p = first; p != NULL; p = p->next) @@ -109,7 +110,7 @@ /* * Add an entry at the beginning of the cache */ -static void add_to_cache (struct dir_cache_entry *p) +static void add_to_cache (struct dir_cache_entry * p) { p->prev = NULL; p->next = first; @@ -123,7 +124,7 @@ /* * Add an entry at the beginning of a queue */ -static void add_to_queue (int queue, struct dir_cache_entry *p) +static void add_to_queue (int queue, struct dir_cache_entry * p) { p->queue_prev = NULL; p->queue_next = queue_head[queue]; @@ -137,7 +138,7 @@ /* * Remove an entry from the cache */ -static void remove_from_cache (struct dir_cache_entry *p) +static void remove_from_cache (struct dir_cache_entry * p) { if (p->prev) p->prev->next = p->next; @@ -152,7 +153,7 @@ /* * Remove an entry from a queue */ -static void remove_from_queue (int queue, struct dir_cache_entry *p) +static void remove_from_queue (int queue, struct dir_cache_entry * p) { if (p->queue_prev) p->queue_prev->queue_next = p->queue_next; @@ -170,8 +171,8 @@ */ void ext2_dcache_invalidate (unsigned short dev) { - struct dir_cache_entry *p; - struct dir_cache_entry *p2; + struct dir_cache_entry * p; + struct dir_cache_entry * p2; if (!cache_initialized) init_cache (); @@ -193,11 +194,11 @@ * Lookup a directory entry in the cache */ unsigned long ext2_dcache_lookup (unsigned short dev, unsigned long dir, - const char *name, int len) + const char * name, int len) { char our_name[EXT2_NAME_LEN]; int queue; - struct dir_cache_entry *p; + struct dir_cache_entry * p; if (!cache_initialized) init_cache (); @@ -208,7 +209,7 @@ #ifdef EXT2FS_DEBUG_CACHE printk ("dcache_lookup (%04x, %d, %s, %d)\n", dev, dir, our_name, len); #endif - queue = hash(dev, dir); + queue = hash (dev, dir); if ((p = find_name (queue, dev, dir, our_name, len))) { if (p != first) { remove_from_cache (p); @@ -219,7 +220,7 @@ add_to_queue (queue, p); } #ifdef EXT2FS_DEBUG_CACHE - hits ++; + hits++; printk ("dcache_lookup: %s,hit,inode=%d,hits=%d,misses=%d\n", our_name, p->ino, hits, misses); show_cache ("dcache_lookup"); @@ -227,7 +228,7 @@ return p->ino; } else { #ifdef EXT2FS_DEBUG_CACHE - misses ++; + misses++; printk ("dcache_lookup: %s,miss,hits=%d,misses=%d\n", our_name, hits, misses); show_cache ("dcache_lookup"); @@ -242,10 +243,10 @@ * This function is called by ext2_lookup(), ext2_readdir() * and the functions which create directory entries */ -void ext2_dcache_add (unsigned short dev, unsigned long dir, const char *name, +void ext2_dcache_add (unsigned short dev, unsigned long dir, const char * name, int len, int ino) { - struct dir_cache_entry *p; + struct dir_cache_entry * p; int queue; if (!cache_initialized) @@ -256,7 +257,7 @@ #endif if (len > EXT2_NAME_LEN) len = EXT2_NAME_LEN; - queue = hash(dev, dir); + queue = hash (dev, dir); if ((p = find_name (queue, dev, dir, name, len))) { p->dir = dir; p->ino = ino; @@ -303,9 +304,9 @@ * This function is called by the functions which remove directory entries */ void ext2_dcache_remove (unsigned short dev, unsigned long dir, - const char *name, int len) + const char * name, int len) { - struct dir_cache_entry *p; + struct dir_cache_entry * p; int queue; if (!cache_initialized) @@ -315,7 +316,7 @@ #endif if (len > EXT2_NAME_LEN) len = EXT2_NAME_LEN; - queue = hash(dev, dir); + queue = hash (dev, dir); if ((p = find_name (queue, dev, dir, name, len))) { remove_from_cache (p); remove_from_queue (queue, p); diff -u --recursive --new-file pl11/linux/fs/ext2/dir.c linux/fs/ext2/dir.c --- pl11/linux/fs/ext2/dir.c Sat Jul 3 10:53:27 1993 +++ linux/fs/ext2/dir.c Thu Aug 5 21:33:22 1993 @@ -104,7 +104,8 @@ sb = inode->i_sb; while (filp->f_pos < inode->i_size) { offset = filp->f_pos & (sb->s_blocksize - 1); - bh = ext2_bread (inode, (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb), 0, &err); + bh = ext2_bread (inode, (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb), + 0, &err); if (!bh) { filp->f_pos += sb->s_blocksize - offset; continue; diff -u --recursive --new-file pl11/linux/fs/ext2/file.c linux/fs/ext2/file.c --- pl11/linux/fs/ext2/file.c Tue Jul 6 09:42:03 1993 +++ linux/fs/ext2/file.c Thu Aug 5 21:33:22 1993 @@ -44,7 +44,7 @@ ext2_file_write, /* write */ NULL, /* readdir - bad */ NULL, /* select - default */ - ext2_ioctl, /* ioctl - default */ + ext2_ioctl, /* ioctl */ generic_mmap, /* mmap */ NULL, /* no special open is needed */ NULL, /* release */ @@ -228,9 +228,6 @@ while (written < count) { bh = ext2_getblk (inode, pos / sb->s_blocksize, 1, &err); if (!bh) { -#ifdef EXT2FS_DEBUG - printk ("ext2_file_write: ext2_getblk returned NULL\n"); -#endif if (!written) written = err; break; diff -u --recursive --new-file pl11/linux/fs/ext2/fsync.c linux/fs/ext2/fsync.c --- pl11/linux/fs/ext2/fsync.c Mon Jun 7 00:38:54 1993 +++ linux/fs/ext2/fsync.c Thu Aug 5 21:33:22 1993 @@ -35,7 +35,7 @@ if (!*block) return 0; tmp = *block; - bh = get_hash_table(inode->i_dev, *block, blocksize); + bh = get_hash_table (inode->i_dev, *block, blocksize); if (!bh) return 0; if (*block != tmp) { @@ -43,21 +43,20 @@ return 1; } if (wait && bh->b_req && !bh->b_uptodate) { - brelse(bh); + brelse (bh); return -1; } - if (wait || !bh->b_uptodate || !bh->b_dirt) - { - brelse(bh); + if (wait || !bh->b_uptodate || !bh->b_dirt) { + brelse (bh); return 0; } - ll_rw_block(WRITE, 1, &bh); + ll_rw_block (WRITE, 1, &bh); bh->b_count--; return 0; } static int sync_iblock (struct inode * inode, unsigned long * iblock, - struct buffer_head **bh, int wait) + struct buffer_head ** bh, int wait) { int rc, tmp; @@ -68,9 +67,9 @@ rc = sync_block (inode, iblock, wait); if (rc) return rc; - *bh = bread(inode->i_dev, tmp, blocksize); + *bh = bread (inode->i_dev, tmp, blocksize); if (tmp != *iblock) { - brelse(*bh); + brelse (*bh); *bh = NULL; return 1; } @@ -80,7 +79,7 @@ } -static int sync_direct(struct inode *inode, int wait) +static int sync_direct (struct inode * inode, int wait) { int i; int rc, err = 0; @@ -95,7 +94,8 @@ return err; } -static int sync_indirect(struct inode *inode, unsigned long *iblock, int wait) +static int sync_indirect (struct inode * inode, unsigned long * iblock, + int wait) { int i; struct buffer_head * ind_bh; @@ -114,12 +114,12 @@ if (rc) err = rc; } - brelse(ind_bh); + brelse (ind_bh); return err; } -static int sync_dindirect(struct inode *inode, unsigned long *diblock, - int wait) +static int sync_dindirect (struct inode * inode, unsigned long * diblock, + int wait) { int i; struct buffer_head * dind_bh; @@ -138,12 +138,12 @@ if (rc) err = rc; } - brelse(dind_bh); + brelse (dind_bh); return err; } -static int sync_tindirect(struct inode *inode, unsigned long *tiblock, - int wait) +static int sync_tindirect (struct inode * inode, unsigned long * tiblock, + int wait) { int i; struct buffer_head * tind_bh; @@ -162,11 +162,11 @@ if (rc) err = rc; } - brelse(tind_bh); + brelse (tind_bh); return err; } -int ext2_sync_file(struct inode * inode, struct file *file) +int ext2_sync_file (struct inode * inode, struct file * file) { int wait, err = 0; @@ -179,16 +179,16 @@ for (wait=0; wait<=1; wait++) { - err |= sync_direct(inode, wait); - err |= sync_indirect(inode, - inode->u.ext2_i.i_data+EXT2_IND_BLOCK, - wait); - err |= sync_dindirect(inode, - inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, - wait); - err |= sync_tindirect(inode, - inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, + err |= sync_direct (inode, wait); + err |= sync_indirect (inode, + inode->u.ext2_i.i_data+EXT2_IND_BLOCK, wait); + err |= sync_dindirect (inode, + inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, + wait); + err |= sync_tindirect (inode, + inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, + wait); } skip: err |= ext2_sync_inode (inode); diff -u --recursive --new-file pl11/linux/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c --- pl11/linux/fs/ext2/ialloc.c Tue Jul 6 09:42:03 1993 +++ linux/fs/ext2/ialloc.c Thu Aug 5 21:33:22 1993 @@ -221,12 +221,13 @@ #endif sb = inode->i_sb; lock_super (sb); - if (inode->i_ino < 1 || inode->i_ino > sb->u.ext2_sb.s_inodes_count) { + if (inode->i_ino < 1 || + inode->i_ino > sb->u.ext2_sb.s_es->s_inodes_count) { printk("free_inode: inode 0 or nonexistent inode\n"); unlock_super (sb); return; } - es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data; + es = sb->u.ext2_sb.s_es; block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(sb); bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb); bitmap_nr = load_inode_bitmap (sb, block_group); @@ -247,14 +248,14 @@ panic ("ext2_free_inode: Group descriptor not loaded"); } gdp = (struct ext2_group_desc *) bh2->b_data; - gdp[desc].bg_free_inodes_count ++; + gdp[desc].bg_free_inodes_count++; if (S_ISDIR(inode->i_mode)) - gdp[desc].bg_used_dirs_count --; + gdp[desc].bg_used_dirs_count--; bh2->b_dirt = 1; set_inode_dtime (inode, gdp, desc); } bh->b_dirt = 1; - es->s_free_inodes_count ++; + es->s_free_inodes_count++; sb->u.ext2_sb.s_sbh->b_dirt = 1; sb->s_dirt = 1; unlock_super (sb); @@ -295,10 +296,11 @@ brelse (bh); } -static struct ext2_group_desc * get_group_desc(struct super_block * sb, - int group) +static struct ext2_group_desc * get_group_desc (struct super_block * sb, + int group) { struct ext2_group_desc * gdp; + if (group >= sb->u.ext2_sb.s_groups_count || group < 0 ) panic ("ext2: get_group_desc: Invalid group\n"); if (!sb->u.ext2_sb.s_group_desc[group / EXT2_DESC_PER_BLOCK(sb)]) @@ -335,7 +337,7 @@ inode->i_sb = sb; inode->i_flags = sb->s_flags; lock_super (sb); - es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data; + es = sb->u.ext2_sb.s_es; repeat: gdp = NULL; i=0; @@ -345,7 +347,7 @@ /* I am not yet convinced that this next bit is necessary. i = dir->u.ext2_i.i_block_group; for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) { - tmp = get_group_desc(sb, i); + tmp = get_group_desc (sb, i); if ((tmp->bg_used_dirs_count << 8) < tmp->bg_free_inodes_count) { gdp = tmp; @@ -357,7 +359,7 @@ */ if (!gdp) { for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) { - tmp = get_group_desc(sb, j); + tmp = get_group_desc (sb, j); if (tmp->bg_free_inodes_count && tmp->bg_free_inodes_count >= avefreei) { if (!gdp || @@ -373,7 +375,7 @@ else { /* Try to place the inode in it\'s parent directory */ i = dir->u.ext2_i.i_block_group; - tmp = get_group_desc(sb, i); + tmp = get_group_desc (sb, i); if (tmp->bg_free_inodes_count) gdp = tmp; else @@ -382,7 +384,7 @@ i += j; if (i >= sb->u.ext2_sb.s_groups_count) i -= sb->u.ext2_sb.s_groups_count; - tmp = get_group_desc(sb, i); + tmp = get_group_desc (sb, i); if (tmp->bg_free_inodes_count) { gdp = tmp; break; @@ -395,7 +397,7 @@ for (j = 2; j < sb->u.ext2_sb.s_groups_count; j++) { if (++i >= sb->u.ext2_sb.s_groups_count) i = 0; - tmp = get_group_desc(sb,i); + tmp = get_group_desc (sb,i); if (tmp->bg_free_inodes_count) { gdp = tmp; break; @@ -426,16 +428,18 @@ } else goto repeat; j += i * EXT2_INODES_PER_GROUP(sb) + 1; - if (j > sb->u.ext2_sb.s_inodes_count) { + if (j > es->s_inodes_count) { printk ("block_group = %d,inode=%d\n", i, j); printk ("ext2_new_inode: inode > inodes count"); + unlock_super (sb); + iput (inode); return NULL; } - gdp->bg_free_inodes_count --; + gdp->bg_free_inodes_count--; if (S_ISDIR(mode)) - gdp->bg_used_dirs_count ++; + gdp->bg_used_dirs_count++; sb->u.ext2_sb.s_group_desc[i / EXT2_DESC_PER_BLOCK(sb)]->b_dirt = 1; - es->s_free_inodes_count --; + es->s_free_inodes_count--; sb->u.ext2_sb.s_sbh->b_dirt = 1; sb->s_dirt = 1; inode->i_mode = mode; @@ -470,8 +474,8 @@ unsigned long ext2_count_free_inodes (struct super_block *sb) { - struct ext2_super_block * es; #ifdef EXT2FS_DEBUG + struct ext2_super_block * es; unsigned long desc_count, bitmap_count, x; unsigned long group_desc; unsigned long desc; @@ -480,7 +484,7 @@ int i; lock_super (sb); - es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data; + es = sb->u.ext2_sb.s_es; desc_count = 0; bitmap_count = 0; group_desc = 0; @@ -507,9 +511,9 @@ printk ("group %d: stored = %d, counted = %d\n", i, gdp[desc].bg_free_inodes_count, x); bitmap_count += x; - desc ++; + desc++; if (desc == EXT2_DESC_PER_BLOCK(sb)) { - group_desc ++; + group_desc++; desc = 0; gdp = NULL; } @@ -519,7 +523,6 @@ unlock_super (sb); return desc_count; #else - es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data; - return es->s_free_inodes_count; + return sb->u.ext2_sb.s_es->s_free_inodes_count; #endif } diff -u --recursive --new-file pl11/linux/fs/ext2/inode.c linux/fs/ext2/inode.c --- pl11/linux/fs/ext2/inode.c Sat Jun 26 10:28:05 1993 +++ linux/fs/ext2/inode.c Thu Aug 5 21:33:23 1993 @@ -37,13 +37,13 @@ void ext2_put_super (struct super_block * sb) { - struct ext2_super_block * es; int i; lock_super (sb); - es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data; - es->s_valid = sb->u.ext2_sb.s_was_mounted_valid; - sb->u.ext2_sb.s_sbh->b_dirt = 1; + if ((sb->s_flags & MS_RDONLY) == 0) { + sb->u.ext2_sb.s_es->s_valid = sb->u.ext2_sb.s_was_mounted_valid; + sb->u.ext2_sb.s_sbh->b_dirt = 1; + } #ifndef DONT_USE_DCACHE ext2_dcache_invalidate (sb->s_dev); #endif @@ -57,6 +57,7 @@ for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) if (sb->u.ext2_sb.s_block_bitmap[i]) brelse (sb->u.ext2_sb.s_block_bitmap[i]); + brelse (sb->u.ext2_sb.s_sbh); unlock_super (sb); return; } @@ -127,7 +128,7 @@ #endif lock_super (s); - set_blocksize(dev, BLOCK_SIZE); + set_blocksize (dev, BLOCK_SIZE); if (!(bh = bread (dev, 1, BLOCK_SIZE))) { s->s_dev = 0; unlock_super (s); @@ -135,30 +136,48 @@ return NULL; } es = (struct ext2_super_block *) bh->b_data; + /* Note: s_es must be initialized s_es as soon as possible because + some ext2 macro-instructions depend on its value */ + s->u.ext2_sb.s_es = es; s->s_magic = es->s_magic; + if (s->s_magic != EXT2_SUPER_MAGIC +#ifdef EXT2FS_PRE_02B_COMPAT + && s->s_magic != EXT2_OLD_SUPER_MAGIC +#endif + ) { + s->s_dev = 0; + unlock_super (s); + brelse (bh); + if (!silent) + printk ("VFS: Can't find an ext2 filesystem on dev 0x%04x.\n", + dev); + return NULL; + } s->s_blocksize = EXT2_MIN_BLOCK_SIZE << es->s_log_block_size; - s->u.ext2_sb.s_log_block_size = es->s_log_block_size; s->s_blocksize_bits = EXT2_BLOCK_SIZE_BITS(s); if (s->s_blocksize != BLOCK_SIZE && (s->s_blocksize == 1024 || s->s_blocksize == 2048 || s->s_blocksize == 4096)) { - brelse(bh); - set_blocksize(dev, s->s_blocksize); - bh = bread (dev, 0, s->s_blocksize); + brelse (bh); + set_blocksize (dev, s->s_blocksize); + bh = bread (dev, 0, s->s_blocksize); if(!bh) return NULL; - es = (struct ext2_super_block *) (((char *)bh->b_data) + BLOCK_SIZE) ; - }; - s->u.ext2_sb.s_inodes_count = es->s_inodes_count; - s->u.ext2_sb.s_blocks_count = es->s_blocks_count; - s->u.ext2_sb.s_r_blocks_count = es->s_r_blocks_count; - s->u.ext2_sb.s_first_data_block = es->s_first_data_block; - s->u.ext2_sb.s_log_frag_size = es->s_log_frag_size; + es = (struct ext2_super_block *) (((char *)bh->b_data) + BLOCK_SIZE); + s->u.ext2_sb.s_es = es; + if (es->s_magic != EXT2_SUPER_MAGIC) { + s->s_dev = 0; + unlock_super (s); + brelse (bh); + printk ("ext2-FS: Magic mismatch, very weird !\n"); + return NULL; + } + } s->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE << - es->s_log_frag_size; + es->s_log_frag_size; if (s->u.ext2_sb.s_frag_size) s->u.ext2_sb.s_frags_per_block = s->s_blocksize / - s->u.ext2_sb.s_frag_size; + s->u.ext2_sb.s_frag_size; else s->s_magic = 0; s->u.ext2_sb.s_blocks_per_group = es->s_blocks_per_group; @@ -165,10 +184,11 @@ s->u.ext2_sb.s_frags_per_group = es->s_frags_per_group; s->u.ext2_sb.s_inodes_per_group = es->s_inodes_per_group; s->u.ext2_sb.s_inodes_per_block = s->s_blocksize / - sizeof (struct ext2_inode); + sizeof (struct ext2_inode); s->u.ext2_sb.s_desc_per_block = s->s_blocksize / - sizeof (struct ext2_group_desc); + sizeof (struct ext2_group_desc); s->u.ext2_sb.s_sbh = bh; + s->u.ext2_sb.s_es = es; s->u.ext2_sb.s_was_mounted_valid = es->s_valid; s->u.ext2_sb.s_rename_lock = 0; s->u.ext2_sb.s_rename_wait = NULL; @@ -208,8 +228,8 @@ unlock_super (s); brelse (bh); if (!silent) - printk("VFS: Can't find an ext2fs filesystem on dev 0x%04x.\n", - dev); + printk ("VFS: Can't find an ext2 filesystem on dev 0x%04x.\n", + dev); return NULL; } if (s->s_blocksize != bh->b_size) { @@ -217,38 +237,41 @@ unlock_super (s); brelse (bh); if (!silent) - printk("VFS: Unsupported blocksize on dev 0x%04x.\n", - dev); + printk ("VFS: Unsupported blocksize on dev 0x%04x.\n", + dev); return NULL; } + if (s->s_blocksize != s->u.ext2_sb.s_frag_size) { s->s_dev = 0; unlock_super (s); brelse (bh); - printk ("EXT2-fs: fragsize != blocksize (not supported yet)\n"); + printk ("EXT2-fs: fragsize %d != blocksize %d (not supported yet)\n", + s->u.ext2_sb.s_frag_size, s->s_blocksize); return NULL; } + if (!es->s_valid) printk ("EXT2-fs warning: mounting unchecked file system, " "running e2fsck is recommended\n"); - s->u.ext2_sb.s_groups_count = (s->u.ext2_sb.s_blocks_count - - s->u.ext2_sb.s_first_data_block + + s->u.ext2_sb.s_groups_count = (es->s_blocks_count - + es->s_first_data_block + EXT2_BLOCKS_PER_GROUP(s) - 1) / EXT2_BLOCKS_PER_GROUP(s); for (i = 0; i < EXT2_MAX_GROUP_DESC; i++) s->u.ext2_sb.s_group_desc[i] = NULL; - bh_count = (s->u.ext2_sb.s_groups_count + - EXT2_DESC_PER_BLOCK(s) - 1) / + bh_count = (s->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(s) - 1) / EXT2_DESC_PER_BLOCK(s); if (bh_count >= EXT2_MAX_GROUP_DESC) { s->s_dev = 0; unlock_super (s); brelse (bh); - printk ("EXT2-fs: Too big file system\n"); + printk ("EXT2-fs: file system too big\n"); return NULL; } for (i = 0; i < bh_count; i++) { - s->u.ext2_sb.s_group_desc[i] = bread (dev, i + 2, s->s_blocksize); + s->u.ext2_sb.s_group_desc[i] = bread (dev, i + 2, + s->s_blocksize); if (!s->u.ext2_sb.s_group_desc[i]) { s->s_dev = 0; unlock_super (s); @@ -255,7 +278,7 @@ for (j = 0; j < i; j++) brelse (s->u.ext2_sb.s_group_desc[i]); brelse (bh); - printk ("ext2_read_super: unable to read group descriptors\n"); + printk ("ext2-FS: unable to read group descriptors\n"); return NULL; } } @@ -271,7 +294,7 @@ /* set up enough so that it can read an inode */ s->s_dev = dev; s->s_op = &ext2_sops; - if (!(s->s_mounted = iget(s, EXT2_ROOT_INO))) { + if (!(s->s_mounted = iget (s, EXT2_ROOT_INO))) { s->s_dev = 0; for (i = 0; i < EXT2_MAX_GROUP_DESC; i++) if (s->u.ext2_sb.s_group_desc[i]) @@ -309,51 +332,13 @@ * flags to 0. We need to set this flag to 0 since the fs * may have been checked while mounted and e2fsck may have * set s_valid to 1 after some corrections. - * - * Note that this function also writes backups of the super block and - * of the group descriptors in each group. */ -static void ext2_commit_super (struct super_block *sb, - struct ext2_super_block *es) +static void ext2_commit_super (struct super_block * sb, + struct ext2_super_block * es) { - struct buffer_head * bh; - unsigned long block; - unsigned long bh_count; - int i, j; - es->s_wtime = CURRENT_TIME; sb->u.ext2_sb.s_sbh->b_dirt = 1; - bh_count = (sb->u.ext2_sb.s_groups_count + - EXT2_DESC_PER_BLOCK(sb) - 1) / - EXT2_DESC_PER_BLOCK(sb); - for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) { - block = sb->u.ext2_sb.s_first_data_block + - i * sb->u.ext2_sb.s_blocks_per_group; - if (!(bh = bread (sb->s_dev, block, BLOCK_SIZE))) - printk ("ext2_commit_super: Unable to read backup super block for group %d\n", i); - else { -#ifdef EXT2FS_DEBUG - printk ("ext2_commit_super: writing super block backup in group %d at block %d\n", i, block); -#endif - memcpy (bh->b_data, es, BLOCK_SIZE); - bh ->b_dirt = 1; - brelse (bh); - } - for (j = 0; j < bh_count; j++) { - block ++; -#ifdef EXT2FS_DEBUG - printk ("ext2_commit_super: writing descriptors (block %d) backup in group %d at block %d\n", j, i, block); -#endif - if (!(bh = bread (sb->s_dev, block, sb->s_blocksize))) - printk ("ext2_commit_super: Unable to read backup descriptor for group %d\n", i); - else { - memcpy (bh->b_data, sb->u.ext2_sb.s_group_desc[j]->b_data, sb->s_blocksize); - bh ->b_dirt = 1; - brelse (bh); - } - } - } sb->s_dirt = 0; } @@ -362,7 +347,7 @@ struct ext2_super_block * es; if ((sb->s_flags & MS_RDONLY) == 0) { - es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data; + es = sb->u.ext2_sb.s_es; #ifdef EXT2FS_DEBUG printk ("ext2_write_super: setting valid to 0\n"); #endif @@ -372,11 +357,11 @@ sb->s_dirt = 0; } -int ext2_remount(struct super_block *sb, int *flags) +int ext2_remount (struct super_block * sb, int * flags) { struct ext2_super_block * es; - es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data; + es = sb->u.ext2_sb.s_es; if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) return 0; if (*flags & MS_RDONLY) { @@ -387,13 +372,16 @@ again. */ sb->s_flags |= MS_RDONLY; es->s_valid = sb->u.ext2_sb.s_was_mounted_valid; - ext2_commit_super(sb, es); + ext2_commit_super (sb, es); } else { /* Mounting a RDONLY partition read-write, so reread and store the current valid flag. (It may have been changed - by ext2fs since we originally mounted the partition.) */ + by e2fsck since we originally mounted the partition.) */ sb->u.ext2_sb.s_was_mounted_valid = es->s_valid; + if (!es->s_valid) + printk ("EXT2-fs warning: remounting unchecked fs, " + "running e2fsck is recommended\n"); } return 0; } @@ -404,17 +392,16 @@ put_fs_long (EXT2_SUPER_MAGIC, &buf->f_type); put_fs_long (sb->s_blocksize, &buf->f_bsize); - put_fs_long (sb->u.ext2_sb.s_blocks_count >> sb->u.ext2_sb.s_log_block_size, - &buf->f_blocks); + put_fs_long (sb->u.ext2_sb.s_es->s_blocks_count, &buf->f_blocks); tmp = ext2_count_free_blocks (sb); put_fs_long (tmp, &buf->f_bfree); - if (tmp >= sb->u.ext2_sb.s_r_blocks_count) - put_fs_long (tmp - sb->u.ext2_sb.s_r_blocks_count, + if (tmp >= sb->u.ext2_sb.s_es->s_r_blocks_count) + put_fs_long (tmp - sb->u.ext2_sb.s_es->s_r_blocks_count, &buf->f_bavail); else put_fs_long (0, &buf->f_bavail); - put_fs_long (sb->u.ext2_sb.s_inodes_count, &buf->f_files); - put_fs_long (ext2_count_free_inodes(sb), &buf->f_ffree); + put_fs_long (sb->u.ext2_sb.s_es->s_inodes_count, &buf->f_files); + put_fs_long (ext2_count_free_inodes (sb), &buf->f_ffree); put_fs_long (EXT2_NAME_LEN, &buf->f_namelen); /* Don't know what value to put in buf->f_fsid */ } @@ -517,7 +504,7 @@ printk ("ext2 inode_getblk: hint = %d,", goal); #endif if (!goal) { - for (tmp = nr-1; tmp>=0; tmp--) { + for (tmp = nr - 1; tmp >= 0; tmp--) { if (inode->u.ext2_i.i_data[tmp]) { goal = inode->u.ext2_i.i_data[tmp]; break; @@ -525,8 +512,8 @@ } if (!goal) goal = (inode->u.ext2_i.i_block_group * - EXT2_BLOCKS_PER_GROUP(inode->i_sb)) - + inode->i_sb->u.ext2_sb.s_first_data_block; + EXT2_BLOCKS_PER_GROUP(inode->i_sb)) + + inode->i_sb->u.ext2_sb.s_es->s_first_data_block; } #ifdef EXT2FS_DEBUG @@ -592,7 +579,7 @@ if (inode->u.ext2_i.i_next_alloc_block == new_block) goal = inode->u.ext2_i.i_next_alloc_goal; if (!goal) { - for (tmp = nr-1; tmp>=0; tmp--) { + for (tmp = nr - 1; tmp >= 0; tmp--) { if (((unsigned long *) bh->b_data)[tmp]) { goal = ((unsigned long *)bh->b_data)[tmp]; break; @@ -714,8 +701,8 @@ if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO && inode->i_ino != EXT2_ACL_DATA_INO && inode->i_ino < EXT2_FIRST_INO) || - inode->i_ino > inode->i_sb->u.ext2_sb.s_inodes_count) { - printk ("ext2_read_inode: bad inode number of dev %0x04: %d\n", + inode->i_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) { + printk ("ext2_read_inode: bad inode number on dev %0x04: %d\n", inode->i_dev, inode->i_ino); return; } @@ -790,7 +777,7 @@ struct ext2_group_desc * gdp; if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino < EXT2_FIRST_INO) || - inode->i_ino > inode->i_sb->u.ext2_sb.s_inodes_count) { + inode->i_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) { printk ("ext2_write_inode: bad inode number of dev %0x04: %d\n", inode->i_dev, inode->i_ino); return 0; @@ -840,7 +827,7 @@ void ext2_write_inode (struct inode * inode) { struct buffer_head * bh; - bh = ext2_update_inode(inode); + bh = ext2_update_inode (inode); brelse (bh); } @@ -849,11 +836,11 @@ int err = 0; struct buffer_head *bh; - bh = ext2_update_inode(inode); + bh = ext2_update_inode (inode); if (bh && bh->b_dirt) { - ll_rw_block(WRITE, 1, &bh); - wait_on_buffer(bh); + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); if (bh->b_req && !bh->b_uptodate) { printk ("IO error syncing ext2 inode [%04x:%08x]\n", diff -u --recursive --new-file pl11/linux/fs/ext2/namei.c linux/fs/ext2/namei.c --- pl11/linux/fs/ext2/namei.c Fri Jul 2 23:12:31 1993 +++ linux/fs/ext2/namei.c Thu Aug 12 20:54:00 1993 @@ -27,10 +27,6 @@ /* #define NO_TRUNCATE */ /* - * ok, we cannot use strncmp, as the name is not in our data space. - * Thus we'll have to use ext2_match. No big problem. ext2_match also makes - * some sanity tests. - * * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure. */ static int ext2_match (int len, const char * const name, @@ -261,6 +257,7 @@ for (i = 0; i < namelen ; i++) de->name[i] = name [i]; dir->i_mtime = dir->i_ctime = CURRENT_TIME; + dir->i_dirt = 1; bh->b_dirt = 1; *res_dir = de; *err = 0; @@ -325,7 +322,7 @@ inode->i_dirt = 1; bh = ext2_add_entry (dir, name, len, &de, &err); if (!bh) { - inode->i_nlink --; + inode->i_nlink--; inode->i_dirt = 1; iput (inode); iput (dir); @@ -388,7 +385,7 @@ inode->i_dirt = 1; bh = ext2_add_entry (dir, name, len, &de, &err); if (!bh) { - inode->i_nlink --; + inode->i_nlink--; inode->i_dirt = 1; iput (inode); iput (dir); @@ -436,7 +433,7 @@ dir_block = ext2_bread (inode, 0, 1, &err); if (!dir_block) { iput (dir); - inode->i_nlink --; + inode->i_nlink--; inode->i_dirt = 1; iput (inode); return err; @@ -455,7 +452,7 @@ inode->i_nlink = 2; dir_block->b_dirt = 1; brelse (dir_block); - inode->i_mode = S_IFDIR | (mode & 0777 & ~current->umask); + inode->i_mode = S_IFDIR | (mode & S_IRWXUGO & ~current->umask); if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; inode->i_dirt = 1; @@ -472,7 +469,7 @@ de->inode); #endif bh->b_dirt = 1; - dir->i_nlink ++; + dir->i_nlink++; dir->i_dirt = 1; iput (dir); iput (inode); @@ -596,7 +593,7 @@ bh->b_dirt = 1; inode->i_nlink = 0; inode->i_dirt = 1; - dir->i_nlink --; + dir->i_nlink--; dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_dirt = 1; end_rmdir: @@ -651,7 +648,7 @@ bh->b_dirt = 1; dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_dirt = 1; - inode->i_nlink --; + inode->i_nlink--; inode->i_dirt = 1; inode->i_ctime = CURRENT_TIME; retval = 0; @@ -677,7 +674,7 @@ iput (dir); return -ENOSPC; } - inode->i_mode = S_IFLNK | 0777; + inode->i_mode = S_IFLNK | S_IRWXUGO; inode->i_op = &ext2_symlink_inode_operations; for (l = 0; l < inode->i_sb->s_blocksize - 1 && symname [l]; l++) @@ -689,7 +686,7 @@ name_block = ext2_bread (inode, 0, 1, &err); if (!name_block) { iput (dir); - inode->i_nlink --; + inode->i_nlink--; inode->i_dirt = 1; iput (inode); return err; @@ -702,7 +699,7 @@ #endif } i = 0; - while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname ++))) + while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname++))) link[i++] = c; link[i] = 0; if (name_block) { @@ -713,7 +710,7 @@ inode->i_dirt = 1; bh = ext2_find_entry (dir, name, len, &de); if (bh) { - inode->i_nlink --; + inode->i_nlink--; inode->i_dirt = 1; iput (inode); brelse (bh); @@ -722,7 +719,7 @@ } bh = ext2_add_entry (dir, name, len, &de, &err); if (!bh) { - inode->i_nlink --; + inode->i_nlink--; inode->i_dirt = 1; iput (inode); iput (dir); @@ -778,7 +775,7 @@ bh->b_dirt = 1; brelse (bh); iput (dir); - oldinode->i_nlink ++; + oldinode->i_nlink++; oldinode->i_ctime = CURRENT_TIME; oldinode->i_dirt = 1; iput (oldinode); @@ -790,7 +787,7 @@ int ino; int result; - new_inode->i_count ++; + new_inode->i_count++; result = 0; for (;;) { if (new_inode == old_inode) { @@ -937,7 +934,7 @@ if (retval) goto end_rename; if (new_inode) { - new_inode->i_nlink --; + new_inode->i_nlink--; new_inode->i_dirt = 1; } old_bh->b_dirt = 1; @@ -945,8 +942,8 @@ if (dir_bh) { PARENT_INO(dir_bh->b_data) = new_dir->i_ino; dir_bh->b_dirt = 1; - old_dir->i_nlink --; - new_dir->i_nlink ++; + old_dir->i_nlink--; + new_dir->i_nlink++; old_dir->i_dirt = 1; new_dir->i_dirt = 1; } diff -u --recursive --new-file pl11/linux/fs/ext2/symlink.c linux/fs/ext2/symlink.c --- pl11/linux/fs/ext2/symlink.c Sun Apr 11 17:43:06 1993 +++ linux/fs/ext2/symlink.c Thu Aug 5 21:33:23 1993 @@ -38,7 +38,7 @@ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ - ext2_readlink, /* readlink */ + ext2_readlink, /* readlink */ ext2_follow_link, /* follow_link */ NULL, /* bmap */ NULL, /* truncate */ @@ -55,7 +55,7 @@ *res_inode = NULL; if (!dir) { dir = current->root; - dir->i_count ++; + dir->i_count++; } if (!inode) { iput (dir); @@ -80,9 +80,9 @@ link = bh->b_data; } else link = (char *) inode->u.ext2_i.i_data; - current->link_count ++; + current->link_count++; error = open_namei (link, flag, mode, res_inode, dir); - current->link_count --; + current->link_count--; iput (inode); if (bh) brelse (bh); @@ -114,7 +114,7 @@ link = (char *) inode->u.ext2_i.i_data; i = 0; while (i < buflen && (c = link[i])) { - i ++; + i++; put_fs_byte (c, buffer++); } iput (inode); diff -u --recursive --new-file pl11/linux/fs/ext2/truncate.c linux/fs/ext2/truncate.c --- pl11/linux/fs/ext2/truncate.c Wed Mar 31 15:44:12 1993 +++ linux/fs/ext2/truncate.c Thu Aug 5 21:33:23 1993 @@ -161,7 +161,7 @@ return 0; } repeat: - for (i = dindirect_block ; i < addr_per_block ; i ++) { + for (i = dindirect_block ; i < addr_per_block ; i++) { if (i < 0) i = 0; if (i < dindirect_block) @@ -218,7 +218,7 @@ return 0; } repeat: - for (i = tindirect_block ; i < addr_per_block ; i ++) { + for (i = tindirect_block ; i < addr_per_block ; i++) { if (i < 0) i = 0; if (i < tindirect_block) diff -u --recursive --new-file pl11/linux/fs/fifo.c linux/fs/fifo.c --- pl11/linux/fs/fifo.c Sun Jul 18 18:18:41 1993 +++ linux/fs/fifo.c Mon Aug 9 17:41:22 1993 @@ -99,7 +99,7 @@ } if (retval || PIPE_BASE(*inode)) return retval; - page = get_free_page(GFP_KERNEL); + page = __get_free_page(GFP_KERNEL); if (PIPE_BASE(*inode)) { free_page(page); return 0; diff -u --recursive --new-file pl11/linux/fs/file_table.c linux/fs/file_table.c --- pl11/linux/fs/file_table.c Sun Jun 27 18:06:16 1993 +++ linux/fs/file_table.c Mon Aug 9 17:41:22 1993 @@ -42,26 +42,21 @@ void grow_files(void) { - unsigned long page; struct file * file; int i; - page = get_free_page(GFP_BUFFER); - if (!page) + file = (struct file*) __get_free_page(GFP_BUFFER); + + if (!file) return; - file = (struct file *) page; - for (i=0; i < (PAGE_SIZE / sizeof(struct file)); i++, file++) - { - if (!first_file) - { - file->f_next = file; - file->f_prev = file; - first_file = file; - } - else - insert_file_free(file); - } - nr_files += i; + + nr_files+=i= PAGE_SIZE/sizeof(struct file); + + if (!first_file) + file->f_next = file->f_prev = first_file = file++, i--; + + for (; i ; i--) + insert_file_free(file++); } unsigned long file_table_init(unsigned long start, unsigned long end) diff -u --recursive --new-file pl11/linux/fs/inode.c linux/fs/inode.c --- pl11/linux/fs/inode.c Fri Jul 2 22:57:20 1993 +++ linux/fs/inode.c Mon Aug 9 17:41:22 1993 @@ -17,7 +17,7 @@ static struct wait_queue * inode_wait = NULL; static int nr_inodes = 0, nr_free_inodes = 0; -static inline int const hashfn(dev_t dev, int i) +static inline int const hashfn(dev_t dev, unsigned int i) { return (dev ^ i) % NR_IHASH; } @@ -84,24 +84,21 @@ void grow_inodes(void) { - unsigned long page; struct inode * inode; int i; - page = get_free_page(GFP_BUFFER); - if (!page) + if(!(inode = (struct inode*) get_free_page(GFP_KERNEL))) return; - inode = (struct inode *) page; - for (i=0; i < (PAGE_SIZE / sizeof(struct inode)); i++, inode++) { - if (!first_inode) { - inode->i_next = inode; - inode->i_prev = inode; - first_inode = inode; - } else - insert_inode_free(inode); - } + + i=PAGE_SIZE / sizeof(struct inode); nr_inodes += i; nr_free_inodes += i; + + if (!first_inode) + inode->i_next = inode->i_prev = first_inode = inode++, i--; + + for ( ; i ; i-- ) + insert_inode_free(inode++); } unsigned long inode_init(unsigned long start, unsigned long end) @@ -400,7 +397,7 @@ if (!(inode = get_empty_inode())) return NULL; - if (!(PIPE_BASE(*inode) = (char *) get_free_page(GFP_USER))) { + if (!(PIPE_BASE(*inode) = (char*) __get_free_page(GFP_USER))) { iput(inode); return NULL; } diff -u --recursive --new-file pl11/linux/fs/isofs/inode.c linux/fs/isofs/inode.c --- pl11/linux/fs/isofs/inode.c Sat Jul 3 11:05:39 1993 +++ linux/fs/isofs/inode.c Sat Aug 14 23:47:22 1993 @@ -18,7 +18,15 @@ #include #include +#if defined(CONFIG_BLK_DEV_SR) extern int check_cdrom_media_change(int, int); +#endif +#if defined(CONFIG_CDU31A) +extern int check_cdu31a_media_change(int, int); +#endif +#if defined(CONFIG_MCD) +extern int check_mcd_media_change(int, int); +#endif #ifdef LEAK_CHECK static int check_malloc = 0; @@ -254,11 +262,27 @@ printk("get root inode failed\n"); return NULL; } +#if defined(CONFIG_BLK_DEV_SR) if(MAJOR(s->s_dev) == 11) { - /* Chech this one more time. */ + /* Check this one more time. */ if(check_cdrom_media_change(s->s_dev, 0)) goto out; }; +#endif +#if defined(CONFIG_CDU31A) + if(MAJOR(s->s_dev) == 15) { + /* Check this one more time. */ + if(check_cdu31a_media_change(s->s_dev, 0)) + goto out; + }; +#endif +#if defined(CONFIG_MCD) + if(MAJOR(s->s_dev) == 23) { + /* Check this one more time. */ + if(check_mcd_media_change(s->s_dev, 0)) + goto out; + }; +#endif return s; out: /* Kick out for various error conditions */ brelse(bh); @@ -321,15 +345,15 @@ raw_inode = ((struct iso_directory_record *) pnt); }; - inode->i_mode = 0444; /* Everybody gets to read the file. */ + inode->i_mode = S_IRUGO; /* Everybody gets to read the file. */ inode->i_nlink = 1; if (raw_inode->flags[-high_sierra] & 2) { - inode->i_mode = 0555 | S_IFDIR; + inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR; inode->i_nlink = 2; /* There are always at least 2. It is hard to figure out what is correct*/ } else { - inode->i_mode = 0444; /* Everybody gets to read the file. */ + inode->i_mode = S_IRUGO; /* Everybody gets to read the file. */ inode->i_nlink = 1; inode->i_mode |= S_IFREG; /* If there are no periods in the name, then set the execute permission bit */ @@ -337,7 +361,7 @@ if(raw_inode->name[i]=='.' || raw_inode->name[i]==';') break; if(i == raw_inode->name_len[0] || raw_inode->name[i] == ';') - inode->i_mode |= 0111; /* execute permission */ + inode->i_mode |= S_IXUGO; /* execute permission */ }; inode->i_uid = 0; inode->i_gid = 0; diff -u --recursive --new-file pl11/linux/fs/locks.c linux/fs/locks.c --- pl11/linux/fs/locks.c Fri Jul 2 23:03:54 1993 +++ linux/fs/locks.c Mon Aug 9 18:02:29 1993 @@ -20,7 +20,7 @@ #include #include -#define OFFSET_MAX 0x7fffffff /* FIXME: move elsewhere? */ +#define OFFSET_MAX ((off_t)0x7fffffff) /* FIXME: move elsewhere? */ static int copy_flock(struct file *filp, struct file_lock *fl, struct flock *l); static int conflict(struct file_lock *caller_fl, struct file_lock *sys_fl); diff -u --recursive --new-file pl11/linux/fs/minix/inode.c linux/fs/minix/inode.c --- pl11/linux/fs/minix/inode.c Sat Jun 26 10:28:06 1993 +++ linux/fs/minix/inode.c Wed Jul 21 00:26:04 1993 @@ -14,6 +14,7 @@ #include #include +#include void minix_put_inode(struct inode *inode) { @@ -116,8 +117,8 @@ printk("MINIX-fs: bad superblock or unable to read bitmaps\n"); return NULL; } - s->u.minix_sb.s_imap[0]->b_data[0] |= 1; - s->u.minix_sb.s_zmap[0]->b_data[0] |= 1; + set_bit(0,s->u.minix_sb.s_imap[0]->b_data); + set_bit(0,s->u.minix_sb.s_zmap[0]->b_data); /* set up enough so that it can read an inode */ s->s_dev = dev; s->s_op = &minix_sops; diff -u --recursive --new-file pl11/linux/fs/msdos/inode.c linux/fs/msdos/inode.c --- pl11/linux/fs/msdos/inode.c Fri Jul 2 23:14:38 1993 +++ linux/fs/msdos/inode.c Mon Aug 9 18:02:29 1993 @@ -315,7 +315,7 @@ inode->i_uid = MSDOS_SB(inode->i_sb)->fs_uid; inode->i_gid = MSDOS_SB(inode->i_sb)->fs_gid; if (inode->i_ino == MSDOS_ROOT_INO) { - inode->i_mode = (0777 & ~MSDOS_SB(inode->i_sb)->fs_umask) | + inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFDIR; inode->i_op = &msdos_dir_inode_operations; inode->i_nlink = msdos_subdirs(inode)+2; @@ -339,7 +339,7 @@ raw_entry = &((struct msdos_dir_entry *) (bh->b_data)) [inode->i_ino & (MSDOS_DPB-1)]; if ((raw_entry->attr & ATTR_DIR) && !IS_FREE(raw_entry->name)) { - inode->i_mode = MSDOS_MKMODE(raw_entry->attr,0777 & + inode->i_mode = MSDOS_MKMODE(raw_entry->attr,S_IRWXUGO & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFDIR; inode->i_op = &msdos_dir_inode_operations; MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start); @@ -363,7 +363,7 @@ } else { inode->i_mode = MSDOS_MKMODE(raw_entry->attr,(IS_NOEXEC(inode) - ? 0666 : 0777) & ~MSDOS_SB(inode->i_sb)->fs_umask) | + ? S_IRUGO|S_IWUGO : S_IRWXUGO) & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFREG; inode->i_op = MSDOS_CAN_BMAP(MSDOS_SB(inode->i_sb)) ? &msdos_file_inode_operations : @@ -436,10 +436,10 @@ error = -EPERM; } if (IS_NOEXEC(inode) && !S_ISDIR(inode->i_mode)) - inode->i_mode &= S_IFMT | 0666; - else inode->i_mode |= 0111; + inode->i_mode &= S_IFMT | S_IRUGO | S_IWUGO; + else inode->i_mode |= S_IXUGO; inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU - & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IRUSR) >> 6)*0111)) & + & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IRUSR) >> 6)*S_IXUGO)) & ~MSDOS_SB(inode->i_sb)->fs_umask; return MSDOS_SB(inode->i_sb)->quiet ? 0 : error; } diff -u --recursive --new-file pl11/linux/fs/msdos/misc.c linux/fs/msdos/misc.c --- pl11/linux/fs/msdos/misc.c Fri Jul 2 23:15:48 1993 +++ linux/fs/msdos/misc.c Mon Aug 9 18:02:29 1993 @@ -15,7 +15,7 @@ /* Well-known binary file extensions */ static char bin_extensions[] = - "EXECOMAPPSYSOVLOBJLIB" /* program code */ + "EXECOMBINAPPSYSDRVOVLOVROBJLIBDLLPIF" /* program code */ "ARCZIPLHALZHZOOTARZ ARJ" /* common archivers */ "TZ TAZTZPTPZ" /* abbreviations of tar.Z and tar.zip */ "GIFBMPTIFGL JPGPCX" /* graphics */ @@ -141,7 +141,7 @@ #endif last = 0; if ((current = MSDOS_I(inode)->i_start) != 0) { - cache_lookup(inode,0x7fffffff,&last,¤t); + cache_lookup(inode,INT_MAX,&last,¤t); while (current && current != -1) if (!(current = fat_access(inode->i_sb, last = current,-1))) { diff -u --recursive --new-file pl11/linux/fs/namei.c linux/fs/namei.c --- pl11/linux/fs/namei.c Sat Jul 3 01:05:17 1993 +++ linux/fs/namei.c Mon Aug 9 18:02:29 1993 @@ -44,8 +44,7 @@ c = get_fs_byte(filename++); if (!c) return -ENOENT; - page = __get_free_page(GFP_KERNEL); - if (!page) + if(!(page = __get_free_page(GFP_KERNEL))) return -ENOMEM; *result = tmp = (char *) page; while (--i) { @@ -283,7 +282,7 @@ struct inode * dir, *inode; struct task_struct ** p; - mode &= 07777 & ~current->umask; + mode &= S_IALLUGO & ~current->umask; mode |= S_IFREG; error = dir_namei(pathname,&namelen,&basename,base,&dir); if (error) diff -u --recursive --new-file pl11/linux/fs/nfs/Makefile linux/fs/nfs/Makefile --- pl11/linux/fs/nfs/Makefile Sun Feb 14 12:48:00 1993 +++ linux/fs/nfs/Makefile Sun Aug 15 11:09:18 1993 @@ -15,7 +15,7 @@ $(AS) -o $*.o $< OBJS= proc.o sock.o inode.o file.o dir.o \ - symlink.o + symlink.o mmap.o nfs.o: $(OBJS) $(LD) -r -o nfs.o $(OBJS) diff -u --recursive --new-file pl11/linux/fs/nfs/dir.c linux/fs/nfs/dir.c --- pl11/linux/fs/nfs/dir.c Sat Jul 3 11:20:41 1993 +++ linux/fs/nfs/dir.c Mon Aug 9 18:02:29 1993 @@ -477,7 +477,7 @@ iput(dir); return -ENAMETOOLONG; } - sattr.mode = S_IFLNK | 0777; /* SunOS 4.1.2 crashes without this! */ + sattr.mode = S_IFLNK | S_IRWXUGO; /* SunOS 4.1.2 crashes without this! */ sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir), diff -u --recursive --new-file pl11/linux/fs/nfs/file.c linux/fs/nfs/file.c --- pl11/linux/fs/nfs/file.c Sat Feb 13 12:50:12 1993 +++ linux/fs/nfs/file.c Sun Aug 15 11:12:59 1993 @@ -19,6 +19,8 @@ static int nfs_file_read(struct inode *, struct file *, char *, int); static int nfs_file_write(struct inode *, struct file *, char *, int); +extern int nfs_mmap(struct inode * inode, struct file * file, + unsigned long addr, size_t len, int prot, unsigned long off); static struct file_operations nfs_file_operations = { NULL, /* lseek - default */ @@ -27,7 +29,7 @@ NULL, /* readdir - bad */ NULL, /* select - default */ NULL, /* ioctl - default */ - NULL, /* mmap */ + nfs_mmap, /* mmap */ NULL, /* no special open is needed */ NULL, /* release */ NULL /* fsync */ diff -u --recursive --new-file pl11/linux/fs/nfs/mmap.c linux/fs/nfs/mmap.c --- pl11/linux/fs/nfs/mmap.c +++ linux/fs/nfs/mmap.c Sun Aug 15 11:46:03 1993 @@ -0,0 +1,159 @@ +/* + * fs/nfs/mmap.c by Jon Tombs 15 Aug 1993 + * + * This code is from + * linux/mm/mmap.c which was written by obz, Linus and Eric + * and + * linux/mm/memory.c by Linus Torvalds and others + * + * Copyright (C) 1993 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int share_page(struct vm_area_struct * area, struct task_struct * tsk, + struct inode * inode, unsigned long address, unsigned long error_code, + unsigned long newpage); + +extern unsigned long put_page(struct task_struct * tsk,unsigned long page, + unsigned long address,int prot); + +static void nfs_file_mmap_nopage(int error_code, struct vm_area_struct * area, + unsigned long address); + +extern void file_mmap_free(struct vm_area_struct * area); +extern int file_mmap_share(struct vm_area_struct * from, struct vm_area_struct * to, + unsigned long address); + +struct vm_operations_struct nfs_file_mmap = { + NULL, /* open */ + file_mmap_free, /* close */ + nfs_file_mmap_nopage, /* nopage */ + NULL, /* wppage */ + file_mmap_share, /* share */ +}; + + +/* This is used for a general mmap of a nfs file */ +int nfs_mmap(struct inode * inode, struct file * file, + unsigned long addr, size_t len, int prot, unsigned long off) +{ + struct vm_area_struct * mpnt; + + if (off & (inode->i_sb->s_blocksize - 1)) + return -EINVAL; + if (len > high_memory || off > high_memory - len) /* avoid overflow */ + return -ENXIO; + if (get_limit(USER_DS) != TASK_SIZE) + return -EINVAL; + if (!inode->i_sb || !S_ISREG(inode->i_mode)) + return -EACCES; + if (!IS_RDONLY(inode)) { + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } + + mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); + if (!mpnt) + return -ENOMEM; + + unmap_page_range(addr, len); + mpnt->vm_task = current; + mpnt->vm_start = addr; + mpnt->vm_end = addr + len; + mpnt->vm_page_prot = prot; + mpnt->vm_share = NULL; + mpnt->vm_inode = inode; + inode->i_count++; + mpnt->vm_offset = off; + mpnt->vm_ops = &nfs_file_mmap; + mpnt->vm_next = current->mmap; + current->mmap = mpnt; +#if 0 + printk("VFS: Loaded mmap at %08x - %08x\n", + mpnt->vm_start, mpnt->vm_end); +#endif + return 0; +} + + +static void nfs_file_mmap_nopage(int error_code, struct vm_area_struct * area, + unsigned long address) +{ + struct inode * inode = area->vm_inode; + unsigned int clear; + unsigned long page; + unsigned long tmp; + int n; + int i; + int pos; + struct nfs_fattr fattr; + + address &= PAGE_MASK; + pos = address - area->vm_start + area->vm_offset; + + page = get_free_page(GFP_KERNEL); + if (share_page(area, area->vm_task, inode, address, error_code, page)) { + ++area->vm_task->min_flt; + return; + } + + ++area->vm_task->maj_flt; + if (!page) { + oom(current); + put_page(area->vm_task, BAD_PAGE, address, PAGE_PRIVATE); + return; + } + + clear = 0; + if (address + PAGE_SIZE > area->vm_end) { + clear = address + PAGE_SIZE - area->vm_end; + } + + n = NFS_SERVER(inode)->rsize; /* what we can read in one go */ + + for (i = 0; i < (PAGE_SIZE - clear); i += n) { + int hunk, result; + + hunk = PAGE_SIZE - i; + if (hunk > n) + hunk = n; + result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode), + pos, hunk, (char *) (page + i), &fattr); + if (result < 0) + break; + pos += result; + if (result < n) { + i += result; + break; + } + } + +#ifdef doweneedthishere + nfs_refresh_inode(inode, &fattr); +#endif + + if (!(error_code & PAGE_RW)) { + if (share_page(area, area->vm_task, inode, address, error_code, page)) + return; + } + + tmp = page + PAGE_SIZE; + while (clear--) { + *(char *)--tmp = 0; + } + if (put_page(area->vm_task,page,address,area->vm_page_prot)) + return; + free_page(page); + oom(current); +} diff -u --recursive --new-file pl11/linux/fs/nfs/proc.c linux/fs/nfs/proc.c --- pl11/linux/fs/nfs/proc.c Tue Jul 13 00:02:55 1993 +++ linux/fs/nfs/proc.c Mon Aug 9 17:41:23 1993 @@ -33,6 +33,11 @@ #define PRINTK if (0) printk #endif +#define PREP_PAGE_RPC(code) \ + if (!(p0 = (int*)__get_free_page(GFP_KERNEL)))\ + return NFSERR_IO;\ + p=nfs_rpc_header(p0,code) + static int *nfs_rpc_header(int *p, int procedure); static int *nfs_rpc_verify(int *p); static int nfs_stat_to_errno(int stat); @@ -168,8 +173,7 @@ int status; PRINTK("NFS call getattr\n"); - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_GETATTR); + PREP_PAGE_RPC(NFSPROC_GETATTR); p = xdr_encode_fhandle(p, fhandle); if ((status = nfs_rpc_call(server, p0, p)) < 0) { free_page((long) p0); @@ -194,8 +198,7 @@ int status; PRINTK("NFS call setattr\n"); - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_SETATTR); + PREP_PAGE_RPC(NFSPROC_SETATTR); p = xdr_encode_fhandle(p, fhandle); p = xdr_encode_sattr(p, sattr); if ((status = nfs_rpc_call(server, p0, p)) < 0) { @@ -225,8 +228,7 @@ if (!strcmp(name, "xyzzy")) proc_debug = 1 - proc_debug; #endif - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_LOOKUP); + PREP_PAGE_RPC(NFSPROC_LOOKUP); p = xdr_encode_fhandle(p, dir); p = xdr_encode_string(p, name); if ((status = nfs_rpc_call(server, p0, p)) < 0) { @@ -253,8 +255,7 @@ int status; PRINTK("NFS call readlink\n"); - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_READLINK); + PREP_PAGE_RPC(NFSPROC_READLINK); p = xdr_encode_fhandle(p, fhandle); if ((status = nfs_rpc_call(server, p0, p)) < 0) { free_page((long) p0); @@ -284,8 +285,7 @@ int len = 0; /* = 0 is for gcc */ PRINTK("NFS call read %d @ %d\n", count, offset); - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_READ); + PREP_PAGE_RPC(NFSPROC_READ); p = xdr_encode_fhandle(p, fhandle); *p++ = htonl(offset); *p++ = htonl(count); @@ -318,8 +318,7 @@ int status; PRINTK("NFS call write %d @ %d\n", count, offset); - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_WRITE); + PREP_PAGE_RPC(NFSPROC_WRITE); p = xdr_encode_fhandle(p, fhandle); *p++ = htonl(offset); /* traditional, could be any value */ *p++ = htonl(offset); @@ -349,8 +348,7 @@ int status; PRINTK("NFS call create %s\n", name); - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_CREATE); + PREP_PAGE_RPC(NFSPROC_CREATE); p = xdr_encode_fhandle(p, dir); p = xdr_encode_string(p, name); p = xdr_encode_sattr(p, sattr); @@ -377,8 +375,7 @@ int status; PRINTK("NFS call remove %s\n", name); - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_REMOVE); + PREP_PAGE_RPC(NFSPROC_REMOVE); p = xdr_encode_fhandle(p, dir); p = xdr_encode_string(p, name); if ((status = nfs_rpc_call(server, p0, p)) < 0) { @@ -404,8 +401,7 @@ int status; PRINTK("NFS call rename %s -> %s\n", old_name, new_name); - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_RENAME); + PREP_PAGE_RPC(NFSPROC_RENAME); p = xdr_encode_fhandle(p, old_dir); p = xdr_encode_string(p, old_name); p = xdr_encode_fhandle(p, new_dir); @@ -432,8 +428,7 @@ int status; PRINTK("NFS call link %s\n", name); - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_LINK); + PREP_PAGE_RPC(NFSPROC_LINK); p = xdr_encode_fhandle(p, fhandle); p = xdr_encode_fhandle(p, dir); p = xdr_encode_string(p, name); @@ -459,8 +454,7 @@ int status; PRINTK("NFS call symlink %s -> %s\n", name, path); - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_SYMLINK); + PREP_PAGE_RPC(NFSPROC_SYMLINK); p = xdr_encode_fhandle(p, dir); p = xdr_encode_string(p, name); p = xdr_encode_string(p, path); @@ -488,8 +482,7 @@ int status; PRINTK("NFS call mkdir %s\n", name); - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_MKDIR); + PREP_PAGE_RPC(NFSPROC_MKDIR); p = xdr_encode_fhandle(p, dir); p = xdr_encode_string(p, name); p = xdr_encode_sattr(p, sattr); @@ -516,8 +509,7 @@ int status; PRINTK("NFS call rmdir %s\n", name); - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_RMDIR); + PREP_PAGE_RPC(NFSPROC_RMDIR); p = xdr_encode_fhandle(p, dir); p = xdr_encode_string(p, name); if ((status = nfs_rpc_call(server, p0, p)) < 0) { @@ -546,8 +538,7 @@ PRINTK("NFS call readdir %d @ %d\n", count, cookie); size = server->rsize; - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_READDIR); + PREP_PAGE_RPC(NFSPROC_READDIR); p = xdr_encode_fhandle(p, fhandle); *p++ = htonl(cookie); *p++ = htonl(size); @@ -588,8 +579,7 @@ int status; PRINTK("NFS call statfs\n"); - p = p0 = (int *) get_free_page(GFP_KERNEL); - p = nfs_rpc_header(p, NFSPROC_STATFS); + PREP_PAGE_RPC(NFSPROC_STATFS); p = xdr_encode_fhandle(p, fhandle); if ((status = nfs_rpc_call(server, p0, p)) < 0) { free_page((long) p0); diff -u --recursive --new-file pl11/linux/fs/nfs/sock.c linux/fs/nfs/sock.c --- pl11/linux/fs/nfs/sock.c Wed May 26 21:31:42 1993 +++ linux/fs/nfs/sock.c Mon Aug 9 18:02:29 1993 @@ -120,7 +120,7 @@ remove_wait_queue(entry.wait_address, &entry.wait); current->state = TASK_RUNNING; addrlen = 0; - result = sock->ops->recvfrom(sock, (void *) start, 4096, 1, 0, + result = sock->ops->recvfrom(sock, (void *) start, PAGE_SIZE, 1, 0, NULL, &addrlen); if (result < 0) { if (result == -EAGAIN) { diff -u --recursive --new-file pl11/linux/fs/open.c linux/fs/open.c --- pl11/linux/fs/open.c Sat Jul 3 01:04:55 1993 +++ linux/fs/open.c Mon Aug 9 18:02:29 1993 @@ -153,7 +153,7 @@ /* * XXX we should use the real ids for checking _all_ components of the - * path. Now we only use them for the final compenent of the path. + * path. Now we only use them for the final component of the path. */ extern "C" int sys_access(const char * filename,int mode) { @@ -160,17 +160,17 @@ struct inode * inode; int res, i_mode; - if (mode != (mode & 0007)) /* where's F_OK, X_OK, W_OK, R_OK? */ + if (mode != (mode & S_IRWXO)) /* where's F_OK, X_OK, W_OK, R_OK? */ return -EINVAL; res = namei(filename,&inode); if (res) return res; i_mode = inode->i_mode; - res = i_mode & 0777; + res = i_mode & S_IRWXUGO; if (current->uid == inode->i_uid) - res >>= 6; + res >>= 6; /* needs cleaning? */ else if (in_group_p(inode->i_gid)) - res >>= 3; + res >>= 3; /* needs cleaning? */ iput(inode); if ((res & mode) == mode) return 0; @@ -184,7 +184,7 @@ * decomposing the path would be racy. */ if ((!current->uid) && - (S_ISDIR(i_mode) || !(mode & 1) || (i_mode & 0111))) + (S_ISDIR(i_mode) || !(mode & S_IXOTH) || (i_mode & S_IXUGO))) return 0; return -EACCES; } @@ -244,7 +244,7 @@ return -EPERM; if (IS_RDONLY(inode)) return -EROFS; - inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777); + inode->i_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); if (!suser() && !in_group_p(inode->i_gid)) inode->i_mode &= ~S_ISGID; inode->i_ctime = CURRENT_TIME; @@ -268,7 +268,7 @@ iput(inode); return -EROFS; } - inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777); + inode->i_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); if (!suser() && !in_group_p(inode->i_gid)) inode->i_mode &= ~S_ISGID; inode->i_ctime = CURRENT_TIME; @@ -350,11 +350,10 @@ * for the internal routines (ie open_namei()/follow_link() etc). 00 is * used by symlinks. */ -extern "C" int sys_open(const char * filename,int flags,int mode) +int do_open(const char * filename,int flags,int mode) { struct inode * inode; struct file * f; - char * tmp; int flag,error,fd; for(fd=0 ; fdfilp[fd]=NULL; f->f_count--; @@ -412,6 +407,19 @@ } f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); return (fd); +} + +extern "C" int sys_open(const char * filename,int flags,int mode) +{ + char * tmp; + int error; + + error = getname(filename, &tmp); + if (error) + return error; + error = do_open(tmp,flags,mode); + putname(tmp); + return error; } extern "C" int sys_creat(const char * pathname, int mode) diff -u --recursive --new-file pl11/linux/fs/proc/array.c linux/fs/proc/array.c --- pl11/linux/fs/proc/array.c Mon Jul 12 22:09:24 1993 +++ linux/fs/proc/array.c Mon Aug 9 18:02:29 1993 @@ -89,18 +89,16 @@ if (!p || !*p || ptr >= TASK_SIZE) return 0; - page = (*p)->tss.cr3; - page += (ptr >> 20) & 0xffc; - page = *(unsigned long *) page; + page = *PAGE_DIR_OFFSET((*p)->tss.cr3,ptr); if (!(page & 1)) return 0; - page &= 0xfffff000; - page += (ptr >> 10) & 0xffc; + page &= PAGE_MASK; + page += PAGE_PTR(ptr); page = *(unsigned long *) page; if (!(page & 1)) return 0; - page &= 0xfffff000; - page += ptr & 0xfff; + page &= PAGE_MASK; + page += ptr & ~PAGE_MASK; return page; } @@ -128,7 +126,7 @@ start++; if (start >= end) return result; - } while (!(addr & 0xfff)); + } while (!(addr & ~PAGE_MASK)); } } @@ -196,7 +194,7 @@ if (vsize) { eip = KSTK_EIP(vsize); esp = KSTK_ESP(vsize); - vsize = (*p)->brk + 4095; + vsize = (*p)->brk + PAGE_SIZE-1; if (esp) vsize += TASK_SIZE - esp; } @@ -266,14 +264,14 @@ return 0; tpag = (*p)->end_code / PAGE_SIZE; if ((*p)->state != TASK_ZOMBIE) { - pagedir = (unsigned long *)((*p)->tss.cr3 + ((*p)->start_code >> 20)); + pagedir = PAGE_DIR_OFFSET((*p)->tss.cr3,(*p)->start_code); for (i = 0; i < 0x300; ++i) { if ((ptbl = pagedir[i]) == 0) { - tpag -= 1024; + tpag -= PTRS_PER_PAGE; continue; } - buf = (unsigned long *)(ptbl & 0xfffff000); - for (pte = buf; pte < (buf + 1024); ++pte) { + buf = (unsigned long *)(ptbl & PAGE_MASK); + for (pte = buf; pte < (buf + PTRS_PER_PAGE); ++pte) { if (*pte != 0) { ++size; if (*pte & 1) { @@ -290,7 +288,7 @@ --drs; } map_nr = MAP_NR(*pte); - if (map_nr < (high_memory / 4096) && mem_map[map_nr] > 1) + if (map_nr < (high_memory / PAGE_SIZE) && mem_map[map_nr] > 1) ++share; } } @@ -311,8 +309,7 @@ if (count < 0) return -EINVAL; - page = (char *) get_free_page(GFP_KERNEL); - if (!page) + if (!(page = (char*) __get_free_page(GFP_KERNEL))) return -ENOMEM; type = inode->i_ino; pid = type >> 16; diff -u --recursive --new-file pl11/linux/fs/proc/inode.c linux/fs/proc/inode.c --- pl11/linux/fs/proc/inode.c Sat Jun 26 10:28:06 1993 +++ linux/fs/proc/inode.c Mon Aug 9 18:02:30 1993 @@ -61,7 +61,7 @@ void proc_statfs(struct super_block *sb, struct statfs *buf) { put_fs_long(PROC_SUPER_MAGIC, &buf->f_type); - put_fs_long(1024, &buf->f_bsize); + put_fs_long(PAGE_SIZE/sizeof(long), &buf->f_bsize); put_fs_long(0, &buf->f_blocks); put_fs_long(0, &buf->f_bfree); put_fs_long(0, &buf->f_bavail); @@ -94,7 +94,7 @@ if (!p || i >= NR_TASKS) return; if (ino == PROC_ROOT_INO) { - inode->i_mode = S_IFDIR | 0555; + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; inode->i_nlink = 2; for (i = 1 ; i < NR_TASKS ; i++) if (task[i]) @@ -103,7 +103,7 @@ return; } if ((ino >= 128) && (ino <= 160)) { /* files within /proc/net */ - inode->i_mode = S_IFREG | 0444; + inode->i_mode = S_IFREG | S_IRUGO; inode->i_op = &proc_net_inode_operations; return; } @@ -110,16 +110,16 @@ if (!pid) { switch (ino) { case 5: - inode->i_mode = S_IFREG | 0444; + inode->i_mode = S_IFREG | S_IRUGO; inode->i_op = &proc_kmsg_inode_operations; break; case 8: /* for the net directory */ - inode->i_mode = S_IFDIR | 0555; + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; inode->i_nlink = 2; inode->i_op = &proc_net_inode_operations; break; default: - inode->i_mode = S_IFREG | 0444; + inode->i_mode = S_IFREG | S_IRUGO; inode->i_op = &proc_array_inode_operations; break; } @@ -131,12 +131,12 @@ switch (ino) { case 2: inode->i_nlink = 4; - inode->i_mode = S_IFDIR | 0555; + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; inode->i_op = &proc_base_inode_operations; return; case 3: inode->i_op = &proc_mem_inode_operations; - inode->i_mode = S_IFREG | 0600; + inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR; return; case 4: case 5: @@ -143,11 +143,11 @@ case 6: inode->i_op = &proc_link_inode_operations; inode->i_size = 64; - inode->i_mode = S_IFLNK | 0700; + inode->i_mode = S_IFLNK | S_IRWXU; return; case 7: case 8: - inode->i_mode = S_IFDIR | 0500; + inode->i_mode = S_IFDIR | S_IRUSR | S_IXUSR; inode->i_op = &proc_fd_inode_operations; inode->i_nlink = 2; return; @@ -155,7 +155,7 @@ case 10: case 11: case 12: - inode->i_mode = S_IFREG | 0444; + inode->i_mode = S_IFREG | S_IRUGO; inode->i_op = &proc_array_inode_operations; return; } @@ -166,7 +166,7 @@ return; inode->i_op = &proc_link_inode_operations; inode->i_size = 64; - inode->i_mode = S_IFLNK | 0700; + inode->i_mode = S_IFLNK | S_IRWXU; return; case 2: ino &= 0xff; @@ -181,7 +181,7 @@ } inode->i_op = &proc_link_inode_operations; inode->i_size = 64; - inode->i_mode = S_IFLNK | 0700; + inode->i_mode = S_IFLNK | S_IRWXU; return; } return; diff -u --recursive --new-file pl11/linux/fs/proc/mem.c linux/fs/proc/mem.c --- pl11/linux/fs/proc/mem.c Sat Feb 13 12:50:12 1993 +++ linux/fs/proc/mem.c Mon Aug 9 18:02:30 1993 @@ -24,7 +24,7 @@ { unsigned long addr, pid, cr3; char *tmp; - unsigned long pde, pte, page; + unsigned long pte, page; int i; if (count < 0) @@ -44,18 +44,17 @@ while (count > 0) { if (current->signal & ~current->blocked) break; - pde = cr3 + (addr >> 20 & 0xffc); - pte = *(unsigned long *) pde; + pte = *PAGE_DIR_OFFSET(cr3,addr); if (!(pte & PAGE_PRESENT)) break; - pte &= 0xfffff000; - pte += (addr >> 10) & 0xffc; + pte &= PAGE_MASK; + pte += PAGE_PTR(addr); page = *(unsigned long *) pte; if (!(page & 1)) break; - page &= 0xfffff000; - page += addr & 0xfff; - i = 4096-(addr & 0xfff); + page &= PAGE_MASK; + page += addr & ~PAGE_MASK; + i = PAGE_SIZE-(addr & ~PAGE_MASK); if (i > count) i = count; memcpy_tofs(tmp,(void *) page,i); @@ -73,7 +72,7 @@ { unsigned long addr, pid, cr3; char *tmp; - unsigned long pde, pte, page; + unsigned long pte, page; int i; if (count < 0) @@ -93,12 +92,11 @@ while (count > 0) { if (current->signal & ~current->blocked) break; - pde = cr3 + (addr >> 20 & 0xffc); - pte = *(unsigned long *) pde; + pte = *PAGE_DIR_OFFSET(cr3,addr); if (!(pte & PAGE_PRESENT)) break; - pte &= 0xfffff000; - pte += (addr >> 10) & 0xffc; + pte &= PAGE_MASK; + pte += PAGE_PTR(addr); page = *(unsigned long *) pte; if (!(page & PAGE_PRESENT)) break; @@ -106,9 +104,9 @@ do_wp_page(0,addr,current,0); continue; } - page &= 0xfffff000; - page += addr & 0xfff; - i = 4096-(addr & 0xfff); + page &= PAGE_MASK; + page += addr & ~PAGE_MASK; + i = PAGE_SIZE-(addr & ~PAGE_MASK); if (i > count) i = count; memcpy_fromfs((void *) page,tmp,i); diff -u --recursive --new-file pl11/linux/fs/proc/net.c linux/fs/proc/net.c --- pl11/linux/fs/proc/net.c Tue Apr 27 20:31:37 1993 +++ linux/fs/proc/net.c Mon Aug 9 17:41:23 1993 @@ -164,8 +164,7 @@ if (count < 0) return -EINVAL; - page = (char *) get_free_page(GFP_KERNEL); - if (!page) + if (!(page = (char*) __get_free_page(GFP_KERNEL))) return -ENOMEM; ino = inode->i_ino; switch (ino) { diff -u --recursive --new-file pl11/linux/fs/select.c linux/fs/select.c --- pl11/linux/fs/select.c Sat Jul 3 01:08:06 1993 +++ linux/fs/select.c Mon Aug 9 18:02:30 1993 @@ -104,8 +104,7 @@ } end_check: n = max + 1; - entry = (struct select_table_entry *) __get_free_page(GFP_KERNEL); - if (!entry) + if(!(entry = (struct select_table_entry*) __get_free_page(GFP_KERNEL))) return -ENOMEM; FD_ZERO(res_in); FD_ZERO(res_out); @@ -219,7 +218,7 @@ if ((i = get_fd_set(n, inp, &in)) || (i = get_fd_set(n, outp, &out)) || (i = get_fd_set(n, exp, &ex))) return i; - timeout = 0xffffffff; + timeout = ~0UL; if (tvp) { i = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp)); if (i) diff -u --recursive --new-file pl11/linux/fs/super.c linux/fs/super.c --- pl11/linux/fs/super.c Sat Jul 3 01:08:27 1993 +++ linux/fs/super.c Wed Aug 11 20:56:05 1993 @@ -176,7 +176,7 @@ memset(unnamed_dev_in_use, 0, sizeof(unnamed_dev_in_use)); unnamed_dev_in_use[0] = 1; /* minor 0 (nodev) is special */ } - for (i = 0; i < 256; i++) { + for (i = 0; i < sizeof unnamed_dev_in_use/sizeof unnamed_dev_in_use[0]; i++) { if (!unnamed_dev_in_use[i]) { unnamed_dev_in_use[i] = 1; return (UNNAMED_MAJOR << 8) | i; @@ -254,8 +254,11 @@ if (!suser()) return -EPERM; retval = namei(name,&inode); - if (retval) - return retval; + if (retval) { + retval = lnamei(name,&inode); + if (retval) + return retval; + } if (S_ISBLK(inode->i_mode)) { dev = inode->i_rdev; if (IS_NODEV(inode)) { @@ -262,7 +265,7 @@ iput(inode); return -EACCES; } - } else if (S_ISDIR(inode->i_mode)) { + } else { if (!inode || !inode->i_sb || inode != inode->i_sb->s_mounted) { iput(inode); return -EINVAL; @@ -272,9 +275,6 @@ memset(&dummy_inode, 0, sizeof(dummy_inode)); dummy_inode.i_rdev = dev; inode = &dummy_inode; - } else { - iput(inode); - return -EINVAL; } if (MAJOR(dev) >= MAX_BLKDEV) { iput(inode); @@ -382,7 +382,7 @@ * Flags is a 16-bit value that allows up to 16 non-fs dependent flags to * be given to the mount() call (ie: read-only, no-dev, no-suid etc). * - * data is a (void *) that can point to any structure up to 4095 bytes, which + * data is a (void *) that can point to any structure up to PAGE_SIZE-1 bytes, which * can contain arbitrary fs-dependent information (or be NULL). * * NOTE! As old versions of mount() didn't use this setup, the flags has to have @@ -458,10 +458,13 @@ iput(inode); return -EFAULT; } - page = get_free_page(GFP_KERNEL); + if (!(page = __get_free_page(GFP_KERNEL))) { + iput(inode); + return -ENOMEM; + } i = TASK_SIZE - (unsigned long) data; - if (i < 0 || i > 4095) - i = 4095; + if ((unsigned long) i >= PAGE_SIZE) + i = PAGE_SIZE-1; memcpy_fromfs((void *) page,data,i); } } diff -u --recursive --new-file pl11/linux/fs/xiafs/namei.c linux/fs/xiafs/namei.c --- pl11/linux/fs/xiafs/namei.c Fri Jul 2 23:34:10 1993 +++ linux/fs/xiafs/namei.c Mon Aug 9 18:02:30 1993 @@ -373,7 +373,7 @@ inode->i_nlink = 2; dir_block->b_dirt = 1; brelse(dir_block); - inode->i_mode = S_IFDIR | (mode & 0777 & ~current->umask); + inode->i_mode = S_IFDIR | (mode & S_IRWXUGO & ~current->umask); if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; inode->i_dirt = 1; @@ -594,7 +594,7 @@ iput(dir); return -ENOSPC; } - inode->i_mode = S_IFLNK | 0777; + inode->i_mode = S_IFLNK | S_IRWXUGO; inode->i_op = &xiafs_symlink_inode_operations; name_block = xiafs_bread(inode,0,1); if (!name_block) { diff -u --recursive --new-file pl11/linux/include/asm/bitops.h linux/include/asm/bitops.h --- pl11/linux/include/asm/bitops.h Sat Jun 26 10:28:06 1993 +++ linux/include/asm/bitops.h Mon Aug 2 14:30:48 1993 @@ -1,4 +1,5 @@ #ifndef _ASM_BITOPS_H +#define _ASM_BITOPS_H /* * Copyright 1992, Linus Torvalds. */ @@ -18,7 +19,7 @@ struct __dummy { unsigned long a[100]; }; #define ADDR (*(struct __dummy *) addr) -extern inline int set_bit(int nr, void * addr) +extern __inline__ int set_bit(int nr, void * addr) { int oldbit; @@ -28,7 +29,7 @@ return oldbit; } -extern inline int clear_bit(int nr, void * addr) +extern __inline__ int clear_bit(int nr, void * addr) { int oldbit; @@ -42,7 +43,7 @@ * This routine doesn't need to be atomic, but it's faster to code it * this way. */ -extern inline int test_bit(int nr, void * addr) +extern __inline__ int test_bit(int nr, void * addr) { int oldbit; @@ -68,7 +69,7 @@ * C language equivalents written by Theodore Ts'o, 9/26/92 */ -extern inline int set_bit(int nr,int * addr) +extern __inline__ int set_bit(int nr,int * addr) { int mask, retval; @@ -81,7 +82,7 @@ return retval; } -extern inline int clear_bit(int nr, int * addr) +extern __inline__ int clear_bit(int nr, int * addr) { int mask, retval; @@ -94,7 +95,7 @@ return retval; } -extern inline int test_bit(int nr, int * addr) +extern __inline__ int test_bit(int nr, int * addr) { int mask; diff -u --recursive --new-file pl11/linux/include/asm/segment.h linux/include/asm/segment.h --- pl11/linux/include/asm/segment.h Fri Jul 2 22:10:21 1993 +++ linux/include/asm/segment.h Mon Aug 2 14:31:28 1993 @@ -1,3 +1,6 @@ +#ifndef _ASM_SEGMENT_H +#define _ASM_SEGMENT_H + static inline unsigned char get_fs_byte(const char * addr) { register unsigned char _v; @@ -164,3 +167,4 @@ __asm__ __volatile__("mov %w0,%%fs": /* no output */ :"r" (val)); } +#endif /* _ASM_SEGMENT_H */ diff -u --recursive --new-file pl11/linux/include/asm/system.h linux/include/asm/system.h --- pl11/linux/include/asm/system.h Wed Jul 14 16:51:48 1993 +++ linux/include/asm/system.h Tue Jul 20 01:35:31 1993 @@ -76,13 +76,13 @@ _set_gate(a,12,3,addr) #define _set_seg_desc(gate_addr,type,dpl,base,limit) {\ - *(gate_addr) = ((base) & 0xff000000) | \ + *((gate_addr)+1) = ((base) & 0xff000000) | \ (((base) & 0x00ff0000)>>16) | \ ((limit) & 0xf0000) | \ ((dpl)<<13) | \ (0x00408000) | \ ((type)<<8); \ - *((gate_addr)+1) = (((base) & 0x0000ffff)<<16) | \ + *(gate_addr) = (((base) & 0x0000ffff)<<16) | \ ((limit) & 0x0ffff); } #define _set_tssldt_desc(n,addr,limit,type) \ @@ -100,6 +100,8 @@ ) #define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),235,"0x89") -#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),23,"0x82") +#define set_ldt_desc(n,addr,size) \ + _set_tssldt_desc(((char *) (n)),((int)(addr)),((size << 3) - 1),"0x82") + #endif diff -u --recursive --new-file pl11/linux/include/linux/a.out.h linux/include/linux/a.out.h --- pl11/linux/include/linux/a.out.h Tue Nov 10 12:54:55 1992 +++ linux/include/linux/a.out.h Mon Aug 9 17:41:23 1993 @@ -134,7 +134,7 @@ #endif #ifdef linux -#define PAGE_SIZE 4096 +#include #define SEGMENT_SIZE 1024 #endif diff -u --recursive --new-file pl11/linux/include/linux/binfmts.h linux/include/linux/binfmts.h --- pl11/linux/include/linux/binfmts.h +++ linux/include/linux/binfmts.h Fri Aug 6 13:32:00 1993 @@ -0,0 +1,36 @@ +#ifndef _LINUX_BINFMTS_H +#define _LINUX_BINFMTS_H + +/* + * MAX_ARG_PAGES defines the number of pages allocated for arguments + * and envelope for the new program. 32 should suffice, this gives + * a maximum env+arg of 128kB ! + */ +#define MAX_ARG_PAGES 32 + +/* + * This structure is used to hold the arguments that are used when loading binaries. + */ +struct linux_binprm{ + char buf[128]; + unsigned long page[MAX_ARG_PAGES]; + unsigned long p; + struct inode * inode; + int e_uid, e_gid; + int argc, envc; + char * filename; /* Name of binary */ +}; + +/* This structure defines the functions that are used to load the binary formats that + * linux accepts. */ + +struct linux_binfmt{ + int (*load_binary)(struct linux_binprm *, struct pt_regs * regs); + int (*load_shlib)(int fd); +}; + +extern struct linux_binfmt formats[]; + + + +#endif diff -u --recursive --new-file pl11/linux/include/linux/busmouse.h linux/include/linux/busmouse.h --- pl11/linux/include/linux/busmouse.h Tue Nov 10 12:54:56 1992 +++ linux/include/linux/busmouse.h Wed Aug 4 22:38:40 1993 @@ -83,8 +83,8 @@ struct mouse_status { - char buttons; - char latch_buttons; + unsigned char buttons; + unsigned char latch_buttons; int dx; int dy; int present; diff -u --recursive --new-file pl11/linux/include/linux/cdu31a.h linux/include/linux/cdu31a.h --- pl11/linux/include/linux/cdu31a.h +++ linux/include/linux/cdu31a.h Tue Aug 3 19:29:23 1993 @@ -0,0 +1,313 @@ +/* + * Definitions for a Sony interface CDROM drive. + * + * Corey Minyard (minyard@wf-rch.cirr.com) + * + * Copyright (C) 1993 Corey Minyard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * Offsets (from the base address) and bits for the various write registers + * of the drive. + */ +#define SONY_CMD_REG_OFFSET 0 +#define SONY_PARAM_REG_OFFSET 1 +#define SONY_WRITE_REG_OFFSET 2 +#define SONY_CONTROL_REG_OFFSET 3 +# define SONY_ATTN_CLR_BIT 0x01 +# define SONY_RES_RDY_CLR_BIT 0x02 +# define SONY_DATA_RDY_CLR_BIT 0x04 +# define SONY_ATTN_INT_EN_BIT 0x08 +# define SONY_RES_RDY_INT_EN_BIT 0x10 +# define SONY_DATA_RDY_INT_EN_BIT 0x20 +# define SONY_PARAM_CLR_BIT 0x40 +# define SONY_DRIVE_RESET_BIT 0x80 + +/* + * Offsets (from the base address) and bits for the various read registers + * of the drive. + */ +#define SONY_STATUS_REG_OFFSET 0 +# define SONY_ATTN_BIT 0x01 +# define SONY_RES_RDY_BIT 0x02 +# define SONY_DATA_RDY_BIT 0x04 +# define SONY_ATTN_INT_ST_BIT 0x08 +# define SONY_RES_RDY_INT_ST_BIT 0x10 +# define SONY_DATA_RDY_INT_ST_BIT 0x20 +# define SONY_DATA_REQUEST_BIT 0x40 +# define SONY_BUSY_BIT 0x80 +#define SONY_RESULT_REG_OFFSET 1 +#define SONY_READ_REG_OFFSET 2 +#define SONY_FIFOST_REG_OFFSET 3 +# define SONY_PARAM_WRITE_RDY_BIT 0x01 +# define SONY_PARAM_REG_EMPTY_BIT 0x02 +# define SONY_RES_REG_NOT_EMP_BIT 0x04 +# define SONY_RES_REG_FULL_BIT 0x08 + +#define LOG_START_OFFSET 150 /* Offset of first logical sector */ + +#define SONY_DETECT_TIMEOUT 80 /* Maximum amount of time + that drive detection code + will wait for response + from drive (in 1/100th's + of seconds). */ + +#define SONY_JIFFIES_TIMEOUT 500 /* Maximum number of times the + drive will wait/try for an + operation */ +#define SONY_RESET_TIMEOUT 100 /* Maximum number of times the + drive will wait/try a reset + operation */ +#define SONY_READY_RETRIES 20000 /* How many times to retry a + spin waiting for a register + to come ready */ + +#define MAX_CDU31A_RETRIES 3 /* How many times to retry an + operation */ + +/* Commands to request or set drive control parameters and disc information */ +#define SONY_REQ_DRIVE_CONFIG_CMD 0x00 /* Returns s_sony_drive_config */ +#define SONY_REQ_DRIVE_MODE_CMD 0x01 +#define SONY_REQ_DRIVE_PARAM_CMD 0x02 +#define SONY_REQ_MECH_STATUS_CMD 0x03 +#define SONY_REQ_AUDIO_STATUS_CMD 0x04 +#define SONY_SET_DRIVE_PARAM_CMD 0x10 +#define SONY_REQ_TOC_DATA_CMD 0x20 /* Returns s_sony_toc */ +#define SONY_REQ_SUBCODE_ADDRESS_CMD 0x21 /* Returns s_sony_subcode */ +#define SONY_REQ_UPC_EAN_CMD 0x22 +#define SONY_REQ_ISRC_CMD 0x23 +#define SONY_REQ_TOC_DATA_SPEC_CMD 0x24 + +/* Commands to request information from the drive */ +#define SONY_READ_TOC_CMD 0x30 +#define SONY_SEEK_CMD 0x31 +#define SONY_READ_CMD 0x32 +#define SONY_READ_BLKERR_STAT_CMD 0x34 +#define SONY_ABORT_CMD 0x35 +#define SONY_READ_TOC_SPEC_CMD 0x36 + +/* Commands to control audio */ +#define SONY_AUDIO_PLAYBACK_CMD 0x40 +#define SONY_AUDIO_STOP_CMD 0x41 +#define SONY_AUDIO_SCAN_CMD 0x42 + +/* Miscellaneous control commands */ +#define SONY_EJECT_CMD 0x50 +#define SONY_SPIN_UP_CMD 0x51 +#define SONY_SPIN_DOWN_CMD 0x52 + +/* Diagnostic commands */ +#define SONY_WRITE_BUFFER_CMD 0x60 +#define SONY_READ_BUFFER_CMD 0x61 +#define SONY_DIAGNOSTICS_CMD 0x62 + + +/* + * The following are command paramters for the set drive parameter command + */ +#define SONY_SD_DECODE_PARAM 0x00 +#define SONY_SD_INTERFACE_PARAM 0x01 +#define SONY_SD_BUFFERING_PARAM 0x02 +#define SONY_SD_AUDIO_PARAM 0x03 +#define SONY_SD_AUDIO_VOLUME 0x04 +#define SONY_SD_MECH_CONTROL 0x05 +#define SONY_SD_AUTO_SPIN_DOWN_TIME 0x06 + +/* + * The following extract information from the drive configuration about + * the drive itself. + */ +#define SONY_HWC_GET_LOAD_MECH(c) (c.hw_config[0] & 0x03) +#define SONY_HWC_EJECT(c) (c.hw_config[0] & 0x04) +#define SONY_HWC_LED_SUPPORT(c) (c.hw_config[0] & 0x08) +#define SONY_HWC_GET_BUF_MEM_SIZE(c) ((c.hw_config[0] & 0xc0) >> 6) +#define SONY_HWC_AUDIO_PLAYBACK(c) (c.hw_config[1] & 0x01) +#define SONY_HWC_ELECTRIC_VOLUME(c) (c.hw_config[1] & 0x02) +#define SONY_HWC_ELECTRIC_VOLUME_CTL(c) (c.hw_config[1] & 0x04) + +#define SONY_HWC_CADDY_LOAD_MECH 0x00 +#define SONY_HWC_TRAY_LOAD_MECH 0x01 +#define SONY_HWC_POPUP_LOAD_MECH 0x02 +#define SONY_HWC_UNKWN_LOAD_MECH 0x03 + +#define SONY_HWC_8KB_BUFFER 0x00 +#define SONY_HWC_32KB_BUFFER 0x01 +#define SONY_HWC_64KB_BUFFER 0x02 +#define SONY_HWC_UNKWN_BUFFER 0x03 + +/* + * This is the complete status returned from the drive configuration request + * command. + */ +struct s_sony_drive_config +{ + unsigned char exec_status[2]; + char vendor_id[8]; + char product_id[16]; + char product_rev_level[8]; + unsigned char hw_config[2]; +}; + +/* The following is returned from the request subcode address command */ +struct s_sony_subcode +{ + unsigned char exec_status[2]; + unsigned char address :4; + unsigned char control :4; + unsigned char track_num; + unsigned char index_num; + unsigned char rel_msf[3]; + unsigned char reserved1; + unsigned char abs_msf[3]; +}; + +/* + * The following is returned from the request TOC (Table Of Contents) command. + * (last_track_num-first_track_num+1) values are valid in tracks. + */ +struct s_sony_toc +{ + unsigned char exec_status[2]; + unsigned char address0 :4; + unsigned char control0 :4; + unsigned char point0; + unsigned char first_track_num; + unsigned char disk_type; + unsigned char dummy0; + unsigned char address1 :4; + unsigned char control1 :4; + unsigned char point1; + unsigned char last_track_num; + unsigned char dummy1; + unsigned char dummy2; + unsigned char address2 :4; + unsigned char control2 :4; + unsigned char point2; + unsigned char lead_out_start_msf[3]; + struct + { + unsigned char address :4; + unsigned char control :4; + unsigned char track; + unsigned char track_start_msf[3]; + } tracks[100]; + + unsigned int lead_out_start_lba; +}; + + +/* + * The following are errors returned from the drive. + */ + +/* Command error group */ +#define SONY_ILL_CMD_ERR 0x10 +#define SONY_ILL_PARAM_ERR 0x11 + +/* Mechanism group */ +#define SONY_NOT_LOAD_ERR 0x20 +#define SONY_NO_DISK_ERR 0x21 +#define SONY_NOT_SPIN_ERR 0x22 +#define SONY_SPIN_ERR 0x23 +#define SONY_SPINDLE_SERVO_ERR 0x25 +#define SONY_FOCUS_SERVO_ERR 0x26 +#define SONY_EJECT_MECH_ERR 0x29 +#define SONY_AUDIO_PLAYING_ERR 0x2a +#define SONY_EMERGENCY_EJECT_ERR 0x2c + +/* Seek error group */ +#define SONY_FOCUS_ERR 0x30 +#define SONY_FRAME_SYNC_ERR 0x31 +#define SONY_SUBCODE_ADDR_ERR 0x32 +#define SONY_BLOCK_SYNC_ERR 0x33 +#define SONY_HEADER_ADDR_ERR 0x34 + +/* Read error group */ +#define SONY_ILL_TRACK_R_ERR 0x40 +#define SONY_MODE_0_R_ERR 0x41 +#define SONY_ILL_MODE_R_ERR 0x42 +#define SONY_ILL_BLOCK_SIZE_R_ERR 0x43 +#define SONY_MODE_R_ERR 0x44 +#define SONY_FORM_R_ERR 0x45 +#define SONY_LEAD_OUT_R_ERR 0x46 +#define SONY_BUFFER_OVERRUN_R_ERR 0x47 + +/* Data error group */ +#define SONY_UNREC_CIRC_ERR 0x53 +#define SONY_UNREC_LECC_ERR 0x57 + +/* Subcode error group */ +#define SONY_NO_TOC_ERR 0x60 +#define SONY_SUBCODE_DATA_NVAL_ERR 0x61 +#define SONY_FOCUS_ON_TOC_READ_ERR 0x63 +#define SONY_FRAME_SYNC_ON_TOC_READ_ERR 0x64 +#define SONY_TOC_DATA_ERR 0x65 + +/* Hardware failure group */ +#define SONY_HW_FAILURE_ERR 0x70 +#define SONY_LEAD_IN_A_ERR 0x91 +#define SONY_LEAD_OUT_A_ERR 0x92 +#define SONY_DATA_TRACK_A_ERR 0x93 + +/* + * The following are returned from the Read With Block Error Status command. + * They are not errors but information (Errors from the 0x5x group above may + * also be returned + */ +#define SONY_NO_CIRC_ERR_BLK_STAT 0x50 +#define SONY_NO_LECC_ERR_BLK_STAT 0x54 +#define SONY_RECOV_LECC_ERR_BLK_STAT 0x55 +#define SONY_NO_ERR_DETECTION_STAT 0x59 + +/* + * The following is not an error returned by the drive, but by the code + * that talks to the drive. It is returned because of a timeout. + */ +#define SONY_TIMEOUT_OP_ERR 0x01 +#define SONY_SIGNAL_OP_ERR 0x02 + + +/* + * The following are attention code for asyncronous events from the drive. + */ + +/* Standard attention group */ +#define SONY_EMER_EJECT_ATTN 0x2c +#define SONY_HW_FAILURE_ATTN 0x70 +#define SONY_MECH_LOADED_ATTN 0x80 +#define SONY_EJECT_PUSHED_ATTN 0x81 + +/* Audio attention group */ +#define SONY_AUDIO_PLAY_DONE_ATTN 0x90 +#define SONY_LEAD_IN_ERR_ATTN 0x91 +#define SONY_LEAD_OUT_ERR_ATTN 0x92 +#define SONY_DATA_TRACK_ERR_ATTN 0x93 +#define SONY_AUDIO_PLAYBACK_ERR_ATTN 0x94 + +/* Auto spin up group */ +#define SONY_SPIN_UP_COMPLETE_ATTN 0x24 +#define SONY_SPINDLE_SERVO_ERR_ATTN 0x25 +#define SONY_FOCUS_SERVO_ERR_ATTN 0x26 +#define SONY_TOC_READ_DONE_ATTN 0x62 +#define SONY_FOCUS_ON_TOC_READ_ERR_ATTN 0x63 +#define SONY_SYNC_ON_TOC_READ_ERR_ATTN 0x65 + +/* Auto eject group */ +#define SONY_SPIN_DOWN_COMPLETE_ATTN 0x27 +#define SONY_EJECT_COMPLETE_ATTN 0x28 +#define SONY_EJECT_MECH_ERR_ATTN 0x29 diff -u --recursive --new-file pl11/linux/include/linux/config.h linux/include/linux/config.h --- pl11/linux/include/linux/config.h Tue May 4 01:51:21 1993 +++ linux/include/linux/config.h Sat Jul 31 20:19:17 1993 @@ -13,9 +13,7 @@ #define UTS_NODENAME "(none)" /* set by sethostname() */ #endif -#ifdef CONFIG_M486 -#define UTS_MACHINE "i486" /* hardware type */ -#else +#ifndef UTS_MACHINE #define UTS_MACHINE "i386" /* hardware type */ #endif diff -u --recursive --new-file pl11/linux/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h --- pl11/linux/include/linux/ext2_fs.h Sat Jul 3 10:52:42 1993 +++ linux/include/linux/ext2_fs.h Thu Aug 5 21:33:23 1993 @@ -28,8 +28,8 @@ /* * The second extended file system version */ -#define EXT2FS_DATE "93/06/06" -#define EXT2FS_VERSION "0.3a" +#define EXT2FS_DATE "93/08/05" +#define EXT2FS_VERSION "0.3c" /* * Special inodes numbers @@ -38,6 +38,7 @@ #define EXT2_ROOT_INO 2 /* Root inode */ #define EXT2_ACL_IDX_INO 3 /* ACL inode */ #define EXT2_ACL_DATA_INO 4 /* ACL inode */ +#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ #define EXT2_FIRST_INO 11 /* First non reserved inode */ /* @@ -65,7 +66,7 @@ #define EXT2_ACLE_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry)) #define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (unsigned long)) #ifdef __KERNEL__ -# define EXT2_BLOCK_SIZE_BITS(s) ((s)->u.ext2_sb.s_log_block_size + 10) +# define EXT2_BLOCK_SIZE_BITS(s) ((s)->u.ext2_sb.s_es->s_log_block_size + 10) #else # define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) #endif @@ -98,12 +99,12 @@ struct ext2_acl_entry /* Access Control List Entry */ { - unsigned long acle_size; + unsigned long acle_size; unsigned short acle_perms; /* Access permissions */ unsigned short acle_type; /* Type of entry */ unsigned short acle_tag; /* User or group identity */ unsigned short acle_pad1; - unsigned long acle_next; /* Pointer on next entry for the */ + unsigned long acle_next; /* Pointer on next entry for the */ /* same inode or on next free entry */ }; @@ -112,9 +113,9 @@ */ struct ext2_old_group_desc { - unsigned long bg_block_bitmap; /* Blocks bitmap block */ - unsigned long bg_inode_bitmap; /* Inodes bitmap block */ - unsigned long bg_inode_table; /* Inodes table block */ + unsigned long bg_block_bitmap; /* Blocks bitmap block */ + unsigned long bg_inode_bitmap; /* Inodes bitmap block */ + unsigned long bg_inode_table; /* Inodes table block */ unsigned short bg_free_blocks_count; /* Free blocks count */ unsigned short bg_free_inodes_count; /* Free inodes count */ }; @@ -121,14 +122,14 @@ struct ext2_group_desc { - unsigned long bg_block_bitmap; /* Blocks bitmap block */ - unsigned long bg_inode_bitmap; /* Inodes bitmap block */ - unsigned long bg_inode_table; /* Inodes table block */ + unsigned long bg_block_bitmap; /* Blocks bitmap block */ + unsigned long bg_inode_bitmap; /* Inodes bitmap block */ + unsigned long bg_inode_table; /* Inodes table block */ unsigned short bg_free_blocks_count; /* Free blocks count */ unsigned short bg_free_inodes_count; /* Free inodes count */ unsigned short bg_used_dirs_count; /* Directories count */ unsigned short bg_pad; - unsigned long bg_reserved[3]; + unsigned long bg_reserved[3]; }; /* @@ -147,11 +148,11 @@ /* * Constants relative to the data blocks */ -#define EXT2_NDIR_BLOCKS 12 -#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS -#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) -#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) -#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) /* * Structure of an inode on the disk @@ -159,25 +160,25 @@ struct ext2_inode { unsigned short i_mode; /* File mode */ unsigned short i_uid; /* Owner Uid */ - unsigned long i_size; /* Size in bytes */ - unsigned long i_atime; /* Access time */ - unsigned long i_ctime; /* Creation time */ - unsigned long i_mtime; /* Modification time */ - unsigned long i_dtime; /* Deletion Time */ + unsigned long i_size; /* Size in bytes */ + unsigned long i_atime; /* Access time */ + unsigned long i_ctime; /* Creation time */ + unsigned long i_mtime; /* Modification time */ + unsigned long i_dtime; /* Deletion Time */ unsigned short i_gid; /* Group Id */ unsigned short i_links_count; /* Links count */ - unsigned long i_blocks; /* Blocks count */ - unsigned long i_flags; /* File flags */ - unsigned long i_reserved1; - unsigned long i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ - unsigned long i_version; /* File version (for NFS) */ - unsigned long i_file_acl; /* File ACL */ - unsigned long i_dir_acl; /* Directory ACL */ - unsigned long i_faddr; /* Fragment address */ - unsigned char i_frag; /* Fragment number */ - unsigned char i_fsize; /* Fragment size */ + unsigned long i_blocks; /* Blocks count */ + unsigned long i_flags; /* File flags */ + unsigned long i_reserved1; + unsigned long i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + unsigned long i_version; /* File version (for NFS) */ + unsigned long i_file_acl; /* File ACL */ + unsigned long i_dir_acl; /* Directory ACL */ + unsigned long i_faddr; /* Fragment address */ + unsigned char i_frag; /* Fragment number */ + unsigned char i_fsize; /* Fragment size */ unsigned short i_pad1; - unsigned long i_reserved2[2]; + unsigned long i_reserved2[2]; }; /* @@ -184,25 +185,25 @@ * Structure of the super block */ struct ext2_super_block { - unsigned long s_inodes_count; /* Inodes count */ - unsigned long s_blocks_count; /* Blocks count */ - unsigned long s_r_blocks_count; /* Reserved blocks count */ - unsigned long s_free_blocks_count;/* Free blocks count */ - unsigned long s_free_inodes_count;/* Free inodes count */ - unsigned long s_first_data_block;/* First Data Block */ - unsigned long s_log_block_size; /* Block size */ - long s_log_frag_size; /* Fragment size */ - unsigned long s_blocks_per_group;/* # Blocks per group */ - unsigned long s_frags_per_group;/* # Fragments per group */ - unsigned long s_inodes_per_group;/* # Inodes per group */ - unsigned long s_mtime; /* Mount time */ - unsigned long s_wtime; /* Write time */ - unsigned long s_pad; /* Padding to get the magic signature*/ + unsigned long s_inodes_count; /* Inodes count */ + unsigned long s_blocks_count; /* Blocks count */ + unsigned long s_r_blocks_count;/* Reserved blocks count */ + unsigned long s_free_blocks_count;/* Free blocks count */ + unsigned long s_free_inodes_count;/* Free inodes count */ + unsigned long s_first_data_block;/* First Data Block */ + unsigned long s_log_block_size;/* Block size */ + long s_log_frag_size; /* Fragment size */ + unsigned long s_blocks_per_group;/* # Blocks per group */ + unsigned long s_frags_per_group;/* # Fragments per group */ + unsigned long s_inodes_per_group;/* # Inodes per group */ + unsigned long s_mtime; /* Mount time */ + unsigned long s_wtime; /* Write time */ + unsigned long s_pad; /* Padding to get the magic signature*/ /* at the same offset as in the */ /* previous ext fs */ unsigned short s_magic; /* Magic signature */ unsigned short s_valid; /* Flag */ - unsigned long s_reserved[243]; /* Padding to the end of the block */ + unsigned long s_reserved[243]; /* Padding to the end of the block */ }; /* @@ -211,10 +212,10 @@ #define EXT2_NAME_LEN 255 struct ext2_dir_entry { - unsigned long inode; /* Inode number */ - unsigned short rec_len; /* Directory entry length */ - unsigned short name_len; /* Name length */ - char name[EXT2_NAME_LEN]; /* File name */ + unsigned long inode; /* Inode number */ + unsigned short rec_len; /* Directory entry length */ + unsigned short name_len; /* Name length */ + char name[EXT2_NAME_LEN]; /* File name */ }; /* @@ -227,6 +228,7 @@ #define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ ~EXT2_DIR_ROUND) +#ifdef __KERNEL__ /* * Function prototypes */ @@ -261,7 +263,7 @@ extern int ext2_write (struct inode *, struct file *, char *, int); /* fsync.c */ -extern int ext2_sync_file(struct inode *, struct file *); +extern int ext2_sync_file (struct inode *, struct file *); /* ialloc.c */ extern struct inode * ext2_new_inode (const struct inode *, int); @@ -318,5 +320,7 @@ /* symlink.c */ extern struct inode_operations ext2_symlink_inode_operations; + +#endif #endif diff -u --recursive --new-file pl11/linux/include/linux/ext2_fs_i.h linux/include/linux/ext2_fs_i.h --- pl11/linux/include/linux/ext2_fs_i.h Fri Apr 23 19:57:55 1993 +++ linux/include/linux/ext2_fs_i.h Thu Aug 5 21:33:23 1993 @@ -5,19 +5,19 @@ * second extended file system inode data in memory */ struct ext2_inode_info { - unsigned long i_data[15]; - unsigned long i_flags; - unsigned long i_faddr; - unsigned char i_frag; - unsigned char i_fsize; + unsigned long i_data[15]; + unsigned long i_flags; + unsigned long i_faddr; + unsigned char i_frag; + unsigned char i_fsize; unsigned short i_pad1; - unsigned long i_file_acl; - unsigned long i_dir_acl; - unsigned long i_dtime; - unsigned long i_version; - unsigned long i_block_group; - unsigned long i_next_alloc_block; - unsigned long i_next_alloc_goal; + unsigned long i_file_acl; + unsigned long i_dir_acl; + unsigned long i_dtime; + unsigned long i_version; + unsigned long i_block_group; + unsigned long i_next_alloc_block; + unsigned long i_next_alloc_goal; }; #endif diff -u --recursive --new-file pl11/linux/include/linux/ext2_fs_sb.h linux/include/linux/ext2_fs_sb.h --- pl11/linux/include/linux/ext2_fs_sb.h Mon Jun 7 00:38:55 1993 +++ linux/include/linux/ext2_fs_sb.h Thu Aug 5 21:33:23 1993 @@ -8,12 +8,6 @@ * second extended-fs super-block data in memory */ struct ext2_sb_info { - unsigned long s_inodes_count; /* Inodes count */ - unsigned long s_blocks_count; /* Blocks count */ - unsigned long s_r_blocks_count; /* Reserved blocks count */ - unsigned long s_first_data_block;/* First data block */ - unsigned long s_log_block_size; /* Log of block size */ - long s_log_frag_size; /* Log of fragment size */ unsigned long s_frag_size; /* Size of a fragment in bytes */ unsigned long s_frags_per_block;/* Number of fragments per block */ unsigned long s_inodes_per_block;/* Number of inodes per block */ @@ -23,6 +17,7 @@ unsigned long s_desc_per_block; /* Number of group descriptors per block */ unsigned long s_groups_count; /* Number of groups in the fs */ struct buffer_head * s_sbh; /* Buffer containing the super block */ + struct ext2_super_block * s_es; /* Pointer to the super block in the buffer */ struct buffer_head * s_group_desc[EXT2_MAX_GROUP_DESC]; unsigned short s_loaded_inode_bitmaps; unsigned short s_loaded_block_bitmaps; diff -u --recursive --new-file pl11/linux/include/linux/fs.h linux/include/linux/fs.h --- pl11/linux/include/linux/fs.h Sun Jul 18 18:18:41 1993 +++ linux/include/linux/fs.h Wed Aug 11 22:52:45 1993 @@ -28,7 +28,7 @@ #define NR_SUPER 32 #define NR_HASH 997 #define NR_IHASH 131 -#define NR_FILE_LOCKS 32 +#define NR_FILE_LOCKS 64 #define BLOCK_SIZE 1024 #define BLOCK_SIZE_BITS 10 #define MAX_CHRDEV 32 @@ -124,6 +124,12 @@ #define BLKROGET 4702 /* get read-only status (0 = read_write) */ #define BLKRRPART 4703 /* re-read partition table */ #define BLKGETSIZE 4704 /* return device size */ +#define BLKFLSBUF 4705 /* flush buffer cache */ + +/* These are a few other constants only used by scsi devices */ + +#define SCSI_IOCTL_GET_IDLUN 0x5382 + #define BMAP_IOCTL 1 /* obsolete - kept for compatibility */ #define FIBMAP 1 /* bmap access */ diff -u --recursive --new-file pl11/linux/include/linux/ioctl.h linux/include/linux/ioctl.h --- pl11/linux/include/linux/ioctl.h Mon Jan 11 21:39:37 1993 +++ linux/include/linux/ioctl.h Mon Aug 2 23:34:18 1993 @@ -1,4 +1,4 @@ -/* $Id: ioctl.h,v 1.2 1992/11/18 01:31:16 root Exp root $ +/* $Id: ioctl.h,v 1.5 1993/07/19 21:53:50 root Exp root $ * * linux/ioctl.h for Linux by H.H. Bergman. */ @@ -8,11 +8,13 @@ /* ioctl command encoding: 32 bits total, command in lower 16 bits, - * size of the parameter structure in the upper 14 bits. - * Encoding size in ioctl request is useful for catching old versions + * size of the parameter structure in the lower 14 bits of the + * upper 16 bits. + * Encoding the size of the parameter structure in the ioctl request + * is useful for catching programs compiled with old versions * and to avoid overwriting user space outside the user buffer area. - * The highest 2 bits are reserved. - * NOTE: This limits the max blocksize to 16kB -1 ! + * The highest 2 bits are reserved for indicating the ``access mode''. + * NOTE: This limits the max parameter size to 16kB -1 ! */ #define IOC_VOID 0x00000000 /* param in size field */ @@ -24,17 +26,22 @@ #define IOCCMD_MASK 0x0000ffff /* command code */ #define IOCCMD_SHIFT 0 -#define _IO(c,d) (IOC_VOID | ((d)<<16) | c) /* param encoded */ -/* use _IOXX(magic, subcode, arg_t) where arg_t is the type of the + +/* _IO(magic, subcode); size field is zero and the + * subcode determines the command. + */ +#define _IO(c,d) (IOC_VOID | ((c)<<8) | (d)) /* param encoded */ + +/* _IOXX(magic, subcode, arg_t); where arg_t is the type of the * (last) argument field in the ioctl call, if present. */ #define _IOW(c,d,t) (IOC_IN | ((sizeof(t)<<16) & IOCSIZE_MASK) | \ - (c<<8) | d) + ((c)<<8) | (d)) #define _IOR(c,d,t) (IOC_OUT | ((sizeof(t)<<16) & IOCSIZE_MASK) | \ - (c<<8) | d) + ((c)<<8) | (d)) /* WR rather than RW to avoid conflict with stdio.h */ #define _IOWR(c,d,t) (IOC_INOUT | ((sizeof(t)<<16) & IOCSIZE_MASK) | \ - (c<<8) | d) + ((c)<<8) | (d)) #endif /* _LINUX_IOCTL_H */ diff -u --recursive --new-file pl11/linux/include/linux/kd.h linux/include/linux/kd.h --- pl11/linux/include/linux/kd.h Wed Jun 2 21:57:55 1993 +++ linux/include/linux/kd.h Thu Jul 29 16:56:24 1993 @@ -176,4 +176,11 @@ #define KDGKBENT 0x4B46 /* gets one entry in translation table */ #define KDSKBENT 0x4B47 /* sets one entry in translation table */ +struct kbsentry { + u_char kb_func; + u_char kb_string[512]; /* FUNC_BUFSIZE from keyboard.h */ +}; +#define KDGKBSENT 0x4B48 /* gets one function key string entry */ +#define KDSKBSENT 0x4B49 /* sets one function key string entry */ + #endif /* _LINUX_KD_H */ diff -u --recursive --new-file pl11/linux/include/linux/kernel.h linux/include/linux/kernel.h --- pl11/linux/include/linux/kernel.h Wed Jul 14 16:57:11 1993 +++ linux/include/linux/kernel.h Mon Aug 9 18:02:30 1993 @@ -9,6 +9,11 @@ #include +#define INT_MAX ((int)(~0U>>1)) +#define UINT_MAX (~0U) +#define LONG_MAX ((long)(~0UL>>1)) +#define ULONG_MAX (~0UL) + #define VERIFY_READ 0 #define VERIFY_WRITE 1 diff -u --recursive --new-file pl11/linux/include/linux/keyboard.h linux/include/linux/keyboard.h --- pl11/linux/include/linux/keyboard.h Sat Jul 3 02:47:18 1993 +++ linux/include/linux/keyboard.h Thu Jul 29 16:57:52 1993 @@ -5,31 +5,6 @@ #define set_leds() mark_bh(KEYBOARD_BH) /* - * Global flags: things that don't change between virtual consoles. - * This includes things like "key-down" flags - if the shift key is - * down when you change a console, it's down in both. - * - * Note that the KG_CAPSLOCK flags is NOT the flag that decides if - * capslock is on or not: it's just a flag about the key being - * physically down. The actual capslock status is in the local flags. - */ -extern unsigned long kbd_flags; - -/* - * These are the hardcoded global flags - use the numbers beyond 16 - * for non-standard or keyboard-dependent flags - */ -#define KG_LSHIFT 0 -#define KG_RSHIFT 1 -#define KG_LCTRL 2 -#define KG_RCTRL 3 -#define KG_LALT 4 -#define KG_RALT 5 /* doesn't exist, but.. */ -#define KG_LALTGR 6 /* doesn't exist, but.. */ -#define KG_RALTGR 7 -#define KG_CAPSLOCK 8 - -/* * "dead" keys - prefix key values that are valid only for the next * character code (sticky shift, E0/E1 special scancodes, diacriticals) */ @@ -72,26 +47,6 @@ extern unsigned long kbd_init(unsigned long); -extern inline int kbd_flag(int flag) -{ - return kbd_flags & (1 << flag); -} - -extern inline void set_kbd_flag(int flag) -{ - kbd_flags |= 1 << flag; -} - -extern inline void clr_kbd_flag(int flag) -{ - kbd_flags &= ~(1 << flag); -} - -extern inline void chg_kbd_flag(int flag) -{ - kbd_flags ^= 1 << flag; -} - extern inline int kbd_dead(int flag) { return kbd_prev_dead_keys & (1 << flag); @@ -132,12 +87,17 @@ kbd->flags ^= 1 << flag; } -#define NR_KEYS 112 -#define NR_KEYMAPS 3 +#define NR_KEYS 128 +#define NR_KEYMAPS 16 extern const int NR_TYPES; extern const int max_vals[]; extern unsigned short key_map[NR_KEYMAPS][NR_KEYS]; +#define NR_FUNC 32 +#define FUNC_BUFSIZE 512 +extern char func_buf[FUNC_BUFSIZE]; +extern char *func_table[NR_FUNC]; + #define KT_LATIN 0 /* we depend on this being zero */ #define KT_FN 1 #define KT_SPEC 2 @@ -146,6 +106,8 @@ #define KT_CONS 5 #define KT_CUR 6 #define KT_SHIFT 7 +#define KT_META 8 +#define KT_ASCII 9 #define K(t,v) (((t)<<8)|(v)) #define KTYP(x) ((x) >> 8) @@ -188,6 +150,10 @@ #define K_CAPS K(KT_SPEC,7) #define K_NUM K(KT_SPEC,8) #define K_HOLD K(KT_SPEC,9) +#define K_SCROLLFORW K(KT_SPEC,10) +#define K_SCROLLBACK K(KT_SPEC,11) +#define K_BOOT K(KT_SPEC,12) +#define K_CAPSON K(KT_SPEC,13) #define K_P0 K(KT_PAD,0) #define K_P1 K(KT_PAD,1) @@ -218,16 +184,29 @@ #define K_RIGHT K(KT_CUR,2) #define K_UP K(KT_CUR,3) -#define K_LSHIFT K(KT_SHIFT,KG_LSHIFT) -#define K_RSHIFT K(KT_SHIFT,KG_RSHIFT) -#define K_LCTRL K(KT_SHIFT,KG_LCTRL) -#define K_RCTRL K(KT_SHIFT,KG_RCTRL) -#define K_LALT K(KT_SHIFT,KG_LALT) -#define K_RALT K(KT_SHIFT,KG_RALT) -#define K_LALTGR K(KT_SHIFT,KG_LALTGR) -#define K_RALTGR K(KT_SHIFT,KG_RALTGR) - -#define K_ALT K_LALT -#define K_ALTGR K_RALTGR +#define KG_SHIFT 0 +#define KG_CTRL 2 +#define KG_ALT 3 +#define KG_ALTGR 1 + +#define K_SHIFT K(KT_SHIFT,KG_SHIFT) +#define K_CTRL K(KT_SHIFT,KG_CTRL) +#define K_ALT K(KT_SHIFT,KG_ALT) +#define K_ALTGR K(KT_SHIFT,KG_ALTGR) + +#define NR_SHIFT 16 + +#define K_CAPSSHIFT K(KT_SHIFT,NR_SHIFT) + +#define K_ASC0 K(KT_ASCII,0) +#define K_ASC1 K(KT_ASCII,1) +#define K_ASC2 K(KT_ASCII,2) +#define K_ASC3 K(KT_ASCII,3) +#define K_ASC4 K(KT_ASCII,4) +#define K_ASC5 K(KT_ASCII,5) +#define K_ASC6 K(KT_ASCII,6) +#define K_ASC7 K(KT_ASCII,7) +#define K_ASC8 K(KT_ASCII,8) +#define K_ASC9 K(KT_ASCII,9) #endif diff -u --recursive --new-file pl11/linux/include/linux/ldt.h linux/include/linux/ldt.h --- pl11/linux/include/linux/ldt.h +++ linux/include/linux/ldt.h Tue Jul 20 02:26:24 1993 @@ -0,0 +1,28 @@ +/* + * ldt.h + * + * Definitions of structures used with the modify_ldt system call. + */ +#ifndef _LINUX_LDT_H +#define _LINUX_LDT_H + +struct modify_ldt_ldt_s { + unsigned int entry_number; + unsigned long base_addr; + unsigned int limit; + unsigned int seg_32bit:1; + unsigned int contents:2; + unsigned int read_exec_only:1; + unsigned int limit_in_pages:1; +}; + +#define MODIFY_LDT_CONTENTS_DATA 0 +#define MODIFY_LDT_CONTENTS_STACK 1 +#define MODIFY_LDT_CONTENTS_CODE 2 + +extern int get_ldt(void *buffer); +extern int set_ldt_entry(int entry, unsigned long base, unsigned int limit, + int seg_32bit_flag, int contents, int read_only_flag, + int limit_in_pages_flag); + +#endif diff -u --recursive --new-file pl11/linux/include/linux/mcd.h linux/include/linux/mcd.h --- pl11/linux/include/linux/mcd.h +++ linux/include/linux/mcd.h Sat Aug 14 23:47:22 1993 @@ -0,0 +1,101 @@ +/* + * Definitions for a Mitsumi CD-ROM interface + * + * Copyright (C) 1992 Martin Harriss + * + * martin@bdsi.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* *** change this to set the I/O port address */ +#define MCDPORT(x) (0x320 + (x)) + +/* *** change this to set the interrupt number */ +#define MCD_INTR_NR 11 + + + + + +/* status bits */ + +#define MST_CMD_CHECK 0x01 /* command error */ +#define MST_BUSY 0x02 /* now playing */ +#define MST_READ_ERR 0x04 /* read error */ +#define MST_DSK_TYPE 0x08 +#define MST_SERVO_CHECK 0x10 +#define MST_DSK_CHG 0x20 /* disk removed or changed */ +#define MST_READY 0x40 /* disk in the drive */ +#define MST_DOOR_OPEN 0x80 /* door is open */ + +/* flag bits */ + +#define MFL_DATA 0x02 /* data available */ +#define MFL_STATUS 0x04 /* status available */ + +/* commands */ + +#define MCMD_GET_DISK_INFO 0x10 /* read info from disk */ +#define MCMD_GET_Q_CHANNEL 0x20 /* read info from q channel */ +#define MCMD_GET_STATUS 0x40 +#define MCMD_SET_MODE 0x50 +#define MCMD_SOFT_RESET 0x60 +#define MCMD_STOP 0x70 /* stop play */ +#define MCMD_CONFIG_DRIVE 0x90 +#define MCMD_SET_VOLUME 0xAE /* set audio level */ +#define MCMD_PLAY_READ 0xC0 /* play or read data */ +#define MCMD_GET_VERSION 0xDC + +/* borrowed from hd.c */ + +#define READ_DATA(port, buf, nr) \ +__asm__("cld;rep;insb": :"d" (port),"D" (buf),"c" (nr):"cx","di") + +#define SET_TIMER(func, jifs) \ + ((timer_table[MCD_TIMER].expires = jiffies + jifs), \ + (timer_table[MCD_TIMER].fn = func), \ + (timer_active |= 1< #include #include diff -u --recursive --new-file pl11/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h --- pl11/linux/include/linux/msdos_fs.h Sat Jul 3 10:54:28 1993 +++ linux/include/linux/msdos_fs.h Mon Aug 9 18:02:30 1993 @@ -103,11 +103,11 @@ /* Convert attribute bits and a mask to the UNIX mode. */ -#define MSDOS_MKMODE(a,m) (m & (a & ATTR_RO ? 0555 : 0777)) +#define MSDOS_MKMODE(a,m) (m & (a & ATTR_RO ? S_IRUGO|S_IXUGO : S_IRWXUGO)) /* Convert the UNIX mode to MS-DOS attribute bits. */ -#define MSDOS_MKATTR(m) ((m & 0200) ? ATTR_NONE : ATTR_RO) +#define MSDOS_MKATTR(m) ((m & S_IWUGO) ? ATTR_NONE : ATTR_RO) static inline struct buffer_head *msdos_sread(int dev,int sector,void **start) diff -u --recursive --new-file pl11/linux/include/linux/mtio.h linux/include/linux/mtio.h --- pl11/linux/include/linux/mtio.h Sun Jul 4 15:06:22 1993 +++ linux/include/linux/mtio.h Wed Jul 28 00:19:31 1993 @@ -48,6 +48,8 @@ #define MTSETDENSITY 21 /* set tape density (SCSI) */ #define MTSEEK 22 /* seek to block (Tandberg, etc.) */ #define MTTELL 23 /* tell block (Tandber, etc.) */ +#define MTSETDRVBUFFER 24 /* set the drive buffering according to SCSI-2 */ + /* ordinary buffered operation with code 1 */ /* structure for MTIOCGET - mag tape get status command */ diff -u --recursive --new-file pl11/linux/include/linux/nfs.h linux/include/linux/nfs.h --- pl11/linux/include/linux/nfs.h Wed May 26 21:38:47 1993 +++ linux/include/linux/nfs.h Mon Aug 9 18:02:30 1993 @@ -8,7 +8,7 @@ #define NFS_MAXGROUPS 16 #define NFS_FHSIZE 32 #define NFS_COOKIESIZE 4 -#define NFS_FIFO_DEV -1 +#define NFS_FIFO_DEV (-1) #define NFSMODE_FMT 0170000 #define NFSMODE_DIR 0040000 #define NFSMODE_CHR 0020000 diff -u --recursive --new-file pl11/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h --- pl11/linux/include/linux/nfs_fs.h Tue Apr 27 20:28:27 1993 +++ linux/include/linux/nfs_fs.h Sun Aug 15 11:13:36 1993 @@ -116,4 +116,9 @@ extern struct inode_operations nfs_symlink_inode_operations; +/* linux/fs/nfs/mmap.c */ + +extern int nfs_mmap(struct inode * inode, struct file * file, + unsigned long addr, size_t len, int prot, unsigned long off); + #endif diff -u --recursive --new-file pl11/linux/include/linux/page.h linux/include/linux/page.h --- pl11/linux/include/linux/page.h +++ linux/include/linux/page.h Mon Aug 9 18:02:30 1993 @@ -0,0 +1,34 @@ +#ifndef _LINUX_PAGE_H +#define _LINUX_PAGE_H + + /* PAGE_SHIFT determines the page size */ +#define PAGE_SHIFT 12 +#define PAGE_SIZE ((unsigned long)1<>(PAGE_SHIFT-SIZEOF_PTR_LOG2)*2&PTR_MASK&~PAGE_MASK))) + /* to find an entry in a page-table */ +#define PAGE_PTR(address) \ + ((unsigned long)(address)>>PAGE_SHIFT-SIZEOF_PTR_LOG2&PTR_MASK&~PAGE_MASK) + /* the no. of pointers that fit on a page */ +#define PTRS_PER_PAGE (PAGE_SIZE/sizeof(void*)) + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_PAGE_H */ diff -u --recursive --new-file pl11/linux/include/linux/param.h linux/include/linux/param.h --- pl11/linux/include/linux/param.h Tue Nov 10 12:54:55 1992 +++ linux/include/linux/param.h Mon Aug 9 18:02:30 1993 @@ -12,7 +12,7 @@ #endif #ifndef NOGROUP -#define NOGROUP -1 +#define NOGROUP (-1) #endif #define MAXHOSTNAMELEN 64 /* max length of hostname */ diff -u --recursive --new-file pl11/linux/include/linux/resource.h linux/include/linux/resource.h --- pl11/linux/include/linux/resource.h Tue Nov 10 12:54:55 1992 +++ linux/include/linux/resource.h Mon Aug 9 18:02:30 1993 @@ -13,8 +13,8 @@ * structure will lose. This reduces the chances of that happening. */ #define RUSAGE_SELF 0 -#define RUSAGE_CHILDREN -1 -#define RUSAGE_BOTH -2 /* sys_wait4() uses this */ +#define RUSAGE_CHILDREN (-1) +#define RUSAGE_BOTH (-2) /* sys_wait4() uses this */ struct rusage { struct timeval ru_utime; /* user time used */ @@ -54,7 +54,7 @@ #define RLIM_NLIMITS 6 -#define RLIM_INFINITY 0x7fffffff +#define RLIM_INFINITY LONG_MAX struct rlimit { int rlim_cur; @@ -61,7 +61,7 @@ int rlim_max; }; -#define PRIO_MIN -99 +#define PRIO_MIN (-99) #define PRIO_MAX 14 #define PRIO_PROCESS 0 diff -u --recursive --new-file pl11/linux/include/linux/sched.h linux/include/linux/sched.h --- pl11/linux/include/linux/sched.h Wed Jul 14 15:58:34 1993 +++ linux/include/linux/sched.h Sat Aug 14 16:01:26 1993 @@ -1,6 +1,8 @@ #ifndef _LINUX_SCHED_H #define _LINUX_SCHED_H +#define NEW_SWAP + /* * define DEBUG if you want the wait-queues to have some extra * debugging code. It's not normally used, but might catch some @@ -165,7 +167,7 @@ int elf_executable:1; int dumpable:1; int swappable:1; - unsigned long start_code,end_code,end_data,brk,start_stack,start_mmap; + unsigned long start_code,end_code,end_data,start_brk,brk,start_stack,start_mmap; unsigned long arg_start, arg_end, env_start, env_end; long pid,pgrp,session,leader; int groups[NGROUPS]; @@ -206,10 +208,17 @@ struct sem_undo *semun; struct file * filp[NR_OPEN]; fd_set close_on_exec; -/* ldt for this task - not currently used */ - struct desc_struct ldt[32]; +/* ldt for this task - used by Wine. If NULL, default_ldt is used */ + struct desc_struct *ldt; /* tss for this task */ struct tss_struct tss; +#ifdef NEW_SWAP + unsigned long old_maj_flt; /* old value of maj_flt */ + unsigned long dec_flt; /* page fault count of the last time */ + unsigned long swap_cnt; /* number of pages to swap on next pass */ + short swap_table; /* current page table */ + short swap_page; /* current page */ +#endif NEW_SWAP }; /* @@ -236,7 +245,7 @@ /* schedlink */ &init_task,&init_task, \ /* signals */ {{ 0, },}, \ /* stack */ 0,0, \ -/* ec,brk... */ 0,0,0,0,0,0,0,0,0,0,0, \ +/* ec,brk... */ 0,0,0,0,0,0,0,0,0,0,0,0, \ /* argv.. */ 0,0,0,0, \ /* pid etc.. */ 0,0,0,0, \ /* suppl grps*/ {NOGROUP,}, \ @@ -244,9 +253,9 @@ /* uid etc */ 0,0,0,0,0,0, \ /* timeout */ 0,0,0,0,0,0,0,0,0,0,0,0, \ /* min_flt */ 0,0,0,0, \ -/* rlimits */ { {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, \ - {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, \ - {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}}, \ +/* rlimits */ { {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}, \ + {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}, \ + {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}}, \ /* math */ 0, \ /* rss */ 2, \ /* comm */ "swapper", \ @@ -255,9 +264,7 @@ /* ipc */ NULL, NULL, \ /* filp */ {NULL,}, \ /* cloe */ {{ 0, }}, \ - { \ -/* ldt */ {0,0}, \ - }, \ +/* ldt */ NULL, \ /*tss*/ {0,0, \ sizeof(init_kernel_stack) + (long) &init_kernel_stack, KERNEL_DS, 0, \ 0,0,0,0,0,0, \ @@ -266,7 +273,7 @@ USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0, \ _LDT(0),0, \ 0, 0x8000, \ -/* ioperm */ {0xffffffff, }, \ +/* ioperm */ {~0, }, \ _TSS(0), \ /* 387 state */ { { 0, }, } \ } \ @@ -280,8 +287,11 @@ extern unsigned long startup_time; extern int jiffies_offset; extern int need_resched; + extern int hard_math; +extern int x86; extern int ignore_irq13; +extern int wp_works_ok; #define CURRENT_TIME (startup_time+(jiffies+jiffies_offset)/HZ) @@ -345,8 +355,6 @@ "c" (tsk) \ :"cx") -#define PAGE_ALIGN(n) (((n)+0xfff)&0xfffff000) - #define _set_base(addr,base) \ __asm__("movw %%dx,%0\n\t" \ "rorl $16,%%edx\n\t" \ @@ -483,24 +491,39 @@ return __limit+1; } -#define REMOVE_LINKS(p) \ +#define REMOVE_LINKS(p) do { unsigned long flags; \ + save_flags(flags) ; cli(); \ (p)->next_task->prev_task = (p)->prev_task; \ (p)->prev_task->next_task = (p)->next_task; \ + restore_flags(flags); \ if ((p)->p_osptr) \ (p)->p_osptr->p_ysptr = (p)->p_ysptr; \ if ((p)->p_ysptr) \ (p)->p_ysptr->p_osptr = (p)->p_osptr; \ else \ - (p)->p_pptr->p_cptr = (p)->p_osptr + (p)->p_pptr->p_cptr = (p)->p_osptr; \ + } while (0) -#define SET_LINKS(p) \ +#define SET_LINKS(p) do { unsigned long flags; \ + save_flags(flags); cli(); \ (p)->next_task = &init_task; \ (p)->prev_task = init_task.prev_task; \ init_task.prev_task->next_task = (p); \ init_task.prev_task = (p); \ + restore_flags(flags); \ (p)->p_ysptr = NULL; \ if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL) \ (p)->p_osptr->p_ysptr = p; \ - (p)->p_pptr->p_cptr = p + (p)->p_pptr->p_cptr = p; \ + } while (0) + +#define for_each_task(p) \ + for (p = &init_task ; (p = p->next_task) != &init_task ; ) + +/* + * This is the ldt that every process will get unless we need + * something other than this. + */ +extern struct desc_struct default_ldt; #endif diff -u --recursive --new-file pl11/linux/include/linux/serial.h linux/include/linux/serial.h --- pl11/linux/include/linux/serial.h Thu Mar 11 19:39:28 1993 +++ linux/include/linux/serial.h Sat Aug 14 18:30:03 1993 @@ -15,6 +15,8 @@ * * For definitions of the flags field, see tty.h */ +#ifndef _LINUX_SERIAL_H +#define _LINUX_SERIAL_H struct async_struct { int baud_base; @@ -21,6 +23,7 @@ int port; int irq; int flags; /* defined in tty.h */ + int hub6; /* HUB6 plus one */ int type; /* UART type */ struct tty_struct *tty; int read_status_mask; @@ -29,11 +32,14 @@ int custom_divisor; int x_char; /* xon/xoff characater */ int close_delay; + int IER; /* Interrupt Enable Register */ int event; int line; int count; /* # of fd on device */ int blocked_open; /* # of blocked opens */ - struct wait_queue *open_wait; + long session; /* Session of opening process */ + long pgrp; /* pgrp of opening process */ + struct wait_queue *open_wait; struct async_struct *next_port; /* For the linked list */ struct async_struct *prev_port; }; @@ -150,3 +156,5 @@ #define UART_MSR_DDSR 0x02 /* Delta DSR */ #define UART_MSR_DCTS 0x01 /* Delta CTS */ #define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */ + +#endif /* _LINUX_SERIAL_H */ diff -u --recursive --new-file pl11/linux/include/linux/shm.h linux/include/linux/shm.h --- pl11/linux/include/linux/shm.h Wed May 26 21:39:11 1993 +++ linux/include/linux/shm.h Mon Aug 9 18:02:30 1993 @@ -34,14 +34,42 @@ int shmall; }; -#define SHMMAX 0x400000 /* <= 4M */ /* max shared seg size (bytes) */ -#define SHMMIN 1 /* really PAGE_SIZE */ /* min shared seg size (bytes)*/ -#define SHMMNI 128 /* <= 4096 */ /* max num of segs system wide */ -#define SHMALL 0x10000 /* <= SHMMAX*SHMMNI/PAGE_SIZE */ /* max shm system wide (pages) */ -#define SHMLBA 0x1000 /* = PAGE_SIZE */ /* attach addr multiple */ -#define SHMSEG SHMMNI /* <= SHMMNI */ /* max shared segs per process */ +#define SHM_RANGE_START 0x40000000 +#define SHM_RANGE_END 0x60000000 + /* _SHM_ID_BITS is a variable you can adjust to */ + /* tune the kernel. It determines the value of */ + /* SHMMNI, which specifies the maximum no. of */ + /* shared segments (system wide). SRB. */ +#define _SHM_ID_BITS 7 /* keep as low as possible */ + /* a static array is declared */ + /* using SHMMNI */ + +#define __SHM_IDX_BITS (BITS_PER_PTR-2-SHM_IDX_SHIFT) + +/* !!!!!!!????? + * Why reserve the two (2) high bits of the signature (shm_sgn) field? + * Since, as far as I can see, only the high bit is used (SHM_READ_ONLY). + * SRB. + */ + +#define _SHM_IDX_BITS (__SHM_IDX_BITS+PAGE_SHIFT>=BITS_PER_PTR?\ + BITS_PER_PTR-PAGE_SHIFT-1:__SHM_IDX_BITS) /* sanity check */ +/* not present page table entry format bit 0 is 0, low byte defined in mm.h */ +#define SHM_ID_SHIFT 8 +#define SHM_ID_MASK ((1<<_SHM_ID_BITS)-1) +#define SHM_IDX_SHIFT (SHM_ID_SHIFT+_SHM_ID_BITS) +#define SHM_IDX_MASK ((1<<_SHM_IDX_BITS)-1) +#define SHM_READ_ONLY (1< #include #include +#include extern unsigned long * prof_buffer; extern unsigned long prof_len; extern char edata, end; extern char *linux_banner; +extern "C" void lcall7(void); +struct desc_struct default_ldt; /* * we need this inline - forking from kernel space will result @@ -61,7 +64,7 @@ static char printbuf[1024]; -extern char empty_zero_page[4096]; +extern char empty_zero_page[PAGE_SIZE]; extern int vsprintf(char *,const char *,va_list); extern void init(void); extern void init_IRQ(void); @@ -332,12 +335,13 @@ * Interrupts are still disabled. Do necessary setups, then * enable them */ + set_call_gate(&default_ldt,lcall7); ROOT_DEV = ORIG_ROOT_DEV; drive_info = DRIVE_INFO; screen_info = SCREEN_INFO; aux_device_present = AUX_DEVICE_INFO; memory_end = (1<<20) + (EXT_MEM_K<<10); - memory_end &= 0xfffff000; + memory_end &= PAGE_MASK; ramdisk_size = RAMDISK_SIZE; strcpy(command_line,COMMAND_LINE); #ifdef CONFIG_MAX_16M @@ -348,13 +352,12 @@ root_mountflags |= MS_RDONLY; if ((unsigned long)&end >= (1024*1024)) { memory_start = (unsigned long) &end; - low_memory_start = 4096; + low_memory_start = PAGE_SIZE; } else { memory_start = 1024*1024; low_memory_start = (unsigned long) &end; } - low_memory_start += 0xfff; - low_memory_start &= 0xfffff000; + low_memory_start = PAGE_ALIGN(low_memory_start); memory_start = paging_init(memory_start,memory_end); trap_init(); init_IRQ(); @@ -409,6 +412,13 @@ printk("Ok, fpu using %s error reporting.\n", ignore_irq13?"exception 16":"irq13"); } +#ifndef CONFIG_MATH_EMULATION + else { + printk("No coprocessor found and no math emulation present.\n"); + printk("Giving up.\n"); + for (;;) ; + } +#endif move_to_user_mode(); if (!fork()) /* we count on this going ok */ init(); @@ -445,7 +455,9 @@ (void) dup(0); (void) dup(0); + system_utsname.machine[1] = '0' + x86; printf(linux_banner); + execve("/etc/init",argv_init,envp_init); execve("/bin/init",argv_init,envp_init); execve("/sbin/init",argv_init,envp_init); diff -u --recursive --new-file pl11/linux/ipc/msg.c linux/ipc/msg.c --- pl11/linux/ipc/msg.c Mon Jul 5 00:00:31 1993 +++ linux/ipc/msg.c Mon Aug 9 18:02:31 1993 @@ -7,6 +7,7 @@ #include #include #include +#include extern int ipcperms (struct ipc_perm *ipcp, short msgflg); @@ -59,7 +60,7 @@ slept: if (ipcp->seq != (msqid / MSGMNI)) return -EIDRM; - if (ipcperms(ipcp, 0222)) + if (ipcperms(ipcp, S_IWUGO)) return -EACCES; if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { @@ -137,7 +138,7 @@ while (!nmsg) { if(ipcp->seq != msqid / MSGMNI) return -EIDRM; - if (ipcperms (ipcp, 0444)) + if (ipcperms (ipcp, S_IRUGO)) return -EACCES; if (msgtyp == 0) nmsg = msq->msg_first; @@ -243,7 +244,7 @@ return -ENOMEM; } ipcp = &msq->msg_perm; - ipcp->mode = (msgflg & 0x01FF); + ipcp->mode = (msgflg & S_IRWXUGO); ipcp->key = key; ipcp->cuid = ipcp->uid = current->euid; ipcp->gid = ipcp->cgid = current->egid; @@ -359,7 +360,7 @@ msq = msgque[msqid]; if (msq == IPC_UNUSED || msq == IPC_NOID) return -EINVAL; - if (ipcperms (&msq->msg_perm, 0444)) + if (ipcperms (&msq->msg_perm, S_IRUGO)) return -EACCES; id = msqid + msq->msg_perm.seq * MSGMNI; memcpy_tofs (buf, msq, sizeof(*msq)); @@ -388,7 +389,7 @@ switch (cmd) { case IPC_STAT: - if (ipcperms (ipcp, 0444)) + if (ipcperms (ipcp, S_IRUGO)) return -EACCES; memcpy_tofs (buf, msq, sizeof (*msq)); return 0; @@ -406,8 +407,8 @@ msq->msg_qbytes = tbuf.msg_qbytes; ipcp->uid = tbuf.msg_perm.uid; ipcp->gid = tbuf.msg_perm.gid; - ipcp->mode = (ipcp->mode & ~0x1FF) | - (0x1FF & tbuf.msg_perm.mode); + ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | + (S_IRWXUGO & tbuf.msg_perm.mode); msq->msg_ctime = CURRENT_TIME; break; default: diff -u --recursive --new-file pl11/linux/ipc/sem.c linux/ipc/sem.c --- pl11/linux/ipc/sem.c Mon Jul 5 00:00:31 1993 +++ linux/ipc/sem.c Mon Aug 9 18:02:31 1993 @@ -9,6 +9,7 @@ #include #include #include +#include extern int ipcperms (struct ipc_perm *ipcp, short semflg); static int newary (key_t, int, int); @@ -79,7 +80,7 @@ memset (sma, 0, size); sma->sem_base = (struct sem *) &sma[1]; ipcp = &sma->sem_perm; - ipcp->mode = (semflg & 0x01FF); + ipcp->mode = (semflg & S_IRWXUGO); ipcp->key = key; ipcp->cuid = ipcp->uid = current->euid; ipcp->gid = ipcp->cgid = current->egid; @@ -198,7 +199,7 @@ sma = semary[semid]; if (sma == IPC_UNUSED || sma == IPC_NOID) return -EINVAL; - if (ipcperms (&sma->sem_perm, 0444)) + if (ipcperms (&sma->sem_perm, S_IRUGO)) return -EACCES; id = semid + sma->sem_perm.seq * SEMMNI; memcpy_tofs (buf, sma, sizeof(*sma)); @@ -223,7 +224,7 @@ case GETNCNT: case GETZCNT: case GETALL: - if (ipcperms (ipcp, 0444)) + if (ipcperms (ipcp, S_IRUGO)) return -EACCES; switch (cmd) { case GETVAL : return curr->semval; @@ -283,7 +284,7 @@ switch (cmd) { case GETALL: - if (ipcperms (ipcp, 0444)) + if (ipcperms (ipcp, S_IRUGO)) return -EACCES; for (i=0; i< sma->sem_nsems; i++) sem_io[i] = sma->sem_base[i].semval; @@ -290,7 +291,7 @@ memcpy_tofs (array, sem_io, nsems*sizeof(ushort)); break; case SETVAL: - if (ipcperms (ipcp, 0222)) + if (ipcperms (ipcp, S_IWUGO)) return -EACCES; for (un = sma->undo; un; un = un->id_next) if (semnum == un->sem_num) @@ -307,19 +308,19 @@ current->euid == ipcp->uid) { ipcp->uid = tbuf.sem_perm.uid; ipcp->gid = tbuf.sem_perm.gid; - ipcp->mode = (ipcp->mode & ~0777) - | (tbuf.sem_perm.mode & 0777); + ipcp->mode = (ipcp->mode & ~S_IRWXUGO) + | (tbuf.sem_perm.mode & S_IRWXUGO); sma->sem_ctime = CURRENT_TIME; return 0; } return -EPERM; case IPC_STAT: - if (ipcperms (ipcp, 0444)) + if (ipcperms (ipcp, S_IRUGO)) return -EACCES; memcpy_tofs (buf, sma, sizeof (*sma)); break; case SETALL: - if (ipcperms (ipcp, 0222)) + if (ipcperms (ipcp, S_IWUGO)) return -EACCES; for (i=0; isem_base[i].semval = sem_io[i]; @@ -368,7 +369,7 @@ semncnt ++; } } - if (ipcperms(&sma->sem_perm, alter ? 0222 : 0444)) + if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) return -EACCES; /* * ensure every sop with undo gets an undo structure diff -u --recursive --new-file pl11/linux/ipc/shm.c linux/ipc/shm.c --- pl11/linux/ipc/shm.c Mon Jul 5 00:00:31 1993 +++ linux/ipc/shm.c Mon Aug 9 18:02:31 1993 @@ -2,7 +2,7 @@ * linux/ipc/shm.c * Copyright (C) 1992, 1993 Krishna Balasubramanian * Many improvements/fixes by Bruno Haible. - * assume user segments start at 0x00 + * assume user segments start at 0x0 */ #include @@ -10,6 +10,7 @@ #include #include #include +#include extern int ipcperms (struct ipc_perm *ipcp, short semflg); extern unsigned int get_swap_page(void); @@ -99,7 +100,7 @@ for (i=0; i< numpages; shp->shm_pages[i++] = 0); shm_tot += numpages; shp->shm_perm.key = key; - shp->shm_perm.mode = (shmflg & 0777); + shp->shm_perm.mode = (shmflg & S_IRWXUGO); shp->shm_perm.cuid = shp->shm_perm.uid = current->euid; shp->shm_perm.cgid = shp->shm_perm.gid = current->egid; shp->shm_perm.seq = shm_seq; @@ -177,7 +178,7 @@ if (!(page = shp->shm_pages[i])) continue; if (page & 1) { - free_page (page & ~0xfff); + free_page (page & PAGE_MASK); shm_rss--; } else { swap_free (page); @@ -252,7 +253,7 @@ shp = shm_segs[shmid]; if (shp == IPC_UNUSED || shp == IPC_NOID) return -EINVAL; - if (ipcperms (&shp->shm_perm, 0444)) + if (ipcperms (&shp->shm_perm, S_IRUGO)) return -EACCES; id = shmid + shp->shm_perm.seq * SHMMNI; memcpy_tofs (buf, shp, sizeof(*shp)); @@ -285,7 +286,7 @@ ipcp->mode |= SHM_LOCKED; break; case IPC_STAT: - if (ipcperms (ipcp, 0444)) + if (ipcperms (ipcp, S_IRUGO)) return -EACCES; if (!buf) return -EFAULT; @@ -299,8 +300,8 @@ current->euid == shp->shm_perm.cuid) { ipcp->uid = tbuf.shm_perm.uid; ipcp->gid = tbuf.shm_perm.gid; - ipcp->mode = (ipcp->mode & ~0777) - | (tbuf.shm_perm.mode & 0777); + ipcp->mode = (ipcp->mode & ~S_IRWXUGO) + | (tbuf.shm_perm.mode & S_IRWXUGO); shp->shm_ctime = CURRENT_TIME; break; } @@ -334,16 +335,16 @@ /* check that the range is unmapped and has page_tables */ for (tmp = shmd->start; tmp < shmd->end; tmp += PAGE_SIZE) { - page_table = (ulong *) (page_dir + ((tmp >> 20) & 0xffc)); + page_table = PAGE_DIR_OFFSET(page_dir,tmp); if (*page_table & PAGE_PRESENT) { - page_table = (ulong *) (0xfffff000 & *page_table); - page_table += ((tmp >> PAGE_SHIFT) & 0x3ff); + page_table = (ulong *) (PAGE_MASK & *page_table); + page_table += ((tmp >> PAGE_SHIFT) & PTRS_PER_PAGE-1); if (*page_table) { if (!remap) return -EINVAL; if (*page_table & PAGE_PRESENT) { --current->rss; - free_page (*page_table & ~0xfff); + free_page (*page_table & PAGE_MASK); } else swap_free (*page_table); @@ -352,11 +353,11 @@ continue; } { - unsigned long new_pt = get_free_page(GFP_KERNEL); - if (!new_pt) + unsigned long new_pt; + if(!(new_pt = get_free_page(GFP_KERNEL))) /* clearing needed? SRB. */ return -ENOMEM; *page_table = new_pt | PAGE_TABLE; - tmp = ((tmp + (PAGE_SIZE << 10) - 1) & 0xff400000) -PAGE_SIZE; + tmp |= ((PAGE_SIZE << 10) - PAGE_SIZE); }} if (invalid) invalidate(); @@ -365,9 +366,9 @@ shm_sgn = shmd->shm_sgn; for (tmp = shmd->start; tmp < shmd->end; tmp += PAGE_SIZE, shm_sgn += (1 << SHM_IDX_SHIFT)) { - page_table = (ulong *) (page_dir + ((tmp >> 20) & 0xffc)); - page_table = (ulong *) (0xfffff000 & *page_table); - page_table += (tmp >> PAGE_SHIFT) & 0x3ff; + page_table = PAGE_DIR_OFFSET(page_dir,tmp); + page_table = (ulong *) (PAGE_MASK & *page_table); + page_table += (tmp >> PAGE_SHIFT) & PTRS_PER_PAGE-1; *page_table = shm_sgn; } return 0; @@ -393,9 +394,6 @@ if (shp == IPC_UNUSED || shp == IPC_NOID) return -EINVAL; -#define SHM_RANGE_END 0x60000000 -#define SHM_RANGE_START 0x40000000 - if (!(addr = (ulong) shmaddr)) { if (shmflg & SHM_REMAP) return -EINVAL; @@ -407,7 +405,7 @@ if (addr >= shmd->start) addr = shmd->start; } - addr = (addr - shp->shm_segsz) & ~0xfff; + addr = (addr - shp->shm_segsz) & PAGE_MASK; } else if (addr & (SHMLBA-1)) { if (shmflg & SHM_RND) addr &= ~(SHMLBA-1); /* round down */ @@ -425,7 +423,7 @@ return -EINVAL; } - if (ipcperms(&shp->shm_perm, shmflg & SHM_RDONLY ? 0444 : 0666)) + if (ipcperms(&shp->shm_perm, shmflg & SHM_RDONLY ? S_IRUGO : S_IRUGO|S_IWUGO)) return -EACCES; if (shp->shm_perm.seq != shmid / SHMMNI) return -EIDRM; @@ -556,7 +554,7 @@ } p2->shm = new_desc; for (shmd = new_desc; shmd; shmd = shmd->task_next) { - id = (shmd->shm_sgn >> SHM_ID_SHIFT) & 0xfff; + id = (shmd->shm_sgn >> SHM_ID_SHIFT) & SHM_ID_MASK; shp = shm_segs[id]; if (shp == IPC_UNUSED) { printk("shm_fork: unused id=%d PANIC\n", id); @@ -598,8 +596,7 @@ } if (!(shp->shm_pages[idx] & PAGE_PRESENT)) { - page = get_free_page(GFP_KERNEL); - if (!page) { + if(!(page = __get_free_page(GFP_KERNEL))) { oom(current); *ptent = BAD_PAGE | PAGE_ACCESSED | 7; return; @@ -692,7 +689,7 @@ printk ("shm_swap: too large idx=%d id=%d PANIC\n",idx, id); continue; } - pte = (ulong *) (shmd->task->tss.cr3 + ((tmp>>20) & 0xffc)); + pte = PAGE_DIR_OFFSET(shmd->task->tss.cr3,tmp); if (!(*pte & 1)) { printk("shm_swap: bad pgtbl! id=%d start=%x idx=%d\n", id, shmd->start, idx); @@ -699,8 +696,8 @@ *pte = 0; continue; } - pte = (ulong *) (0xfffff000 & *pte); - pte += ((tmp >> PAGE_SHIFT) & 0x3ff); + pte = (ulong *) (PAGE_MASK & *pte); + pte += ((tmp >> PAGE_SHIFT) & PTRS_PER_PAGE-1); tmp = *pte; if (!(tmp & PAGE_PRESENT)) continue; @@ -717,7 +714,7 @@ if (mem_map[MAP_NR(page)] != 1) goto check_table; - page &= ~0xfff; + page &= PAGE_MASK; shp->shm_pages[idx] = swap_nr; if (invalid) invalidate(); diff -u --recursive --new-file pl11/linux/ipc/util.c linux/ipc/util.c --- pl11/linux/ipc/util.c Sat Jul 3 01:10:07 1993 +++ linux/ipc/util.c Mon Aug 9 18:02:31 1993 @@ -10,6 +10,7 @@ #include #include #include +#include void ipc_init (void); extern "C" int sys_ipc (uint call, int first, int second, int third, void *ptr); @@ -45,16 +46,19 @@ */ int ipcperms (struct ipc_perm *ipcp, short flag) { - int i, perm = 0007, euid = current->euid, egid; + int i; mode_t perm; uid_t euid; int egid; if (suser()) return 0; + + perm = S_IRWXO; euid = current->euid; + if (euid == ipcp->cuid || euid == ipcp->uid) - perm = 0700; + perm = S_IRWXU; else { for (i = 0; (egid = current->groups[i]) != NOGROUP; i++) if ((egid == ipcp->cgid) || (egid == ipcp->gid)) { - perm = 0070; + perm = S_IRWXG; break; } } diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/README linux/kernel/FPU-emu/README --- pl11/linux/kernel/FPU-emu/README Sun May 23 14:48:34 1993 +++ linux/kernel/FPU-emu/README Thu Jul 22 01:11:49 1993 @@ -29,17 +29,16 @@ math emulator by Linus Torvalds. My target FPU for wm-FPU-emu is that described in the Intel486 -Programmer's Reference Manual (1992 edition). Numerous facets of the -functioning of the FPU are not well covered in the Reference Manual; -in the absence of clear details I have made guesses about the most -reasonable behaviour. Recently, this situation has improved because -I now have some access to the results produced by a real 80486 FPU. - -wm-FPU-emu does not implement all of the behaviour of the 80486 FPU. -See "Limitations" later in this file for a partial list of some -differences. I believe that the missing features are never used by -normal C or FORTRAN programs. +Programmer's Reference Manual (1992 edition). Unfortunately, numerous +facets of the functioning of the FPU are not well covered in the +Reference Manual. The information in the manual has been supplemented +with measurements on real 80486's. Unfortunately, it is simply not +possible to be sure that all of the peculiarities of the 80486 have +been discovered, so there is always likely to be obscure differences +in the detailed behaviour of the emulator and a real 80486. +wm-FPU-emu does not implement all of the behaviour of the 80486 FPU. +See "Limitations" later in this file for a list of some differences. Please report bugs, etc to me at: apm233m@vaxc.cc.monash.edu.au @@ -46,7 +45,7 @@ --Bill Metzenthen - May 1993 + July 1993 ----------------------- Internals of wm-FPU-emu ----------------------- @@ -66,6 +65,12 @@ (4) The trig, log, and exp functions are based in each case upon quasi- "optimal" polynomial approximations. My definition of "optimal" was based upon getting good accuracy with reasonable speed. +(5) The argument reducing code for the trig function effectively uses + a value of pi which is accurate to more than 128 bits. As a consequence, + the reduced argument is accurate to more than 64 bits for arguments up + to a few pi, and accurate to more than 64 bits for most arguments, + even for arguments approaching 2^63. This is far superior to an + 80486, which uses a value of pi which is accurate to 66 bits. The code of the emulator is complicated slightly by the need to account for a limited form of re-entrancy. Normally, the emulator will @@ -85,28 +90,54 @@ ----------------------- Limitations of wm-FPU-emu ----------------------- There are a number of differences between the current wm-FPU-emu -(version beta 1.4) and the 80486 FPU (apart from bugs). Some of the +(version beta 1.5) and the 80486 FPU (apart from bugs). Some of the more important differences are listed below: +Segment overrides don't do anything yet. + All internal computations are performed at 64 bit or higher precision -and rounded etc as required by the PC bits of the FPU control word. -Under the crt0 version for Linux current at March 1993, the FPU PC -bits specify 53 bits precision. +and the results rounded etc as required by the PC bits of the FPU +control word. Under the crt0 version for Linux current at June 1993, +the FPU PC bits specify 64 bits precision. The precision flag (PE of the FPU status word) and the Roundup flag -(C1 of the status word) are now partially implemented. Does anyone -write code which uses these features? - -The functions which load/store the FPU state are partially implemented, -but the implementation should be sufficient for handling FPU errors etc -in 32 bit protected mode. - -The implementation of the exception mechanism is flawed for unmasked -interrupts. +(C1 of the status word) are now implemented. Does anyone write code +which uses these features? The Roundup flag does not have much meaning +for the transcendental functions and its 80486 value with these +functions is likely to differ from its emulator value. + +In a few rare cases the Underflow flag obtained with the emulator will +be different from that obtained with an 80486. This occurs when the +following conditions apply simultaneously: +(a) the operands have a higher precision than the current setting of the + precision control (PC) flags. +(b) the underflow exception is masked. +(c) the magnitude of the exact result (before rounding) is less than 2^-16382. +(d) the magnitude of the final result (after rounding) is exactly 2^-16382. +(e) the magnitude of the exact result would be exactly 2^-16382 if the + operands were rounded to the current precision before the arithmetic + operation was performed. +If all of these apply, the emulator will set the Underflow flag but a real +80486 will not. + +NOTE: Certain formats of Extended Real are UNSUPPORTED. They are +unsupported by the 80486. They are the Pseudo-NaNs, Pseudoinfinities, +and Unnormals. None of these will be generated by an 80486 or by the +emulator. Do not use them. The emulator treats them differently in +detail from the way an 80486 does. + +The emulator treats PseudoDenormals differently from an 80486. These +numbers are in fact properly normalised numbers with the exponent +offset by 1, and the emulator treats them as such. Unlike the 80486, +the emulator does not generate a Denormal Operand exception for these +numbers. The arithmetical results produced when using such a number as +an operand are the same for the emulator and a real 80486 (apart from +any slight precision difference for the transcendental functions). +Neither the emulator nor an 80486 produces one of these numbers as the +result of any arithmetic operation. An 80486 can keep one of these +numbers in an FPU register with its identity as a PseudoDenormal, but +the emulator will not; they are always converted to a valid number. -Detection of certain conditions, such as denormal operands, is not yet -complete. - ----------------------- Performance of wm-FPU-emu ----------------------- Speed. @@ -186,7 +217,8 @@ in the last column. -Function Tested x range Worst result (bits) Turbo C +Function Tested x range Worst result Turbo C + (relative bits) sqrt(x) 1 .. 2 64.1 63.2 atan(x) 1e-10 .. 200 62.6 62.8 @@ -213,31 +245,64 @@ pass the 'paranoia' tests for 'double' variables when precision control is set to 64 bits). +For version 1.5, the accuracy of fprem and fprem1 has been improved. +These functions now produce exact results. The code for reducing the +argument for the trig functions (fsin, fcos, fptan and fsincos) has +been improved and now effectively uses a value for pi which is +accurate to more than 128 bits precision. As a consquence, the +accuracy of these functions for large arguments has been dramatically +improved (and is now very much better than an 80486 FPU). There is +also now no degradation of accuracy for fcos and ftan for operands +close to pi/2. Measured results are (note that the definition of +accuracy has changed slightly from that used for the above table): + +Function Tested x range Worst result + (absolute bits) + +cos(x) 0 .. 9.22e+18 62.0 +sin(x) 1e-16 .. 9.22e+18 62.1 +tan(x) 1e-16 .. 9.22e+18 61.8 + +It is possible with some effort to find very large arguments which +give much degraded precision. For example, the integer number + 8227740058411162616.0 +is within about 10e-7 of a multiple of pi. To find the tan (for +example) of this number to 64 bits precision it would be necessary to +have a value of pi which had about 150 bits precision. The FPU +emulator computes the result to about 42.6 bits precision (the correct +result is about -9.739715e-8). On the other hand, an 80486 FPU returns +0.01059, which in relative terms is hopelessly inaccurate. + +For arguments close to critical angles (which occur at multiples of +pi/2) the emulator is more accurate than an 80486 FPU. For very large +arguments, the emulator is far more accurate. + ------------------------- Contributors ------------------------------- A number of people have contributed to the development of the -emulator, often by just reporting bugs, sometimes with a suggested -fix, and a few kind people have provided me with access in one way or -another to an 80486 machine. Contributors include (to those people who -I have forgotten, please excuse me): +emulator, often by just reporting bugs, sometimes with suggested +fixes, and a few kind people have provided me with access in one way +or another to an 80486 machine. Contributors include (to those people +who I may have forgotten, please forgive me): Linus Torvalds Tommy.Thorn@daimi.aau.dk Andrew.Tridgell@anu.edu.au -Nick Holloway alfie@dcs.warwick.ac.uk -Hermano Moura moura@dcs.gla.ac.uk -Jon Jagger J.Jagger@scp.ac.uk +Nick Holloway, alfie@dcs.warwick.ac.uk +Hermano Moura, moura@dcs.gla.ac.uk +Jon Jagger, J.Jagger@scp.ac.uk Lennart Benschop -Brian Gallew geek+@CMU.EDU -Thomas Staniszewski ts3v+@andrew.cmu.edu -Martin Howell mph@plasma.apana.org.au -M Saggaf alsaggaf@athena.mit.edu -Peter Barker PETER@socpsy.sci.fau.edu +Brian Gallew, geek+@CMU.EDU +Thomas Staniszewski, ts3v+@andrew.cmu.edu +Martin Howell, mph@plasma.apana.org.au +M Saggaf, alsaggaf@athena.mit.edu +Peter Barker, PETER@socpsy.sci.fau.edu tom@vlsivie.tuwien.ac.at -Dan Russel russed@rpi.edu -Daniel Carosone danielce@ee.mu.oz.au +Dan Russel, russed@rpi.edu +Daniel Carosone, danielce@ee.mu.oz.au cae@jpmorgan.com -Hamish Coleman t933093@minyos.xx.rmit.oz.au +Hamish Coleman, t933093@minyos.xx.rmit.oz.au +Bruce Evans, bde@kralizec.zeta.org.au ...and numerous others who responded to my request for help with a real 80486. diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/errors.c linux/kernel/FPU-emu/errors.c --- pl11/linux/kernel/FPU-emu/errors.c Sat Jul 3 01:54:19 1993 +++ linux/kernel/FPU-emu/errors.c Thu Jul 22 01:11:49 1993 @@ -68,26 +68,24 @@ RE_ENTRANT_CHECK_OFF byte1 = get_fs_byte((unsigned char *) FPU_ORIG_EIP); FPU_modrm = get_fs_byte(1 + (unsigned char *) FPU_ORIG_EIP); + partial_status = status_word(); #ifdef DEBUGGING -if ( status_word & SW_Backward ) printk("SW: backward compatibility\n"); -if ( status_word & SW_C3 ) printk("SW: condition bit 3\n"); -if ( status_word & SW_C2 ) printk("SW: condition bit 2\n"); -if ( status_word & SW_C1 ) printk("SW: condition bit 1\n"); -if ( status_word & SW_C0 ) printk("SW: condition bit 0\n"); -if ( status_word & SW_Summary ) printk("SW: exception summary\n"); -if ( status_word & SW_Stack_Fault ) printk("SW: stack fault\n"); -if ( status_word & SW_Precision ) printk("SW: loss of precision\n"); -if ( status_word & SW_Underflow ) printk("SW: underflow\n"); -if ( status_word & SW_Overflow ) printk("SW: overflow\n"); -if ( status_word & SW_Zero_Div ) printk("SW: divide by zero\n"); -if ( status_word & SW_Denorm_Op ) printk("SW: denormalized operand\n"); -if ( status_word & SW_Invalid ) printk("SW: invalid operation\n"); +if ( partial_status & SW_Backward ) printk("SW: backward compatibility\n"); +if ( partial_status & SW_C3 ) printk("SW: condition bit 3\n"); +if ( partial_status & SW_C2 ) printk("SW: condition bit 2\n"); +if ( partial_status & SW_C1 ) printk("SW: condition bit 1\n"); +if ( partial_status & SW_C0 ) printk("SW: condition bit 0\n"); +if ( partial_status & SW_Summary ) printk("SW: exception summary\n"); +if ( partial_status & SW_Stack_Fault ) printk("SW: stack fault\n"); +if ( partial_status & SW_Precision ) printk("SW: loss of precision\n"); +if ( partial_status & SW_Underflow ) printk("SW: underflow\n"); +if ( partial_status & SW_Overflow ) printk("SW: overflow\n"); +if ( partial_status & SW_Zero_Div ) printk("SW: divide by zero\n"); +if ( partial_status & SW_Denorm_Op ) printk("SW: denormalized operand\n"); +if ( partial_status & SW_Invalid ) printk("SW: invalid operation\n"); #endif DEBUGGING - status_word = status_word & ~SW_Top; - status_word |= (top&7) << SW_Top_Shift; - printk("At %p: %02x ", FPU_ORIG_EIP, byte1); if (FPU_modrm >= 0300) printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); @@ -96,15 +94,15 @@ (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7); printk(" SW: b=%d st=%d es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n", - status_word & 0x8000 ? 1 : 0, /* busy */ - (status_word & 0x3800) >> 11, /* stack top pointer */ - status_word & 0x80 ? 1 : 0, /* Error summary status */ - status_word & 0x40 ? 1 : 0, /* Stack flag */ - status_word & SW_C3?1:0, status_word & SW_C2?1:0, /* cc */ - status_word & SW_C1?1:0, status_word & SW_C0?1:0, /* cc */ - status_word & SW_Precision?1:0, status_word & SW_Underflow?1:0, - status_word & SW_Overflow?1:0, status_word & SW_Zero_Div?1:0, - status_word & SW_Denorm_Op?1:0, status_word & SW_Invalid?1:0); + partial_status & 0x8000 ? 1 : 0, /* busy */ + (partial_status & 0x3800) >> 11, /* stack top pointer */ + partial_status & 0x80 ? 1 : 0, /* Error summary status */ + partial_status & 0x40 ? 1 : 0, /* Stack flag */ + partial_status & SW_C3?1:0, partial_status & SW_C2?1:0, /* cc */ + partial_status & SW_C1?1:0, partial_status & SW_C0?1:0, /* cc */ + partial_status & SW_Precision?1:0, partial_status & SW_Underflow?1:0, + partial_status & SW_Overflow?1:0, partial_status & SW_Zero_Div?1:0, + partial_status & SW_Denorm_Op?1:0, partial_status & SW_Invalid?1:0); printk(" CW: ic=%d rc=%d%d pc=%d%d iem=%d ef=%d%d%d%d%d%d\n", control_word & 0x1000 ? 1 : 0, @@ -124,12 +122,14 @@ continue; break; case TW_Zero: +#if 0 printk("st(%d) %c .0000 0000 0000 0000 ", i, r->sign ? '-' : '+'); break; +#endif case TW_Valid: case TW_NaN: - case TW_Denormal: +/* case TW_Denormal: */ case TW_Infinity: printk("st(%d) %c .%04x %04x %04x %04x e%+-6d ", i, r->sign ? '-' : '+', @@ -179,21 +179,22 @@ error was detected. Internal error types: - 0x14 in e14.c + 0 in load_store.c + 0x14 in fpu_etc.c 0x1nn in a *.c file: 0x101 in reg_add_sub.c 0x102 in reg_mul.c 0x103 in poly_sin.c - 0x104 in poly_tan.c + 0x104 in poly_atan.c 0x105 in reg_mul.c - 0x106 in reg_mov.c + 0x106 in reg_ld_str.c 0x107 in fpu_trig.c 0x108 in reg_compare.c 0x109 in reg_compare.c 0x110 in reg_add_sub.c - 0x111 in interface.c + 0x111 in fpe_entry.c 0x112 in fpu_trig.c - 0x113 in reg_add_sub.c + 0x113 in errors.c 0x114 in reg_ld_str.c 0x115 in fpu_trig.c 0x116 in fpu_trig.c @@ -204,8 +205,11 @@ 0x121 in reg_compare.c 0x122 in reg_compare.c 0x123 in reg_compare.c + 0x125 in fpu_trig.c + 0x126 in fpu_entry.c + 0x127 in poly_2xm1.c 0x2nn in an *.s file: - 0x201 in reg_u_add.S + 0x201 in reg_u_add.S, reg_round.S 0x202 in reg_u_div.S 0x203 in reg_u_div.S 0x204 in reg_u_div.S @@ -225,7 +229,7 @@ 0x218 in reg_round.S */ -extern "C" void exception(int n) +void exception(int n) { int i, int_type; @@ -235,7 +239,7 @@ int_type = n - EX_INTERNAL; n = EX_INTERNAL; /* Set lots of exception bits! */ - status_word |= (SW_Exc_Mask | SW_Summary | FPU_BUSY); + partial_status |= (SW_Exc_Mask | SW_Summary | SW_Backward); } else { @@ -242,15 +246,16 @@ /* Extract only the bits which we use to set the status word */ n &= (SW_Exc_Mask); /* Set the corresponding exception bit */ - status_word |= n; - if ( status_word & ~control_word & CW_Exceptions ) - status_word |= SW_Summary; + partial_status |= n; + /* Set summary bits iff exception isn't masked */ + if ( partial_status & ~control_word & CW_Exceptions ) + partial_status |= (SW_Summary | SW_Backward); if ( n & (SW_Stack_Fault | EX_Precision) ) { if ( !(n & SW_C1) ) /* This bit distinguishes over- from underflow for a stack fault, and roundup from round-down for precision loss. */ - status_word &= ~SW_C1; + partial_status &= ~SW_C1; } } @@ -305,12 +310,15 @@ } -/* Real operation attempted on two operands, one a NaN */ -extern "C" void real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest) +/* Real operation attempted on two operands, one a NaN. */ +/* Returns nz if the exception is unmasked */ +extern "C" int real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest) { FPU_REG *x; int signalling; + /* The default result for the case of two "equal" NaNs (signs may + differ) is chosen to reproduce 80486 behaviour */ x = a; if (a->tag == TW_NaN) { @@ -349,7 +357,7 @@ if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */ x = &CONST_QNaN; reg_move(x, dest); - return; + return 0; } if ( control_word & CW_Invalid ) @@ -364,12 +372,16 @@ EXCEPTION(EX_Invalid); - return; + return !(control_word & CW_Invalid); } + /* Invalid arith operation on Valid registers */ -extern "C" void arith_invalid(FPU_REG *dest) +/* Returns nz if the exception is unmasked */ +extern "C" int arith_invalid(FPU_REG *dest) { + + EXCEPTION(EX_Invalid); if ( control_word & CW_Invalid ) { @@ -376,16 +388,14 @@ /* The masked response */ reg_move(&CONST_QNaN, dest); } - - EXCEPTION(EX_Invalid); - return; + return !(control_word & CW_Invalid); } /* Divide a finite number by zero */ -extern "C" void divide_by_zero(int sign, FPU_REG *dest) +extern "C" int divide_by_zero(int sign, FPU_REG *dest) { if ( control_word & CW_ZeroDiv ) @@ -397,8 +407,25 @@ EXCEPTION(EX_ZeroDiv); - return; + return !(control_word & CW_ZeroDiv); + +} + +/* This may be called often, so keep it lean */ +int set_precision_flag(int flags) +{ + if ( control_word & CW_Precision ) + { + partial_status &= ~(SW_C1 & flags); + partial_status |= flags; /* The masked response */ + return 0; + } + else + { + exception(flags); + return 1; + } } @@ -406,7 +433,7 @@ extern "C" void set_precision_flag_up(void) { if ( control_word & CW_Precision ) - status_word |= (SW_Precision | SW_C1); /* The masked response */ + partial_status |= (SW_Precision | SW_C1); /* The masked response */ else exception(EX_Precision | SW_C1); @@ -418,8 +445,8 @@ { if ( control_word & CW_Precision ) { /* The masked response */ - status_word &= ~SW_C1; - status_word |= SW_Precision; + partial_status &= ~SW_C1; + partial_status |= SW_Precision; } else exception(EX_Precision); @@ -430,7 +457,7 @@ { if ( control_word & CW_Denormal ) { /* The masked response */ - status_word |= SW_Denorm_Op; + partial_status |= SW_Denorm_Op; return 0; } else @@ -441,7 +468,7 @@ } -extern "C" void arith_overflow(FPU_REG *dest) +extern "C" int arith_overflow(FPU_REG *dest) { if ( control_word & CW_Overflow ) @@ -448,7 +475,7 @@ { char sign; /* The masked response */ -/* **** The response here depends upon the rounding mode */ +/* ###### The response here depends upon the rounding mode */ sign = dest->sign; reg_move(&CONST_INF, dest); dest->sign = sign; @@ -459,16 +486,23 @@ dest->exp -= (3 * (1 << 13)); } - /* By definition, precision is lost. - It appears that the roundup bit (C1) is also set by convention. */ - EXCEPTION(EX_Overflow | EX_Precision | SW_C1); + EXCEPTION(EX_Overflow); + if ( control_word & CW_Overflow ) + { + /* The overflow exception is masked. */ + /* By definition, precision is lost. + The roundup bit (C1) is also set because we have + "rounded" upwards to Infinity. */ + EXCEPTION(EX_Precision | SW_C1); + return !(control_word & CW_Precision); + } - return; + return !(control_word & CW_Overflow); } -extern "C" void arith_underflow(FPU_REG *dest) +extern "C" int arith_underflow(FPU_REG *dest) { if ( control_word & CW_Underflow ) @@ -475,17 +509,27 @@ { /* The masked response */ if ( dest->exp <= EXP_UNDER - 63 ) - reg_move(&CONST_Z, dest); + { + reg_move(&CONST_Z, dest); + partial_status &= ~SW_C1; /* Round down. */ + } } else { - /* Add the magic number to the exponent */ + /* Add the magic number to the exponent. */ dest->exp += (3 * (1 << 13)); } EXCEPTION(EX_Underflow); + if ( control_word & CW_Underflow ) + { + /* The underflow exception is masked. */ + EXCEPTION(EX_Precision); + return !(control_word & CW_Precision); + } + + return !(control_word & CW_Underflow); - return; } diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/exception.h linux/kernel/FPU-emu/exception.h --- pl11/linux/kernel/FPU-emu/exception.h Tue Nov 10 12:54:54 1992 +++ linux/kernel/FPU-emu/exception.h Thu Jul 22 01:11:50 1993 @@ -35,6 +35,10 @@ #define EX_Invalid Const_(0x0001) /* invalid operation */ +#define PRECISION_LOST_UP Const_((EX_Precision | SW_C1)) +#define PRECISION_LOST_DOWN Const_(EX_Precision) + + #ifndef __ASSEMBLER__ #ifdef DEBUG diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/fpu_arith.c linux/kernel/FPU-emu/fpu_arith.c --- pl11/linux/kernel/FPU-emu/fpu_arith.c Fri Apr 23 16:48:12 1993 +++ linux/kernel/FPU-emu/fpu_arith.c Thu Jul 22 01:11:50 1993 @@ -13,11 +13,16 @@ #include "fpu_system.h" #include "fpu_emu.h" #include "control_w.h" +#include "status_w.h" void fadd__() { /* fadd st,st(i) */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_add(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); } @@ -25,6 +30,10 @@ void fmul__() { /* fmul st,st(i) */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_mul(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); } @@ -33,6 +42,10 @@ void fsub__() { /* fsub st,st(i) */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_sub(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); } @@ -40,6 +53,10 @@ void fsubr_() { /* fsubr st,st(i) */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_sub(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word); } @@ -47,6 +64,10 @@ void fdiv__() { /* fdiv st,st(i) */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_div(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); } @@ -54,6 +75,10 @@ void fdivr_() { /* fdivr st,st(i) */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_div(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word); } @@ -62,6 +87,10 @@ void fadd_i() { /* fadd st(i),st */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); } @@ -69,7 +98,11 @@ void fmul_i() { /* fmul st(i),st */ - reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 + reg_mul(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); } @@ -78,6 +111,10 @@ /* fsubr st(i),st */ /* This is the sense of the 80486 manual reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); } @@ -87,6 +124,10 @@ /* fsub st(i),st */ /* This is the sense of the 80486 manual reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); } @@ -94,6 +135,10 @@ void fdivri() { /* fdivr st(i),st */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); } @@ -101,6 +146,10 @@ void fdiv_i() { /* fdiv st(i),st */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); } @@ -109,8 +158,12 @@ void faddp_() { /* faddp st(i),st */ - reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); - pop(); +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 + if ( !reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) ) + pop(); } @@ -117,8 +170,12 @@ void fmulp_() { /* fmulp st(i),st */ - reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); - pop(); +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 + if ( !reg_mul(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) ) + pop(); } @@ -128,8 +185,12 @@ /* fsubrp st(i),st */ /* This is the sense of the 80486 manual reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); */ - reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); - pop(); +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 + if ( !reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) ) + pop(); } @@ -138,8 +199,12 @@ /* fsubp st(i),st */ /* This is the sense of the 80486 manual reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); */ - reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); - pop(); +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 + if ( !reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word) ) + pop(); } @@ -146,8 +211,12 @@ void fdivrp() { /* fdivrp st(i),st */ - reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); - pop(); +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 + if ( !reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) ) + pop(); } @@ -154,7 +223,11 @@ void fdivp_() { /* fdivp st(i),st */ - reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); - pop(); +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 + if ( !reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word) ) + pop(); } diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/fpu_aux.c linux/kernel/FPU-emu/fpu_aux.c --- pl11/linux/kernel/FPU-emu/fpu_aux.c Tue Jun 1 21:59:58 1993 +++ linux/kernel/FPU-emu/fpu_aux.c Thu Jul 22 01:11:50 1993 @@ -14,14 +14,16 @@ #include "exception.h" #include "fpu_emu.h" #include "status_w.h" +#include "control_w.h" void fclex(void) { - status_word &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision| + partial_status &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision| SW_Underflow|SW_Overflow|SW_Zero_Div|SW_Denorm_Op| SW_Invalid); + NO_NET_DATA_EFFECT; FPU_entry_eip = ip_offset; /* We want no net effect */ } @@ -30,12 +32,18 @@ { int r; control_word = 0x037f; - status_word = 0; + partial_status = 0; top = 0; /* We don't keep top in the status word internally. */ for (r = 0; r < 8; r++) { regs[r].tag = TW_Empty; } + /* The behaviour is different to that detailed in + Section 15.1.6 of the Intel manual */ + data_operand_offset = 0; + operand_selector = 0; + NO_NET_DATA_EFFECT; + FPU_entry_op_cs = 0; FPU_entry_eip = ip_offset = 0; } @@ -51,12 +59,8 @@ static void fstsw_ax(void) { - - status_word &= ~SW_Top; - status_word |= (top&7) << SW_Top_Shift; - - *(short *) &FPU_EAX = status_word; - + *(short *) &FPU_EAX = status_word(); + NO_NET_INSTR_EFFECT; } static FUNC fstsw_table[] = { @@ -123,16 +127,22 @@ stack_underflow_i(FPU_rm); return; } - reg_move(sti_ptr, FPU_st0_ptr); + if ( control_word & CW_Invalid ) + reg_move(sti_ptr, FPU_st0_ptr); /* Masked response */ stack_underflow_i(FPU_rm); return; } if ( sti_ptr->tag == TW_Empty ) { - reg_move(FPU_st0_ptr, sti_ptr); + if ( control_word & CW_Invalid ) + reg_move(FPU_st0_ptr, sti_ptr); /* Masked response */ stack_underflow(); return; } +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_move(FPU_st0_ptr, &t); reg_move(sti_ptr, FPU_st0_ptr); reg_move(&t, sti_ptr); diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/fpu_emu.h linux/kernel/FPU-emu/fpu_emu.h --- pl11/linux/kernel/FPU-emu/fpu_emu.h Sat Jul 3 01:38:56 1993 +++ linux/kernel/FPU-emu/fpu_emu.h Thu Jul 22 01:11:50 1993 @@ -38,6 +38,8 @@ #define EXP_BIAS Const(0) #define EXP_OVER Const(0x4000) /* smallest invalid large exponent */ #define EXP_UNDER Const(-0x3fff) /* largest invalid small exponent */ +#define EXP_Infinity EXP_OVER +#define EXP_NaN EXP_OVER #define SIGN_POS Const(0) #define SIGN_NEG Const(1) @@ -46,15 +48,13 @@ #define TW_Valid Const(0) /* valid */ #define TW_Zero Const(1) /* zero */ /* The following fold to 2 (Special) in the Tag Word */ -#define TW_Denormal Const(4) /* De-normal */ +/* #define TW_Denormal Const(4) */ /* De-normal */ #define TW_Infinity Const(5) /* + or - infinity */ #define TW_NaN Const(6) /* Not a Number */ #define TW_Empty Const(7) /* empty */ -/* #define TW_FPU_Interrupt Const(0x80) */ /* Signals an interrupt */ - #ifndef __ASSEMBLER__ #include @@ -68,6 +68,16 @@ # define RE_ENTRANT_CHECK_ON #endif PARANOID +/* These are to defeat the default action, giving the instruction + no net effect: */ +#define NO_NET_DATA_EFFECT \ + { FPU_data_address = (void *)data_operand_offset; \ + FPU_data_selector = operand_selector; } +#define NO_NET_INSTR_EFFECT \ + { FPU_entry_eip = ip_offset; \ + FPU_entry_op_cs = cs_selector; } + + typedef void (*FUNC)(void); typedef struct fpu_reg FPU_REG; @@ -82,7 +92,10 @@ extern char FPU_st0_tag; extern FPU_REG *FPU_st0_ptr; -extern void *FPU_data_address; +/* ###### These need to be shifted to somewhere safe. */ +/* extern void *FPU_data_address; has been shifted */ +extern unsigned short FPU_data_selector; +extern unsigned long FPU_entry_op_cs; extern FPU_REG FPU_loaded_data; @@ -99,7 +112,7 @@ /*----- Prototypes for functions written in assembler -----*/ -/* extern "C" void reg_move(FPU_REG *a, FPU_REG *b); */ +/* extern void reg_move(FPU_REG *a, FPU_REG *b); */ extern "C" void mul64(long long *a, long long *b, long long *result); extern "C" void poly_div2(long long *x); @@ -109,17 +122,17 @@ unsigned short terms[][4], int n); extern "C" void normalize(FPU_REG *x); extern "C" void normalize_nuo(FPU_REG *x); -extern "C" void reg_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, +extern "C" int reg_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, unsigned int control_w); -extern "C" void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, +extern "C" int reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, unsigned int control_w); -extern "C" void reg_u_mul(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, +extern "C" int reg_u_mul(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, unsigned int control_w); -extern "C" void reg_u_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, +extern "C" int reg_u_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, unsigned int control_w); -extern "C" void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, +extern "C" int reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, unsigned int control_w); -extern "C" void wm_sqrt(FPU_REG *n, unsigned int control_w); +extern "C" int wm_sqrt(FPU_REG *n, unsigned int control_w); extern "C" unsigned shrx(void *l, unsigned x); extern "C" unsigned shrxs(void *v, unsigned x); extern "C" unsigned long div_small(unsigned long long *x, unsigned long y); diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/fpu_entry.c linux/kernel/FPU-emu/fpu_entry.c --- pl11/linux/kernel/FPU-emu/fpu_entry.c Sat Jul 3 00:57:11 1993 +++ linux/kernel/FPU-emu/fpu_entry.c Thu Jul 22 01:11:50 1993 @@ -38,6 +38,15 @@ #include +#define FWAIT_OPCODE 0x9b +#define OP_SIZE_PREFIX 0x66 +#define ADDR_SIZE_PREFIX 0x67 +#define PREFIX_CS 0x2e +#define PREFIX_DS 0x3e +#define PREFIX_ES 0x26 +#define PREFIX_SS 0x36 +#define PREFIX_FS 0x64 +#define PREFIX_GS 0x65 #define __BAD__ Un_impl /* Not implemented */ @@ -133,11 +142,17 @@ char FPU_st0_tag; FPU_REG *FPU_st0_ptr; +/* ######## To be shifted */ +unsigned long FPU_entry_op_cs; +unsigned short FPU_data_selector; + + #ifdef PARANOID char emulating=0; #endif PARANOID #define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x)) +static int valid_prefix(unsigned char byte); extern "C" void math_emulate(long arg) @@ -144,6 +159,7 @@ { unsigned char FPU_modrm; unsigned short code; + int unmasked; #ifdef PARANOID if ( emulating ) @@ -150,11 +166,19 @@ { printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n"); } - RE_ENTRANT_CHECK_ON + RE_ENTRANT_CHECK_ON; #endif PARANOID if (!current->used_math) { + int i; + for ( i = 0; i < 8; i++ ) + { + /* Make sure that the registers are compatible + with the assumptions of the emulator. */ + regs[i].exp = 0; + regs[i].sigh = 0x80000000; + } finit(); current->used_math = 1; } @@ -181,22 +205,44 @@ do_another_FPU_instruction: - RE_ENTRANT_CHECK_OFF + RE_ENTRANT_CHECK_OFF; code = get_fs_word((unsigned short *) FPU_EIP); - RE_ENTRANT_CHECK_ON + RE_ENTRANT_CHECK_ON; + +#ifdef PECULIAR_486 + /* It would be more logical to do this only in get_address(), + but although it is supposed to be undefined for many fpu + instructions, an 80486 behaves as if this were done here: */ + FPU_data_selector = FPU_DS; +#endif PECULIAR_486 - if ( (code & 0xff) == 0x9b ) /* fwait */ + if ( (code & 0xf8) != 0xd8 ) { - if (status_word & SW_Summary) - goto do_the_FPU_interrupt; - else + if ( (code & 0xff) == FWAIT_OPCODE ) { - FPU_EIP++; - goto FPU_instruction_done; + if (partial_status & SW_Summary) + goto do_the_FPU_interrupt; + else + { + FPU_EIP++; + goto FPU_fwait_done; + } + } + else if ( valid_prefix(code & 0xff) ) + { + goto do_another_FPU_instruction; } +#ifdef PARANOID + RE_ENTRANT_CHECK_OFF; + printk("FPU emulator: Unknown prefix byte 0x%02x\n", code & 0xff); + RE_ENTRANT_CHECK_ON; + EXCEPTION(EX_INTERNAL|0x126); + FPU_EIP++; + goto do_the_FPU_interrupt; +#endif PARANOID } - if (status_word & SW_Summary) + if (partial_status & SW_Summary) { /* Ignore the error for now if the current instruction is a no-wait control instruction */ @@ -210,9 +256,6 @@ fnstsw */ ((code & 0xc000) != 0xc000))) ) ) { - /* This is a guess about what a real FPU might do to this bit: */ -/* status_word &= ~SW_Summary; ****/ - /* * We need to simulate the action of the kernel to FPU * interrupts here. @@ -223,9 +266,9 @@ */ do_the_FPU_interrupt: cs_selector &= 0xffff0000; - cs_selector |= (status_word & ~SW_Top) | ((top&7) << SW_Top_Shift); + cs_selector |= status_word(); operand_selector = tag_word(); - status_word = 0; + partial_status = 0; top = 0; { int r; @@ -235,7 +278,7 @@ } } - RE_ENTRANT_CHECK_OFF + RE_ENTRANT_CHECK_OFF; send_sig(SIGFPE, current, 1); return; } @@ -243,12 +286,18 @@ FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP; - if ( (code & 0xff) == 0x66 ) /* size prefix */ + { + unsigned short swapped_code = code; + bswapw(swapped_code); + FPU_entry_op_cs = (swapped_code << 16) | (FPU_CS & 0xffff) ; + } + + if ( (code & 0xff) == OP_SIZE_PREFIX ) { FPU_EIP++; - RE_ENTRANT_CHECK_OFF + RE_ENTRANT_CHECK_OFF; code = get_fs_word((unsigned short *) FPU_EIP); - RE_ENTRANT_CHECK_ON + RE_ENTRANT_CHECK_ON; } FPU_EIP += 2; @@ -261,7 +310,7 @@ get_address(FPU_modrm); if ( !(code & 1) ) { - unsigned short status1 = status_word; + unsigned short status1 = partial_status; FPU_st0_ptr = &st(0); FPU_st0_tag = FPU_st0_ptr->tag; @@ -268,16 +317,17 @@ /* Stack underflow has priority */ if ( NOT_EMPTY_0 ) { + unmasked = 0; /* Do this here to stop compiler warnings. */ switch ( (code >> 1) & 3 ) { case 0: - reg_load_single(); + unmasked = reg_load_single(); break; case 1: reg_load_int32(); break; case 2: - reg_load_double(); + unmasked = reg_load_double(); break; case 3: reg_load_int16(); @@ -297,17 +347,54 @@ { /* Restore the status word; we might have loaded a denormal. */ - status_word = status1; + partial_status = status1; if ( (FPU_modrm & 0x30) == 0x10 ) { /* fcom or fcomp */ EXCEPTION(EX_Invalid); setcc(SW_C3 | SW_C2 | SW_C0); - if ( FPU_modrm & 0x08 ) - pop(); /* fcomp, so we pop. */ + if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) ) + pop(); /* fcomp, masked, so we pop. */ } else - real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr); + { +#ifdef PECULIAR_486 + /* This is not really needed, but gives behaviour + identical to an 80486 */ + if ( (FPU_modrm & 0x28) == 0x20 ) + /* fdiv or fsub */ + real_2op_NaN(&FPU_loaded_data, FPU_st0_ptr, + FPU_st0_ptr); + else +#endif PECULIAR_486 + /* fadd, fdivr, fmul, or fsubr */ + real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data, + FPU_st0_ptr); + } + goto reg_mem_instr_done; + } + + if ( unmasked && !((FPU_modrm & 0x30) == 0x10) ) + { + /* Is not a comparison instruction. */ + if ( (FPU_modrm & 0x38) == 0x38 ) + { + /* fdivr */ + if ( (FPU_st0_tag == TW_Zero) && + (FPU_loaded_data.tag == TW_Valid) ) + { + if ( divide_by_zero(FPU_loaded_data.sign, + FPU_st0_ptr) ) + { + /* We use the fact here that the unmasked + exception in the loaded data was for a + denormal operand */ + /* Restore the state of the denormal op bit */ + partial_status &= ~SW_Denorm_Op; + partial_status |= status1 & SW_Denorm_Op; + } + } + } goto reg_mem_instr_done; } @@ -314,10 +401,20 @@ switch ( (FPU_modrm >> 3) & 7 ) { case 0: /* fadd */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, + but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word); break; case 1: /* fmul */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, + but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word); break; @@ -325,24 +422,44 @@ compare_st_data(); break; case 3: /* fcomp */ - compare_st_data(); - pop(); + if ( !compare_st_data() && !unmasked ) + pop(); break; case 4: /* fsub */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, + but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word); break; case 5: /* fsubr */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, + but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, control_word); break; case 6: /* fdiv */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, + but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word); break; case 7: /* fdivr */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, + but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 if ( FPU_st0_tag == TW_Zero ) - status_word = status1; /* Undo any denorm tag, + partial_status = status1; /* Undo any denorm tag, zero-divide has priority. */ reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, control_word); @@ -356,8 +473,8 @@ /* The instruction is fcom or fcomp */ EXCEPTION(EX_StackUnder); setcc(SW_C3 | SW_C2 | SW_C0); - if ( FPU_modrm & 0x08 ) - pop(); /* fcomp, Empty or not, we pop. */ + if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) ) + pop(); /* fcomp */ } else stack_underflow(); @@ -370,7 +487,10 @@ reg_mem_instr_done: - data_operand_offset = (unsigned long)FPU_data_address; +#ifndef PECULIAR_486 + *(unsigned short *)&operand_selector = FPU_data_selector; +#endif PECULIAR_486 + ; } else { @@ -377,6 +497,12 @@ /* None of these instructions access user memory */ unsigned char instr_index = (FPU_modrm & 0x38) | (code & 7); +#ifdef PECULIAR_486 + /* This is supposed to be undefined, but a real 80486 seems + to do this: */ + FPU_data_address = 0; +#endif PECULIAR_486 + FPU_st0_ptr = &st(0); FPU_st0_tag = FPU_st0_ptr->tag; switch ( type_table[(int) instr_index] ) @@ -400,8 +526,7 @@ case _REGIp: if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) ) { - stack_underflow_i(FPU_rm); - pop(); + stack_underflow_pop(FPU_rm); goto FPU_instruction_done; } break; @@ -427,13 +552,18 @@ FPU_instruction_done: ip_offset = FPU_entry_eip; - bswapw(code); - *(1 + (unsigned short *)&cs_selector) = code & 0x7ff; + cs_selector = FPU_entry_op_cs; + data_operand_offset = (unsigned long)FPU_data_address; +#ifdef PECULIAR_486 + *(unsigned short *)&operand_selector = FPU_data_selector; +#endif PECULIAR_486 + +FPU_fwait_done: #ifdef DEBUG - RE_ENTRANT_CHECK_OFF + RE_ENTRANT_CHECK_OFF; emu_printall(); - RE_ENTRANT_CHECK_ON + RE_ENTRANT_CHECK_ON; #endif DEBUG if (FPU_lookahead && !need_resched) @@ -440,32 +570,54 @@ { unsigned char next; - /* (This test should generate no machine code) */ - while ( 1 ) + RE_ENTRANT_CHECK_OFF; + next = get_fs_byte((unsigned char *) FPU_EIP); + RE_ENTRANT_CHECK_ON; + if ( valid_prefix(next) ) + goto do_another_FPU_instruction; + } + + RE_ENTRANT_CHECK_OFF; +} + + +/* This function is not yet complete. To properly handle all prefix + bytes, it will be necessary to change all emulator code which + accesses user address space. Access to separate segments is + important for msdos emulation. */ +static int valid_prefix(unsigned char byte) +{ + unsigned long ip = FPU_EIP; + + while ( 1 ) + { + switch ( byte ) { - RE_ENTRANT_CHECK_OFF - next = get_fs_byte((unsigned char *) FPU_EIP); - RE_ENTRANT_CHECK_ON - if ( ((next & 0xf8) == 0xd8) || (next == 0x9b) ) /* fwait */ - { - goto do_another_FPU_instruction; - } - else if ( next == 0x66 ) /* size prefix */ + case ADDR_SIZE_PREFIX: + case PREFIX_DS: /* Redundant */ + case PREFIX_CS: + case PREFIX_ES: + case PREFIX_SS: + case PREFIX_FS: + case PREFIX_GS: + + case OP_SIZE_PREFIX: /* Used often by gcc, but has no effect. */ + RE_ENTRANT_CHECK_OFF; + byte = get_fs_byte((unsigned char *) (++FPU_EIP)); + RE_ENTRANT_CHECK_ON; + break; + case FWAIT_OPCODE: + return 1; + default: + if ( (byte & 0xf8) == 0xd8 ) + return 1; + else { - RE_ENTRANT_CHECK_OFF - next = get_fs_byte((unsigned char *) (FPU_EIP+1)); - RE_ENTRANT_CHECK_ON - if ( (next & 0xf8) == 0xd8 ) - { - FPU_EIP++; - goto do_another_FPU_instruction; - } + FPU_EIP = ip; + return 0; } - break; } } - - RE_ENTRANT_CHECK_OFF } @@ -473,7 +625,7 @@ { FPU_EIP = FPU_ORIG_EIP; send_sig(signal,current,1); - RE_ENTRANT_CHECK_OFF + RE_ENTRANT_CHECK_OFF; __asm__("movl %0,%%esp ; ret": :"g" (((long) info)-4)); #ifdef PARANOID printk("ERROR: wm-FPU-emu math_abort failed!\n"); @@ -487,7 +639,7 @@ extern "C" void math_emulate(long arg) { - printk("math-meulation not enabled and no coprocessor found.\n"); + printk("math-emulation not enabled and no coprocessor found.\n"); printk("killing %s.\n",current->comm); send_sig(SIGFPE,current,1); schedule(); diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/fpu_etc.c linux/kernel/FPU-emu/fpu_etc.c --- pl11/linux/kernel/FPU-emu/fpu_etc.c Sun May 23 14:48:35 1993 +++ linux/kernel/FPU-emu/fpu_etc.c Thu Jul 22 01:11:50 1993 @@ -22,7 +22,10 @@ if ( NOT_EMPTY_0 ) { FPU_st0_ptr->sign ^= SIGN_POS^SIGN_NEG; - status_word &= ~SW_C1; +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 } else stack_underflow(); @@ -33,7 +36,10 @@ if ( FPU_st0_tag ^ TW_Empty ) { FPU_st0_ptr->sign = SIGN_POS; - status_word &= ~SW_C1; +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 } else stack_underflow(); @@ -48,16 +54,23 @@ setcc(SW_C3); break; case TW_Valid: + if (FPU_st0_ptr->sign == SIGN_POS) + setcc(0); + else + setcc(SW_C0); #ifdef DENORM_OPERAND if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; + { +#ifdef PECULIAR_486 + /* This is wierd! */ + if (FPU_st0_ptr->sign == SIGN_POS) + setcc(SW_C3); +#endif PECULIAR_486 + return; + } #endif DENORM_OPERAND - if (FPU_st0_ptr->sign == SIGN_POS) - setcc(0); - else - setcc(SW_C0); break; case TW_NaN: setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */ @@ -68,7 +81,6 @@ setcc(0); else setcc(SW_C0); - EXCEPTION(EX_Invalid); break; case TW_Empty: setcc(SW_C0|SW_C2|SW_C3); @@ -97,7 +109,7 @@ if ( FPU_st0_ptr->exp <= EXP_UNDER ) c = SW_C2|SW_C3; /* Denormal */ else - c = SW_C3; + c = SW_C2; break; case TW_NaN: c = SW_C0; diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/fpu_proto.h linux/kernel/FPU-emu/fpu_proto.h --- pl11/linux/kernel/FPU-emu/fpu_proto.h Sat Jul 3 01:55:17 1993 +++ linux/kernel/FPU-emu/fpu_proto.h Thu Jul 22 01:11:50 1993 @@ -5,15 +5,16 @@ extern void stack_underflow(void); extern void stack_underflow_i(int i); extern void stack_underflow_pop(int i); +extern int set_precision_flag(int flags); extern "C" void exception(int n); -extern "C" void real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest); -extern "C" void arith_invalid(FPU_REG *dest); -extern "C" void divide_by_zero(int sign, FPU_REG *dest); +extern "C" int real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest); +extern "C" int arith_invalid(FPU_REG *dest); +extern "C" int divide_by_zero(int sign, FPU_REG *dest); extern "C" void set_precision_flag_up(void); extern "C" void set_precision_flag_down(void); extern "C" int denormal_operand(void); -extern "C" void arith_overflow(FPU_REG *dest); -extern "C" void arith_underflow(FPU_REG *dest); +extern "C" int arith_overflow(FPU_REG *dest); +extern "C" int arith_underflow(FPU_REG *dest); /* fpu_arith.c */ extern void fadd__(void); @@ -81,11 +82,11 @@ extern void poly_sine(FPU_REG *arg, FPU_REG *result); /* poly_tan.c */ -extern void poly_tan(FPU_REG *arg, FPU_REG *y_reg); +extern void poly_tan(FPU_REG *arg, FPU_REG *y_reg, int invert); /* reg_add_sub.c */ -extern void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w); -extern void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w); +extern int reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w); +extern int reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w); /* reg_compare.c */ extern int compare(FPU_REG *b); @@ -101,9 +102,9 @@ extern void fconst(void); /* reg_ld_str.c */ -extern void reg_load_extended(void); -extern void reg_load_double(void); -extern void reg_load_single(void); +extern int reg_load_extended(void); +extern int reg_load_double(void); +extern int reg_load_single(void); extern void reg_load_int64(void); extern void reg_load_int32(void); extern void reg_load_int16(void); @@ -123,4 +124,4 @@ extern void fsave(void); /* reg_mul.c */ -extern void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w); +extern int reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w); diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/fpu_system.h linux/kernel/FPU-emu/fpu_system.h --- pl11/linux/kernel/FPU-emu/fpu_system.h Tue Nov 10 12:54:54 1992 +++ linux/kernel/FPU-emu/fpu_system.h Thu Jul 22 01:11:50 1993 @@ -27,7 +27,7 @@ #define FPU_lookahead (I387.soft.lookahead) #define FPU_entry_eip (I387.soft.entry_eip) -#define status_word (I387.soft.swd) +#define partial_status (I387.soft.swd) #define control_word (I387.soft.cwd) #define regs (I387.soft.regs) #define top (I387.soft.top) @@ -36,5 +36,8 @@ #define cs_selector (I387.soft.fcs) #define data_operand_offset (I387.soft.foo) #define operand_selector (I387.soft.fos) + +/* ######## temporary and ugly ;-) */ +#define FPU_data_address ((void *)(I387.soft.twd)) #endif diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/fpu_trig.c linux/kernel/FPU-emu/fpu_trig.c --- pl11/linux/kernel/FPU-emu/fpu_trig.c Tue Jun 1 21:59:58 1993 +++ linux/kernel/FPU-emu/fpu_trig.c Thu Jul 22 01:11:51 1993 @@ -18,33 +18,105 @@ #include "reg_constant.h" - -static int trig_arg(FPU_REG *X) +static void rem_kernel(unsigned long long st0, unsigned long long *y, + unsigned long long st1, + long long q, int n); + +#define BETTER_THAN_486 + +#define FCOS 4 +#define FPTAN 1 + +/* Used only by fptan, fsin, fcos, and fsincos. */ +/* This routine produces very accurate results, similar to + using a value of pi with more than 128 bits precision. */ +/* Limited measurements show no results worse than 64 bit precision + except for the results for arguments close to 2^63, where the + precision of the result sometimes degrades to about 63.9 bits */ +static int trig_arg(FPU_REG *X, int even) { - FPU_REG tmp, quot; - int rv; + FPU_REG tmp; long long q; - int old_cw = control_word; + int old_cw = control_word, saved_status = partial_status; + + if ( X->exp >= EXP_BIAS + 63 ) + { + partial_status |= SW_C2; /* Reduction incomplete. */ + return -1; + } control_word &= ~CW_RC; control_word |= RC_CHOP; - - reg_move(X, "); - reg_div(", &CONST_PI2, ", FULL_PRECISION); - - reg_move(", &tmp); - round_to_int(&tmp); - if ( tmp.sigh & 0x80000000 ) - return -1; /* |Arg| is >= 2^63 */ - tmp.exp = EXP_BIAS + 63; - q = *(long long *)&(tmp.sigl); - normalize(&tmp); - - reg_sub(", &tmp, X, FULL_PRECISION); - rv = q & 7; - + + reg_div(X, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f); + round_to_int(&tmp); /* Fortunately, this can't overflow + to 2^64 */ + q = ((long long *)&(tmp.sigl))[0]; + if ( q ) + { + rem_kernel(((unsigned long long *)&(X->sigl))[0], + (unsigned long long *)&(tmp.sigl), + ((unsigned long long *)&(CONST_PI2.sigl))[0], + q, X->exp - CONST_PI2.exp); + tmp.exp = CONST_PI2.exp; + normalize(&tmp); + reg_move(&tmp, X); + } + + if ( even == FPTAN ) + { + if ( ((X->exp >= EXP_BIAS) || + ((X->exp == EXP_BIAS-1) + && (X->sigh >= 0xc90fdaa2))) ^ (q & 1) ) + even = FCOS; + else + even = 0; + } + + if ( (even && !(q & 1)) || (!even && (q & 1)) ) + { + reg_sub(&CONST_PI2, X, X, FULL_PRECISION); +#ifdef BETTER_THAN_486 + /* So far, the results are exact but based upon a 64 bit + precision approximation to pi/2. The technique used + now is equivalent to using an approximation to pi/2 which + is accurate to about 128 bits. */ + if ( (X->exp <= CONST_PI2extra.exp + 64) || (q > 1) ) + { + /* This code gives the effect of having p/2 to better than + 128 bits precision. */ + ((long long *)&(tmp.sigl))[0] = q + 1; + tmp.exp = EXP_BIAS + 63; + normalize(&tmp); + reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION); + reg_add(X, &tmp, X, FULL_PRECISION); + } +#endif BETTER_THAN_486 + } +#ifdef BETTER_THAN_486 + else + { + /* So far, the results are exact but based upon a 64 bit + precision approximation to pi/2. The technique used + now is equivalent to using an approximation to pi/2 which + is accurate to about 128 bits. */ + if ( ((q > 0) && (X->exp <= CONST_PI2extra.exp + 64)) || (q > 1) ) + { + /* This code gives the effect of having p/2 to better than + 128 bits precision. */ + ((long long *)&(tmp.sigl))[0] = q; + tmp.exp = EXP_BIAS + 63; + normalize(&tmp); + reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION); + reg_sub(X, &tmp, X, FULL_PRECISION); + } + } +#endif BETTER_THAN_486 + control_word = old_cw; - return rv;; + partial_status = saved_status & ~SW_C2; /* Reduction complete. */ + + return (q & 3) | even; } @@ -52,7 +124,7 @@ void convert_l2reg(long *arg, FPU_REG *dest) { long num = *arg; - + if (num == 0) { reg_move(&CONST_Z, dest); return; } @@ -60,7 +132,7 @@ dest->sign = SIGN_POS; else { num = -num; dest->sign = SIGN_NEG; } - + dest->sigh = num; dest->sigl = 0; dest->exp = EXP_BIAS + 31; @@ -77,8 +149,8 @@ if ( !(FPU_st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ { EXCEPTION(EX_Invalid); - /* Convert to a QNaN */ - FPU_st0_ptr->sigh |= 0x40000000; + if ( control_word & CW_Invalid ) + FPU_st0_ptr->sigh |= 0x40000000; /* Convert to a QNaN */ } break; /* return with a NaN in st(0) */ case TW_Empty: @@ -92,10 +164,50 @@ } +static void single_arg_2_error(void) +{ + FPU_REG *st_new_ptr; + + switch ( FPU_st0_tag ) + { + case TW_NaN: + if ( !(FPU_st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ + { + EXCEPTION(EX_Invalid); + if ( control_word & CW_Invalid ) + { + /* The masked response */ + /* Convert to a QNaN */ + FPU_st0_ptr->sigh |= 0x40000000; + st_new_ptr = &st(-1); + push(); + reg_move(&st(1), FPU_st0_ptr); + } + } + else + { + /* A QNaN */ + st_new_ptr = &st(-1); + push(); + reg_move(&st(1), FPU_st0_ptr); + } + break; /* return with a NaN in st(0) */ +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL|0x0112); +#endif PARANOID + } +} + + /*---------------------------------------------------------------------------*/ static void f2xm1(void) { +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 switch ( FPU_st0_tag ) { case TW_Valid: @@ -102,31 +214,41 @@ { FPU_REG rv, tmp; -#ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - - if ( FPU_st0_ptr->sign == SIGN_POS ) + if ( FPU_st0_ptr->exp >= 0 ) + { + /* For an 80486 FPU, the result is undefined. */ + } + else if ( FPU_st0_ptr->exp >= -64 ) { - /* poly_2xm1(x) requires 0 < x < 1. */ - if ( poly_2xm1(FPU_st0_ptr, &rv) ) - return; /* error */ - reg_mul(&rv, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); + if ( FPU_st0_ptr->sign == SIGN_POS ) + { + /* poly_2xm1(x) requires 0 < x < 1. */ + poly_2xm1(FPU_st0_ptr, &rv); + reg_mul(&rv, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); + } + else + { + /* poly_2xm1(x) doesn't handle negative numbers yet. */ + /* So we compute z=poly_2xm1(-x), and the answer is + then -z/(1+z) */ + FPU_st0_ptr->sign = SIGN_POS; + poly_2xm1(FPU_st0_ptr, &rv); + reg_mul(&rv, FPU_st0_ptr, &rv, FULL_PRECISION); + reg_add(&rv, &CONST_1, &tmp, FULL_PRECISION); + reg_div(&rv, &tmp, FPU_st0_ptr, FULL_PRECISION); + FPU_st0_ptr->sign = SIGN_NEG; + } } else { -/* **** Should change poly_2xm1() to at least handle numbers near 0 */ - /* poly_2xm1(x) doesn't handle negative numbers. */ - /* So we compute (poly_2xm1(x+1)-1)/2, for -1 < x < 0 */ - reg_add(FPU_st0_ptr, &CONST_1, &tmp, FULL_PRECISION); - poly_2xm1(&tmp, &rv); - reg_mul(&rv, &tmp, &tmp, FULL_PRECISION); - reg_sub(&tmp, &CONST_1, FPU_st0_ptr, FULL_PRECISION); - FPU_st0_ptr->exp--; - if ( FPU_st0_ptr->exp <= EXP_UNDER ) - arith_underflow(FPU_st0_ptr); +#ifdef DENORM_OPERAND + if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + /* For very small arguments, this is accurate enough. */ + reg_mul(&CONST_LN2, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); } + set_precision_flag_up(); return; } case TW_Zero: @@ -144,6 +266,7 @@ } } + static void fptan(void) { FPU_REG *st_new_ptr; @@ -150,6 +273,19 @@ int q; char arg_sign = FPU_st0_ptr->sign; + /* Stack underflow has higher priority */ + if ( FPU_st0_tag == TW_Empty ) + { + stack_underflow(); /* Puts a QNaN in st(0) */ + if ( control_word & CW_Invalid ) + { + st_new_ptr = &st(-1); + push(); + stack_underflow(); /* Puts a QNaN in the new st(0) */ + } + return; + } + if ( STACK_OVERFLOW ) { stack_overflow(); return; } @@ -157,40 +293,56 @@ { case TW_Valid: -#ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - - FPU_st0_ptr->sign = SIGN_POS; - if ( (q = trig_arg(FPU_st0_ptr)) != -1 ) + if ( FPU_st0_ptr->exp > EXP_BIAS - 40 ) { - if (q & 1) - reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); - - poly_tan(FPU_st0_ptr, FPU_st0_ptr); - - FPU_st0_ptr->sign = (q & 1) ^ arg_sign; - - if ( FPU_st0_ptr->exp <= EXP_UNDER ) - arith_underflow(FPU_st0_ptr); - - push(); - reg_move(&CONST_1, FPU_st0_ptr); - setcc(0); + FPU_st0_ptr->sign = SIGN_POS; + if ( (q = trig_arg(FPU_st0_ptr, FPTAN)) != -1 ) + { + reg_div(FPU_st0_ptr, &CONST_PI2, FPU_st0_ptr, + FULL_PRECISION); + poly_tan(FPU_st0_ptr, FPU_st0_ptr, q & FCOS); + FPU_st0_ptr->sign = (q & 1) ^ arg_sign; + } + else + { + /* Operand is out of range */ + FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ + return; + } } else { - /* Operand is out of range */ - setcc(SW_C2); - FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ - return; + /* For a small arg, the result == the argument */ + /* Underflow may happen */ + + if ( FPU_st0_ptr->exp <= EXP_UNDER ) + { +#ifdef DENORM_OPERAND + if ( denormal_operand() ) + return; +#endif DENORM_OPERAND + /* A denormal result has been produced. + Precision must have been lost, this is always + an underflow. */ + if ( arith_underflow(FPU_st0_ptr) ) + return; + } + else + set_precision_flag_up(); /* Must be up. */ } + push(); + reg_move(&CONST_1, FPU_st0_ptr); + return; break; case TW_Infinity: - /* Operand is out of range */ - setcc(SW_C2); - FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ + /* The 80486 treats infinity as an invalid operand */ + arith_invalid(FPU_st0_ptr); + if ( control_word & CW_Invalid ) + { + st_new_ptr = &st(-1); + push(); + arith_invalid(FPU_st0_ptr); + } return; case TW_Zero: push(); @@ -198,7 +350,7 @@ setcc(0); break; default: - single_arg_error(); + single_arg_2_error(); break; } } @@ -212,6 +364,10 @@ if ( STACK_OVERFLOW ) { stack_overflow(); return; } +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 if ( !(FPU_st0_tag ^ TW_Valid) ) { long e; @@ -231,7 +387,8 @@ else if ( FPU_st0_tag == TW_Zero ) { char sign = FPU_st0_ptr->sign; - divide_by_zero(SIGN_NEG, FPU_st0_ptr); + if ( divide_by_zero(SIGN_NEG, FPU_st0_ptr) ) + return; push(); reg_move(&CONST_Z, FPU_st0_ptr); FPU_st0_ptr->sign = sign; @@ -248,12 +405,8 @@ } else if ( FPU_st0_tag == TW_NaN ) { - if ( !(FPU_st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ - { - EXCEPTION(EX_Invalid); - /* Convert to a QNaN */ - FPU_st0_ptr->sigh |= 0x40000000; - } + if ( real_2op_NaN(FPU_st0_ptr, FPU_st0_ptr, FPU_st0_ptr) ) + return; push(); reg_move(st1_ptr, FPU_st0_ptr); return; @@ -279,11 +432,19 @@ static void fdecstp(void) { +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 top--; /* FPU_st0_ptr will be fixed in math_emulate() before the next instr */ } static void fincstp(void) { +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 top++; /* FPU_st0_ptr will be fixed in math_emulate() before the next instr */ } @@ -290,6 +451,10 @@ static void fsqrt_(void) { +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 if ( !(FPU_st0_tag ^ TW_Valid) ) { int expon; @@ -329,6 +494,8 @@ static void frndint_(void) { + int flags; + if ( !(FPU_st0_tag ^ TW_Valid) ) { if (FPU_st0_ptr->exp > EXP_BIAS+63) @@ -339,7 +506,10 @@ return; #endif DENORM_OPERAND - round_to_int(FPU_st0_ptr); /* Fortunately, this can't overflow to 2^64 */ + /* Fortunately, this can't overflow to 2^64 */ + if ( (flags = round_to_int(FPU_st0_ptr)) ) + set_precision_flag(flags); + FPU_st0_ptr->exp = EXP_BIAS + 63; normalize(FPU_st0_ptr); return; @@ -357,42 +527,53 @@ if ( FPU_st0_tag == TW_Valid ) { + FPU_REG rv; int q; - FPU_st0_ptr->sign = SIGN_POS; - -#ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - if ( (q = trig_arg(FPU_st0_ptr)) != -1 ) + if ( FPU_st0_ptr->exp > EXP_BIAS - 40 ) { - FPU_REG rv; - - if (q & 1) - reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); - - poly_sine(FPU_st0_ptr, &rv); - - setcc(0); - if (q & 2) - rv.sign ^= SIGN_POS ^ SIGN_NEG; - rv.sign ^= arg_sign; - reg_move(&rv, FPU_st0_ptr); + FPU_st0_ptr->sign = SIGN_POS; + if ( (q = trig_arg(FPU_st0_ptr, 0)) != -1 ) + { + reg_div(FPU_st0_ptr, &CONST_PI2, FPU_st0_ptr, FULL_PRECISION); - if ( FPU_st0_ptr->exp <= EXP_UNDER ) - arith_underflow(FPU_st0_ptr); + poly_sine(FPU_st0_ptr, &rv); - set_precision_flag_up(); /* We do not really know if up or down */ + if (q & 2) + rv.sign ^= SIGN_POS ^ SIGN_NEG; + rv.sign ^= arg_sign; + reg_move(&rv, FPU_st0_ptr); - return; + /* We do not really know if up or down */ + set_precision_flag_up(); + return; + } + else + { + /* Operand is out of range */ + FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ + return; + } } else { - /* Operand is out of range */ - setcc(SW_C2); - FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ - return; + /* For a small arg, the result == the argument */ + /* Underflow may happen */ + + if ( FPU_st0_ptr->exp <= EXP_UNDER ) + { +#ifdef DENORM_OPERAND + if ( denormal_operand() ) + return; +#endif DENORM_OPERAND + /* A denormal result has been produced. + Precision must have been lost, this is always + an underflow. */ + arith_underflow(FPU_st0_ptr); + return; + } + + set_precision_flag_up(); /* Must be up. */ } } else if ( FPU_st0_tag == TW_Zero ) @@ -402,9 +583,8 @@ } else if ( FPU_st0_tag == TW_Infinity ) { - /* Operand is out of range */ - setcc(SW_C2); - FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ + /* The 80486 treats infinity as an invalid operand */ + arith_invalid(FPU_st0_ptr); return; } else @@ -418,38 +598,49 @@ if ( arg->tag == TW_Valid ) { + FPU_REG rv; int q; -#ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return 1; -#endif DENORM_OPERAND - - arg->sign = SIGN_POS; - if ( (q = trig_arg(arg)) != -1 ) + if ( arg->exp > EXP_BIAS - 40 ) { - FPU_REG rv; - - if ( !(q & 1) ) - reg_sub(&CONST_1, arg, arg, FULL_PRECISION); - - poly_sine(arg, &rv); - - setcc(0); - if ((q+1) & 2) - rv.sign ^= SIGN_POS ^ SIGN_NEG; - reg_move(&rv, arg); + arg->sign = SIGN_POS; + if ( (q = trig_arg(arg, FCOS)) != -1 ) + { + reg_div(arg, &CONST_PI2, arg, FULL_PRECISION); + + poly_sine(arg, &rv); + + if ((q+1) & 2) + rv.sign ^= SIGN_POS ^ SIGN_NEG; + reg_move(&rv, arg); - set_precision_flag_up(); /* We do not really know if up or down */ + /* We do not really know if up or down */ + set_precision_flag_down(); - return 0; + return 0; + } + else + { + /* Operand is out of range */ + arg->sign = arg_sign; /* restore st(0) */ + return 1; + } } else { - /* Operand is out of range */ - setcc(SW_C2); - arg->sign = arg_sign; /* restore st(0) */ - return 1; +#ifdef DENORM_OPERAND + if ( (arg->exp <= EXP_UNDER) && (denormal_operand()) ) + return 1; +#endif DENORM_OPERAND + + setcc(0); + reg_move(&CONST_1, arg); +#ifdef PECULIAR_486 + set_precision_flag_down(); /* 80486 appears to do this. */ +#else + set_precision_flag_up(); /* Must be up. */ +#endif PECULIAR_486 + return 0; } } else if ( arg->tag == TW_Zero ) @@ -460,9 +651,8 @@ } else if ( FPU_st0_tag == TW_Infinity ) { - /* Operand is out of range */ - setcc(SW_C2); - arg->sign = arg_sign; /* restore st(0) */ + /* The 80486 treats infinity as an invalid operand */ + arith_invalid(FPU_st0_ptr); return 1; } else @@ -483,10 +673,40 @@ { FPU_REG *st_new_ptr; FPU_REG arg; - + + /* Stack underflow has higher priority */ + if ( FPU_st0_tag == TW_Empty ) + { + stack_underflow(); /* Puts a QNaN in st(0) */ + if ( control_word & CW_Invalid ) + { + st_new_ptr = &st(-1); + push(); + stack_underflow(); /* Puts a QNaN in the new st(0) */ + } + return; + } + if ( STACK_OVERFLOW ) { stack_overflow(); return; } + if ( FPU_st0_tag == TW_NaN ) + { + single_arg_2_error(); + return; + } + else if ( FPU_st0_tag == TW_Infinity ) + { + /* The 80486 treats infinity as an invalid operand */ + if ( !arith_invalid(FPU_st0_ptr) ) + { + /* unmasked response */ + push(); + arith_invalid(FPU_st0_ptr); + } + return; + } + reg_move(FPU_st0_ptr,&arg); if ( !f_cos(&arg) ) { @@ -501,84 +721,186 @@ /*---------------------------------------------------------------------------*/ /* The following all require two arguments: st(0) and st(1) */ -/* remainder of st(0) / st(1) */ -/* Assumes that st(0) and st(1) are both TW_Valid */ -static void fprem_kernel(int round) +/* A lean, mean kernel for the fprem instructions. This relies upon + the division and rounding to an integer in do_fprem giving an + exact result. Because of this, rem_kernel() needs to deal only with + the least significant 64 bits, the more significant bits of the + result must be zero. + */ +static void rem_kernel(unsigned long long st0, unsigned long long *y, + unsigned long long st1, + long long q, int n) +{ + unsigned long long x; + + x = st0 << n; + + /* Do the required multiplication and subtraction in the one operation */ + asm volatile ("movl %2,%%eax; mull %4; subl %%eax,%0; sbbl %%edx,%1; + movl %3,%%eax; mull %4; subl %%eax,%1; + movl %2,%%eax; mull %5; subl %%eax,%1;" + :"=m" (x), "=m" (((unsigned *)&x)[1]) + :"m" (st1),"m" (((unsigned *)&st1)[1]), + "m" (q),"m" (((unsigned *)&q)[1]) + :"%ax","%dx"); + + *y = x; +} + + +/* Remainder of st(0) / st(1) */ +/* This routine produces exact results, i.e. there is never any + rounding or truncation, etc of the result. */ +static void do_fprem(int round) { FPU_REG *st1_ptr = &st(1); char st1_tag = st1_ptr->tag; + char sign = FPU_st0_ptr->sign; if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) { FPU_REG tmp; int old_cw = control_word; - int expdif = FPU_st0_ptr->exp - (st1_ptr)->exp; + int expdif = FPU_st0_ptr->exp - st1_ptr->exp; + long long q; + unsigned short saved_status; + int cc = 0; #ifdef DENORM_OPERAND - if ( ((FPU_st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) - return; + if ( ((FPU_st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) + return; #endif DENORM_OPERAND + /* We want the status following the denorm tests, but don't want + the status changed by the arithmetic operations. */ + saved_status = partial_status; control_word &= ~CW_RC; - control_word |= round; - + control_word |= RC_CHOP; + if (expdif < 64) { /* This should be the most common case */ - long long q; - int c = 0; - reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION); - - round_to_int(&tmp); /* Fortunately, this can't overflow to 2^64 */ - tmp.exp = EXP_BIAS + 63; - q = *(long long *)&(tmp.sigl); - normalize(&tmp); - - reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION); - reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION); - - if (q&4) c |= SW_C3; - if (q&2) c |= SW_C1; - if (q&1) c |= SW_C0; - - setcc(c); + if ( expdif > -2 ) + { + reg_div(FPU_st0_ptr, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f); + + if ( tmp.exp >= EXP_BIAS ) + { + round_to_int(&tmp); /* Fortunately, this can't overflow + to 2^64 */ + q = ((long long *)&(tmp.sigl))[0]; + + rem_kernel(((unsigned long long *)&(FPU_st0_ptr->sigl))[0], + (unsigned long long *)&(tmp.sigl), + ((unsigned long long *)&(st1_ptr->sigl))[0], + q, expdif); + + tmp.exp = st1_ptr->exp; + } + else + { + reg_move(FPU_st0_ptr, &tmp); + q = 0; + } + tmp.sign = sign; + + if ( (round == RC_RND) && (tmp.sigh & 0xc0000000) ) + { + /* We may need to subtract st(1) once more. */ + unsigned long long x; + x = ((unsigned long long *)&(st1_ptr->sigl))[0] - + ((unsigned long long *)&(tmp.sigl))[0]; + if ( x < ((unsigned long long *)&(tmp.sigl))[0] ) + { + tmp.sign ^= (SIGN_POS^SIGN_NEG); + ((unsigned long long *)&(tmp.sigl))[0] = x; + } + } + + if (q & 4) cc |= SW_C0; + if (q & 2) cc |= SW_C3; + if (q & 1) cc |= SW_C1; + } + else + { + control_word = old_cw; + setcc(0); + return; + } } else { /* There is a large exponent difference ( >= 64 ) */ - int N_exp; - - reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION); + /* To make much sense, the code in this section should + be done at high precision. */ + int exp_1; + + /* prevent overflow here */ /* N is 'a number between 32 and 63' (p26-113) */ - N_exp = (tmp.exp & 31) + 32; - tmp.exp = EXP_BIAS + N_exp; - + reg_move(FPU_st0_ptr, &tmp); + tmp.exp = EXP_BIAS + 56; + exp_1 = st1_ptr->exp; st1_ptr->exp = EXP_BIAS; + expdif -= 56; + + reg_div(&tmp, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f); + st1_ptr->exp = exp_1; + round_to_int(&tmp); /* Fortunately, this can't overflow to 2^64 */ - tmp.exp = EXP_BIAS + 63; - normalize(&tmp); - - tmp.exp = EXP_BIAS + expdif - N_exp; - - reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION); - reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION); - - setcc(SW_C2); + + rem_kernel(((unsigned long long *)&(FPU_st0_ptr->sigl))[0], + ((unsigned long long *)&(tmp.sigl)), + ((unsigned long long *)&(st1_ptr->sigl))[0], + ((long long *)&(tmp.sigl))[0], + tmp.exp - EXP_BIAS + ); + tmp.exp = exp_1 + expdif; + tmp.sign = sign; + + /* It is possible for the operation to be complete here. + What does the IEEE standard say? The Intel 80486 manual + implies that the operation will never be completed at this + point, and the behaviour of a real 80486 confirms this. + */ + if ( !(tmp.sigh | tmp.sigl) ) + { + /* The result is zero */ + control_word = old_cw; + partial_status = saved_status; + reg_move(&CONST_Z, FPU_st0_ptr); + FPU_st0_ptr->sign = sign; +#ifdef PECULIAR_486 + setcc(SW_C2); +#else + setcc(0); +#endif PECULIAR_486 + return; + } + cc = SW_C2; } + control_word = old_cw; + partial_status = saved_status; + normalize(&tmp); + reg_move(&tmp, FPU_st0_ptr); + setcc(cc); + + if ( FPU_st0_ptr->tag != TW_Zero ) + /* Use round_reg() to properly detect under/overflow etc */ + round_reg(FPU_st0_ptr, 0, control_word); - if ( FPU_st0_ptr->exp <= EXP_UNDER ) - arith_underflow(FPU_st0_ptr); return; } else if ( (FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) - { stack_underflow(); return; } + { + stack_underflow(); + return; + } else if ( FPU_st0_tag == TW_Zero ) { if ( st1_tag == TW_Valid ) { - #ifdef DENORM_OPERAND if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; @@ -628,7 +950,7 @@ EXCEPTION(EX_INTERNAL | 0x118); #endif PARANOID - real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); + real_2op_NaN(st1_ptr, FPU_st0_ptr, FPU_st0_ptr); } @@ -639,6 +961,10 @@ FPU_REG *st1_ptr = &st(1); char st1_tag = st1_ptr->tag; +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) { if ( FPU_st0_ptr->sign == SIGN_POS ) @@ -653,7 +979,7 @@ /* We use the general purpose arithmetic, so we need to save these. */ - saved_status = status_word; + saved_status = partial_status; saved_control = control_word; control_word = FULL_PRECISION; @@ -661,7 +987,7 @@ /* Enough of the basic arithmetic is done now */ control_word = saved_control; - status_word = saved_status; + partial_status = saved_status; /* Let the multiply set the flags */ reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION); @@ -670,9 +996,9 @@ } else { - /* negative */ - pop(); FPU_st0_ptr = &st(0); - arith_invalid(FPU_st0_ptr); /* st(0) cannot be negative */ + /* negative */ + if ( !arith_invalid(st1_ptr) ) + pop(); return; } } @@ -683,8 +1009,8 @@ } else if ( (FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); - pop(); + if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) ) + pop(); return; } else if ( (FPU_st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) ) @@ -692,9 +1018,12 @@ /* one of the args is zero, the other valid, or both zero */ if ( FPU_st0_tag == TW_Zero ) { - pop(); FPU_st0_ptr = &st(0); - if ( FPU_st0_ptr->tag == TW_Zero ) - arith_invalid(FPU_st0_ptr); /* Both args zero is invalid */ + if ( st1_tag == TW_Zero ) + { + /* Both args zero is invalid */ + if ( !arith_invalid(st1_ptr) ) + pop(); + } #ifdef PECULIAR_486 /* This case is not specifically covered in the manual, but divide-by-zero would seem to be the best response. @@ -701,12 +1030,15 @@ However, a real 80486 does it this way... */ else if ( FPU_st0_ptr->tag == TW_Infinity ) { - reg_move(&CONST_INF, FPU_st0_ptr); - return; + reg_move(&CONST_INF, st1_ptr); + pop(); } #endif PECULIAR_486 else - divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, FPU_st0_ptr); + { + if ( !divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, st1_ptr) ) + pop(); + } return; } else @@ -715,17 +1047,19 @@ /* Zero is the valid answer */ char sign = st1_ptr->sign; -#ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND if ( FPU_st0_ptr->sign == SIGN_NEG ) { - pop(); FPU_st0_ptr = &st(0); - arith_invalid(FPU_st0_ptr); /* log(negative) */ + /* log(negative) */ + if ( !arith_invalid(st1_ptr) ) + pop(); return; } +#ifdef DENORM_OPERAND + if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + if ( FPU_st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS; pop(); FPU_st0_ptr = &st(0); reg_move(&CONST_Z, FPU_st0_ptr); @@ -737,9 +1071,12 @@ else if ( FPU_st0_tag == TW_Infinity ) { if ( (FPU_st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) ) - { pop(); FPU_st0_ptr = &st(0); - arith_invalid(FPU_st0_ptr); /* log(-infinity) or 0*log(infinity) */ - return; } + { + /* log(-infinity) or 0*log(infinity) */ + if ( !arith_invalid(st1_ptr) ) + pop(); + return; + } else { char sign = st1_ptr->sign; @@ -765,8 +1102,9 @@ (FPU_st0_ptr->sigl == 0) ) { /* st(0) holds 1.0 */ - pop(); FPU_st0_ptr = &st(0); - arith_invalid(FPU_st0_ptr); /* infinity*log(1) */ + /* infinity*log(1) */ + if ( !arith_invalid(st1_ptr) ) + pop(); return; } /* st(0) is positive and > 1.0 */ @@ -791,17 +1129,20 @@ /* st(0) must be zero or negative */ if ( FPU_st0_ptr->tag == TW_Zero ) { - pop(); FPU_st0_ptr = st1_ptr; - st1_ptr->sign ^= SIGN_NEG^SIGN_POS; /* This should be invalid, but a real 80486 is happy with it. */ #ifndef PECULIAR_486 - divide_by_zero(st1_ptr->sign, FPU_st0_ptr); + if ( !divide_by_zero(st1_ptr->sign, st1_ptr) ) #endif PECULIAR_486 + { + st1_ptr->sign ^= SIGN_NEG^SIGN_POS; + pop(); + } } else { - pop(); FPU_st0_ptr = st1_ptr; - arith_invalid(FPU_st0_ptr); /* log(negative) */ + /* log(negative) */ + if ( !arith_invalid(st1_ptr) ) + pop(); } return; } @@ -812,12 +1153,17 @@ { FPU_REG *st1_ptr = &st(1); char st1_tag = st1_ptr->tag; + char st1_sign = st1_ptr->sign, st0_sign = FPU_st0_ptr->sign; +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) { int saved_control, saved_status; FPU_REG sum; - int quadrant = st1_ptr->sign | ((FPU_st0_ptr->sign)<<1); + char inverted; #ifdef DENORM_OPERAND if ( ((FPU_st0_ptr->exp <= EXP_UNDER) || @@ -826,35 +1172,49 @@ #endif DENORM_OPERAND /* We use the general purpose arithmetic so we need to save these. */ - saved_status = status_word; + saved_status = partial_status; saved_control = control_word; control_word = FULL_PRECISION; st1_ptr->sign = FPU_st0_ptr->sign = SIGN_POS; - if ( compare(st1_ptr) == COMP_A_lt_B ) + if ( (compare(st1_ptr) & ~COMP_Denormal) == COMP_A_lt_B ) { - quadrant |= 4; + inverted = 1; reg_div(FPU_st0_ptr, st1_ptr, &sum, FULL_PRECISION); } else - reg_div(st1_ptr, FPU_st0_ptr, &sum, FULL_PRECISION); + { + inverted = 0; + if ( (st0_sign == 0) && + (st1_ptr->exp - FPU_st0_ptr->exp < -64) ) + { + control_word = saved_control; + partial_status = saved_status; + reg_div(st1_ptr, FPU_st0_ptr, st1_ptr, + control_word | PR_64_BITS); + st1_ptr->sign = st1_sign; + pop(); + set_precision_flag_down(); + return; + } + reg_div(st1_ptr, FPU_st0_ptr, &sum, FULL_PRECISION); + } poly_atan(&sum); - - if (quadrant & 4) + + if ( inverted ) { reg_sub(&CONST_PI2, &sum, &sum, FULL_PRECISION); } - if (quadrant & 2) + if ( st0_sign ) { reg_sub(&CONST_PI, &sum, &sum, FULL_PRECISION); } - if (quadrant & 1) - sum.sign ^= SIGN_POS^SIGN_NEG; + sum.sign = st1_sign; /* All of the basic arithmetic is done now */ control_word = saved_control; - status_word = saved_status; + partial_status = saved_status; reg_move(&sum, st1_ptr); } @@ -865,8 +1225,8 @@ } else if ( (FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); - pop(); + if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) ) + pop(); return; } else if ( (FPU_st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) ) @@ -883,14 +1243,21 @@ } else { - #ifdef DENORM_OPERAND - if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; + if ( st1_tag != TW_Zero ) + { + if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; + } #endif DENORM_OPERAND if ( FPU_st0_ptr->sign == SIGN_POS ) - { reg_move(&CONST_Z, st1_ptr); pop(); return; } + { + reg_move(&CONST_Z, st1_ptr); + st1_ptr->sign = sign; /* An 80486 preserves the sign */ + pop(); + return; + } else reg_move(&CONST_PI, st1_ptr); } @@ -899,8 +1266,11 @@ { /* st(1) is infinity, st(0) not infinity */ #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; + if ( FPU_st0_tag != TW_Zero ) + { + if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; + } #endif DENORM_OPERAND reg_move(&CONST_PI2, st1_ptr); @@ -913,12 +1283,15 @@ char sign = st1_ptr->sign; #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; + if ( FPU_st0_tag != TW_Zero ) + { + if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; + } #endif DENORM_OPERAND if ( FPU_st0_ptr->sign == SIGN_POS ) - { reg_move(&CONST_Z, st1_ptr); pop(); return; } + { /* An 80486 preserves the sign */ pop(); return; } else reg_move(&CONST_PI, st1_ptr); st1_ptr->sign = sign; @@ -938,7 +1311,7 @@ } #ifdef PARANOID else - EXCEPTION(EX_INTERNAL | 0x220); + EXCEPTION(EX_INTERNAL | 0x125); #endif PARANOID pop(); @@ -948,13 +1321,13 @@ static void fprem(void) { - fprem_kernel(RC_CHOP); + do_fprem(RC_CHOP); } static void fprem1(void) { - fprem_kernel(RC_RND); + do_fprem(RC_RND); } @@ -963,6 +1336,10 @@ FPU_REG *st1_ptr = &st(1); char st1_tag = st1_ptr->tag; +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) { int saved_control, saved_status; @@ -969,24 +1346,32 @@ #ifdef DENORM_OPERAND if ( ((FPU_st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) + (st1_ptr->exp <= EXP_UNDER)) && denormal_operand() ) return; #endif DENORM_OPERAND /* We use the general purpose arithmetic so we need to save these. */ - saved_status = status_word; + saved_status = partial_status; saved_control = control_word; control_word = FULL_PRECISION; if ( poly_l2p1(FPU_st0_ptr, FPU_st0_ptr) ) { - arith_invalid(st1_ptr); /* poly_l2p1() returned invalid */ +#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ + st1_ptr->sign ^= SIGN_POS^SIGN_NEG; + control_word = saved_control; + partial_status = saved_status; + set_precision_flag_down(); +#else + if ( arith_invalid(st1_ptr) ) /* poly_l2p1() returned invalid */ + return; +#endif PECULIAR_486 pop(); return; } /* Enough of the basic arithmetic is done now */ control_word = saved_control; - status_word = saved_status; + partial_status = saved_status; /* Let the multiply set the flags */ reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION); @@ -1002,26 +1387,26 @@ { if ( st1_tag <= TW_Zero ) { - #ifdef DENORM_OPERAND - if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) && - (denormal_operand()) ) - return; + if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) && + (denormal_operand()) ) + return; #endif DENORM_OPERAND - - st1_ptr->sign ^= FPU_st0_ptr->sign; + + FPU_st0_ptr->sign ^= st1_ptr->sign; reg_move(FPU_st0_ptr, st1_ptr); } else if ( st1_tag == TW_Infinity ) { - arith_invalid(st1_ptr); /* Infinity*log(1) */ - pop(); + /* Infinity*log(1) */ + if ( !arith_invalid(st1_ptr) ) + pop(); return; } else if ( st1_tag == TW_NaN ) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); - pop(); + if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) ) + pop(); return; } #ifdef PARANOID @@ -1042,7 +1427,11 @@ if ( FPU_st0_ptr->exp >= EXP_BIAS ) { /* st(0) holds <= -1.0 */ - arith_invalid(st1_ptr); /* infinity*log(1) */ +#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ + st1_ptr->sign ^= SIGN_POS^SIGN_NEG; +#else + if ( arith_invalid(st1_ptr) ) return; +#endif PECULIAR_486 pop(); return; } #ifdef DENORM_OPERAND @@ -1067,7 +1456,11 @@ (FPU_st0_ptr->sigl == 0)) ) { /* st(0) holds < -1.0 */ - arith_invalid(st1_ptr); +#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ + st1_ptr->sign ^= SIGN_POS^SIGN_NEG; +#else + if ( arith_invalid(st1_ptr) ) return; +#endif PECULIAR_486 pop(); return; } #ifdef DENORM_OPERAND @@ -1085,15 +1478,15 @@ } if ( st1_tag == TW_NaN ) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); - pop(); + if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) ) + pop(); return; } } else if ( FPU_st0_tag == TW_NaN ) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); - pop(); + if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) ) + pop(); return; } else if ( FPU_st0_tag == TW_Infinity ) @@ -1100,23 +1493,46 @@ { if ( st1_tag == TW_NaN ) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); - pop(); + if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) ) + pop(); return; } - else if ( (FPU_st0_ptr->sign == SIGN_NEG) || - (st1_tag == TW_Zero) ) + else if ( FPU_st0_ptr->sign == SIGN_NEG ) { - arith_invalid(st1_ptr); /* log(infinity) */ + int exponent = st1_ptr->exp; +#ifndef PECULIAR_486 + /* This should have higher priority than denormals, but... */ + if ( arith_invalid(st1_ptr) ) /* log(-infinity) */ + return; +#endif PECULIAR_486 +#ifdef DENORM_OPERAND + if ( st1_tag != TW_Zero ) + { + if ( (exponent <= EXP_UNDER) && (denormal_operand()) ) + return; + } +#endif DENORM_OPERAND +#ifdef PECULIAR_486 + /* Denormal operands actually get higher priority */ + if ( arith_invalid(st1_ptr) ) /* log(-infinity) */ + return; +#endif PECULIAR_486 pop(); return; } + else if ( st1_tag == TW_Zero ) + { + /* log(infinity) */ + if ( !arith_invalid(st1_ptr) ) + pop(); + return; + } /* st(1) must be valid here. */ #ifdef DENORM_OPERAND - if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; + if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; #endif DENORM_OPERAND /* The Manual says that log(Infinity) is invalid, but a real @@ -1142,7 +1558,12 @@ FPU_REG *st1_ptr = &st(1); char st1_tag = st1_ptr->tag; int old_cw = control_word; + char sign = FPU_st0_ptr->sign; +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) { long scale; @@ -1204,14 +1625,12 @@ } if ( st1_tag == TW_Infinity ) { - char sign = st1_ptr->sign; - #ifdef DENORM_OPERAND if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; #endif DENORM_OPERAND - if ( sign == SIGN_POS ) + if ( st1_ptr->sign == SIGN_POS ) { reg_move(&CONST_INF, FPU_st0_ptr); } else reg_move(&CONST_Z, FPU_st0_ptr); diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/get_address.c linux/kernel/FPU-emu/get_address.c --- pl11/linux/kernel/FPU-emu/get_address.c Wed Mar 31 16:12:43 1993 +++ linux/kernel/FPU-emu/get_address.c Thu Jul 22 01:11:51 1993 @@ -40,9 +40,6 @@ #define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info)) -void *FPU_data_address; - - /* Decode the SIB byte. This function assumes mod != 0 */ static void *sib(int mod) { @@ -117,7 +114,12 @@ unsigned char mod; long *cpu_reg_ptr; int offset = 0; /* Initialized just to stop compiler warnings. */ - + +#ifndef PECULIAR_486 + /* This is a reasonable place to do this */ + FPU_data_selector = FPU_DS; +#endif PECULIAR_486 + mod = (FPU_modrm >> 6) & 3; if (FPU_rm == 4 && mod != 3) diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/load_store.c linux/kernel/FPU-emu/load_store.c --- pl11/linux/kernel/FPU-emu/load_store.c Sat Jul 3 12:49:45 1993 +++ linux/kernel/FPU-emu/load_store.c Thu Jul 22 01:11:51 1993 @@ -24,6 +24,7 @@ #include "exception.h" #include "fpu_emu.h" #include "status_w.h" +#include "control_w.h" #define _NONE_ 0 /* FPU_st0_ptr etc not needed */ @@ -47,7 +48,8 @@ void load_store_instr(char type) { - FPU_REG *pop_ptr; /* We need a version of FPU_st0_ptr which won't change. */ + FPU_REG *pop_ptr; /* We need a version of FPU_st0_ptr which won't + change if the emulator is re-entered. */ pop_ptr = NULL; /* Initialized just to stop compiler warnings. */ switch ( type_table[(int) (unsigned) type] ) @@ -82,53 +84,109 @@ switch ( type ) { case 000: /* fld m32real */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_load_single(); - setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */ + if ( (FPU_loaded_data.tag == TW_NaN) && + real_2op_NaN(&FPU_loaded_data, &FPU_loaded_data, &FPU_loaded_data) ) + { + top++; + break; + } reg_move(&FPU_loaded_data, pop_ptr); break; case 001: /* fild m32int */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_load_int32(); - setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */ reg_move(&FPU_loaded_data, pop_ptr); break; case 002: /* fld m64real */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_load_double(); - setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */ + if ( (FPU_loaded_data.tag == TW_NaN) && + real_2op_NaN(&FPU_loaded_data, &FPU_loaded_data, &FPU_loaded_data) ) + { + top++; + break; + } reg_move(&FPU_loaded_data, pop_ptr); break; case 003: /* fild m16int */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_load_int16(); - setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */ reg_move(&FPU_loaded_data, pop_ptr); break; case 010: /* fst m32real */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_store_single(); break; case 011: /* fist m32int */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_store_int32(); break; case 012: /* fst m64real */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_store_double(); break; case 013: /* fist m16int */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_store_int16(); break; case 014: /* fstp m32real */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 if ( reg_store_single() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 015: /* fistp m32int */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 if ( reg_store_int32() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 016: /* fstp m64real */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 if ( reg_store_double() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 017: /* fistp m16int */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 if ( reg_store_int16() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ @@ -140,72 +198,90 @@ frstor(); break; case 023: /* fbld m80dec */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_load_bcd(); - setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */ reg_move(&FPU_loaded_data, pop_ptr); break; case 024: /* fldcw */ - RE_ENTRANT_CHECK_OFF + RE_ENTRANT_CHECK_OFF; control_word = get_fs_word((unsigned short *) FPU_data_address); - RE_ENTRANT_CHECK_ON -#ifdef NO_UNDERFLOW_TRAP - if ( !(control_word & EX_Underflow) ) - { - control_word |= EX_Underflow; - } -#endif - FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ - FPU_entry_eip = ip_offset; /* We want no net effect */ + RE_ENTRANT_CHECK_ON; + if ( partial_status & ~control_word & CW_Exceptions ) + partial_status |= (SW_Summary | SW_Backward); + else + partial_status &= ~(SW_Summary | SW_Backward); +#ifdef PECULIAR_486 + control_word |= 0x40; /* An 80486 appears to always set this bit */ +#endif PECULIAR_486 + NO_NET_DATA_EFFECT; + NO_NET_INSTR_EFFECT; break; case 025: /* fld m80real */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_load_extended(); - setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */ reg_move(&FPU_loaded_data, pop_ptr); break; case 027: /* fild m64int */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 reg_load_int64(); - setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */ reg_move(&FPU_loaded_data, pop_ptr); break; case 030: /* fstenv m14/28byte */ fstenv(); - FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ - FPU_entry_eip = ip_offset; /* We want no net effect */ + NO_NET_DATA_EFFECT; break; case 032: /* fsave */ fsave(); - FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ - FPU_entry_eip = ip_offset; /* We want no net effect */ + NO_NET_DATA_EFFECT; break; case 033: /* fbstp m80dec */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 if ( reg_store_bcd() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 034: /* fstcw m16int */ - RE_ENTRANT_CHECK_OFF + RE_ENTRANT_CHECK_OFF; verify_area(VERIFY_WRITE,FPU_data_address,2); put_fs_word(control_word, (short *) FPU_data_address); - RE_ENTRANT_CHECK_ON - FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ - FPU_entry_eip = ip_offset; /* We want no net effect */ + RE_ENTRANT_CHECK_ON; + NO_NET_DATA_EFFECT; + NO_NET_INSTR_EFFECT; break; case 035: /* fstp m80real */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 if ( reg_store_extended() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 036: /* fstsw m2byte */ - status_word &= ~SW_Top; - status_word |= (top&7) << SW_Top_Shift; - RE_ENTRANT_CHECK_OFF + RE_ENTRANT_CHECK_OFF; verify_area(VERIFY_WRITE,FPU_data_address,2); - put_fs_word(status_word,(short *) FPU_data_address); - RE_ENTRANT_CHECK_ON - FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ - FPU_entry_eip = ip_offset; /* We want no net effect */ + put_fs_word(status_word(),(short *) FPU_data_address); + RE_ENTRANT_CHECK_ON; + NO_NET_DATA_EFFECT; + NO_NET_INSTR_EFFECT; break; case 037: /* fistp m64int */ +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 if ( reg_store_int64() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/poly_2xm1.c linux/kernel/FPU-emu/poly_2xm1.c --- pl11/linux/kernel/FPU-emu/poly_2xm1.c Tue Jun 1 21:59:58 1993 +++ linux/kernel/FPU-emu/poly_2xm1.c Thu Jul 22 01:11:51 1993 @@ -3,7 +3,8 @@ | | | Function to compute 2^x-1 by a polynomial approximation. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | @@ -35,52 +36,38 @@ /*--- poly_2xm1() -----------------------------------------------------------+ - | | + | Requires a positive argument which is TW_Valid and < 1. | +---------------------------------------------------------------------------*/ int poly_2xm1(FPU_REG *arg, FPU_REG *result) { short exponent; long long Xll; - FPU_REG accum; + FPU_REG accum; exponent = arg->exp - EXP_BIAS; - if ( arg->tag == TW_Zero ) - { - /* Return 0.0 */ - reg_move(&CONST_Z, result); - return 0; - } - - if ( exponent >= 0 ) /* Can't hack a number >= 1.0 */ - { - arith_invalid(result); /* Number too large */ - return 1; - } - - if ( arg->sign != SIGN_POS ) /* Can't hack a number < 0.0 */ +#ifdef PARANOID + if ( (arg->sign != SIGN_POS) /* Can't hack a number < 0.0 */ + || (exponent >= 0) /* or a |number| >= 1.0 */ + || (arg->tag != TW_Valid) ) { - arith_invalid(result); /* Number negative */ + /* Number negative, too large, or not Valid. */ + EXCEPTION(EX_INTERNAL|0x127); return 1; } - - if ( exponent < -64 ) - { - reg_move(&CONST_LN2, result); - return 0; - } +#endif PARANOID *(unsigned *)&Xll = arg->sigl; *(((unsigned *)&Xll)+1) = arg->sigh; if ( exponent < -1 ) { - /* shift the argument right by the required places */ + /* Shift the argument right by the required places. */ if ( shrx(&Xll, -1-exponent) >= 0x80000000U ) Xll++; /* round up */ } - *(short *)&(accum.sign) = 0; /* will be a valid positive nr with expon = 0 */ + *(short *)&(accum.sign) = 0; /* Will be a valid positive nr with expon = 0 */ accum.exp = 0; /* Do the basic fixed point polynomial evaluation */ diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/poly_atan.c linux/kernel/FPU-emu/poly_atan.c --- pl11/linux/kernel/FPU-emu/poly_atan.c Tue Jun 1 21:59:58 1993 +++ linux/kernel/FPU-emu/poly_atan.c Thu Jul 22 01:11:51 1993 @@ -51,7 +51,7 @@ { char recursions = 0; short exponent; - FPU_REG odd_poly, even_poly, pos_poly, neg_poly; + FPU_REG odd_poly, even_poly, pos_poly, neg_poly, ratio; FPU_REG argSq; long long arg_signif, argSqSq; @@ -85,6 +85,7 @@ } #ifdef PARANOID EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */ + return; #endif PARANOID } @@ -162,9 +163,6 @@ reg_move(&pos_poly, &odd_poly); poly_add_1(&odd_poly); - /* The complete odd polynomial */ - reg_u_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION); - /* will be a valid positive nr with expon = 0 */ *(short *)&(even_poly.sign) = 0; @@ -173,10 +171,13 @@ poly_add_1(&even_poly); - reg_div(&odd_poly, &even_poly, arg, FULL_PRECISION); + reg_div(&odd_poly, &even_poly, &ratio, FULL_PRECISION); + + reg_u_mul(&ratio, arg, arg, FULL_PRECISION); if ( recursions ) reg_sub(&CONST_PI4, arg, arg, FULL_PRECISION); + } diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/poly_sin.c linux/kernel/FPU-emu/poly_sin.c --- pl11/linux/kernel/FPU-emu/poly_sin.c Fri Apr 23 16:48:13 1993 +++ linux/kernel/FPU-emu/poly_sin.c Thu Jul 22 01:11:51 1993 @@ -43,7 +43,7 @@ void poly_sine(FPU_REG *arg, FPU_REG *result) { short exponent; - FPU_REG Xx, Xx2, Xx4, accum, negaccum; + FPU_REG fixed_arg, arg_sqrd, arg_to_4, accum, negaccum; exponent = arg->exp - EXP_BIAS; @@ -76,19 +76,19 @@ } #endif PARANOID - Xx.sigl = arg->sigl; - Xx.sigh = arg->sigh; + fixed_arg.sigl = arg->sigl; + fixed_arg.sigh = arg->sigh; if ( exponent < -1 ) { /* shift the argument right by the required places */ - if ( shrx(&(Xx.sigl), -1-exponent) >= 0x80000000U ) - (*((long long *)(&(Xx.sigl))))++; /* round up */ + if ( shrx(&(fixed_arg.sigl), -1-exponent) >= 0x80000000U ) + (*((long long *)(&(fixed_arg.sigl))))++; /* round up */ } - mul64((long long *)&(Xx.sigl), (long long *)&(Xx.sigl), - (long long *)&(Xx2.sigl)); - mul64((long long *)&(Xx2.sigl), (long long *)&(Xx2.sigl), - (long long *)&(Xx4.sigl)); + mul64((long long *)&(fixed_arg.sigl), (long long *)&(fixed_arg.sigl), + (long long *)&(arg_sqrd.sigl)); + mul64((long long *)&(arg_sqrd.sigl), (long long *)&(arg_sqrd.sigl), + (long long *)&(arg_to_4.sigl)); /* will be a valid positive nr with expon = 0 */ *(short *)&(accum.sign) = 0; @@ -95,7 +95,7 @@ accum.exp = 0; /* Do the basic fixed point polynomial evaluation */ - polynomial(&(accum.sigl), &(Xx4.sigl), lterms, HIPOWER-1); + polynomial(&(accum.sigl), &(arg_to_4.sigl), lterms, HIPOWER-1); /* will be a valid positive nr with expon = 0 */ *(short *)&(negaccum.sign) = 0; @@ -102,8 +102,8 @@ negaccum.exp = 0; /* Do the basic fixed point polynomial evaluation */ - polynomial(&(negaccum.sigl), &(Xx4.sigl), negterms, HIPOWER-1); - mul64((long long *)&(Xx2.sigl), (long long *)&(negaccum.sigl), + polynomial(&(negaccum.sigl), &(arg_to_4.sigl), negterms, HIPOWER-1); + mul64((long long *)&(arg_sqrd.sigl), (long long *)&(negaccum.sigl), (long long *)&(negaccum.sigl)); /* Subtract the mantissas */ @@ -111,20 +111,17 @@ /* Convert to 64 bit signed-compatible */ accum.exp = EXP_BIAS - 1 + accum.exp; - - *(short *)&(result->sign) = *(short *)&(accum.sign); - result->exp = accum.exp; - result->sigl = accum.sigl; - result->sigh = accum.sigh; - + + reg_move(&accum, result); + normalize(result); reg_mul(result, arg, result, FULL_PRECISION); reg_u_add(result, arg, result, FULL_PRECISION); - /* A small overflow may be possible... but an illegal result. */ if ( result->exp >= EXP_BIAS ) { + /* A small overflow may be possible... but an illegal result. */ if ( (result->exp > EXP_BIAS) /* Larger or equal 2.0 */ || (result->sigl > 1) /* Larger than 1.0+msb */ || (result->sigh != 0x80000000) /* Much > 1.0 */ @@ -149,4 +146,5 @@ result->sigl = 0; /* Truncate the result to 1.00 */ } + } diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/poly_tan.c linux/kernel/FPU-emu/poly_tan.c --- pl11/linux/kernel/FPU-emu/poly_tan.c Tue Jun 1 21:59:58 1993 +++ linux/kernel/FPU-emu/poly_tan.c Thu Jul 22 01:11:51 1993 @@ -49,9 +49,8 @@ /*--- poly_tan() ------------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ -void poly_tan(FPU_REG *arg, FPU_REG *y_reg) +void poly_tan(FPU_REG *arg, FPU_REG *y_reg, int invert) { - char invert = 0; short exponent; FPU_REG odd_poly, even_poly, pos_poly, neg_poly; FPU_REG argSq; @@ -59,39 +58,6 @@ exponent = arg->exp - EXP_BIAS; - - if ( arg->tag == TW_Zero ) - { - /* Return 0.0 */ - reg_move(&CONST_Z, y_reg); - return; - } - - if ( exponent >= -1 ) - { - /* argument is in the range [0.5 .. 1.0] */ - if ( exponent >= 0 ) - { -#ifdef PARANOID - if ( (exponent == 0) && - (arg->sigl == 0) && (arg->sigh == 0x80000000) ) -#endif PARANOID - { - arith_overflow(y_reg); - return; - } -#ifdef PARANOID - EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */ - return; -#endif PARANOID - } - /* The argument is in the range [0.5 .. 1.0) */ - /* Convert the argument to a number in the range (0.0 .. 0.5] */ - *((long long *)(&arg->sigl)) = - *((long long *)(&arg->sigl)); - normalize(arg); /* Needed later */ - exponent = arg->exp - EXP_BIAS; - invert = 1; - } #ifdef PARANOID if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */ @@ -135,8 +101,8 @@ normalize(&odd_poly); reg_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION); - reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION); /* This is just the odd polynomial */ - + /* Complete the odd polynomial. */ + reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION); /* will be a valid positive nr with expon = 0 */ *(short *)&(pos_poly.sign) = 0; @@ -171,7 +137,8 @@ reg_mul(&even_poly, &argSq, &even_poly, FULL_PRECISION); reg_add(&even_poly, &argSq, &even_poly, FULL_PRECISION); - reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION); /* This is just the even polynomial */ + /* Complete the even polynomial */ + reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION); /* Now ready to copy the results */ if ( invert ) diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/polynomial.S linux/kernel/FPU-emu/polynomial.S --- pl11/linux/kernel/FPU-emu/polynomial.S Fri Apr 23 16:48:13 1993 +++ linux/kernel/FPU-emu/polynomial.S Thu Jul 22 01:11:51 1993 @@ -22,11 +22,18 @@ #include "fpu_asm.h" -// #define EXTRA_PRECISE +// #define EXTRA_PRECISE // Do not use: not complete #define TERM_SIZE $8 +#define SUM_MS -20(%ebp) /* sum ms long */ +#define SUM_MIDDLE -24(%ebp) /* sum middle long */ +#define SUM_LS -28(%ebp) /* sum ls long */ +#define SUM_LS_HI -25(%ebp) /* high byte of sum ls */ +#define ACCUM_MS -4(%ebp) /* accum ms long */ +#define ACCUM_MIDDLE -8(%ebp) /* accum middle long */ +#define ACCUM_LS -12(%ebp) /* accum ls long */ +#define ACCUM_LS_HI -9(%ebp) /* high byte of accum ls */ - .text .align 2,144 .globl _polynomial @@ -38,101 +45,92 @@ pushl %edi pushl %ebx - movl PARAM1,%esi // accum - movl PARAM2,%edi // x - movl PARAM3,%ebx // terms - movl PARAM4,%ecx // n + movl PARAM2,%esi // x + movl PARAM3,%edi // terms movl TERM_SIZE,%eax - mull %ecx - movl %eax,%ecx + mull PARAM4 // n + addl %eax,%edi - movl 4(%ebx,%ecx,1),%edx // terms[n] - movl %edx,-20(%ebp) - movl (%ebx,%ecx,1),%edx // terms[n] - movl %edx,-24(%ebp) + movl 4(%edi),%edx // terms[n] + movl %edx,SUM_MS + movl (%edi),%edx // terms[n] + movl %edx,SUM_MIDDLE xor %eax,%eax - movl %eax,-28(%ebp) + movl %eax,SUM_LS - subl TERM_SIZE,%ecx + subl TERM_SIZE,%edi + decl PARAM4 js L_accum_done L_accum_loop: xor %eax,%eax - movl %eax,-4(%ebp) - movl %eax,-8(%ebp) - -#ifdef EXTRA_PRECISE - movl -28(%ebp),%eax - mull 4(%edi) // x ms long - movl %edx,-12(%ebp) -#endif EXTRA_PRECISE + movl %eax,ACCUM_MS + movl %eax,ACCUM_MIDDLE - movl -24(%ebp),%eax - mull (%edi) // x ls long + movl SUM_MIDDLE,%eax + mull (%esi) // x ls long // movl %eax,-16(%ebp) // Not needed - addl %edx,-12(%ebp) - adcl $0,-8(%ebp) + movl %edx,ACCUM_LS - movl -24(%ebp),%eax - mull 4(%edi) // x ms long - addl %eax,-12(%ebp) - adcl %edx,-8(%ebp) - adcl $0,-4(%ebp) - - movl -20(%ebp),%eax - mull (%edi) - addl %eax,-12(%ebp) - adcl %edx,-8(%ebp) - adcl $0,-4(%ebp) - - movl -20(%ebp),%eax - mull 4(%edi) - addl %eax,-8(%ebp) - adcl %edx,-4(%ebp) - -/* Now add the next term */ - movl (%ebx,%ecx,1),%eax - addl %eax,-8(%ebp) - movl 4(%ebx,%ecx,1),%eax - adcl %eax,-4(%ebp) - -/* And put into the second register */ - movl -4(%ebp),%eax - movl %eax,-20(%ebp) - movl -8(%ebp),%eax - movl %eax,-24(%ebp) + movl SUM_MIDDLE,%eax + mull 4(%esi) // x ms long + addl %eax,ACCUM_LS + adcl %edx,ACCUM_MIDDLE + adcl $0,ACCUM_MS + + movl SUM_MS,%eax + mull (%esi) // x ls long + addl %eax,ACCUM_LS + adcl %edx,ACCUM_MIDDLE + adcl $0,ACCUM_MS + + movl SUM_MS,%eax + mull 4(%esi) // x ms long + addl %eax,ACCUM_MIDDLE + adcl %edx,ACCUM_MS + +// Now put the sum of next term and the accumulator +// into the sum register + movl ACCUM_MIDDLE,%eax + addl (%edi),%eax // term ls long + movl %eax,SUM_MIDDLE + movl ACCUM_MS,%eax + adcl 4(%edi),%eax // term ms long + movl %eax,SUM_MS #ifdef EXTRA_PRECISE - movl -12(%ebp),%eax - movl %eax,-28(%ebp) + movl ACCUM_LS,%eax + movl %eax,SUM_LS #else - testb $128,-25(%ebp) + testb $0x80,ACCUM_LS_HI // ms bit of ACCUM_LS je L_no_poly_round - addl $1,-24(%ebp) - adcl $0,-20(%ebp) + addl $1,SUM_MIDDLE + adcl $0,SUM_MS L_no_poly_round: #endif EXTRA_PRECISE - subl TERM_SIZE,%ecx + subl TERM_SIZE,%edi + decl PARAM4 jns L_accum_loop L_accum_done: #ifdef EXTRA_PRECISE /* And round the result */ - testb $128,-25(%ebp) + testb $128,SUM_LS_HI je L_poly_done - addl $1,-24(%ebp) - adcl $0,-20(%ebp) + addl $1,SUM_MIDDLE + adcl $0,SUM_MS #endif EXTRA_PRECISE L_poly_done: - movl -24(%ebp),%eax - movl %eax,(%esi) - movl -20(%ebp),%eax - movl %eax,4(%esi) + movl PARAM1,%edi // accum + movl SUM_MIDDLE,%eax + movl %eax,(%edi) + movl SUM_MS,%eax + movl %eax,4(%edi) popl %ebx popl %edi diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/reg_add_sub.c linux/kernel/FPU-emu/reg_add_sub.c --- pl11/linux/kernel/FPU-emu/reg_add_sub.c Tue Jun 1 21:59:59 1993 +++ linux/kernel/FPU-emu/reg_add_sub.c Thu Jul 22 01:11:51 1993 @@ -22,8 +22,9 @@ #include "fpu_system.h" -void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w) +int reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w) { + char saved_sign = dest->sign; int diff; if ( !(a->tag | b->tag) ) @@ -32,9 +33,13 @@ if (!(a->sign ^ b->sign)) { /* signs are the same */ - reg_u_add(a, b, dest, control_w); dest->sign = a->sign; - return; + if ( reg_u_add(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + return 0; } /* The signs are different, so do a subtraction */ @@ -52,11 +57,20 @@ if (diff > 0) { - reg_u_sub(a, b, dest, control_w); dest->sign = a->sign; + if ( reg_u_sub(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } } else if ( diff == 0 ) { +#ifdef DENORM_OPERAND + if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND reg_move(&CONST_Z, dest); /* sign depends upon rounding mode */ dest->sign = ((control_w & CW_RC) != RC_DOWN) @@ -64,15 +78,19 @@ } else { - reg_u_sub(b, a, dest, control_w); dest->sign = b->sign; + if ( reg_u_sub(b, a, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } } - return; + return 0; } else { if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) - { real_2op_NaN(a, b, dest); return; } + { return real_2op_NaN(a, b, dest); } else if (a->tag == TW_Zero) { if (b->tag == TW_Zero) @@ -93,11 +111,11 @@ #ifdef DENORM_OPERAND if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand() ) - return; + return 1; #endif DENORM_OPERAND reg_move(b, dest); } - return; + return 0; } else if (b->tag == TW_Zero) { @@ -104,9 +122,9 @@ #ifdef DENORM_OPERAND if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && denormal_operand() ) - return; + return 1; #endif DENORM_OPERAND - reg_move(a, dest); return; + reg_move(a, dest); return 0; } else if (a->tag == TW_Infinity) { @@ -115,17 +133,16 @@ #ifdef DENORM_OPERAND if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand() ) - return; + return 1; #endif DENORM_OPERAND - reg_move(a, dest); return; + reg_move(a, dest); return 0; } if (a->sign == b->sign) { /* They are both + or - infinity */ - reg_move(a, dest); return; + reg_move(a, dest); return 0; } - arith_invalid(dest); /* Infinity-Infinity is undefined. */ - return; + return arith_invalid(dest); /* Infinity-Infinity is undefined. */ } else if (b->tag == TW_Infinity) { @@ -132,20 +149,22 @@ #ifdef DENORM_OPERAND if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && denormal_operand() ) - return; + return 1; #endif DENORM_OPERAND - reg_move(b, dest); return; + reg_move(b, dest); return 0; } } #ifdef PARANOID EXCEPTION(EX_INTERNAL|0x101); #endif + return 1; } /* Subtract b from a. (a-b) -> dest */ -void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w) +int reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w) { + char saved_sign = dest->sign; int diff; if ( !(a->tag | b->tag) ) @@ -162,7 +181,7 @@ diff = -(a->sigl < b->sigl); } } - + switch (a->sign*2 + b->sign) { case 0: /* P - P */ @@ -169,8 +188,14 @@ case 3: /* N - N */ if (diff > 0) { - reg_u_sub(a, b, dest, control_w); + /* |a| > |b| */ dest->sign = a->sign; + if ( reg_u_sub(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + return 0; } else if ( diff == 0 ) { @@ -177,7 +202,7 @@ #ifdef DENORM_OPERAND if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand() ) - return; + return 1; #endif DENORM_OPERAND reg_move(&CONST_Z, dest); /* sign depends upon rounding mode */ @@ -186,24 +211,37 @@ } else { - reg_u_sub(b, a, dest, control_w); dest->sign = a->sign ^ SIGN_POS^SIGN_NEG; + if ( reg_u_sub(b, a, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } } - return; + break; case 1: /* P - N */ - reg_u_add(a, b, dest, control_w); dest->sign = SIGN_POS; - return; + if ( reg_u_add(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + break; case 2: /* N - P */ - reg_u_add(a, b, dest, control_w); dest->sign = SIGN_NEG; - return; + if ( reg_u_add(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + break; } + return 0; } else { if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) - { real_2op_NaN(a, b, dest); return; } + { return real_2op_NaN(b, a, dest); } else if (b->tag == TW_Zero) { if (a->tag == TW_Zero) @@ -223,11 +261,11 @@ #ifdef DENORM_OPERAND if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && denormal_operand() ) - return; + return 1; #endif DENORM_OPERAND reg_move(a, dest); } - return; + return 0; } else if (a->tag == TW_Zero) { @@ -234,11 +272,11 @@ #ifdef DENORM_OPERAND if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand() ) - return; + return 1; #endif DENORM_OPERAND reg_move(b, dest); dest->sign ^= SIGN_POS^SIGN_NEG; - return; + return 0; } else if (a->tag == TW_Infinity) { @@ -247,18 +285,18 @@ #ifdef DENORM_OPERAND if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand() ) - return; + return 1; #endif DENORM_OPERAND - reg_move(a, dest); return; + reg_move(a, dest); return 0; } /* Both args are Infinity */ if (a->sign == b->sign) { - arith_invalid(dest); /* Infinity-Infinity is undefined. */ - return; + /* Infinity-Infinity is undefined. */ + return arith_invalid(dest); } reg_move(a, dest); - return; + return 0; } else if (b->tag == TW_Infinity) { @@ -265,15 +303,16 @@ #ifdef DENORM_OPERAND if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && denormal_operand() ) - return; + return 1; #endif DENORM_OPERAND reg_move(b, dest); dest->sign ^= SIGN_POS^SIGN_NEG; - return; + return 0; } } #ifdef PARANOID EXCEPTION(EX_INTERNAL|0x110); #endif + return 1; } diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/reg_compare.c linux/kernel/FPU-emu/reg_compare.c --- pl11/linux/kernel/FPU-emu/reg_compare.c Sat Jul 3 10:37:19 1993 +++ linux/kernel/FPU-emu/reg_compare.c Thu Jul 22 01:11:52 1993 @@ -32,11 +32,12 @@ if ( b->tag == TW_Zero ) return COMP_A_eq_B; if ( b->tag == TW_Valid ) { + return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) #ifdef DENORM_OPERAND - if ( (b->exp <= EXP_UNDER) && (denormal_operand()) ) - return COMP_Denormal; + | ((b->exp <= EXP_UNDER) ? + COMP_Denormal : 0) #endif DENORM_OPERAND - return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B ; + ; } } else if ( b->tag == TW_Zero ) @@ -43,11 +44,13 @@ { if ( FPU_st0_ptr->tag == TW_Valid ) { + return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B + : COMP_A_lt_B) #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return COMP_Denormal; + | ((FPU_st0_ptr->exp <= EXP_UNDER ) + ? COMP_Denormal : 0 ) #endif DENORM_OPERAND - return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B ; + ; } } @@ -55,12 +58,13 @@ { if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) ) { + return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B + : COMP_A_lt_B) #ifdef DENORM_OPERAND - if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) - && (denormal_operand()) ) - return COMP_Denormal; + | (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0 ) #endif DENORM_OPERAND - return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B; +; } else if ( b->tag == TW_Infinity ) { @@ -74,13 +78,13 @@ { if ( (FPU_st0_ptr->tag == TW_Valid) || (FPU_st0_ptr->tag == TW_Zero) ) { + return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->tag == TW_Valid) - && (FPU_st0_ptr->exp <= EXP_UNDER) - && (denormal_operand()) ) - return COMP_Denormal; + | (((FPU_st0_ptr->tag == TW_Valid) + && (FPU_st0_ptr->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) #endif DENORM_OPERAND - return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B; + ; } /* Fall through to the NaN code */ } @@ -106,15 +110,18 @@ if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid); #endif PARANOID -#ifdef DENORM_OPERAND - if ( ((FPU_st0_ptr->exp <= EXP_UNDER) || - (b->exp <= EXP_UNDER)) && (denormal_operand()) ) - return COMP_Denormal; -#endif DENORM_OPERAND if (FPU_st0_ptr->sign != b->sign) - return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B; - + { + return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) +#ifdef DENORM_OPERAND + | + ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + } + diff = FPU_st0_ptr->exp - b->exp; if ( diff == 0 ) { @@ -129,10 +136,33 @@ } if ( diff > 0 ) - return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B ; + { + return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) +#ifdef DENORM_OPERAND + | + ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + } if ( diff < 0 ) - return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B ; - return COMP_A_eq_B; + { + return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) +#ifdef DENORM_OPERAND + | + ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + } + + return COMP_A_eq_B +#ifdef DENORM_OPERAND + | + ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; } @@ -144,21 +174,13 @@ c = compare(&FPU_loaded_data); - if (c & (COMP_NaN|COMP_Denormal)) + if (c & COMP_NaN) { - if (c & COMP_NaN) - { - EXCEPTION(EX_Invalid); - f = SW_C3 | SW_C2 | SW_C0; - } - else - { - /* One of the operands is a de-normal */ - return 0; - } + EXCEPTION(EX_Invalid); + f = SW_C3 | SW_C2 | SW_C0; } else - switch (c) + switch (c & 7) { case COMP_A_lt_B: f = SW_C0; @@ -180,7 +202,11 @@ #endif PARANOID } setcc(f); - return 1; + if (c & COMP_Denormal) + { + return denormal_operand(); + } + return 0; } @@ -193,26 +219,18 @@ setcc(SW_C3 | SW_C2 | SW_C0); /* Stack fault */ EXCEPTION(EX_StackUnder); - return control_word & CW_Invalid; + return !(control_word & CW_Invalid); } c = compare(&st(nr)); - if (c & (COMP_NaN|COMP_Denormal)) + if (c & COMP_NaN) { - if (c & COMP_NaN) - { - setcc(SW_C3 | SW_C2 | SW_C0); - EXCEPTION(EX_Invalid); - return control_word & CW_Invalid; - } - else - { - /* One of the operands is a de-normal */ - return control_word & CW_Denormal; - } + setcc(SW_C3 | SW_C2 | SW_C0); + EXCEPTION(EX_Invalid); + return !(control_word & CW_Invalid); } else - switch (c) + switch (c & 7) { case COMP_A_lt_B: f = SW_C0; @@ -234,7 +252,11 @@ #endif PARANOID } setcc(f); - return 1; + if (c & COMP_Denormal) + { + return denormal_operand(); + } + return 0; } @@ -247,31 +269,23 @@ setcc(SW_C3 | SW_C2 | SW_C0); /* Stack fault */ EXCEPTION(EX_StackUnder); - return control_word & CW_Invalid; + return !(control_word & CW_Invalid); } c = compare(&st(nr)); - if (c & (COMP_NaN|COMP_Denormal)) + if (c & COMP_NaN) { - if (c & COMP_NaN) - { - setcc(SW_C3 | SW_C2 | SW_C0); - if (c & COMP_SNaN) /* This is the only difference between - un-ordered and ordinary comparisons */ - { - EXCEPTION(EX_Invalid); - return control_word & CW_Invalid; - } - return 1; - } - else + setcc(SW_C3 | SW_C2 | SW_C0); + if (c & COMP_SNaN) /* This is the only difference between + un-ordered and ordinary comparisons */ { - /* One of the operands is a de-normal */ - return control_word & CW_Denormal; + EXCEPTION(EX_Invalid); + return !(control_word & CW_Invalid); } + return 0; } else - switch (c) + switch (c & 7) { case COMP_A_lt_B: f = SW_C0; @@ -293,12 +307,16 @@ #endif PARANOID } setcc(f); - return 1; + if (c & COMP_Denormal) + { + return denormal_operand(); + } + return 0; } /*---------------------------------------------------------------------------*/ -void fcom_st(void) +void fcom_st() { /* fcom st(i) */ compare_st_st(FPU_rm); @@ -305,22 +323,23 @@ } -void fcompst(void) +void fcompst() { /* fcomp st(i) */ - if ( compare_st_st(FPU_rm) ) + if ( !compare_st_st(FPU_rm) ) pop(); } -void fcompp(void) +void fcompp() { /* fcompp */ - if (FPU_rm != 1) { - Un_impl(); - return; - } - if ( compare_st_st(1) ) + if (FPU_rm != 1) + { + Un_impl(); + return; + } + if ( !compare_st_st(1) ) { pop(); FPU_st0_ptr = &st(0); pop(); @@ -328,7 +347,7 @@ } -void fucom_(void) +void fucom_() { /* fucom st(i) */ compare_u_st_st(FPU_rm); @@ -336,20 +355,20 @@ } -void fucomp(void) +void fucomp() { /* fucomp st(i) */ - if ( compare_u_st_st(FPU_rm) ) + if ( !compare_u_st_st(FPU_rm) ) pop(); } -void fucompp(void) +void fucompp() { /* fucompp */ if (FPU_rm == 1) { - if ( compare_u_st_st(1) ) + if ( !compare_u_st_st(1) ) { pop(); FPU_st0_ptr = &st(0); pop(); diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/reg_constant.c linux/kernel/FPU-emu/reg_constant.c --- pl11/linux/kernel/FPU-emu/reg_constant.c Fri Apr 23 16:48:13 1993 +++ linux/kernel/FPU-emu/reg_constant.c Thu Jul 22 01:11:52 1993 @@ -37,8 +37,12 @@ FPU_REG CONST_LN2 = { SIGN_POS, TW_Valid, EXP_BIAS-1, 0xd1cf79ac, 0xb17217f7 }; +/* Extra bits to take pi/2 to more than 128 bits precision. */ +FPU_REG CONST_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66, + 0xfc8f8cbb, 0xece675d1 }; + /* Only the sign (and tag) is used in internal zeroes */ -FPU_REG CONST_Z = { SIGN_POS, TW_Zero, 0, 0x0, 0x0 }; +FPU_REG CONST_Z = { SIGN_POS, TW_Zero, EXP_UNDER, 0x0, 0x0 }; /* Only the sign and significand (and tag) are used in internal NaNs */ /* The 80486 never generates one of these @@ -63,7 +67,10 @@ } push(); reg_move(c, FPU_st0_ptr); - status_word &= ~SW_C1; +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + clear_C1(); +#endif PECULIAR_486 } diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/reg_constant.h linux/kernel/FPU-emu/reg_constant.h --- pl11/linux/kernel/FPU-emu/reg_constant.h Tue Nov 10 12:54:54 1992 +++ linux/kernel/FPU-emu/reg_constant.h Thu Jul 22 01:11:52 1993 @@ -18,6 +18,7 @@ extern FPU_REG CONST_L2E; extern FPU_REG CONST_PI; extern FPU_REG CONST_PI2; +extern FPU_REG CONST_PI2extra; extern FPU_REG CONST_PI4; extern FPU_REG CONST_LG2; extern FPU_REG CONST_LN2; diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/reg_div.S linux/kernel/FPU-emu/reg_div.S --- pl11/linux/kernel/FPU-emu/reg_div.S Tue Jun 1 21:59:59 1993 +++ linux/kernel/FPU-emu/reg_div.S Thu Jul 22 01:11:52 1993 @@ -86,8 +86,9 @@ L_arg1_NaN: L_arg2_NaN: pushl %edi // Destination - pushl %ebx +// pushl %ebx pushl %esi + pushl %ebx // Ordering is important here call _real_2op_NaN jmp LDiv_exit @@ -196,23 +197,18 @@ movl SIGH(%esi),%eax movl %eax,SIGH(%edi) +LDiv_set_result_sign: movb SIGN(%esi),%cl cmpb %cl,SIGN(%ebx) jne LDiv_negative_result movb SIGN_POS,SIGN(%edi) - jmp LDiv_exit - -LDiv_set_result_sign: - movb SIGN(%esi),%cl - cmpb %cl,SIGN(%edi) - jne LDiv_negative_result - - movb SIGN_POS,SIGN(%ebx) + xorl %eax,%eax // Valid result jmp LDiv_exit LDiv_negative_result: movb SIGN_NEG,SIGN(%edi) + xorl %eax,%eax // Valid result LDiv_exit: leal -12(%ebp),%esp @@ -225,6 +221,10 @@ L_return_zero: + xorl %eax,%eax + movl %eax,SIGH(%edi) + movl %eax,SIGL(%edi) + movl EXP_UNDER,EXP(%edi) movb TW_Zero,TAG(%edi) jmp LDiv_set_result_sign @@ -240,5 +240,5 @@ movl %eax,SIGL(%edi) movl _CONST_QNaN+8,%eax movl %eax,SIGH(%edi) - jmp LDiv_exit + jmp LDiv_exit // %eax is nz #endif PARANOID diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/reg_ld_str.c linux/kernel/FPU-emu/reg_ld_str.c --- pl11/linux/kernel/FPU-emu/reg_ld_str.c Sat Jul 3 10:38:46 1993 +++ linux/kernel/FPU-emu/reg_ld_str.c Thu Jul 22 01:11:52 1993 @@ -27,7 +27,6 @@ #include "status_w.h" -#define EXTENDED_Emax 0x3fff /* largest valid exponent */ #define EXTENDED_Ebias 0x3fff #define EXTENDED_Emin (-0x3ffe) /* smallest valid exponent */ @@ -39,14 +38,13 @@ #define SINGLE_Ebias 127 #define SINGLE_Emin (-126) /* smallest valid exponent */ -#define LOST_UP (EX_Precision | SW_C1) -#define LOST_DOWN EX_Precision +static void write_to_extended(FPU_REG *rp, char *d); FPU_REG FPU_loaded_data; /* Get a long double from user memory */ -void reg_load_extended(void) +int reg_load_extended(void) { long double *s = (long double *)FPU_data_address; unsigned long sigl, sigh, exp; @@ -59,63 +57,94 @@ exp = get_fs_word(4 + (unsigned short *) s); RE_ENTRANT_CHECK_ON + FPU_loaded_data.tag = TW_Valid; /* Default */ FPU_loaded_data.sigl = sigl; FPU_loaded_data.sigh = sigh; - FPU_loaded_data.exp = exp; - - if (FPU_loaded_data.exp & 0x8000) + if (exp & 0x8000) FPU_loaded_data.sign = SIGN_NEG; else FPU_loaded_data.sign = SIGN_POS; - if ( (FPU_loaded_data.exp &= 0x7fff) == 0 ) + exp &= 0x7fff; + FPU_loaded_data.exp = exp - EXTENDED_Ebias + EXP_BIAS; + + /* Assume that optimisation can keep sigl, sigh, and exp in + registers, otherwise it would be more efficient to work + with FPU_loaded_data (which is static) here. */ + if ( exp == 0 ) { - if ( !(FPU_loaded_data.sigl | FPU_loaded_data.sigh) ) + if ( !(sigh | sigl) ) { FPU_loaded_data.tag = TW_Zero; - return; + return 0; } /* The number is a de-normal or pseudodenormal. */ - /* The 80486 doesn't regard pseudodenormals as denormals here. */ - if ( !(FPU_loaded_data.sigh & 0x80000000) ) - EXCEPTION(EX_Denormal); - FPU_loaded_data.exp++; - - /* The default behaviour will now take care of it. */ - } - else if ( FPU_loaded_data.exp == 0x7fff ) - { - FPU_loaded_data.exp = EXTENDED_Emax; - if ( (FPU_loaded_data.sigh == 0x80000000) - && (FPU_loaded_data.sigl == 0) ) + if (sigh & 0x80000000) { - FPU_loaded_data.tag = TW_Infinity; - return; + /* Is a pseudodenormal. */ + /* Convert it for internal use. */ + /* This is non-80486 behaviour because the number + loses its 'denormal' identity. */ + FPU_loaded_data.exp++; + return 1; } - else if ( !(FPU_loaded_data.sigh & 0x80000000) ) + else { - /* Unsupported NaN data type */ - EXCEPTION(EX_Invalid); - FPU_loaded_data.tag = TW_NaN; - return; + /* Is a denormal. */ + /* Convert it for internal use. */ + FPU_loaded_data.exp++; + normalize_nuo(&FPU_loaded_data); + return 0; } + } + else if ( exp == 0x7fff ) + { + if ( !((sigh ^ 0x80000000) | sigl) ) + { + /* Matches the bit pattern for Infinity. */ + FPU_loaded_data.exp = EXP_Infinity; + FPU_loaded_data.tag = TW_Infinity; + return 0; + } + + FPU_loaded_data.exp = EXP_NaN; FPU_loaded_data.tag = TW_NaN; - return; + if ( !(sigh & 0x80000000) ) + { + /* NaNs have the ms bit set to 1. */ + /* This is therefore an Unsupported NaN data type. */ + /* This is non 80486 behaviour */ + /* This should generate an Invalid Operand exception + later, so we convert it to a SNaN */ + FPU_loaded_data.sigh = 0x80000000; + FPU_loaded_data.sigl = 0x00000001; + FPU_loaded_data.sign = SIGN_NEG; + return 1; + } + return 0; } - FPU_loaded_data.exp = (FPU_loaded_data.exp & 0x7fff) - EXTENDED_Ebias - + EXP_BIAS; - FPU_loaded_data.tag = TW_Valid; if ( !(sigh & 0x80000000) ) { - /* Unsupported data type */ - EXCEPTION(EX_Invalid); - normalize_nuo(&FPU_loaded_data); + /* Unsupported data type. */ + /* Valid numbers have the ms bit set to 1. */ + /* Unnormal. */ + /* Convert it for internal use. */ + /* This is non-80486 behaviour */ + /* This should generate an Invalid Operand exception + later, so we convert it to a SNaN */ + FPU_loaded_data.sigh = 0x80000000; + FPU_loaded_data.sigl = 0x00000001; + FPU_loaded_data.sign = SIGN_NEG; + FPU_loaded_data.exp = EXP_NaN; + FPU_loaded_data.tag = TW_NaN; + return 1; } + return 0; } /* Get a double from user memory */ -void reg_load_double(void) +int reg_load_double(void) { double *dfloat = (double *)FPU_data_address; int exp; @@ -138,19 +167,21 @@ if ((m64 == 0) && (l64 == 0)) { /* +- infinity */ - FPU_loaded_data.exp = EXTENDED_Emax; + FPU_loaded_data.sigh = 0x80000000; + FPU_loaded_data.sigl = 0x00000000; + FPU_loaded_data.exp = EXP_Infinity; FPU_loaded_data.tag = TW_Infinity; - return; + return 0; } else { /* Must be a signaling or quiet NaN */ - FPU_loaded_data.exp = EXTENDED_Emax; + FPU_loaded_data.exp = EXP_NaN; FPU_loaded_data.tag = TW_NaN; FPU_loaded_data.sigh = (m64 << 11) | 0x80000000; FPU_loaded_data.sigh |= l64 >> 21; FPU_loaded_data.sigl = l64 << 11; - return; + return 0; /* The calling function must look for NaNs */ } } else if ( exp < DOUBLE_Emin ) @@ -162,12 +193,11 @@ int c = FPU_loaded_data.sign; reg_move(&CONST_Z, &FPU_loaded_data); FPU_loaded_data.sign = c; - return; + return 0; } else { /* De-normal */ - EXCEPTION(EX_Denormal); FPU_loaded_data.exp = DOUBLE_Emin + EXP_BIAS; FPU_loaded_data.tag = TW_Valid; FPU_loaded_data.sigh = m64 << 11; @@ -174,7 +204,7 @@ FPU_loaded_data.sigh |= l64 >> 21; FPU_loaded_data.sigl = l64 << 11; normalize_nuo(&FPU_loaded_data); - return; + return denormal_operand(); } } else @@ -185,13 +215,13 @@ FPU_loaded_data.sigh |= l64 >> 21; FPU_loaded_data.sigl = l64 << 11; - return; + return 0; } } /* Get a float from user memory */ -void reg_load_single(void) +int reg_load_single(void) { float *single = (float *)FPU_data_address; unsigned m32; @@ -211,7 +241,7 @@ int c = FPU_loaded_data.sign; reg_move(&CONST_Z, &FPU_loaded_data); FPU_loaded_data.sign = c; - return; + return 0; } exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias; m32 = (m32 & 0x7fffff) << 8; @@ -218,13 +248,12 @@ if ( exp < SINGLE_Emin ) { /* De-normals */ - EXCEPTION(EX_Denormal); FPU_loaded_data.exp = SINGLE_Emin + EXP_BIAS; FPU_loaded_data.tag = TW_Valid; FPU_loaded_data.sigh = m32; FPU_loaded_data.sigl = 0; normalize_nuo(&FPU_loaded_data); - return; + return denormal_operand(); } else if ( exp > SINGLE_Emax ) { @@ -232,18 +261,20 @@ if ( m32 == 0 ) { /* +- infinity */ - FPU_loaded_data.exp = EXTENDED_Emax; + FPU_loaded_data.sigh = 0x80000000; + FPU_loaded_data.sigl = 0x00000000; + FPU_loaded_data.exp = EXP_Infinity; FPU_loaded_data.tag = TW_Infinity; - return; + return 0; } else { /* Must be a signaling or quiet NaN */ - FPU_loaded_data.exp = EXTENDED_Emax; + FPU_loaded_data.exp = EXP_NaN; FPU_loaded_data.tag = TW_NaN; FPU_loaded_data.sigh = m32 | 0x80000000; FPU_loaded_data.sigl = 0; - return; + return 0; /* The calling function must look for NaNs */ } } else @@ -252,6 +283,7 @@ FPU_loaded_data.sigh = m32 | 0x80000000; FPU_loaded_data.sigl = 0; FPU_loaded_data.tag = TW_Valid; + return 0; } } @@ -397,123 +429,37 @@ /* Put a long double into user memory */ int reg_store_extended(void) { + /* + The only exception raised by an attempt to store to an + extended format is the Invalid Stack exception, i.e. + attempting to store from an empty register. + */ long double *d = (long double *)FPU_data_address; - long e = FPU_st0_ptr->exp - EXP_BIAS + EXTENDED_Ebias; - unsigned short sign = FPU_st0_ptr->sign*0x8000; - unsigned long ls, ms; - - if ( FPU_st0_tag == TW_Valid ) - { - if ( e >= 0x7fff ) - { - EXCEPTION(EX_Overflow); /* Overflow */ - /* This is a special case: see sec 16.2.5.1 of the 80486 book */ - if ( control_word & EX_Overflow ) - { - /* Overflow to infinity */ - ls = 0; - ms = 0x80000000; - e = 0x7fff; - } - else - return 0; - } - else if ( e <= 0 ) - { - if ( e > -63 ) - { - /* Correctly format the de-normal */ - int precision_loss; - FPU_REG tmp; - - EXCEPTION(EX_Denormal); - reg_move(FPU_st0_ptr, &tmp); - tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 62 */ - if ( (precision_loss = round_to_int(&tmp)) ) - { - EXCEPTION(EX_Underflow | precision_loss); - /* This is a special case: see sec 16.2.5.1 of - the 80486 book */ - if ( !(control_word & EX_Underflow) ) - return 0; - } - e = 0; - ls = tmp.sigl; - ms = tmp.sigh; - } - else - { - /* ****** ??? This should not be possible */ - EXCEPTION(EX_Underflow); /* Underflow */ - /* This is a special case: see sec 16.2.5.1 of the 80486 book */ - if ( control_word & EX_Underflow ) - { - /* Underflow to zero */ - ls = 0; - ms = 0; - e = FPU_st0_ptr->sign == SIGN_POS ? 0x7fff : 0xffff; - } - else - return 0; - } - } - else - { - ls = FPU_st0_ptr->sigl; - ms = FPU_st0_ptr->sigh; - } - } - else if ( FPU_st0_tag == TW_Zero ) - { - ls = ms = 0; - e = 0; - } - else if ( FPU_st0_tag == TW_Infinity ) - { - ls = 0; - ms = 0x80000000; - e = 0x7fff; - } - else if ( FPU_st0_tag == TW_NaN ) - { - ls = FPU_st0_ptr->sigl; - ms = FPU_st0_ptr->sigh; - e = 0x7fff; - } - else if ( FPU_st0_tag == TW_Empty ) + if ( FPU_st0_tag != TW_Empty ) { - /* Empty register (stack underflow) */ - EXCEPTION(EX_StackUnder); - if ( control_word & EX_Invalid ) - { - /* The masked response */ - /* Put out the QNaN indefinite */ - ls = 0; - ms = 0xc0000000; - e = 0xffff; - } - else - return 0; + verify_area(VERIFY_WRITE, d, 10); + write_to_extended(FPU_st0_ptr, (char *) FPU_data_address); + return 1; + } + + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + if ( control_word & CW_Invalid ) + { + /* The masked response */ + /* Put out the QNaN indefinite */ + RE_ENTRANT_CHECK_OFF; + verify_area(VERIFY_WRITE,d,10); + put_fs_long(0, (unsigned long *) d); + put_fs_long(0xc0000000, 1 + (unsigned long *) d); + put_fs_word(0xffff, 4 + (short *) d); + RE_ENTRANT_CHECK_ON; + return 1; } else - { - /* We don't use TW_Denormal yet ... perhaps never! */ - EXCEPTION(EX_Invalid); - /* Store a NaN */ - e = 0x7fff; - ls = 1; - ms = 0x80000000; - } - RE_ENTRANT_CHECK_OFF - verify_area(VERIFY_WRITE,d,10); - put_fs_long(ls, (unsigned long *) d); - put_fs_long(ms, 1 + (unsigned long *) d); - put_fs_word((unsigned short)e | sign, 4 + (short *) d); - RE_ENTRANT_CHECK_ON + return 0; - return 1; - } @@ -522,6 +468,7 @@ { double *dfloat = (double *)FPU_data_address; unsigned long l[2]; + unsigned long increment = 0; /* avoid gcc warnings */ if (FPU_st0_tag == TW_Valid) { @@ -533,11 +480,19 @@ if ( exp < DOUBLE_Emin ) /* It may be a denormal */ { - /* Make a de-normal */ int precision_loss; - if ( exp <= -EXTENDED_Ebias ) - EXCEPTION(EX_Denormal); + /* A denormal will always underflow. */ +#ifndef PECULIAR_486 + /* An 80486 is supposed to be able to generate + a denormal exception here, but... */ + if ( FPU_st0_ptr->exp <= EXP_UNDER ) + { + /* Underflow has priority. */ + if ( control_word & CW_Underflow ) + denormal_operand(); + } +#endif PECULIAR_486 tmp.exp += -DOUBLE_Emin + 52; /* largest exp to be 51 */ @@ -548,18 +503,19 @@ /* This behaviour might be regarded as peculiar, it appears that the 80486 rounds to the dest precision, then converts to decide underflow. */ - if ( (tmp.sigh == 0x00100000) && (tmp.sigl == 0) && - (FPU_st0_ptr->sigl & 0x000007ff) ) - EXCEPTION(precision_loss); - else + if ( !((tmp.sigh == 0x00100000) && (tmp.sigl == 0) && + (FPU_st0_ptr->sigl & 0x000007ff)) ) #endif PECULIAR_486 { - EXCEPTION(EX_Underflow | precision_loss); + EXCEPTION(EX_Underflow); /* This is a special case: see sec 16.2.5.1 of the 80486 book */ - if ( !(control_word & EX_Underflow) ) + if ( !(control_word & CW_Underflow) ) return 0; } + EXCEPTION(precision_loss); + if ( !(control_word & CW_Precision) ) + return 0; } l[0] = tmp.sigl; l[1] = tmp.sigh; @@ -568,8 +524,6 @@ { if ( tmp.sigl & 0x000007ff ) { - unsigned long increment = 0; /* avoid gcc warnings */ - switch (control_word & CW_RC) { case RC_RND: @@ -629,15 +583,16 @@ { overflow: EXCEPTION(EX_Overflow); - /* This is a special case: see sec 16.2.5.1 of the 80486 book */ - if ( control_word & EX_Overflow ) - { - /* Overflow to infinity */ - l[0] = 0x00000000; /* Set to */ - l[1] = 0x7ff00000; /* + INF */ - } - else + if ( !(control_word & CW_Overflow) ) + return 0; + set_precision_flag_up(); + if ( !(control_word & CW_Precision) ) return 0; + + /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + /* Overflow to infinity */ + l[0] = 0x00000000; /* Set to */ + l[1] = 0x7ff00000; /* + INF */ } else { @@ -662,12 +617,13 @@ /* See if we can get a valid NaN from the FPU_REG */ l[0] = (FPU_st0_ptr->sigl >> 11) | (FPU_st0_ptr->sigh << 21); l[1] = ((FPU_st0_ptr->sigh >> 11) & 0xfffff); - if ( !(l[0] | l[1]) ) + if ( !(FPU_st0_ptr->sigh & 0x40000000) ) { - /* This case does not seem to be handled by the 80486 specs */ + /* It is a signalling NaN */ EXCEPTION(EX_Invalid); - /* Make the quiet NaN "real indefinite" */ - goto put_indefinite; + if ( !(control_word & CW_Invalid) ) + return 0; + l[1] |= (0x40000000 >> 11); } l[1] |= 0x7ff00000; } @@ -675,11 +631,10 @@ { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); - if ( control_word & EX_Invalid ) + if ( control_word & CW_Invalid ) { /* The masked response */ /* Put out the QNaN indefinite */ -put_indefinite: RE_ENTRANT_CHECK_OFF verify_area(VERIFY_WRITE,(void *)dfloat,8); put_fs_long(0, (unsigned long *) dfloat); @@ -690,14 +645,6 @@ else return 0; } -#if 0 /* TW_Denormal is not used yet, and probably won't be */ - else if (FPU_st0_tag == TW_Denormal) - { - /* Extended real -> double real will always underflow */ - l[0] = l[1] = 0; - EXCEPTION(EX_Underflow); - } -#endif if (FPU_st0_ptr->sign) l[1] |= 0x80000000; @@ -716,6 +663,7 @@ { float *single = (float *)FPU_data_address; long templ; + unsigned long increment = 0; /* avoid gcc warnings */ if (FPU_st0_tag == TW_Valid) { @@ -727,11 +675,19 @@ if ( exp < SINGLE_Emin ) { - /* Make a de-normal */ int precision_loss; - if ( exp <= -EXTENDED_Ebias ) - EXCEPTION(EX_Denormal); + /* A denormal will always underflow. */ +#ifndef PECULIAR_486 + /* An 80486 is supposed to be able to generate + a denormal exception here, but... */ + if ( FPU_st0_ptr->exp <= EXP_UNDER ) + { + /* Underflow has priority. */ + if ( control_word & CW_Underflow ) + denormal_operand(); + } +#endif PECULIAR_486 tmp.exp += -SINGLE_Emin + 23; /* largest exp to be 22 */ @@ -742,18 +698,19 @@ /* This behaviour might be regarded as peculiar, it appears that the 80486 rounds to the dest precision, then converts to decide underflow. */ - if ( (tmp.sigl == 0x00800000) && - ((FPU_st0_ptr->sigh & 0x000000ff) || FPU_st0_ptr->sigl) ) - EXCEPTION(precision_loss); - else + if ( !((tmp.sigl == 0x00800000) && + ((FPU_st0_ptr->sigh & 0x000000ff) || FPU_st0_ptr->sigl)) ) #endif PECULIAR_486 { - EXCEPTION(EX_Underflow | precision_loss); + EXCEPTION(EX_Underflow); /* This is a special case: see sec 16.2.5.1 of the 80486 book */ if ( !(control_word & EX_Underflow) ) return 0; } + EXCEPTION(precision_loss); + if ( !(control_word & EX_Precision) ) + return 0; } templ = tmp.sigl; } @@ -761,7 +718,6 @@ { if ( tmp.sigl | (tmp.sigh & 0x000000ff) ) { - unsigned long increment = 0; /* avoid gcc warnings */ unsigned long sigh = tmp.sigh; unsigned long sigl = tmp.sigl; @@ -819,14 +775,15 @@ { overflow: EXCEPTION(EX_Overflow); - /* This is a special case: see sec 16.2.5.1 of the 80486 book */ - if ( control_word & EX_Overflow ) - { - /* Overflow to infinity */ - templ = 0x7f800000; - } - else + if ( !(control_word & CW_Overflow) ) return 0; + set_precision_flag_up(); + if ( !(control_word & CW_Precision) ) + return 0; + + /* This is a special case: see sec 16.2.5.1 of the 80486 book. */ + /* Masked respose is overflow to infinity. */ + templ = 0x7f800000; } else templ |= ((exp+SINGLE_Ebias) & 0xff) << 23; @@ -844,12 +801,13 @@ { /* See if we can get a valid NaN from the FPU_REG */ templ = FPU_st0_ptr->sigh >> 8; - if ( !(templ & 0x3fffff) ) + if ( !(FPU_st0_ptr->sigh & 0x40000000) ) { - /* This case does not seem to be handled by the 80486 specs */ + /* It is a signalling NaN */ EXCEPTION(EX_Invalid); - /* Make the quiet NaN "real indefinite" */ - goto put_indefinite; + if ( !(control_word & CW_Invalid) ) + return 0; + templ |= (0x40000000 >> 8); } templ |= 0x7f800000; } @@ -861,7 +819,6 @@ { /* The masked response */ /* Put out the QNaN indefinite */ -put_indefinite: RE_ENTRANT_CHECK_OFF verify_area(VERIFY_WRITE,(void *)single,4); put_fs_long(0xffc00000, (unsigned long *) single); @@ -871,14 +828,6 @@ else return 0; } -#if 0 /* TW_Denormal is not used yet, and probably won't be */ - else if (FPU_st0_tag == TW_Denormal) - { - /* Extended real -> real will always underflow */ - templ = 0; - EXCEPTION(EX_Underflow); - } -#endif #ifdef PARANOID else { @@ -904,42 +853,48 @@ long long *d = (long long *)FPU_data_address; FPU_REG t; long long tll; + int precision_loss; if ( FPU_st0_tag == TW_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); - if ( control_word & EX_Invalid ) - { - /* The masked response */ - /* Put out the QNaN indefinite */ - goto put_indefinite; - } - else - return 0; + goto invalid_operand; + } + else if ( (FPU_st0_tag == TW_Infinity) || + (FPU_st0_tag == TW_NaN) ) + { + EXCEPTION(EX_Invalid); + goto invalid_operand; } reg_move(FPU_st0_ptr, &t); - round_to_int(&t); + precision_loss = round_to_int(&t); ((long *)&tll)[0] = t.sigl; ((long *)&tll)[1] = t.sigh; - if ( (t.sigh & 0x80000000) && - !((t.sigh == 0x80000000) && (t.sigl == 0) && (t.sign == SIGN_NEG)) ) + if ( (precision_loss == 1) || + ((t.sigh & 0x80000000) && + !((t.sigh == 0x80000000) && (t.sigl == 0) && + (t.sign == SIGN_NEG))) ) { EXCEPTION(EX_Invalid); /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + invalid_operand: if ( control_word & EX_Invalid ) { - /* Produce "indefinite" */ -put_indefinite: - ((long *)&tll)[1] = 0x80000000; - ((long *)&tll)[0] = 0; + /* Produce something like QNaN "indefinite" */ + tll = 0x8000000000000000LL; } else return 0; } - else if ( t.sign ) - tll = - tll; + else + { + if ( precision_loss ) + set_precision_flag(precision_loss); + if ( t.sign ) + tll = - tll; + } RE_ENTRANT_CHECK_OFF verify_area(VERIFY_WRITE,(void *)d,8); @@ -956,27 +911,23 @@ { long *d = (long *)FPU_data_address; FPU_REG t; + int precision_loss; if ( FPU_st0_tag == TW_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); - if ( control_word & EX_Invalid ) - { - /* The masked response */ - /* Put out the QNaN indefinite */ - RE_ENTRANT_CHECK_OFF - verify_area(VERIFY_WRITE,d,4); - put_fs_long(0x80000000, (unsigned long *) d); - RE_ENTRANT_CHECK_ON - return 1; - } - else - return 0; + goto invalid_operand; + } + else if ( (FPU_st0_tag == TW_Infinity) || + (FPU_st0_tag == TW_NaN) ) + { + EXCEPTION(EX_Invalid); + goto invalid_operand; } reg_move(FPU_st0_ptr, &t); - round_to_int(&t); + precision_loss = round_to_int(&t); if (t.sigh || ((t.sigl & 0x80000000) && !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG))) ) @@ -983,16 +934,22 @@ { EXCEPTION(EX_Invalid); /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + invalid_operand: if ( control_word & EX_Invalid ) { - /* Produce "indefinite" */ + /* Produce something like QNaN "indefinite" */ t.sigl = 0x80000000; } else return 0; } - else if ( t.sign ) - t.sigl = -(long)t.sigl; + else + { + if ( precision_loss ) + set_precision_flag(precision_loss); + if ( t.sign ) + t.sigl = -(long)t.sigl; + } RE_ENTRANT_CHECK_OFF verify_area(VERIFY_WRITE,d,4); @@ -1008,28 +965,23 @@ { short *d = (short *)FPU_data_address; FPU_REG t; - short ts; + int precision_loss; if ( FPU_st0_tag == TW_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); - if ( control_word & EX_Invalid ) - { - /* The masked response */ - /* Put out the QNaN indefinite */ - RE_ENTRANT_CHECK_OFF - verify_area(VERIFY_WRITE,d,2); - put_fs_word(0x8000, (unsigned short *) d); - RE_ENTRANT_CHECK_ON - return 1; - } - else - return 0; + goto invalid_operand; } + else if ( (FPU_st0_tag == TW_Infinity) || + (FPU_st0_tag == TW_NaN) ) + { + EXCEPTION(EX_Invalid); + goto invalid_operand; + } reg_move(FPU_st0_ptr, &t); - round_to_int(&t); + precision_loss = round_to_int(&t); if (t.sigh || ((t.sigl & 0xffff8000) && !((t.sigl == 0x8000) && (t.sign == SIGN_NEG))) ) @@ -1036,16 +988,22 @@ { EXCEPTION(EX_Invalid); /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + invalid_operand: if ( control_word & EX_Invalid ) { - /* Produce "indefinite" */ - ts = 0x8000; + /* Produce something like QNaN "indefinite" */ + t.sigl = 0x8000; } else return 0; } - else if ( t.sign ) - t.sigl = -t.sigl; + else + { + if ( precision_loss ) + set_precision_flag(precision_loss); + if ( t.sign ) + t.sigl = -t.sigl; + } RE_ENTRANT_CHECK_OFF verify_area(VERIFY_WRITE,d,2); @@ -1063,7 +1021,7 @@ FPU_REG t; unsigned long long ll; unsigned char b; - int i; + int i, precision_loss; unsigned char sign = (FPU_st0_ptr->sign == SIGN_NEG) ? 0x80 : 0; if ( FPU_st0_tag == TW_Empty ) @@ -1070,18 +1028,11 @@ { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); - if ( control_word & EX_Invalid ) - { - /* The masked response */ - /* Put out the QNaN indefinite */ - goto put_indefinite; - } - else - return 0; + goto invalid_operand; } reg_move(FPU_st0_ptr, &t); - round_to_int(&t); + precision_loss = round_to_int(&t); ll = *(unsigned long long *)(&t.sigl); /* Check for overflow, by comparing with 999999999999999999 decimal. */ @@ -1090,13 +1041,13 @@ { EXCEPTION(EX_Invalid); /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + invalid_operand: if ( control_word & EX_Invalid ) { -put_indefinite: - /* Produce "indefinite" */ + /* Produce the QNaN "indefinite" */ RE_ENTRANT_CHECK_OFF verify_area(VERIFY_WRITE,d,10); - put_fs_byte(0xff,(unsigned char *) d+7); + put_fs_byte(0xff,(unsigned char *) d+7); /* This byte undefined */ put_fs_byte(0xff,(unsigned char *) d+8); put_fs_byte(0xff,(unsigned char *) d+9); RE_ENTRANT_CHECK_ON @@ -1105,6 +1056,11 @@ else return 0; } + else if ( precision_loss ) + { + if ( set_precision_flag(precision_loss) ) + return 0; + } verify_area(VERIFY_WRITE,d,10); for ( i = 0; i < 9; i++) @@ -1132,7 +1088,6 @@ /* Overflow is signalled by a non-zero return value (in eax). In the case of overflow, the returned significand always has the the largest possible value */ -/* The value returned in eax is never actually needed :-) */ int round_to_int(FPU_REG *r) { char very_big; @@ -1164,7 +1119,7 @@ { if ( very_big ) return 1; /* overflow */ (*(long long *)(&r->sigl)) ++; - return LOST_UP; + return PRECISION_LOST_UP; } break; case RC_DOWN: @@ -1172,7 +1127,7 @@ { if ( very_big ) return 1; /* overflow */ (*(long long *)(&r->sigl)) ++; - return LOST_UP; + return PRECISION_LOST_UP; } break; case RC_UP: @@ -1180,7 +1135,7 @@ { if ( very_big ) return 1; /* overflow */ (*(long long *)(&r->sigl)) ++; - return LOST_UP; + return PRECISION_LOST_UP; } break; case RC_CHOP: @@ -1187,7 +1142,7 @@ break; } - return eax ? LOST_DOWN : 0; + return eax ? PRECISION_LOST_DOWN : 0; } @@ -1202,7 +1157,7 @@ RE_ENTRANT_CHECK_OFF control_word = get_fs_word((unsigned short *) s); - status_word = get_fs_word((unsigned short *) (s+4)); + partial_status = get_fs_word((unsigned short *) (s+4)); tag_word = get_fs_word((unsigned short *) (s+8)); ip_offset = get_fs_long((unsigned long *) (s+0x0c)); cs_selector = get_fs_long((unsigned long *) (s+0x10)); @@ -1210,7 +1165,12 @@ operand_selector = get_fs_long((unsigned long *) (s+0x18)); RE_ENTRANT_CHECK_ON - top = (status_word >> SW_Top_Shift) & 7; + top = (partial_status >> SW_Top_Shift) & 7; + + if ( partial_status & ~control_word & CW_Exceptions ) + partial_status |= (SW_Summary | SW_Backward); + else + partial_status &= ~(SW_Summary | SW_Backward); for ( i = 0; i < 8; i++ ) { @@ -1217,25 +1177,38 @@ tag = tag_word & 3; tag_word >>= 2; - switch ( tag ) - { - case 0: - regs[i].tag = TW_Valid; - break; - case 1: - regs[i].tag = TW_Zero; - break; - case 2: - regs[i].tag = TW_NaN; - break; - case 3: - regs[i].tag = TW_Empty; - break; - } + if ( tag == 3 ) + /* New tag is empty. Accept it */ + regs[i].tag = TW_Empty; + else if ( regs[i].tag == TW_Empty ) + { + /* Old tag is empty and new tag is not empty. New tag is determined + by old reg contents */ + if ( regs[i].exp == EXP_BIAS - EXTENDED_Ebias ) + { + if ( !(regs[i].sigl | regs[i].sigh) ) + regs[i].tag = TW_Zero; + else + regs[i].tag = TW_Valid; + } + else if ( regs[i].exp == 0x7fff + EXP_BIAS - EXTENDED_Ebias ) + { + if ( !((regs[i].sigh & ~0x80000000) | regs[i].sigl) ) + regs[i].tag = TW_Infinity; + else + regs[i].tag = TW_NaN; + } + else + regs[i].tag = TW_Valid; + } + /* Else old tag is not empty and new tag is not empty. Old tag + remains correct */ } - FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ - FPU_entry_eip = ip_offset; /* We want no net effect */ + /* Ensure that the values just loaded are not changed by + fix-up operations. */ + NO_NET_DATA_EFFECT; + NO_NET_INSTR_EFFECT; return s + 0x1c; } @@ -1245,35 +1218,24 @@ { int i, stnr; unsigned char tag; - unsigned short saved_status, saved_control; - char *s = (char *)fldenv(); + char *s = fldenv(); - saved_status = status_word; - saved_control = control_word; - control_word = 0x037f; /* Mask all interrupts while we load. */ for ( i = 0; i < 8; i++ ) { - /* load each register */ + /* Load each register. */ FPU_data_address = (void *)(s+i*10); reg_load_extended(); stnr = (i+top) & 7; - tag = regs[stnr].tag; /* derived from the loaded tag word */ + tag = regs[stnr].tag; /* Derived from the loaded tag word. */ reg_move(&FPU_loaded_data, ®s[stnr]); - if ( tag == TW_NaN ) - { - /* The current data is a special, i.e. NaN, unsupported, infinity, - or denormal */ - unsigned char t = regs[stnr].tag; /* derived from the new data */ - if ( /* (t == TW_Valid) || ****/ (t == TW_Zero) ) - regs[stnr].tag = TW_NaN; - } - else + if ( tag == TW_Empty ) /* The loaded data over-rides all other cases. */ regs[stnr].tag = tag; } - control_word = saved_control; - status_word = saved_status; - FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ + /* Reverse the effect which loading the registers had on the + data pointer */ + NO_NET_DATA_EFFECT; + } @@ -1287,9 +1249,6 @@ { switch ( tag = regs[i].tag ) { -#if 0 /* TW_Denormal is not used yet, and probably won't be */ - case TW_Denormal: -#endif case TW_Valid: if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) ) tag = 2; @@ -1301,7 +1260,7 @@ case TW_Empty: tag = 3; break; - /* TW_Valid and TW_Zero already have the correct value */ + /* TW_Zero already has the correct value */ } word <<= 2; word |= tag; @@ -1316,21 +1275,30 @@ verify_area(VERIFY_WRITE,d,28); -#if 0 /****/ - *(unsigned short *)&cs_selector = fpu_cs; - *(unsigned short *)&operand_selector = fpu_os; -#endif /****/ - RE_ENTRANT_CHECK_OFF +#ifdef PECULIAR_486 + /* An 80486 sets all the reserved bits to 1. */ + put_fs_long(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d); + put_fs_long(0xffff0000 | status_word(), (unsigned long *) (d+4)); + put_fs_long(0xffff0000 | tag_word(), (unsigned long *) (d+8)); +#else put_fs_word(control_word, (unsigned short *) d); - put_fs_word((status_word & ~SW_Top) | ((top&7) << SW_Top_Shift), - (unsigned short *) (d+4)); + put_fs_word(status_word(), (unsigned short *) (d+4)); put_fs_word(tag_word(), (unsigned short *) (d+8)); +#endif PECULIAR_486 put_fs_long(ip_offset, (unsigned long *) (d+0x0c)); - put_fs_long(cs_selector, (unsigned long *) (d+0x10)); + put_fs_long(cs_selector & ~0xf8000000, (unsigned long *) (d+0x10)); put_fs_long(data_operand_offset, (unsigned long *) (d+0x14)); +#ifdef PECULIAR_486 + /* An 80486 sets all the reserved bits to 1. */ + put_fs_long(0xffff0000 | operand_selector, (unsigned long *) (d+0x18)); +#else put_fs_long(operand_selector, (unsigned long *) (d+0x18)); +#endif PECULIAR_486 RE_ENTRANT_CHECK_ON + + control_word |= CW_Exceptions; + partial_status &= ~(SW_Summary | SW_Backward); return d + 0x1c; } @@ -1339,98 +1307,12 @@ void fsave(void) { char *d; - FPU_REG tmp, *rp; int i; - short e; d = fstenv(); verify_area(VERIFY_WRITE,d,80); for ( i = 0; i < 8; i++ ) - { - /* Store each register in the order: st(0), st(1), ... */ - rp = ®s[(top+i) & 7]; - - e = rp->exp - EXP_BIAS + EXTENDED_Ebias; - - if ( rp->tag == TW_Valid ) - { - if ( e >= 0x7fff ) - { - /* Overflow to infinity */ - RE_ENTRANT_CHECK_OFF - put_fs_long(0, (unsigned long *) (d+i*10)); - put_fs_long(0x80000000, (unsigned long *) (d+i*10+4)); - RE_ENTRANT_CHECK_ON - e = 0x7fff; - } - else if ( e <= 0 ) - { - if ( e > -63 ) - { - /* Make a de-normal */ - reg_move(rp, &tmp); - tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 62 */ - round_to_int(&tmp); - RE_ENTRANT_CHECK_OFF - put_fs_long(tmp.sigl, (unsigned long *) (d+i*10)); - put_fs_long(tmp.sigh, (unsigned long *) (d+i*10+4)); - RE_ENTRANT_CHECK_ON - } - else - { - /* Underflow to zero */ - RE_ENTRANT_CHECK_OFF - put_fs_long(0, (unsigned long *) (d+i*10)); - put_fs_long(0, (unsigned long *) (d+i*10+4)); - RE_ENTRANT_CHECK_ON - } - e = 0; - } - else - { - RE_ENTRANT_CHECK_OFF - put_fs_long(rp->sigl, (unsigned long *) (d+i*10)); - put_fs_long(rp->sigh, (unsigned long *) (d+i*10+4)); - RE_ENTRANT_CHECK_ON - } - } - else if ( rp->tag == TW_Zero ) - { - RE_ENTRANT_CHECK_OFF - put_fs_long(0, (unsigned long *) (d+i*10)); - put_fs_long(0, (unsigned long *) (d+i*10+4)); - RE_ENTRANT_CHECK_ON - e = 0; - } - else if ( rp->tag == TW_Infinity ) - { - RE_ENTRANT_CHECK_OFF - put_fs_long(0, (unsigned long *) (d+i*10)); - put_fs_long(0x80000000, (unsigned long *) (d+i*10+4)); - RE_ENTRANT_CHECK_ON - e = 0x7fff; - } - else if ( rp->tag == TW_NaN ) - { - RE_ENTRANT_CHECK_OFF - put_fs_long(rp->sigl, (unsigned long *) (d+i*10)); - put_fs_long(rp->sigh, (unsigned long *) (d+i*10+4)); - RE_ENTRANT_CHECK_ON - e = 0x7fff; - } - else if ( rp->tag == TW_Empty ) - { - /* just copy the reg */ - RE_ENTRANT_CHECK_OFF - put_fs_long(rp->sigl, (unsigned long *) (d+i*10)); - put_fs_long(rp->sigh, (unsigned long *) (d+i*10+4)); - RE_ENTRANT_CHECK_ON - } - e |= rp->sign == SIGN_POS ? 0 : 0x8000; - RE_ENTRANT_CHECK_OFF - put_fs_word(e, (unsigned short *) (d+i*10+8)); - RE_ENTRANT_CHECK_ON - } + write_to_extended(®s[(top + i) & 7], d + 10 * i); finit(); @@ -1437,3 +1319,67 @@ } /*===========================================================================*/ + +/* + A call to this function must be preceeded by a call to + verify_area() to verify access to the 10 bytes at d + */ +static void write_to_extended(FPU_REG *rp, char *d) +{ + long e; + FPU_REG tmp; + + e = rp->exp - EXP_BIAS + EXTENDED_Ebias; + +#ifdef PARANOID + switch ( rp->tag ) + { + case TW_Zero: + if ( rp->sigh | rp->sigl | e ) + EXCEPTION(EX_INTERNAL | 0x114); + break; + case TW_Infinity: + case TW_NaN: + if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) ) + EXCEPTION(EX_INTERNAL | 0x114); + break; + default: + if (e > 0x7fff || e < -63) + EXCEPTION(EX_INTERNAL | 0x114); + } +#endif PARANOID + + /* + All numbers except denormals are stored internally in a + format which is compatible with the extended real number + format. + */ + if ( e > 0 ) + { + /* just copy the reg */ + RE_ENTRANT_CHECK_OFF; + put_fs_long(rp->sigl, (unsigned long *) d); + put_fs_long(rp->sigh, (unsigned long *) (d + 4)); + RE_ENTRANT_CHECK_ON; + } + else + { + /* + The number is a de-normal stored as a normal using our + extra exponent range, or is Zero. + Convert it back to a de-normal, or leave it as Zero. + */ + reg_move(rp, &tmp); + tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 63 */ + round_to_int(&tmp); + e = 0; + RE_ENTRANT_CHECK_OFF; + put_fs_long(tmp.sigl, (unsigned long *) d); + put_fs_long(tmp.sigh, (unsigned long *) (d + 4)); + RE_ENTRANT_CHECK_ON; + } + e |= rp->sign == SIGN_POS ? 0 : 0x8000; + RE_ENTRANT_CHECK_OFF; + put_fs_word(e, (unsigned short *) (d + 8)); + RE_ENTRANT_CHECK_ON; +} diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/reg_mul.c linux/kernel/FPU-emu/reg_mul.c --- pl11/linux/kernel/FPU-emu/reg_mul.c Tue Jun 1 21:59:59 1993 +++ linux/kernel/FPU-emu/reg_mul.c Thu Jul 22 01:11:52 1993 @@ -21,16 +21,21 @@ /* This routine must be called with non-empty source registers */ -void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w) +int reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w) { + char saved_sign = dest->sign; char sign = (a->sign ^ b->sign); if (!(a->tag | b->tag)) { - /* This should be the most common case */ - reg_u_mul(a, b, dest, control_w); + /* Both regs Valid, this should be the most common case. */ dest->sign = sign; - return; + if ( reg_u_mul(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + return 0; } else if ((a->tag <= TW_Zero) && (b->tag <= TW_Zero)) { @@ -38,7 +43,7 @@ if ( ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) || ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER)) ) { - if ( denormal_operand() ) return; + if ( denormal_operand() ) return 1; } #endif DENORM_OPERAND /* Must have either both arguments == zero, or @@ -50,58 +55,50 @@ 80486 appears to behave this way... */ dest->sign = sign; #endif PECULIAR_486 - return; - } -#if 0 /* TW_Denormal is not used yet... perhaps never will be. */ - else if ((a->tag <= TW_Denormal) && (b->tag <= TW_Denormal)) - { - /* One or both arguments are de-normalized */ - /* Internal de-normalized numbers are not supported yet */ - EXCEPTION(EX_INTERNAL|0x105); - reg_move(&CONST_Z, dest); + return 0; } -#endif else { /* Must have infinities, NaNs, etc */ if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) - { real_2op_NaN(a, b, dest); return; } + { return real_2op_NaN(a, b, dest); } else if (a->tag == TW_Infinity) { if (b->tag == TW_Zero) - { arith_invalid(dest); return; } /* Zero*Infinity is invalid */ + { return arith_invalid(dest); } /* Zero*Infinity is invalid */ else { #ifdef DENORM_OPERAND if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand() ) - return; + return 1; #endif DENORM_OPERAND reg_move(a, dest); dest->sign = sign; } - return; + return 0; } else if (b->tag == TW_Infinity) { if (a->tag == TW_Zero) - { arith_invalid(dest); return; } /* Zero*Infinity is invalid */ + { return arith_invalid(dest); } /* Zero*Infinity is invalid */ else { #ifdef DENORM_OPERAND if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && denormal_operand() ) - return; + return 1; #endif DENORM_OPERAND reg_move(b, dest); dest->sign = sign; } - return; + return 0; } #ifdef PARANOID else { EXCEPTION(EX_INTERNAL|0x102); + return 1; } #endif PARANOID } diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/reg_round.S linux/kernel/FPU-emu/reg_round.S --- pl11/linux/kernel/FPU-emu/reg_round.S Tue Jun 1 21:59:59 1993 +++ linux/kernel/FPU-emu/reg_round.S Thu Jul 22 01:11:53 1993 @@ -16,6 +16,9 @@ | From C, call as: | | void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w) | | | + | For correct "up" and "down" rounding, the argument must have the correct | + | sign. | + | | +---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------+ @@ -70,8 +73,11 @@ #include "exception.h" #include "control_w.h" +/* Flags for FPU_bits_lost */ #define LOST_DOWN $1 #define LOST_UP $2 + +/* Flags for FPU_denormal */ #define DENORMAL $1 #define UNMASKED_UNDERFLOW $2 @@ -345,9 +351,9 @@ adcl $0,%eax LCheck_Round_Overflow: - jnc LRe_normalise /* Rounding done, no overflow */ + jnc LRe_normalise - /* Overflow, adjust the result (to 1.0) */ + /* Overflow, adjust the result (significand to 1.0) */ rcrl $1,%eax rcrl $1,%ebx incl EXP(%edi) @@ -372,9 +378,6 @@ je xL_precision_lost_down xL_no_precision_loss: - cmpl EXP_OVER,EXP(%edi) - jge L_overflow - /* store the result */ movb TW_Valid,TAG(%edi) @@ -382,6 +385,11 @@ movl %eax,SIGH(%edi) movl %ebx,SIGL(%edi) + xorl %eax,%eax // No errors detected. + + cmpl EXP_OVER,EXP(%edi) + jge L_overflow + FPU_Arith_exit: popl %ebx popl %edi @@ -419,9 +427,8 @@ movb DENORMAL,FPU_denormal pushl %ecx // Save - movl EXP(%edi),%ecx - subl EXP_UNDER+1,%ecx - negl %ecx + movl EXP_UNDER+1,%ecx + subl EXP(%edi),%ecx cmpl $64,%ecx /* shrd only works for 0..31 bits */ jnc xDenorm_shift_more_than_63 @@ -434,7 +441,7 @@ // Shift by [1..31] bits addl %ecx,EXP(%edi) orl %edx,%edx // extension - setne %ch + setne %ch // Save whether %edx is non-zero xorl %edx,%edx shrd %cl,%ebx,%edx shrd %cl,%eax,%ebx @@ -495,8 +502,6 @@ xUnmasked_underflow: - // Increase the exponent by the magic number - addl $(3*(1<<13)),EXP(%edi) movb UNMASKED_UNDERFLOW,FPU_denormal jmp xDenorm_done @@ -513,7 +518,20 @@ jne L_norm_bugged #endif PARANOID - orl %eax,%eax // ms bits +#ifdef PECULIAR_486 + // This implements a special feature of 80486 behaviour. + // Underflow will be signalled even if the number is + // not a denormal after rounding. + // This difference occurs only for masked underflow, and not + // in the unmasked case. + // Actual 80486 behaviour differs from this in some circumstances. + orl %eax,%eax // ms bits + js LNormalise_shift_done // Will be masked underflow +#endif PECULIAR_486 + + orl %eax,%eax // ms bits + js xL_Normalised // No longer a denormal + jnz LNormalise_shift_up_to_31 // Shift left 0 - 31 bits orl %ebx,%ebx @@ -558,6 +576,8 @@ popl %eax popl %eax +// Reduce the exponent to EXP_UNDER + movl EXP_UNDER,EXP(%edi) movb TW_Zero,TAG(%edi) jmp xL_Store_significand @@ -571,6 +591,16 @@ xSignal_underflow: + // The number may have been changed to a non-denormal + // by the rounding operations. + cmpl EXP_UNDER,EXP(%edi) + jle xDo_unmasked_underflow + + jmp xL_Normalised + +xDo_unmasked_underflow: + // Increase the exponent by the magic number + addl $(3*(1<<13)),EXP(%edi) push %eax pushl EX_Underflow call EXCEPTION @@ -585,17 +615,19 @@ pushl EX_INTERNAL|0x201 call EXCEPTION popl %ebx - jmp FPU_Arith_exit + jmp L_exception_exit L_norm_bugged: pushl EX_INTERNAL|0x216 call EXCEPTION popl %ebx - jmp FPU_Arith_exit + jmp L_exception_exit L_entry_bugged: pushl EX_INTERNAL|0x217 call EXCEPTION popl %ebx +L_exception_exit: + mov $1,%eax jmp FPU_Arith_exit #endif PARANOID diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/reg_u_add.S linux/kernel/FPU-emu/reg_u_add.S --- pl11/linux/kernel/FPU-emu/reg_u_add.S Tue Jun 1 21:59:59 1993 +++ linux/kernel/FPU-emu/reg_u_add.S Thu Jul 22 01:11:53 1993 @@ -82,8 +82,8 @@ L_accum_loaded: movl PARAM3,%edi /* destination */ - movb SIGN(%esi),%dl - movb %dl,SIGN(%edi) /* Copy the sign from the first arg */ +// movb SIGN(%esi),%dl +// movb %dl,SIGN(%edi) /* Copy the sign from the first arg */ movl EXP(%esi),%edx diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/reg_u_sub.S linux/kernel/FPU-emu/reg_u_sub.S --- pl11/linux/kernel/FPU-emu/reg_u_sub.S Tue Jun 1 21:59:59 1993 +++ linux/kernel/FPU-emu/reg_u_sub.S Thu Jul 22 01:11:53 1993 @@ -61,13 +61,11 @@ xOp2_not_denorm: #endif DENORM_OPERAND -// xorl %ecx,%ecx movl EXP(%esi),%ecx subl EXP(%edi),%ecx /* exp1 - exp2 */ #ifdef PARANOID /* source 2 is always smaller than source 1 */ -// jc L_bugged js L_bugged_1 testl $0x80000000,SIGH(%edi) /* The args are assumed to be be normalized */ @@ -87,8 +85,8 @@ movl PARAM3,%edi /* destination */ movl EXP(%esi),%edx movl %edx,EXP(%edi) /* Copy exponent to destination */ - movb SIGN(%esi),%dl - movb %dl,SIGN(%edi) /* Copy the sign from the first arg */ +// movb SIGN(%esi),%dl +// movb %dl,SIGN(%edi) /* Copy the sign from the first arg */ xorl %edx,%edx // register extension @@ -213,8 +211,8 @@ /* Shift left 64 bits */ subl $64,EXP(%edi) - movl %edx,%eax - jmp L_store + xchg %edx,%eax + jmp FPU_round L_must_be_zero: #ifdef PARANOID @@ -227,7 +225,7 @@ movl $0,EXP(%edi) /* exponent */ movl $0,SIGL(%edi) movl $0,SIGH(%edi) - jmp L_exit // Does not underflow + jmp L_exit // %eax contains zero L_shift_32: movl %ebx,%eax @@ -254,46 +252,36 @@ pushl EX_INTERNAL|0x206 call EXCEPTION pop %ebx - jmp L_exit + jmp L_error_exit L_bugged_2: pushl EX_INTERNAL|0x209 call EXCEPTION pop %ebx - jmp L_exit + jmp L_error_exit L_bugged_3: pushl EX_INTERNAL|0x210 call EXCEPTION pop %ebx - jmp L_exit + jmp L_error_exit L_bugged_4: pushl EX_INTERNAL|0x211 call EXCEPTION pop %ebx - jmp L_exit + jmp L_error_exit L_bugged: pushl EX_INTERNAL|0x212 call EXCEPTION pop %ebx - jmp L_exit + jmp L_error_exit #endif PARANOID -L_store: -/*------------------------------+ - | Store the result | - +------------------------------*/ - movl %eax,SIGH(%edi) - movl %ebx,SIGL(%edi) - - movb TW_Valid,TAG(%edi) /* Set the tags to TW_Valid */ - - cmpl EXP_UNDER,EXP(%edi) - jle L_underflow - +L_error_exit: + movl $1,%eax L_exit: popl %ebx popl %edi @@ -300,11 +288,3 @@ popl %esi leave ret - - -L_underflow: - push %edi - call _arith_underflow - pop %ebx - jmp L_exit - diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/status_w.h linux/kernel/FPU-emu/status_w.h --- pl11/linux/kernel/FPU-emu/status_w.h Sun May 23 14:48:36 1993 +++ linux/kernel/FPU-emu/status_w.h Thu Jul 22 01:11:53 1993 @@ -45,9 +45,14 @@ #define COMP_NaN 0x40 #define COMP_SNaN 0x80 +#define status_word() \ + ((partial_status & ~SW_Top & 0xffff) | ((top << SW_Top_Shift) & SW_Top)) #define setcc(cc) ({ \ - status_word &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \ - status_word |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); }) + partial_status &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \ + partial_status |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); }) + +/* Clear the SW_C1 bit, "other bits undefined" */ +#define clear_C1() { partial_status &= ~SW_C1; } #endif __ASSEMBLER__ diff -u --recursive --new-file pl11/linux/kernel/FPU-emu/version.h linux/kernel/FPU-emu/version.h --- pl11/linux/kernel/FPU-emu/version.h Sun May 23 14:48:36 1993 +++ linux/kernel/FPU-emu/version.h Thu Jul 22 01:11:53 1993 @@ -9,5 +9,5 @@ | | +---------------------------------------------------------------------------*/ -#define FPU_VERSION "wm-FPU-emu version BETA 1.4" +#define FPU_VERSION "wm-FPU-emu version BETA 1.5" diff -u --recursive --new-file pl11/linux/kernel/Makefile linux/kernel/Makefile --- pl11/linux/kernel/Makefile Sun Jul 4 22:05:31 1993 +++ linux/kernel/Makefile Tue Jul 20 01:35:31 1993 @@ -21,7 +21,7 @@ OBJS = sched.o sys_call.o traps.o irq.o dma.o fork.o \ panic.o printk.o vsprintf.o sys.o exit.o \ signal.o mktime.o ptrace.o ioport.o itimer.o \ - info.o + info.o ldt.o all: kernel.o kernelsubdirs diff -u --recursive --new-file pl11/linux/kernel/blk_drv/Makefile linux/kernel/blk_drv/Makefile --- pl11/linux/kernel/blk_drv/Makefile Fri Mar 19 17:48:03 1993 +++ linux/kernel/blk_drv/Makefile Sat Aug 14 23:47:22 1993 @@ -18,7 +18,7 @@ SUBDIRS = scsi -OBJS = xd.o hd.o ll_rw_blk.o floppy.o ramdisk.o genhd.o +OBJS = xd.o hd.o ll_rw_blk.o floppy.o ramdisk.o genhd.o cdu31a.o mcd.o all: blk_drv.a scsisubdirs diff -u --recursive --new-file pl11/linux/kernel/blk_drv/blk.h linux/kernel/blk_drv/blk.h --- pl11/linux/kernel/blk_drv/blk.h Tue Jul 6 02:01:51 1993 +++ linux/kernel/blk_drv/blk.h Sat Aug 14 23:47:22 1993 @@ -79,6 +79,8 @@ extern int * blksize_size[MAX_BLKDEV]; extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end); +extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end); +extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end); extern int is_read_only(int dev); extern void set_device_ro(int dev,int flag); @@ -165,6 +167,23 @@ #define DEVICE_NAME "xt disk" #define DEVICE_REQUEST do_xd_request #define DEVICE_NR(device) (MINOR(device) >> 6) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#elif (MAJOR_NR == 15) +/* CDU31A CD-ROM */ +#define DEVICE_NAME "CDU31A" +#define DEVICE_REQUEST do_cdu31a_request +#define DEVICE_NR(device) (MINOR(device)) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#elif (MAJOR_NR == 23) +/* MITSUMI CD-ROM */ +#define DEVICE_NAME "Mitsumi CD-ROM" +/* #define DEVICE_INTR do_mcd */ +#define DEVICE_REQUEST do_mcd_request +#define DEVICE_NR(device) (MINOR(device)) #define DEVICE_ON(device) #define DEVICE_OFF(device) diff -u --recursive --new-file pl11/linux/kernel/blk_drv/cdu31a.c linux/kernel/blk_drv/cdu31a.c --- pl11/linux/kernel/blk_drv/cdu31a.c +++ linux/kernel/blk_drv/cdu31a.c Tue Aug 3 19:31:11 1993 @@ -0,0 +1,1705 @@ +/* + * Sony CDU-31A CDROM interface device driver. + * + * Corey Minyard (minyard@wf-rch.cirr.com) + * + * Colossians 3:17 + * + * The Sony interface device driver handles Sony interface CDROM + * drives and provides a complete block-level interface as well as an + * ioctl() interface compatible with the Sun (as specified in + * include/linux/cdrom.h). With this interface, CDROMs can be + * accessed and standard audio CDs can be played back normally. + * + * This interface is (unfortunatly) a polled interface. This is + * because most Sony interfaces are set up with DMA and interrupts + * disables. Some (like mine) do not even have the capability to + * handle interrupts or DMA. For this reason you will see a lot of + * the following: + * + * retry_count = jiffies+ SONY_JIFFIES_TIMEOUT; + * while ((retry_count > jiffies) && (! +#ifdef CONFIG_CDU31A + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define MAJOR_NR 15 +#include "blk.h" + + +static unsigned short cdu31a_addresses[] = +{ + 0x340, /* Standard configuration Sony Interface */ + 0x1f88, /* Fusion CD-16 */ + 0x360, /* Secondary standard Sony Interface */ + 0x320, /* Secondary standard Sony Interface */ + 0x330, /* Secondary standard Sony Interface */ + 0 +}; + + +static int handle_sony_cd_attention(void); +static int read_subcode(void); +static void sony_get_toc(void); +static int scd_open(struct inode *inode, struct file *filp); + + +/* The base I/O address of the Sony Interface. This is a variable (not a + #define) so it can be easily changed via some future ioctl() */ +static unsigned short sony_cd_base_io = 0; + +/* + * The following are I/O addresses of the various registers for the drive. The + * comment for the base address also applies here. + */ +static volatile unsigned short sony_cd_cmd_reg; +static volatile unsigned short sony_cd_param_reg; +static volatile unsigned short sony_cd_write_reg; +static volatile unsigned short sony_cd_control_reg; +static volatile unsigned short sony_cd_status_reg; +static volatile unsigned short sony_cd_result_reg; +static volatile unsigned short sony_cd_read_reg; +static volatile unsigned short sony_cd_fifost_reg; + + +static int initialized = 0; /* Has the drive been initialized? */ +static int sony_disc_changed = 1; /* Has the disk been changed + since the last check? */ +static int sony_toc_read = 0; /* Has the table of contents been + read? */ +static int sony_spun_up = 0; /* Has the drive been spun up? */ +static unsigned int sony_buffer_size; /* Size in bytes of the read-ahead + buffer. */ +static unsigned int sony_buffer_sectors; /* Size (in 2048 byte records) of + the read-ahead buffer. */ +static unsigned int sony_usage = 0; /* How many processes have the + drive open. */ + +static volatile int sony_first_block = -1; /* First OS block (512 byte) in + the read-ahead buffer */ +static volatile int sony_last_block = -1; /* Last OS block (512 byte) in + the read-ahead buffer */ + +static struct s_sony_toc *sony_toc; /* Points to the table of + contents. */ +static struct s_sony_subcode * volatile last_sony_subcode; /* Points to the last + subcode address read */ +static unsigned char * volatile sony_buffer; /* Points to the read-ahead + buffer */ + +static volatile int sony_inuse = 0; /* Is the drive in use? Only one operation at a time + allowed */ + +static struct wait_queue * sony_wait = NULL; + +static struct task_struct *has_cd_task = NULL; /* The task that is currently using the + CDROM drive, or NULL if none. */ + +/* + * The audio status uses the values from read subchannel data as specified + * in include/linux/cdrom.h. + */ +static volatile int sony_audio_status = CDROM_AUDIO_NO_STATUS; + +/* + * The following are a hack for pausing and resuming audio play. The drive + * does not work as I would expect it, if you stop it then start it again, + * the drive seeks back to the beginning and starts over. This holds the + * position during a pause so a resume can restart it. It uses the + * audio status variable above to tell if it is paused. + */ +unsigned volatile char cur_pos_msf[3] = { 0, 0, 0 }; +unsigned volatile char final_pos_msf[3] = { 0, 0, 0 }; + +/* + * This routine returns 1 if the disk has been changed since the last + * check or 0 if it hasn't. Setting flag to 0 resets the changed flag. + */ +int +check_cdu31a_media_change(int full_dev, int flag) +{ + int retval, target; + + + target = MINOR(full_dev); + + if (target > 0) { + printk("Sony CD-ROM request error: invalid device.\n"); + return 0; + } + + retval = sony_disc_changed; + if (!flag) + { + sony_disc_changed = 0; + } + + return retval; +} + + +/* + * Wait a little while (used for polling the drive). If in initialization, + * setting a timeout doesn't work, so just loop for a while. + */ +static inline void +sony_sleep(void) +{ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies; + schedule(); +} + + +/* + * The following are convenience routine to read various status and set + * various conditions in the drive. + */ +static inline int +is_attention(void) +{ + return((inb(sony_cd_status_reg) & SONY_ATTN_BIT) != 0); +} + +static inline int +is_busy(void) +{ + return((inb(sony_cd_status_reg) & SONY_BUSY_BIT) != 0); +} + +static inline int +is_data_ready(void) +{ + return((inb(sony_cd_status_reg) & SONY_DATA_RDY_BIT) != 0); +} + +static inline int +is_data_requested(void) +{ + return((inb(sony_cd_status_reg) & SONY_DATA_REQUEST_BIT) != 0); +} + +static inline int +is_result_ready(void) +{ + return((inb(sony_cd_status_reg) & SONY_RES_RDY_BIT) != 0); +} + +static inline int +is_param_write_rdy(void) +{ + return((inb(sony_cd_fifost_reg) & SONY_PARAM_WRITE_RDY_BIT) != 0); +} + +static inline void +reset_drive(void) +{ + outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg); +} + +static inline void +clear_attention(void) +{ + outb(SONY_ATTN_CLR_BIT, sony_cd_control_reg); +} + +static inline void +clear_result_ready(void) +{ + outb(SONY_RES_RDY_CLR_BIT, sony_cd_control_reg); +} + +static inline void +clear_data_ready(void) +{ + outb(SONY_DATA_RDY_CLR_BIT, sony_cd_control_reg); +} + +static inline void +clear_param_reg(void) +{ + outb(SONY_PARAM_CLR_BIT, sony_cd_control_reg); +} + +static inline unsigned char +read_status_register(void) +{ + return(inb(sony_cd_status_reg)); +} + +static inline unsigned char +read_result_register(void) +{ + return(inb(sony_cd_result_reg)); +} + +static inline unsigned char +read_data_register(void) +{ + return(inb(sony_cd_read_reg)); +} + +static inline void +write_param(unsigned char param) +{ + outb(param, sony_cd_param_reg); +} + +static inline void +write_cmd(unsigned char cmd) +{ + outb(cmd, sony_cd_cmd_reg); + outb(SONY_RES_RDY_INT_EN_BIT, sony_cd_control_reg); +} + + +/* + * This routine writes data to the parameter register. Since this should + * happen fairly fast, it is polled with no OS waits between. + */ +static int +write_params(unsigned char *params, + int num_params) +{ + unsigned int retry_count; + + + retry_count = SONY_READY_RETRIES; + while ((retry_count > 0) && (!is_param_write_rdy())) + { + retry_count--; + } + if (!is_param_write_rdy()) + { + return -EIO; + } + + while (num_params > 0) + { + write_param(*params); + params++; + num_params--; + } + + return 0; +} + + +/* + * The following reads data from the command result register. It is a + * fairly complex routine, all status info flows back through this + * interface. The algorithm is stolen directly from the flowcharts in + * the drive manual. + */ +static void +get_result(unsigned char *result_buffer, + unsigned int *result_size) +{ + unsigned char a, b; + int i; + unsigned int retry_count; + + + while (handle_sony_cd_attention()) + ; + /* Wait for the result data to be ready */ + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while ((retry_count > jiffies) && (is_busy() || (!(is_result_ready())))) + { + sony_sleep(); + + while (handle_sony_cd_attention()) + ; + } + if (is_busy() || (!(is_result_ready()))) + { + result_buffer[0] = 0x20; + result_buffer[1] = SONY_TIMEOUT_OP_ERR; + *result_size = 2; + return; + } + + /* + * Get the first two bytes. This determines what else needs + * to be done. + */ + clear_result_ready(); + a = read_result_register(); + *result_buffer = a; + result_buffer++; + b = read_result_register(); + *result_buffer = b; + result_buffer++; + *result_size = 2; + + /* + * 0x20 means an error occured. Byte 2 will have the error code. + * Otherwise, the command succeded, byte 2 will have the count of + * how many more status bytes are coming. + * + * The result register can be read 10 bytes at a time, a wait for + * result ready to be asserted must be done between every 10 bytes. + */ + if ((a & 0xf0) != 0x20) + { + if (b > 8) + { + for (i=0; i<8; i++) + { + *result_buffer = read_result_register(); + result_buffer++; + (*result_size)++; + } + b = b - 8; + + while (b > 10) + { + retry_count = SONY_READY_RETRIES; + while ((retry_count > 0) && (!is_result_ready())) + { + retry_count--; + } + if (!is_result_ready()) + { + result_buffer[0] = 0x20; + result_buffer[1] = SONY_TIMEOUT_OP_ERR; + *result_size = 2; + return; + } + + clear_result_ready(); + + for (i=0; i<10; i++) + { + *result_buffer = read_result_register(); + result_buffer++; + (*result_size)++; + } + b = b - 10; + } + + if (b > 0) + { + retry_count = SONY_READY_RETRIES; + while ((retry_count > 0) && (!is_result_ready())) + { + retry_count--; + } + if (!is_result_ready()) + { + result_buffer[0] = 0x20; + result_buffer[1] = SONY_TIMEOUT_OP_ERR; + *result_size = 2; + return; + } + } + } + + while (b > 0) + { + *result_buffer = read_result_register(); + result_buffer++; + (*result_size)++; + b--; + } + } +} + + +/* + * This routine issues a read data command and gets the data. I don't + * really like the way this is done (I would prefer for do_sony_cmd() to + * handle it automatically) but I found that the drive returns status + * when it finishes reading (not when the host has read all the data) + * or after it gets an error. This means that the status can be + * received at any time and should be handled immediately (at least + * between every 2048 byte block) to check for errors, we can't wait + * until all the data is read. + */ +static void +get_data(unsigned char *data, + unsigned char *params, /* 6 bytes with the MSF start address + and number of sectors to read. */ + unsigned int data_size, + unsigned char *result_buffer, + unsigned int *result_size) +{ + int i; + unsigned int cur_offset; + unsigned int retry_count; + int result_read; + int num_retries; + + + cli(); + if (current != has_cd_task) /* Allow recursive calls to this routine */ + { + while (sony_inuse) + { + interruptible_sleep_on(&sony_wait); + if (current->signal & ~current->blocked) + { + result_buffer[0] = 0x20; + result_buffer[1] = SONY_SIGNAL_OP_ERR; + *result_size = 2; + return; + } + } + } + sony_inuse = 1; + has_cd_task = current; + sti(); + + num_retries = 0; +retry_data_operation: + + /* + * Clear any outstanding attentions and wait for the drive to + * complete any pending operations. + */ + while (handle_sony_cd_attention()) + ; + + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while ((retry_count > jiffies) && (is_busy())) + { + sony_sleep(); + + while (handle_sony_cd_attention()) + ; + } + if (is_busy()) + { + result_buffer[0] = 0x20; + result_buffer[1] = SONY_TIMEOUT_OP_ERR; + *result_size = 2; + goto get_data_end; + } + + /* Issue the command */ + clear_result_ready(); + clear_param_reg(); + + write_params(params, 6); + write_cmd(SONY_READ_CMD); + + /* + * Read the data from the drive one 2048 byte sector at a time. Handle + * any results received between sectors, if an error result is returned + * terminate the operation immediately. + */ + cur_offset = 0; + result_read = 0; + while (data_size > 0) + { + /* Wait for the drive to tell us we have something */ + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while ((retry_count > jiffies) && (!(is_result_ready() || is_data_ready()))) + { + while (handle_sony_cd_attention()) + ; + + sony_sleep(); + } + if (!(is_result_ready() || is_data_ready())) + { + result_buffer[0] = 0x20; + result_buffer[1] = SONY_TIMEOUT_OP_ERR; + *result_size = 2; + goto get_data_end; + } + + /* Handle results first */ + if (is_result_ready()) + { + result_read = 1; + get_result(result_buffer, result_size); + if ((*result_size < 2) || (result_buffer[0] != 0)) + { + goto get_data_end; + } + } + else /* Handle data next */ + { + /* + * The drive has to be polled for status on a byte-by-byte basis + * to know if the data is ready. Yuck. I really wish I could use DMA. + */ + clear_data_ready(); + for (i=0; i<2048; i++) + { + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while ((retry_count > jiffies) && (!is_data_requested())) + { + while (handle_sony_cd_attention()) + ; + + sony_sleep(); + } + if (!is_data_requested()) + { + result_buffer[0] = 0x20; + result_buffer[1] = SONY_TIMEOUT_OP_ERR; + *result_size = 2; + goto get_data_end; + } + + *data = read_data_register(); + data++; + data_size--; + } + cur_offset = cur_offset + 2048; + } + } + + /* Make sure the result has been read */ + if (!result_read) + { + get_result(result_buffer, result_size); + } + +get_data_end: + if ( ((result_buffer[0] & 0x20) == 0x20) + && (num_retries < MAX_CDU31A_RETRIES)) + { + num_retries++; + goto retry_data_operation; + } + + has_cd_task = NULL; + sony_inuse = 0; + wake_up_interruptible(&sony_wait); +} + + +/* + * Do a command that does not involve data transfer. + */ +static void +do_sony_cd_cmd(unsigned char cmd, + unsigned char *params, + unsigned int num_params, + unsigned char *result_buffer, + unsigned int *result_size) +{ + unsigned int retry_count; + int num_retries; + + + cli(); + if (current != has_cd_task) /* Allow recursive calls to this routine */ + { + while (sony_inuse) + { + interruptible_sleep_on(&sony_wait); + if (current->signal & ~current->blocked) + { + result_buffer[0] = 0x20; + result_buffer[1] = SONY_SIGNAL_OP_ERR; + *result_size = 2; + return; + } + } + } + sony_inuse = 1; + has_cd_task = current; + sti(); + + num_retries = 0; +retry_cd_operation: + + while (handle_sony_cd_attention()) + ; + + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while ((retry_count > jiffies) && (is_busy())) + { + sony_sleep(); + + while (handle_sony_cd_attention()) + ; + } + if (is_busy()) + { + result_buffer[0] = 0x20; + result_buffer[1] = SONY_TIMEOUT_OP_ERR; + *result_size = 2; + goto do_cmd_end; + } + + clear_result_ready(); + clear_param_reg(); + + write_params(params, num_params); + write_cmd(cmd); + + get_result(result_buffer, result_size); + +do_cmd_end: + if ( ((result_buffer[0] & 0x20) == 0x20) + && (num_retries < MAX_CDU31A_RETRIES)) + { + num_retries++; + goto retry_cd_operation; + } + + has_cd_task = NULL; + sony_inuse = 0; + wake_up_interruptible(&sony_wait); +} + + +/* + * Handle an attention from the drive. This will return 1 if it found one + * or 0 if not (if one is found, the caller might want to call again). + */ +static int +handle_sony_cd_attention(void) +{ + unsigned char atten_code; + unsigned char res_reg[2]; + unsigned int res_size; + + + if (is_attention()) + { + clear_attention(); + atten_code = read_result_register(); + + switch (atten_code) + { + /* Someone changed the CD. Mark it as changed */ + case SONY_MECH_LOADED_ATTN: + sony_disc_changed = 1; + sony_toc_read = 0; + sony_audio_status = CDROM_AUDIO_NO_STATUS; + sony_first_block = -1; + sony_last_block = -1; + if (initialized) + { + do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + sony_get_toc(); + } + break; + + case SONY_AUDIO_PLAY_DONE_ATTN: + sony_audio_status = CDROM_AUDIO_COMPLETED; + read_subcode(); + break; + + case SONY_EJECT_PUSHED_ATTN: + sony_audio_status = CDROM_AUDIO_INVALID; + break; + + case SONY_LEAD_IN_ERR_ATTN: + case SONY_LEAD_OUT_ERR_ATTN: + case SONY_DATA_TRACK_ERR_ATTN: + case SONY_AUDIO_PLAYBACK_ERR_ATTN: + sony_audio_status = CDROM_AUDIO_ERROR; + break; + } + return(1); + } + + return(0); +} + + +/* Convert from an integer 0-99 to BCD */ +static inline unsigned int +int_to_bcd(unsigned int val) +{ + int retval; + + + retval = (val / 10) << 4; + retval = retval | val % 10; + return(retval); +} + + +/* Convert from BCD to an integer from 0-99 */ +static unsigned int +bcd_to_int(unsigned int bcd) +{ + return((((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f)); +} + + +/* + * Convert a logical sector value (like the OS would want to use for + * a block device) to an MSF format. + */ +static void +log_to_msf(unsigned int log, unsigned char *msf) +{ + log = log + LOG_START_OFFSET; + msf[0] = int_to_bcd(log / 4500); + log = log % 4500; + msf[1] = int_to_bcd(log / 75); + msf[2] = int_to_bcd(log % 75); +} + + +/* + * Convert an MSF format to a logical sector. + */ +static unsigned int +msf_to_log(unsigned char *msf) +{ + unsigned int log; + + + log = bcd_to_int(msf[2]); + log += bcd_to_int(msf[1]) * 75; + log += bcd_to_int(msf[0]) * 4500; + log = log - LOG_START_OFFSET; + + return log; +} + + +/* + * Take in integer size value and put it into a buffer like + * the drive would want to see a number-of-sector value. + */ +static void +size_to_buf(unsigned int size, + unsigned char *buf) +{ + buf[0] = size / 65536; + size = size % 65536; + buf[1] = size / 256; + buf[2] = size % 256; +} + + +/* + * The OS calls this to perform a read or write operation to the drive. + * Write obviously fail. Reads to a read ahead of sony_buffer_size + * bytes to help speed operations. This especially helps since the OS + * uses 1024 byte blocks and the drive uses 2048 byte blocks. Since most + * data access on a CD is done sequentially, this saves a lot of operations. + */ +static void +do_cdu31a_request(void) +{ + int block; + unsigned int dev; + int nsect; + unsigned char params[10]; + unsigned char res_reg[2]; + unsigned int res_size; + int copyoff; + int spin_up_retry; + unsigned int read_size; + + + if (!sony_spun_up) + { + scd_open (NULL,NULL); + } + + while (1) + { + /* + * The beginning here is stolen from the hard disk driver. I hope + * its right. + */ + if (!(CURRENT) || CURRENT->dev < 0) + { + return; + } + + INIT_REQUEST; + dev = MINOR(CURRENT->dev); + block = CURRENT->sector; + nsect = CURRENT->nr_sectors; + if (dev != 0) + { + end_request(0); + continue; + } + + switch(CURRENT->cmd) + { + case READ: + /* + * If the block address is invalid or the request goes beyond the end of + * the media, return an error. + */ + if ((block / 4) >= sony_toc->lead_out_start_lba) + { + end_request(0); + return; + } + if (((block + nsect) / 4) >= sony_toc->lead_out_start_lba) + { + end_request(0); + return; + } + + while (nsect > 0) + { + /* + * If the requested sector is not currently in the read-ahead buffer, + * it must be read in. + */ + if ((block < sony_first_block) || (block > sony_last_block)) + { + sony_first_block = (block / 4) * 4; + log_to_msf(block/4, params); + + /* + * If the full read-ahead would go beyond the end of the media, trim + * it back to read just till the end of the media. + */ + if (((block / 4) + sony_buffer_sectors) >= sony_toc->lead_out_start_lba) + { + sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1; + read_size = sony_toc->lead_out_start_lba - (block / 4); + } + else + { + sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1; + read_size = sony_buffer_sectors; + } + size_to_buf(read_size, ¶ms[3]); + + /* + * Read the data. If the drive was not spinning, spin it up and try + * once more. I know, the goto is ugly, but I am too lazy to fix it. + */ + spin_up_retry = 0; +try_read_again: + get_data(sony_buffer, params, (read_size * 2048), res_reg, &res_size); + if ((res_size < 2) || (res_reg[0] != 0)) + { + if ((res_reg[1] == SONY_NOT_SPIN_ERR) && (!spin_up_retry)) + { + do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + spin_up_retry = 1; + goto try_read_again; + } + + printk("Sony CDROM Read error: 0x%2.2x\n", res_reg[1]); + sony_first_block = -1; + sony_last_block = -1; + end_request(0); + return; + } + } + + /* + * The data is in memory now, copy it to the buffer and advance to the + * next block to read. + */ + copyoff = (block - sony_first_block) * 512; + memcpy(CURRENT->buffer, sony_buffer+copyoff, 512); + + block += 1; + nsect -= 1; + CURRENT->buffer += 512; + } + + end_request(1); + break; + + case WRITE: + end_request(0); + break; + + default: + panic("Unkown SONY CD cmd"); + } + } +} + + +/* + * Read the table of contents from the drive and set sony_toc_read if + * successful. + */ +static void +sony_get_toc(void) +{ + unsigned int res_size; + + + if (!sony_toc_read) + { + do_sony_cd_cmd(SONY_REQ_TOC_DATA_CMD, + NULL, + 0, + (unsigned char *) sony_toc, + &res_size); + if ((res_size < 2) || ((sony_toc->exec_status[0] & 0x20) == 0x20)) + { + return; + } + sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf); + sony_toc_read = 1; + } +} + + +/* + * Search for a specific track in the table of contents. + */ +static int +find_track(int track) +{ + int i; + int num_tracks; + + + num_tracks = sony_toc->last_track_num + sony_toc->first_track_num + 1; + for (i = 0; i < num_tracks; i++) + { + if (sony_toc->tracks[i].track == track) + { + return i; + } + } + + return -1; +} + + +/* + * Read the subcode and put it int last_sony_subcode for future use. + */ +static int +read_subcode(void) +{ + unsigned int res_size; + + + do_sony_cd_cmd(SONY_REQ_SUBCODE_ADDRESS_CMD, + NULL, + 0, + (unsigned char *) last_sony_subcode, + &res_size); + if ((res_size < 2) || ((last_sony_subcode->exec_status[0] & 0x20) == 0x20)) + { + printk("Sony CDROM error 0x%2.2x (read_subcode)\n", + last_sony_subcode->exec_status[1]); + return -EIO; + } + + return 0; +} + + +/* + * Get the subchannel info like the CDROMSUBCHNL command wants to see it. If + * the drive is playing, the subchannel needs to be read (since it would be + * changing). If the drive is paused or completed, the subcode information has + * already been stored, just use that. The ioctl call wants things in decimal + * (not BCD), so all the conversions are done. + */ +static int +sony_get_subchnl_info(long arg) +{ + struct cdrom_subchnl schi; + + + /* Get attention stuff */ + while (handle_sony_cd_attention()) + ; + + sony_get_toc(); + if (!sony_toc_read) + { + return -EIO; + } + + verify_area(VERIFY_READ, (char *) arg, sizeof(schi)); + verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi)); + + memcpy_fromfs(&schi, (char *) arg, sizeof(schi)); + + switch (sony_audio_status) + { + case CDROM_AUDIO_PLAY: + if (read_subcode() < 0) + { + return -EIO; + } + break; + + case CDROM_AUDIO_PAUSED: + case CDROM_AUDIO_COMPLETED: + break; + + case CDROM_AUDIO_NO_STATUS: + schi.cdsc_audiostatus = sony_audio_status; + memcpy_tofs((char *) arg, &schi, sizeof(schi)); + return 0; + break; + + case CDROM_AUDIO_INVALID: + case CDROM_AUDIO_ERROR: + default: + return -EIO; + } + + schi.cdsc_audiostatus = sony_audio_status; + schi.cdsc_adr = last_sony_subcode->address; + schi.cdsc_ctrl = last_sony_subcode->control; + schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num); + schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num); + if (schi.cdsc_format == CDROM_MSF) + { + schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]); + schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]); + schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]); + + schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]); + schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]); + schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]); + } + else if (schi.cdsc_format == CDROM_LBA) + { + schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf); + schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf); + } + + memcpy_tofs((char *) arg, &schi, sizeof(schi)); + return 0; +} + + +/* + * The big ugly ioctl handler. + */ +static int +scd_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned int arg) +{ + unsigned int dev; + unsigned char res_reg[2]; + unsigned int res_size; + unsigned char params[7]; + int i; + + + if (!inode) + { + return -EINVAL; + } + dev = MINOR(inode->i_rdev) >> 6; + if (dev != 0) + { + return -EINVAL; + } + + switch (cmd) + { + case CDROMSTART: /* Spin up the drive */ + do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) + { + printk("Sony CDROM error 0x%2.2x (CDROMSTART)\n", res_reg[1]); + return -EIO; + } + return 0; + break; + + case CDROMSTOP: /* Spin down the drive */ + do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size); + + /* + * Spin the drive down, ignoring the error if the disk was + * already not spinning. + */ + sony_audio_status = CDROM_AUDIO_NO_STATUS; + do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + if ( ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) + && (res_reg[1] != SONY_NOT_SPIN_ERR)) + { + printk("Sony CDROM error 0x%2.2x (CDROMSTOP)\n", res_reg[1]); + return -EIO; + } + + return 0; + break; + + case CDROMPAUSE: /* Pause the drive */ + do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size); + if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) + { + printk("Sony CDROM error 0x%2.2x (CDROMPAUSE)\n", res_reg[1]); + return -EIO; + } + + /* Get the current position and save it for resuming */ + if (read_subcode() < 0) + { + return -EIO; + } + cur_pos_msf[0] = last_sony_subcode->abs_msf[0]; + cur_pos_msf[1] = last_sony_subcode->abs_msf[1]; + cur_pos_msf[2] = last_sony_subcode->abs_msf[2]; + sony_audio_status = CDROM_AUDIO_PAUSED; + return 0; + break; + + case CDROMRESUME: /* Start the drive after being paused */ + if (sony_audio_status != CDROM_AUDIO_PAUSED) + { + return -EINVAL; + } + + do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + + /* Start the drive at the saved position. */ + params[1] = cur_pos_msf[0]; + params[2] = cur_pos_msf[1]; + params[3] = cur_pos_msf[2]; + params[4] = final_pos_msf[0]; + params[5] = final_pos_msf[1]; + params[6] = final_pos_msf[2]; + params[0] = 0x03; + do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size); + if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) + { + printk("Sony CDROM error 0x%2.2x (CDROMRESUME)\n", res_reg[1]); + return -EIO; + } + sony_audio_status = CDROM_AUDIO_PLAY; + return 0; + break; + + case CDROMPLAYMSF: /* Play starting at the given MSF address. */ + verify_area(VERIFY_READ, (char *) arg, 6); + do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + memcpy_fromfs(&(params[1]), (void *) arg, 6); + + /* The parameters are given in int, must be converted */ + for (i=1; i<7; i++) + { + params[i] = int_to_bcd(params[i]); + } + params[0] = 0x03; + do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size); + if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) + { + printk("Sony CDROM error 0x%2.2x (CDROMPLAYMSF)\n", res_reg[1]); + return -EIO; + } + + /* Save the final position for pauses and resumes */ + final_pos_msf[0] = params[4]; + final_pos_msf[1] = params[5]; + final_pos_msf[2] = params[6]; + sony_audio_status = CDROM_AUDIO_PLAY; + return 0; + break; + + case CDROMREADTOCHDR: /* Read the table of contents header */ + { + struct cdrom_tochdr *hdr; + struct cdrom_tochdr loc_hdr; + + sony_get_toc(); + if (!sony_toc_read) + { + return -EIO; + } + + hdr = (struct cdrom_tochdr *) arg; + verify_area(VERIFY_WRITE, hdr, sizeof(*hdr)); + loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num); + loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num); + memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr)); + } + return 0; + break; + + case CDROMREADTOCENTRY: /* Read a given table of contents entry */ + { + struct cdrom_tocentry *entry; + struct cdrom_tocentry loc_entry; + int track_idx; + unsigned char *msf_val = NULL; + + sony_get_toc(); + if (!sony_toc_read) + { + return -EIO; + } + + entry = (struct cdrom_tocentry *) arg; + verify_area(VERIFY_READ, entry, sizeof(*entry)); + verify_area(VERIFY_WRITE, entry, sizeof(*entry)); + + memcpy_fromfs(&loc_entry, entry, sizeof(loc_entry)); + + /* Lead out is handled separately since it is special. */ + if (loc_entry.cdte_track == CDROM_LEADOUT) + { + loc_entry.cdte_adr = sony_toc->address2; + loc_entry.cdte_ctrl = sony_toc->control2; + msf_val = sony_toc->lead_out_start_msf; + } + else + { + track_idx = find_track(int_to_bcd(loc_entry.cdte_track)); + if (track_idx < 0) + { + return -EINVAL; + } + + loc_entry.cdte_adr = sony_toc->tracks[track_idx].address; + loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control; + msf_val = sony_toc->tracks[track_idx].track_start_msf; + } + + /* Logical buffer address or MSF format requested? */ + if (loc_entry.cdte_format == CDROM_LBA) + { + loc_entry.cdte_addr.lba = msf_to_log(msf_val); + } + else if (loc_entry.cdte_format == CDROM_MSF) + { + loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val); + loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val+1)); + loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val+2)); + } + memcpy_tofs(entry, &loc_entry, sizeof(*entry)); + } + return 0; + break; + + case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ + { + struct cdrom_ti ti; + int track_idx; + + sony_get_toc(); + if (!sony_toc_read) + { + return -EIO; + } + + verify_area(VERIFY_READ, (char *) arg, sizeof(ti)); + + memcpy_fromfs(&ti, (char *) arg, sizeof(ti)); + if ( (ti.cdti_trk0 < sony_toc->first_track_num) + || (ti.cdti_trk0 > sony_toc->last_track_num) + || (ti.cdti_trk1 < ti.cdti_trk0)) + { + return -EINVAL; + } + + track_idx = find_track(int_to_bcd(ti.cdti_trk0)); + if (track_idx < 0) + { + return -EINVAL; + } + params[1] = sony_toc->tracks[track_idx].track_start_msf[0]; + params[2] = sony_toc->tracks[track_idx].track_start_msf[1]; + params[3] = sony_toc->tracks[track_idx].track_start_msf[2]; + + /* + * If we want to stop after the last track, use the lead-out + * MSF to do that. + */ + if (ti.cdti_trk1 >= bcd_to_int(sony_toc->last_track_num)) + { + log_to_msf(msf_to_log(sony_toc->lead_out_start_msf)-1, + &(params[4])); + } + else + { + track_idx = find_track(int_to_bcd(ti.cdti_trk1+1)); + if (track_idx < 0) + { + return -EINVAL; + } + log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf)-1, + &(params[4])); + } + params[0] = 0x03; + + do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + + do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size); + if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) + { + printk("Params: %x %x %x %x %x %x %x\n", params[0], params[1], + params[2], params[3], params[4], params[5], params[6]); + printk("Sony CDROM error 0x%2.2x (CDROMPLAYTRKIND\n", res_reg[1]); + return -EIO; + } + + /* Save the final position for pauses and resumes */ + final_pos_msf[0] = params[4]; + final_pos_msf[1] = params[5]; + final_pos_msf[2] = params[6]; + sony_audio_status = CDROM_AUDIO_PLAY; + return 0; + } + + case CDROMSUBCHNL: /* Get subchannel info */ + return sony_get_subchnl_info(arg); + + case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */ + { + struct cdrom_volctrl volctrl; + + verify_area(VERIFY_READ, (char *) arg, sizeof(volctrl)); + + memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl)); + params[0] = SONY_SD_AUDIO_VOLUME; + params[1] = volctrl.channel0; + params[2] = volctrl.channel1; + do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, params, 3, res_reg, &res_size); + if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) + { + printk("Sony CDROM error 0x%2.2x (CDROMVOLCTRL)\n", res_reg[1]); + return -EIO; + } + } + return 0; + + case CDROMEJECT: /* Eject the drive */ + do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size); + do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + + sony_audio_status = CDROM_AUDIO_INVALID; + do_sony_cd_cmd(SONY_EJECT_CMD, NULL, 0, res_reg, &res_size); + if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) + { + printk("Sony CDROM error 0x%2.2x (CDROMEJECT)\n", res_reg[1]); + return -EIO; + } + return 0; + break; + + default: + return -EINVAL; + } +} + + +/* + * Open the drive for operations. Spin the drive up and read the table of + * contents if these have not already been done. + */ +static int +scd_open(struct inode *inode, + struct file *filp) +{ + unsigned char res_reg[2]; + unsigned int res_size; + + + if (!sony_spun_up) + { + do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + + /* The drive sometimes returns error 0. I don't know why, but ignore + it. It seems to mean the drive has already done the operation. */ + if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0))) + { + printk("Sony CDROM error 0x%2.2x (scd_open, spin up)\n", res_reg[1]); + return -EIO; + } + + do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size); + + /* The drive sometimes returns error 0. I don't know why, but ignore + it. It seems to mean the drive has already done the operation. */ + if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0))) + { + /* If the drive is already playing, its ok. */ + if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0)) + { + goto drive_spinning; + } + + printk("Sony CDROM error 0x%2.2x (scd_open, read toc)\n", res_reg[1]); + do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + + return -EIO; + } + + sony_get_toc(); + if (!sony_toc_read) + { + do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + return -EIO; + } + + sony_spun_up = 1; + } + +drive_spinning: + + if (inode) + { + check_disk_change(inode->i_rdev); + } + + sony_usage++; + + return 0; +} + + +/* + * Close the drive. Spin it down if no task is using it. The spin + * down will fail if playing audio, so audio play is OK. + */ +static void +scd_release(struct inode *inode, + struct file *filp) +{ + unsigned char res_reg[2]; + unsigned int res_size; + + + if (sony_usage > 0) + { + sony_usage--; + } + if (sony_usage == 0) + { + sync_dev(inode->i_rdev); + do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + + sony_spun_up = 0; + } +} + + +static struct file_operations scd_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + scd_ioctl, /* ioctl */ + NULL, /* mmap */ + scd_open, /* open */ + scd_release /* release */ +}; + + +/* The different types of disc loading mechanisms supported */ +static char *load_mech[] = { "caddy", "tray", "pop-up", "unknown" }; + +/* Read-ahead buffer sizes for different drives. These are just arbitrary + values, I don't know what is really optimum. */ +static unsigned int mem_size[] = { 4096, 8192, 16384, 2048 }; + +void +get_drive_configuration(unsigned short base_io, + unsigned char res_reg[], + unsigned int *res_size) +{ + int retry_count; + + + /* Set the base address */ + sony_cd_base_io = base_io; + + /* Set up all the register locations */ + sony_cd_cmd_reg = sony_cd_base_io + SONY_CMD_REG_OFFSET; + sony_cd_param_reg = sony_cd_base_io + SONY_PARAM_REG_OFFSET; + sony_cd_write_reg = sony_cd_base_io + SONY_WRITE_REG_OFFSET; + sony_cd_control_reg = sony_cd_base_io + SONY_CONTROL_REG_OFFSET; + sony_cd_status_reg = sony_cd_base_io + SONY_STATUS_REG_OFFSET; + sony_cd_result_reg = sony_cd_base_io + SONY_RESULT_REG_OFFSET; + sony_cd_read_reg = sony_cd_base_io + SONY_READ_REG_OFFSET; + sony_cd_fifost_reg = sony_cd_base_io + SONY_FIFOST_REG_OFFSET; + + /* + * Check to see if anything exists at the status register location. + * I don't know if this is a good way to check, but it seems to work + * ok for me. + */ + if (read_status_register() != 0xff) + { + /* + * Reset the drive and wait for attention from it (to say its reset). + * If you don't wait, the next operation will probably fail. + */ + reset_drive(); + retry_count = jiffies + SONY_RESET_TIMEOUT; + while ((retry_count > jiffies) && (!is_attention())) + { + sony_sleep(); + } + + /* If attention is never seen probably not a CDU31a present */ + if (!is_attention()) + { + res_reg[0] = 0x20; + return; + } + + /* + * Get the drive configuration. + */ + do_sony_cd_cmd(SONY_REQ_DRIVE_CONFIG_CMD, + NULL, + 0, + (unsigned char *) res_reg, + res_size); + return; + } + + /* Return an error */ + res_reg[0] = 0x20; +} + + +/* + * Initialize the driver. + */ +unsigned long +cdu31a_init(unsigned long mem_start, unsigned long mem_end) +{ + struct s_sony_drive_config drive_config; + unsigned char params[3]; + unsigned char res_reg[2]; + unsigned int res_size; + int i; + int drive_found; + + + /* + * According to Alex Freed (freed@europa.orion.adobe.com), this is + * required for the Fusion CD-16 package. If the sound driver is + * loaded, it should work fine, but just in case... + * + * The following turn on the CD-ROM interface for a Fusion CD-16. + */ + outb(0xbc, 0x9a01); + outb(0xe2, 0x9a01); + + i = 0; + drive_found = 0; + while ( (cdu31a_addresses[i] != 0) + && (!drive_found)) + { + get_drive_configuration(cdu31a_addresses[i], + drive_config.exec_status, + &res_size); + if ((res_size > 2) && ((drive_config.exec_status[0] & 0x20) == 0x00)) + { + drive_found = 1; + + if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops)) + { + printk("Unable to get major %d for CDU-31a\n", MAJOR_NR); + return mem_start; + } + + sony_buffer_size = mem_size[SONY_HWC_GET_BUF_MEM_SIZE(drive_config)]; + sony_buffer_sectors = sony_buffer_size / 2048; + + printk("Sony I/F CDROM : %8.8s %16.16s %8.8s with %s load mechanism\n", + drive_config.vendor_id, + drive_config.product_id, + drive_config.product_rev_level, + load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]); + printk(" using %d byte buffer", sony_buffer_size); + if (SONY_HWC_AUDIO_PLAYBACK(drive_config)) + { + printk(", capable of audio playback"); + } + printk("\n"); + + params[0] = SONY_SD_MECH_CONTROL; + params[1] = 0x03; + do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, + params, + 2, + res_reg, + &res_size); + if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) + { + printk(" Unable to set mechanical parameters: 0x%2.2x\n", res_reg[1]); + } + + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ + + sony_toc = (struct s_sony_toc *) mem_start; + mem_start += sizeof(*sony_toc); + last_sony_subcode = (struct s_sony_subcode *) mem_start; + mem_start += sizeof(*last_sony_subcode); + sony_buffer = (unsigned char *) mem_start; + mem_start += sony_buffer_size; + + initialized = 1; + } + + i++; + } + + return mem_start; +} + +#endif diff -u --recursive --new-file pl11/linux/kernel/blk_drv/floppy.c linux/kernel/blk_drv/floppy.c --- pl11/linux/kernel/blk_drv/floppy.c Wed Jul 14 17:08:54 1993 +++ linux/kernel/blk_drv/floppy.c Sun Aug 15 11:24:36 1993 @@ -121,8 +121,12 @@ /* * The DMA channel used by the floppy controller cannot access data at * addresses >= 16MB + * + * Went back to the 1MB limit, as some people had problems with the floppy + * driver otherwise. It doesn't matter much for performance anyway, as most + * floppy accesses go through the track buffer. */ -#define LAST_DMA_ADDR (0x1000000 - BLOCK_SIZE) +#define LAST_DMA_ADDR (0x100000 - BLOCK_SIZE) /* * globals used by 'result()' @@ -1137,6 +1141,8 @@ case FDFMTTRK: if (!suser()) return -EPERM; + if (fd_ref[drive & 3] != 1) + return -EBUSY; cli(); while (format_status != FORMAT_NONE) sleep_on(&format_done); diff -u --recursive --new-file pl11/linux/kernel/blk_drv/hd.c linux/kernel/blk_drv/hd.c --- pl11/linux/kernel/blk_drv/hd.c Sat Jul 3 02:59:51 1993 +++ linux/kernel/blk_drv/hd.c Wed Jul 28 00:19:32 1993 @@ -556,6 +556,13 @@ put_fs_long(hd[MINOR(inode->i_rdev)].nr_sects, (long *) arg); return 0; + case BLKFLSBUF: + if(!suser()) return -EACCES; + if(!inode->i_rdev) return -EINVAL; + sync_dev(inode->i_rdev); + invalidate_buffers(inode->i_rdev); + return 0; + case BLKRRPART: /* Re-read partition tables */ return revalidate_hddisk(inode->i_rdev, 1); RO_IOCTLS(inode->i_rdev,arg); diff -u --recursive --new-file pl11/linux/kernel/blk_drv/ll_rw_blk.c linux/kernel/blk_drv/ll_rw_blk.c --- pl11/linux/kernel/blk_drv/ll_rw_blk.c Sat Jun 26 10:28:06 1993 +++ linux/kernel/blk_drv/ll_rw_blk.c Sat Aug 14 23:47:22 1993 @@ -185,6 +185,22 @@ sti(); return; } + else if ( req->dev == bh->b_dev && + !req->waiting && + req->cmd == rw && + req->sector - count == sector && + req->nr_sectors < 254) + { + req->nr_sectors += count; + bh->b_reqnext = req->bh; + req->buffer = bh->b_data; + req->current_nr_sectors = count; + req->sector = sector; + bh->b_dirt = 0; + req->bh = bh; + sti(); + return; + } req = req->next; } } @@ -414,6 +430,12 @@ #endif #ifdef CONFIG_BLK_DEV_XD mem_start = xd_init(mem_start,mem_end); +#endif +#ifdef CONFIG_CDU31A + mem_start = cdu31a_init(mem_start,mem_end); +#endif +#ifdef CONFIG_MCD + mem_start = mcd_init(mem_start,mem_end); #endif if (ramdisk_size) mem_start += rd_init(mem_start, ramdisk_size*1024); diff -u --recursive --new-file pl11/linux/kernel/blk_drv/mcd.c linux/kernel/blk_drv/mcd.c --- pl11/linux/kernel/blk_drv/mcd.c +++ linux/kernel/blk_drv/mcd.c Sat Aug 14 23:47:22 1993 @@ -0,0 +1,1196 @@ +/* + linux/kernel/blk_drv/mcd.c - Mitsumi CDROM driver + + Copyright (C) 1992 Martin Harriss + + martin@bdsi.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + HISTORY + + 0.1 First attempt - internal use only + 0.2 Cleaned up delays and use of timer - alpha release + 0.3 Audio support added + 0.3.1 Changes for mitsumi CRMC LU005S march version + (stud11@cc4.kuleuven.ac.be) + 0.3.2 bug fixes to the ioclts and merged with ALPHA0.99-pl12 + (Jon Tombs ) +*/ + +#include +#ifdef CONFIG_MCD + +#include +#include +#include +#include +#include +#include +#include + +/* #define REALLY_SLOW_IO */ +#include +#include +#include + +#define MAJOR_NR 23 +#include "blk.h" +#include + +#if 0 +static int mcd_sizes[] = { 0 }; +#endif + +static int mcdPresent = 0; + +static char mcd_buf[2048]; /* buffer for block size conversion */ +static int mcd_bn = -1; + +static int McdTimeout, McdTries; +static struct wait_queue *mcd_waitq = NULL; + +static struct mcd_DiskInfo DiskInfo; +static struct mcd_Toc Toc[MAX_TRACKS]; +static struct mcd_Play_msf mcd_Play; + +static int audioStatus; +static char mcdDiskChanged; +static char tocUpToDate; +static char mcdVersion; + +static void mcd_transfer(void); +static void mcd_start(void); +static void mcd_status(void); +static void mcd_read_cmd(void); +static void mcd_data(void); +static void do_mcd_request(void); +static void hsg2msf(long hsg, struct msf *msf); +static void bin2bcd(unsigned char *p); +static int bcd2bin(unsigned char bcd); +static int mcdStatus(void); +static void sendMcdCmd(int cmd, struct mcd_Play_msf *params); +static int getMcdStatus(int timeout); +static int GetQChannelInfo(struct mcd_Toc *qp); +static int updateToc(void); +static int GetDiskInfo(void); +static int GetToc(void); +static int getValue(unsigned char *result); + + +int +check_mcd_media_change(int full_dev, int flag) +{ + int retval, target; + + + target = MINOR(full_dev); + + if (target > 0) { + printk("Mitsumi CD-ROM request error: invalid device.\n"); + return 0; + } + + retval = mcdDiskChanged; + if (!flag) + { + mcdDiskChanged = 0; + } + + return retval; +} + + +/* + * Do a 'get status' command and get the result. Only use from the top half + * because it calls 'getMcdStatus' which sleeps. + */ + +static int +statusCmd(void) +{ + int st, retry; + + for (retry = 0; retry < 3; retry++) + { + outb(MCMD_GET_STATUS, MCDPORT(0)); /* send get-status cmd */ + st = getMcdStatus(100); + if (st != -1) + break; + } + + return st; +} + + +/* + * Send a 'Play' command and get the status. Use only from the top half. + */ + +static int +mcdPlay(struct mcd_Play_msf *arg) +{ + int retry, st; + + for (retry = 0; retry < 3; retry++) + { + sendMcdCmd(MCMD_PLAY_READ, arg); + st = getMcdStatus(200); + if (st != -1) + break; + } + + return st; +} + + +long +msf2hsg(struct msf *mp) +{ + return bcd2bin(mp -> frame) + + bcd2bin(mp -> sec) * 75 + + bcd2bin(mp -> min) * 4500 + - 150; +} + + +static int +mcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, + unsigned long arg) +{ + int i, st; + struct mcd_Toc qInfo; + struct cdrom_ti ti; + struct cdrom_tochdr tocHdr; + struct cdrom_msf msf; + struct cdrom_tocentry entry; + struct mcd_Toc *tocPtr; + struct cdrom_subchnl subchnl; +#if 0 + struct cdrom_volctrl volctrl; +#endif + + if (!ip) + return -EINVAL; + + st = statusCmd(); + if (st < 0) + return -EIO; + + if (!tocUpToDate) + { + i = updateToc(); + if (i < 0) + return i; /* error reading TOC */ + } + + switch (cmd) + { + case CDROMSTART: /* Spin up the drive */ + /* Don't think we can do this. Even if we could, + * I think the drive times out and stops after a while + * anyway. For now, ignore it. + */ + + return 0; + + case CDROMSTOP: /* Spin down the drive */ + outb(MCMD_STOP, MCDPORT(0)); + i = getMcdStatus(100); + + /* should we do anything if it fails? */ + + audioStatus = CDROM_AUDIO_NO_STATUS; + return 0; + + case CDROMPAUSE: /* Pause the drive */ + if (audioStatus != CDROM_AUDIO_PLAY) + return -EINVAL; + + outb(MCMD_STOP, MCDPORT(0)); + i = getMcdStatus(100); + + if (GetQChannelInfo(&qInfo) < 0) + { + /* didn't get q channel info */ + + audioStatus = CDROM_AUDIO_NO_STATUS; + return 0; + } + + mcd_Play.start = qInfo.diskTime; /* remember restart point */ + + audioStatus = CDROM_AUDIO_PAUSED; + return 0; + + case CDROMRESUME: /* Play it again, Sam */ + if (audioStatus != CDROM_AUDIO_PAUSED) + return -EINVAL; + + /* restart the drive at the saved position. */ + + i = mcdPlay(&mcd_Play); + if (i < 0) + { + audioStatus = CDROM_AUDIO_ERROR; + return -EIO; + } + + audioStatus = CDROM_AUDIO_PLAY; + return 0; + + case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ + + st = verify_area(VERIFY_READ, (void *) arg, sizeof ti); + if (st) + return st; + + memcpy_fromfs(&ti, (void *) arg, sizeof ti); + + if (ti.cdti_trk0 < DiskInfo.first + || ti.cdti_trk0 > DiskInfo.last + || ti.cdti_trk1 < ti.cdti_trk0) + { + return -EINVAL; + } + + if (ti.cdti_trk1 > DiskInfo.last) + ti. cdti_trk1 = DiskInfo.last; + + mcd_Play.start = Toc[ti.cdti_trk0].diskTime; + mcd_Play.end = Toc[ti.cdti_trk1 + 1].diskTime; + +#ifdef MCD_DEBUG +printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n", + mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame, + mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame); +#endif + + i = mcdPlay(&mcd_Play); + if (i < 0) + { + audioStatus = CDROM_AUDIO_ERROR; + return -EIO; + } + + audioStatus = CDROM_AUDIO_PLAY; + return 0; + + case CDROMPLAYMSF: /* Play starting at the given MSF address. */ + + if (audioStatus == CDROM_AUDIO_PLAY) { + outb(MCMD_STOP, MCDPORT(0)); + i = getMcdStatus(100); + audioStatus = CDROM_AUDIO_NO_STATUS; + } + + st = verify_area(VERIFY_READ, (void *) arg, sizeof msf); + if (st) + return st; + + memcpy_fromfs(&msf, (void *) arg, sizeof msf); + + /* convert to bcd */ + + bin2bcd(&msf.cdmsf_min0); + bin2bcd(&msf.cdmsf_sec0); + bin2bcd(&msf.cdmsf_frame0); + bin2bcd(&msf.cdmsf_min1); + bin2bcd(&msf.cdmsf_sec1); + bin2bcd(&msf.cdmsf_frame1); + + mcd_Play.start.min = msf.cdmsf_min0; + mcd_Play.start.sec = msf.cdmsf_sec0; + mcd_Play.start.frame = msf.cdmsf_frame0; + mcd_Play.end.min = msf.cdmsf_min1; + mcd_Play.end.sec = msf.cdmsf_sec1; + mcd_Play.end.frame = msf.cdmsf_frame1; + +#ifdef MCD_DEBUG +printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n", +mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame, +mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame); +#endif + + i = mcdPlay(&mcd_Play); + if (i < 0) + { + audioStatus = CDROM_AUDIO_ERROR; + return -EIO; + } + + audioStatus = CDROM_AUDIO_PLAY; + return 0; + + case CDROMREADTOCHDR: /* Read the table of contents header */ + st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr); + if (st) + return st; + + tocHdr.cdth_trk0 = DiskInfo.first; + tocHdr.cdth_trk1 = DiskInfo.last; + memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr); + return 0; + + case CDROMREADTOCENTRY: /* Read an entry in the table of contents */ + + st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry); + if (st) + return st; + + memcpy_fromfs(&entry, (void *) arg, sizeof entry); + if (entry.cdte_track == CDROM_LEADOUT) + /* XXX */ + tocPtr = &Toc[DiskInfo.last + 1]; + + else if (entry.cdte_track > DiskInfo.last + || entry.cdte_track < DiskInfo.first) + return -EINVAL; + + else + tocPtr = &Toc[entry.cdte_track]; + + entry.cdte_adr = tocPtr -> ctrl_addr; + entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4; + + if (entry.cdte_format == CDROM_LBA) + entry.cdte_addr.lba = msf2hsg(&tocPtr -> diskTime); + + else if (entry.cdte_format == CDROM_MSF) + { + entry.cdte_addr.msf.minute = bcd2bin(tocPtr -> diskTime.min); + entry.cdte_addr.msf.second = bcd2bin(tocPtr -> diskTime.sec); + entry.cdte_addr.msf.frame = bcd2bin(tocPtr -> diskTime.frame); + } + + else + return -EINVAL; + + memcpy_tofs((void *) arg, &entry, sizeof entry); + return 0; + + case CDROMSUBCHNL: /* Get subchannel info */ + + st = verify_area(VERIFY_WRITE, (void *) arg, sizeof subchnl); + if (st) + return st; + + memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl); + + if (GetQChannelInfo(&qInfo) < 0) + return -EIO; + + subchnl.cdsc_audiostatus = audioStatus; + subchnl.cdsc_adr = qInfo.ctrl_addr; + subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4; + subchnl.cdsc_trk = bcd2bin(qInfo.track); + subchnl.cdsc_ind = bcd2bin(qInfo.pointIndex); + + if (subchnl.cdsc_format == CDROM_LBA) + { + subchnl.cdsc_absaddr.lba = msf2hsg(&qInfo.diskTime); + subchnl.cdsc_reladdr.lba = msf2hsg(&qInfo.trackTime); + } + + else if (subchnl.cdsc_format == CDROM_MSF) + { + subchnl.cdsc_absaddr.msf.minute = bcd2bin(qInfo.diskTime.min); + subchnl.cdsc_absaddr.msf.second = bcd2bin(qInfo.diskTime.sec); + subchnl.cdsc_absaddr.msf.frame = bcd2bin(qInfo.diskTime.frame); + + subchnl.cdsc_reladdr.msf.minute = bcd2bin(qInfo.trackTime.min); + subchnl.cdsc_reladdr.msf.second = bcd2bin(qInfo.trackTime.sec); + subchnl.cdsc_reladdr.msf.frame = bcd2bin(qInfo.trackTime.frame); + } + + else + return -EINVAL; + + memcpy_tofs((void *) arg, &subchnl, sizeof subchnl); + return 0; + + case CDROMVOLCTRL: /* Volume control */ + /* + * This is not working yet. Setting the volume by itself does + * nothing. Following the 'set' by a 'play' results in zero + * volume. Something to work on for the next release. + */ +#if 0 + st = verify_area(VERIFY_READ, (void *) arg, sizeof(volctrl)); + if (st) + return st; + + memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl)); +printk("VOL %d %d\n", volctrl.channel0 & 0xFF, volctrl.channel1 & 0xFF); + outb(MCMD_SET_VOLUME, MCDPORT(0)); + outb(volctrl.channel0, MCDPORT(0)); + outb(0, MCDPORT(0)); + outb(volctrl.channel1, MCDPORT(0)); + outb(1, MCDPORT(0)); + + i = getMcdStatus(100); + if (i < 0) + return -EIO; + + { + int a, b, c, d; + + getValue(&a); + getValue(&b); + getValue(&c); + getValue(&d); + printk("%02X %02X %02X %02X\n", a, b, c, d); + } + + outb(0xF8, MCDPORT(0)); + i = getMcdStatus(100); + printk("F8 -> %02X\n", i & 0xFF); +#endif + return 0; + + case CDROMEJECT: /* Eject the drive - N/A */ + return 0; + + default: + return -EINVAL; + } +} + + +/* + * Take care of the different block sizes between cdrom and Linux. + * When Linux gets variable block sizes this will probably go away. + */ + +static void +mcd_transfer(void) +{ + long offs; + + while (CURRENT -> nr_sectors > 0 && mcd_bn == CURRENT -> sector / 4) + { + offs = (CURRENT -> sector & 3) * 512; + memcpy(CURRENT -> buffer, mcd_buf + offs, 512); + CURRENT -> nr_sectors--; + CURRENT -> sector++; + CURRENT -> buffer += 512; + } +} + + +/* + * We only seem to get interrupts after an error. + * Just take the interrupt and clear out the status reg. + */ + +static void +mcd_interrupt(int unused) +{ + int st; + + st = inb(MCDPORT(1)) & 0xFF; + if (st != 0xFF) + { + st = inb(MCDPORT(0)) & 0xFF; +#if 0 + printk("", st); +#endif + } +} + + +/* + * I/O request routine called from Linux kernel. + */ + +static void +do_mcd_request(void) +{ + unsigned int block,dev; + unsigned int nsect; + +repeat: + INIT_REQUEST; + dev = MINOR(CURRENT->dev); + block = CURRENT->sector; + nsect = CURRENT->nr_sectors; + + if (CURRENT == NULL || CURRENT -> sector == -1) + return; + + if (CURRENT -> cmd != READ) + { + printk("mcd: bad cmd %d\n", CURRENT -> cmd); + end_request(0); + goto repeat; + } + + mcd_transfer(); + + /* if we satisfied the request from the buffer, we're done. */ + + if (CURRENT -> nr_sectors == 0) + { + end_request(1); + goto repeat; + } + + McdTries = 3; + mcd_start(); +} + + +/* + * Start the I/O for the cdrom. Handle retry count. + */ + +static void +mcd_start() +{ + if (McdTries == 0) + { + printk("mcd: read failed after 3 tries\n"); + end_request(0); + SET_TIMER(do_mcd_request, 1); /* wait a bit, try again */ + return; + } + + McdTries--; + outb(0x40, MCDPORT(0)); /* get status */ + McdTimeout = 100; + SET_TIMER(mcd_status, 1); +} + + +/* + * Called from the timer to check the results of the get-status cmd. + * On success, send the set-mode command. + */ + +static void +mcd_status() +{ + int st; + + McdTimeout--; + st = mcdStatus(); + if (st == -1) + { + if (McdTimeout == 0) + { + printk("mcd: status timed out\n"); + SET_TIMER(mcd_start, 1); /* wait a bit, try again */ + return; + } + + SET_TIMER(mcd_status, 1); + return; + } + + if (st & MST_DSK_CHG) + { + mcdDiskChanged = 1; + } + + if ((st & MST_READY) == 0) + { + printk("mcd: disk removed\n"); + mcdDiskChanged = 1; + end_request(0); + do_mcd_request(); + return; + } + + outb(0x50, MCDPORT(0)); /* set mode */ + outb(0x01, MCDPORT(0)); /* mode = cooked data */ + McdTimeout = 100; + SET_TIMER(mcd_read_cmd, 1); +} + + +/* + * Check the result of the set-mode command. On success, send the + * read-data command. + */ + +static void +mcd_read_cmd() +{ + int st; + long block; + struct mcd_Play_msf mcdcmd; + + McdTimeout--; + st = mcdStatus(); + + if (st & MST_DSK_CHG) + { + mcdDiskChanged = 1; + } + + if (st == -1) + { + if (McdTimeout == 0) + { + printk("mcd: set mode timed out\n"); + SET_TIMER(mcd_start, 1); /* wait a bit, try again */ + return; + } + + SET_TIMER(mcd_read_cmd, 1); + return; + } + + mcd_bn = -1; /* purge our buffer */ + block = CURRENT -> sector / 4; + hsg2msf(block, &mcdcmd.start); /* cvt to msf format */ + + mcdcmd.end.min = 0; + mcdcmd.end.sec = 0; + mcdcmd.end.frame = 1; + + sendMcdCmd(MCMD_PLAY_READ, &mcdcmd); /* read command */ + McdTimeout = 200; + SET_TIMER(mcd_data, 1); +} + + +/* + * Check the completion of the read-data command. On success, read + * the 2048 bytes of data from the disk into our buffer. + */ + +static void +mcd_data() +{ + int i; + + McdTimeout--; + cli(); + i =inb(MCDPORT(1)) & (MFL_STATUS | MFL_DATA); + if (i == MFL_DATA) + { + printk("mcd: read failed\n"); +#ifdef MCD_DEBUG + printk("got 0xB %02X\n", inb(MCDPORT(0)) & 0xFF); +#endif + SET_TIMER(mcd_start, 1); + sti(); + return; + } + + if (i == (MFL_STATUS | MFL_DATA)) + { + if (McdTimeout == 0) + { + printk("mcd: data timeout, retrying\n"); + SET_TIMER(mcd_start, 1); + } + + else + SET_TIMER(mcd_data, 1); + + sti(); + return; + } + + CLEAR_TIMER; + READ_DATA(MCDPORT(0), &mcd_buf[0], 2048); + sti(); + + mcd_bn = CURRENT -> sector / 4; + mcd_transfer(); + end_request(1); + SET_TIMER(do_mcd_request, 1); +} + + +/* + * Open the device special file. Check that a disk is in. + */ + +int +mcd_open(struct inode *ip, struct file *fp) +{ + int st; + + if (mcdPresent == 0) + return -ENXIO; /* no hardware */ + + st = statusCmd(); /* check drive status */ + if (st == -1) + return -EIO; /* drive doesn't respond */ + + if ((st & MST_READY) == 0) /* no disk in drive */ + { + printk("mcd: no disk in drive\n"); + return -EIO; + } + + if (updateToc() < 0) + return -EIO; + + return 0; +} + + +/* + * On close, we flush all mcd blocks from the buffer cache. + */ + +static void +mcd_release(struct inode * inode, struct file * file) +{ + mcd_bn = -1; + sync_dev(inode->i_rdev); + invalidate_buffers(inode -> i_rdev); +} + + +static struct file_operations mcd_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + mcd_ioctl, /* ioctl */ + NULL, /* mmap */ + mcd_open, /* open */ + mcd_release /* release */ +}; + + +/* + * MCD interrupt descriptor + */ + +static struct sigaction mcd_sigaction = { + mcd_interrupt, + 0, + SA_INTERRUPT, + NULL +}; + + +/* + * Test for presence of drive and initialize it. Called at boot time. + */ + +unsigned long +mcd_init(unsigned long mem_start, unsigned long mem_end) +{ + int count; + unsigned char result[3]; + + if (register_blkdev(MAJOR_NR, "mcd", &mcd_fops) != 0) + { + printk("Unable to get major %d for Mitsumi CD-ROM\n", MAJOR_NR); + return mem_start; + } + + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + read_ahead[MAJOR_NR] = 4; + + /* check for card */ + + outb(0, MCDPORT(1)); /* send reset */ + for (count = 0; count < 1000000; count++) + (void) inb(MCDPORT(1)); /* delay a bit */ + + outb(0x40, MCDPORT(0)); /* send get-stat cmd */ + for (count = 0; count < 1000000; count++) + if (!(inb(MCDPORT(1)) & MFL_STATUS)) + break; + + if (count >= 1000000) { + printk("mitsumi init failed...\n"); + return mem_start; + } + count = inb(MCDPORT(0)); /* pick up the status */ + + outb(MCMD_GET_VERSION,MCDPORT(0)); + for(count=0;count<3;count++) + if(getValue(result+count)) { + printk("mitsumi get version failed...\n"); + return mem_start; + } + + printk("Mitsumi version : %02X %c %x\n",result[0],result[1],result[2]); + + mcdVersion=result[2]; + + if (mcdVersion >=4) + outb(4,MCDPORT(2)); /* magic happens */ + + /* don't get the IRQ until we know for sure the drive is there */ + + if (irqaction(MCD_INTR_NR, &mcd_sigaction)) + { + printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", MCD_INTR_NR); + return mem_start; + } + + mcdPresent = 1; + printk("Mitsumi CD-ROM Drive present\n"); + return mem_start; +} + + +static void +hsg2msf(long hsg, struct msf *msf) +{ + hsg += 150; + msf -> min = hsg / 4500; + hsg %= 4500; + msf -> sec = hsg / 75; + msf -> frame = hsg % 75; + + bin2bcd(&msf -> min); /* convert to BCD */ + bin2bcd(&msf -> sec); + bin2bcd(&msf -> frame); +} + + +static void +bin2bcd(unsigned char *p) +{ + int u, t; + + u = *p % 10; + t = *p / 10; + *p = u | (t << 4); +} + +static int +bcd2bin(unsigned char bcd) +{ + return (bcd >> 4) * 10 + (bcd & 0xF); +} + + +/* + * See if a status is ready from the drive and return it + * if it is ready. + */ + +static int +mcdStatus(void) +{ + int i; + int st; + + st = inb(MCDPORT(1)) & MFL_STATUS; + if (!st) + { + i = inb(MCDPORT(0)) & 0xFF; + return i; + } + else + return -1; +} + + +/* + * Send a play or read command to the drive + */ + +static void +sendMcdCmd(int cmd, struct mcd_Play_msf *params) +{ + outb(cmd, MCDPORT(0)); + outb(params -> start.min, MCDPORT(0)); + outb(params -> start.sec, MCDPORT(0)); + outb(params -> start.frame, MCDPORT(0)); + outb(params -> end.min, MCDPORT(0)); + outb(params -> end.sec, MCDPORT(0)); + outb(params -> end.frame, MCDPORT(0)); +} + + +/* + * Timer interrupt routine to test for status ready from the drive. + * (see the next routine) + */ + +static void +mcdStatTimer(void) +{ + if (!(inb(MCDPORT(1)) & MFL_STATUS)) + { + wake_up(&mcd_waitq); + return; + } + + McdTimeout--; + if (McdTimeout <= 0) + { + wake_up(&mcd_waitq); + return; + } + + SET_TIMER(mcdStatTimer, 1); +} + + +/* + * Wait for a status to be returned from the drive. The actual test + * (see routine above) is done by the timer interrupt to avoid + * excessive rescheduling. + */ + +static int +getMcdStatus(int timeout) +{ + int st; + + McdTimeout = timeout; + SET_TIMER(mcdStatTimer, 1); + sleep_on(&mcd_waitq); + if (McdTimeout <= 0) + return -1; + + st = inb(MCDPORT(0)) & 0xFF; + if (st == 0xFF) + return -1; + + if ((st & MST_BUSY) == 0 && audioStatus == CDROM_AUDIO_PLAY) + /* XXX might be an error? look at q-channel? */ + audioStatus = CDROM_AUDIO_COMPLETED; + + if (st & MST_DSK_CHG) + { + mcdDiskChanged = 1; + tocUpToDate = 0; + audioStatus = CDROM_AUDIO_NO_STATUS; + } + + return st; +} + + +/* + * Read a value from the drive. Should return quickly, so a busy wait + * is used to avoid excessive rescheduling. + */ + +static int +getValue(unsigned char *result) +{ + int count; + int s; + + for (count = 0; count < 2000; count++) + if (!(inb(MCDPORT(1)) & MFL_STATUS)) + break; + + if (count >= 2000) + { + printk("mcd: getValue timeout\n"); + return -1; + } + + s = inb(MCDPORT(0)) & 0xFF; + *result = (unsigned char) s; + return 0; +} + + +/* + * Read the current Q-channel info. Also used for reading the + * table of contents. + */ + +int +GetQChannelInfo(struct mcd_Toc *qp) +{ + unsigned char notUsed; + int retry; + + for (retry = 0; retry < 3; retry++) + { + outb(MCMD_GET_Q_CHANNEL, MCDPORT(0)); + if (getMcdStatus(100) != -1) + break; + } + + if (retry >= 3) + return -1; + + if (getValue(&qp -> ctrl_addr) < 0) return -1; + if (getValue(&qp -> track) < 0) return -1; + if (getValue(&qp -> pointIndex) < 0) return -1; + if (getValue(&qp -> trackTime.min) < 0) return -1; + if (getValue(&qp -> trackTime.sec) < 0) return -1; + if (getValue(&qp -> trackTime.frame) < 0) return -1; + if (getValue(¬Used) < 0) return -1; + if (getValue(&qp -> diskTime.min) < 0) return -1; + if (getValue(&qp -> diskTime.sec) < 0) return -1; + if (getValue(&qp -> diskTime.frame) < 0) return -1; + + return 0; +} + + +/* + * Read the table of contents (TOC) and TOC header if neccessary + */ + +static int +updateToc() +{ + if (tocUpToDate) + return 0; + + if (GetDiskInfo() < 0) + return -EIO; + + if (GetToc() < 0) + return -EIO; + + tocUpToDate = 1; + return 0; +} + + +/* + * Read the table of contents header + */ + +static int +GetDiskInfo() +{ + int retry; + + for (retry = 0; retry < 3; retry++) + { + outb(MCMD_GET_DISK_INFO, MCDPORT(0)); + if (getMcdStatus(100) != -1) + break; + } + + if (retry >= 3) + return -1; + + if (getValue(&DiskInfo.first) < 0) return -1; + if (getValue(&DiskInfo.last) < 0) return -1; + + DiskInfo.first = bcd2bin(DiskInfo.first); + DiskInfo.last = bcd2bin(DiskInfo.last); + + if (getValue(&DiskInfo.diskLength.min) < 0) return -1; + if (getValue(&DiskInfo.diskLength.sec) < 0) return -1; + if (getValue(&DiskInfo.diskLength.frame) < 0) return -1; + if (getValue(&DiskInfo.firstTrack.min) < 0) return -1; + if (getValue(&DiskInfo.firstTrack.sec) < 0) return -1; + if (getValue(&DiskInfo.firstTrack.frame) < 0) return -1; + +#ifdef MCD_DEBUG +printk("Disk Info: first %d last %d length %02x:%02x.%02x first %02x:%02x.%02x\n", + DiskInfo.first, + DiskInfo.last, + DiskInfo.diskLength.min, + DiskInfo.diskLength.sec, + DiskInfo.diskLength.frame, + DiskInfo.firstTrack.min, + DiskInfo.firstTrack.sec, + DiskInfo.firstTrack.frame); +#endif + + return 0; +} + + +/* + * Read the table of contents (TOC) + */ + +static int +GetToc() +{ + int i, px; + int limit; + int retry; + struct mcd_Toc qInfo; + + for (i = 0; i < MAX_TRACKS; i++) + Toc[i].pointIndex = 0; + + i = DiskInfo.last + 3; + + for (retry = 0; retry < 3; retry++) + { + outb(MCMD_STOP, MCDPORT(0)); + if (getMcdStatus(100) != -1) + break; + } + + if (retry >= 3) + return -1; + + for (retry = 0; retry < 3; retry++) + { + outb(MCMD_SET_MODE, MCDPORT(0)); + outb(0x05, MCDPORT(0)); /* mode: toc */ + if (getMcdStatus(100) != -1) + break; + } + + if (retry >= 3) + return -1; + + for (limit = 300; limit > 0; limit--) + { + if (GetQChannelInfo(&qInfo) < 0) + break; + + px = bcd2bin(qInfo.pointIndex); + if (px > 0 && px < MAX_TRACKS && qInfo.track == 0) + if (Toc[px].pointIndex == 0) + { + Toc[px] = qInfo; + i--; + } + + if (i <= 0) + break; + } + + Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength; + + for (retry = 0; retry < 3; retry++) + { + outb(MCMD_SET_MODE, MCDPORT(0)); + outb(0x01, MCDPORT(0)); + if (getMcdStatus(100) != -1) + break; + } + +#ifdef MCD_DEBUG +for (i = 1; i <= DiskInfo.last; i++) +printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n", +i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, +Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame, +Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame); +for (i = 100; i < 103; i++) +printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n", +i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, +Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame, +Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame); +#endif + + return limit > 0 ? 0 : -1; +} + +#endif diff -u --recursive --new-file pl11/linux/kernel/blk_drv/ramdisk.c linux/kernel/blk_drv/ramdisk.c --- pl11/linux/kernel/blk_drv/ramdisk.c Sat Jun 26 10:28:06 1993 +++ linux/kernel/blk_drv/ramdisk.c Wed Aug 4 22:38:40 1993 @@ -104,7 +104,7 @@ { struct buffer_head *bh; struct minix_super_block s; - int block, try; + int block, tries; int i = 1; int nblocks; char *cp; @@ -125,8 +125,8 @@ * case, we have to look at block 0. Be intelligent about * this, and check both... - FvK */ - for (try = 0; try < 1000; try += 512) { - block = try; + for (tries = 0; tries < 1000; tries += 512) { + block = tries; bh = breada(ROOT_DEV,block+1,block,block+2,-1); if (!bh) { printk("RAMDISK: I/O error while looking for super block!\n"); diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/Makefile linux/kernel/blk_drv/scsi/Makefile --- pl11/linux/kernel/blk_drv/scsi/Makefile Fri Jul 2 22:54:41 1993 +++ linux/kernel/blk_drv/scsi/Makefile Wed Jul 28 00:19:32 1993 @@ -6,12 +6,11 @@ # unless its something special (ie not a .c file). # -SCSI_HOSTS := 0 SCSI_OBJS := ifdef CONFIG_SCSI -SCSI_OBJS := hosts.o scsi.o scsi_ioctl.o +SCSI_OBJS := hosts.o scsi.o scsi_ioctl.o constants.o ifdef CONFIG_BLK_DEV_ST SCSI_OBJS := $(SCSI_OBJS) st.o @@ -27,42 +26,34 @@ ifdef CONFIG_SCSI_AHA1542 SCSI_OBJS := $(SCSI_OBJS) aha1542.o -SCSI_HOSTS := 1+$(SCSI_HOSTS) endif ifdef CONFIG_SCSI_AHA1740 SCSI_OBJS := $(SCSI_OBJS) aha1740.o -SCSI_HOSTS := 1+$(SCSI_HOSTS) endif ifdef CONFIG_SCSI_FUTURE_DOMAIN SCSI_OBJS := $(SCSI_OBJS) fdomain.o -SCSI_HOSTS := 1+$(SCSI_HOSTS) endif ifdef CONFIG_SCSI_ULTRASTOR SCSI_OBJS := $(SCSI_OBJS) ultrastor.o -SCSI_HOSTS := 1+$(SCSI_HOSTS) endif ifdef CONFIG_SCSI_7000FASST SCSI_OBJS := $(SCSI_OBJS) wd7000.o -SCSI_HOSTS := 1+$(SCSI_HOSTS) endif ifdef CONFIG_SCSI_SEAGATE SCSI_OBJS := $(SCSI_OBJS) seagate.o -SCSI_HOSTS := 1+$(SCSI_HOSTS) else ifdef CONFIG_SCSI_FD_88x SCSI_OBJS := $(SCSI_OBJS) seagate.o -SCSI_HOSTS := 1+$(SCSI_HOSTS) endif endif ifdef CONFIG_SCSI_DEBUG SCSI_OBJS := $(SCSI_OBJS) scsi_debug.o -SCSI_HOSTS := 1+$(SCSI_HOSTS) endif scsi.a: $(SCSI_OBJS) @@ -79,10 +70,11 @@ endif -CFLAGS := $(CFLAGS) "-DMAX_SCSI_HOSTS=($(SCSI_HOSTS))" - seagate.o: seagate.c - $(CC) -Wall -I$(KERNELHDRS) -x c++ -c seagate.c + $(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -c seagate.c + +constants.o: constants.c + $(CC) $(CFLAGS) -c constants.c clean: rm -f core *.o *.a *.s diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/aha1542.c linux/kernel/blk_drv/scsi/aha1542.c --- pl11/linux/kernel/blk_drv/scsi/aha1542.c Fri Jul 9 01:29:14 1993 +++ linux/kernel/blk_drv/scsi/aha1542.c Wed Jul 28 00:19:32 1993 @@ -376,7 +376,7 @@ if(*cmd == REQUEST_SENSE){ #ifndef DEBUG - if (bufflen != 16) { + if (bufflen != sizeof(SCpnt->sense_buffer)) { printk("Wrong buffer length supplied for request sense (%d)\n",bufflen); panic("aha1542.c"); }; @@ -472,9 +472,11 @@ panic("Foooooooood fight!"); }; any2scsi(cptr[i].dataptr, sgpnt[i].address); + if(((unsigned int) sgpnt[i].address) & 0xff000000) goto baddma; any2scsi(cptr[i].datalen, sgpnt[i].length); }; any2scsi(ccb[mbo].datalen, SCpnt->use_sg * sizeof(struct chain)); + if(((unsigned int) buff & 0xff000000)) goto baddma; any2scsi(ccb[mbo].dataptr, cptr); #ifdef DEBUG printk("cptr %x: ",cptr); @@ -511,6 +513,8 @@ printk("aha1542_queuecommand: done can't be NULL\n"); return 0; + baddma: + panic("Buffer at address > 16Mb used for 1542B"); } static volatile int internal_done_flag = 0; diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/aha1740.c linux/kernel/blk_drv/scsi/aha1740.c --- pl11/linux/kernel/blk_drv/scsi/aha1740.c Sat Jul 3 03:39:36 1993 +++ linux/kernel/blk_drv/scsi/aha1740.c Wed Jul 28 00:19:32 1993 @@ -247,7 +247,7 @@ if(*cmd == REQUEST_SENSE) { - if (bufflen != 16) + if (bufflen != sizeof(SCpnt->sense_buffer)) { printk("Wrong buffer length supplied for request sense (%d)\n",bufflen); panic("aha1740.c"); diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/constants.c linux/kernel/blk_drv/scsi/constants.c --- pl11/linux/kernel/blk_drv/scsi/constants.c +++ linux/kernel/blk_drv/scsi/constants.c Wed Jul 28 00:19:33 1993 @@ -0,0 +1,460 @@ +/* + * ASCII values for a number of symbolic constants, printing functions, + * etc. + */ + +#include +#ifdef CONFIG_SCSI +#include "../blk.h" +#include +#include "scsi.h" +static const char reserved[] = "RESERVED"; +static const char unknown[] = "UNKNOWN"; +static const char vendor[] = "VENDOR SPECIFIC"; + +#define CONST_COMMAND 0x01 +#define CONST_STATUS 0x02 +#define CONST_SENSE 0x04 +#define CONST_XSENSE 0x08 + +#ifdef CONFIG_SCSI_CONSTANTS +#ifdef CONSTANTS +#undef CONSTANTS +#endif +#define CONSTANTS (CONST_CMD | CONST_STATUS | CONST_SENSE | CONST_XSENSE) +#endif + +#if (CONSTANTS & CONST_COMMAND) +static const char * group_0_commands[] = { +/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense", +/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks", +/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown, +/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry", +/* 13-16 */ unknown, "Recover Buffered Data", "Mode Select", "Reserve", +/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit", +/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic", +/* 1e-1f */ "Prevent/Allow Medium Removal", unknown, +}; + + +static const char *group_1_commands[] = { +/* 20-22 */ unknown, unknown, unknown, +/* 23-28 */ unknown, unknown, "Read Capacity", unknown, unknown, "Read (10)", +/* 29-2d */ unknown, "Write (10)", "Seek (10)", unknown, unknown, +/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal", +/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position", +/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Deffect Data", +/* 38-3c */ unknown, "Compare","Copy Verify", "Write Buffer", "Read Buffer", +/* 3d-39 */ unknown, "Read Long", unknown, +}; + + +static const char *group_2_commands[] = { +/* 40-41 */ "Change Definition", unknown, +/* 42-48 */ unknown, unknown, unknown, unknown, unknown, unknown, unknown, +/* 49-4f */ unknown, unknown, unknown, "Log Select", "Log Sense", unknown, +/* 50-55 */ unknown, unknown, unknown, unknown, unknown, "Mode Select (10)", +/* 56-5b */ unknown, unknown, unknown, unknown, "Mode Sense (10)", unknown, +/* 5c-5f */ unknown, unknown, unknown, +}; + + + +#define group(opcode) (((opcode) >> 5) & 7) + +#define RESERVED_GROUP 0 +#define VENDOR_GROUP 1 +#define NOTEXT_GROUP 2 + +static const char **commands[] = { +group_0_commands, group_1_commands, group_2_commands, +(const char **) RESERVED_GROUP, (const char **) RESERVED_GROUP, +(const char **) NOTEXT_GROUP, (const char **) VENDOR_GROUP, +(const char **) VENDOR_GROUP}; + + +static void print_opcode(int opcode) { + char **table = commands[ group(opcode) ]; + switch ((int) table) { + case RESERVED_GROUP: + printk("%s(0x%02x) ", reserved, opcode); + break; + case NOTEXT_GROUP: + printk("%s(0x%02x) ", unknown, opcode); + break; + case VENDOR_GROUP: + printk("%s(0x%02x) ", vendor, opcode); + break; + default: + printk("%s ",table[opcode & 0x31]); + } +} +#else /* CONST & CONST_COMMAND */ +static void print_opcode(int opcode) { + printk("0x%02x ", opcode); +} +#endif + +void print_command (unsigned char *command) { + int i,s; + print_opcode(command[0]); + for ( i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i) + printk("%02x ", command[i]); + printk("\n"); +} + +#if (CONSTANTS & CONST_STATUS) +static const char * statuses[] = { +/* 0-4 */ "Good", "Check Condition", "Condition Good", unknown, "Busy", +/* 5-9 */ unknown, unknown, unknown, "Intermediate Good", unknown, +/* a-d */ "Interemediate Good", unknown, "Reservation Conflict", unknown, +/* e-f */ unknown, unknown, +}; +#endif + +void print_status (int status) { + status = (status >> 1) & 0xf; +#if (CONSTANTS & CONST_STATUS) + printk("%s ",statuses[status]); +#else + printk("0x%0x ", status); +#endif +} + +#if (CONSTANTS & CONST_MSG) +static const char * msgs[] = { +}; +void print_msg (int msg) { + printk("%s ", msgs[msg]); +} +#else +void print_msg (int msg) { + printk("0x%02x ", msg); +} +#endif + +#if (CONSTANTS & CONST_XSENSE) +#define D 0x001 /* DIRECT ACCESS DEVICE (disk) */ +#define T 0x002 /* SEQUENTIAL ACCESS DEVICE (tape) */ +#define L 0x004 /* PRINTER DEVICE */ +#define P 0x008 /* PROCESSOR DEVICE */ +#define W 0x010 /* WRITE ONCE READ MULTIPLE DEVICE */ +#define R 0x020 /* READ ONLY (CD-ROM) DEVICE */ +#define S 0x040 /* SCANNER DEVICE */ +#define O 0x080 /* OPTICAL MEMORY DEVICE */ +#define M 0x100 /* MEDIA CHANGER DEVICE */ +#define C 0x200 /* COMMUNICATION DEVICE */ + +struct error_info{ + unsigned char code1, code2; + unsigned short int devices; + char * text; +}; + +struct error_info2{ + unsigned char code1, code2_min, code2_max; + unsigned short int devices; + char * text; +}; + +static struct error_info2 additional2[] = +{ + {0x40,0x00,0x7f,D,"Ram failure (%x)"}, + {0x40,0x80,0xff,D|T|L|P|W|R|S|O|M|C,"Diagnostic failure on component (%x)"}, + {0x41,0x00,0xff,D,"Data path failure (%x)"}, + {0x42,0x00,0xff,D,"Power-on or self-test failure (%x)"}, + {0, 0, 0, 0, NULL} +}; + +static struct error_info additional[] = +{ + {0x00,0x01,T,"Filemark detected"}, + {0x00,0x02,T|S,"End-of-partition/medium detected"}, + {0x00,0x03,T,"Setmark detected"}, + {0x00,0x04,T|S,"Beginning-of-partition/medium detected"}, + {0x00,0x05,T|S,"End-of-data detected"}, + {0x00,0x06,D|T|L|P|W|R|S|O|M|C,"I/O process terminated"}, + {0x00,0x11,R,"Audio play operation in progress"}, + {0x00,0x12,R,"Audio play operation paused"}, + {0x00,0x13,R,"Audio play operation successfully completed"}, + {0x00,0x14,R,"Audio play operation stopped due to error"}, + {0x00,0x15,R,"No current audio status to return"}, + {0x01,0x00,D|W|O,"No index/sector signal"}, + {0x02,0x00,D|W|R|O|M,"No seek complete"}, + {0x03,0x00,D|T|L|W|S|O,"Peripheral device write fault"}, + {0x03,0x01,T,"No write current"}, + {0x03,0x02,T,"Excessive write errors"}, + {0x04,0x00,D|T|L|P|W|R|S|O|M|C, + "Logical unit not ready, cause not reportable"}, + {0x04,0x01,D|T|L|P|W|R|S|O|M|C, + "Logical unit is in process of becoming ready"}, + {0x04,0x02,D|T|L|P|W|R|S|O|M|C, + "Logical unit not ready, initializing command required"}, + {0x04,0x03,D|T|L|P|W|R|S|O|M|C, + "Logical unit not ready, manual intervention required"}, + {0x04,0x04,D|T|L|O,"Logical unit not ready, format in progress"}, + {0x05,0x00,D|T|L|W|R|S|O|M|C,"Logical unit does not respond to selection"}, + {0x06,0x00,D|W|R|O|M,"No reference position found"}, + {0x07,0x00,D|T|L|W|R|S|O|M,"Multiple peripheral devices selected"}, + {0x08,0x00,D|T|L|W|R|S|O|M|C,"Logical unit communication failure"}, + {0x08,0x01,D|T|L|W|R|S|O|M|C,"Logical unit communication time-out"}, + {0x08,0x02,D|T|L|W|R|S|O|M|C,"Logical unit communication parity error"}, + {0x09,0x00,D|T|W|R|O,"Track following error"}, + {0x09,0x01,W|R|O,"Tracking servo failure"}, + {0x09,0x02,W|R|O,"Focus servo failure"}, + {0x09,0x03,W|R|O,"Spindle servo failure"}, + {0x0A,0x00,D|T|L|P|W|R|S|O|M|C,"Error log overflow"}, + {0x0C,0x00,T|S,"Write error"}, + {0x0C,0x01,D|W|O,"Write error recovered with auto reallocation"}, + {0x0C,0x02,D|W|O,"Write error - auto reallocation failed"}, + {0x10,0x00,D|W|O,"Id crc or ecc error"}, + {0x11,0x00,D|T|W|R|S|O,"Unrecovered read error"}, + {0x11,0x01,D|T|W|S|O,"Read retries exhausted"}, + {0x11,0x02,D|T|W|S|O,"Error too long to correct"}, + {0x11,0x03,D|T|W|S|O,"Multiple read errors"}, + {0x11,0x04,D|W|O,"Unrecovered read error - auto reallocate failed"}, + {0x11,0x05,W|R|O,"L-ec uncorrectable error"}, + {0x11,0x06,W|R|O,"Circ unrecovered error"}, + {0x11,0x07,W|O,"Data resychronization error"}, + {0x11,0x08,T,"Incomplete block read"}, + {0x11,0x09,T,"No gap found"}, + {0x11,0x0A,D|T|O,"Miscorrected error"}, + {0x11,0x0B,D|W|O,"Unrecovered read error - recommend reassignment"}, + {0x11,0x0C,D|W|O,"Unrecovered read error - recommend rewrite the data"}, + {0x12,0x00,D|W|O,"Address mark not found for id field"}, + {0x13,0x00,D|W|O,"Address mark not found for data field"}, + {0x14,0x00,D|T|L|W|R|S|O,"Recorded entity not found"}, + {0x14,0x01,D|T|W|R|O,"Record not found"}, + {0x14,0x02,T,"Filemark or setmark not found"}, + {0x14,0x03,T,"End-of-data not found"}, + {0x14,0x04,T,"Block sequence error"}, + {0x15,0x00,D|T|L|W|R|S|O|M,"Random positioning error"}, + {0x15,0x01,D|T|L|W|R|S|O|M,"Mechanical positioning error"}, + {0x15,0x02,D|T|W|R|O,"Positioning error detected by read of medium"}, + {0x16,0x00,D|W|O,"Data synchronization mark error"}, + {0x17,0x00,D|T|W|R|S|O,"Recovered data with no error correction applied"}, + {0x17,0x01,D|T|W|R|S|O,"Recovered data with retries"}, + {0x17,0x02,D|T|W|R|O,"Recovered data with positive head offset"}, + {0x17,0x03,D|T|W|R|O,"Recovered data with negative head offset"}, + {0x17,0x04,W|R|O,"Recovered data with retries and/or circ applied"}, + {0x17,0x05,D|W|R|O,"Recovered data using previous sector id"}, + {0x17,0x06,D|W|O,"Recovered data without ecc - data auto-reallocated"}, + {0x17,0x07,D|W|O,"Recovered data without ecc - recommend reassignment"}, + {0x18,0x00,D|T|W|R|O,"Recovered data with error correction applied"}, + {0x18,0x01,D|W|R|O,"Recovered data with error correction and retries applied"}, + {0x18,0x02,D|W|R|O,"Recovered data - data auto-reallocated"}, + {0x18,0x03,R,"Recovered data with circ"}, + {0x18,0x04,R,"Recovered data with lec"}, + {0x18,0x05,D|W|R|O,"Recovered data - recommend reassignment"}, + {0x19,0x00,D|O,"Defect list error"}, + {0x19,0x01,D|O,"Defect list not available"}, + {0x19,0x02,D|O,"Defect list error in primary list"}, + {0x19,0x03,D|O,"Defect list error in grown list"}, + {0x1A,0x00,D|T|L|P|W|R|S|O|M|C,"Parameter list length error"}, + {0x1B,0x00,D|T|L|P|W|R|S|O|M|C,"Synchronous data transfer error"}, + {0x1C,0x00,D|O,"Defect list not found"}, + {0x1C,0x01,D|O,"Primary defect list not found"}, + {0x1C,0x02,D|O,"Grown defect list not found"}, + {0x1D,0x00,D|W|O,"Miscompare during verify operation"}, + {0x1E,0x00,D|W|O,"Recovered id with ecc correction"}, + {0x20,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid command operation code"}, + {0x21,0x00,D|T|W|R|O|M,"Logical block address out of range"}, + {0x21,0x01,M,"Invalid element address"}, + {0x22,0x00,D,"Illegal function (should use 20 00, 24 00, or 26 00)"}, + {0x24,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in cdb"}, + {0x25,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit not supported"}, + {0x26,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in parameter list"}, + {0x26,0x01,D|T|L|P|W|R|S|O|M|C,"Parameter not supported"}, + {0x26,0x02,D|T|L|P|W|R|S|O|M|C,"Parameter value invalid"}, + {0x26,0x03,D|T|L|P|W|R|S|O|M|C,"Threshold parameters not supported"}, + {0x27,0x00,D|T|W|O,"Write protected"}, + {0x28,0x00,D|T|L|P|W|R|S|O|M|C,"Not ready to ready transition (medium may have changed)"}, + {0x28,0x01,M,"Import or export element accessed"}, + {0x29,0x00,D|T|L|P|W|R|S|O|M|C,"Power on, reset, or bus device reset occurred"}, + {0x2A,0x00,D|T|L|W|R|S|O|M|C,"Parameters changed"}, + {0x2A,0x01,D|T|L|W|R|S|O|M|C,"Mode parameters changed"}, + {0x2A,0x02,D|T|L|W|R|S|O|M|C,"Log parameters changed"}, + {0x2B,0x00,D|T|L|P|W|R|S|O|C,"Copy cannot execute since host cannot disconnect"}, + {0x2C,0x00,D|T|L|P|W|R|S|O|M|C,"Command sequence error"}, + {0x2C,0x01,S,"Too many windows specified"}, + {0x2C,0x02,S,"Invalid combination of windows specified"}, + {0x2D,0x00,T,"Overwrite error on update in place"}, + {0x2F,0x00,D|T|L|P|W|R|S|O|M|C,"Commands cleared by another initiator"}, + {0x30,0x00,D|T|W|R|O|M,"Incompatible medium installed"}, + {0x30,0x01,D|T|W|R|O,"Cannot read medium - unknown format"}, + {0x30,0x02,D|T|W|R|O,"Cannot read medium - incompatible format"}, + {0x30,0x03,D|T,"Cleaning cartridge installed"}, + {0x31,0x00,D|T|W|O,"Medium format corrupted"}, + {0x31,0x01,D|L|O,"Format command failed"}, + {0x32,0x00,D|W|O,"No defect spare location available"}, + {0x32,0x01,D|W|O,"Defect list update failure"}, + {0x33,0x00,T,"Tape length error"}, + {0x36,0x00,L,"Ribbon, ink, or toner failure"}, + {0x37,0x00,D|T|L|W|R|S|O|M|C,"Rounded parameter"}, + {0x39,0x00,D|T|L|W|R|S|O|M|C,"Saving parameters not supported"}, + {0x3A,0x00,D|T|L|W|R|S|O|M,"Medium not present"}, + {0x3B,0x00,T|L,"Sequential positioning error"}, + {0x3B,0x01,T,"Tape position error at beginning-of-medium"}, + {0x3B,0x02,T,"Tape position error at end-of-medium"}, + {0x3B,0x03,L,"Tape or electronic vertical forms unit not ready"}, + {0x3B,0x04,L,"Slew failure"}, + {0x3B,0x05,L,"Paper jam"}, + {0x3B,0x06,L,"Failed to sense top-of-form"}, + {0x3B,0x07,L,"Failed to sense bottom-of-form"}, + {0x3B,0x08,T,"Reposition error"}, + {0x3B,0x09,S,"Read past end of medium"}, + {0x3B,0x0A,S,"Read past beginning of medium"}, + {0x3B,0x0B,S,"Position past end of medium"}, + {0x3B,0x0C,S,"Position past beginning of medium"}, + {0x3B,0x0D,M,"Medium destination element full"}, + {0x3B,0x0E,M,"Medium source element empty"}, + {0x3D,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid bits in identify message"}, + {0x3E,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit has not self-configured yet"}, + {0x3F,0x00,D|T|L|P|W|R|S|O|M|C,"Target operating conditions have changed"}, + {0x3F,0x01,D|T|L|P|W|R|S|O|M|C,"Microcode has been changed"}, + {0x3F,0x02,D|T|L|P|W|R|S|O|M|C,"Changed operating definition"}, + {0x3F,0x03,D|T|L|P|W|R|S|O|M|C,"Inquiry data has changed"}, + {0x43,0x00,D|T|L|P|W|R|S|O|M|C,"Message error"}, + {0x44,0x00,D|T|L|P|W|R|S|O|M|C,"Internal target failure"}, + {0x45,0x00,D|T|L|P|W|R|S|O|M|C,"Select or reselect failure"}, + {0x46,0x00,D|T|L|P|W|R|S|O|M|C,"Unsuccessful soft reset"}, + {0x47,0x00,D|T|L|P|W|R|S|O|M|C,"Scsi parity error"}, + {0x48,0x00,D|T|L|P|W|R|S|O|M|C,"Initiator detected error message received"}, + {0x49,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid message error"}, + {0x4A,0x00,D|T|L|P|W|R|S|O|M|C,"Command phase error"}, + {0x4B,0x00,D|T|L|P|W|R|S|O|M|C,"Data phase error"}, + {0x4C,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit failed self-configuration"}, + {0x4E,0x00,D|T|L|P|W|R|S|O|M|C,"Overlapped commands attempted"}, + {0x50,0x00,T,"Write append error"}, + {0x50,0x01,T,"Write append position error"}, + {0x50,0x02,T,"Position error related to timing"}, + {0x51,0x00,T|O,"Erase failure"}, + {0x52,0x00,T,"Cartridge fault"}, + {0x53,0x00,D|T|L|W|R|S|O|M,"Media load or eject failed"}, + {0x53,0x01,T,"Unload tape failure"}, + {0x53,0x02,D|T|W|R|O|M,"Medium removal prevented"}, + {0x54,0x00,P,"Scsi to host system interface failure"}, + {0x55,0x00,P,"System resource failure"}, + {0x57,0x00,R,"Unable to recover table-of-contents"}, + {0x58,0x00,O,"Generation does not exist"}, + {0x59,0x00,O,"Updated block read"}, + {0x5A,0x00,D|T|L|P|W|R|S|O|M,"Operator request or state change input (unspecified)"}, + {0x5A,0x01,D|T|W|R|O|M,"Operator medium removal request"}, + {0x5A,0x02,D|T|W|O,"Operator selected write protect"}, + {0x5A,0x03,D|T|W|O,"Operator selected write permit"}, + {0x5B,0x00,D|T|L|P|W|R|S|O|M,"Log exception"}, + {0x5B,0x01,D|T|L|P|W|R|S|O|M,"Threshold condition met"}, + {0x5B,0x02,D|T|L|P|W|R|S|O|M,"Log counter at maximum"}, + {0x5B,0x03,D|T|L|P|W|R|S|O|M,"Log list codes exhausted"}, + {0x5C,0x00,D|O,"Rpl status change"}, + {0x5C,0x01,D|O,"Spindles synchronized"}, + {0x5C,0x02,D|O,"Spindles not synchronized"}, + {0x60,0x00,S,"Lamp failure"}, + {0x61,0x00,S,"Video acquisition error"}, + {0x61,0x01,S,"Unable to acquire video"}, + {0x61,0x02,S,"Out of focus"}, + {0x62,0x00,S,"Scan head positioning error"}, + {0x63,0x00,R,"End of user area encountered on this track"}, + {0x64,0x00,R,"Illegal mode for this track"}, + {0, 0, 0, NULL} +}; +#endif + +#if (CONSTANTS & CONST_SENSE) +static char *snstext[] = { + "None","Recovered Error","Not Ready","Medium Error","Hardware Error", + "Illegal Request","Unit Attention","Data Protect","Blank Check", + "Key=E","Key=F","Filemark","End-Of-Medium","Incorrect Block Length", + "14","15"}; +#endif + + +/* Print sense information */ +void print_sense(char * devclass, Scsi_Cmnd * SCpnt) +{ + int i, s; + int sense_class, valid, code; + unsigned char * sense_buffer = SCpnt->sense_buffer; + char * error = NULL; + int dev = SCpnt->request.dev; + + sense_class = (sense_buffer[0] >> 4) & 0x07; + code = sense_buffer[0] & 0xf; + valid = sense_buffer[0] & 0x80; + + if (sense_class == 7) { + s = sense_buffer[7] + 8; + if(s > sizeof(SCpnt->sense_buffer)) s = sizeof(SCpnt->sense_buffer); + + if (!valid) + printk("extra data not valid "); + + if (sense_buffer[2] & 0x80) printk( "FMK "); + if (sense_buffer[2] & 0x40) printk( "EOM "); + if (sense_buffer[2] & 0x20) printk( "ILI "); + + switch (code) { + case 0x0: + error = "Current"; + break; + case 0x1: + error = "Deferred"; + break; + default: + error = "Invalid"; + } + + printk("%s error ", error); + +#if (CONSTANTS & CONST_SENSE) + if (sense_buffer[2] & 0x80) printk( "FMK "); + if (sense_buffer[2] & 0x40) printk( "EOM "); + if (sense_buffer[2] & 0x20) printk( "ILI "); + printk( "%s%x: sense key %s\n", devclass, dev, snstext[sense_buffer[2] & 0x0f]); +#else + printk("%s%x: sns = %2x %2x\n", devclass, dev, sense_buffer[0], sense_buffer[2]); +#endif + + /* Check to see if additional sense information is available */ + if(sense_buffer[7] + 7 < 13 || + (sense_buffer[12] == 0 && sense_buffer[13] == 0)) goto done; + +#if (CONSTANTS & CONST_XSENSE) + for(i=0; additional[i].text; i++) + if(additional[i].code1 == sense_buffer[12] && + additional[i].code2 == sense_buffer[13]) + printk("Additional sense indicates %s\n", additional[i].text); + + for(i=0; additional2[i].text; i++) + if(additional2[i].code1 == sense_buffer[12] && + additional2[i].code2_min >= sense_buffer[13] && + additional2[i].code2_max <= sense_buffer[13]) { + printk("Additional sense indicates "); + printk(additional2[i].text, sense_buffer[13]); + printk("\n"); + }; +#else + printk("ASC=%2x ASCQ=%2x\n", sense_buffer[12], sense_buffer[13]); +#endif + } else { + +#if (CONSTANTS & CONST_SENSE) + if (sense_buffer[0] < 15) + printk("%s%x: old sense key %s\n", devclass, dev, snstext[sense_buffer[0] & 0x0f]); + else +#endif + printk("%s%x: sns = %2x %2x\n", devclass, dev, sense_buffer[0], sense_buffer[2]); + + printk("Non-extended sense class %d code 0x%0x ", sense_class, code); + s = 4; + } + + done: + for (i = 0; i < s; ++i) + printk("0x%02x ", sense_buffer[i]); + + return; +} + + +#endif CONFIG_SCSI diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/constants.h linux/kernel/blk_drv/scsi/constants.h --- pl11/linux/kernel/blk_drv/scsi/constants.h +++ linux/kernel/blk_drv/scsi/constants.h Wed Jul 28 00:19:33 1993 @@ -0,0 +1,6 @@ +#ifndef _CONSTANTS_H +#define _CONSTANTS_H +extern void print_command(unsigned char *); +extern void print_sense(char *, Scsi_Cmnd *); +extern void print_status(int);; +#endif /* def _CONSTANTS_H */ diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/fdomain.c linux/kernel/blk_drv/scsi/fdomain.c --- pl11/linux/kernel/blk_drv/scsi/fdomain.c Sat Jul 3 22:07:48 1993 +++ linux/kernel/blk_drv/scsi/fdomain.c Sat Aug 14 09:05:16 1993 @@ -1,10 +1,10 @@ /* fdomain.c -- Future Domain TMC-16x0 driver * Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu - * Revised: Sat Jul 3 13:48:18 1993 by faith@cs.unc.edu + * Revised: Fri Aug 13 22:44:05 1993 by faith@cs.unc.edu * Author: Rickard E. Faith, faith@cs.unc.edu * Copyright 1992, 1993 Rickard E. Faith * - * $Id: fdomain.c,v 3.17 1993/07/03 14:06:31 root Exp $ + * $Id: fdomain.c,v 3.18 1993/08/13 22:44:12 root Exp $ * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -17,8 +17,6 @@ * General Public License for more details. ************************************************************************** - - DESCRIPTION: @@ -141,7 +139,7 @@ #include #include -#define VERSION "$Revision: 3.17 $" +#define VERSION "$Revision: 3.18 $" /* START OF USER DEFINABLE OPTIONS */ @@ -240,7 +238,6 @@ static volatile int in_interrupt_flag = 0; #endif - static int SCSI_Mode_Cntl_port; static int FIFO_Data_Count_port; static int Interrupt_Cntl_port; @@ -277,11 +274,12 @@ READ EVERY WORD, ESPECIALLY THE WORD *NOT* - This driver works *ONLY* for Future Domain cards using the TMC-1800 chip. - This includes models TMC-1660, 1670, and 1680 *ONLY*. + This driver works *ONLY* for Future Domain cards using the TMC-1800 or + the TMC-18C50 chip. This includes models TMC-1650, 1660, 1670, and 1680. The following BIOS signature signatures are for boards which do *NOT* - work with this driver (but they should work with the Seagate driver): + work with this driver (these TMC-8xx and TMC-9xx boards may work with the + Seagate driver): FUTURE DOMAIN CORP. (C) 1986-1988 V4.0I 03/16/88 FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89 @@ -302,14 +300,22 @@ } signatures[] = { /* 1 2 3 4 5 6 */ /* 123456789012345678901234567890123456789012345678901234567890 */ - { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 5, 50, 2, 0 }, - { "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92", 5, 44, 3, 0 }, - { "FUTURE DOMAIN TMC-18XX (C) 1993 V3.203/12/93", 5, 44, 3, 2 }, + { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 5, 50, 2, 0 }, + { "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92", 5, 44, 3, 0 }, + { "FUTURE DOMAIN TMC-18XX (C) 1993 V3.203/12/93", 5, 44, 3, 2 }, + { "FUTURE DOMAIN TMC-18XX", 5, 22, -1, -1 }, /* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGANTURE Also, fix the disk geometry code for your signature and send your changes for faith@cs.unc.edu. Above all, do *NOT* change any old - signatures! */ + signatures! + + Note that the last line will match a "generic" 18XX bios. Because + Future Domain has changed the host SCSI ID and/or the location of the + geometry information in the on-board RAM area for each of the first + three BIOS's, it is still important to enter a fully qualified + signature in the table for any new BIOS's (after the host SCSI ID and + geometry location are verified. */ }; #define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature )) @@ -561,6 +567,46 @@ return 0; } + this_host = hostnum; + + if (!interrupt_level) { + panic( "Future Domain: *NO* interrupt level selected!\n" ); + } else { + /* Register the IRQ with the kernel */ + + sa.sa_handler = fdomain_16x0_intr; + sa.sa_flags = SA_INTERRUPT; + sa.sa_mask = 0; + sa.sa_restorer = NULL; + + retcode = irqaction( interrupt_level, &sa ); + + if (retcode < 0) { + if (retcode == -EINVAL) { + printk( "Future Domain: IRQ %d is bad!\n", interrupt_level ); + printk( " This shouldn't happen!\n" ); + printk( " Send mail to faith@cs.unc.edu\n" ); + } else if (retcode == -EBUSY) { + printk( "Future Domain: IRQ %d is already in use!\n", + interrupt_level ); + printk( " Please use another IRQ!\n" ); + } else { + printk( "Future Domain: Error getting IRQ %d\n", interrupt_level ); + printk( " This shouldn't happen!\n" ); + printk( " Send mail to faith@cs.unc.edu\n" ); + } + panic( "Future Domain: Driver requires interruptions\n" ); + } else { + printk( "Future Domain: IRQ %d requested from kernel\n", + interrupt_level ); + } + } + + if ((bios_major == 3 && bios_minor >= 2) || bios_major < 0) { + adapter_mask = 0x80; + scsi_hosts[this_host].this_id = 7; + } + #if DO_DETECT /* These routines are here because of the way the SCSI bus behaves after @@ -614,46 +660,6 @@ } #endif - this_host = hostnum; - - if (!interrupt_level) { - panic( "Future Domain: *NO* interrupt level selected!\n" ); - } else { - /* Register the IRQ with the kernel */ - - sa.sa_handler = fdomain_16x0_intr; - sa.sa_flags = SA_INTERRUPT; - sa.sa_mask = 0; - sa.sa_restorer = NULL; - - retcode = irqaction( interrupt_level, &sa ); - - if (retcode < 0) { - if (retcode == -EINVAL) { - printk( "Future Domain: IRQ %d is bad!\n", interrupt_level ); - printk( " This shouldn't happen!\n" ); - printk( " Send mail to faith@cs.unc.edu\n" ); - } else if (retcode == -EBUSY) { - printk( "Future Domain: IRQ %d is already in use!\n", - interrupt_level ); - printk( " Please use another IRQ!\n" ); - } else { - printk( "Future Domain: Error getting IRQ %d\n", interrupt_level ); - printk( " This shouldn't happen!\n" ); - printk( " Send mail to faith@cs.unc.edu\n" ); - } - panic( "Future Domain: Driver requires interruptions\n" ); - } else { - printk( "Future Domain: IRQ %d requested from kernel\n", - interrupt_level ); - } - } - - if (bios_major == 3 && bios_minor >= 2) { - adapter_mask = 0x80; - scsi_hosts[this_host].this_id = 7; - } - return 1; } @@ -1306,12 +1312,31 @@ return 0; } +/* The following code, which simulates the old-style command function, was + taken from Tommy Thorn's aha1542.c file. This code is Copyright (C) + 1992 Tommy Thorn. */ + +static volatile int internal_done_flag = 0; +static volatile int internal_done_errcode = 0; + +static void internal_done( Scsi_Cmnd *SCpnt ) +{ + internal_done_errcode = SCpnt->result; + ++internal_done_flag; +} + int fdomain_16x0_command( Scsi_Cmnd *SCpnt ) { - panic( "Future Domain: must use interrupt-driven driver\n" ); - return -1; + fdomain_16x0_queue( SCpnt, internal_done ); + + while (!internal_done_flag) + ; + internal_done_flag = 0; + return internal_done_errcode; } +/* End of code derived from Tommy Thorn's work. */ + void print_info( Scsi_Cmnd *SCpnt ) { unsigned int imr; @@ -1490,12 +1515,24 @@ info_array[2] = i->cylinders; } else { /* How the data is stored in the RAM area is very BIOS-dependent. - Therefore, return nothing if we are not *SURE* of the information. */ + Therefore, assume a version 3 layout, and check for validity. */ - info_array[0] - = info_array[1] - = info_array[2] - = 0; + i = (struct drive_info *)( (char *)bios_base + 0x1f71 + drive * 10 ); + info_array[0] = i->heads + 1; + info_array[1] = i->sectors; + info_array[2] = i->cylinders; + + if (!info_array[0] + || !info_array[1] + || !info_array[2] + || info_array[2] > 1024 /* DOS uses only 10 bits */ + ) { + + info_array[0] + = info_array[1] + = info_array[2] + = 0; + } } return 0; diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/hosts.c linux/kernel/blk_drv/scsi/hosts.c --- pl11/linux/kernel/blk_drv/scsi/hosts.c Wed Mar 31 14:02:12 1993 +++ linux/kernel/blk_drv/scsi/hosts.c Wed Jul 28 00:19:33 1993 @@ -106,6 +106,8 @@ #endif }; +#define MAX_SCSI_HOSTS (sizeof(scsi_hosts) / sizeof(Scsi_Host)) + /* * Our semaphores and timeout counters, where size depends on MAX_SCSI_HOSTS here. */ diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/scsi.c linux/kernel/blk_drv/scsi/scsi.c --- pl11/linux/kernel/blk_drv/scsi/scsi.c Sat Jul 3 03:18:12 1993 +++ linux/kernel/blk_drv/scsi/scsi.c Sun Aug 15 10:55:23 1993 @@ -23,11 +23,15 @@ #include "../blk.h" #include "scsi.h" #include "hosts.h" +#include "constants.h" /* static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/scsi.c,v 1.1 1992/07/24 06:27:38 root Exp root $"; */ +/* Command groups 3 and 4 are reserved and should never be used. */ +const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, 12, 12, 10, 10 }; + #define INTERNAL_ERROR (printk ("Internal error in file %s, line %d.\n", __FILE__, __LINE__), panic("")) static void scsi_done (Scsi_Cmnd *SCpnt); @@ -91,7 +95,7 @@ #define SENSE_TIMEOUT 50 #define RESET_TIMEOUT 50 #define ABORT_TIMEOUT 50 - #define MIN_RESET_DELAY 25 + #define MIN_RESET_DELAY 100 #endif /* The following devices are known not to tolerate a lun != 0 scan for @@ -109,6 +113,9 @@ {"SEAGATE","ST296","921"}, /* Responds to all lun */ {"SONY","CD-ROM CDU-541","4.3d"}, {"DENON","DRD-25X","V"}, /* A cdrom that locks up when probed at lun != 0 */ + {"TEXEL","CD-ROM","1.06"}, /* causes failed REQUEST SENSE on lun 1 for seagate + * controller, which causes SCSI code to reset bus.*/ + {"MAXTOR","XT-4380S","B3C"}, /* Locks-up when LUN>0 polled. */ {NULL, NULL, NULL}}; static int blacklisted(unsigned char * response_data){ @@ -174,7 +181,15 @@ time out */ for (dev = 0; dev < 8; ++dev) if (scsi_hosts[host_nr].this_id != dev) +/* + * We need the for so our continue, etc. work fine. + */ + +#ifdef NO_MULTI_LUN + for (lun = 0; lun < 1; ++lun) +#else for (lun = 0; lun < 8; ++lun) +#endif { scsi_devices[NR_SCSI_DEVICES].host_no = host_nr; scsi_devices[NR_SCSI_DEVICES].id = dev; @@ -181,6 +196,11 @@ scsi_devices[NR_SCSI_DEVICES].lun = lun; scsi_devices[NR_SCSI_DEVICES].index = NR_SCSI_DEVICES; scsi_devices[NR_SCSI_DEVICES].device_wait = NULL; +/* + * Assume that the device will have handshaking problems, and then + * fix this field later if it turns out it doesn't. + */ + scsi_devices[NR_SCSI_DEVICES].borken = 1; scsi_cmd[0] = TEST_UNIT_READY; scsi_cmd[1] = lun << 5; @@ -196,14 +216,20 @@ SCmd.old_use_sg = 0; SCmd.transfersize = 0; SCmd.underflow = 0; + SCmd.index = NR_SCSI_DEVICES; scsi_do_cmd (&SCmd, (void *) scsi_cmd, (void *) scsi_result, 256, scan_scsis_done, - SCSI_TIMEOUT + 400, 3); + SCSI_TIMEOUT + 400, 5); while (SCmd.request.dev != 0xfffe); +#if defined(DEBUG) || defined(DEBUG_INIT) + printk("scsi: scan SCSIS id %d lun %d\n", dev, lun); + printk("scsi: return code %08x\n", SCmd.result); +#endif + if(SCmd.result) { if ((driver_byte(SCmd.result) & DRIVER_SENSE) && ((SCmd.sense_buffer[0] & 0x70) >> 4) == 7) { @@ -217,6 +243,10 @@ break; }; +#if defined (DEBUG) || defined(DEBUG_INIT) + printk("scsi: performing INQUIRY\n"); +#endif + /* * Build an INQUIRY command block. */ @@ -239,7 +269,15 @@ the_result = SCmd.result; +#if defined(DEBUG) || defined(DEBUG_INIT) + if (!the_result) + printk("scsi: INQUIRY successful\n"); + else + printk("scsi: INQUIRY failed with code %08x\n"); +#endif + if(the_result) break; + /* skip other luns on this device */ if (!the_result) @@ -273,6 +311,12 @@ scsi_devices[NR_SCSI_DEVICES].writeable = 0; break; default : +#if 0 +#ifdef DEBUG + printk("scsi: unknown type %d\n", type); + print_inquiry(scsi_result); +#endif +#endif type = -1; } @@ -311,6 +355,29 @@ (scsi_result[3] & 0x0f) == 1)) scsi_devices[NR_SCSI_DEVICES].scsi_level++; +/* + * Some revisions of the Texel CD ROM drives have handshaking + * problems when used with the Seagate controllers. Before we + * know what type of device we're talking to, we assume it's + * borken and then change it here if it turns out that it isn't + * a TEXEL drive. + */ + + if(memcmp("TEXEL", &scsi_result[8], 5) != 0 || + memcmp("CD-ROM", &scsi_result[16], 6) != 0 +/* + * XXX 1.06 has problems, some one should figure out the others too so + * ALL TEXEL drives don't suffer in performance, especially when I finish + * integrating my seagate patches which do multiple I_T_L nexuses. + */ + +#ifdef notyet + || (strncmp("1.06", &scsi_result[[, 4) != 0) +#endif + ) + scsi_devices[NR_SCSI_DEVICES].borken = 0; + + /* These devices need this "key" to unlock the device so we can use it */ if(memcmp("INSITE", &scsi_result[8], 6) == 0 && @@ -693,6 +760,9 @@ SCpnt->timeout_per_command = timeout; memcpy ((void *) SCpnt->cmnd , (void *) cmnd, 10); + /* Zero the sense buffer. Some host adapters automatically request + sense on error. 0 is not a valid sense code. */ + memset ((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer); SCpnt->request_buffer = buffer; SCpnt->request_bufflen = bufflen; SCpnt->old_use_sg = SCpnt->use_sg; @@ -718,7 +788,7 @@ static void reset (Scsi_Cmnd * SCpnt) { #ifdef DEBUG - printk("reset(%d)\n", SCpnt->host); + printk("scsi: reset(%d)\n", SCpnt->host); #endif SCpnt->flags |= (WAS_RESET | IS_RESETTING); @@ -735,9 +805,18 @@ static int check_sense (Scsi_Cmnd * SCpnt) { - if (((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) { + /* If there is no sense information, request it. */ + if (((SCpnt->sense_buffer[0] & 0x70) >> 4) != 7) + return SUGGEST_SENSE; + +#ifdef DEBUG_INIT + printk("scsi%d : ", SCpnt->host); + print_sense("", SCpnt); + printk("\n"); +#endif if (SCpnt->sense_buffer[2] &0xe0) return SUGGEST_ABORT; + switch (SCpnt->sense_buffer[2] & 0xf) { case NO_SENSE: @@ -765,9 +844,6 @@ return SUGGEST_ABORT; } } - else - return SUGGEST_RETRY; - } /* This function is the mid-level interrupt routine, which decides how * to handle error conditions. Each invocation of this function must @@ -824,8 +900,8 @@ break; } - if (status_byte(result) && (SCpnt->flags & - WAS_SENSE)) /* Failed to obtain sense information */ + if (status_byte(result) && (SCpnt->flags & WAS_SENSE)) + /* Failed to obtain sense information */ { SCpnt->flags &= ~WAS_SENSE; SCpnt->internal_timeout &= ~SENSE_TIMEOUT; @@ -832,6 +908,8 @@ if (!(SCpnt->flags & WAS_RESET)) { + printk("scsi%d : target %d lun %d request sense failed, performing reset.\n", + SCpnt->host, SCpnt->target, SCpnt->lun); reset(SCpnt); return; } @@ -858,6 +936,7 @@ switch (checked = check_sense(SCpnt)) { + case SUGGEST_SENSE: case 0: #ifdef DEBUG printk("NO SENSE. status = REDO\n"); @@ -900,32 +979,41 @@ break; case CHECK_CONDITION: - -#ifdef DEBUG - printk("CHECK CONDITION message returned, performing request sense.\n"); -#endif - + switch (check_sense(SCpnt)) + { + case 0: + update_timeout(SCpnt, oldto); + status = REDO; + break; + case SUGGEST_REMAP: + case SUGGEST_RETRY: + status = MAYREDO; + exit = DRIVER_SENSE | SUGGEST_RETRY; + break; + case SUGGEST_ABORT: + status = FINISHED; + exit = DRIVER_SENSE | SUGGEST_ABORT; + break; + case SUGGEST_SENSE: scsi_request_sense (SCpnt); status = PENDING; break; + } + break; case CONDITION_GOOD: case INTERMEDIATE_GOOD: case INTERMEDIATE_C_GOOD: -#ifdef DEBUG - printk("CONDITION GOOD, INTERMEDIATE GOOD, or INTERMEDIATE CONDITION GOOD recieved and ignored. \n"); -#endif break; case BUSY: -#ifdef DEBUG - printk("BUSY message returned, performing REDO"); -#endif update_timeout(SCpnt, oldto); status = REDO; break; case RESERVATION_CONFLICT: + printk("scsi%d : RESERVATION CONFLICT performing reset.\n", + SCpnt->host); reset(SCpnt); return; #if 0 @@ -987,12 +1075,29 @@ case DID_RESET: if(msg_byte(result) == GOOD && status_byte(result) == CHECK_CONDITION) { + switch (check_sense(SCpnt)) { + case 0: + update_timeout(SCpnt, oldto); + status = REDO; + break; + case SUGGEST_REMAP: + case SUGGEST_RETRY: + status = MAYREDO; + exit = DRIVER_SENSE | SUGGEST_RETRY; + break; + case SUGGEST_ABORT: + status = FINISHED; + exit = DRIVER_SENSE | SUGGEST_ABORT; + break; + case SUGGEST_SENSE: scsi_request_sense (SCpnt); status = PENDING; break; - }; + } + } else { status=REDO; exit = SUGGEST_RETRY; + } break; default : exit = (DRIVER_ERROR | SUGGEST_DIE); @@ -1015,6 +1120,8 @@ if ((SCpnt->retries >= (SCpnt->allowed >> 1)) && !(SCpnt->flags & WAS_RESET)) { + printk("scsi%d : reseting for second half of retries.\n", + SCpnt->host); reset(SCpnt); break; } diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/scsi.h linux/kernel/blk_drv/scsi/scsi.h --- pl11/linux/kernel/blk_drv/scsi/scsi.h Sat Jul 3 03:18:32 1993 +++ linux/kernel/blk_drv/scsi/scsi.h Wed Jul 28 00:19:33 1993 @@ -76,28 +76,8 @@ #define MODE_SELECT_10 0x55 #define MODE_SENSE_10 0x5a -static __inline__ int COMMAND_SIZE (int opcode) { - int group = (opcode >> 5) & 7; - switch (group) { - case 0: - return 6; - case 1: - case 2: - return 10; - case 3: - case 4: - printk("COMMAND_SIZE : reserved command group %d\n", group); - panic (""); - case 5: - return 12; - default: -#ifdef DEBUG - printk("COMMAND_SIZE : vendor specific command group %d - assuming" - " 10 bytes\n", group); -#endif - return 10; - } -} +extern const unsigned char scsi_command_size[8]; +#define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7] /* MESSAGE CODES @@ -206,6 +186,7 @@ #define SUGGEST_ABORT 0x20 #define SUGGEST_REMAP 0x30 #define SUGGEST_DIE 0x40 +#define SUGGEST_SENSE 0x80 #define DRIVER_SENSE 0x08 @@ -280,6 +261,8 @@ unsigned changed:1; /* Data invalid due to media change */ unsigned busy:1; /* Used to prevent races */ unsigned lockable:1; /* Able to prevent media removal */ + unsigned borken:1; /* Tell the Seagate driver to be + painfully slow on this device */ } Scsi_Device; /* Use these to separate status msg and our bytes diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/scsi_debug.c linux/kernel/blk_drv/scsi/scsi_debug.c --- pl11/linux/kernel/blk_drv/scsi/scsi_debug.c Thu Nov 12 17:01:28 1992 +++ linux/kernel/blk_drv/scsi/scsi_debug.c Mon Aug 9 18:02:31 1993 @@ -402,7 +402,7 @@ }; if(i == SCSI_DEBUG_MAILBOXES){ - pending = 0x7fffffff; + pending = INT_MAX; for(i=0;i @@ -38,7 +39,7 @@ * Time out in seconds */ -#define SD_TIMEOUT 200 +#define SD_TIMEOUT 300 struct hd_struct * sd; @@ -245,11 +246,6 @@ */ if (driver_byte(result) != 0) { - { - int i; - printk("Dumping sense buffer: "); - for(i=0;i<10;i++) printk(" %d",SCpnt->sense_buffer[i]); - }; if (sugestion(result) == SUGGEST_REMAP) { #ifdef REMAP /* @@ -301,11 +297,7 @@ rscsi_disks[DEVICE_NR(SCpnt->request.dev)].device->lun, result); if (driver_byte(result) & DRIVER_SENSE) - printk("\tSense class %x, sense error %x, extended sense %x\n", - sense_class(SCpnt->sense_buffer[0]), - sense_error(SCpnt->sense_buffer[0]), - SCpnt->sense_buffer[2] & 0xf); - + print_sense("sd", SCpnt); end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors); requeue_sd_request(SCpnt); return; @@ -675,7 +667,7 @@ { int j = 0; unsigned char cmd[10]; - unsigned char buffer[513]; + unsigned char *buffer; int the_result, retries; Scsi_Cmnd * SCpnt; @@ -684,6 +676,7 @@ bus reset. */ SCpnt = allocate_device(NULL, rscsi_disks[i].device->index, 1); + buffer = (unsigned char *) scsi_malloc(512); retries = 3; do { @@ -690,6 +683,7 @@ cmd[0] = READ_CAPACITY; cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0; memset ((void *) &cmd[2], 0, 8); + memset ((void *) buffer, 0, 8); SCpnt->request.dev = 0xffff; /* Mark as really busy again */ SCpnt->sense_buffer[0] = 0; SCpnt->sense_buffer[2] = 0; @@ -696,7 +690,7 @@ scsi_do_cmd (SCpnt, (void *) cmd, (void *) buffer, - 512, sd_init_done, SD_TIMEOUT, + 8, sd_init_done, SD_TIMEOUT, MAX_RETRIES); if (current == task[0]) @@ -784,6 +778,7 @@ rscsi_disks[j] = rscsi_disks[++j]; --i; --NR_SD; + scsi_free(buffer, 512); return i; }; } @@ -795,6 +790,7 @@ rscsi_disks[i].ten = 1; rscsi_disks[i].remap = 1; + scsi_free(buffer, 512); return i; } diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/sd_ioctl.c linux/kernel/blk_drv/scsi/sd_ioctl.c --- pl11/linux/kernel/blk_drv/scsi/sd_ioctl.c Sat Jul 3 03:25:37 1993 +++ linux/kernel/blk_drv/scsi/sd_ioctl.c Wed Jul 28 00:19:33 1993 @@ -52,6 +52,13 @@ put_fs_long(sd[MINOR(inode->i_rdev)].nr_sects, (long *) arg); return 0; + case BLKFLSBUF: + if(!suser()) return -EACCES; + if(!inode->i_rdev) return -EINVAL; + sync_dev(inode->i_rdev); + invalidate_buffers(inode->i_rdev); + return 0; + case BLKRRPART: /* Re-read partition tables */ return revalidate_scsidisk(dev, 1); default: diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/seagate.c linux/kernel/blk_drv/scsi/seagate.c --- pl11/linux/kernel/blk_drv/scsi/seagate.c Mon Jul 5 02:17:49 1993 +++ linux/kernel/blk_drv/scsi/seagate.c Sat Aug 7 11:27:00 1993 @@ -1,9 +1,15 @@ /* - * seagate.c Copyright (C) 1992 Drew Eckhardt - * low level scsi driver for ST01/ST02 by + * seagate.c Copyright (C) 1992, 1993 Drew Eckhardt + * low level scsi driver for ST01/ST02, Future Domain TMC-885, + * TMC-950 by + * * Drew Eckhardt * * + * + * Note : TMC-880 boards don't work because they have two bits in + * the status register flipped, I'll fix this "RSN" + * */ /* @@ -13,6 +19,18 @@ * * -DFAST or -DFAST32 will use blind transfers where possible * + * -DARBITRATE will cause the host adapter to arbitrate for the + * bus for better SCSI-II compatability, rather than just + * waiting for BUS FREE and then doing it's thing. Should + * let us do one command per Lun when I integrate my + * reorganization changes into the distribution sources. + * + * -DSLOW_HANDSHAKE will allow compatability with broken devices that don't + * handshake fast enough (ie, some CD ROM's) for the Seagate + * code. + * + * -DSLOW_RATE=x, x some number will let you specify a default + * transfer rate if handshaking isn't working correctly. */ #include @@ -26,6 +44,8 @@ #include "scsi.h" #include "hosts.h" #include "seagate.h" +#include "constants.h" + #ifndef IRQ #define IRQ 5 @@ -35,6 +55,15 @@ #define FAST #endif +#if defined(SLOW_RATE) && !defined(SLOW_HANDSHAKE) +#define SLOW_HANDSHAKE +#endif + +#if defined(SLOW_HANDSHAKE) && !defined(SLOW_RATE) +#define SLOW_RATE 50 +#endif + + #if defined(LINKED) #undef LINKED /* Linked commands are currently broken ! */ #endif @@ -88,6 +117,7 @@ #define CONTROL STATUS #define DATA (*(volatile unsigned char *) st0x_dr) + #ifndef OVERRIDE static const char * seagate_bases[] = { (char *) 0xc8000, (char *) 0xca000, (char *) 0xcc000, @@ -152,15 +182,83 @@ static int hostno = -1; static void seagate_reconnect_intr(int); +#ifdef FAST +static int fast = 1; +#endif + +#ifdef SLOW_HANDSHAKE /* - * We try to autodetect the 0ws jumper. If we get an over / under run, - * then we assume that the the handshaking done by 0ws to synchronize the - * SCSI and ISA busses failed, and disable fast transfers. + * Support for broken devices : + * The Seagate board has a handshaking problem. Namely, a lack + * thereof for slow devices. You can blast 600K/second through + * it if you are polling for each byte, more if you do a blind + * transfer. In the first case, with a fast device, REQ will + * transition high-low or high-low-high before your loop restarts + * and you'll have no problems. In the second case, the board + * will insert wait states for up to 13.2 usecs for REQ to + * transition low->high, and everything will work. + * + * However, there's nothing in the state machine that says + * you *HAVE* to see a high-low-high set of transitions before + * sending the next byte, and slow things like the Trantor CD ROMS + * will break because of this. + * + * So, we need to slow things down, which isn't as simple as it + * seems. We can't slow things down period, because then people + * who don't recompile their kernels will shoot me for ruining + * their performance. We need to do it on a case per case basis. + * + * The best for performance will be to, only for borken devices + * (this is stored on a per-target basis in the scsi_devices array) + * + * Wait for a low->high transition before continuing with that + * transfer. If we timeout, continue anyways. We don't need + * a long timeout, because REQ should only be asserted until the + * corresponding ACK is recieved and processed. + * + * Note that we can't use the system timer for this, because of + * resolution, and we *really* can't use the timer chip since + * gettimeofday() and the beeper routines use that. So, + * the best thing for us to do will be to calibrate a timing + * loop in the initialization code using the timer chip before + * gettimeofday() can screw with it. */ -#ifdef FAST -static int fast = 1; +static int borken_calibration = 0; +static void borken_init (void) { + register int count = 0, start = jiffies + 1, stop = start + 25; + + while (jiffies < start); + for (;jiffies < stop; ++count); + +/* + * Ok, we now have a count for .25 seconds. Convert to a + * count per second and divide by transer rate in K. + */ + + borken_calibration = (count * 4) / (SLOW_RATE*1024); + + if (borken_calibration < 1) + borken_calibration = 1; +#if (DEBUG & DEBUG_BORKEN) + printk("scsi%d : borken calibrated to %dK/sec, %d cycles per transfer\n", + hostno, BORKEN_RATE, borken_calibration); +#endif +} + +static inline void borken_wait(void) { + register int count; + for (count = borken_calibration; count && (STATUS & STAT_REQ); + --count); + if (count) +#if (DEBUG & DEBUG_BORKEN) + printk("scsi%d : borken timeout\n", hostno); +#else + ; #endif +} + +#endif /* def SLOW_HANDSHAKE */ int seagate_st0x_detect (int hostnum) { @@ -207,6 +305,9 @@ * from the BIOS version notice in all the possible locations * of the ROM's. This has a nice sideeffect of not trashing * any register locations that might be used by something else. + * + * XXX - note that we probably should be probing the address + * space for the on-board RAM instead. */ for (i = 0; i < (sizeof (seagate_bases) / sizeof (char * )); ++i) @@ -238,6 +339,10 @@ hostno, IRQ); return 0; } +#ifdef SLOW_HANDSHAKE + borken_init(); +#endif + return -1; } else @@ -252,6 +357,12 @@ const char *seagate_st0x_info(void) { static char buffer[256]; sprintf(buffer, "scsi%d : %s at irq %d address %p options :" +#ifdef ARBITRATE +" ARBITRATE" +#endif +#ifdef SLOW_HANDSHAKE +" SLOW_HANDSHAKE" +#endif #ifdef FAST #ifdef FAST32 " FAST32" @@ -459,13 +570,21 @@ int nobuffs; int clock; int temp; +#ifdef SLOW_HANDSHAKE + int borken; /* Does the current target require Very Slow I/O ? */ +#endif -#if ((DEBUG & PHASE_ETC) || (DEBUG & PRINT_COMMAND) || (DEBUG & PHASE_EXIT)) +#if (DEBUG & PHASE_DATAIN) || (DEBUG & PHASE_DATOUT) + int transfered = 0; +#endif + +#if (((DEBUG & PHASE_ETC) == PHASE_ETC) || (DEBUG & PRINT_COMMAND) || \ + (DEBUG & PHASE_EXIT)) int i; #endif -#if (DEBUG & PHASE_ETC) +#if ((DEBUG & PHASE_ETC) == PHASE_ETC) int phase=0, newphase; #endif @@ -479,10 +598,13 @@ incommand = 0; st0x_aborted = 0; +#ifdef SLOW_HANDSHAKE + borken = (int) scsi_devices[SCint->index].borken; +#endif + #if (DEBUG & PRINT_COMMAND) printk ("scsi%d : target = %d, command = ", hostno, target); - for (i = 0; i < COMMAND_SIZE(((unsigned char *)cmnd)[0]); ++i) - printk("%02x ", ((unsigned char *) cmnd)[i]); + print_command((unsigned char *) cmnd); printk("\n"); #endif @@ -577,7 +699,11 @@ * we must respond to the reselection by asserting BSY ourselves */ +#if 1 CONTROL = (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY); +#else + CONTROL = (BASE_CMD | CMD_BSY); +#endif /* * The target will drop SEL, and raise BSY, at which time we must drop @@ -632,6 +758,7 @@ clock = jiffies + ST0X_BUS_FREE_DELAY; +#if !defined (ARBITRATE) while (((STATUS | STATUS | STATUS) & (STAT_BSY | STAT_SEL)) && (!st0x_aborted) && (jiffies < clock)); @@ -640,19 +767,7 @@ return retcode(DID_BUS_BUSY); else if (st0x_aborted) return retcode(st0x_aborted); - -/* - * Bus free has been detected, within BUS settle. I used to - * support an arbitration phase - however, on the Seagate, this - * degraded performance by a factor > 10 - so it is no more. - */ - -/* - * SELECTION PHASE - * - * Now, we select the disk, giving it the SCSI ID at data - * and a command of PARITY if necessary, and we raise SEL. - */ +#endif #if (DEBUG & PHASE_SELECTION) printk("scsi%d : phase = SELECTION\n", hostno); @@ -661,35 +776,63 @@ clock = jiffies + ST0X_SELECTION_DELAY; /* - * If we wish to disconnect, we should request a MESSAGE OUT - * at this point. Technically, ATTN should be raised before - * SEL = true and BSY = false (from arbitration), but I think this - * should do. - */ - if (reselect) - CONTROL = BASE_CMD | CMD_DRVR_ENABLE | - CMD_ATTN; - -/* - * We must assert both our ID and our target's ID on the bus. + * Arbitration/selection procedure : + * 1. Disable drivers + * 2. Write HOST adapter address bit + * 3. Set start arbitration. + * 4. We get either ARBITRATION COMPLETE or SELECT at this + * point. + * 5. OR our ID and targets on bus. + * 6. Enable SCSI drivers and asserted SEL and ATTN */ - DATA = (unsigned char) ((1 << target) | (controller_type == SEAGATE ? 0x80 : 0x40)); + +#if defined(ARBITRATE) + cli(); + CONTROL = 0; + DATA = (controller_type == SEAGATE) ? 0x80 : 0x40; + CONTROL = CMD_START_ARB; + sti(); + while (!((status_read = STATUS) & (STAT_ARB_CMPL | STAT_SEL)) && + (jiffies < clock) && !st0x_aborted); -/* - * If we are allowing ourselves to reconnect, then I will keep - * ATTN raised so we get MSG OUT. - */ - CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL | - (reselect ? CMD_ATTN : 0); + if (!(status_read & STAT_ARB_CMPL)) { +#if (DEBUG & PHASE_SELECTION) + if (status_read & STAT_SEL) + printk("scsi%d : arbitration lost\n", hostno); + else + printk("scsi%d : arbitration timeout.\n", hostno); +#endif + CONTROL = BASE_CMD; + return retcode(DID_NO_CONNECT); + }; + +#if (DEBUG & PHASE_SELECTION) + printk("scsi%d : arbitration complete\n", hostno); +#endif +#endif + /* * When the SCSI device decides that we're gawking at it, it will * respond by asserting BUSY on the bus. - */ + * + * Note : the Seagate ST-01/02 product manual says that we should + * twiddle the DATA register before the control register. However, + * this does not work reliably so we do it the other way arround. + * + * Probably could be a problem with arbitration too, we really should + * try this with a SCSI protocol or logic analyzer to see what is + * going on. + */ + cli(); + CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL | + (reselect ? CMD_ATTN : 0); + DATA = (unsigned char) ((1 << target) | (controller_type == SEAGATE ? 0x80 : 0x40)); + sti(); while (!((status_read = STATUS) & STAT_BSY) && (jiffies < clock) && !st0x_aborted) -#if (DEBUG & PHASE_SELECTION) +#if 0 && (DEBUG & PHASE_SELECTION) { temp = clock - jiffies; @@ -705,10 +848,9 @@ #endif - if ((jiffies > clock) || (!st0x_aborted && - !(status_read & STAT_BSY))) + if ((jiffies >= clock) && !(status_read & STAT_BSY)) { -#if (DEBUG & PHASE_SELECT) +#if (DEBUG & PHASE_SELECTION) printk ("scsi%d : NO CONNECT with target %d, status = %x \n", hostno, target, STATUS); #endif @@ -724,6 +866,8 @@ if (st0x_aborted) { CONTROL = BASE_CMD; if (STATUS & STAT_BSY) { + printk("scsi%d : BST asserted after we've been aborted.\n", + hostno); seagate_st0x_reset(); return retcode(DID_RESET); } @@ -756,6 +900,10 @@ data = (unsigned char *) SCint->request_buffer; } +#if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT)) + printk("scsi%d : len = %d\n", hostno, len); +#endif + break; #ifdef LINKED case LINKED_RIGHT: @@ -791,7 +939,7 @@ * really adds up. */ -#if (DEBUG & PHASE_ETC) +#if ((DEBUG & PHASE_ETC) == PHASE_ETC) printk("scsi%d : phase = INFORMATION TRANSFER\n", hostno); #endif @@ -813,7 +961,7 @@ #ifdef PARITY if (status_read & STAT_PARITY) { - done = 1; + printk("scsi%d : got parity error\n", hostno); st0x_aborted = DID_PARITY; } #endif @@ -820,7 +968,7 @@ if (status_read & STAT_REQ) { -#if (DEBUG & PHASE_ETC) +#if ((DEBUG & PHASE_ETC) == PHASE_ETC) if ((newphase = (status_read & REQ_MASK)) != phase) { phase = newphase; @@ -853,8 +1001,7 @@ default : printk("scsi%d : phase = UNKNOWN\n", hostno); - st0x_aborted = 1; - done = 1; + st0x_aborted = DID_ERROR; } } #endif @@ -890,7 +1037,6 @@ __asm__(" cld; - movl %0, %%edi; " #ifdef FAST32 " shr $2, %%ecx; @@ -904,9 +1050,9 @@ #endif " loop 1b;" : : /* input */ - "r" (st0x_dr), "S" (data), "c" (SCint->transfersize) : + "D" (st0x_dr), "S" (data), "c" (SCint->transfersize) : /* clobbered */ - "eax", "esi", "ecx", "edi"); + "eax", "ecx", "esi" ); len -= transfersize; data += transfersize; @@ -936,8 +1082,6 @@ Test for any data here at all. */ - "movl %0, %%esi\n" /* local value of data */ - "\tmovl %1, %%ecx\n" /* local value of len */ "\torl %%ecx, %%ecx jz 2f @@ -969,15 +1113,13 @@ loop 1b 2: - movl %%esi, %2 - movl %%ecx, %3 ": /* output */ -"=r" (data), "=r" (len) : +"=S" (data), "=c" (len) : /* input */ "0" (data), "1" (len) : /* clobbered */ -"ebx", "ecx", "edi", "esi"); +"eax", "ebx", "edi"); } if (!len && nobuffs) { @@ -993,17 +1135,22 @@ break; case REQ_DATAIN : -#ifdef FAST -if (!len) { -#if 0 - printk("scsi%d: overflow from target %d lun %d \n", - hostno, target, lun); - st0x_aborted = DID_ERROR; - fast = 0; -#endif - break; +#ifdef SLOW_HANDSHAKE + if (borken) { +#if (DEBUG & (PHASE_DATAIN)) + transfered += len; +#endif + for (; len && (STATUS & (REQ_MASK | STAT_REQ)) == (REQ_DATAIN | + STAT_REQ); --len) { + *data++ = DATA; + borken_wait(); } - +#if (DEBUG & (PHASE_DATAIN)) + transfered -= len; +#endif + } else +#endif +#ifdef FAST if (fast && transfersize && !(len % transfersize) && (len >= transfersize) #ifdef FAST32 && !(transfersize % 4) @@ -1016,7 +1163,6 @@ #endif __asm__(" cld; - movl %0, %%esi; " #ifdef FAST32 " shr $2, %%ecx; @@ -1031,13 +1177,18 @@ " loop 1b;" : : /* input */ - "r" (st0x_dr), "D" (data), "c" (SCint->transfersize) : + "S" (st0x_dr), "D" (data), "c" (SCint->transfersize) : /* clobbered */ - "eax", "ecx", "edi", "esi"); + "eax", "ecx", "edi"); len -= transfersize; data += transfersize; +#if (DEBUG & PHASE_DATAIN) + printk("scsi%d: transfered += %d\n", hostno, transfersize); + transfered += transfersize; +#endif + #if (DEBUG & DEBUG_FAST) printk("scsi%d : FAST transfer complete len = %d data = %08x\n", hostno, len, data); @@ -1046,6 +1197,13 @@ } else #endif { + +#if (DEBUG & PHASE_DATAIN) + printk("scsi%d: transfered += %d\n", hostno, len); + transfered += len; /* Assume we'll transfer it all, then + subtract what we *didn't* transfer */ +#endif + /* * We loop as long as we are in a data in phase, there is room to read, * and BSY is still active @@ -1061,9 +1219,6 @@ Test for room to read */ - - "movl %0, %%edi\n" /* data */ - "\tmovl %1, %%ecx\n" /* len */ "\torl %%ecx, %%ecx jz 2f @@ -1095,18 +1250,24 @@ movb (%%ebx), %%al stosb - loop 1b + loop 1b\n" -2: movl %%edi, %2\n" /* data */ - "\tmovl %%ecx, %3\n" /* len */ +"2:\n" : /* output */ -"=r" (data), "=r" (len) : +"=D" (data), "=c" (len) : /* input */ "0" (data), "1" (len) : /* clobbered */ -"ebx", "ecx", "edi", "esi"); +"eax","ebx", "esi"); + +#if (DEBUG & PHASE_DATAIN) + printk("scsi%d: transfered -= %d\n", hostno, len); + transfered -= len; /* Since we assumed all of Len got + * transfered, correct our mistake */ +#endif } + if (!len && nobuffs) { --nobuffs; ++buffer; @@ -1126,6 +1287,10 @@ if (status_read & STAT_REQ) { DATA = *(unsigned char *) cmnd; cmnd = 1+(unsigned char *) cmnd; +#ifdef SLOW_HANDSHAKE + if (borken) + borken_wait(); +#endif } break; @@ -1147,6 +1312,7 @@ switch (reselect) { case CAN_RECONNECT: DATA = IDENTIFY(1, lun); + #if (DEBUG & (PHASE_RESELECT | PHASE_MSGOUT)) printk("scsi%d : sent IDENTIFY message.\n", hostno); #endif @@ -1166,9 +1332,7 @@ #endif default: DATA = NOP; -#if (DEBUG & PHASE_MSGOUT) - printk("scsi%d : sent NOP message.\n", hostno); -#endif + printk("scsi%d : target %d requested MSGOUT, sent NOP message.\n", hostno, target); } break; @@ -1263,21 +1427,39 @@ printk("scsi%d : unknown phase.\n", hostno); st0x_aborted = DID_ERROR; } - } /* while ends */ + +#ifdef SLOW_HANDSHAKE +/* + * I really don't care to deal with borken devices in each single + * byte transfer case (ie, message in, message out, status), so + * I'll do the wait here if necessary. + */ + if (borken) + borken_wait(); +#endif + } /* if ends */ + } /* while ends */ #if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT | PHASE_EXIT)) - printk("Transfered %d bytes, allowed %d additional bytes\n", (bufflen - len), len); + printk("scsi%d : Transfered %d bytes\n", hostno, transfered); #endif #if (DEBUG & PHASE_EXIT) +#if 0 /* Doesn't work for scatter / gather */ printk("Buffer : \n"); for (i = 0; i < 20; ++i) printk ("%02x ", ((unsigned char *) data)[i]); /* WDE mod */ printk("\n"); - printk("Status = %02x, message = %02x\n", status, message); +#endif + printk("scsi%d : status = ", hostno); + print_status(status); + printk("message = %02x\n", message); #endif + +/* We shouldn't reach this until *after* BSY has been deasserted */ +#ifdef notyet if (st0x_aborted) { if (STATUS & STAT_BSY) { seagate_st0x_reset(); @@ -1285,6 +1467,8 @@ } abort_confirm = 1; } +#endif + #ifdef LINKED else { /* @@ -1404,7 +1588,6 @@ int *sizes, result, formatted_sectors, total_sectors; int cylinders, heads, sectors; - unsigned long oldfs; Scsi_Device *disk; disk = rscsi_disks[MINOR(dev) >> 4].device; @@ -1420,14 +1603,6 @@ sizes = (int *) buf; data = (unsigned char *) (sizes + 2); -/* - * Point fs, which normally points to user space, at kernel space so that - * we can do a syscall with the data coming from kernel space. - */ - - oldfs = get_fs(); - set_fs(get_ds()); - cmd[0] = MODE_SENSE; cmd[1] = (disk->lun << 5) & 0xe5; cmd[2] = 0x04; /* Read page 4, rigid disk geometry page current values */ @@ -1445,7 +1620,7 @@ memcpy (data, cmd, 6); - if (!(result = scsi_ioctl (disk, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) { + if (!(result = kernel_scsi_ioctl (disk, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) { /* * The mode page lies beyond the MODE SENSE header, with length 4, and * the BLOCK DESCRIPTOR, with length header[3]. @@ -1458,7 +1633,7 @@ cmd[2] = 0x03; /* Read page 3, format page current values */ memcpy (data, cmd, 6); - if (!(result = scsi_ioctl (disk, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) { + if (!(result = kernel_scsi_ioctl (disk, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) { page = data + 4 + data[3]; sectors = (page[10] << 8) | page[11]; @@ -1484,7 +1659,10 @@ printk("scsi%d : heads = %d cylinders = %d sectors = %d total = %d formatted = %d\n", hostno, heads, cylinders, sectors, total_sectors, formatted_sectors); - cylinders -= ((total_sectors - formatted_sectors) / (heads * sectors)); + if (!heads || !sectors) + cylinders = 1025; + else + cylinders -= ((total_sectors - formatted_sectors) / (heads * sectors)); /* * Now, we need to do a sanity check on the geometry to see if it is @@ -1508,7 +1686,6 @@ } } - set_fs(oldfs); return result; } #endif /* CONFIG_BLK_DEV_SD */ diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/seagate.h linux/kernel/blk_drv/scsi/seagate.h --- pl11/linux/kernel/blk_drv/scsi/seagate.h Sat Jun 26 10:37:21 1993 +++ linux/kernel/blk_drv/scsi/seagate.h Wed Jul 28 00:19:34 1993 @@ -12,7 +12,6 @@ $Header */ #ifndef ASM -int seagate_st0x_biosparam(int, int, int*); int seagate_st0x_detect(int); int seagate_st0x_command(Scsi_Cmnd *); int seagate_st0x_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); @@ -26,18 +25,16 @@ #endif #ifdef CONFIG_BLK_DEV_SD -#define SEAGATE_ST0X {"Seagate ST-01/ST-02", seagate_st0x_detect, \ - seagate_st0x_info, seagate_st0x_command, \ - seagate_st0x_queue_command, seagate_st0x_abort, \ - seagate_st0x_reset, NULL, seagate_st0x_biosparam, \ - 1, 7, SG_ALL, 1, 0, 0} +int seagate_st0x_biosparam(int, int, int*); #else +#define seagate_st0x_biosparam NULL +#endif + #define SEAGATE_ST0X {"Seagate ST-01/ST-02", seagate_st0x_detect, \ seagate_st0x_info, seagate_st0x_command, \ seagate_st0x_queue_command, seagate_st0x_abort, \ - seagate_st0x_reset, NULL, NULL, \ + seagate_st0x_reset, NULL, seagate_st0x_biosparam, \ 1, 7, SG_ALL, 1, 0, 0} -#endif /* CONFIG_BLK_DEV_SD */ #endif @@ -47,13 +44,6 @@ #define PARITY -/* - defining ARBITRATE causes the arbitration sequence to be used. And speed to drop by a - factor of ten. -*/ - -#undef ARBITRATE - /* Thanks to Brian Antoine for the example code in his Messy-Loss ST-01 @@ -127,13 +117,15 @@ #define DEBUG_FAST 0x1000 #define DEBUG_SG 0x2000 #define DEBUG_LINKED 0x4000 +#define DEBUG_BORKEN 0x8000 /* * Control options - these are timeouts specified in .01 seconds. */ +/* 30, 20 work */ #define ST0X_BUS_FREE_DELAY 25 -#define ST0X_SELECTION_DELAY 15 +#define ST0X_SELECTION_DELAY 25 #define eoi() __asm__("push %%eax\nmovb $0x20, %%al\noutb %%al, $0x20\npop %%eax"::) diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/sr.c linux/kernel/blk_drv/scsi/sr.c --- pl11/linux/kernel/blk_drv/scsi/sr.c Sat Jul 3 03:29:16 1993 +++ linux/kernel/blk_drv/scsi/sr.c Wed Jul 28 00:19:34 1993 @@ -27,6 +27,7 @@ #include "hosts.h" #include "sr.h" #include "scsi_ioctl.h" /* For the door lock/unlock commands */ +#include "constants.h" #define MAX_RETRIES 1 #define SR_TIMEOUT 500 @@ -247,15 +248,12 @@ scsi_CDs[DEVICE_NR(SCpnt->request.dev)].device->lun, result); - if (status_byte(result) == CHECK_CONDITION) - printk("\tSense class %x, sense error %x, extended sense %x\n", - sense_class(SCpnt->sense_buffer[0]), - sense_error(SCpnt->sense_buffer[0]), - SCpnt->sense_buffer[2] & 0xf); - - end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors); - requeue_sr_request(SCpnt); - } + if (status_byte(result) == CHECK_CONDITION) + print_sense("sr", SCpnt); + + end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors); + requeue_sr_request(SCpnt); + } } static int sr_open(struct inode * inode, struct file * filp) diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/st.c linux/kernel/blk_drv/scsi/st.c --- pl11/linux/kernel/blk_drv/scsi/st.c Sat Jul 3 03:10:25 1993 +++ linux/kernel/blk_drv/scsi/st.c Wed Aug 11 22:36:49 1993 @@ -8,6 +8,8 @@ Features: - support for different block sizes and internal buffering + - support for fixed and variable block size (within buffer limit; + blocksize set to zero) - *nix-style ioctl with codes from mtio.h from the QIC-02 driver by Hennus Bergman (command MTSETBLK added) - character device @@ -28,9 +30,10 @@ you should disable write behind and in addition to that check that the tapes are readable. - Kai Makisara, Nov 9, 1992 email makisara@vtinsx.ins.vtt.fi or - Kai.Makisara@vtt.fi - Last changes Jan 3, 1993. + Copyright 1992, 1993 Kai Makisara + email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi + + Last modified: Sat Jul 10 17:40:05 1993 by root */ #include @@ -50,7 +53,8 @@ #include "scsi_ioctl.h" #include "st.h" -#define MAX_RETRIES 5 +#define MAX_RETRIES 0 +#define MAX_READY_RETRIES 5 #define NO_TAPE NOT_READY /* Uncomment the following if you want the rewind, etc. commands return @@ -64,7 +68,7 @@ /* #define DEBUG */ -#define ST_TIMEOUT 6000 +#define ST_TIMEOUT 9000 #define ST_LONG_TIMEOUT 200000 /* Number of ST_BLOCK_SIZE blocks in the buffers */ @@ -76,6 +80,12 @@ #define ST_BUFFER_SIZE (ST_BUFFER_BLOCKS * ST_BLOCK_SIZE) #define ST_WRITE_THRESHOLD (ST_WRITE_THRESHOLD_BLOCKS * ST_BLOCK_SIZE) +/* The buffer size should fit into the 24 bits reserved for length in the + 6-byte SCSI read and write commands. */ +#if ST_BUFFER_SIZE >= (2 << 24 - 1) +#error "Buffer size should not exceed (2 << 24 - 1) bytes!" +#endif + static int st_nbr_buffers; static ST_buffer *st_buffers[2]; @@ -98,7 +108,7 @@ if (scsi_tapes[st_nbr].buffer->writing && (SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x40)) - scsi_tapes[st_nbr].buffer->last_result = 0x7fffffff; + scsi_tapes[st_nbr].buffer->last_result = INT_MAX; else scsi_tapes[st_nbr].buffer->last_result = SCpnt->result; if (scsi_tapes[st_nbr].buffer->writing) @@ -114,44 +124,18 @@ #endif } - - -#ifdef DEBUG -/* Print sense information */ -static void decode_sns(int dev, char *sense_buffer) -{ - static char *snstext[] = { - "None","Recovered Error","Not Ready","Medium Error","Hardware Error", - "Illegal Request","Unit Attention","Data Protect","Blank Check", - "Key=E","Key=F","Filemark","End-Of-Medium","Incorrect Block Length", - "14","15"}; - - if (sense_buffer[0]!=0) { - if ((sense_buffer[0] & 0x70) == 0x70) { - if (sense_buffer[2] & 0x80) printk( "FMK "); - if (sense_buffer[2] & 0x40) printk( "EOM "); - if (sense_buffer[2] & 0x20) printk( "ILI "); - printk( "st%d: sense key %s\n", dev, snstext[sense_buffer[2] & 0x0f]); - } else { - if (sense_buffer[0] < 15) - printk("st%d: old sense key %s\n", dev, snstext[sense_buffer[0] & 0x0f]); - else - printk("st%d: sns = %2x %2x\n", dev, sense_buffer[0], sense_buffer[2]); - } - } - return; -} -#endif - - /* Convert the result to success code */ -static int st_chk_result(int dev, int result, unsigned char *sense) +static int st_chk_result(Scsi_Cmnd * SCpnt) { + int dev = SCpnt->request.dev; + int result = SCpnt->result; + char * sense = SCpnt->sense_buffer; + if (!result) return 0; #ifdef DEBUG printk("st%d: Error: %x\n", dev, result); - decode_sns(dev, sense); + print_sense("st", SCpnt); #endif if ((sense[0] & 0x70) == 0x70 && ((sense[2] & 0x80) /* || ((sense[2] & 0x0f) == 8) */ )) @@ -185,7 +169,7 @@ #endif -/* Flush the write buffer */ +/* Flush the write buffer (never need to write if variable blocksize). */ static int flush_write_buffer(int dev) { int offset, transfer, blks; @@ -236,7 +220,7 @@ if (SCpnt->result != 0) { printk("st%d: Error on flush:\n", dev); #ifdef DEBUG - st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer); + st_chk_result(SCpnt); #endif result = (-EIO); } @@ -258,11 +242,14 @@ int dev; int backspace, result; - dev = inode->i_rdev & 127; + dev = MINOR(inode->i_rdev) & 127; if (scsi_tapes[dev].rw == 2) /* Writing */ return flush_write_buffer(dev); + if (scsi_tapes[dev].block_size == 0) + return 0; + backspace = (scsi_tapes[dev].buffer->buffer_bytes + scsi_tapes[dev].buffer->read_pointer) / scsi_tapes[dev].block_size - (scsi_tapes[dev].buffer->read_pointer + scsi_tapes[dev].block_size - 1) / @@ -291,7 +278,7 @@ unsigned char cmd[10]; Scsi_Cmnd * SCpnt; - dev = inode->i_rdev & 127; + dev = MINOR(inode->i_rdev) & 127; if (dev >= NR_ST) return (-ENODEV); if (scsi_tapes[dev].in_use) { @@ -332,7 +319,8 @@ SCpnt->request.dev = dev; scsi_do_cmd(SCpnt, (void *) cmd, (void *) scsi_tapes[dev].buffer->b_data, - ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT, MAX_RETRIES); + ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT, + MAX_READY_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting ); @@ -339,7 +327,7 @@ if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */ #ifdef DEBUG - decode_sns(dev, SCpnt->sense_buffer); + print_sense("st", SCpnt); #endif SCpnt->sense_buffer[0]=0; memset ((void *) &cmd[0], 0, 10); @@ -347,7 +335,8 @@ SCpnt->request.dev = dev; scsi_do_cmd(SCpnt, (void *) cmd, (void *) scsi_tapes[dev].buffer->b_data, - ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT, MAX_RETRIES); + ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT, + MAX_READY_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting ); } @@ -354,7 +343,7 @@ if (SCpnt->result != 0) { #ifdef DEBUG - decode_sns(dev, SCpnt->sense_buffer); + print_sense("st", SCpnt); #endif if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) @@ -373,7 +362,7 @@ SCpnt->request.dev = dev; scsi_do_cmd(SCpnt, (void *) cmd, (void *) scsi_tapes[dev].buffer->b_data, - ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_RETRIES); + ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting ); @@ -401,11 +390,11 @@ SCpnt->request.dev = dev; scsi_do_cmd(SCpnt, (void *) cmd, (void *) scsi_tapes[dev].buffer->b_data, - ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_RETRIES); + ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting ); - i = st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer); + i = st_chk_result(SCpnt); if (i) { #ifdef DEBUG printk("st%d: No Mode Sense.\n", dev); @@ -422,15 +411,18 @@ #endif if (scsi_tapes[dev].buffer->b_data[3] >= 8) { + scsi_tapes[dev].drv_buffer = (scsi_tapes[dev].buffer->b_data[2] >> 4) & 7; + scsi_tapes[dev].density = scsi_tapes[dev].buffer->b_data[4]; scsi_tapes[dev].block_size = scsi_tapes[dev].buffer->b_data[9] * 65536 + scsi_tapes[dev].buffer->b_data[10] * 256 + scsi_tapes[dev].buffer->b_data[11]; #ifdef DEBUG - printk("st%d: Density %x, tape length: %x, blocksize: %d\n", dev, + printk("st%d: Density %x, tape length: %x, blocksize: %d, drv buffer: %d\n", dev, scsi_tapes[dev].buffer->b_data[4], scsi_tapes[dev].buffer->b_data[5] * 65536 + scsi_tapes[dev].buffer->b_data[6] * 256 + scsi_tapes[dev].buffer->b_data[7], scsi_tapes[dev].buffer->b_data[9] * 65536 + scsi_tapes[dev].buffer->b_data[10] * 256 + - scsi_tapes[dev].buffer->b_data[11]); + scsi_tapes[dev].buffer->b_data[11], + scsi_tapes[dev].drv_buffer); #endif if (scsi_tapes[dev].block_size > ST_BUFFER_SIZE) { printk("st%d: Blocksize %d too large for buffer.\n", dev, @@ -440,24 +432,20 @@ return (-EIO); } - if (scsi_tapes[dev].block_size == 0) { - printk("st%d: Fixing block size to 512 bytes.\n", dev); - if (st_int_ioctl(inode, filp, MTSETBLK, ST_BLOCK_SIZE)) { - printk("st%d: Can't set fixed block size.\n", dev); - scsi_tapes[dev].buffer->in_use = 0; - scsi_tapes[dev].in_use = 0; - return (-EIO); - } - scsi_tapes[dev].block_size = ST_BLOCK_SIZE; - } } else scsi_tapes[dev].block_size = ST_BLOCK_SIZE; - scsi_tapes[dev].buffer->buffer_blocks = - ST_BUFFER_SIZE / scsi_tapes[dev].block_size; - scsi_tapes[dev].buffer->buffer_size = - scsi_tapes[dev].buffer->buffer_blocks * scsi_tapes[dev].block_size; + if (scsi_tapes[dev].block_size > 0) { + scsi_tapes[dev].buffer->buffer_blocks = + ST_BUFFER_SIZE / scsi_tapes[dev].block_size; + scsi_tapes[dev].buffer->buffer_size = + scsi_tapes[dev].buffer->buffer_blocks * scsi_tapes[dev].block_size; + } + else { + scsi_tapes[dev].buffer->buffer_blocks = 1; + scsi_tapes[dev].buffer->buffer_size = ST_BUFFER_SIZE; + } scsi_tapes[dev].buffer->buffer_bytes = scsi_tapes[dev].buffer->read_pointer = 0; #ifdef DEBUG @@ -486,7 +474,7 @@ static unsigned char cmd[10]; Scsi_Cmnd * SCpnt; - dev = inode->i_rdev; + dev = MINOR(inode->i_rdev); rewind = (dev & 0x80) == 0; dev = dev & 127; @@ -513,9 +501,9 @@ if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting ); if (SCpnt->result) { - printk("st%d: Error on write filemark:\n", dev); + printk("st%d: Error on write filemark.\n", dev); #ifdef DEBUG - st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer); + st_chk_result(SCpnt); #endif } SCpnt->request.dev = -1; /* Mark as not busy */ @@ -552,7 +540,7 @@ char *b_point; Scsi_Cmnd * SCpnt; - dev = inode->i_rdev & 127; + dev = MINOR(inode->i_rdev) & 127; #ifdef DEBUG if (!scsi_tapes[dev].in_use) { printk("st%d: Incorrect device.\n", dev); @@ -563,6 +551,9 @@ if (scsi_tapes[dev].write_prot) return (-EACCES); + if (scsi_tapes[dev].block_size == 0 && count > ST_BUFFER_SIZE) + return (-EOVERFLOW); + if (scsi_tapes[dev].rw == 1) { retval = flush_buffer(inode, filp, 0); if (retval) @@ -578,7 +569,7 @@ printk("st%d: Async write error %x.\n", dev, scsi_tapes[dev].buffer->last_result); #endif - /*if (scsi_tapes[dev].buffer->last_result = 0x7fffffff) + /*if (scsi_tapes[dev].buffer->last_result = INT_MAX) retval = (-ENOSPC); else */ retval = (-EIO); @@ -593,19 +584,34 @@ memset(cmd, 0, 10); cmd[0] = WRITE_6; - cmd[1] = 1; + cmd[1] = (scsi_tapes[dev].block_size != 0); scsi_tapes[dev].rw = 2; b_point = buf; - while((scsi_tapes[dev].buffer->buffer_bytes + count) >= - scsi_tapes[dev].buffer->buffer_size) { - do_count = scsi_tapes[dev].buffer->buffer_size - - scsi_tapes[dev].buffer->buffer_bytes; + while( +#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS + scsi_tapes[dev].block_size != 0 && + (scsi_tapes[dev].buffer->buffer_bytes + count) > + scsi_tapes[dev].buffer->buffer_size) +#else + (scsi_tapes[dev].block_size == 0 && count > 0) || + (scsi_tapes[dev].buffer->buffer_bytes + count) >= + scsi_tapes[dev].buffer->buffer_size) +#endif + { + if (scsi_tapes[dev].block_size == 0) + do_count = count; + else + do_count = scsi_tapes[dev].buffer->buffer_size - + scsi_tapes[dev].buffer->buffer_bytes; memcpy_fromfs(scsi_tapes[dev].buffer->b_data + - scsi_tapes[dev].buffer->buffer_bytes,b_point,do_count); + scsi_tapes[dev].buffer->buffer_bytes, b_point, do_count); - blks = scsi_tapes[dev].buffer->buffer_blocks; + if (scsi_tapes[dev].block_size == 0) + blks = do_count; + else + blks = scsi_tapes[dev].buffer->buffer_blocks; cmd[2] = blks >> 16; cmd[3] = blks >> 8; cmd[4] = blks; @@ -621,7 +627,7 @@ if (SCpnt->result || SCpnt->sense_buffer[0] != 0) { #ifdef DEBUG printk("st%d: Error on write:\n", dev); - st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer); + st_chk_result(SCpnt); #endif if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x40)) @@ -649,7 +655,7 @@ count = 0; } - do_count = st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer); + do_count = st_chk_result(SCpnt); if (do_count) { SCpnt->request.dev = -1; return do_count; @@ -656,13 +662,20 @@ } #if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS - if (scsi_tapes[dev].buffer->buffer_bytes >= ST_WRITE_THRESHOLD) { + if (scsi_tapes[dev].buffer->buffer_bytes >= ST_WRITE_THRESHOLD || + scsi_tapes[dev].block_size == 0) { /* Schedule an asynchronous write */ - scsi_tapes[dev].buffer->writing = (scsi_tapes[dev].buffer->buffer_bytes / - scsi_tapes[dev].block_size) * scsi_tapes[dev].block_size; + if (scsi_tapes[dev].block_size == 0) + scsi_tapes[dev].buffer->writing = scsi_tapes[dev].buffer->buffer_bytes; + else + scsi_tapes[dev].buffer->writing = (scsi_tapes[dev].buffer->buffer_bytes / + scsi_tapes[dev].block_size) * scsi_tapes[dev].block_size; scsi_tapes[dev].dirty = 0; - blks = scsi_tapes[dev].buffer->writing / scsi_tapes[dev].block_size; + if (scsi_tapes[dev].block_size == 0) + blks = scsi_tapes[dev].buffer->writing; + else + blks = scsi_tapes[dev].buffer->writing / scsi_tapes[dev].block_size; cmd[2] = blks >> 16; cmd[3] = blks >> 8; cmd[4] = blks; @@ -687,11 +700,11 @@ { int dev; int total; - int transfer, blks; + int transfer, blks, bytes; static unsigned char cmd[10]; Scsi_Cmnd * SCpnt; - dev = inode->i_rdev & 127; + dev = MINOR(inode->i_rdev) & 127; #ifdef DEBUG if (!scsi_tapes[dev].in_use) { printk("st%d: Incorrect device.\n", dev); @@ -699,6 +712,9 @@ } #endif + if (scsi_tapes[dev].block_size == 0 && count > ST_BUFFER_SIZE) + return (-EOVERFLOW); + if (scsi_tapes[dev].rw == 2) { transfer = flush_buffer(inode, filp, 0); if (transfer) @@ -726,8 +742,13 @@ memset(cmd, 0, 10); cmd[0] = READ_6; - cmd[1] = 1; - blks = scsi_tapes[dev].buffer->buffer_blocks; + cmd[1] = (scsi_tapes[dev].block_size != 0); + if (scsi_tapes[dev].block_size == 0) + blks = bytes = count; + else { + blks = scsi_tapes[dev].buffer->buffer_blocks; + bytes = blks * scsi_tapes[dev].block_size; + } cmd[2] = blks >> 16; cmd[3] = blks >> 8; cmd[4] = blks; @@ -760,9 +781,16 @@ (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6]; if (SCpnt->sense_buffer[2] & 0x20) { - printk("st%d: Incorrect block size.\n", dev); - SCpnt->request.dev = -1; /* Mark as not busy */ - return (-EIO); + if (scsi_tapes[dev].block_size == 0) { + if (transfer <= 0) + transfer = 0; + scsi_tapes[dev].buffer->buffer_bytes = count - transfer; + } + else { + printk("st%d: Incorrect block size.\n", dev); + SCpnt->request.dev = -1; /* Mark as not busy */ + return (-EIO); + } } else if (SCpnt->sense_buffer[2] & 0x40) { scsi_tapes[dev].eof = 2; /* What should be done at EOM ? */ @@ -776,9 +804,13 @@ } else if (SCpnt->sense_buffer[2] & 0x80) { scsi_tapes[dev].eof = 1; - scsi_tapes[dev].buffer->buffer_bytes = - (scsi_tapes[dev].buffer->buffer_blocks - transfer) * - scsi_tapes[dev].block_size; + if (scsi_tapes[dev].block_size == 0) + scsi_tapes[dev].buffer->buffer_bytes = + count - transfer; + else + scsi_tapes[dev].buffer->buffer_bytes = + (scsi_tapes[dev].buffer->buffer_blocks - transfer) * + scsi_tapes[dev].block_size; #ifdef DEBUG printk("st%d: EOF detected (%d blocks read, transferred %d bytes).\n", dev, scsi_tapes[dev].buffer->buffer_blocks - transfer, total); @@ -789,7 +821,7 @@ #ifdef DEBUG printk("st%d: Tape error. Sense key %x\n", dev, SCpnt->sense_buffer[2] & 0x0f); - decode_sns(dev, SCpnt->sense_buffer); + print_sense("st", SCpnt); #endif SCpnt->request.dev = -1; if (total) @@ -799,14 +831,14 @@ } } else { - transfer = st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer); + transfer = st_chk_result(SCpnt); SCpnt->request.dev = -1; /* Mark as not busy */ return transfer; } } - else - scsi_tapes[dev].buffer->buffer_bytes = - scsi_tapes[dev].buffer->buffer_size; + else /* Read successfu| */ + scsi_tapes[dev].buffer->buffer_bytes = bytes; + } /* if (scsi_tapes[dev].buffer->buffer_bytes == 0 && scsi_tapes[dev].eof == 0) */ @@ -836,6 +868,9 @@ return total; } + if (scsi_tapes[dev].block_size == 0) + count = total; /* Read only one variable length block */ + } /* for (total = 0; total < count; ) */ SCpnt->request.dev = -1; /* Mark as not busy */ @@ -848,7 +883,7 @@ static int st_int_ioctl(struct inode * inode,struct file * file, unsigned int cmd_in, unsigned long arg) { - int dev = inode->i_rdev; + int dev = MINOR(inode->i_rdev); int timeout = ST_LONG_TIMEOUT; long ltmp; int ioctl_result; @@ -1003,9 +1038,11 @@ break; case MTSETBLK: /* Set block length */ case MTSETDENSITY: /* Set tape density */ + case MTSETDRVBUFFER: /* Set drive buffering */ if (scsi_tapes[dev].dirty || scsi_tapes[dev].buffer->buffer_bytes != 0) return (-EIO); /* Not allowed if data in buffer */ if (cmd_in == MTSETBLK && + arg != 0 && (arg < scsi_tapes[dev].min_block || arg > scsi_tapes[dev].max_block || arg > ST_BUFFER_SIZE)) { printk("st%d: Illegal block size.\n", dev); @@ -1015,14 +1052,20 @@ cmd[4] = 12; memset(scsi_tapes[dev].buffer->b_data, 0, 12); - scsi_tapes[dev].buffer->b_data[2] = 0x10; /* buffered mode */ + if (cmd_in == MTSETDRVBUFFER) + scsi_tapes[dev].buffer->b_data[2] = (arg & 7) << 4; + else + scsi_tapes[dev].buffer->b_data[2] = + scsi_tapes[dev].drv_buffer << 4; scsi_tapes[dev].buffer->b_data[3] = 8; /* block descriptor length */ + if (cmd_in == MTSETDENSITY) + scsi_tapes[dev].buffer->b_data[4] = arg; + else + scsi_tapes[dev].buffer->b_data[4] = scsi_tapes[dev].density; if (cmd_in == MTSETBLK) ltmp = arg; - else { - scsi_tapes[dev].buffer->b_data[4] = arg; + else ltmp = scsi_tapes[dev].block_size; - } scsi_tapes[dev].buffer->b_data[9] = (ltmp >> 16); scsi_tapes[dev].buffer->b_data[10] = (ltmp >> 8); scsi_tapes[dev].buffer->b_data[11] = ltmp; @@ -1033,9 +1076,12 @@ scsi_tapes[dev].buffer->b_data[9] * 65536 + scsi_tapes[dev].buffer->b_data[10] * 256 + scsi_tapes[dev].buffer->b_data[11]); - else + else if (cmd_in == MTSETDENSITY) printk("st%d: Setting density code to %x.\n", dev, scsi_tapes[dev].buffer->b_data[4]); + else + printk("st%d: Setting drive buffer code to %d.\n", + (scsi_tapes[dev].buffer->b_data[2] >> 4) & 7); #endif break; default: @@ -1052,9 +1098,9 @@ if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting ); - SCpnt->request.dev = -1; /* Mark as not busy */ + ioctl_result = st_chk_result(SCpnt); - ioctl_result = st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer); + SCpnt->request.dev = -1; /* Mark as not busy */ if (!ioctl_result) { if (cmd_in == MTBSFM) @@ -1063,13 +1109,23 @@ ioctl_result = st_int_ioctl(inode, file, MTBSF, 1); else if (cmd_in == MTSETBLK) { scsi_tapes[dev].block_size = arg; - scsi_tapes[dev].buffer->buffer_blocks = - ST_BUFFER_SIZE / scsi_tapes[dev].block_size; - scsi_tapes[dev].buffer->buffer_size = - scsi_tapes[dev].buffer->buffer_blocks * scsi_tapes[dev].block_size; + if (arg != 0) { + scsi_tapes[dev].buffer->buffer_blocks = + ST_BUFFER_SIZE / scsi_tapes[dev].block_size; + scsi_tapes[dev].buffer->buffer_size = + scsi_tapes[dev].buffer->buffer_blocks * scsi_tapes[dev].block_size; + } + else { + scsi_tapes[dev].buffer->buffer_blocks = 1; + scsi_tapes[dev].buffer->buffer_size = ST_BUFFER_SIZE; + } scsi_tapes[dev].buffer->buffer_bytes = scsi_tapes[dev].buffer->read_pointer = 0; } + else if (cmd_in == MTSETDRVBUFFER) + scsi_tapes[dev].drv_buffer = arg; + else if (cmd_in == MTSETDENSITY) + scsi_tapes[dev].density = arg; if (cmd_in == MTEOM || cmd_in == MTWEOF) { scsi_tapes[dev].eof = 2; scsi_tapes[dev].eof_hit = 0; @@ -1089,7 +1145,7 @@ static int st_ioctl(struct inode * inode,struct file * file, unsigned int cmd_in, unsigned long arg) { - int dev = inode->i_rdev; + int dev = MINOR(inode->i_rdev); int i, cmd, result; struct mtop mtc; struct mtpos mt_pos; @@ -1167,7 +1223,7 @@ SCpnt->sense_buffer[0] = 0; scsi_do_cmd(SCpnt, (void *) scmd, (void *) scsi_tapes[dev].buffer->b_data, - ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_RETRIES); + ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting ); @@ -1249,6 +1305,8 @@ scsi_tapes[i].eof = 0; scsi_tapes[i].waiting = NULL; scsi_tapes[i].in_use = 0; + scsi_tapes[i].drv_buffer = 1; /* Try buffering if no mode sense */ + scsi_tapes[i].density = 0; } diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/st.h linux/kernel/blk_drv/scsi/st.h --- pl11/linux/kernel/blk_drv/scsi/st.h Sat Dec 19 19:05:00 1992 +++ linux/kernel/blk_drv/scsi/st.h Wed Jul 28 00:19:34 1993 @@ -31,6 +31,8 @@ unsigned write_prot:1; unsigned in_use:1; unsigned eof_hit:1; + unsigned drv_buffer:3; + unsigned char density; ST_buffer * buffer; int block_size; int min_block; diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/ultrastor.c linux/kernel/blk_drv/scsi/ultrastor.c --- pl11/linux/kernel/blk_drv/scsi/ultrastor.c Wed Jul 7 10:16:28 1993 +++ linux/kernel/blk_drv/scsi/ultrastor.c Wed Jul 28 00:19:35 1993 @@ -384,7 +384,14 @@ mscp.opcode = OP_SCSI; mscp.xdir = DTD_SCSI; mscp.dcn = FALSE; - mscp.ca = TRUE; + /* Tape drives don't work properly if the cache is used. The SCSI + READ command for a tape doesn't have a block offset, and the adapter + incorrectly assumes that all reads from the tape read the same + blocks. Results will depend on read buffer size and other disk + activity. + + ??? Which other device types should never use the cache? */ + mscp.ca = scsi_devices[SCpnt->index].type != TYPE_TAPE; mscp.target_id = SCpnt->target; mscp.ch_no = 0; mscp.lun = SCpnt->lun; diff -u --recursive --new-file pl11/linux/kernel/blk_drv/scsi/ultrastor.h linux/kernel/blk_drv/scsi/ultrastor.h --- pl11/linux/kernel/blk_drv/scsi/ultrastor.h Wed Feb 10 00:41:52 1993 +++ linux/kernel/blk_drv/scsi/ultrastor.h Wed Jul 28 00:01:25 1993 @@ -33,7 +33,7 @@ { "UltraStor 14F", ultrastor_detect, ultrastor_info, 0, \ ultrastor_queuecommand, ultrastor_abort, ultrastor_reset, \ 0, ultrastor_biosparam, ULTRASTOR_14F_MAX_CMDS, 0, \ - ULTRASTOR_14F_MAX_SG, ULTRASTOR_14F_MAX_CMDS_PER_LUN, 0, 0 } + ULTRASTOR_14F_MAX_SG, ULTRASTOR_14F_MAX_CMDS_PER_LUN, 0, 1 } #ifdef ULTRASTOR_PRIVATE diff -u --recursive --new-file pl11/linux/kernel/blk_drv/xd.c linux/kernel/blk_drv/xd.c --- pl11/linux/kernel/blk_drv/xd.c Sat Jul 3 02:59:00 1993 +++ linux/kernel/blk_drv/xd.c Wed Jul 28 00:19:35 1993 @@ -219,6 +219,13 @@ return (0); } break; + case BLKFLSBUF: + if(!suser()) return -EACCES; + if(!inode->i_rdev) return -EINVAL; + sync_dev(inode->i_rdev); + invalidate_buffers(inode->i_rdev); + return 0; + case BLKRRPART: return (xd_reread_partitions(inode->i_rdev)); RO_IOCTLS(inode->i_rdev,arg); } diff -u --recursive --new-file pl11/linux/kernel/chr_drv/Makefile linux/kernel/chr_drv/Makefile --- pl11/linux/kernel/chr_drv/Makefile Sun Jul 4 15:06:23 1993 +++ linux/kernel/chr_drv/Makefile Sat Aug 14 18:53:43 1993 @@ -21,7 +21,7 @@ OBJS = tty_io.o console.o keyboard.o serial.o \ tty_ioctl.o pty.o lp.o vt.o mem.o mouse.o \ busmouse.o psaux.o msbusmouse.o atixlmouse.o \ - tpqic02.o + tpqic02.o defkeymap.o all: chr_drv.a subdirs @@ -37,7 +37,7 @@ for i in $(SUBDIRS); do (cd $$i && $(MAKE) clean) || exit; done dep: - $(CPP) -M $(KEYBOARD) *.c > .depend + $(CPP) -M *.c > .depend for i in $(SUBDIRS); do (cd $$i && $(MAKE) dep) || exit; done dummy: diff -u --recursive --new-file pl11/linux/kernel/chr_drv/atixlmouse.c linux/kernel/chr_drv/atixlmouse.c --- pl11/linux/kernel/chr_drv/atixlmouse.c Tue Nov 10 22:30:04 1992 +++ linux/kernel/chr_drv/atixlmouse.c Wed Aug 4 22:38:40 1993 @@ -52,8 +52,8 @@ /* Same general mouse structure */ static struct mouse_status { - char buttons; - char latch_buttons; + unsigned char buttons; + unsigned char latch_buttons; int dx; int dy; int present; @@ -70,7 +70,7 @@ outb(ATIXL_MSE_READ_Y, ATIXL_MSE_CONTROL_PORT); /* Select IR2 - Y movement */ mouse.dy += inb( ATIXL_MSE_DATA_PORT); outb(ATIXL_MSE_READ_BUTTONS, ATIXL_MSE_CONTROL_PORT); /* Select IR0 - Button Status */ - mouse.latch_buttons |= inb( ATIXL_MSE_DATA_PORT); + mouse.latch_buttons |= inb(ATIXL_MSE_DATA_PORT); ATIXL_MSE_ENABLE_UPDATE(); mouse.ready = 1; wake_up_interruptible(&mouse.wait); @@ -118,9 +118,9 @@ return -EAGAIN; ATIXL_MSE_DISABLE_UPDATE(); /* Allowed interrupts to occur during data gathering - shouldn't hurt */ - put_fs_byte((char)(~mouse.latch_buttons&7) | 0x80 , buffer); - put_fs_byte((char)mouse.dx, buffer + 1); - put_fs_byte((char)-mouse.dy, buffer + 2); + put_fs_byte((~mouse.latch_buttons & 7) | 0x80 , buffer); + put_fs_byte(mouse.dx, buffer + 1); + put_fs_byte(-mouse.dy, buffer + 2); mouse.dx = 0; mouse.dy = 0; mouse.latch_buttons = mouse.buttons; diff -u --recursive --new-file pl11/linux/kernel/chr_drv/busmouse.c linux/kernel/chr_drv/busmouse.c --- pl11/linux/kernel/chr_drv/busmouse.c Sat Jun 26 10:28:07 1993 +++ linux/kernel/chr_drv/busmouse.c Tue Aug 10 11:10:35 1993 @@ -42,7 +42,8 @@ static void mouse_interrupt(int unused) { - char dx, dy, buttons; + char dx, dy; + unsigned char buttons; MSE_INT_OFF(); outb(MSE_READ_X_LOW, MSE_CONTROL_PORT); diff -u --recursive --new-file pl11/linux/kernel/chr_drv/console.c linux/kernel/chr_drv/console.c --- pl11/linux/kernel/chr_drv/console.c Tue Jul 13 00:20:06 1993 +++ linux/kernel/chr_drv/console.c Fri Aug 13 20:08:47 1993 @@ -21,6 +21,8 @@ * * Virtual Consoles, Screen Blanking, Screen Dumping, Color, Graphics * Chars, and VT100 enhancements by Peter MacDonald. + * + * Copy and paste function by Andrew Haylett. */ /* @@ -222,9 +224,9 @@ "\040\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376" "\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250" "\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376" - "\376\245\376\376\376\376\231\376\376\376\376\376\232\376\376\341" + "\376\245\376\376\376\376\231\376\235\376\376\376\232\376\376\341" "\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213" - "\376\244\225\242\223\376\224\366\376\227\243\226\201\376\376\230", + "\376\244\225\242\223\376\224\366\233\227\243\226\201\376\376\230", /* vt100 graphics */ (unsigned char *) "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" @@ -308,6 +310,9 @@ static inline void __set_origin(unsigned short offset) { +#ifdef CONFIG_SELECTION + clear_selection(); +#endif /* CONFIG_SELECTION */ cli(); __origin = offset; outb_p(12, video_port_reg); @@ -1545,13 +1550,16 @@ } #ifdef CONFIG_SELECTION +/* correction factor for when screen is hardware-scrolled */ +#define hwscroll_offset ((__real_origin - __origin) << 1) + /* set reverse video on characters s-e of console with selection. */ static void highlight(const int currcons, const int s, const int e) { unsigned char *p, *p1, *p2; - p1 = (unsigned char *)origin + s + 1; - p2 = (unsigned char *)origin + e + 1; + p1 = (unsigned char *)origin - hwscroll_offset + s + 1; + p2 = (unsigned char *)origin - hwscroll_offset + e + 1; if (p1 > p2) { p = p1; @@ -1585,7 +1593,7 @@ int sel_mode, new_sel_start, new_sel_end, spc; char *bp, *obp, *spos; int i, ps, pe; - char *off = (char *)origin; + char *off = (char *)origin - hwscroll_offset; unblank_screen(); args = (unsigned short *)(arg + 1); @@ -1689,7 +1697,7 @@ obp = bp = sel_buffer; for (i = sel_start; i <= sel_end; i += 2) { - spos = (char *)origin + i; + spos = (char *)off + i; *bp++ = *spos; if (!isspace(*spos)) obp = bp; @@ -1719,6 +1727,9 @@ { char *bp = sel_buffer; + if (! *bp) + return 0; + unblank_screen(); while (*bp) { put_tty_queue(*bp, &tty->read_q); diff -u --recursive --new-file pl11/linux/kernel/chr_drv/defkeymap.c linux/kernel/chr_drv/defkeymap.c --- pl11/linux/kernel/chr_drv/defkeymap.c +++ linux/kernel/chr_drv/defkeymap.c Sat Aug 14 18:51:41 1993 @@ -0,0 +1,351 @@ +/* Automatically generated by mktable */ +/* Do not edit this file! */ + +#include +#include + +u_short key_map[NR_KEYMAPS][NR_KEYS] = { + { + 0x0200, 0x001b, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, + 0x0037, 0x0038, 0x0039, 0x0030, 0x002d, 0x003d, 0x007f, 0x0009, + 0x0071, 0x0077, 0x0065, 0x0072, 0x0074, 0x0079, 0x0075, 0x0069, + 0x006f, 0x0070, 0x005b, 0x005d, 0x0201, 0x0702, 0x0061, 0x0073, + 0x0064, 0x0066, 0x0067, 0x0068, 0x006a, 0x006b, 0x006c, 0x003b, + 0x0027, 0x0060, 0x0700, 0x005c, 0x007a, 0x0078, 0x0063, 0x0076, + 0x0062, 0x006e, 0x006d, 0x002c, 0x002e, 0x002f, 0x0700, 0x030c, + 0x0703, 0x0020, 0x0207, 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, + 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x0208, 0x0209, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x003c, 0x010a, + 0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x001b, 0x0021, 0x0040, 0x0023, 0x0024, 0x0025, 0x005e, + 0x0026, 0x002a, 0x0028, 0x0029, 0x005f, 0x002b, 0x007f, 0x0009, + 0x0051, 0x0057, 0x0045, 0x0052, 0x0054, 0x0059, 0x0055, 0x0049, + 0x004f, 0x0050, 0x007b, 0x007d, 0x0201, 0x0702, 0x0041, 0x0053, + 0x0044, 0x0046, 0x0047, 0x0048, 0x004a, 0x004b, 0x004c, 0x003a, + 0x0022, 0x007e, 0x0700, 0x007c, 0x005a, 0x0058, 0x0043, 0x0056, + 0x0042, 0x004e, 0x004d, 0x003c, 0x003e, 0x003f, 0x0700, 0x030c, + 0x0703, 0x0020, 0x0207, 0x010a, 0x010b, 0x010c, 0x010d, 0x010e, + 0x010f, 0x0110, 0x0111, 0x0112, 0x0113, 0x0208, 0x0203, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x003e, 0x010a, + 0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, + 0x020b, 0x0601, 0x0602, 0x0117, 0x0600, 0x020a, 0x0115, 0x0116, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x0200, 0x0200, 0x0040, 0x0200, 0x0024, 0x0200, 0x0200, + 0x007b, 0x005b, 0x005d, 0x007d, 0x005c, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x007e, 0x0201, 0x0702, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0703, 0x0200, 0x0207, 0x050c, 0x050d, 0x050e, 0x050f, 0x0510, + 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0208, 0x0202, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x007c, 0x0516, + 0x0517, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x0200, 0x0200, 0x0000, 0x001b, 0x001c, 0x001d, 0x001e, + 0x001f, 0x007f, 0x0200, 0x0200, 0x001f, 0x0200, 0x0200, 0x0200, + 0x0011, 0x0017, 0x0005, 0x0012, 0x0014, 0x0019, 0x0015, 0x0009, + 0x000f, 0x0010, 0x001b, 0x001d, 0x0201, 0x0702, 0x0001, 0x0013, + 0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x0200, + 0x0007, 0x0000, 0x0700, 0x001c, 0x001a, 0x0018, 0x0003, 0x0016, + 0x0002, 0x000e, 0x000d, 0x0200, 0x0200, 0x007f, 0x0700, 0x030c, + 0x0703, 0x0000, 0x0207, 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, + 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x0208, 0x0204, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x010a, + 0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x020c, 0x0206, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x020c, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x081b, 0x0831, 0x0832, 0x0833, 0x0834, 0x0835, 0x0836, + 0x0837, 0x0838, 0x0839, 0x0830, 0x082d, 0x083d, 0x087f, 0x0809, + 0x0871, 0x0877, 0x0865, 0x0872, 0x0874, 0x0879, 0x0875, 0x0869, + 0x086f, 0x0870, 0x085b, 0x085d, 0x080d, 0x0702, 0x0861, 0x0873, + 0x0864, 0x0866, 0x0867, 0x0868, 0x086a, 0x086b, 0x086c, 0x083b, + 0x0827, 0x0860, 0x0700, 0x085c, 0x087a, 0x0878, 0x0863, 0x0876, + 0x0862, 0x086e, 0x086d, 0x082c, 0x082e, 0x082f, 0x0700, 0x030c, + 0x0703, 0x0820, 0x0207, 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, + 0x0505, 0x0506, 0x0507, 0x0508, 0x0509, 0x0208, 0x0209, 0x0907, + 0x0908, 0x0909, 0x030b, 0x0904, 0x0905, 0x0906, 0x030a, 0x0901, + 0x0902, 0x0903, 0x0900, 0x0310, 0x0206, 0x0200, 0x083c, 0x050a, + 0x050b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x020c, 0x0206, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x020c, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, { + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, + 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, + 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, + 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, +}; + +char func_buf[FUNC_BUFSIZE] = { + '\033', '[', '[', 'A', 0, + '\033', '[', '[', 'B', 0, + '\033', '[', '[', 'C', 0, + '\033', '[', '[', 'D', 0, + '\033', '[', '[', 'E', 0, + '\033', '[', '1', '7', '~', 0, + '\033', '[', '1', '8', '~', 0, + '\033', '[', '1', '9', '~', 0, + '\033', '[', '2', '0', '~', 0, + '\033', '[', '2', '1', '~', 0, + '\033', '[', '2', '3', '~', 0, + '\033', '[', '2', '4', '~', 0, + '\033', '[', '2', '5', '~', 0, + '\033', '[', '2', '6', '~', 0, + '\033', '[', '2', '8', '~', 0, + '\033', '[', '2', '9', '~', 0, + '\033', '[', '3', '1', '~', 0, + '\033', '[', '3', '2', '~', 0, + '\033', '[', '3', '3', '~', 0, + '\033', '[', '3', '4', '~', 0, + '\033', '[', '1', '~', 0, + '\033', '[', '2', '~', 0, + '\033', '[', '3', '~', 0, + '\033', '[', '4', '~', 0, + '\033', '[', '5', '~', 0, + '\033', '[', '6', '~', 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; + +char *func_table[NR_FUNC] = { + func_buf + 0, + func_buf + 5, + func_buf + 10, + func_buf + 15, + func_buf + 20, + func_buf + 25, + func_buf + 31, + func_buf + 37, + func_buf + 43, + func_buf + 49, + func_buf + 55, + func_buf + 61, + func_buf + 67, + func_buf + 73, + func_buf + 79, + func_buf + 85, + func_buf + 91, + func_buf + 97, + func_buf + 103, + func_buf + 109, + func_buf + 115, + func_buf + 120, + func_buf + 125, + func_buf + 130, + func_buf + 135, + func_buf + 140, + func_buf + 145, + func_buf + 146, + func_buf + 147, + func_buf + 148, + func_buf + 149, + func_buf + 150, +}; diff -u --recursive --new-file pl11/linux/kernel/chr_drv/defkeymap.map linux/kernel/chr_drv/defkeymap.map --- pl11/linux/kernel/chr_drv/defkeymap.map +++ linux/kernel/chr_drv/defkeymap.map Sat Aug 14 18:51:41 1993 @@ -0,0 +1,246 @@ +keycode 0 = +keycode 1 = Escape Escape + alt keycode 1 = Meta_Escape +keycode 2 = one exclam + alt keycode 2 = Meta_one +keycode 3 = two at at + control keycode 3 = nul + alt keycode 3 = Meta_two +keycode 4 = three numbersign + control keycode 4 = Escape + alt keycode 4 = Meta_three +keycode 5 = four dollar dollar + control keycode 5 = Control_backslash + alt keycode 5 = Meta_four +keycode 6 = five percent + control keycode 6 = Control_bracketright + alt keycode 6 = Meta_five +keycode 7 = six asciicircum + control keycode 7 = Control_asciicircum + alt keycode 7 = Meta_six +keycode 8 = seven ampersand braceleft + control keycode 8 = Control_underscore + alt keycode 8 = Meta_seven +keycode 9 = eight asterisk bracketleft + control keycode 9 = Delete + alt keycode 9 = Meta_eight +keycode 10 = nine parenleft bracketright + alt keycode 10 = Meta_nine +keycode 11 = zero parenright braceright + alt keycode 11 = Meta_zero +keycode 12 = minus underscore backslash + control keycode 12 = Control_underscore + alt keycode 12 = Meta_minus +keycode 13 = equal plus + alt keycode 13 = Meta_equal +keycode 14 = Delete Delete + alt keycode 14 = Meta_Delete +keycode 15 = Tab Tab + alt keycode 15 = Meta_Tab +keycode 16 = q +keycode 17 = w +keycode 18 = e +keycode 19 = r +keycode 20 = t +keycode 21 = y +keycode 22 = u +keycode 23 = i +keycode 24 = o +keycode 25 = p +keycode 26 = bracketleft braceleft + control keycode 26 = Escape + alt keycode 26 = Meta_bracketleft +keycode 27 = bracketright braceright asciitilde + control keycode 27 = Control_bracketright + alt keycode 27 = Meta_bracketright +keycode 28 = Return + alt keycode 28 = 0x080d +keycode 29 = Control +keycode 30 = a +keycode 31 = s +keycode 32 = d +keycode 33 = f +keycode 34 = g +keycode 35 = h +keycode 36 = j +keycode 37 = k +keycode 38 = l +keycode 39 = semicolon colon + alt keycode 39 = Meta_semicolon +keycode 40 = apostrophe quotedbl + control keycode 40 = Control_g + alt keycode 40 = Meta_apostrophe +keycode 41 = grave asciitilde + control keycode 41 = nul + alt keycode 41 = Meta_grave +keycode 42 = Shift +keycode 43 = backslash bar + control keycode 43 = Control_backslash + alt keycode 43 = Meta_backslash +keycode 44 = z +keycode 45 = x +keycode 46 = c +keycode 47 = v +keycode 48 = b +keycode 49 = n +keycode 50 = m +keycode 51 = comma less + alt keycode 51 = Meta_comma +keycode 52 = period greater + alt keycode 52 = Meta_period +keycode 53 = slash question + control keycode 53 = Delete + alt keycode 53 = Meta_slash +keycode 54 = Shift +keycode 55 = KP_Multiply +keycode 56 = Alt +keycode 57 = space space + control keycode 57 = nul + alt keycode 57 = Meta_space +keycode 58 = Caps_Lock +keycode 59 = F1 F11 Console_13 + control keycode 59 = F1 + alt keycode 59 = Console_1 +keycode 60 = F2 F12 Console_14 + control keycode 60 = F2 + alt keycode 60 = Console_2 +keycode 61 = F3 F13 Console_15 + control keycode 61 = F3 + alt keycode 61 = Console_3 +keycode 62 = F4 F14 Console_16 + control keycode 62 = F4 + alt keycode 62 = Console_4 +keycode 63 = F5 F15 Console_17 + control keycode 63 = F5 + alt keycode 63 = Console_5 +keycode 64 = F6 F16 Console_18 + control keycode 64 = F6 + alt keycode 64 = Console_6 +keycode 65 = F7 F17 Console_19 + control keycode 65 = F7 + alt keycode 65 = Console_7 +keycode 66 = F8 F18 Console_20 + control keycode 66 = F8 + alt keycode 66 = Console_8 +keycode 67 = F9 F19 Console_21 + control keycode 67 = F9 + alt keycode 67 = Console_9 +keycode 68 = F10 F20 Console_22 + control keycode 68 = F10 + alt keycode 68 = Console_10 +keycode 69 = Num_Lock +keycode 70 = Scroll_Lock Show_Memory Show_Registers + control keycode 70 = Show_State + alt keycode 70 = Scroll_Lock +keycode 71 = KP_7 + alt keycode 71 = Ascii_7 +keycode 72 = KP_8 + alt keycode 72 = Ascii_8 +keycode 73 = KP_9 + alt keycode 73 = Ascii_9 +keycode 74 = KP_Subtract +keycode 75 = KP_4 + alt keycode 75 = Ascii_4 +keycode 76 = KP_5 + alt keycode 76 = Ascii_5 +keycode 77 = KP_6 + alt keycode 77 = Ascii_6 +keycode 78 = KP_Add +keycode 79 = KP_1 + alt keycode 79 = Ascii_1 +keycode 80 = KP_2 + alt keycode 80 = Ascii_2 +keycode 81 = KP_3 + alt keycode 81 = Ascii_3 +keycode 82 = KP_0 + alt keycode 82 = Ascii_0 +keycode 83 = KP_Period + altgr control keycode 83 = Boot + control alt keycode 83 = Boot +keycode 84 = Last_Console +keycode 85 = +keycode 86 = less greater bar + alt keycode 86 = Meta_less +keycode 87 = F11 F11 Console_23 + control keycode 87 = F11 + alt keycode 87 = Console_11 +keycode 88 = F12 F12 Console_24 + control keycode 88 = F12 + alt keycode 88 = Console_12 +keycode 89 = +keycode 90 = +keycode 91 = +keycode 92 = +keycode 93 = +keycode 94 = +keycode 95 = +keycode 96 = KP_Enter +keycode 97 = Control +keycode 98 = KP_Divide +keycode 99 = Control_backslash + control keycode 99 = Control_backslash + alt keycode 99 = Control_backslash +keycode 100 = AltGr +keycode 101 = Break +keycode 102 = Find +keycode 103 = Up +keycode 104 = Prior + shift keycode 104 = Scroll_Backward +keycode 105 = Left +keycode 106 = Right +keycode 107 = Select +keycode 108 = Down +keycode 109 = Next + shift keycode 109 = Scroll_Forward +keycode 110 = Insert +keycode 111 = Remove + altgr control keycode 111 = Boot + control alt keycode 111 = Boot +keycode 112 = +keycode 113 = +keycode 114 = +keycode 115 = +keycode 116 = +keycode 117 = +keycode 118 = +keycode 119 = +keycode 120 = +keycode 121 = +keycode 122 = +keycode 123 = +keycode 124 = +keycode 125 = +keycode 126 = +keycode 127 = +string F1 = "\033[[A" +string F2 = "\033[[B" +string F3 = "\033[[C" +string F4 = "\033[[D" +string F5 = "\033[[E" +string F6 = "\033[17~" +string F7 = "\033[18~" +string F8 = "\033[19~" +string F9 = "\033[20~" +string F10 = "\033[21~" +string F11 = "\033[23~" +string F12 = "\033[24~" +string F13 = "\033[25~" +string F14 = "\033[26~" +string F15 = "\033[28~" +string F16 = "\033[29~" +string F17 = "\033[31~" +string F18 = "\033[32~" +string F19 = "\033[33~" +string F20 = "\033[34~" +string Find = "\033[1~" +string Insert = "\033[2~" +string Remove = "\033[3~" +string Select = "\033[4~" +string Prior = "\033[5~" +string Next = "\033[6~" +string F21 = "" +string F22 = "" +string F23 = "" +string F24 = "" +string F25 = "" +string F26 = "" diff -u --recursive --new-file pl11/linux/kernel/chr_drv/keyboard.c linux/kernel/chr_drv/keyboard.c --- pl11/linux/kernel/chr_drv/keyboard.c Wed Jul 7 10:30:06 1993 +++ linux/kernel/chr_drv/keyboard.c Sat Aug 14 18:52:34 1993 @@ -21,19 +21,26 @@ #include #include #include +#include +#include + #ifndef KBD_DEFFLAGS + #ifdef CONFIG_KBD_META -#define KBD_DEFFLAGS ((1 << VC_NUMLOCK) | (1 << VC_REPEAT) | (1 << VC_META)) +#define KBD_META (1 << VC_META) #else -#define KBD_DEFFLAGS ((1 << VC_NUMLOCK) | (1 << VC_REPEAT)) +#define KBD_META 0 #endif + +#ifdef CONFIG_KBD_NUML +#define KBD_NUML (1 << VC_NUMLOCK) +#else +#define KBD_NUML 0 #endif -#define SHIFT_KEYS ((1 << KG_LSHIFT) | (1 << KG_RSHIFT)) -#define CTRL_KEYS ((1 << KG_LCTRL) | (1 << KG_RCTRL)) -#define ALT_KEYS ((1 << KG_LALT) | (1 << KG_RALT)) -#define ALTGR_KEYS ((1 << KG_LALTGR) | (1 << KG_RALTGR)) +#define KBD_DEFFLAGS (KBD_NUML | (1 << VC_REPEAT) | KBD_META) +#endif /* * The default IO slowdown is doing 'inb()'s from 0x61, which should be @@ -55,10 +62,10 @@ #define fake_keyboard_interrupt() \ __asm__ __volatile__("int $0x21") -unsigned long kbd_flags = 0; unsigned long kbd_dead_keys = 0; unsigned long kbd_prev_dead_keys = 0; +static unsigned char k_down[NR_SHIFT] = {0, }; static int want_console = -1; static int last_console = 0; /* last used VC */ static char rep = 0; /* flag telling character repeat */ @@ -69,7 +76,6 @@ static volatile unsigned char acknowledge = 0; static volatile unsigned char resend = 0; -typedef unsigned short u_word; typedef void (*k_hand)(unsigned char value, char up_flag); static void do_self(unsigned char value, char up_flag); @@ -80,17 +86,21 @@ static void do_cons(unsigned char value, char up_flag); static void do_cur(unsigned char value, char up_flag); static void do_shift(unsigned char value, char up_flag); +static void do_meta(unsigned char value, char up_flag); +static void do_ascii(unsigned char value, char up_flag); static k_hand key_handler[] = { - do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift + do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift, + do_meta, do_ascii }; /* maximum values each key_handler can handle */ const int max_vals[] = { - 255, 25, 13, 16, 4, 255, 3, 255 + 255, NR_FUNC - 1, 13, 16, 4, 255, 3, NR_SHIFT, + 255, 9 }; -const int NR_TYPES = (sizeof(max_vals) / sizeof(u_char)); +const int NR_TYPES = (sizeof(max_vals) / sizeof(int)); #define E0_BASE 96 @@ -103,6 +113,8 @@ static struct pt_regs * pt_regs; +static inline void translate(unsigned char scancode); + static inline void kb_wait(void) { int i; @@ -115,7 +127,6 @@ static void keyboard_interrupt(int int_pt_regs) { unsigned char scancode; - static unsigned char prev_scancode = 0; pt_regs = (struct pt_regs *) int_pt_regs; kbd_prev_dead_keys |= kbd_dead_keys; @@ -137,16 +148,53 @@ tty = TTY_TABLE(0); kbd = kbd_table + fg_console; if (vc_kbd_flag(kbd,VC_RAW)) { - kbd_flags = 0; + memset(k_down, 0, sizeof(k_down)); put_queue(scancode); goto end_kbd_intr; - } + } else + translate(scancode); +end_kbd_intr: + return; +} + +static inline void translate(unsigned char scancode) +{ + char break_flag; + static unsigned long key_down[8] = { 0, }; + static unsigned char e0_keys[] = { + 0x1c, /* keypad enter */ + 0x1d, /* right control */ + 0x35, /* keypad slash */ + 0x37, /* print screen */ + 0x38, /* right alt */ + 0x46, /* break (control-pause) */ + 0x47, /* editpad home */ + 0x48, /* editpad up */ + 0x49, /* editpad pgup */ + 0x4b, /* editpad left */ + 0x4d, /* editpad right */ + 0x4f, /* editpad end */ + 0x50, /* editpad dn */ + 0x51, /* editpad pgdn */ + 0x52, /* editpad ins */ + 0x53, /* editpad del */ +#ifdef LK450 + 0x3d, /* f13 */ + 0x3e, /* f14 */ + 0x3f, /* help */ + 0x40, /* do */ + 0x41, /* f17 */ + 0x4e /* keypad minus/plus */ +#endif + }; + if (scancode == 0xe0) { set_kbd_dead(KGD_E0); - goto end_kbd_intr; - } else if (scancode == 0xe1) { + return; + } + if (scancode == 0xe1) { set_kbd_dead(KGD_E1); - goto end_kbd_intr; + return; } /* * The keyboard maintains its own internal caps lock and num lock @@ -157,67 +205,54 @@ */ if (kbd_dead(KGD_E0) && (scancode == 0x2a || scancode == 0xaa || scancode == 0x36 || scancode == 0xb6)) - goto end_kbd_intr; + return; + + /* map two byte scancodes into one byte id's */ + + break_flag = scancode > 0x7f; + scancode &= 0x7f; + if (scancode >= E0_BASE) { +#if 0 + printk("keyboard: scancode (%02x) not in range 00 - %2x\n", scancode, E0_BASE - 1); +#endif + return; + } + + if (kbd_dead(KGD_E0)) { + int i; + for (i = 0; i < sizeof(e0_keys); i++) + if (scancode == e0_keys[i]) { + scancode = E0_BASE + i; + i = -1; + break; + } + if (i != -1) { +#if 0 + printk("keyboard: unknown scancode e0 %02x\n", scancode); +#endif + return; + } + } + + rep = 0; + if (break_flag) + clear_bit(scancode, key_down); + else + rep = set_bit(scancode, key_down); + /* * Repeat a key only if the input buffers are empty or the * characters get echoed locally. This makes key repeat usable * with slow applications and under heavy loads. */ - rep = scancode == prev_scancode; - prev_scancode = scancode; if (!rep || (vc_kbd_flag(kbd,VC_REPEAT) && tty && (L_ECHO(tty) || (EMPTY(&tty->secondary) && EMPTY(&tty->read_q))))) { - static unsigned char e0_keys[] = { - 0x1c, /* keypad enter */ - 0x1d, /* right control */ - 0x35, /* keypad slash */ - 0x37, /* print screen */ - 0x38, /* right alt */ - 0x46, /* break (control-pause) */ - 0x47, /* editpad home */ - 0x48, /* editpad up */ - 0x49, /* editpad pgup */ - 0x4b, /* editpad left */ - 0x4d, /* editpad right */ - 0x4f, /* editpad end */ - 0x50, /* editpad dn */ - 0x51, /* editpad pgdn */ - 0x52, /* editpad ins */ - 0x53 /* editpad del */ - }; - u_word key_code; - char break_flag = scancode > 0x7f; - - scancode &= 0x7f; - if (scancode >= E0_BASE) { -#if 0 - printk("keyboard: scancode (%02x) not in range 00 - %2x\n", scancode, E0_BASE - 1); -#endif - goto end_kbd_intr; - } - - if (kbd_dead(KGD_E0)) { - int i; - for (i = 0; i < sizeof(e0_keys); i++) - if (scancode == e0_keys[i]) { - scancode = E0_BASE + i; - i = -1; - break; - } - if (i != -1) { -#if 0 - printk("keyboard: unknown scancode e0 %02x\n", scancode); -#endif - goto end_kbd_intr; - } - } + u_short key_code; key_code = key_map[shift_state][scancode]; (*key_handler[key_code >> 8])(key_code & 0xff, break_flag); - } -end_kbd_intr: - return; + } } static void put_queue(int ch) @@ -276,10 +311,16 @@ { if (rep) return; - set_kbd_flag(KG_CAPSLOCK); chg_vc_kbd_flag(kbd,VC_CAPSLOCK); } +static void caps_on(void) +{ + if (rep) + return; + set_vc_kbd_flag(kbd,VC_CAPSLOCK); +} + static void show_ptregs(void) { if (!pt_regs) @@ -300,12 +341,6 @@ static void hold(void) { - /* kludge, should have a control key table */ - if (kbd_flags & CTRL_KEYS) { - show_state(); - return; - } - if (rep || !tty) return; if (vc_kbd_flag(kbd, VC_SCROLLOCK)) @@ -320,7 +355,7 @@ static void num(void) { #if 0 - if (kbd_flags & CTRL_KEYS) { + if (k_down[KG_CTRL]) { /* pause key pressed, sends E1 1D 45, ChN */ chg_vc_kbd_flag(kbd,VC_PAUSE); return; @@ -346,6 +381,22 @@ put_queue(INTR_CHAR(tty)); } +static void scrll_forw(void) +{ + scrollfront(0); +} + +static void scrll_back(void) +{ + scrollback(0); +} + +static void boot_it(void) +{ + ctrl_alt_del(); +} + + static void do_spec(unsigned char value, char up_flag) { typedef void (*fnp)(void); @@ -352,7 +403,8 @@ fnp fn_table[] = { NULL, enter, show_ptregs, show_mem, show_state, send_intr, lastcons, caps_toggle, - num, hold + num, hold, scrll_forw, scrll_back, + boot_it, caps_on }; if (value >= sizeof(fn_table)/sizeof(fnp)) @@ -371,42 +423,20 @@ value = handle_diacr(value); - /* kludge... */ + /* kludge... but works for ISO 8859-1 */ if (vc_kbd_flag(kbd,VC_CAPSLOCK)) if ((value >= 'a' && value <= 'z') || (value >= 224 && value <= 254)) { value -= 32; } - /* kludge... */ - if (kbd_flags & CTRL_KEYS) { - if (value >= 64 && value < 127) - value &= 0x1f; - else if (value == ' ' || value == '2') - value = 0; - else if (value >= '3' && value < '8') - value -= 24; - else if (value == '?' || value == '8' || value == '/') - value = 127; - else if (value == '-' || value == '_') - value = 0x1f; - else - return; - } - if (kbd_flags & ALT_KEYS) - if (vc_kbd_flag(kbd,VC_META)) { - put_queue('\033'); - put_queue(value); - } else - put_queue(value|0x80); - else - put_queue(value); + put_queue(value); } static unsigned char ret_diacr[] = {'`', '\'', '^', '~', '"' }; /* Must not end with 0 */ -/* If a dead key pressed twice, output a character corresponding it, */ +/* If a dead key pressed twice, output a character corresponding to it, */ /* otherwise just remember the dead key. */ static void do_dead(unsigned char value, char up_flag) @@ -469,37 +499,10 @@ want_console = value; } -static char *func_table[] = { - "\033[[A", "\033[[B", "\033[[C", "\033[[D", "\033[[E", /* F1 -F5 */ - "\033[17~", "\033[18~", "\033[19~", "\033[20~", "\033[21~",/* F6 -F10 */ - "\033[23~", "\033[24~", "\033[25~", "\033[26~", "\033[28~",/* F11-F15 */ - "\033[29~", "\033[31~", "\033[32~", "\033[33~", "\033[34~",/* F16-F20 */ - "\033[1~", /* Find */ - "\033[2~", /* Insert */ - "\033[3~", /* Remove */ - "\033[4~", /* Select */ - "\033[5~", /* Prev page */ - "\033[6~" /* Next page */ -}; - static void do_fn(unsigned char value, char up_flag) { if (up_flag) return; - if (kbd_flags & SHIFT_KEYS) { - if (value == KVAL(K_PGDN)) { - scrollfront(0); - return; - } - if (value == KVAL(K_PGUP)) { - scrollback(0); - return; - } - } - if ((kbd_flags & ALT_KEYS) && value < 12) { - want_console = value; - return; - } puts_queue(func_table[value]); } @@ -511,19 +514,8 @@ if (up_flag) return; /* no action, if this is a key release */ - if ((value == KVAL(K_PCOMMA) || value == KVAL(K_PDOT)) && - (kbd_flags & CTRL_KEYS) && (kbd_flags & (ALT_KEYS | ALTGR_KEYS))) { - ctrl_alt_del(); - return; - } - - if ((kbd_flags & ALT_KEYS) && value <= 9) { /* Alt-numpad */ - npadch = (npadch * 10 + value) % 1000; - return; - } - /* kludge... shift forces cursor/number keys */ - if (vc_kbd_flag(kbd,VC_APPLIC) && shift_state != 1) { + if (vc_kbd_flag(kbd,VC_APPLIC) && !k_down[KG_SHIFT]) { applkey(app_map[value], 1); return; } @@ -562,12 +554,12 @@ do_fn(KVAL(K_PGUP), 0); return; case KVAL(K_P5): - applkey('G', vc_kbd_flag(kbd,VC_APPLIC)); + applkey('G', vc_kbd_flag(kbd, VC_APPLIC)); return; } put_queue(pad_chars[value]); - if (value == KVAL(K_PENTER) && vc_kbd_flag(kbd,VC_CRLF)) + if (value == KVAL(K_PENTER) && vc_kbd_flag(kbd, VC_CRLF)) put_queue(10); } @@ -582,73 +574,54 @@ static void do_shift(unsigned char value, char up_flag) { - shift_state = 0; - if (up_flag) - clr_kbd_flag(value); + int old_state = shift_state; + + if (rep) + return; + + /* kludge... */ + if (value == KVAL(K_CAPSSHIFT)) { + value = KVAL(K_SHIFT); + clr_vc_kbd_flag(kbd, VC_CAPSLOCK); + } + + if (up_flag) { + if (k_down[value]) + k_down[value]--; + } else + k_down[value]++; + + if (k_down[value]) + shift_state |= (1 << value); else - set_kbd_flag(value); - if (kbd_flags & SHIFT_KEYS) - shift_state = 1; - if (kbd_flags & ALTGR_KEYS) - shift_state = 2; - /* cludge */ - if (up_flag && value == KG_LALT && npadch != 0) { + shift_state &= ~ (1 << value); + + /* kludge */ + if (up_flag && shift_state != old_state && npadch != 0) { put_queue(npadch); - npadch=0; + npadch = 0; } } -#define C(x) ((KT_CONS<<8)|x) +static void do_meta(unsigned char value, char up_flag) +{ + if (up_flag) + return; + + if (vc_kbd_flag(kbd, VC_META)) { + put_queue('\033'); + put_queue(value); + } else + put_queue(value | 0x80); +} + +static void do_ascii(unsigned char value, char up_flag) +{ + if (up_flag) + return; -u_word key_map[NR_KEYMAPS][NR_KEYS] = { - { /* unshifted keys */ - K_HOLE, 27, '1', '2', '3', '4', '5', '6', - '7', '8', '9', '0', '-', '=', 127, 9, - 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', - 'o', 'p', '[', ']', K_ENTER, K_LCTRL, 'a', 's', - 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', - '\'', '`', K_LSHIFT, '\\', 'z', 'x', 'c', 'v', - 'b', 'n', 'm', ',', '.', '/', K_RSHIFT, K_PSTAR, - K_ALT, ' ', K_CAPS, K_F1, K_F2, K_F3, K_F4, K_F5, - K_F6, K_F7, K_F8, K_F9, K_F10, K_NUM, K_HOLD, K_P7, - K_P8, K_P9, K_PMINUS, K_P4, K_P5, K_P6, K_PPLUS, K_P1, - K_P2, K_P3, K_P0, K_PDOT, K_CONS, K_HOLE, '<', K_F11, - K_F12, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, - /* unshifted e0 keys */ - K_PENTER, K_RCTRL, K_PSLASH, 28, K_ALTGR, K_BREAK, K_FIND, K_UP, - K_PGUP, K_LEFT, K_RIGHT, K_SELECT, K_DOWN, K_PGDN, K_INSERT, K_REMOVE }, - { /* shifted keys */ - K_HOLE, 27, '!', '@', '#', '$', '%', '^', - '&', '*', '(', ')', '_', '+', 127, 9, - 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', - 'O', 'P', '{', '}', K_ENTER, K_LCTRL, 'A', 'S', - 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', - '"', '~', K_LSHIFT, '|', 'Z', 'X', 'C', 'V', - 'B', 'N', 'M', '<', '>', '?', K_RSHIFT, K_PSTAR, - K_ALT, 32, K_CAPS, K_F11, K_F12, K_F13, K_F14, K_F15, - K_F16, K_F17, K_F18, K_F19, K_F20, K_NUM, K_SH_MEM, K_P7, - K_P8, K_P9, K_PMINUS, K_P4, K_P5, K_P6, K_PPLUS, K_P1, - K_P2, K_P3, K_P0, K_PDOT, K_CONS, K_HOLE, '>', K_F11, - K_F12, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, - /* shifted e0 keys */ - K_PENTER, K_RCTRL, K_PSLASH, K_HOLE, K_ALTGR, K_BREAK, K_FIND, K_UP, - K_PGUP, K_LEFT, K_RIGHT, K_SELECT, K_DOWN, K_PGDN, K_INSERT, K_REMOVE }, - { /* alted keys */ - K_HOLE, K_HOLE, K_HOLE, '@', K_HOLE, '$', K_HOLE, K_HOLE, - '{', '[', ']', '}', '\\', K_HOLE, K_HOLE, K_HOLE, - K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, - K_HOLE, K_HOLE, K_HOLE, '~', K_ENTER, K_LCTRL, K_HOLE, K_HOLE, - K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, - K_HOLE, K_HOLE, K_LSHIFT, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, - K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_RSHIFT, K_HOLE, - K_ALT, K_HOLE, K_CAPS, C(12), C(13), C(14), C(15), C(16), - C(17), C(18), C(19), C(20), C(21), K_NUM, K_SH_REGS, K_P7, - K_P8, K_P9, K_PMINUS, K_P4, K_P5, K_P6, K_PPLUS, K_P1, - K_P2, K_P3, K_P0, K_PDOT, K_CONS, K_HOLE, '|', C(22), - C(23), K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, - /* alted e0 keys */ - K_PENTER, K_RCTRL, K_PSLASH, K_HOLE, K_ALTGR, K_BREAK, K_FIND, K_UP, - K_PGUP, K_LEFT, K_RIGHT, K_SELECT, K_DOWN, K_PGDN, K_INSERT, K_REMOVE }}; + npadch = (npadch * 10 + value) % 1000; +} /* * send_data sends a character to the keyboard and waits @@ -752,6 +725,7 @@ kbd->flags = KBD_DEFFLAGS; kbd->default_flags = KBD_DEFFLAGS; } + bh_base[KEYBOARD_BH].routine = kbd_bh; request_irq(KEYBOARD_IRQ,keyboard_interrupt); mark_bh(KEYBOARD_BH); diff -u --recursive --new-file pl11/linux/kernel/chr_drv/mem.c linux/kernel/chr_drv/mem.c --- pl11/linux/kernel/chr_drv/mem.c Sun Jul 4 15:06:23 1993 +++ linux/kernel/chr_drv/mem.c Mon Aug 9 18:02:32 1993 @@ -63,7 +63,7 @@ read += count1; } - while (p < (4096 + 4096) && count > 0) { + while (p < 2*PAGE_SIZE && count > 0) { put_fs_byte(0,buf); buf++; p++; @@ -70,7 +70,7 @@ count--; read++; } - memcpy_tofs(buf,(void *) (p - 4096),count); + memcpy_tofs(buf,(void *) (p - PAGE_SIZE),count); read += count; file->f_pos += read; return read; @@ -88,7 +88,7 @@ if (count > high_memory - p) count = high_memory - p; read = 0; - while (p < 4096 && count > 0) { + while (p < PAGE_SIZE && count > 0) { put_fs_byte(0,buf); buf++; p++; @@ -113,7 +113,7 @@ if (count > high_memory - p) count = high_memory - p; written = 0; - while (p < 4096 && count > 0) { + while (p < PAGE_SIZE && count > 0) { /* Hmm. Do something? */ buf++; p++; diff -u --recursive --new-file pl11/linux/kernel/chr_drv/msbusmouse.c linux/kernel/chr_drv/msbusmouse.c --- pl11/linux/kernel/chr_drv/msbusmouse.c Wed Jun 30 17:53:31 1993 +++ linux/kernel/chr_drv/msbusmouse.c Sat Aug 7 11:30:02 1993 @@ -44,7 +44,8 @@ static void ms_mouse_interrupt(int unused) { - char dx, dy, buttons; + char dx, dy; + unsigned char buttons; outb(MS_MSE_COMMAND_MODE, MS_MSE_CONTROL_PORT); outb((inb(MS_MSE_DATA_PORT) | 0x20), MS_MSE_DATA_PORT); @@ -103,7 +104,7 @@ static int read_mouse(struct inode * inode, struct file * file, char * buffer, int count) { - int i; + int i, dx, dy; if (count < 3) return -EINVAL; @@ -110,11 +111,15 @@ if (!mouse.ready) return -EAGAIN; put_fs_byte(mouse.buttons | 0x80, buffer); - put_fs_byte((char)(mouse.dx<-127 ? -127 : mouse.dx>127 ? 127 : mouse.dx), buffer + 1); - put_fs_byte((char)(mouse.dy<-127 ? 127 : mouse.dy>127 ? -127 : -mouse.dy), buffer + 2); + dx = mouse.dx < -127 ? -127 : mouse.dx > 127 ? 127 : mouse.dx; + dy = mouse.dy < -127 ? 127 : mouse.dy > 127 ? -127 : -mouse.dy; + put_fs_byte((char)dx, buffer + 1); + put_fs_byte((char)dy, buffer + 2); for (i = 3; i < count; i++) put_fs_byte(0x00, buffer + i); - mouse.dx = mouse.dy = mouse.ready = 0; + mouse.dx -= dx; + mouse.dy += dy; + mouse.ready = 0; return i; } diff -u --recursive --new-file pl11/linux/kernel/chr_drv/serial.c linux/kernel/chr_drv/serial.c --- pl11/linux/kernel/chr_drv/serial.c Fri Jul 2 21:39:00 1993 +++ linux/kernel/chr_drv/serial.c Fri Aug 13 21:20:52 1993 @@ -49,9 +49,14 @@ * CONFIG_ACCENT_ASYNC * Enables support for the Accent Async 4 port serial * port. - * + * + * CONFIG_HUB6 + * Enables support for the venerable Bell Technologies + * HUB6 card. */ +#undef ISR_HACK + #define WAKEUP_CHARS (3*TTY_BUF_SIZE/4) /* @@ -109,13 +114,35 @@ #else #define BOCA_FLAGS AUTO_IRQ_FLAG #endif + +#ifdef CONFIG_HUB6 +#define HUB6_FLAGS (ASYNC_BOOT_AUTOCONF) +#else +#define HUB6_FLAGS 0 +#endif +/* + * The following define the access methods for the HUB6 card. All + * access is through two ports for all 24 possible chips. The card is + * selected through the high 2 bits, the port on that card with the + * "middle" 3 bits, and the register on that port with the bottom + * 3 bits. + * + * While the access port and interrupt is configurable, the default + * port locations are 0x302 for the port control register, and 0x303 + * for the data read/write register. Normally, the interrupt is at irq3 + * but can be anything from 3 to 7 inclusive. Note tht using 3 will + * require disabling com2. + */ + +#define C_P(card,port) (((card)<<6|(port)<<3) + 1) + struct async_struct rs_table[] = { /* UART CLK PORT IRQ FLAGS */ - { BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS, }, /* ttyS0 */ - { BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS, }, /* ttyS1 */ - { BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS, }, /* ttyS2 */ - { BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS, }, /* ttyS3 */ + { BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ + { BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ + { BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */ + { BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */ { BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, /* ttyS4 */ { BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS }, /* ttyS5 */ @@ -127,10 +154,10 @@ { BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS }, /* ttyS10 */ { BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS }, /* ttyS11 */ - { BASE_BAUD, 0x330, 4, ACCENT_FLAGS }, /* ttyS12 */ - { BASE_BAUD, 0x338, 4, ACCENT_FLAGS }, /* ttyS13 */ - { BASE_BAUD, 0x000, 0 }, /* ttyS14 (spare; user configurable) */ - { BASE_BAUD, 0x000, 0 }, /* ttyS15 (spare; user configurable) */ + { BASE_BAUD, 0x330, 4, ACCENT_FLAGS }, /* ttyS12 */ + { BASE_BAUD, 0x338, 4, ACCENT_FLAGS }, /* ttyS13 */ + { BASE_BAUD, 0x000, 0, 0 }, /* ttyS14 (spare; user configurable) */ + { BASE_BAUD, 0x000, 0, 0 }, /* ttyS15 (spare; user configurable) */ { BASE_BAUD, 0x100, 12, BOCA_FLAGS }, /* ttyS16 */ { BASE_BAUD, 0x108, 12, BOCA_FLAGS }, /* ttyS17 */ @@ -148,6 +175,22 @@ { BASE_BAUD, 0x168, 12, BOCA_FLAGS }, /* ttyS29 */ { BASE_BAUD, 0x170, 12, BOCA_FLAGS }, /* ttyS30 */ { BASE_BAUD, 0x178, 12, BOCA_FLAGS }, /* ttyS31 */ + +/* You can have up to four HUB6's in the system, but I've only + * included two cards here for a total of twelve ports. + */ + { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) }, /* ttyS32 */ + { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) }, /* ttyS33 */ + { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) }, /* ttyS34 */ + { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) }, /* ttyS35 */ + { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) }, /* ttyS36 */ + { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) }, /* ttyS37 */ + { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) }, /* ttyS32 */ + { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) }, /* ttyS33 */ + { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) }, /* ttyS34 */ + { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) }, /* ttyS35 */ + { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS36 */ + { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS37 */ }; #define NR_PORTS (sizeof(rs_table)/sizeof(struct async_struct)) @@ -163,16 +206,28 @@ static inline unsigned int serial_in(struct async_struct *info, int offset) { + if (info->hub6) { + outb(info->hub6 - 1 + offset, info->port); + return inb(info->port+1); + } else return inb(info->port + offset); } static inline unsigned int serial_inp(struct async_struct *info, int offset) { + if (info->hub6) { + outb(info->hub6 - 1 + offset, info->port); + return inb_p(info->port+1); + } else return inb_p(info->port + offset); } static inline void serial_out(struct async_struct *info, int offset, int value) { + if (info->hub6) { + outb(info->hub6 - 1 + offset, info->port); + outb(value, info->port+1); + } else outb(value, info->port+offset); } @@ -179,10 +234,47 @@ static inline void serial_outp(struct async_struct *info, int offset, int value) { + if (info->hub6) { + outb(info->hub6 - 1 + offset, info->port); + outb_p(value, info->port+1); + } else outb_p(value, info->port+offset); } /* + * ------------------------------------------------------------ + * rs_stop() and rs_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + * ------------------------------------------------------------ + */ +static void rs_stop(struct tty_struct *tty) +{ + struct async_struct *info; + + info = rs_table + DEV_TO_SL(tty->line); + + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; +#ifdef ISR_HACK + serial_out(info, UART_IER, info->IER); +#endif +} + +static void rs_start(struct tty_struct *tty) +{ + struct async_struct *info; + + info = rs_table + DEV_TO_SL(tty->line); + + info->IER = (UART_IER_MSI | UART_IER_RLSI | + UART_IER_THRI | UART_IER_RDI); +#ifdef ISR_HACK + serial_out(info, UART_IER, info->IER); +#endif +} + +/* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines. All of the following @@ -268,6 +360,9 @@ &info->tty->flags)) rs_throttle(info->tty, TTY_THROTTLE_RQ_FULL); rs_sched_event(info, RS_EVENT_READ_PROCESS); +#ifdef SERIAL_DEBUG_INTR + printk("DR..."); +#endif } static inline void transmit_chars(struct async_struct *info, int *done_work) @@ -278,8 +373,13 @@ queue = &info->tty->write_q; head = queue->head; tail = queue->tail; - if (head==tail && !info->x_char) + if (head==tail && !info->x_char) { + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; +#ifdef ISR_HACK + serial_out(info, UART_IER, info->IER); +#endif return; + } count = info->xmit_fifo_size; if (info->x_char) { serial_outp(info, UART_TX, info->x_char); @@ -298,7 +398,7 @@ mark_bh(TTY_BH); } } -#ifdef SERIAL_INT_DEBUG +#ifdef SERIAL_DEBUG_INTR printk("THRE..."); #endif (*done_work)++; @@ -311,19 +411,34 @@ status = serial_in(info, UART_MSR); if ((status & UART_MSR_DDCD) && !C_LOCAL(info->tty)) { +#ifdef SERIAL_DEBUG_INTR + printk("DDCD..."); +#endif if (status & UART_MSR_DCD) rs_sched_event(info, RS_EVENT_OPEN_WAKEUP); - else + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_CALLOUT_NOHUP))) rs_sched_event(info, RS_EVENT_HANGUP); } if (C_RTSCTS(info->tty)) { if (info->tty->stopped) { if (status & UART_MSR_CTS) { +#ifdef SERIAL_DEBUG_INTR + printk("CTS tx start..."); +#endif info->tty->stopped = 0; + rs_start(info->tty); return 1; } - } else - info->tty->stopped = !(status & UART_MSR_CTS); + } else { + if (!(status & UART_MSR_CTS)) { +#ifdef SERIAL_DEBUG_INTR + printk("CTS tx stop..."); +#endif + info->tty->stopped = 1; + rs_stop(info->tty); + } + } } return 0; } @@ -353,7 +468,7 @@ { int status; struct async_struct * info; - int done, done_work, pass_number; + int done, done_work, pass_number, recheck_count; rs_irq_triggered = irq; rs_triggered |= 1 << irq; @@ -372,14 +487,24 @@ receive_chars(info, &status); done_work++; } + recheck_count = 0; recheck_write: if ((status & UART_LSR_THRE) && !info->tty->stopped) { transmit_chars(info, &done_work); } - if (check_modem_status(info)) + if (check_modem_status(info) && + (recheck_count++ <= 64)) goto recheck_write; +#ifdef SERIAL_DEBUG_INTR + if (recheck_count > 16) + printk("recheck_count = %d\n", recheck_count); +#endif } +#ifdef ISR_HACK + serial_outp(info, UART_IER, 0); + serial_out(info, UART_IER, info->IER); +#endif info = info->next_port; if (!info && !done) { @@ -389,7 +514,14 @@ break; /* Prevent infinite loops */ } } - if (IRQ_ports[irq]) { + if (info = IRQ_ports[irq]) { +#ifdef 0 + do { + serial_outp(info, UART_IER, 0); + serial_out(info, UART_IER, info->IER); + info = info->next_port; + } while (info); +#endif if (irq && !done_work) IRQ_timer[irq] = jiffies + 1500; else @@ -476,12 +608,9 @@ for (i = 0, mask = 1; mask <= IRQ_active; i++, mask <<= 1) { if ((mask & IRQ_active) && (IRQ_timer[i] <= jiffies)) { IRQ_active &= ~mask; - if (i) { - cli(); - rs_interrupt(i); - sti(); - } else - rs_interrupt(i); + cli(); + rs_interrupt(i); + sti(); } if (mask & IRQ_active) { if (!timeout || (IRQ_timer[i] < timeout)) @@ -553,7 +682,7 @@ info = IRQ_ports[irq]; if (!info) { - IRQ_timeout[irq] = 0; + IRQ_timeout[irq] = 6000; return; } while (info) { @@ -563,11 +692,15 @@ } if (!irq) timeout = timeout / 2; - IRQ_timeout[irq] = timeout; + IRQ_timeout[irq] = timeout ? timeout : 1; } static inline void unlink_port(struct async_struct *info) { +#ifdef SERIAL_DEBUG_OPEN + printk("unlinking serial port %d from irq %d....", info->line, + info->irq); +#endif if (info->next_port) info->next_port->prev_port = info->prev_port; if (info->prev_port) @@ -579,6 +712,10 @@ static inline void link_port(struct async_struct *info) { +#ifdef SERIAL_DEBUG_OPEN + printk("linking serial port %d into irq %d...", info->line, + info->irq); +#endif info->prev_port = 0; info->next_port = IRQ_ports[info->irq]; if (info->next_port) @@ -631,7 +768,14 @@ /* * Finally, enable interrupts */ - serial_outp(info, UART_IER, 0x0f); /* enable all intrs */ +#ifdef ISR_HACK + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + serial_outp(info, UART_IER, info->IER); /* enable interrupts */ +#else + info->IER = (UART_IER_MSI | UART_IER_RLSI | + UART_IER_THRI | UART_IER_RDI); + serial_outp(info, UART_IER, info->IER); /* enable all intrs */ +#endif if (info->flags & ASYNC_FOURPORT) { /* Enable interrupts on the AST Fourport board */ ICP = (info->port & 0xFE0) | 0x01F; @@ -654,10 +798,11 @@ * Set up parity check flag */ if (info->tty && info->tty->termios && I_INPCK(info->tty)) - info->read_status_mask = UART_LSR_BI | UART_LSR_FE | - UART_LSR_PE; + info->read_status_mask = (UART_LSR_OE | UART_LSR_BI | + UART_LSR_FE | UART_LSR_PE); else - info->read_status_mask = UART_LSR_BI | UART_LSR_FE; + info->read_status_mask = (UART_LSR_OE | UART_LSR_BI | + UART_LSR_FE); restore_flags(flags); } @@ -670,7 +815,12 @@ unsigned long flags; save_flags(flags); cli(); + info->IER = 0; serial_outp(info, UART_IER, 0x00); /* disable all intrs */ + if (info->flags & ASYNC_FOURPORT) { + /* reset interrupts on the AST Fourport board */ + (void) inb((info->port & 0xFE0) | 0x01F); + } if (info->tty && !(info->tty->termios->c_cflag & HUPCL)) serial_outp(info, UART_MCR, UART_MCR_DTR); else @@ -726,13 +876,16 @@ quot = 0; info->timeout = 0; } + cli(); mcr = serial_in(info, UART_MCR); if (quot) serial_out(info, UART_MCR, mcr | UART_MCR_DTR); else { serial_out(info, UART_MCR, mcr & ~UART_MCR_DTR); + sti(); return; } + sti(); /* byte size and parity */ cval = cflag & (CSIZE | CSTOPB); cval >>= 4; @@ -761,7 +914,7 @@ * * Note: this subroutine must be called with the interrupts *off* */ -static void restart_port(struct async_struct *info) +static inline void restart_port(struct async_struct *info) { struct tty_queue * queue; int head, tail, count; @@ -768,6 +921,7 @@ if (!info) return; + if (serial_inp(info, UART_LSR) & UART_LSR_THRE) { if (info->x_char) { serial_outp(info, UART_TX, info->x_char); @@ -799,8 +953,15 @@ if (!tty || tty->stopped) return; info = rs_table + DEV_TO_SL(tty->line); + if (!info) + return; cli(); restart_port(info); + info->IER = (UART_IER_MSI | UART_IER_RLSI | + UART_IER_THRI | UART_IER_RDI); +#ifdef ISR_HACK + serial_out(info, UART_IER, info->IER); +#endif sti(); } @@ -809,7 +970,7 @@ * rs_throttle() * * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled (and that the throttled + * incoming characters should be throttled (and that the throttle * should be released). * ------------------------------------------------------------ */ @@ -817,8 +978,10 @@ { struct async_struct *info; unsigned char mcr; + unsigned long flags; -#if 0 + save_flags(flags); cli(); +#if SERIAL_DEBUG_THROTTLE printk("throttle tty%d: %d (%d, %d)....\n", DEV_TO_SL(tty->line), status, LEFT(&tty->read_q), LEFT(&tty->secondary)); #endif @@ -836,12 +999,10 @@ case TTY_THROTTLE_RQ_AVAIL: info = rs_table + DEV_TO_SL(tty->line); if (tty->termios->c_iflag & IXOFF) { - cli(); if (info->x_char) info->x_char = 0; else info->x_char = START_CHAR(tty); - sti(); } else { mcr = serial_in(info, UART_MCR); mcr |= UART_MCR_RTS; @@ -849,6 +1010,7 @@ } break; } + restore_flags(flags); } /* @@ -873,6 +1035,7 @@ tmp.baud_base = info->baud_base; tmp.close_delay = info->close_delay; tmp.custom_divisor = info->custom_divisor; + tmp.hub6 = info->hub6; memcpy_tofs(retinfo,&tmp,sizeof(*retinfo)); return 0; } @@ -892,7 +1055,7 @@ old_info = *info; change_irq = new_serial.irq != info->irq; - change_port = new_serial.port != info->port; + change_port = (new_serial.port != info->port) || (new_serial.hub6 != info->hub6); if (!suser()) { if (change_irq || change_port || @@ -899,11 +1062,11 @@ (new_serial.baud_base != info->baud_base) || (new_serial.type != info->type) || (new_serial.close_delay != info->close_delay) || - ((new_serial.flags & ~ASYNC_FLAGS) != - (info->flags & ~ASYNC_FLAGS))) + ((new_serial.flags & ~ASYNC_USR_MASK) != + (info->flags & ~ASYNC_USR_MASK))) return -EPERM; - info->flags = ((info->flags & ~ASYNC_SPD_MASK) | - (new_serial.flags & ASYNC_SPD_MASK)); + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); info->custom_divisor = new_serial.custom_divisor; new_serial.port = 0; /* Prevent initialization below */ goto check_and_exit; @@ -969,6 +1132,7 @@ } info->irq = new_serial.irq; info->port = new_serial.port; + info->hub6 = new_serial.hub6; } check_and_exit: @@ -993,8 +1157,10 @@ unsigned char control, status; unsigned int result; + cli(); control = serial_in(info, UART_MCR); status = serial_in(info, UART_MSR); + sti(); result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) @@ -1011,7 +1177,9 @@ unsigned char control; unsigned int arg = get_fs_long((unsigned long *) value); + cli(); control = serial_in(info, UART_MCR); + sti(); switch (cmd) { case TIOCMBIS: @@ -1034,7 +1202,9 @@ default: return -EINVAL; } + cli(); serial_out(info, UART_MCR, control); + sti(); return 0; } @@ -1052,7 +1222,7 @@ if (info->flags & ASYNC_INITIALIZED) { shutdown(info); unlink_port(info); - if (info->irq) + if (info->irq && !IRQ_ports[info->irq]) free_irq(info->irq); } @@ -1087,9 +1257,11 @@ return; current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + duration; + cli(); serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC); schedule(); serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + sti(); } /* @@ -1236,10 +1408,11 @@ wake_up_interruptible(&info->open_wait); if (I_INPCK(tty)) - info->read_status_mask = UART_LSR_BI | UART_LSR_FE | - UART_LSR_PE; + info->read_status_mask = (UART_LSR_OE | UART_LSR_BI | + UART_LSR_FE | UART_LSR_PE); else - info->read_status_mask = UART_LSR_BI | UART_LSR_FE; + info->read_status_mask = (UART_LSR_OE | UART_LSR_BI | + UART_LSR_FE); } /* @@ -1267,6 +1440,7 @@ if (--info->count > 0) return; tty->stopped = 0; /* Force flush to succeed */ + rs_start(tty); wait_until_sent(tty); clear_bit(line, rs_event); info->event = 0; @@ -1305,7 +1479,10 @@ struct async_struct *info) { struct wait_queue wait = { current, NULL }; - int retval; + int retval; + int do_clocal = C_LOCAL(tty); + struct termios orig_termios; + int tty_line = tty->line; /* * If this is a callout device, then just make sure the normal @@ -1314,6 +1491,14 @@ if (MAJOR(filp->f_rdev) == 5) { if (info->flags & ASYNC_NORMAL_ACTIVE) return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; info->flags |= ASYNC_CALLOUT_ACTIVE; return 0; } @@ -1344,22 +1529,27 @@ #endif info->count--; info->blocked_open++; + memset(&orig_termios, 0, sizeof(orig_termios)); + if (tty_termios[tty_line]) + orig_termios = *tty_termios[tty_line]; while (1) { + cli(); if (!(info->flags & ASYNC_CALLOUT_ACTIVE)) serial_out(info, UART_MCR, serial_inp(info, UART_MCR) | UART_MCR_DTR); + sti(); current->state = TASK_INTERRUPTIBLE; - if (tty_hung_up_p(filp)) { - if (info->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTNOINTR; + if (tty_hung_up_p(filp) && (info->flags & ASYNC_HUP_NOTIFY)) { + retval = -EAGAIN; break; } if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && - (C_LOCAL(tty) || - (serial_in(info, UART_MSR) & UART_MSR_DCD))) + (do_clocal || (serial_in(info, UART_MSR) & + UART_MSR_DCD))) { + if (tty_hung_up_p(filp)) + retval = -ERESTARTNOINTR; break; + } if (current->signal & ~current->blocked) { retval = -ERESTARTSYS; break; @@ -1381,6 +1571,9 @@ if (retval) return retval; info->flags |= ASYNC_NORMAL_ACTIVE; + if ((info->flags & ASYNC_TERMIOS_RESTORE) && + tty_termios[tty_line]) + *tty_termios[tty_line] = orig_termios; return 0; } @@ -1411,6 +1604,8 @@ tty->ioctl = rs_ioctl; tty->throttle = rs_throttle; tty->set_termios = rs_set_termios; + tty->stop = rs_stop; + tty->start = rs_start; if (!(info->flags & ASYNC_INITIALIZED)) { if (!info->port || !info->type) { @@ -1443,6 +1638,9 @@ retval = block_til_ready(tty, filp, info); if (retval) return retval; + + info->session = current->session; + info->pgrp = current->pgrp; return 0; } @@ -1462,7 +1660,7 @@ */ static void show_serial_version(void) { - printk("Serial driver version 3.95 with"); + printk("Serial driver version 3.96 with"); #ifdef CONFIG_AST_FOURPORT printk(" AST_FOURPORT"); #define SERIAL_OPT @@ -1471,6 +1669,10 @@ printk(" ACCENT_ASYNC"); #define SERIAL_OPT #endif +#ifdef CONFIG_HUB6 + printk(" HUB-6"); +#define SERIAL_OPT +#endif #ifdef CONFIG_AUTO_IRQ printk (" AUTO_IRQ"); #define SERIAL_OPT @@ -1498,6 +1700,7 @@ * Enable interrupts and see who answers */ rs_irq_triggered = 0; + cli(); save_IER = serial_inp(info, UART_IER); save_MCR = serial_inp(info, UART_MCR); if (info->flags & ASYNC_FOURPORT) { @@ -1512,6 +1715,7 @@ UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); serial_outp(info, UART_IER, 0x0f); /* enable all intrs */ } + sti(); /* * Next, clear the interrupt registers. */ @@ -1528,10 +1732,12 @@ /* * Now check to see if we got any business, and clean up. */ + cli(); serial_outp(info, UART_IER, save_IER); serial_outp(info, UART_MCR, save_MCR); if (info->flags & ASYNC_FOURPORT) outb_p(save_ICP, ICP); + sti(); return(rs_irq_triggered); } @@ -1582,11 +1788,14 @@ { unsigned char status1, status2, scratch, scratch2; unsigned port = info->port; + unsigned long flags; info->type = PORT_UNKNOWN; if (!port) return; + + save_flags(flags); cli(); /* * Do a simple existence test first; if we fail this, there's @@ -1596,8 +1805,10 @@ serial_outp(info, UART_IER, 0); scratch2 = serial_inp(info, UART_IER); serial_outp(info, UART_IER, scratch); - if (scratch2) + if (scratch2) { + restore_flags(flags); return; /* We failed; there's nothing here */ + } /* * Check to see if a UART is really there. Certain broken @@ -1616,8 +1827,10 @@ status1 = serial_inp(info, UART_MSR) & 0xF0; serial_outp(info, UART_MCR, scratch); serial_outp(info, UART_MSR, scratch2); - if (status1 != 0x90) + if (status1 != 0x90) { + restore_flags(flags); return; + } } /* @@ -1627,8 +1840,8 @@ if (info->flags & ASYNC_AUTO_IRQ) info->irq = do_auto_irq(info); - outb_p(UART_FCR_ENABLE_FIFO, UART_FCR + port); - scratch = inb(UART_IIR + port) >> 6; + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(info, UART_IIR) >> 6; info->xmit_fifo_size = 1; switch (scratch) { case 0: @@ -1646,16 +1859,18 @@ break; } if (info->type == PORT_16450) { - scratch = inb(UART_SCR + port); - outb_p(0xa5, UART_SCR + port); - status1 = inb(UART_SCR + port); - outb_p(0x5a, UART_SCR + port); - status2 = inb(UART_SCR + port); - outb_p(scratch, UART_SCR + port); + scratch = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, 0xa5); + status1 = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, 0x5a); + status2 = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, scratch); + if ((status1 != 0xa5) || (status2 != 0x5a)) info->type = PORT_8250; } shutdown(info); + restore_flags(flags); } /* diff -u --recursive --new-file pl11/linux/kernel/chr_drv/tpqic02.c linux/kernel/chr_drv/tpqic02.c --- pl11/linux/kernel/chr_drv/tpqic02.c Sun Jul 4 23:27:54 1993 +++ linux/kernel/chr_drv/tpqic02.c Wed Aug 11 19:16:52 1993 @@ -1,9 +1,9 @@ /* $Id: tpqic02.c,v 0.2.1.21 1993/06/18 19:04:33 root Exp root $ * - * Driver for tape drive support for Linux-i386 0.99.8. + * Driver for tape drive support for Linux-i386 0.99.12. * - * Copyright (c) 1992 by H. H. Bergman. All rights reserved. - * Current e-mail address: s0356514@let.rug.nl + * Copyright (c) 1993 by H. H. Bergman. All rights reserved. + * Current e-mail address: csg279@wing.rug.nl * [If you are unable to reach me directly, try the TAPE mailing list * channel on linux-activists@niksula.hut.fi using "X-Mn-Key: TAPE" as * the first line in your message.] diff -u --recursive --new-file pl11/linux/kernel/chr_drv/tty_io.c linux/kernel/chr_drv/tty_io.c --- pl11/linux/kernel/chr_drv/tty_io.c Sat Jul 3 02:48:46 1993 +++ linux/kernel/chr_drv/tty_io.c Mon Aug 9 18:02:32 1993 @@ -58,6 +58,7 @@ struct tty_struct *tty_table[MAX_TTYS]; struct termios *tty_termios[MAX_TTYS]; /* We need to keep the termios state */ /* around, even when a tty is closed */ +struct termios *termios_locked[MAX_TTYS]; /* Bitfield of locked termios flags*/ struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */ int tty_check_write[MAX_TTYS/32]; /* bitfield for the bh handler */ @@ -125,9 +126,11 @@ /* * This routine copies out a maximum of buflen characters from the - * read_q; it is a convenience for line disciplins so they can grab a + * read_q; it is a convenience for line disciplines so they can grab a * large block of data without calling get_tty_char directly. It - * returns the number of characters actually read. + * returns the number of characters actually read. Return terminates + * if an error character is read from the queue and the return value + * is negated. */ int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp, int buflen) { @@ -135,12 +138,14 @@ unsigned char *p = bufp; unsigned long flags; int head, tail; + int ok = 1; save_flags(flags); cli(); tail = tty->read_q.tail; head = tty->read_q.head; - while ((result < buflen) && (tail!=head)) { + while ((result < buflen) && (tail!=head) && ok) { + ok = !clear_bit (tail, &tty->readq_flags); *p++ = tty->read_q.buf[tail++]; tail &= TTY_BUF_SIZE-1; result++; @@ -147,7 +152,7 @@ } tty->read_q.tail = tail; restore_flags(flags); - return result; + return (ok) ? result : -result; } @@ -616,6 +621,8 @@ tty->status_changed = 1; tty->ctrl_status |= TIOCPKT_STOP; tty->stopped=1; + if (tty->stop) + (tty->stop)(tty); if (IS_A_CONSOLE(tty->line)) { set_vc_kbd_flag(kbd_table + fg_console, VC_SCROLLOCK); set_leds(); @@ -627,6 +634,8 @@ tty->status_changed = 1; tty->ctrl_status |= TIOCPKT_START; tty->stopped=0; + if (tty->start) + (tty->start)(tty); if (IS_A_CONSOLE(tty->line)) { clr_vc_kbd_flag(kbd_table + fg_console, VC_SCROLLOCK); set_leds(); @@ -787,20 +796,22 @@ if (tty->throttle && (LEFT(&tty->secondary) >= SQ_THRESHOLD_HW) && clear_bit(TTY_SQ_THROTTLED, &tty->flags)) tty->throttle(tty, TTY_THROTTLE_SQ_AVAIL); - if (b-buf >= minimum || !current->timeout) - break; - if (current->signal & ~current->blocked) - break; if (tty->link) { if (IS_A_PTY_MASTER(tty->line)) { if ((tty->flags & (1 << TTY_SLAVE_OPENED)) - && tty->link->count <= 1) - break; - } else { - if (!tty->link->count) + && tty->link->count <= 1) { + file->f_flags &= ~O_NONBLOCK; break; + } + } else if (!tty->link->count) { + file->f_flags &= ~O_NONBLOCK; + break; } } + if (b-buf >= minimum || !current->timeout) + break; + if (current->signal & ~current->blocked) + break; TTY_READ_FLUSH(tty); if (tty->link) TTY_WRITE_FLUSH(tty->link); @@ -1042,7 +1053,7 @@ static int init_dev(int dev) { struct tty_struct *tty, *o_tty; - struct termios *tp, *o_tp; + struct termios *tp, *o_tp, *ltp, *o_ltp; int retval; int o_dev; @@ -1049,6 +1060,7 @@ o_dev = PTY_OTHER(dev); tty = o_tty = NULL; tp = o_tp = NULL; + ltp = o_ltp = NULL; repeat: retval = -EAGAIN; if (IS_A_PTY_MASTER(dev) && tty_table[dev] && tty_table[dev]->count) @@ -1055,22 +1067,31 @@ goto end_init; retval = -ENOMEM; if (!tty_table[dev] && !tty) { - tty = (struct tty_struct *) get_free_page(GFP_KERNEL); - if (!tty) + if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL))) goto end_init; initialize_tty_struct(dev, tty); goto repeat; } if (!tty_termios[dev] && !tp) { - tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); + tp = (struct termios *) kmalloc(sizeof(struct termios), + GFP_KERNEL); if (!tp) goto end_init; initialize_termios(dev, tp); goto repeat; } + if (!termios_locked[dev] && !ltp) { + ltp = (struct termios *) kmalloc(sizeof(struct termios), + GFP_KERNEL); + if (!ltp) + goto end_init; + memset(ltp, 0, sizeof(struct termios)); + goto repeat; + } if (IS_A_PTY(dev)) { if (!tty_table[o_dev] && !o_tty) { - o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL); + o_tty = (struct tty_struct *) + get_free_page(GFP_KERNEL); if (!o_tty) goto end_init; initialize_tty_struct(o_dev, o_tty); @@ -1077,12 +1098,22 @@ goto repeat; } if (!tty_termios[o_dev] && !o_tp) { - o_tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); + o_tp = (struct termios *) + kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_tp) goto end_init; initialize_termios(o_dev, o_tp); goto repeat; } + if (!termios_locked[o_dev] && !o_ltp) { + o_ltp = (struct termios *) + kmalloc(sizeof(struct termios), GFP_KERNEL); + if (!o_ltp) + goto end_init; + memset(o_ltp, 0, sizeof(struct termios)); + goto repeat; + } + } /* Now we have allocated all the structures: update all the pointers.. */ if (!tty_termios[dev]) { @@ -1094,11 +1125,19 @@ tty_table[dev] = tty; tty = NULL; } + if (!termios_locked[dev]) { + termios_locked[dev] = ltp; + ltp = NULL; + } if (IS_A_PTY(dev)) { if (!tty_termios[o_dev]) { tty_termios[o_dev] = o_tp; o_tp = NULL; } + if (!termios_locked[o_dev]) { + termios_locked[o_dev] = o_ltp; + o_ltp = NULL; + } if (!tty_table[o_dev]) { o_tty->termios = tty_termios[o_dev]; tty_table[o_dev] = o_tty; @@ -1120,6 +1159,10 @@ kfree_s(tp, sizeof(struct termios)); if (o_tp) kfree_s(o_tp, sizeof(struct termios)); + if (ltp) + kfree_s(ltp, sizeof(struct termios)); + if (o_ltp) + kfree_s(o_ltp, sizeof(struct termios)); return retval; } @@ -1405,7 +1448,7 @@ ((session > 0) && ((*p)->session == session))) send_sig(SIGKILL, *p, 1); else { - for (i=0; i < NR_FILE; i++) { + for (i=0; i < NR_OPEN; i++) { filp = (*p)->filp[i]; if (filp && (filp->f_op == &tty_fops) && (MINOR(filp->f_rdev) == line)) { @@ -1581,8 +1624,8 @@ { int i; - if (sizeof(struct tty_struct) > 4096) - panic("size of tty structure > 4096!"); + if (sizeof(struct tty_struct) > PAGE_SIZE) + panic("size of tty structure > PAGE_SIZE!"); if (register_chrdev(4,"tty",&tty_fops)) panic("unable to get major 4 for tty device"); if (register_chrdev(5,"tty",&tty_fops)) diff -u --recursive --new-file pl11/linux/kernel/chr_drv/tty_ioctl.c linux/kernel/chr_drv/tty_ioctl.c --- pl11/linux/kernel/chr_drv/tty_ioctl.c Sun Jul 4 12:31:37 1993 +++ linux/kernel/chr_drv/tty_ioctl.c Thu Jul 29 12:15:25 1993 @@ -37,6 +37,8 @@ extern int paste_selection(struct tty_struct *tty); #endif /* CONFIG_SELECTION */ +static int tty_set_ldisc(struct tty_struct *tty, int ldisc); + static void flush(struct tty_queue * queue) { if (queue) { @@ -127,6 +129,29 @@ return(0); } +static void unset_locked_termios(struct termios *termios, + struct termios *old, + struct termios *locked) +{ + int i; + +#define NOSET_MASK(x,y,z) (x = ((x) & ~(z)) | ((y) & (z))) + + if (!locked) { + printk("Warning?!? termios_locked is NULL.\n"); + return; + } + + NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag); + NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag); + NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag); + NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag); + termios->c_line = locked->c_line ? old->c_line : termios->c_line; + for (i=0; i < NCCS; i++) + termios->c_cc[i] = locked->c_cc[i] ? + old->c_cc[i] : termios->c_cc[i]; +} + static int get_termios(struct tty_struct * tty, struct termios * termios) { int i; @@ -173,6 +198,15 @@ copy characters back and forth. -RAB */ if (IS_A_PTY_MASTER(channel)) tty->termios->c_lflag &= ~ECHO; + unset_locked_termios(tty->termios, &old_termios, + termios_locked[tty->line]); + +#if 0 + retval = tty_set_ldisc(tty, tty->termios->c_line); + if (retval) + return retval; +#endif + if (tty->set_termios) (*tty->set_termios)(tty, &old_termios); @@ -234,10 +268,18 @@ *(unsigned short *)&tty->termios->c_oflag = tmp_termio.c_oflag; *(unsigned short *)&tty->termios->c_cflag = tmp_termio.c_cflag; *(unsigned short *)&tty->termios->c_lflag = tmp_termio.c_lflag; - tty->termios->c_line = tmp_termio.c_line; for(i=0 ; i < NCC ; i++) tty->termios->c_cc[i] = tmp_termio.c_cc[i]; + unset_locked_termios(tty->termios, &old_termios, + termios_locked[tty->line]); + +#if 0 + retval = tty_set_ldisc(tty, tmp_termio.c_line); + if (retval) + return retval; +#endif + if (tty->set_termios) (*tty->set_termios)(tty, &old_termios); @@ -244,6 +286,34 @@ return 0; } +static int get_lcktrmios(struct tty_struct * tty, struct termios * termios, + int channel) +{ + int i; + + i = verify_area(VERIFY_WRITE, termios, sizeof (*termios)); + if (i) + return i; + for (i=0 ; i< (sizeof (*termios)) ; i++) + put_fs_byte( ((char *)termios_locked[channel])[i], + i+(char *)termios); + return 0; +} + +static int set_lcktrmios(struct tty_struct * tty, struct termios * termios, + int channel) +{ + int i; + + if (!suser()) + return -EPERM; + for (i=0 ; i< (sizeof (*termios)) ; i++) + ((char *)termios_locked[channel])[i] = + get_fs_byte(i+(char *)termios); + + return 0; +} + static int set_window_size(struct tty_struct * tty, struct winsize * ws) { int i,changed; @@ -299,6 +369,7 @@ /* Now set up the new line discipline. */ tty->disc = ldisc; + tty->termios->c_line = ldisc; if (ldiscs[tty->disc].open) return(ldiscs[tty->disc].open(tty)); else @@ -359,10 +430,14 @@ switch (arg) { case TCOOFF: tty->stopped = 1; + if (tty->stop) + (tty->stop)(tty); TTY_WRITE_FLUSH(tty); return 0; case TCOON: tty->stopped = 0; + if (tty->start) + (tty->start)(tty); TTY_WRITE_FLUSH(tty); return 0; case TCIOFF: @@ -507,7 +582,15 @@ case TIOCSETD: arg = get_fs_long((unsigned long *) arg); return tty_set_ldisc(tty, arg); - case TIOCPKT: + case TIOCGLCKTRMIOS: + arg = get_fs_long((unsigned long *) arg); + return get_lcktrmios(tty, (struct termios *) arg, + termios_dev); + case TIOCSLCKTRMIOS: + arg = get_fs_long((unsigned long *) arg); + return set_lcktrmios(tty, (struct termios *) arg, + termios_dev); + case TIOCPKT: { int on; if (!IS_A_PTY_MASTER(dev)) diff -u --recursive --new-file pl11/linux/kernel/chr_drv/vt.c linux/kernel/chr_drv/vt.c --- pl11/linux/kernel/chr_drv/vt.c Sat Jul 3 01:35:20 1993 +++ linux/kernel/chr_drv/vt.c Sat Jul 31 20:32:36 1993 @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -255,6 +256,60 @@ if (KVAL(v) > max_vals[KTYP(v)]) return -EINVAL; key_map[s][i] = v; + return 0; + } + + case KDGKBSENT: + { + const struct kbsentry *a = (struct kbsentry *)arg; + u_char i; + char *p; + u_char *q; + + verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbsentry)); + if ((i = get_fs_byte(&a->kb_func)) >= NR_FUNC) + return -EINVAL; + q = a->kb_string; + for (p = func_table[i]; *p; p++) + put_fs_byte(*p, q++); + put_fs_byte(0, q); + return 0; + } + + case KDSKBSENT: + { + struct kbsentry * const a = (struct kbsentry *)arg; + int delta; + char *first_free; + u_char i; + int k; + u_char *p; + char *q; + + verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry)); + if ((i = get_fs_byte(&a->kb_func)) >= NR_FUNC) + return -EINVAL; + delta = -strlen(func_table[i]); + for (p = a->kb_string; get_fs_byte(p); p++) + delta++; + first_free = func_table[NR_FUNC - 1] + + strlen(func_table[NR_FUNC - 1]) + 1; + if ( + delta > 0 && + first_free + delta > func_buf + FUNC_BUFSIZE + ) + return -EINVAL; + if (i < NR_FUNC - 1) { + memmove( + func_table[i + 1] + delta, + func_table[i + 1], + first_free - func_table[i + 1]); + for (k = i + 1; k < NR_FUNC; k++) + func_table[k] += delta; + } + for (p = a->kb_string, q = func_table[i]; ; p++, q++) + if (!(*q = get_fs_byte(p))) + break; return 0; } diff -u --recursive --new-file pl11/linux/kernel/exit.c linux/kernel/exit.c --- pl11/linux/kernel/exit.c Wed Jul 7 10:39:38 1993 +++ linux/kernel/exit.c Mon Aug 2 18:16:26 1993 @@ -62,8 +62,7 @@ if ((sig >= SIGSTOP) && (sig <= SIGTTOU)) p->signal &= ~(1<<(SIGCONT-1)); /* Actually generate the signal */ - if (!generate(sig,p)) - return 0; + generate(sig,p); return 0; } @@ -196,17 +195,17 @@ */ int session_of_pgrp(int pgrp) { - struct task_struct **p; + struct task_struct *p; int fallback; fallback = -1; - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { - if (!*p || (*p)->session <= 0) + for_each_task(p) { + if (p->session <= 0) continue; - if ((*p)->pgrp == pgrp) - return (*p)->session; - if ((*p)->pid == pgrp) - fallback = (*p)->session; + if (p->pgrp == pgrp) + return p->session; + if (p->pid == pgrp) + fallback = p->session; } return fallback; } @@ -217,19 +216,20 @@ */ int kill_pg(int pgrp, int sig, int priv) { - struct task_struct **p; + struct task_struct *p; int err,retval = -ESRCH; int found = 0; if (sig<0 || sig>32 || pgrp<=0) return -EINVAL; - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) - if (*p && (*p)->pgrp == pgrp) { - if ((err = send_sig(sig,*p,priv)) != 0) + for_each_task(p) { + if (p->pgrp == pgrp) { + if ((err = send_sig(sig,p,priv)) != 0) retval = err; else found++; } + } return(found ? 0 : retval); } @@ -240,31 +240,33 @@ */ int kill_sl(int sess, int sig, int priv) { - struct task_struct **p; + struct task_struct *p; int err,retval = -ESRCH; int found = 0; if (sig<0 || sig>32 || sess<=0) return -EINVAL; - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) - if (*p && (*p)->session == sess && (*p)->leader) { - if ((err = send_sig(sig,*p,priv)) != 0) + for_each_task(p) { + if (p->session == sess && p->leader) { + if ((err = send_sig(sig,p,priv)) != 0) retval = err; else found++; } + } return(found ? 0 : retval); } int kill_proc(int pid, int sig, int priv) { - struct task_struct **p; + struct task_struct *p; if (sig<0 || sig>32) return -EINVAL; - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) - if (*p && (*p)->pid == pid) - return send_sig(sig,*p,priv); + for_each_task(p) { + if (p && p->pid == pid) + return send_sig(sig,p,priv); + } return(-ESRCH); } @@ -274,18 +276,19 @@ */ extern "C" int sys_kill(int pid,int sig) { - struct task_struct **p = NR_TASKS + task; int err, retval = 0, count = 0; if (!pid) return(kill_pg(current->pgrp,sig,0)); if (pid == -1) { - while (--p > &FIRST_TASK) - if (*p && (*p)->pid > 1 && *p != current) { + struct task_struct * p; + for_each_task(p) { + if (p->pid > 1 && p != current) { ++count; - if ((err = send_sig(sig,*p,0)) != -EPERM) + if ((err = send_sig(sig,p,0)) != -EPERM) retval = err; } + } return(count ? retval : -ESRCH); } if (pid < 0) @@ -304,16 +307,15 @@ */ int is_orphaned_pgrp(int pgrp) { - struct task_struct **p; + struct task_struct *p; - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { - if (!(*p) || - ((*p)->pgrp != pgrp) || - ((*p)->state == TASK_ZOMBIE) || - ((*p)->p_pptr->pid == 1)) + for_each_task(p) { + if ((p->pgrp != pgrp) || + (p->state == TASK_ZOMBIE) || + (p->p_pptr->pid == 1)) continue; - if (((*p)->p_pptr->pgrp != pgrp) && - ((*p)->p_pptr->session == (*p)->session)) + if ((p->p_pptr->pgrp != pgrp) && + (p->p_pptr->session == p->session)) return 0; } return(1); /* (sighing) "Often!" */ @@ -321,12 +323,12 @@ static int has_stopped_jobs(int pgrp) { - struct task_struct ** p; + struct task_struct * p; - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { - if (!*p || (*p)->pgrp != pgrp) + for_each_task(p) { + if (p->pgrp != pgrp) continue; - if ((*p)->state == TASK_STOPPED) + if (p->state == TASK_STOPPED) return(1); } return(0); @@ -334,14 +336,15 @@ static void forget_original_parent(struct task_struct * father) { - struct task_struct ** p; + struct task_struct * p; - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) - if (*p && (*p)->p_opptr == father) + for_each_task(p) { + if (p->p_opptr == father) if (task[1]) - (*p)->p_opptr = task[1]; + p->p_opptr = task[1]; else - (*p)->p_opptr = task[0]; + p->p_opptr = task[0]; + } } volatile void do_exit(long code) @@ -380,6 +383,17 @@ } } + if (current->ldt) { + free_page((unsigned long) current->ldt); + current->ldt = NULL; + for (i=1 ; istate = TASK_ZOMBIE; current->exit_code = code; current->rss = 0; @@ -438,7 +452,7 @@ } } if (current->leader) { - struct task_struct **p; + struct task_struct *p; struct tty_struct *tty; if (current->tty >= 0) { @@ -450,9 +464,10 @@ tty->session = 0; } } - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) - if (*p && (*p)->session == current->session) - (*p)->tty = -1; + for_each_task(p) { + if (p->session == current->session) + p->tty = -1; + } } if (last_task_used_math == current) last_task_used_math = NULL; diff -u --recursive --new-file pl11/linux/kernel/fork.c linux/kernel/fork.c --- pl11/linux/kernel/fork.c Wed Jul 14 14:20:33 1993 +++ linux/kernel/fork.c Mon Aug 9 17:41:24 1993 @@ -23,7 +23,6 @@ #include #include -extern "C" void lcall7(void); extern "C" void ret_from_sys_call(void) __asm__("ret_from_sys_call"); #define MAX_TASKS_PER_USER (NR_TASKS/2) @@ -122,8 +121,7 @@ struct file *f; unsigned long clone_flags = COPYVM | SIGCHLD; - p = (struct task_struct *) __get_free_page(GFP_KERNEL); - if (!p) + if(!(p = (struct task_struct*)__get_free_page(GFP_KERNEL))) goto bad_fork; nr = find_empty_process(); if (nr < 0) @@ -150,14 +148,13 @@ /* * set up new TSS and kernel stack */ - p->kernel_stack_page = __get_free_page(GFP_KERNEL); - if (!p->kernel_stack_page) + if (!(p->kernel_stack_page = __get_free_page(GFP_KERNEL))) goto bad_fork_cleanup; p->tss.es = KERNEL_DS; p->tss.cs = KERNEL_CS; p->tss.ss = KERNEL_DS; p->tss.ds = KERNEL_DS; - p->tss.fs = KERNEL_DS; + p->tss.fs = USER_DS; p->tss.gs = KERNEL_DS; p->tss.ss0 = KERNEL_DS; p->tss.esp0 = p->kernel_stack_page + PAGE_SIZE; @@ -178,8 +175,11 @@ } p->exit_signal = clone_flags & CSIGNAL; p->tss.ldt = _LDT(nr); + if (p->ldt) { + if (p->ldt = (struct desc_struct*) __get_free_page(GFP_KERNEL)) + memcpy(p->ldt, current->ldt, PAGE_SIZE); + } p->tss.bitmap = offsetof(struct tss_struct,io_bitmap); - set_call_gate(p->ldt+0,lcall7); for (i = 0; i < IO_BITMAP_SIZE+1 ; i++) /* IO bitmap is actually SIZE+1 */ p->tss.io_bitmap[i] = ~0; if (last_task_used_math == current) @@ -204,7 +204,11 @@ current->executable->i_count++; dup_mmap(p); set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss)); - set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt)); + if (p->ldt) + set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, 512); + else + set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&default_ldt, 1); + p->counter = current->counter >> 1; p->state = TASK_RUNNING; /* do this last, just in case */ return p->pid; diff -u --recursive --new-file pl11/linux/kernel/ldt.c linux/kernel/ldt.c --- pl11/linux/kernel/ldt.c +++ linux/kernel/ldt.c Mon Aug 9 18:02:32 1993 @@ -0,0 +1,96 @@ +/* + * linux/kernel/ldt.c + * + * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include +#include + +static int read_ldt(void * ptr, unsigned long bytecount) +{ + int error; + void * address = current->ldt; + unsigned long size; + + if (!ptr) + return -EINVAL; + size = PAGE_SIZE; + if (!address) { + address = &default_ldt; + size = sizeof(default_ldt); + } + if (size > bytecount) + size = bytecount; + error = verify_area(VERIFY_WRITE, ptr, size); + if (error) + return error; + memcpy_tofs(ptr, address, size); + return size; +} + +static int write_ldt(void * ptr, unsigned long bytecount) +{ + struct modify_ldt_ldt_s ldt_info; + unsigned long *lp; + unsigned long base, limit; + int error, i; + + if (bytecount != sizeof(ldt_info)) + return -EINVAL; + error = verify_area(VERIFY_READ, ptr, sizeof(ldt_info)); + if (error) + return error; + + memcpy_fromfs(&ldt_info, ptr, sizeof(ldt_info)); + + if (ldt_info.contents == 3 || ldt_info.entry_number >= 512) + return -EINVAL; + + limit = ldt_info.limit; + base = ldt_info.base_addr; + if (ldt_info.limit_in_pages) + limit *= PAGE_SIZE; + + limit += base; + if (limit < base || limit >= 0xC0000000) + return -EINVAL; + + if (!current->ldt) { + for (i=1 ; ildt = (struct desc_struct*) get_free_page(GFP_KERNEL))) + return -ENOMEM; + set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, current->ldt, 512); + load_ldt(i); + } + } + } + + lp = (unsigned long *) ¤t->ldt[ldt_info.entry_number]; + *lp = ((ldt_info.base_addr & 0x0000ffff) << 16) | + (ldt_info.limit & 0x0ffff); + *(lp+1) = (ldt_info.base_addr & 0xff000000) | + ((ldt_info.base_addr & 0x00ff0000)>>16) | + (ldt_info.limit & 0xf0000) | + (ldt_info.contents << 10) | + ((ldt_info.read_exec_only ^ 1) << 9) | + (ldt_info.seg_32bit << 22) | + (ldt_info.limit_in_pages << 23) | + 0xf000; + return 0; +} + +extern "C" int sys_modify_ldt(int func, void *ptr, unsigned long bytecount) +{ + if (func == 0) + return read_ldt(ptr, bytecount); + if (func == 1) + return write_ldt(ptr, bytecount); + return -ENOSYS; +} diff -u --recursive --new-file pl11/linux/kernel/printk.c linux/kernel/printk.c --- pl11/linux/kernel/printk.c Sat Jul 3 01:01:06 1993 +++ linux/kernel/printk.c Mon Aug 9 18:02:32 1993 @@ -20,6 +20,8 @@ #include #include +#define LOG_BUF_LEN 4096 + static char buf[1024]; extern int vsprintf(char * buf, const char * fmt, va_list args); @@ -26,7 +28,7 @@ extern void console_print(const char *); static void (*console_print_proc)(const char *) = 0; -static char log_buf[4096]; +static char log_buf[LOG_BUF_LEN]; static unsigned long log_start = 0; static unsigned long logged_chars = 0; unsigned long log_size = 0; @@ -73,15 +75,19 @@ sti(); } i = 0; + cli(); while (log_size && i < len) { + cli(); c = *((char *) log_buf+log_start); log_start++; log_size--; - log_start &= 4095; + log_start &= LOG_BUF_LEN-1; + sti(); put_fs_byte(c,buf); buf++; i++; } + sti(); return i; case 4: /* Read/clear last 4k of kernel messages */ do_clear = 1; @@ -92,13 +98,13 @@ return 0; verify_area(VERIFY_WRITE,buf,len); count = len; - if (count > 4096) - count = 4096; + if (count > LOG_BUF_LEN) + count = LOG_BUF_LEN; if (count > logged_chars) count = logged_chars; j = log_start + log_size - count; for (i = 0; i < count; i++) { - c = *((char *) log_buf + (j++ & 4095)); + c = *((char *) log_buf + (j++ & LOG_BUF_LEN-1)); put_fs_byte(c, buf++); } if (do_clear) @@ -127,8 +133,8 @@ i=vsprintf(buf,fmt,args); va_end(args); for (j = 0; j < i ; j++) { - log_buf[(log_start+log_size) & 4095] = buf[j]; - if (log_size < 4096) + log_buf[(log_start+log_size) & LOG_BUF_LEN-1] = buf[j]; + if (log_size < LOG_BUF_LEN) log_size++; else log_start++; @@ -156,7 +162,7 @@ for (i=0,j=0; i < log_size; i++) { buf[j++] = log_buf[p]; - p++; p &= 4095; + p++; p &= LOG_BUF_LEN-1; if (j < sizeof(buf)-1) continue; buf[j] = 0; diff -u --recursive --new-file pl11/linux/kernel/ptrace.c linux/kernel/ptrace.c --- pl11/linux/kernel/ptrace.c Thu Jul 15 12:51:25 1993 +++ linux/kernel/ptrace.c Mon Aug 9 18:02:32 1993 @@ -19,7 +19,7 @@ /* determines which flags the user has access to. */ /* 1 = access 0 = no access */ -#define FLAG_MASK 0x00000dd9 +#define FLAG_MASK 0x00044dd5 /* set's the trap flag. */ #define TRAP_FLAG 0x100 @@ -89,11 +89,10 @@ unsigned long page; repeat: - page = tsk->tss.cr3 + ((addr >> 20) & 0xffc); - page = *(unsigned long *) page; + page = *PAGE_DIR_OFFSET(tsk->tss.cr3,addr); if (page & PAGE_PRESENT) { - page &= 0xfffff000; - page += (addr >> 10) & 0xffc; + page &= PAGE_MASK; + page += PAGE_PTR(addr); page = *((unsigned long *) page); } if (!(page & PAGE_PRESENT)) { @@ -100,8 +99,8 @@ do_no_page(0,addr,tsk,0); goto repeat; } - page &= 0xfffff000; - page += addr & 0xfff; + page &= PAGE_MASK; + page += addr & ~PAGE_MASK; return *(unsigned long *) page; } @@ -117,11 +116,10 @@ unsigned long page, pte; repeat: - page = tsk->tss.cr3 + ((addr >> 20) & 0xffc); - page = *(unsigned long *) page; + page = *PAGE_DIR_OFFSET(tsk->tss.cr3,addr); if (page & PAGE_PRESENT) { - page &= 0xfffff000; - page += (addr >> 10) & 0xffc; + page &= PAGE_MASK; + page += PAGE_PTR(addr); pte = page; page = *((unsigned long *) page); } @@ -135,8 +133,8 @@ } /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ *(unsigned long *) pte |= (PAGE_DIRTY|PAGE_COW); - page &= 0xfffff000; - page += addr & 0xfff; + page &= PAGE_MASK; + page += addr & ~PAGE_MASK; *(unsigned long *) page = data; } @@ -149,12 +147,12 @@ { unsigned long low,high; - if (addr > TASK_SIZE-4) + if (addr > TASK_SIZE-sizeof(long)) return -EIO; - if ((addr & 0xfff) > PAGE_SIZE-4) { - low = get_long(tsk,addr & 0xfffffffc); - high = get_long(tsk,(addr+4) & 0xfffffffc); - switch (addr & 3) { + if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) { + low = get_long(tsk,addr & ~(sizeof(long)-1)); + high = get_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1)); + switch (addr & sizeof(long)-1) { case 1: low >>= 8; low |= high << 24; @@ -183,12 +181,12 @@ { unsigned long low,high; - if (addr > TASK_SIZE-4) + if (addr > TASK_SIZE-sizeof(long)) return -EIO; - if ((addr & 0xfff) > PAGE_SIZE-4) { - low = get_long(tsk,addr & 0xfffffffc); - high = get_long(tsk,(addr+4) & 0xfffffffc); - switch (addr & 3) { + if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) { + low = get_long(tsk,addr & ~(sizeof(long)-1)); + high = get_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1)); + switch (addr & sizeof(long)-1) { case 0: /* shouldn't happen, but safety first */ low = data; break; @@ -195,24 +193,24 @@ case 1: low &= 0x000000ff; low |= data << 8; - high &= 0xffffff00; + high &= ~0xff; high |= data >> 24; break; case 2: low &= 0x0000ffff; low |= data << 16; - high &= 0xffff0000; + high &= ~0xffff; high |= data >> 16; break; case 3: low &= 0x00ffffff; low |= data << 24; - high &= 0xff000000; + high &= ~0xffffff; high |= data >> 8; break; } - put_long(tsk,addr & 0xfffffffc,low); - put_long(tsk,(addr+4) & 0xfffffffc,high); + put_long(tsk,addr & ~(sizeof(long)-1),low); + put_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1),high); } else put_long(tsk,addr,data); return 0; @@ -269,7 +267,7 @@ res = read_long(child, addr, &tmp); if (res < 0) return res; - res = verify_area(VERIFY_WRITE, (void *) data, 4); + res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long)); if (!res) put_fs_long(tmp,(unsigned long *) data); return res; @@ -283,10 +281,10 @@ addr = addr >> 2; /* temporary hack. */ if (addr < 0 || addr >= 17) return -EIO; - res = verify_area(VERIFY_WRITE, (void *) data, 4); + res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long)); if (res) return res; - tmp = get_stack_long(child, 4*addr - MAGICNUMBER); + tmp = get_stack_long(child, sizeof(long)*addr - MAGICNUMBER); put_fs_long(tmp,(unsigned long *) data); return 0; } @@ -302,11 +300,15 @@ return -EIO; if (addr == ORIG_EAX) return -EIO; + if (addr == DS || addr == ES || + addr == FS || addr == GS || + addr == CS || addr == SS) + return -EIO; if (addr == EFL) { /* flags. */ data &= FLAG_MASK; - data |= get_stack_long(child, EFL*4-MAGICNUMBER) & ~FLAG_MASK; + data |= get_stack_long(child, EFL*sizeof(long)-MAGICNUMBER) & ~FLAG_MASK; } - if (put_stack_long(child, 4*addr-MAGICNUMBER, data)) + if (put_stack_long(child, sizeof(long)*addr-MAGICNUMBER, data)) return -EIO; return 0; @@ -323,8 +325,8 @@ child->exit_code = data; child->state = TASK_RUNNING; /* make sure the single step bit is not set. */ - tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG; - put_stack_long(child, 4*EFL-MAGICNUMBER,tmp); + tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG; + put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp); return 0; } @@ -339,8 +341,8 @@ child->state = TASK_RUNNING; child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ - tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG; - put_stack_long(child, 4*EFL-MAGICNUMBER,tmp); + tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG; + put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp); return 0; } @@ -350,8 +352,8 @@ if ((unsigned long) data > NSIG) return -EIO; child->flags &= ~PF_TRACESYS; - tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) | TRAP_FLAG; - put_stack_long(child, 4*EFL-MAGICNUMBER,tmp); + tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) | TRAP_FLAG; + put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp); child->state = TASK_RUNNING; child->exit_code = data; /* give it a chance to run. */ @@ -370,8 +372,8 @@ child->p_pptr = child->p_opptr; SET_LINKS(child); /* make sure the single step bit is not set. */ - tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG; - put_stack_long(child, 4*EFL-MAGICNUMBER,tmp); + tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG; + put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp); return 0; } diff -u --recursive --new-file pl11/linux/kernel/sched.c linux/kernel/sched.c --- pl11/linux/kernel/sched.c Fri Jul 16 13:55:59 1993 +++ linux/kernel/sched.c Mon Aug 9 18:02:32 1993 @@ -31,8 +31,14 @@ #define TIMER_IRQ 0 int need_resched = 0; + +/* + * Tell us the machine setup.. + */ int hard_math = 0; /* set by boot/head.S */ +int x86 = 0; /* set by boot/head.S to 3 or 4 */ int ignore_irq13 = 0; /* set if exception 16 works */ +int wp_works_ok = 0; /* not used currently */ extern int _setitimer(int, struct itimerval *, struct itimerval *); unsigned long * prof_buffer = NULL; @@ -148,8 +154,7 @@ } confuse_gcc2: if (!c) { - p = &init_task; - while ((p = p->next_task) != &init_task) + for_each_task(p) p->counter = (p->counter >> 1) + p->priority; } switch_to(next); @@ -277,9 +282,10 @@ restore_flags(flags); } -void del_timer(struct timer_list * timer) +int del_timer(struct timer_list * timer) { unsigned long flags; + unsigned long expires = 0; struct timer_list **p; p = &next_timer; @@ -289,11 +295,15 @@ if (*p == timer) { if ((*p = timer->next) != NULL) (*p)->expires += timer->expires; - break; + timer->expires += expires; + restore_flags(flags); + return 1; } + expires += (*p)->expires; p = &(*p)->next; } restore_flags(flags); + return 0; } unsigned long timer_active = 0; @@ -345,7 +355,7 @@ { unsigned long mask; struct timer_struct *tp = timer_table+0; - struct task_struct ** task_p; + struct task_struct * task_p; jiffies++; calc_load(); @@ -372,13 +382,15 @@ need_resched = 1; } /* Update ITIMER_REAL for every task */ - for (task_p = &LAST_TASK; task_p >= &FIRST_TASK; task_p--) - if (*task_p && (*task_p)->it_real_value - && !(--(*task_p)->it_real_value)) { - send_sig(SIGALRM,*task_p,1); - (*task_p)->it_real_value = (*task_p)->it_real_incr; - need_resched = 1; - } + for_each_task(task_p) { + if (!task_p->it_real_value) + continue; + if (--task_p->it_real_value) + continue; + send_sig(SIGALRM,task_p,1); + task_p->it_real_value = task_p->it_real_incr; + need_resched = 1; + } /* Update ITIMER_PROF for the current task */ if (current->it_prof_value && !(--current->it_prof_value)) { current->it_prof_value = current->it_prof_incr; @@ -473,7 +485,7 @@ printk("%d: pid=%d, state=%d, father=%d, child=%d, ",(p == current)?-nr:nr,p->pid, p->state, p->p_pptr->pid, p->p_cptr ? p->p_cptr->pid : -1); i = 0; - j = 4096; + j = PAGE_SIZE; if (!(stack = (unsigned char *) p->kernel_stack_page)) { stack = (unsigned char *) init_kernel_stack; j = sizeof(init_kernel_stack); @@ -508,7 +520,7 @@ if (sizeof(struct sigaction) != 16) panic("Struct sigaction MUST be 16 bytes"); set_tss_desc(gdt+FIRST_TSS_ENTRY,&init_task.tss); - set_ldt_desc(gdt+FIRST_LDT_ENTRY,&init_task.ldt); + set_ldt_desc(gdt+FIRST_LDT_ENTRY,&default_ldt,1); set_system_gate(0x80,&system_call); p = gdt+2+FIRST_TSS_ENTRY; for(i=1 ; iesp; signr = 1; sa = current->sigaction; + if (regs->cs != USER_CS || regs->ss != USER_DS) + printk("Warning: signal handler with nonstandard code/stack segment\n"); for (mask = 1 ; mask ; sa++,signr++,mask += mask) { if (mask > handler_signal) break; diff -u --recursive --new-file pl11/linux/kernel/sys.c linux/kernel/sys.c --- pl11/linux/kernel/sys.c Sun Jul 18 13:11:23 1993 +++ linux/kernel/sys.c Mon Aug 9 18:02:32 1993 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -156,7 +157,7 @@ if ((tmp = tsk->tss.cr3) != 0) { tmp = *(unsigned long *) tmp; if (tmp & PAGE_PRESENT) { - tmp &= 0xfffff000; + tmp &= PAGE_MASK; pg_table = (0xA0000 >> PAGE_SHIFT) + (unsigned long *) tmp; tmp = 32; while (tmp--) { @@ -189,7 +190,7 @@ * inherited from protected mode. */ info.regs.eflags &= 0x00000dd5; - info.regs.eflags |= 0xfffff22a & pt_regs->eflags; + info.regs.eflags |= ~0x00000dd5 & pt_regs->eflags; info.regs.eflags |= VM_MASK; current->saved_kernel_stack = current->tss.esp0; current->tss.esp0 = (unsigned long) pt_regs; @@ -433,8 +434,8 @@ if (brk < current->end_code) return current->brk; - newbrk = (brk + 0x00000fff) & 0xfffff000; - oldbrk = (current->brk + 0x00000fff) & 0xfffff000; + newbrk = PAGE_ALIGN(brk); + oldbrk = PAGE_ALIGN(current->brk); /* * Always allow shrinking brk */ @@ -448,7 +449,7 @@ */ rlim = current->rlim[RLIMIT_DATA].rlim_cur; if (rlim >= RLIM_INFINITY) - rlim = 0xffffffff; + rlim = ~0; if (brk - current->end_code > rlim || brk >= current->start_stack - 16384) return current->brk; /* @@ -891,7 +892,7 @@ { int old = current->umask; - current->umask = mask & 0777; + current->umask = mask & S_IRWXUGO; return (old); } diff -u --recursive --new-file pl11/linux/kernel/sys_call.S linux/kernel/sys_call.S --- pl11/linux/kernel/sys_call.S Tue Jul 13 22:46:04 1993 +++ linux/kernel/sys_call.S Tue Jul 20 10:33:38 1993 @@ -194,10 +194,8 @@ 9: movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are testl $(VM_MASK),%eax # different then jne 1f - cmpw $(USER_CS),CS(%esp) # was old code segment supervisor ? - jne 2f - cmpw $(USER_DS),OLDSS(%esp) # was stack segment user segment ? - jne 2f + cmpw $(KERNEL_CS),CS(%esp) # was old code segment supervisor ? + je 2f 1: sti orl $(IF_MASK),%eax # these just try to make sure andl $~NT_MASK,%eax # the program doesn't do anything diff -u --recursive --new-file pl11/linux/lib/malloc.c linux/lib/malloc.c --- pl11/linux/lib/malloc.c Wed Jul 14 17:18:34 1993 +++ linux/lib/malloc.c Mon Aug 9 18:02:32 1993 @@ -146,10 +146,8 @@ int i; bdesc = (struct bucket_desc *) page; - for (i = PAGE_SIZE/sizeof(struct bucket_desc); i > 1; i--) { + for (i = PAGE_SIZE/sizeof(struct bucket_desc); --i > 0; bdesc++ ) bdesc->next = bdesc+1; - bdesc++; - } /* * This is done last, to avoid race conditions in case * get_free_page() sleeps and this routine gets called again.... @@ -214,8 +212,7 @@ */ if (!free_bucket_desc) { restore_flags(flags); - page = get_free_page(priority); - if (!page) + if(!(page=__get_free_page(priority))) return NULL; init_bucket_desc(page); } @@ -224,12 +221,10 @@ free_bucket_desc = bdesc->next; restore_flags(flags); - page = get_free_page(priority); - + if(!(page=__get_free_page(priority))) { /* * Out of memory? Put the bucket descriptor back on the free list */ - if (!page) { cli(); bdesc->next = free_bucket_desc; free_bucket_desc = bdesc; @@ -345,16 +340,17 @@ hd--; if(hd->magic != DEB_MAGIC_ALLOC) { - if(hd->magic != DEB_MAGIC_USED && hd->magic != DEB_MAGIC_FREED) - printk("DEB_MALLOC Using %s block of 0x%x from %s:%d, by %s:%d, last %s:%d\n", - (hd->magic == DEB_MAGIC_FREE)?"free":"bad",obj,deb_file,deb_line,hd->file,hd->line,hd->ok_file,hd->ok_line); - if(hd->magic == DEB_MAGIC_FREE) + if(hd->magic == DEB_MAGIC_FREE) { + printk("DEB_MALLOC Using free block of 0x%x at %s:%d, by %s:%d, wasOK %s:%d\n", + obj,deb_file,deb_line,hd->file,hd->line,hd->ok_file,hd->ok_line); + /* For any other condition it is either superfluous or dangerous to print something. */ hd->magic = DEB_MAGIC_FREED; + } return; } if(hd->size != size) { if(size != 0) { - printk("DEB_MALLOC size for 0x%x given as %d, stored %d, from %s:%d, last %s:%d\n", + printk("DEB_MALLOC size for 0x%x given as %d, stored %d, at %s:%d, wasOK %s:%d\n", obj,size,hd->size,deb_file,deb_line,hd->ok_file,hd->ok_line); } size = hd->size; @@ -361,7 +357,8 @@ } he = (struct hdr_end *)(((char *)obj)+size); if(he->magic != DEB_MAGIC_END) { - printk("DEB_MALLOC overran block 0x%x:%d, free at %s:%d\n",obj,hd->size,deb_file,deb_line); + printk("DEB_MALLOC overran block 0x%x:%d, at %s:%d, wasOK %s:%d\n", + obj,hd->size,deb_file,deb_line,hd->ok_file,hd->ok_line); hd->magic = DEB_MAGIC_USED; return; } @@ -398,10 +395,8 @@ hd = (struct hdr_start *) obj; hd--; - if(hd->magic != DEB_MAGIC_ALLOC && hd->magic != DEB_MAGIC_USED) { - if(hd->magic != DEB_MAGIC_FREED) - printk("DEB_MALLOC %s free of 0x%x from %s:%d by %s:%d, last %s:%d\n", - (hd->magic == DEB_MAGIC_FREE)?"dup":"bad", + if(hd->magic == DEB_MAGIC_FREE) { + printk("DEB_MALLOC dup free of 0x%x at %s:%d by %s:%d, wasOK %s:%d\n", obj,deb_file,deb_line,hd->file,hd->line,hd->ok_file,hd->ok_line); return; } @@ -408,7 +403,7 @@ if(hd->size != size) { if(size != 0) { if(hd->magic != DEB_MAGIC_USED) - printk("DEB_MALLOC size for 0x%x given as %d, stored %d, from %s:%d, last %s:%d\n", + printk("DEB_MALLOC size for 0x%x given as %d, stored %d, at %s:%d, wasOK %s:%d\n", obj,size,hd->size,deb_file,deb_line,hd->ok_file,hd->ok_line); } size = hd->size; @@ -416,9 +411,8 @@ he = (struct hdr_end *)(((char *)obj)+size); if(he->magic != DEB_MAGIC_END) { if(hd->magic != DEB_MAGIC_USED) - printk("DEB_MALLOC overran block 0x%x:%d, free at %s:%d, last %s:%d\n", - obj,hd->size,deb_file,deb_line,hd->ok_file,hd->ok_line); - return; + printk("DEB_MALLOC overran block 0x%x:%d, at %s:%d, from %s:%d, wasOK %s:%d\n", + obj,hd->size,deb_file,deb_line,hd->file,hd->line,hd->ok_file,hd->ok_line); } size += sizeof(struct hdr_start)+sizeof(struct hdr_end); } @@ -425,7 +419,7 @@ #endif save_flags(flags); /* Calculate what page this object lives in */ - page = (void *) ((unsigned long) obj & 0xfffff000); + page = (void *) ((unsigned long) obj & PAGE_MASK); /* Now search the buckets looking for that page */ for (bdir = bucket_dir; bdir->size; bdir++) { @@ -528,7 +522,7 @@ if(hd->magic == DEB_MAGIC_ALLOC) { if(len > PAGE_SIZE-80) { restore_flags(flags); - len += sprintf(buffer+len,"+++\n"); + len += sprintf(buffer+len,"...\n"); return len; } len += sprintf(buffer+len,"%08x:%03x %s:%d %s:%d\n", diff -u --recursive --new-file pl11/linux/mm/memory.c linux/mm/memory.c --- pl11/linux/mm/memory.c Thu Jul 15 13:12:10 1993 +++ linux/mm/memory.c Sun Aug 15 11:09:18 1993 @@ -91,8 +91,8 @@ } if (mem_map[MAP_NR(pg_table)] & MAP_PAGE_RESERVED) return; - page_table = (unsigned long *) (pg_table & 0xfffff000); - for (j = 0 ; j < 1024 ; j++,page_table++) { + page_table = (unsigned long *) (pg_table & PAGE_MASK); + for (j = 0 ; j < PTRS_PER_PAGE ; j++,page_table++) { unsigned long pg = *page_table; if (!pg) @@ -99,11 +99,11 @@ continue; *page_table = 0; if (pg & PAGE_PRESENT) - free_page(0xfffff000 & pg); + free_page(PAGE_MASK & pg); else swap_free(pg); } - free_page(0xfffff000 & pg_table); + free_page(PAGE_MASK & pg_table); } /* @@ -130,19 +130,16 @@ return; } if (mem_map[MAP_NR(pg_dir)] > 1) { - unsigned long page; unsigned long * new_pg; - page = get_free_page(GFP_KERNEL); - if (!page) { + if (!(new_pg = (unsigned long*) get_free_page(GFP_KERNEL))) { oom(tsk); return; } - new_pg = (unsigned long *) page; for (i = 768 ; i < 1024 ; i++) new_pg[i] = page_dir[i]; free_page(pg_dir); - tsk->tss.cr3 = page; + tsk->tss.cr3 = (unsigned long) new_pg; return; } for (i = 0 ; i < 768 ; i++,page_dir++) @@ -179,7 +176,7 @@ return; } page_dir = (unsigned long *) pg_dir; - for (i = 0 ; i < 1024 ; i++,page_dir++) + for (i = 0 ; i < PTRS_PER_PAGE ; i++,page_dir++) free_one_table(page_dir); free_page(pg_dir); invalidate(); @@ -212,14 +209,13 @@ unsigned long old_pg_dir, *old_page_dir; unsigned long new_pg_dir, *new_page_dir; - new_pg_dir = get_free_page(GFP_KERNEL); - if (!new_pg_dir) + if (!(new_pg_dir = get_free_page(GFP_KERNEL))) return -ENOMEM; old_pg_dir = current->tss.cr3; tsk->tss.cr3 = new_pg_dir; old_page_dir = (unsigned long *) old_pg_dir; new_page_dir = (unsigned long *) new_pg_dir; - for (i = 0 ; i < 1024 ; i++,old_page_dir++,new_page_dir++) { + for (i = 0 ; i < PTRS_PER_PAGE ; i++,old_page_dir++,new_page_dir++) { int j; unsigned long old_pg_table, *old_page_table; unsigned long new_pg_table, *new_page_table; @@ -237,14 +233,13 @@ *new_page_dir = old_pg_table; continue; } - new_pg_table = get_free_page(GFP_KERNEL); - if (!new_pg_table) { + if (!(new_pg_table = get_free_page(GFP_KERNEL))) { free_page_tables(tsk); return -ENOMEM; } - old_page_table = (unsigned long *) (0xfffff000 & old_pg_table); - new_page_table = (unsigned long *) (0xfffff000 & new_pg_table); - for (j = 0 ; j < 1024 ; j++,old_page_table++,new_page_table++) { + old_page_table = (unsigned long *) (PAGE_MASK & old_pg_table); + new_page_table = (unsigned long *) (PAGE_MASK & new_pg_table); + for (j = 0 ; j < PTRS_PER_PAGE ; j++,old_page_table++,new_page_table++) { unsigned long pg; pg = *old_page_table; if (!pg) @@ -277,18 +272,18 @@ unsigned long *page_table, *dir; unsigned long poff, pcnt, pc; - if (from & 0xfff) { + if (from & ~PAGE_MASK) { printk("unmap_page_range called with wrong alignment\n"); return -EINVAL; } - size = (size + 0xfff) >> PAGE_SHIFT; - dir = (unsigned long *) (current->tss.cr3 + ((from >> 20) & 0xffc)); - poff = (from >> PAGE_SHIFT) & 0x3ff; - if ((pcnt = 1024 - poff) > size) + size = (size + ~PAGE_MASK) >> PAGE_SHIFT; + dir = PAGE_DIR_OFFSET(current->tss.cr3,from); + poff = (from >> PAGE_SHIFT) & PTRS_PER_PAGE-1; + if ((pcnt = PTRS_PER_PAGE - poff) > size) pcnt = size; for ( ; size > 0; ++dir, size -= pcnt, - pcnt = (size > 1024 ? 1024 : size)) { + pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size)) { if (!(page_dir = *dir)) { poff = 0; continue; @@ -297,7 +292,7 @@ printk("unmap_page_range: bad page directory."); continue; } - page_table = (unsigned long *)(0xfffff000 & page_dir); + page_table = (unsigned long *)(PAGE_MASK & page_dir); if (poff) { page_table += poff; poff = 0; @@ -309,14 +304,14 @@ if (!(mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)) --current->rss; - free_page(0xfffff000 & page); + free_page(PAGE_MASK & page); } else swap_free(page); } } - if (pcnt == 1024) { - free_page(0xfffff000 & page_dir); + if (pcnt == PTRS_PER_PAGE) { *dir = 0; + free_page(PAGE_MASK & page_dir); } } invalidate(); @@ -330,35 +325,36 @@ unsigned long page; if (mask) { - if ((mask & 0xfffff001) != PAGE_PRESENT) { + if ((mask & (PAGE_MASK|PAGE_PRESENT)) != PAGE_PRESENT) { printk("zeromap_page_range: mask = %08x\n",mask); return -EINVAL; } mask |= ZERO_PAGE; } - if (from & 0xfff) { + if (from & ~PAGE_MASK) { printk("zeromap_page_range: from = %08x\n",from); return -EINVAL; } - dir = (unsigned long *) (current->tss.cr3 + ((from >> 20) & 0xffc)); - size = (size + 0xfff) >> PAGE_SHIFT; - poff = (from >> PAGE_SHIFT) & 0x3ff; - if ((pcnt = 1024 - poff) > size) + dir = PAGE_DIR_OFFSET(current->tss.cr3,from); + size = (size + ~PAGE_MASK) >> PAGE_SHIFT; + poff = (from >> PAGE_SHIFT) & PTRS_PER_PAGE-1; + if ((pcnt = PTRS_PER_PAGE - poff) > size) pcnt = size; while (size > 0) { if (!(PAGE_PRESENT & *dir)) { - if (!(page_table = (unsigned long *)get_free_page(GFP_KERNEL))) { + /* clear page needed here? SRB. */ + if (!(page_table = (unsigned long*) get_free_page(GFP_KERNEL))) { invalidate(); return -ENOMEM; } if (PAGE_PRESENT & *dir) { free_page((unsigned long) page_table); - page_table = (unsigned long *)(0xfffff000 & *dir++); + page_table = (unsigned long *)(PAGE_MASK & *dir++); } else *dir++ = ((unsigned long) page_table) | PAGE_TABLE; } else - page_table = (unsigned long *)(0xfffff000 & *dir++); + page_table = (unsigned long *)(PAGE_MASK & *dir++); page_table += poff; poff = 0; for (size -= pcnt; pcnt-- ;) { @@ -368,13 +364,13 @@ if (!(mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)) --current->rss; - free_page(0xfffff000 & page); + free_page(PAGE_MASK & page); } else swap_free(page); } *page_table++ = mask; } - pcnt = (size > 1024 ? 1024 : size); + pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size); } invalidate(); return 0; @@ -392,24 +388,25 @@ unsigned long page; if (mask) { - if ((mask & 0xfffff001) != PAGE_PRESENT) { + if ((mask & (PAGE_MASK|PAGE_PRESENT)) != PAGE_PRESENT) { printk("remap_page_range: mask = %08x\n",mask); return -EINVAL; } } - if ((from & 0xfff) || (to & 0xfff)) { + if ((from & ~PAGE_MASK) || (to & ~PAGE_MASK)) { printk("remap_page_range: from = %08x, to=%08x\n",from,to); return -EINVAL; } - dir = (unsigned long *) (current->tss.cr3 + ((from >> 20) & 0xffc)); - size = (size + 0xfff) >> PAGE_SHIFT; - poff = (from >> PAGE_SHIFT) & 0x3ff; - if ((pcnt = 1024 - poff) > size) + dir = PAGE_DIR_OFFSET(current->tss.cr3,from); + size = (size + ~PAGE_MASK) >> PAGE_SHIFT; + poff = (from >> PAGE_SHIFT) & PTRS_PER_PAGE-1; + if ((pcnt = PTRS_PER_PAGE - poff) > size) pcnt = size; while (size > 0) { if (!(PAGE_PRESENT & *dir)) { - if (!(page_table = (unsigned long *)get_free_page(GFP_KERNEL))) { + /* clearing page here, needed? SRB. */ + if (!(page_table = (unsigned long*) get_free_page(GFP_KERNEL))) { invalidate(); return -1; } @@ -416,7 +413,7 @@ *dir++ = ((unsigned long) page_table) | PAGE_TABLE; } else - page_table = (unsigned long *)(0xfffff000 & *dir++); + page_table = (unsigned long *)(PAGE_MASK & *dir++); if (poff) { page_table += poff; poff = 0; @@ -429,7 +426,7 @@ if (!(mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)) --current->rss; - free_page(0xfffff000 & page); + free_page(PAGE_MASK & page); } else swap_free(page); } @@ -455,7 +452,7 @@ } to += PAGE_SIZE; } - pcnt = (size > 1024 ? 1024 : size); + pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size); } invalidate(); return 0; @@ -467,20 +464,20 @@ * out of memory (either when trying to access page-table or * page.) */ -static unsigned long put_page(struct task_struct * tsk,unsigned long page, +unsigned long put_page(struct task_struct * tsk,unsigned long page, unsigned long address,int prot) { unsigned long *page_table; - if ((prot & 0xfffff001) != PAGE_PRESENT) + if ((prot & (PAGE_MASK|PAGE_PRESENT)) != PAGE_PRESENT) printk("put_page: prot = %08x\n",prot); if (page >= high_memory) { printk("put_page: trying to put page %p at %p\n",page,address); return 0; } - page_table = (unsigned long *) (tsk->tss.cr3 + ((address>>20) & 0xffc)); + page_table = PAGE_DIR_OFFSET(tsk->tss.cr3,address); if ((*page_table) & PAGE_PRESENT) - page_table = (unsigned long *) (0xfffff000 & *page_table); + page_table = (unsigned long *) (PAGE_MASK & *page_table); else { printk("put_page: bad page directory entry\n"); oom(tsk); @@ -487,7 +484,7 @@ *page_table = BAD_PAGETABLE | PAGE_TABLE; return 0; } - page_table += ((address & 0x003ff000) >> PAGE_SHIFT); + page_table += (address >> PAGE_SHIFT) & PTRS_PER_PAGE-1; if (*page_table) { printk("put_page: page already exists\n"); *page_table = 0; @@ -512,21 +509,21 @@ printk("put_dirty_page: trying to put page %p at %p\n",page,address); if (mem_map[MAP_NR(page)] != 1) printk("mem_map disagrees with %p at %p\n",page,address); - page_table = (unsigned long *) (tsk->tss.cr3 + ((address>>20) & 0xffc)); + page_table = PAGE_DIR_OFFSET(tsk->tss.cr3,address); if (PAGE_PRESENT & *page_table) - page_table = (unsigned long *) (0xfffff000 & *page_table); + page_table = (unsigned long *) (PAGE_MASK & *page_table); else { - if (!(tmp=get_free_page(GFP_KERNEL))) + if (!(tmp = get_free_page(GFP_KERNEL))) return 0; if (PAGE_PRESENT & *page_table) { free_page(tmp); - page_table = (unsigned long *) (0xfffff000 & *page_table); + page_table = (unsigned long *) (PAGE_MASK & *page_table); } else { *page_table = tmp | PAGE_TABLE; page_table = (unsigned long *) tmp; } } - page_table += (address >> PAGE_SHIFT) & 0x3ff; + page_table += (address >> PAGE_SHIFT) & PTRS_PER_PAGE-1; if (*page_table) { printk("put_dirty_page: page already exists\n"); *page_table = 0; @@ -551,18 +548,18 @@ static void __do_wp_page(unsigned long error_code, unsigned long address, struct task_struct * tsk, unsigned long user_esp) { - unsigned long pde, pte, old_page, prot; + unsigned long *pde, pte, old_page, prot; unsigned long new_page; new_page = __get_free_page(GFP_KERNEL); - pde = tsk->tss.cr3 + ((address>>20) & 0xffc); - pte = *(unsigned long *) pde; + pde = PAGE_DIR_OFFSET(tsk->tss.cr3,address); + pte = *pde; if (!(pte & PAGE_PRESENT)) goto end_wp_page; if ((pte & PAGE_TABLE) != PAGE_TABLE || pte >= high_memory) goto bad_wp_pagetable; - pte &= 0xfffff000; - pte += (address>>10) & 0xffc; + pte &= PAGE_MASK; + pte += PAGE_PTR(address); old_page = *(unsigned long *) pte; if (!(old_page & PAGE_PRESENT)) goto end_wp_page; @@ -571,8 +568,8 @@ if (old_page & PAGE_RW) goto end_wp_page; tsk->min_flt++; - prot = (old_page & 0x00000fff) | PAGE_RW; - old_page &= 0xfffff000; + prot = (old_page & ~PAGE_MASK) | PAGE_RW; + old_page &= PAGE_MASK; if (mem_map[MAP_NR(old_page)] != 1) { if (new_page) { if (mem_map[MAP_NR(old_page)] & MAP_PAGE_RESERVED) @@ -601,7 +598,7 @@ goto end_wp_page; bad_wp_pagetable: printk("do_wp_page: bogus page-table at address %08x (%08x)\n",address,pte); - *(unsigned long *) pde = BAD_PAGETABLE | PAGE_TABLE; + *pde = BAD_PAGETABLE | PAGE_TABLE; send_sig(SIGKILL, tsk, 1); end_wp_page: if (new_page) @@ -619,12 +616,12 @@ unsigned long page; unsigned long * pg_table; - pg_table = (unsigned long *) (tsk->tss.cr3 + ((address>>20) & 0xffc)); + pg_table = PAGE_DIR_OFFSET(tsk->tss.cr3,address); page = *pg_table; if (!page) return; if ((page & PAGE_PRESENT) && page < high_memory) { - pg_table = (unsigned long *) ((page & 0xfffff000) + ((address>>10) & 0xffc)); + pg_table = (unsigned long *) ((page & PAGE_MASK) + PAGE_PTR(address)); page = *pg_table; if (!(page & PAGE_PRESENT)) return; @@ -662,22 +659,21 @@ if (!size) return 0; size--; - size += start & 0xfff; - size >>= 12; - start &= 0xfffff000; + size += start & ~PAGE_MASK; + size >>= PAGE_SHIFT; + start &= PAGE_MASK; do { do_wp_page(1,start,current,0); - start += 4096; + start += PAGE_SIZE; } while (size--); return 0; } -static void get_empty_page(struct task_struct * tsk, unsigned long address) +static inline void get_empty_page(struct task_struct * tsk, unsigned long address) { unsigned long tmp; - tmp = get_free_page(GFP_KERNEL); - if (!tmp) { + if (!(tmp = get_free_page(GFP_KERNEL))) { oom(tsk); tmp = BAD_PAGE; } @@ -707,14 +703,14 @@ unsigned long from_page; unsigned long to_page; - from_page = p->tss.cr3 + ((address>>20) & 0xffc); - to_page = tsk->tss.cr3 + ((address>>20) & 0xffc); + from_page = (unsigned long)PAGE_DIR_OFFSET(p->tss.cr3,address); + to_page = (unsigned long)PAGE_DIR_OFFSET(tsk->tss.cr3,address); /* is there a page-directory at from? */ from = *(unsigned long *) from_page; if (!(from & PAGE_PRESENT)) return 0; - from &= 0xfffff000; - from_page = from + ((address>>10) & 0xffc); + from &= PAGE_MASK; + from_page = from + PAGE_PTR(address); from = *(unsigned long *) from_page; /* is the page clean and present? */ if ((from & (PAGE_PRESENT | PAGE_DIRTY)) != PAGE_PRESENT) @@ -727,19 +723,22 @@ to = *(unsigned long *) to_page; if (!(to & PAGE_PRESENT)) return 0; - to &= 0xfffff000; - to_page = to + ((address>>10) & 0xffc); + to &= PAGE_MASK; + to_page = to + PAGE_PTR(address); if (*(unsigned long *) to_page) return 0; /* share them if read - do COW immediately otherwise */ if (error_code & PAGE_RW) { - copy_page((from & 0xfffff000),newpage); + if(!newpage) /* did the page exist? SRB. */ + return 0; + copy_page((from & PAGE_MASK),newpage); to = newpage | PAGE_PRIVATE; } else { mem_map[MAP_NR(from)]++; from &= ~PAGE_RW; to = from; - free_page(newpage); + if(newpage) /* only if it existed. SRB. */ + free_page(newpage); } *(unsigned long *) from_page = from; *(unsigned long *) to_page = to; @@ -755,7 +754,7 @@ * We first check if it is at all feasible by checking executable->i_count. * It should be >1 if there are other tasks sharing this inode. */ -static int share_page(struct vm_area_struct * area, struct task_struct * tsk, +int share_page(struct vm_area_struct * area, struct task_struct * tsk, struct inode * inode, unsigned long address, unsigned long error_code, unsigned long newpage) { @@ -799,7 +798,7 @@ unsigned long page; unsigned long *p; - p = (unsigned long *) (tsk->tss.cr3 + ((address >> 20) & 0xffc)); + p = PAGE_DIR_OFFSET(tsk->tss.cr3,address); if (PAGE_PRESENT & *p) return *p; if (*p) { @@ -807,7 +806,7 @@ *p = 0; } page = get_free_page(GFP_KERNEL); - p = (unsigned long *) (tsk->tss.cr3 + ((address >> 20) & 0xffc)); + p = PAGE_DIR_OFFSET(tsk->tss.cr3,address); if (PAGE_PRESENT & *p) { free_page(page); return *p; @@ -828,18 +827,15 @@ void do_no_page(unsigned long error_code, unsigned long address, struct task_struct *tsk, unsigned long user_esp) { - int nr[8], prot; unsigned long tmp; unsigned long page; - unsigned int block,i, j; - struct inode * inode; struct vm_area_struct * mpnt; page = get_empty_pgtable(tsk,address); if (!page) return; - page &= 0xfffff000; - page += (address >> 10) & 0xffc; + page &= PAGE_MASK; + page += PAGE_PTR(address); tmp = *(unsigned long *) page; if (tmp & PAGE_PRESENT) return; @@ -850,67 +846,25 @@ return; } address &= 0xfffff000; - inode = NULL; - block = 0xffffffff; - if (address < tsk->end_data) { - inode = tsk->executable; - block = 1 + address / BLOCK_SIZE; - } else { - for (mpnt = tsk->mmap ; mpnt ; mpnt = mpnt->vm_next) { - if (address < mpnt->vm_start) - continue; - if (address >= ((mpnt->vm_end + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) - continue; - mpnt->vm_ops->nopage(error_code, mpnt, address); - return; - } - } - if (!inode) { - ++tsk->min_flt; - get_empty_page(tsk,address); - if (tsk != current) - return; - if (address < tsk->brk) - return; - if (address+8192 >= (user_esp & 0xfffff000) && - address <= current->start_stack) - return; - send_sig(SIGSEGV,tsk,1); - return; - } - page = get_free_page(GFP_KERNEL); - if (share_page(NULL, tsk,inode,address,error_code,page)) { - ++tsk->min_flt; + for (mpnt = tsk->mmap ; mpnt ; mpnt = mpnt->vm_next) { + if (address < mpnt->vm_start) + continue; + if (address >= ((mpnt->vm_end + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + continue; + mpnt->vm_ops->nopage(error_code, mpnt, address); return; } - if (!page) { - oom(current); - put_page(tsk,BAD_PAGE,address,PAGE_PRIVATE); + ++tsk->min_flt; + get_empty_page(tsk,address); + if (tsk != current) return; - } - prot = PAGE_PRIVATE; - if (CODE_SPACE(address, tsk)) - prot = PAGE_READONLY; - if (block != 0xffffffff) { - for (i=0, j=0; i< PAGE_SIZE ; j++, block++,i +=inode->i_sb->s_blocksize) - nr[j] = bmap(inode,block); - page = bread_page(page,inode->i_dev,nr, - inode->i_sb->s_blocksize,prot); - } - if (!(error_code & PAGE_RW) && share_page(NULL, tsk,inode,address, error_code,page)) + if (address < tsk->brk) return; - i = address + PAGE_SIZE - tsk->end_data; - if (i > PAGE_SIZE-1) - i = 0; - tmp = page + PAGE_SIZE; - while (i--) { - tmp--; - *(char *)tmp = 0; - } - if (put_page(tsk,page,address,prot)) + if (address+8192 >= (user_esp & 0xfffff000) && + address <= current->start_stack) return; - free_page(page); - oom(current); + send_sig(SIGSEGV,tsk,1); + return; } /* @@ -977,7 +931,7 @@ __asm__ __volatile__("cld ; rep ; stosl": :"a" (BAD_PAGE + PAGE_TABLE), "D" ((long) empty_bad_page_table), - "c" (1024) + "c" (PTRS_PER_PAGE) :"di","cx"); return (unsigned long) empty_bad_page_table; } @@ -989,7 +943,7 @@ __asm__ __volatile__("cld ; rep ; stosl": :"a" (0), "D" ((long) empty_bad_page), - "c" (1024) + "c" (PTRS_PER_PAGE) :"di","cx"); return (unsigned long) empty_bad_page; } @@ -1001,7 +955,7 @@ __asm__ __volatile__("cld ; rep ; stosl": :"a" (0), "D" ((long) empty_zero_page), - "c" (1024) + "c" (PTRS_PER_PAGE) :"di","cx"); return (unsigned long) empty_zero_page; } @@ -1012,9 +966,9 @@ int shared = 0; printk("Mem-info:\n"); - printk("Free pages: %6dkB\n",nr_free_pages<<2); - printk("Secondary pages: %6dkB\n",nr_secondary_pages<<2); - printk("Free swap: %6dkB\n",nr_swap_pages<<2); + printk("Free pages: %6dkB\n",nr_free_pages<>10); printk("Buffer heads: %6d\n",nr_buffer_heads); printk("Buffer blocks: %6d\n",nr_buffers); @@ -1053,9 +1007,8 @@ * stay that way - it's write-protected and when there is a c-o-w, the * mm handler treats it specially. */ - memset((void *) 0, 0, 4096); - start_mem += 4095; - start_mem &= 0xfffff000; + memset((void *) 0, 0, PAGE_SIZE); + start_mem = PAGE_ALIGN(start_mem); address = 0; pg_dir = swapper_pg_dir; while (address < end_mem) { @@ -1063,17 +1016,17 @@ if (!tmp) { tmp = start_mem | PAGE_TABLE; *(pg_dir + 768) = tmp; - start_mem += 4096; + start_mem += PAGE_SIZE; } *pg_dir = tmp; /* also map it in at 0x0000000 for init */ pg_dir++; - pg_table = (unsigned long *) (tmp & 0xfffff000); - for (tmp = 0 ; tmp < 1024 ; tmp++,pg_table++) { + pg_table = (unsigned long *) (tmp & PAGE_MASK); + for (tmp = 0 ; tmp < PTRS_PER_PAGE ; tmp++,pg_table++) { if (address && address < end_mem) *pg_table = address | PAGE_SHARED; else *pg_table = 0; - address += 4096; + address += PAGE_SIZE; } } invalidate(); @@ -1091,10 +1044,10 @@ extern int etext; cli(); - end_mem &= 0xfffff000; + end_mem &= PAGE_MASK; high_memory = end_mem; - start_mem += 0x0000000f; - start_mem &= 0xfffffff0; + start_mem += 0x0000000f; + start_mem &= ~0x0000000f; tmp = MAP_NR(end_mem); mem_map = (unsigned short *) start_mem; p = mem_map + tmp; @@ -1101,22 +1054,20 @@ start_mem = (unsigned long) p; while (p > mem_map) *--p = MAP_PAGE_RESERVED; - start_low_mem += 0x00000fff; - start_low_mem &= 0xfffff000; - start_mem += 0x00000fff; - start_mem &= 0xfffff000; + start_low_mem = PAGE_ALIGN(start_low_mem); + start_mem = PAGE_ALIGN(start_mem); while (start_low_mem < 0xA0000) { mem_map[MAP_NR(start_low_mem)] = 0; - start_low_mem += 4096; + start_low_mem += PAGE_SIZE; } while (start_mem < end_mem) { mem_map[MAP_NR(start_mem)] = 0; - start_mem += 4096; + start_mem += PAGE_SIZE; } sound_mem_init(); free_page_list = 0; nr_free_pages = 0; - for (tmp = 0 ; tmp < end_mem ; tmp += 4096) { + for (tmp = 0 ; tmp < end_mem ; tmp += PAGE_SIZE) { if (mem_map[MAP_NR(tmp)]) { if (tmp >= 0xA0000 && tmp < 0x100000) reservedpages++; @@ -1134,9 +1085,9 @@ printk("Memory: %dk/%dk available (%dk kernel code, %dk reserved, %dk data)\n", tmp >> 10, end_mem >> 10, - codepages << 2, - reservedpages << 2, - datapages << 2); + codepages << PAGE_SHIFT-10, + reservedpages << PAGE_SHIFT-10, + datapages << PAGE_SHIFT-10); return; } @@ -1178,7 +1129,7 @@ int i, j; int prot = area->vm_page_prot; /* prot for buffer cache.. */ - address &= 0xfffff000; + address &= PAGE_MASK; block = address - area->vm_start + area->vm_offset; block >>= inode->i_sb->s_blocksize_bits; @@ -1217,8 +1168,7 @@ tmp = page + PAGE_SIZE; while (clear--) { - tmp--; - *(char *)tmp = 0; + *(char *)--tmp = 0; } if (put_page(area->vm_task,page,address,area->vm_page_prot)) return; diff -u --recursive --new-file pl11/linux/mm/mmap.c linux/mm/mmap.c --- pl11/linux/mm/mmap.c Thu Jul 15 13:22:29 1993 +++ linux/mm/mmap.c Mon Aug 9 18:02:33 1993 @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -31,8 +32,8 @@ * */ -#define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \ - current->start_code + current->end_code) +#define CODE_SPACE(addr) \ + (PAGE_ALIGN(addr) < current->start_code + current->end_code) int do_mmap(struct file * file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long off) @@ -68,7 +69,7 @@ */ if (flags & MAP_FIXED) { - if ((addr & 0xfff) || addr == 0) + if (addr & ~PAGE_MASK) return -EINVAL; if (len > TASK_SIZE || addr > TASK_SIZE - len) return -ENOMEM; @@ -76,20 +77,20 @@ struct vm_area_struct * vmm; /* Maybe this works.. Ugly it is. */ - addr = 0x40000000; - while (addr+len < 0x60000000) { + addr = SHM_RANGE_START; + while (addr+len < SHM_RANGE_END) { for (vmm = current->mmap ; vmm ; vmm = vmm->vm_next) { if (addr >= vmm->vm_end) continue; if (addr + len <= vmm->vm_start) continue; - addr = (vmm->vm_end + 0xfff) & 0xfffff000; + addr = PAGE_ALIGN(vmm->vm_end); break; } if (!vmm) break; } - if (addr+len >= 0x60000000) + if (addr+len >= SHM_RANGE_END) return -ENOMEM; } @@ -136,7 +137,7 @@ { struct vm_area_struct *mpnt, **p, *free; - if ((addr & 0xfff) || addr > 0x7fffffff || addr == 0 || addr + len > TASK_SIZE) + if ((addr & ~PAGE_MASK) || addr > LONG_MAX || addr == 0 || addr + len > TASK_SIZE) return -EINVAL; /* This needs a bit of work - we need to figure out how to @@ -198,25 +199,16 @@ if (off & (inode->i_sb->s_blocksize - 1)) return -EINVAL; - if (len > high_memory || off > high_memory - len) /* avoid overflow */ return -ENXIO; - if (get_limit(USER_DS) != TASK_SIZE) return -EINVAL; - - if (!inode->i_sb || !S_ISREG(inode->i_mode) || !permission(inode,MAY_READ)) { - iput(inode); + if (!inode->i_sb || !S_ISREG(inode->i_mode)) return -EACCES; - } - if (!inode->i_op || !inode->i_op->bmap) { - iput(inode); + if (!inode->i_op || !inode->i_op->bmap) return -ENOEXEC; - } - if (!(bh = bread(inode->i_dev,bmap(inode,0),inode->i_sb->s_blocksize))) { - iput(inode); + if (!(bh = bread(inode->i_dev,bmap(inode,0),inode->i_sb->s_blocksize))) return -EACCES; - } if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; @@ -224,10 +216,8 @@ brelse(bh); mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!mpnt){ - iput(inode); + if (!mpnt) return -ENOMEM; - } unmap_page_range(addr, len); mpnt->vm_task = current; diff -u --recursive --new-file pl11/linux/mm/swap.c linux/mm/swap.c --- pl11/linux/mm/swap.c Sat Jul 3 01:09:13 1993 +++ linux/mm/swap.c Sat Aug 14 15:59:34 1993 @@ -37,7 +37,7 @@ struct inode * swap_file; unsigned int swap_device; unsigned char * swap_map; - char * swap_lockmap; + unsigned char * swap_lockmap; int pages; int lowest_bit; int highest_bit; @@ -53,7 +53,7 @@ #define NR_LAST_FREE_PAGES 32 static unsigned long last_free_pages[NR_LAST_FREE_PAGES] = {0,}; -#define SWAP_BITS 4096 +#define SWAP_BITS PAGE_SIZE void rw_swap_page(int rw, unsigned long entry, char * buf) { @@ -208,8 +208,7 @@ shm_no_page ((unsigned long *) table_ptr); return; } - page = get_free_page(GFP_KERNEL); - if (!page) { + if (!(page = get_free_page(GFP_KERNEL))) { oom(current); page = BAD_PAGE; } else @@ -222,7 +221,7 @@ swap_free(entry); } -static int try_to_swap_out(unsigned long * table_ptr) +static inline int try_to_swap_out(unsigned long * table_ptr) { int i; unsigned long page; @@ -240,10 +239,10 @@ return 0; } for (i = 0; i < NR_LAST_FREE_PAGES; i++) - if (last_free_pages[i] == (page & 0xfffff000)) + if (last_free_pages[i] == (page & PAGE_MASK)) return 0; if (PAGE_DIRTY & page) { - page &= 0xfffff000; + page &= PAGE_MASK; if (mem_map[MAP_NR(page)] != 1) return 0; if (!(entry = get_swap_page())) @@ -254,7 +253,7 @@ free_page(page); return 1; } - page &= 0xfffff000; + page &= PAGE_MASK; *table_ptr = 0; invalidate(); free_page(page); @@ -273,6 +272,136 @@ } /* + * A new implementation of swap_out(). We do not swap complete processes, + * but only a small number of blocks, before we continue with the next + * process. The number of blocks actually swapped is determined on the + * number of page faults, that this process actually had in the last time, + * so we won't swap heavily used processes all the time ... + * + * Note: the priority argument is a hint on much CPU to waste with the + * swap block search, not a hint, of how much blocks to swap with + * each process. + * + * (C) 1993 Kai Petzke, wpp@marie.physik.tu-berlin.de + */ +#ifdef NEW_SWAP +/* + * These are the miminum and maximum number of pages to swap from one process, + * before proceeding to the next: + */ +#define SWAP_MIN 4 +#define SWAP_MAX 32 + +/* + * The actual number of pages to swap is determined as: + * SWAP_RATIO / (number of recent major page faults) + */ +#define SWAP_RATIO 128 + +static int swap_out(unsigned int priority) +{ + static int swap_task; + int table; + int page; + long pg_table; + int loop; + int counter = NR_TASKS * 2 >> priority; + struct task_struct *p; + + counter = NR_TASKS * 2 >> priority; + for(; counter >= 0; counter--, swap_task++) { + /* + * Check that swap_task is suitable for swapping. If not, look for + * the next suitable process. + */ + loop = 0; + while(1) { + if(swap_task >= NR_TASKS) { + swap_task = 1; + if(loop) + /* all processes are unswappable or already swapped out */ + return 0; + loop = 1; + } + + p = task[swap_task]; + if(p && p->swappable && p->rss) + break; + + swap_task++; + } + + /* + * Determine the number of pages to swap from this process. + */ + if(! p -> swap_cnt) { + p->dec_flt = (p->dec_flt * 3) / 4 + p->maj_flt - p->old_maj_flt; + p->old_maj_flt = p->maj_flt; + + if(p->dec_flt >= SWAP_RATIO / SWAP_MIN) { + p->dec_flt = SWAP_RATIO / SWAP_MIN; + p->swap_cnt = SWAP_MIN; + } else if(p->dec_flt <= SWAP_RATIO / SWAP_MAX) + p->swap_cnt = SWAP_MAX; + else + p->swap_cnt = SWAP_RATIO / p->dec_flt; + } + + /* + * Go through process' page directory. + */ + for(table = p->swap_table; table < 1024; table++) { + pg_table = ((unsigned long *) p->tss.cr3)[table]; + if(pg_table >= high_memory) + continue; + if(mem_map[MAP_NR(pg_table)] & MAP_PAGE_RESERVED) + continue; + if(!(PAGE_PRESENT & pg_table)) { + printk("swap_out: bad page-table at pg_dir[%d]: %08x\n", + table, pg_table); + ((unsigned long *) p->tss.cr3)[table] = 0; + continue; + } + pg_table &= 0xfffff000; + + /* + * Go through this page table. + */ + for(page = p->swap_page; page < 1024; page++) { + switch(try_to_swap_out(page + (unsigned long *) pg_table)) { + case 0: + break; + + case 1: + p->rss--; + /* continue with the following page the next time */ + p->swap_table = table; + p->swap_page = page + 1; + if((--p->swap_cnt) == 0) + swap_task++; + return 1; + + default: + p->rss--; + break; + } + } + + p->swap_page = 0; + } + + /* + * Finish work with this process, if we reached the end of the page + * directory. Mark restart from the beginning the next time. + */ + p->swap_table = 0; + } + return 0; +} + +#else /* old swapping procedure */ + +/* * Go through the page tables, searching for a user page that * we can swap out. * @@ -303,7 +432,7 @@ goto check_task; } check_dir: - if (swap_table >= 1024) { + if (swap_table >= PTRS_PER_PAGE) { swap_table = 0; swap_task++; goto check_task; @@ -320,9 +449,9 @@ swap_table++; goto check_dir; } - pg_table &= 0xfffff000; + pg_table &= PAGE_MASK; check_table: - if (swap_page >= 1024) { + if (swap_page >= PTRS_PER_PAGE) { swap_page = 0; swap_table++; goto check_dir; @@ -336,6 +465,8 @@ goto check_table; } +#endif + static int try_to_free_page(void) { int i=6; @@ -358,7 +489,7 @@ */ static inline void add_mem_queue(unsigned long addr, unsigned long * queue) { - addr &= 0xfffff000; + addr &= PAGE_MASK; *(unsigned long *) addr = *queue; *queue = addr; } @@ -418,7 +549,7 @@ #define REMOVE_FROM_MEM_QUEUE(queue,nr) \ cli(); \ if ((result = queue) != 0) { \ - if (!(result & 0xfff) && result < high_memory) { \ + if (!(result & ~PAGE_MASK) && result < high_memory) { \ queue = *(unsigned long *) result; \ if (!mem_map[MAP_NR(result)]) { \ mem_map[MAP_NR(result)] = 1; \ @@ -492,7 +623,7 @@ p = task[nr]; if (!p) continue; - for (pgt = 0 ; pgt < 1024 ; pgt++) { + for (pgt = 0 ; pgt < PTRS_PER_PAGE ; pgt++) { ppage = pgt + ((unsigned long *) p->tss.cr3); page = *ppage; if (!page) @@ -501,8 +632,8 @@ continue; if (mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED) continue; - ppage = (unsigned long *) (page & 0xfffff000); - for (pg = 0 ; pg < 1024 ; pg++,ppage++) { + ppage = (unsigned long *) (page & PAGE_MASK); + for (pg = 0 ; pg < PTRS_PER_PAGE ; pg++,ppage++) { page = *ppage; if (!page) continue; @@ -511,8 +642,7 @@ if (SWP_TYPE(page) != type) continue; if (!tmp) { - tmp = get_free_page(GFP_KERNEL); - if (!tmp) + if (!(tmp = __get_free_page(GFP_KERNEL))) return -ENOMEM; goto repeat; } @@ -588,7 +718,7 @@ struct swap_info_struct * p; struct inode * swap_inode; unsigned int type; - char * tmp; + unsigned char * tmp; int i,j; if (!suser()) @@ -641,8 +771,8 @@ p->flags = 0; return -EINVAL; } - tmp = (char *) get_free_page(GFP_USER); - p->swap_lockmap = (char *) get_free_page(GFP_USER); + tmp = (unsigned char *) get_free_page(GFP_USER); + p->swap_lockmap = (unsigned char *) get_free_page(GFP_USER); if (!tmp || !p->swap_lockmap) { printk("Unable to start swapping: out of memory :-)\n"); free_page((long) tmp); @@ -655,8 +785,8 @@ p->flags = 0; return -ENOMEM; } - read_swap_page(SWP_ENTRY(type,0),tmp); - if (strncmp("SWAP-SPACE",tmp+4086,10)) { + read_swap_page(SWP_ENTRY(type,0), (char *) tmp); + if (memcmp("SWAP-SPACE",tmp+4086,10)) { printk("Unable to find swap-space signature\n"); free_page((long) tmp); free_page((long) p->swap_lockmap); @@ -668,7 +798,7 @@ p->flags = 0; return -EINVAL; } - memset(tmp+4086,0,10); + memset(tmp+PAGE_SIZE-10,0,10); j = 0; p->lowest_bit = 0; p->highest_bit = 0; @@ -696,9 +826,9 @@ if (test_bit(i,tmp)) tmp[i] = 0; else - tmp[i] = 128; - tmp[0] = 128; - p->swap_map = (unsigned char *) tmp; + tmp[i] = 0x80; + tmp[0] = 0x80; + p->swap_map = tmp; p->flags = SWP_WRITEOK; p->pages = j; nr_swap_pages += j; @@ -714,7 +844,7 @@ for (i = 0; i < nr_swapfiles; i++) { if (!(swap_info[i].flags & SWP_USED)) continue; - for (j = 0; j < 4096; ++j) + for (j = 0; j < SWAP_BITS; ++j) switch (swap_info[i].swap_map[j]) { case 128: continue; diff -u --recursive --new-file pl11/linux/net/inet/3c509.c linux/net/inet/3c509.c --- pl11/linux/net/inet/3c509.c +++ linux/net/inet/3c509.c Fri Aug 13 06:35:44 1993 @@ -0,0 +1,594 @@ +/* el3.c: An 3c509 EtherLink3 ethernet driver for linux. */ +/* + Written 1993 by Donald Becker. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. This software may be used and + distributed according to the terms of the GNU Public License, + incorporated herein by reference. + + This driver should work with the 3Com EtherLinkIII series. + + The author may be reached as becker@super.org or + C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 +*/ + +static char *version = "el3.c: v0.02 8/13/93 becker@super.org\n"; + +#include +#include +#include +#include +#include +#include +#include +#include +/*#include */ +#include +#ifndef port_read +#include "iow.h" +#endif + +#include "dev.h" +#include "eth.h" +#include "skbuff.h" +#include "arp.h" + +/* From auto_irq.c, should be in a *.h file. */ +extern void autoirq_setup(int waittime); +extern int autoirq_report(int waittime); +extern struct device *irq2dev_map[16]; + +/* These should be in . */ +#define port_read_l(port,buf,nr) \ +__asm__("cld;rep;insl": :"d" (port),"D" (buf),"c" (nr):"cx","di") +#define port_write_l(port,buf,nr) \ +__asm__("cld;rep;outsl": :"d" (port),"S" (buf),"c" (nr):"cx","si") + + +#ifdef EL3_DEBUG +int el3_debug = EL3_DEBUG; +#else +int el3_debug = 1; +#endif + +/* Offsets from base I/O address. */ +#define EL3_DATA 0x00 +#define EL3_CMD 0x0e +#define EL3_STATUS 0x0e +#define ID_PORT 0x100 +#define EEPROM_READ 0x80 + +/* Register window 1 offsets, used in normal operation. */ +#define TX_FREE 0x0C +#define TX_STATUS 0x0B +#define TX_FIFO 0x00 +#define RX_STATUS 0x08 +#define RX_FIFO 0x00 + +struct el3_private { + struct enet_statistics stats; +}; + +static int el3_init(struct device *dev); +static int read_eeprom(int index); +static int el3_open(struct device *dev); +static int el3_start_xmit(struct sk_buff *skb, struct device *dev); +static void el3_interrupt(int reg_ptr); +static void update_stats(int addr, struct device *dev); +static struct enet_statistics *el3_get_stats(struct device *dev); +static int el3_rx(struct device *dev); +static int el3_close(struct device *dev); + + + +int el3_probe(struct device *dev) +{ + short lrs_state = 0xff, i; + unsigned short iobase = 0; + + /* Send the ID sequence to the ID_PORT. */ + outb(0x00, ID_PORT); + outb(0x00, ID_PORT); + for(i = 0; i < 255; i++) { + outb(lrs_state, ID_PORT); + lrs_state <<= 1; + lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state; + } + + /* The current Space.c initialization makes it difficult to have more + than one adaptor initialized. Send me email if you have a need for + multiple adaptors. */ + + /* Read in EEPROM data. + Only the highest address board will stay on-line. */ + + { + short *phys_addr = (short *)dev->dev_addr; + phys_addr[0] = htons(read_eeprom(0)); + if (phys_addr[0] != 0x6000) + return 1; + phys_addr[1] = htons(read_eeprom(1)); + phys_addr[2] = htons(read_eeprom(2)); + } + + iobase = read_eeprom(8); + dev->irq = read_eeprom(9) >> 12; + + /* Activate the adaptor at the EEPROM location (if set), else 0x320. */ + + if (iobase == 0x0000) { + dev->base_addr = 0x320; + outb(0xf2, ID_PORT); + } else { + dev->base_addr = 0x200 + ((iobase & 0x1f) << 4); + outb(0xff, ID_PORT); + } + + outw(0x0800, dev->base_addr + EL3_CMD); /* Window 0. */ + printk("%s: 3c509 at %#3.3x key %4.4x iobase %4.4x.\n", + dev->name, dev->base_addr, inw(dev->base_addr), iobase); + + if (inw(dev->base_addr) == 0x6d50) { + el3_init(dev); + return 0; + } else + return -ENODEV; +} + +static int +read_eeprom(int index) +{ + int timer, bit, word = 0; + + /* Issue read command, and pause for at least 162 us. for it to complete. + Assume extra-fast 16Mhz bus. */ + outb(EEPROM_READ + index, ID_PORT); + + for (timer = 0; timer < 162*4 + 400; timer++) + SLOW_DOWN_IO; + + for (bit = 15; bit >= 0; bit--) + word = (word << 1) + (inb(ID_PORT) & 0x01); + + if (el3_debug > 3) + printk(" 3c509 EEPROM word %d %#4.4x.\n", index, word); + + return word; +} + +static int +el3_init(struct device *dev) +{ + struct el3_private *lp; + int ioaddr = dev->base_addr; + int i; + + printk("%s: EL3 at %#3x, address", dev->name, ioaddr); + + /* Read in the station address. */ + for (i = 0; i < 6; i++) + printk(" %2.2x", dev->dev_addr[i]); + printk(", IRQ %d.\n", dev->irq); + + /* Make up a EL3-specific-data structure. */ + dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL); + memset(dev->priv, 0, sizeof(struct el3_private)); + lp = (struct el3_private *)dev->priv; + + if (el3_debug > 1) + printk(version); + + /* The EL3-specific entries in the device structure. */ + dev->open = &el3_open; + dev->hard_start_xmit = &el3_start_xmit; + dev->stop = &el3_close; + dev->get_stats = &el3_get_stats; + + /* Fill in the generic field of the device structure. */ + for (i = 0; i < DEV_NUMBUFFS; i++) + dev->buffs[i] = NULL; + + dev->hard_header = eth_header; + dev->add_arp = eth_add_arp; + dev->queue_xmit = dev_queue_xmit; + dev->rebuild_header = eth_rebuild_header; + dev->type_trans = eth_type_trans; + + dev->type = ARPHRD_ETHER; + dev->hard_header_len = ETH_HLEN; + dev->mtu = 1500; /* eth_mtu */ + dev->addr_len = ETH_ALEN; + for (i = 0; i < dev->addr_len; i++) { + dev->broadcast[i]=0xff; + } + + /* New-style flags. */ + dev->flags = IFF_BROADCAST; + dev->family = AF_INET; + dev->pa_addr = 0; + dev->pa_brdaddr = 0; + dev->pa_mask = 0; + dev->pa_alen = sizeof(unsigned long); + + return 0; +} + + +static int +el3_open(struct device *dev) +{ + int ioaddr = dev->base_addr; + int i; + + if (request_irq(dev->irq, &el3_interrupt)) { + return -EAGAIN; + } + + if (el3_debug > 3) + printk("%s: Opening, IRQ %d status@%x %4.4x reg4 %4.4x.\n", + dev->name, dev->irq, ioaddr + EL3_STATUS, + inw(ioaddr + EL3_STATUS), inw(ioaddr + 4)); + outw(0x0800, ioaddr + EL3_CMD); /* Make certain we are in window 0. */ + + /* This is probably unnecessary. */ + outw(0x0001, ioaddr + 4); + + outw((dev->irq << 12) | 0x0f00, ioaddr + 8); + + irq2dev_map[dev->irq] = dev; + + /* Set the station address in window 2 each time opened. */ + outw(0x0802, ioaddr + EL3_CMD); + + for (i = 0; i < 6; i++) + outb(dev->dev_addr[i], ioaddr + i); + + outw(0x1000, ioaddr + EL3_CMD); /* Start the thinnet transceiver. */ + + outw(0x8005, ioaddr + EL3_CMD); /* Accept b-case and phys addr only. */ + outw(0xA800, ioaddr + EL3_CMD); /* Turn on statistics. */ + outw(0x2000, ioaddr + EL3_CMD); /* Enable the receiver. */ + outw(0x4800, ioaddr + EL3_CMD); /* Enable transmitter. */ + outw(0x78ff, ioaddr + EL3_CMD); /* Allow all status bits to be seen. */ + outw(0x7098, ioaddr + EL3_CMD); /* Set interrupt mask. */ + + /* Switch to register set 1 for normal use. */ + outw(0x0801, ioaddr + EL3_CMD); + + if (el3_debug > 3) + printk("%s: Opened 3c509 IRQ %d status %4.4x.\n", + dev->name, dev->irq, inw(ioaddr + EL3_STATUS)); + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + return 0; /* Always succeed */ +} + +static int +el3_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + int ioaddr = dev->base_addr; + + /* Transmitter timeout, serious problems. */ + if (dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 10) + return 1; + printk("%s: transmit timed out, tx_status %4.4x status %4.4x.\n", + dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS)); + dev->trans_start = jiffies; + /* Issue TX_RESET and TX_START commands. */ + outw(0x5800, ioaddr + EL3_CMD); /* TX_RESET */ + outw(0x4800, ioaddr + EL3_CMD); /* TX_START */ + dev->tbusy = 0; + } + + if (skb == NULL) { + dev_tint(dev); + return 0; + } + + /* Fill in the ethernet header. */ + if (!skb->arp && dev->rebuild_header(skb+1, dev)) { + skb->dev = dev; + arp_queue (skb); + return 0; + } + + if (skb->len <= 0) + return 0; + + if (el3_debug > 4) { + printk("%s: el3_start_xmit(lenght = %d) called, status %4.4x.\n", + dev->name, skb->len, inw(ioaddr + EL3_STATUS)); + } + + if (inw(ioaddr + EL3_STATUS) & 0x0001) { /* IRQ line active, missed one. */ + printk("%s: Missed interrupt, status %4.4x.\n", dev->name, + inw(ioaddr + EL3_STATUS)); + outw(0x7800, ioaddr + EL3_CMD); /* Fake interrupt trigger. */ + outw(0x6899, ioaddr + EL3_CMD); /* Ack IRQ */ + outw(0x78ff, ioaddr + EL3_CMD); /* Allow all status bits to be seen. */ + } + + /* Avoid timer-based retransmission conflicts. */ + dev->tbusy=1; + + /* Put out the doubleword header... */ + outw(skb->len, ioaddr + TX_FIFO); + outw(0x00, ioaddr + TX_FIFO); + /* ... and the packet rounded to a doubleword. */ + port_write(ioaddr + TX_FIFO, (void *)(skb+1), + ((skb->len + 3) >> 1) & ~0x1); + + dev->trans_start = jiffies; + if (skb->free) + kfree_skb (skb, FREE_WRITE); + + if (inw(ioaddr + TX_FREE) > 1536) { + dev->tbusy=0; + } else + /* Interrupt us when the FIFO has room for max-sized packet. */ + outw(0x9000 + 1536, ioaddr + EL3_CMD); + + if (el3_debug > 4) + printk(" Finished queueing packet, FIFO room remaining %d.\n", + inw(ioaddr + TX_FREE)); + /* Clear the Tx status stack. */ + { + short tx_status; + int i = 4; + + while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { + if (el3_debug > 5) + printk(" Tx status %4.4x.\n", tx_status); + if (tx_status & 0x38) lp->stats.tx_aborted_errors++; + if (tx_status & 0x30) outw(0x5800, ioaddr + EL3_CMD); + if (tx_status & 0x3C) outw(0x4800, ioaddr + EL3_CMD); + outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ + } + } + return 0; +} + +/* The EL3 interrupt handler. */ +static void +el3_interrupt(int reg_ptr) +{ + int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2); + struct device *dev = irq2dev_map[irq]; + int ioaddr, status; + + if (dev == NULL) { + printk ("el3_interrupt(): irq %d for unknown device.\n", irq); + return; + } + + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + dev->interrupt = 1; + + ioaddr = dev->base_addr; + status = inw(ioaddr + EL3_STATUS); + + if (el3_debug > 4) + printk("%s: interrupt, status %4.4x.\n", dev->name, status); + + while ((status = inw(ioaddr + EL3_STATUS)) & 0x01) { + + if (status & 0x08) { + if (el3_debug > 5) + printk(" TX room bit was handled.\n"); + /* There's room in the FIFO for a full-sized packet. */ + outw(0x6808, ioaddr + EL3_CMD); /* Ack IRQ */ + dev->tbusy = 0; + mark_bh(INET_BH); + } + if (status & 0x80) /* Statistics full. */ + update_stats(ioaddr, dev); + + if (status & 0x10) + el3_rx(dev); + + /* Clear the interrupts we've handled. */ + outw(0x6899, ioaddr + EL3_CMD); /* Ack IRQ */ + } + + if (el3_debug > 4) { + printk("%s: exiting interrupt, status %4.4x.\n", dev->name, + inw(ioaddr + EL3_STATUS)); + } + + if (inw(ioaddr + EL3_STATUS) & 0x01) { + int i = 100000; + printk("%s: exiting interrupt with status %4.4x.\n", dev->name, + inw(ioaddr + EL3_STATUS)); + while (i--) /* Delay loop to see the message. */ + inw(ioaddr + EL3_STATUS); + while ((inw(ioaddr + EL3_STATUS) & 0x0010) && i++ < 20) + outw(0x00, ioaddr + RX_STATUS); + } + + dev->interrupt = 0; + return; +} + + +static struct enet_statistics * +el3_get_stats(struct device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + + sti(); + update_stats(dev->base_addr, dev); + cli(); + return &lp->stats; +} + +/* Update statistics. We change to register window 6, so this + must be run single-threaded. */ +static void update_stats(int ioaddr, struct device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + + if (el3_debug > 5) + printk(" Updating the statistics.\n"); + /* Turn off statistics updates while reading. */ + outw(0xB000, ioaddr + EL3_CMD); + /* Switch to the stats window, and read everything. */ + outw(0x0806, ioaddr + EL3_CMD); + lp->stats.tx_carrier_errors += inb(ioaddr + 0); + lp->stats.tx_heartbeat_errors += inb(ioaddr + 1); + /* Multiple collisions. */ inb(ioaddr + 2); + lp->stats.collisions += inb(ioaddr + 3); + lp->stats.tx_window_errors += inb(ioaddr + 4); + lp->stats.rx_fifo_errors += inb(ioaddr + 5); + lp->stats.tx_packets += inb(ioaddr + 6); + lp->stats.rx_packets += inb(ioaddr + 7); + /* Tx deferrals */ inb(ioaddr + 8); + inw(ioaddr + 10); /* Total Rx and Tx octets. */ + inw(ioaddr + 12); + + /* Back to window 1, and turn statistics back on. */ + outw(0x0801, ioaddr + EL3_CMD); + outw(0xA800, ioaddr + EL3_CMD); + return; +} + +/* Print statistics on the kernel error output. */ +void printk_stats(struct enet_statistics *stats) +{ + + printk(" Ethernet statistics: Rx packets %6d Tx packets %6d.\n", + stats->rx_packets, stats->tx_packets); + printk(" Carrier errors: %6d.\n", stats->tx_carrier_errors); + printk(" Heartbeat errors: %6d.\n", stats->tx_heartbeat_errors); + printk(" Collisions: %6d.\n", stats->collisions); + printk(" Rx FIFO problems: %6d.\n", stats->rx_fifo_errors); + + return; +} + +static int +el3_rx(struct device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + int ioaddr = dev->base_addr; + short rx_status; + + if (el3_debug > 5) + printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n", + inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS)); + while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) { + if (rx_status & 0x4000) { /* Error, update stats. */ + short error = rx_status & 0x3C00; + lp->stats.rx_errors++; + switch (error) { + case 0x2000: lp->stats.rx_over_errors++; break; + case 0x2C00: lp->stats.rx_length_errors++; break; + case 0x3400: lp->stats.rx_crc_errors++; break; + case 0x2400: lp->stats.rx_length_errors++; break; + case 0x3000: lp->stats.rx_frame_errors++; break; + case 0x0800: lp->stats.rx_frame_errors++; break; + } + } + if ( (! (rx_status & 0x4000)) + || ! (rx_status & 0x2000)) { /* Dribble bits are OK. */ + short length = rx_status & 0x3ff; + int sksize = sizeof(struct sk_buff) + length + 3; + struct sk_buff *skb; + skb = (struct sk_buff *) kmalloc(sksize, GFP_ATOMIC); + + if (el3_debug > 4) + printk(" Receiving packet size %d status %4.4x.\n", + length, rx_status); + if (skb != NULL) { + skb->lock = 0; + skb->mem_len = sksize; + skb->mem_addr = skb; + /* 'skb+1' points to the start of sk_buff data area. */ + port_read(ioaddr+RX_FIFO, (void *)(skb+1), ((length + 3) >> 2) << 1); + if (dev_rint((unsigned char *)skb, length, IN_SKBUFF,dev)== 0){ + if (el3_debug > 6) + printk(" dev_rint() happy, status %4.4x.\n", + inb(ioaddr + EL3_STATUS)); + outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */ + while (inw(ioaddr + EL3_STATUS) & 0x1000) + printk(" Waiting for 3c509 to discard packet, status %x.\n", + inw(ioaddr + EL3_STATUS) ); + if (el3_debug > 6) + printk(" discarded packet, status %4.4x.\n", + inb(ioaddr + EL3_STATUS)); + continue; + } else { + printk("%s: receive buffers full.\n", dev->name); + kfree_s(skb, sksize); + } + } else if (el3_debug) + printk("%s: Couldn't allocate a sk_buff of size %d.\n", + dev->name, sksize); + } + lp->stats.rx_dropped++; + outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */ + while (inw(ioaddr + EL3_STATUS) & 0x1000) + printk(" Waiting for 3c509 to discard packet, status %x.\n", + inw(ioaddr + EL3_STATUS) ); + } + + if (el3_debug > 5) + printk(" Exiting rx_packet(), status %4.4x, rx_status %4.4x.\n", + inw(ioaddr+EL3_STATUS), inw(ioaddr+8)); + + return 0; +} + +static int +el3_close(struct device *dev) +{ + int ioaddr = dev->base_addr; + + if (el3_debug > 2) + printk("%s: Shutting down ethercard.\n", dev->name); + + dev->tbusy = 1; + dev->start = 0; + + /* Turn off statistics. We update lp->stats below. */ + outw(0xB000, ioaddr + EL3_CMD); + + /* Disable the receiver and transmitter. */ + outw(0x1800, ioaddr + EL3_CMD); + outw(0x5000, ioaddr + EL3_CMD); + + /* Turn off thinnet power. */ + outw(0xb800, ioaddr + EL3_CMD); + + if (el3_debug > 2) { + struct el3_private *lp = (struct el3_private *)dev->priv; + printk("%s: Status was %4.4x.\n", dev->name, inw(ioaddr + EL3_STATUS)); + printk_stats(&lp->stats); + } + + /* Free the interrupt line. */ + free_irq(dev->irq); + outw(0x1000, ioaddr + EL3_CMD); + outw(0x0f00, ioaddr + 8); + + /* Switch back to register window 0. */ + outw(0x0800, ioaddr + EL3_CMD); + + irq2dev_map[dev->irq] = 0; + + update_stats(ioaddr, dev); + return 0; +} + +/* + * Local variables: + * compile-command: "gcc -D__KERNEL__ -Wall -O6 -x c++ -c 3c509.c" + * End: + */ diff -u --recursive --new-file pl11/linux/net/inet/8390.c linux/net/inet/8390.c --- pl11/linux/net/inet/8390.c Sat Jul 3 11:58:27 1993 +++ linux/net/inet/8390.c Fri Aug 13 04:24:31 1993 @@ -1,12 +1,13 @@ /* 8390.c: A general NS8390 ethernet driver core for linux. */ /* - Written 1992,1993 by Donald Becker. This is alpha test code. + Written 1992,1993 by Donald Becker. + Copyright 1993 United States Government as represented by the Director, National Security Agency. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. - - This driver should work with many 8390-based ethernet adaptors. + + This is the chip-specific code for many 8390-based ethernet adaptors. The Author may be reached as becker@super.org or C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 @@ -13,7 +14,7 @@ */ static char *version = - "8390.c:v0.99-10 5/28/93 for 0.99.6+ Donald Becker (becker@super.org)\n"; + "8390.c:v0.99-12 8/9/93 for 0.99.12+ Donald Becker (becker@super.org)\n"; #include #if !defined(EL2) && !defined(NE2000) && !defined(WD80x3) && !defined(HPLAN) /* They don't know what they want -- give it all to them! */ @@ -31,15 +32,11 @@ statistics, and maybe read() and write() access to raw packets. This won't be done until after Linux 1.00. - This driver should support multiple, diverse boards simultaneousely. - This won't be done until after Linux 1.00. - Sources: The National Semiconductor LAN Databook, and the 3Com 3c503 databook. The NE* programming info came from the Crynwr packet driver, and figuring out that the those boards are similar to the NatSemi evaluation board described in AN-729. Thanks NS, no thanks to Novell/Eagle. - Cabletron provided only info I had already gotten from other sources -- hiss. */ #include @@ -60,7 +57,6 @@ #include "dev.h" #include "eth.h" -#include "timer.h" #include "ip.h" #include "protocol.h" #include "tcp.h" @@ -78,17 +74,15 @@ #ifdef EI_DEBUG int ei_debug = EI_DEBUG; #else -int ei_debug = 2; +int ei_debug = 1; #endif -struct device *irq2dev_map[16] = {0,0,0, /* zeroed...*/}; - /* Max number of packets received at one Intr. */ /*static int high_water_mark = 0;*/ /* Index to functions. */ /* Put in the device structure. */ -static int ei_open(struct device *dev); +int ei_open(struct device *dev); /* Dispatch from interrupts. */ void ei_interrupt(int reg_ptr); static void ei_tx_intr(struct device *dev); @@ -95,7 +89,6 @@ static void ei_receive(struct device *dev); static void ei_rx_overrun(struct device *dev); -int ethdev_init(struct device *dev); /* Routines generic to NS8390-based boards. */ void NS8390_init(struct device *dev, int startp); static void NS8390_trigger_send(struct device *dev, unsigned int length, @@ -113,7 +106,7 @@ up anew at each open, even though many of these registers should only need to be set once at boot. */ -static int +int ei_open(struct device *dev) { struct ei_device *ei_local = (struct ei_device *) dev->priv; @@ -195,7 +188,7 @@ return 1; } outb(0x00, e8390_base + EN0_IMR); - tmp_tbusy=dev->tbusy; + tmp_tbusy=dev->tbusy; dev->tbusy = 1; /* lock dev_tint() in dev.c */ ei_local->irqlock = 1; sti(); @@ -243,7 +236,8 @@ tmp_tbusy = 1; } else { dev->trans_start = jiffies; - ei_block_output(dev, length, (unsigned char *)(skb+1), ei_local->tx_start_page); + ei_block_output(dev, length, (unsigned char *)(skb+1), + ei_local->tx_start_page); NS8390_trigger_send(dev, send_length, ei_local->tx_start_page); tmp_tbusy = 1; } /* PINGPONG */ @@ -290,7 +284,7 @@ dev->interrupt = 1; sti(); /* Allow other interrupts. */ - + /* Change to page 0 and read the intr status reg. */ outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD); if (ei_debug > 3) @@ -315,9 +309,9 @@ ei_tx_intr(dev); } else if (interrupts & ENISR_COUNTERS) { struct ei_device *ei_local = (struct ei_device *) dev->priv; - ei_local->soft_rx_errors += inb_p(e8390_base + EN0_COUNTER0); - ei_local->soft_rx_errors += inb_p(e8390_base + EN0_COUNTER1); - ei_local->missed_packets += inb_p(e8390_base + EN0_COUNTER2); + ei_local->stat.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0); + ei_local->stat.rx_crc_errors += inb_p(e8390_base + EN0_COUNTER1); + ei_local->stat.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2); outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */ } @@ -347,10 +341,6 @@ struct ei_device *ei_local = (struct ei_device *) dev->priv; outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */ - if ((status & ENTSR_PTX) == 0) - ei_local->tx_errors++; - else - ei_local->tx_packets++; if (ei_local->pingpong) { ei_local->txqueue--; @@ -389,6 +379,25 @@ ei_local->txing = 0; dev->tbusy = 0; } + + /* Do the statistics _after_ we start the next TX. */ + if (status & ENTSR_PTX) + ei_local->stat.tx_packets++; + else + ei_local->stat.tx_errors++; + if (status & ENTSR_COL) + ei_local->stat.collisions++; + if (status & ENTSR_ABT) + ei_local->stat.tx_aborted_errors++; + if (status & ENTSR_CRS) + ei_local->stat.tx_carrier_errors++; + if (status & ENTSR_FU) + ei_local->stat.tx_fifo_errors++; + if (status & ENTSR_CDH) + ei_local->stat.tx_heartbeat_errors++; + if (status & ENTSR_OWC) + ei_local->stat.tx_window_errors++; + mark_bh (INET_BH); } @@ -450,19 +459,19 @@ dev->name, rx_frame.status, rx_frame.next, rx_frame.count, current_offset); - if (ei_local->rx_packets != last_rx_bogosity) { + if (ei_local->stat.rx_packets != last_rx_bogosity) { /* Maybe we can avoid resetting the chip... empty the packet ring. */ ei_local->current_page = rxing_page; printk("%s: setting next frame to %#2x (nxt=%#2x, rx_frm.nx=%#2x rx_frm.stat=%#2x).\n", dev->name, ei_local->current_page, next_frame, rx_frame.next, rx_frame.status); - last_rx_bogosity = ei_local->rx_packets; + last_rx_bogosity = ei_local->stat.rx_packets; outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY); continue; } else { /* Oh no Mr Bill! Last ditch error recovery. */ printk("%s: recovery failed, resetting at packet #%d..", - dev->name, ei_local->rx_packets); + dev->name, ei_local->stat.rx_packets); sti(); ei_reset_8390(dev); NS8390_init(dev, 1); @@ -488,6 +497,7 @@ current_offset + sizeof(rx_frame)); if (dev_rint((unsigned char *)skb, size, IN_SKBUFF, dev)) { printk("%s: receive buffers full.\n", dev->name); + kfree_s(skb, sksize); break; } } else if (ei_debug) { @@ -495,13 +505,14 @@ dev->name, sksize); break; } - ei_local->rx_packets++; + ei_local->stat.rx_packets++; } else { + int errs = rx_frame.status; if (ei_debug) printk("%s: bogus packet, status=%#2x nxpg=%#2x size=%d\n", dev->name, rx_frame.status, rx_frame.next, rx_frame.count); - ei_local->soft_rx_err_bits |= rx_frame.status, - ei_local->soft_rx_errors++; + if (errs & ENRSR_FO) + ei_local->stat.rx_fifo_errors++; } next_frame = rx_frame.next; @@ -537,7 +548,7 @@ if (ei_debug) printk("%s: Receiver overrun.\n", dev->name); - ei_local->rx_overruns++; + ei_local->stat.rx_over_errors++; /* The we.c driver does dummy = inb_p( RBCR[01] ); at this point. It might mean something -- magic to speed up a reset? A 8390 bug?*/ @@ -553,13 +564,9 @@ return; }; - { - int old_rx_packets = ei_local->rx_packets; - /* Remove packets right away. */ - ei_receive(dev); - ei_local->rx_overrun_packets += - (ei_local->rx_packets - old_rx_packets); - } + /* Remove packets right away. */ + ei_receive(dev); + outb_p(0xff, e8390_base+EN0_ISR); /* Generic 8390 insns to start up again, same as in open_8390(). */ outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD); @@ -566,46 +573,35 @@ outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */ } +static struct enet_statistics * +get_stats(struct device *dev) +{ + struct ei_device *ei_local = (struct ei_device *) dev->priv; + return &ei_local->stat; +} + int ethif_init(struct device *dev) { - struct ei_device *ei_local; - - if (ei_debug > 1) - printk(version); - - /* The open call may be overridden by the card-specific code. */ - dev->open = &ei_open; - - /* Make up a ei_local structure. */ - dev->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL); - memset(dev->priv, 0, sizeof(struct ei_device)); - ei_local = (struct ei_device *)dev->priv; -#ifndef NO_PINGPONG - ei_local->pingpong = 1; -#endif - if (1 #ifdef WD80x3 && ! wdprobe(dev->base_addr, dev) -#endif +#endif #ifdef EL2 && ! el2autoprobe(dev->base_addr, dev) #endif #ifdef NE2000 && ! neprobe(dev->base_addr, dev) -#endif +#endif #ifdef HPLAN && ! hpprobe(dev->base_addr, dev) -#endif +#endif && 1 ) { - printk("No ethernet device found.\n"); - kfree(dev->priv); - dev->priv = NULL; - return 1; /* ENODEV or EAGAIN would be more accurate. */ + return 1; /* -ENODEV or -EAGAIN would be more accurate. */ } - - return ethdev_init(dev); + if (ei_debug > 1) + printk(version); + return 0; } /* Initialize the rest of the device structure. */ @@ -617,6 +613,11 @@ for (i = 0; i < DEV_NUMBUFFS; i++) dev->buffs[i] = NULL; + /* The open call may be overridden by the card-specific code. */ + if (dev->open == NULL) + dev->open = &ei_open; + + dev->get_stats = get_stats; dev->hard_header = eth_header; dev->add_arp = eth_add_arp; dev->queue_xmit = dev_queue_xmit; @@ -624,8 +625,14 @@ dev->type_trans = eth_type_trans; if (dev->priv == NULL) { + struct ei_device *ei_local; + dev->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL); memset(dev->priv, 0, sizeof(struct ei_device)); + ei_local = (struct ei_device *)dev->priv; +#ifndef NO_PINGPONG + ei_local->pingpong = 1; +#endif } dev->hard_start_xmit = &ei_start_xmit; @@ -727,7 +734,7 @@ /* * Local variables: - * compile-command: "gcc -DKERNEL -Wall -O6 -I/usr/src/linux/net/tcp -c 8390.c" + * compile-command: "gcc -D__KERNEL__ -Wall -O6 -x c++ -c 8390.c" * version-control: t * kept-new-versions: 5 * End: diff -u --recursive --new-file pl11/linux/net/inet/8390.h linux/net/inet/8390.h --- pl11/linux/net/inet/8390.h Sat Jul 3 00:06:26 1993 +++ linux/net/inet/8390.h Fri Aug 13 04:24:32 1993 @@ -1,11 +1,14 @@ /* Generic NS8390 register definitions. */ /* This file is part of Donald Becker's 8390 drivers, and is distributed under the same license. - Some of these names and comments are from the Crynwr packet drivers. */ + Some of these names and comments originated from the Crynwr + packet drivers, which are distributed under the GPL. */ -#ifndef e8390_h -#define e8390_h +#ifndef _8390_h +#define _8390_h +#include + #define TX_2X_PAGES 12 #define TX_1X_PAGES 6 #define TX_PAGES (ei_status.pingpong ? TX_2X_PAGES : TX_1X_PAGES) @@ -13,8 +16,17 @@ #define ETHER_ADDR_LEN 6 /* From 8390.c */ -void ei_interrupt(int reg_ptr); +extern int ei_debug; +extern struct sigaction ei_sigaction; + +extern int ethif_init(struct device *dev); +extern int ethdev_init(struct device *dev); +extern void NS8390_init(struct device *dev, int startp); +extern int ei_open(struct device *dev); +extern void ei_interrupt(int reg_ptr); + /* From auto_irq.c */ +extern struct device *irq2dev_map[16]; extern void autoirq_setup(int waittime); extern int autoirq_report(int waittime); @@ -39,15 +51,11 @@ unsigned char in_interrupt; short tx1, tx2; /* Packet lengths for ping-pong tx. */ short lasttx; /* Alpha version consistency check. */ - /* The statistics: these are returned from the ioctl() as a block. */ - int tx_packets; - int tx_errors; - int rx_packets; - int soft_rx_errors; - int soft_rx_err_bits; - int missed_packets; - int rx_overruns; - int rx_overrun_packets; + unsigned char reg0; /* Register '0' in a WD8013 */ + unsigned char reg5; /* Register '5' in a WD8013 */ + unsigned char saved_irq; /* Original dev->irq value. */ + /* The new statistics table. */ + struct enet_statistics stat; }; #define ei_status (*(struct ei_device *)(dev->priv)) @@ -130,16 +138,14 @@ #define ENRSR_DEF 0x80 /* deferring */ /* Transmitted packet status, EN0_TSR. */ -#define ENTSR_PTX 0x01 /* Packet transmitted without error */ -/* The other bits in the TX status register mean: - 0x02 The transmit wasn't deferred. - 0x04 The transmit collided at least once. - 0x08 The transmit collided 16 times, and was deferred. - 0x10 The carrier sense was lost (from the ethernet transceiver) - 0x20 A "FIFO underrun" (internal error) occured during transmit. - 0x40 The collision detect "heartbeat" signal was lost. - 0x80 There was an out-of-window collision. - */ +#define ENTSR_PTX 0x01 /* Packet transmitted without error */ +#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */ +#define ENTSR_COL 0x04 /* The transmit collided at least once. */ +#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */ +#define ENTSR_CRS 0x10 /* The carrier sense was lost. */ +#define ENTSR_FU 0x20 /* A "FIFO underrun" occured during transmit. */ +#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */ +#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */ /* The per-packet-header format. */ struct e8390_pkt_hdr { @@ -147,4 +153,4 @@ unsigned char next; /* pointer to next packet. */ unsigned short count; /* header + packet lenght in bytes */ }; -#endif /* e8390_h */ +#endif /* _8390_h */ diff -u --recursive --new-file pl11/linux/net/inet/CONFIG linux/net/inet/CONFIG --- pl11/linux/net/inet/CONFIG Sun Jul 18 14:06:52 1993 +++ linux/net/inet/CONFIG Fri Aug 6 20:46:27 1993 @@ -26,8 +26,18 @@ # the shared memory (WD_SHMEM=xxxx). All other supported cards # behave like they should, you can leave the values to 0. -FvK # -CARDS = -DSLIP -DPLIP -DWD80x3 -DNE2000 -DHPLAN -DEL2 -DD_LINK +# Comment out the lines you don't want.. + +#CARDS := $(CARDS) -DSLIP +#CARDS := $(CARDS) -DPLIP +CARDS := $(CARDS) -DWD80x3 +CARDS := $(CARDS) -DNE2000 +CARDS := $(CARDS) -DHPLAN +CARDS := $(CARDS) -DEL2 +CARDS := $(CARDS) -DD_LINK +CARDS := $(CARDS) -DCONFIG_AT1500 + # For WD and SMC cards: #OPTS = -DEI8390=0x280 -DEI8390_IRQ=15 #WD_OPTS = -DWD_SHMEM=0xCC000 -UFORCE_8BIT @@ -38,9 +48,10 @@ #OPTS = -DEI8390=0 -DEI8390_IRQ=0 #WD_OPTS = -DUD_SHMEM=0xCC000 -UFORCE_8BIT -EL_OPTS = -UEL2_AUI +EL2_OPTS = #-UEL2_AUI NE_OPTS = HP_OPTS = PLIP_OPTS = SLIP_OPTS = -DSL_DUMP -DSL_COMPRESSED DL_OPTS = -DD_LINK_IO=0x378 -DD_LINK_IRQ=7 -UD_LINK_DEBUG +AT_OPTS = # -DLANCE_DMA=5 diff -u --recursive --new-file pl11/linux/net/inet/LICENSE.SRC linux/net/inet/LICENSE.SRC --- pl11/linux/net/inet/LICENSE.SRC +++ linux/net/inet/LICENSE.SRC Wed Aug 4 22:36:53 1993 @@ -0,0 +1,15 @@ +Code in this directory written at the IDA Supercomputing Research Center +carries the following copyright and license. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. This software may be used + and distributed according to the terms of the GNU Public License, + incorporated herein by reference. + + In addition to the disclaimers in the GPL, SRC expressly disclaims any + and all warranties, expressed or implied, concerning the enclosed software. + This software was developed at SRC for use in internal research, and the + intent in sharing this software is to promote the productive interchange + of ideas throughout the research community. All software is furnished + on an "as-is" basis. No further updates to this software should be + expected. Although updates may occur, no commitment exists. diff -u --recursive --new-file pl11/linux/net/inet/Makefile linux/net/inet/Makefile --- pl11/linux/net/inet/Makefile Fri Jul 2 18:38:39 1993 +++ linux/net/inet/Makefile Fri Aug 6 20:44:14 1993 @@ -18,8 +18,9 @@ OBJS = Space.o sock.o utils.o route.o proc.o timer.o protocol.o loopback.o \ - eth.o packet.o arp.o dev.o 8390.o wd.o ne.o el.o hp.o plip.o \ - slip.o slhc.o d_link.o auto_irq.o ip.o raw.o icmp.o tcp.o udp.o + eth.o packet.o arp.o dev.o 8390.o wd.o ne.o el2.o hp.o plip.o \ + slip.o slhc.o d_link.o auto_irq.o ip.o raw.o icmp.o tcp.o udp.o\ + lance.o ifdef CONFIG_INET @@ -33,6 +34,7 @@ endif +CARDS := include CONFIG @@ -46,8 +48,8 @@ wd.o: CONFIG wd.c Makefile $(CC) $(CPPFLAGS) $(CFLAGS) $(WD_OPTS) -c wd.c -o $@ -el.o: CONFIG el.c elreg.h Makefile - $(CC) $(CPPFLAGS) $(CFLAGS) $(EL_OPTS) -c el.c -o $@ +el2.o: CONFIG el2.c el2reg.h Makefile + $(CC) $(CPPFLAGS) $(CFLAGS) $(EL2_OPTS) -c el2.c -o $@ ne.o: CONFIG ne.c Makefile $(CC) $(CPPFLAGS) $(CFLAGS) $(NE_OPTS) -c ne.c -o $@ @@ -63,6 +65,9 @@ d_link.o: CONFIG d_link.c Makefile $(CC) $(CPPFLAGS) $(CFLAGS) $(DL_OPTS) -c d_link.c -o $@ + +lance.o: CONFIG lance.c Makefile + $(CC) $(CPPFLAGS) $(CFLAGS) $(AT_OPTS) -c lance.c -o $@ subdirs: dummy for i in $(SUBDIRS); do (cd $$i; $(MAKE)); done diff -u --recursive --new-file pl11/linux/net/inet/README.8390 linux/net/inet/README.8390 --- pl11/linux/net/inet/README.8390 Sat May 29 12:29:07 1993 +++ linux/net/inet/README.8390 Wed Aug 4 22:36:53 1993 @@ -1,37 +1,60 @@ -Installation Directions: -EMail me (becker@super.org) telling me which version you have gotten. -I need to know how many people have tried, succeeded and -failed before this is released as part of the official Linux. - -Use Linux 0.99.5 or later. Make certain you can make a working kernel -_before_ you install the ethercard driver. - -Put the all of the files into linux/net/tcp/. You'll need all of the -files in this directory. -GNUmakefile 8390.c 8390.h auto_irq.c Space.c wd.c ne.c 3c503.[ch] and hp.c. -Space.c is the only tricky one -- it overwrites the old Space.c. -Stock versions of Space.c leave the "we" driver enabled and will not work. - -Change the GNUmakefile to reflect your configuration. Use the guide at -the end of these instructions and in the README file. Note that the -'GNUmakefile' name is magic; it is loaded in preference to 'Makefile'. - -Make and install your new kernel. - -To actually use this driver you must get the TCP/IP package and edit -your /usr/etc/inet/rc.net file to config whatever you named your -ethernet device. (You can edit the GNUmakefile to use something -besides the "eth0" name. Note that the default name has changed to -the now-standard "eth0".) - -If you try to 'config' an interface that doesn't exist your kernel -will report "invalid ioctl()" for anthing that tries to use the card. -Note that the ethercard devices aren't (yet) '/dev/eth0' devices -- -they only exist in the socket namespace and thus you don't need to -'mknode' them. +These drivers support all common 8390-based ethernet boards. Currently +"common" is defined as: -________________ + 3Com Products: +* 3Com 3c503 Board loaned by Chance Reschke, USRA.edu (thanks!) + 3Com 3c503/16 and excellent documentation provided by 3Com. + + Clones-n-things + NE1000 Novell and Eagle are useless for documentation, +* NE2000 but copied the designs directly from NatSemi;->. + + WD/SMC products + WD8003 +* WD8013 Board loaned by Russ Nelson, Crynwr Software. Thanks! + +* I've seen it work myself! + +There is support for the following boards, but since I've only been +able to borrow a thinnet of an HP ethercard I've relied upon reports +from others: + + HP LAN adaptors +** HP27245 +** HP27247 +** HP27250 + +Thanks are due to the dozens of alpha testers. Special thanks are due +to Chance Reschke <@usra.edu> and Russ Nelson <@crynwr.com> for +loaning me ethercards. + +The following addresses are autoprobed, in this order: +wd.c: 0x300, 0x280, 0x380, 0x240 +3c503: 0x300, 0x310, 0x330, 0x350, 0x250, 0x280, 0x2a0, 0x2e0 +ne.c: 0x300, 0x280, 0x320, 0x340, 0x360 +hp.c: 0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240 + +80x3 clones that are reported to work: + LANNET LEC-45 + +"NE2000" clones that are reported to work: + Alta Combo(NE2000 clone) + Aritsoft LANtastic AE-2 (NE2000 clone w/ extra memory) + Asante Etherpak 2001/2003 + D-Link Ethernet II + LTC E-NET/16 P/N: 8300-200-002 (lipka@lip.hanse.de) + Network Solutions HE-203 + SVEC 4 Dimension Ethernet + 4-Dimension FD0490 EtherBoard16 + Cabletron products: + E1010 No ID PROM and sketchy info from Ctron means you'll + E1010-x have to compile-in information about your board. + E2010 + E2010-x + N.B. The E2100 will not work with Linux until Cabletron + releases the programming information! + Important defines For Space.c diff -u --recursive --new-file pl11/linux/net/inet/README.DRIVERS linux/net/inet/README.DRIVERS --- pl11/linux/net/inet/README.DRIVERS Sat May 29 12:29:07 1993 +++ linux/net/inet/README.DRIVERS Sat Jul 31 15:42:22 1993 @@ -48,7 +48,6 @@ #include "inet.h" #include "dev.h" #include "eth.h" -#include "timer.h" #include "ip.h" #include "route.h" #include "protocol.h" diff -u --recursive --new-file pl11/linux/net/inet/Space.c linux/net/inet/Space.c --- pl11/linux/net/inet/Space.c Sat Jul 3 12:51:18 1993 +++ linux/net/inet/Space.c Fri Aug 13 04:27:58 1993 @@ -13,7 +13,7 @@ * field of the 'device' structure to store the unit number... * -FvK * - * Version: @(#)Space.c 1.0.7 05/28/93 + * Version: @(#)Space.c 1.0.7 08/12/93 * * Authors: Ross Biro, * Fred N. van Kempen, @@ -28,14 +28,12 @@ #include #include "dev.h" - #define LOOPBACK /* always present, right? */ - #define NEXT_DEV NULL -#ifdef D_LINK +#if defined(D_LINK) || defined(CONFIG_DE600) extern int d_link_init(struct device *); static struct device d_link_dev = { "dl0", @@ -53,30 +51,79 @@ # define NEXT_DEV (&d_link_dev) #endif - -#ifdef EL1 +#ifdef CONFIG_EL1 +#error # ifndef EL1_IRQ # define EL1_IRQ 9 # endif +# ifndef EL1 +# define EL1 0 +# endif extern int el1_init(struct device *); static struct device el1_dev = { - "el0", - 0, - 0, - 0, - 0, - EL1, - EL1_IRQ, - 0, 0, 0, - NEXT_DEV, - el1_init + "el0", 0, 0, 0, 0, EL1, EL1_IRQ, 0, 0, 0, NEXT_DEV, el1_init }; # undef NEXT_DEV # define NEXT_DEV (&el1_dev) #endif /* EL1 */ -#if defined(EI8390) || defined(EL2) || defined(NE2000) \ - || defined(WD80x3) || defined(HPLAN) +#ifdef CONFIG_DEPCA + extern int depca_probe(struct device *); + static struct device depca_dev = { + "depca0", 0,0,0,0, 0, 0, 0, 0, 0, NEXT_DEV, depca_probe, + }; +# undef NEXT_DEV +# define NEXT_DEV (&depca_dev) +#endif /* CONFIG_DEPCA */ + + +#ifdef CONFIG_ATP /* AT-LAN-TEC (RealTek) pocket adaptor. */ + extern int atp_probe(struct device *); + static struct device atp_dev = { + "atp0", 0,0,0,0, 0, 0, 0, 0, 0, NEXT_DEV, atp_probe, + }; +# undef NEXT_DEV +# define NEXT_DEV (&atp_dev) +#endif /* CONFIG_ATP */ + +#ifdef CONFIG_EL3 + extern int el3_probe(struct device *); + static struct device eliii0_dev = { + "eliii0", 0,0,0,0, 0, 0, 0, 0, 0, NEXT_DEV, el3_probe, + }; +# undef NEXT_DEV +# define NEXT_DEV (&eliii0_dev) +#endif /* CONFIG_3C509 aka EL3 */ + +#ifdef CONFIG_ZNET + extern int znet_probe(struct device *); + static struct device znet_dev = { + "znet", 0,0,0,0, 0, 0, 0, 0, 0, NEXT_DEV, znet_probe, }; +# undef NEXT_DEV +# define NEXT_DEV (&znet_dev) +#endif /* CONFIG_ZNET */ + +#ifdef CONFIG_EEXPRESS + extern int express_probe(struct device *); + static struct device express0_dev = { + "exp0", 0,0,0,0, 0, 0, 0, 0, 0, NEXT_DEV, express_probe, }; +# undef NEXT_DEV +# define NEXT_DEV (&express0_dev) +#endif /* CONFIG_EEPRESS */ + +#ifdef CONFIG_AT1500 + extern int at1500_probe(struct device *); + static struct device lance_dev = { + "le0", + 0,0,0,0, 0 /* I/O Base */, 0 /* pre-set IRQ */, + 0, 0, 0, NEXT_DEV, at1500_probe, + }; +# undef NEXT_DEV +# define NEXT_DEV (&lance_dev) +#endif /* AT1500BT */ + +#if defined(EI8390) || defined(CONFIG_EL2) || defined(CONFIG_NE2000) \ + || defined(CONFIG_WD80x3) || defined(CONFIG_HPLAN) # ifndef EI8390 # define EI8390 0 # endif @@ -100,50 +147,19 @@ # define NEXT_DEV (&ei8390_dev) #endif /* The EI8390 drivers. */ -#ifdef PLIP +#if defined(PLIP) || defined(CONFIG_PLIP) extern int plip_init(struct device *); static struct device plip2_dev = { - "plip2", - 0, - 0, - 0, - 0, - 0x278, - 2, - 0, 0, 0, - NEXT_DEV, - plip_init - }; + "plip2", 0, 0, 0, 0, 0x278, 2, 0, 0, 0, NEXT_DEV, plip_init, }; static struct device plip1_dev = { - "plip1", - 0, - 0, - 0, - 0, - 0x378, - 7, - 0, 0, 0, - &plip2_dev, - plip_init - }; + "plip1", 0, 0, 0, 0, 0x378, 7, 0, 0, 0, &plip2_dev, plip_init, }; static struct device plip0_dev = { - "plip0", - 0, - 0, - 0, - 0, - 0x3BC, - 5, - 0, 0, 0, - &plip1_dev, - plip_init - }; + "plip0", 0, 0, 0, 0, 0x3BC, 5, 0, 0, 0, &plip1_dev, plip_init, }; # undef NEXT_DEV # define NEXT_DEV (&plip0_dev) #endif /* PLIP */ - -#ifdef SLIP +#if defined(SLIP) || defined(CONFIG_SLIP) extern int slip_init(struct device *); static struct device slip3_dev = { "sl3", /* Internal SLIP driver, channel 3 */ diff -u --recursive --new-file pl11/linux/net/inet/arp.c linux/net/inet/arp.c --- pl11/linux/net/inet/arp.c Sat Jul 3 11:50:55 1993 +++ linux/net/inet/arp.c Sat Jul 31 15:42:22 1993 @@ -39,7 +39,6 @@ #include #include #include "inet.h" -#include "timer.h" #include "dev.h" #include "eth.h" #include "ip.h" @@ -410,7 +409,7 @@ apt->htype = htype; apt->flags = (ATF_INUSE | ATF_COM); /* USED and COMPLETED entry */ memcpy(apt->ha, addr, hlen); - apt->last_used = timer_seq; + apt->last_used = jiffies; cli(); apt->next = arp_tables[hash]; arp_tables[hash] = apt; @@ -469,7 +468,7 @@ memcpy(tbl->ha, ptr, arp->ar_hln); tbl->hlen = arp->ar_hln; tbl->flags |= ATF_COM; - tbl->last_used = timer_seq; + tbl->last_used = jiffies; } else { memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln); if (chk_addr(dst) != IS_MYADDR) { @@ -531,6 +530,7 @@ skb = (struct sk_buff *) kmalloc(sizeof(struct sk_buff) + sizeof(struct arphdr) + (2 * dev->addr_len) + + dev->hard_header_len + (2 * 4 /* arp->plen */), GFP_ATOMIC); if (skb == NULL) { printk("ARP: No memory available for REQUEST %s\n", in_ntoa(paddr)); @@ -603,8 +603,8 @@ * verify the address for us. */ if ((!(apt->flags & ATF_PERM)) || - (!before(apt->last_used, timer_seq+ARP_TIMEOUT) && apt->hlen != 0)) { - apt->last_used = timer_seq; + (!before(apt->last_used, jiffies+ARP_TIMEOUT) && apt->hlen != 0)) { + apt->last_used = jiffies; memcpy(haddr, apt->ha, dev->addr_len); return(0); } else { @@ -647,7 +647,7 @@ apt = arp_lookup(addr); if (apt != NULL) { DPRINTF((DBG_ARP, "ARP: updating entry for %s\n", in_ntoa(addr))); - apt->last_used = timer_seq; + apt->last_used = jiffies; memcpy(apt->ha, haddr , dev->addr_len); return; } @@ -789,7 +789,7 @@ /* We now have a pointer to an ARP entry. Update it! */ memcpy((char *) &apt->ha, (char *) &r.arp_ha.sa_data, hlen); - apt->last_used = timer_seq; + apt->last_used = jiffies; apt->flags = r.arp_flags; return(0); diff -u --recursive --new-file pl11/linux/net/inet/auto_irq.c linux/net/inet/auto_irq.c --- pl11/linux/net/inet/auto_irq.c Sat May 29 12:29:07 1993 +++ linux/net/inet/auto_irq.c Fri Aug 13 04:24:27 1993 @@ -27,7 +27,7 @@ #ifdef version -static char *version="auto_irq.c:v0.01 1993 Donald Becker (becker@super.org)"; +static char *version="auto_irq.c:v0.02 1993 Donald Becker (becker@super.org)"; #endif /*#include */ @@ -35,7 +35,10 @@ #include #include #include +#include "dev.h" /*#include */ + +struct device *irq2dev_map[16] = {0, 0, /* ... zeroed */}; int irqs_busy = 0x01; /* The set of fixed IRQs always enabled */ int irqs_used = 0x01; /* The set of fixed IRQs sometimes enabled. */ diff -u --recursive --new-file pl11/linux/net/inet/d_link.c linux/net/inet/d_link.c --- pl11/linux/net/inet/d_link.c Sat Jul 3 12:05:51 1993 +++ linux/net/inet/d_link.c Sat Jul 31 15:42:22 1993 @@ -93,7 +93,6 @@ #include "inet.h" #include "dev.h" #include "eth.h" -#include "timer.h" #include "ip.h" #include "route.h" #include "protocol.h" diff -u --recursive --new-file pl11/linux/net/inet/dev.c linux/net/inet/dev.c --- pl11/linux/net/inet/dev.c Sun Jul 18 16:06:48 1993 +++ linux/net/inet/dev.c Sat Aug 14 14:32:05 1993 @@ -32,7 +32,6 @@ #include "inet.h" #include "dev.h" #include "eth.h" -#include "timer.h" #include "ip.h" #include "route.h" #include "protocol.h" @@ -253,30 +252,21 @@ /* Find an interface that can handle addresses for a certain address. */ struct device * -dev_check(int which, unsigned long addr) +dev_check(unsigned long addr) { struct device *dev; - for(dev = dev_base; dev != NULL; dev = dev->next) { - if ((dev->flags & IFF_UP) == 0) continue; - switch(which) { - case 0: /* local address */ - if (ip_addr_match(addr & dev->pa_mask, dev->pa_addr)) - return(dev); - break; - case 1: /* p-p destination address */ - if ((dev->flags & IFF_POINTOPOINT) && - ip_addr_match(addr & dev->pa_mask, dev->pa_dstaddr)) - return(dev); - break; - case 2: /* broadcast address */ - if ((dev->flags & IFF_BROADCAST) && - ip_addr_match(addr, dev->pa_brdaddr)) - return(dev); - break; - } - } - return(NULL); + for (dev = dev_base; dev; dev = dev->next) + if ((dev->flags & IFF_UP) && (dev->flags & IFF_POINTOPOINT) && + (addr == dev->pa_dstaddr)) + return dev; + for (dev = dev_base; dev; dev = dev->next) + if ((dev->flags & IFF_UP) && !(dev->flags & IFF_POINTOPOINT) && + addr == (dev->flags & IFF_LOOPBACK ? dev->pa_addr : dev->pa_addr & + dev->pa_mask)) + break; + /* no need to check broadcast addresses */ + return dev; } diff -u --recursive --new-file pl11/linux/net/inet/dev.h linux/net/inet/dev.h --- pl11/linux/net/inet/dev.h Fri Jul 2 23:51:27 1993 +++ linux/net/inet/dev.h Fri Aug 13 04:24:31 1993 @@ -5,11 +5,12 @@ * * Definitions for the Interfaces handler. * - * Version: @(#)dev.h 1.0.9 05/31/93 + * Version: @(#)dev.h 1.0.10 08/12/93 * * Authors: Ross Biro, * Fred N. van Kempen, * Corey Minyard + * Donald J. Becker, * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -20,6 +21,7 @@ #define _DEV_H #include +#include /* for future expansion when we will have different priorities. */ @@ -73,6 +75,13 @@ /* The device initialization function. Called only once. */ int (*init)(struct device *dev); + /* Some hardware also needs these fields, but they are not part of the + usual set specified in Space.c. */ + unsigned char if_port; /* Selectable AUI, TP,..*/ + unsigned char dma; /* DMA channel */ + + struct enet_statistics* (*get_stats)(struct device *dev); + /* * This marks the end of the "visible" part of the structure. All * fields hereafter are internal to the system, and may change at @@ -79,12 +88,15 @@ * will (read: may be cleaned up at will). */ + /* These may be needed for future network-power-down code. */ + unsigned long trans_start; /* Time (in jiffies) of last Tx */ + unsigned long last_rx; /* Time of last Rx */ + unsigned short flags; /* interface flags (a la BSD) */ unsigned short family; /* address family ID (AF_INET) */ unsigned short metric; /* routing metric (not used) */ unsigned short mtu; /* interface MTU value */ unsigned short type; /* interface hardware type */ - unsigned long trans_start; /* ?? */ unsigned short hard_header_len; /* hardware hdr length */ void *priv; /* pointer to private data */ @@ -147,7 +159,7 @@ extern int ip_addr_match(unsigned long addr1, unsigned long addr2); extern int chk_addr(unsigned long addr); -extern struct device *dev_check(int which, unsigned long daddr); +extern struct device *dev_check(unsigned long daddr); extern unsigned long my_addr(void); extern void dev_add_pack(struct packet_type *pt); diff -u --recursive --new-file pl11/linux/net/inet/el2.c linux/net/inet/el2.c --- pl11/linux/net/inet/el2.c +++ linux/net/inet/el2.c Fri Aug 13 04:24:27 1993 @@ -0,0 +1,428 @@ +/* el2.c: A shared-memory NS8390 ethernet driver for linux. */ +/* + Written 1992,1993 by Donald Becker. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. This software may be used and + distributed according to the terms of the GNU Public License, + incorporated herein by reference. + + This driver should work with the 3c503 and 3c503/16. It should be used + in shared memory mode for best performance, although it may also work + in programmed-I/O mode. + + The Author may be reached as becker@super.org or + C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 +*/ + +static char *version = + "el2.c:v0.99.12B 8/12/93 Donald Becker (becker@super.org)\n"; + +#include +#include +#include +#include +#include +#include + +#include "dev.h" + +#include "8390.h" +#include "el2reg.h" + +int el2autoprobe(int ioaddr, struct device *dev); +int el2_pio_autoprobe(struct device *dev); +int el2probe(int ioaddr, struct device *dev); + +static int el2_open(struct device *dev); +static int el2_close(struct device *dev); +static void el2_reset_8390(struct device *dev); +static void el2_init_card(struct device *dev); +static void el2_block_output(struct device *dev, int count, + const unsigned char *buf, const start_page); +static int el2_block_input(struct device *dev, int count, char *buf, + int ring_offset); + + +/* This routine probes for a memory-mapped 3c503 board by looking for + the "location register" at the end of the jumpered boot PROM space. + This works even if a PROM isn't there. + + If the ethercard isn't found there is an optional probe for + ethercard jumpered to programmed-I/O mode. + */ + +static int ports[] = {0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0}; + +int +el2autoprobe(int ioaddr, struct device *dev) +{ + int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0}; + + /* Non-autoprobe case first: */ + if (ioaddr > 0) + return el2probe(ioaddr, dev); + + for (addr = addrs; *addr; addr++) { + int i; + unsigned int base_bits = *(unsigned char *)*addr; + /* Find first set bit. */ + for(i = 7; i >= 0; i--, base_bits >>= 1) + if (base_bits & 0x1) + break; + if (base_bits == 1 && el2probe(ports[i], dev)) + return dev->base_addr; + } +#ifdef probe_nonshared_memory + return el2_pio_autoprobe(dev); +#else + return 0; +#endif +} + +/* Try all of the locations that aren't obviously empty. This touches + a lot of locations, and is much riskier than the code above. */ +int +el2_pio_autoprobe(struct device *dev) +{ + int i; + for (i = 0; i < 8; i++) { + /* Reset and/or avoid any lurking NE2000 */ + if (inb_p(ports[i] + 0x408) == 0xff) + continue; + if (inb(ports[i] + 0x403) == (0x80 >> i) /* Preliminary check */ + && el2probe(ports[i], dev)) + return dev->base_addr; + } + return 0; +} + +/* Probe for the Etherlink II card at I/O port base IOADDR, + returning non-zero on sucess. If found, set the station + address and memory parameters in DEVICE. */ +int +el2probe(int ioaddr, struct device *dev) +{ + int i, iobase_reg, membase_reg, saved_406; + unsigned char *station_addr = dev->dev_addr; + + /* We verify that it's a 3C503 board by checking the first three octets + of its ethernet address. */ + printk("3c503 probe at %#3x:", ioaddr); + iobase_reg = inb(ioaddr+0x403); + membase_reg = inb(ioaddr+0x404); + /* Verify ASIC register that should be 0 or have a single bit set. */ + if ( (iobase_reg & (iobase_reg - 1)) + || (membase_reg & (membase_reg - 1))) { + printk(" not found.\n"); + return 0; + } + saved_406 = inb_p(ioaddr + 0x406); + outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */ + outb_p(ECNTRL_THIN, ioaddr + 0x406); + /* Map the station addr PROM into the lower I/O ports. */ + outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406); + for (i = 0; i < ETHER_ADDR_LEN; i++) { + printk(" %2.2X", (station_addr[i] = inb(ioaddr + i))); + } + if ( station_addr[0] != 0x02 + || station_addr[1] != 0x60 + || station_addr[2] != 0x8c) { + printk(" 3C503 not found.\n"); + /* Restore the register we frobbed. */ + outb(saved_406, ioaddr + 0x406); + return 0; + } + + ethdev_init(dev); + + /* Map the 8390 back into the window. */ + outb(ECNTRL_THIN, ioaddr + 0x406); + dev->base_addr = ioaddr; + /* Probe for, turn on and clear the board's shared memory. */ + if (ei_debug > 2) printk(" memory jumpers %2.2x ", membase_reg); + outb(EGACFR_NORM, ioaddr + 0x405); /* Enable RAM */ + + /* This should be probed for (or set via an ioctl()) at run-time. + Right now we use a sleazy hack to pass in the interface number + at boot-time via the low bits of the mem_end field. That value is + unused, and the low bits would be discarded even if it was used. */ +#if defined(EI8390_THICK) || defined(EL2_AUI) + ei_status.interface_num = 1; +#else + ei_status.interface_num = dev->mem_end & 0xf; +#endif + + if ((membase_reg & 0xf0) == 0) { + dev->mem_start = 0; + } else { + dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) + + ((membase_reg & 0xA0) ? 0x4000 : 0); + +#define EL2_MEMSIZE (EL2SM_STOP_PG - EL2SM_START_PG)*256 +#ifdef EL2MEMTEST + /* This has never found an error, but someone might care. */ + { /* Check the card's memory. */ + int *mem_base = (int *)dev->mem_start; + int memtest_value = 0xbbadf00d; + mem_base[0] = 0xba5eba5e; + for (i = 1; i < EL2_MEMSIZE/sizeof(mem_base[0]); i++) { + mem_base[i] = memtest_value; + if (mem_base[0] != 0xba5eba5e + || mem_base[i] != memtest_value) { + printk(" memory failure or memory address conflict.\n"); + dev->mem_start = 0; + break; + } + memtest_value += 0x55555555; + mem_base[i] = 0; + } + } +#endif /* EL2MEMTEST */ + /* Divide the on-board memory into a single maximum-sized transmit + (double-sized for ping-pong transmit) buffer at the base, and + use the rest as a receive ring. */ + dev->mem_end = dev->rmem_end = dev->mem_start + EL2_MEMSIZE; + dev->rmem_start = TX_PAGES*256 + dev->mem_start; + } + if (ei_debug > 2) + printk("\n3c503: memory params start=%#5x rstart=%#5x end=%#5x rend=%#5x.\n", + dev->mem_start, dev->rmem_start, dev->mem_end, dev->rmem_end); + + /* Finish setting the board's parameters. */ + ei_status.name = "3C503"; + ei_status.tx_start_page = EL2SM_START_PG; + ei_status.rx_start_page = EL2SM_START_PG + TX_PAGES; + ei_status.stop_page = EL2SM_STOP_PG; + ei_status.reset_8390 = &el2_reset_8390; + ei_status.block_input = &el2_block_input; + ei_status.block_output = &el2_block_output; + + if (dev->irq == 2) + dev->irq = 9; + else if (dev->irq > 5 && dev->irq != 9) { + printk("\n3c503: configured interrupt %d invalid, using autoIRQ.\n", + dev->irq); + dev->irq = 0; + } + + ei_status.saved_irq = dev->irq; + + dev->start = 0; + dev->open = &el2_open; + dev->stop = &el2_close; + el2_init_card(dev); + + if (dev->mem_start) + printk("\n%s: %s with shared memory at %#6x-%#6x,\n", + dev->name, ei_status.name, dev->mem_start, dev->mem_end-1); + else + printk("\n%s: %s using programmed I/O (REJUMPER for SHARED MEMORY).\n", + dev->name, ei_status.name); + if (ei_debug > 1) + printk(version); + + return ioaddr; +} + +static int +el2_open(struct device *dev) +{ + + if (dev->irq < 2) { + int irqlist[] = {5, 2, 3, 4, 0}; + int *irqp = irqlist; + + outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */ + do { + if (request_irq (*irqp, NULL) != -EBUSY) { + /* Twinkle the interrupt, and check if it's seen. */ + autoirq_setup(0); + outb_p(0x04 << *irqp, E33G_IDCFR); + outb_p(0x00, E33G_IDCFR); + if (*irqp == autoirq_report(0) /* It's a good IRQ line! */ + && request_irq (dev->irq = *irqp, &ei_interrupt) == 0) + break; + } + } while (*++irqp); + if (*irqp == 0) { + outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */ + return -EAGAIN; + } + } else { + if (request_irq(dev->irq, &ei_interrupt)) { + return -EAGAIN; + } + } + return ei_open(dev); +} + +static int +el2_close(struct device *dev) +{ + free_irq(dev->irq); + dev->irq = ei_status.saved_irq; + irq2dev_map[dev->irq] = NULL; + outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */ + + NS8390_init(dev, 0); + + return 0; +} + +/* This is called whenever we have a unrecoverable failure: + transmit timeout + Bad ring buffer packet header + */ +static void +el2_reset_8390(struct device *dev) +{ + if (ei_debug > 1) { + printk("%s: Resetting the 3c503 board...", dev->name); + printk("%#x=%#02x %#x=%#02x %#x=%#02x...", E33G_IDCFR, inb(E33G_IDCFR), + E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR)); + } + outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL); + ei_status.txing = 0; + outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL); + el2_init_card(dev); + if (ei_debug > 1) printk("done\n"); +} + +/* Initialize the 3c503 GA registers after a reset. */ +static void +el2_init_card(struct device *dev) +{ + /* Unmap the station PROM and select the DIX or BNC connector. */ + outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL); + + /* Set ASIC copy of rx's first and last+1 buffer pages */ + /* These must be the same as in the 8390. */ + outb(ei_status.rx_start_page, E33G_STARTPG); + outb(ei_status.stop_page, E33G_STOPPG); + + /* Point the vector pointer registers somewhere ?harmless?. */ + outb(0xff, E33G_VP2); /* Point at the ROM restart location 0xffff0 */ + outb(0xff, E33G_VP1); + outb(0x00, E33G_VP0); + /* Turn off all interrupts until we're opened. */ + outb_p(0x00, dev->base_addr + EN0_IMR); + /* Enable IRQs iff started. */ + outb(EGACFR_NORM, E33G_GACFR); + + /* Set the interrupt line. */ + outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR); + outb_p(8, E33G_DRQCNT); /* Set burst size to 8 */ + outb_p(0x20, E33G_DMAAH); /* Put a valid addr in the GA DMA */ + outb_p(0x00, E33G_DMAAL); + return; /* We always succeed */ +} + +/* Either use the shared memory (if enabled on the board) or put the packet + out through the ASIC FIFO. The latter is probably much slower. */ +static void +el2_block_output(struct device *dev, int count, + const unsigned char *buf, const start_page) +{ + int i; /* Buffer index */ + int boguscount = 0; /* timeout counter */ + + /* This should really be set with during an open(). */ + outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */ + + if (dev->mem_start) { /* Shared memory transfer */ + void *dest_addr = (void *)(dev->mem_start + + ((start_page - ei_status.tx_start_page) << 8)); + memcpy(dest_addr, buf, count); + if (ei_debug > 2 && memcmp(dest_addr, buf, count)) + printk("%s: 3c503 send_packet() bad memory copy @ %#5x.\n", + dev->name, dest_addr); + else if (ei_debug > 4) + printk("%s: 3c503 send_packet() good memory copy @ %#5x.\n", + dev->name, dest_addr); + return; + } + /* No shared memory, put the packet out the slow way. */ + /* Set up then start the internal memory transfer to Tx Start Page */ + outb(0x00, E33G_DMAAL); + outb_p(start_page, E33G_DMAAH); + outb_p((ei_status.interface_num ? ECNTRL_AUI : ECNTRL_THIN ) | ECNTRL_OUTPUT + | ECNTRL_START, E33G_CNTRL); + + /* This is the byte copy loop: it should probably be tuned for + for speed once everything is working. I think it is possible + to output 8 bytes between each check of the status bit. */ + for(i = 0; i < count; i++) { + if (i % 8 == 0) + while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0) + if (++boguscount > (i<<3) + 32) { + printk("%s: FIFO blocked in el2_block_output (at %d of %d, bc=%d).\n", + dev->name, i, count, boguscount); + return; + } + outb(buf[i], E33G_FIFOH); + } + outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL); + return; +} + +/* Returns the new ring pointer. */ +static int +el2_block_input(struct device *dev, int count, char *buf, int ring_offset) +{ + int boguscount = 0; + int end_of_ring = dev->rmem_end; + unsigned int i; + + /* Maybe enable shared memory just be to be safe... nahh.*/ + if (dev->mem_start) { /* Use the shared memory. */ + ring_offset -= (EL2SM_START_PG<<8); + if (dev->mem_start + ring_offset + count > end_of_ring) { + /* We must wrap the input move. */ + int semi_count = end_of_ring - (dev->mem_start + ring_offset); + if (ei_debug > 4) + printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n", + dev->name, dev->mem_start, ring_offset, + (char *)dev->mem_start + ring_offset); + memcpy(buf, (char *)dev->mem_start + ring_offset, semi_count); + count -= semi_count; + memcpy(buf + semi_count, (char *)dev->rmem_start, count); + return dev->rmem_start + count; + } + if (ei_debug > 4) + printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n", + dev->name, dev->mem_start, ring_offset, + (char *)dev->mem_start + ring_offset); + memcpy(buf, (char *)dev->mem_start + ring_offset, count); + return ring_offset + count; + } + /* No shared memory, use programmed I/O. */ + outb(ring_offset & 0xff, E33G_DMAAL); + outb_p((ring_offset >> 8) & 0xff, E33G_DMAAH); + outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT + | ECNTRL_START, E33G_CNTRL); + + /* This is the byte copy loop: it should probably be tuned for + for speed once everything is working. */ + for(i = 0; i < count; i++) { + if (i % 8 == 0) + while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0) + if (++boguscount > (i<<3) + 32) { + printk("%s: FIFO blocked in el2_block_input() (at %d of %d, bc=%d).\n", + dev->name, i, count, boguscount); + boguscount = 0; + break; + } + buf[i] = inb_p(E33G_FIFOH); + } + outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL); + return 0; +} + +/* + * Local variables: + * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c 3c503.c" + * version-control: t + * kept-new-versions: 5 + * End: + */ diff -u --recursive --new-file pl11/linux/net/inet/el2reg.h linux/net/inet/el2reg.h --- pl11/linux/net/inet/el2reg.h +++ linux/net/inet/el2reg.h Wed Aug 4 22:36:53 1993 @@ -0,0 +1,59 @@ +/* Definitions for the 3Com 3c503 Etherlink 2. */ +/* This file is distributed under the GPL. + Some of these names and comments are from the Crynwr packet drivers. */ + +#define EL2H (dev->base_addr + 0x400) +#define EL2L (dev->base_addr) + +/* Shared memory management parameters */ + +#define EL2SM_START_PG (0x20) /* First page of TX buffer */ +#define EL2SM_STOP_PG (0x40) /* Last page +1 of RX ring */ + +/* 3Com 3c503 ASIC registers */ +#define E33G_STARTPG (EL2H+0) /* Start page, must match EN0_STARTPG */ +#define E33G_STOPPG (EL2H+1) /* Stop page, must match EN0_STOPPG */ +#define E33G_DRQCNT (EL2H+2) /* DMA burst count */ +#define E33G_IOBASE (EL2H+3) /* Read of I/O base jumpers. */ + /* (non-useful, but it also appears at the end of EPROM space) */ +#define E33G_ROMBASE (EL2H+4) /* Read of memory base jumpers. */ +#define E33G_GACFR (EL2H+5) /* Config/setup bits for the ASIC GA */ +#define E33G_CNTRL (EL2H+6) /* Board's main control register */ +#define E33G_STATUS (EL2H+7) /* Status on completions. */ +#define E33G_IDCFR (EL2H+8) /* Interrupt/DMA config register */ + /* (Which IRQ to assert, DMA chan to use) */ +#define E33G_DMAAH (EL2H+9) /* High byte of DMA address reg */ +#define E33G_DMAAL (EL2H+10) /* Low byte of DMA address reg */ +/* "Vector pointer" - if this address matches a read, the EPROM (rather than + shared RAM) is mapped into memory space. */ +#define E33G_VP2 (EL2H+11) +#define E33G_VP1 (EL2H+12) +#define E33G_VP0 (EL2H+13) +#define E33G_FIFOH (EL2H+14) /* FIFO for programmed I/O moves */ +#define E33G_FIFOL (EL2H+15) /* ... low byte of above. */ + +/* Bits in E33G_CNTRL register: */ + +#define ECNTRL_RESET (0x01) /* Software reset of the ASIC and 8390 */ +#define ECNTRL_THIN (0x02) /* Onboard xcvr enable, AUI disable */ +#define ECNTRL_AUI (0x00) /* Onboard xcvr disable, AUI enable */ +#define ECNTRL_SAPROM (0x04) /* Map the station address prom */ +#define ECNTRL_DBLBFR (0x20) /* FIFO configuration bit */ +#define ECNTRL_OUTPUT (0x40) /* PC-to-3C503 direction if 1 */ +#define ECNTRL_INPUT (0x00) /* 3C503-to-PC direction if 0 */ +#define ECNTRL_START (0x80) /* Start the DMA logic */ + +/* Bits in E33G_STATUS register: */ + +#define ESTAT_DPRDY (0x80) /* Data port (of FIFO) ready */ +#define ESTAT_UFLW (0x40) /* Tried to read FIFO when it was empty */ +#define ESTAT_OFLW (0x20) /* Tried to write FIFO when it was full */ +#define ESTAT_DTC (0x10) /* Terminal Count from PC bus DMA logic */ +#define ESTAT_DIP (0x08) /* DMA In Progress */ + +/* Bits in E33G_GACFR register: */ + +#define EGACFR_NORM (0x49) /* Enable 8K shared mem, no DMA TC int */ +#define EGACFR_IRQOFF (0xc9) /* Above, and disable 8390 IRQ line */ + +/* End of 3C503 parameter definitions */ diff -u --recursive --new-file pl11/linux/net/inet/eth.c linux/net/inet/eth.c --- pl11/linux/net/inet/eth.c Sat Jul 3 11:46:43 1993 +++ linux/net/inet/eth.c Sat Jul 31 15:42:22 1993 @@ -28,7 +28,6 @@ #include "inet.h" #include "dev.h" #include "eth.h" -#include "timer.h" #include "ip.h" #include "route.h" #include "protocol.h" diff -u --recursive --new-file pl11/linux/net/inet/hp.c linux/net/inet/hp.c --- pl11/linux/net/inet/hp.c Sat Jul 3 00:07:39 1993 +++ linux/net/inet/hp.c Fri Aug 13 04:24:29 1993 @@ -1,6 +1,6 @@ /* hp.c: A HP LAN ethernet driver for linux. */ /* - Written 1993 by Donald Becker. This is alpha test code. + Written 1993 by Donald Becker. Copyright 1993 United States Government as represented by the Director, National Security Agency. This software may be used and distributed according to the terms of the GNU Public License, @@ -12,29 +12,22 @@ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 */ -static char *version = "hp.c:v0.99-10 5/28/93 Donald Becker (becker@super.org)\n"; +static char *version = + "hp.c:v0.99.12+ 8/12/93 Donald Becker (becker@super.org)\n"; #include #include #include #include -#include #include +#include +#ifndef port_read +#include "iow.h" +#endif #include "dev.h" #include "8390.h" -/* These should be in someday, borrowed from blk_drv/hd.c. */ -#define port_read(port,buf,nr) \ -__asm__("cld;rep;insw": :"d" (port),"D" (buf),"c" (nr):"cx","di") -#define port_write(port,buf,nr) \ -__asm__("cld;rep;outsw": :"d" (port),"S" (buf),"c" (nr):"cx","si") - -#define port_read_b(port,buf,nr) \ -__asm__("cld;rep;insb": :"d" (port),"D" (buf),"c" (nr):"cx","di") -#define port_write_b(port,buf,nr) \ -__asm__("cld;rep;outsb": :"d" (port),"S" (buf),"c" (nr):"cx","si") - #define HP_DATAPORT 0x0c /* "Remote DMA" data port. */ #define HP_ID 0x07 #define HP_CONFIGURE 0x08 /* Configuration register. */ @@ -47,10 +40,6 @@ #define HP_8BSTOP_PG 0x80 /* Last page +1 of RX ring */ #define HP_16BSTOP_PG 0xFF /* Last page +1 of RX ring */ -extern void NS8390_init(struct device *dev, int startp); -extern int ei_debug; -extern struct sigaction ei_sigaction; - int hpprobe(int ioaddr, struct device *dev); int hpprobe1(int ioaddr, struct device *dev); @@ -87,48 +76,40 @@ { int i; unsigned char *station_addr = dev->dev_addr; - unsigned char SA_prom[6]; int tmp; - int hplan; - - printk("HP-LAN ethercard probe at %#3x:", ioaddr); - tmp = inb_p(ioaddr); - if (tmp == 0xFF) { - printk(" not found (nothing there).\n"); - return 0; - } - for(i = 0; i < sizeof(SA_prom); i++) { - SA_prom[i] = inb(ioaddr + i); - if (i < ETHER_ADDR_LEN && station_addr) { - printk(" %2.2x", SA_prom[i]); - station_addr[i] = SA_prom[i]; - } - } - hplan = (SA_prom[0] == 0x08 && SA_prom[1] == 0x00 && SA_prom[2] == 0x09); - if (hplan == 0) { - printk(" not found (invalid station address prefix).\n"); + /* Check for the HP physical address, 08 00 09 xx xx xx. */ + if (inb(ioaddr) != 0x08 + || inb(ioaddr+1) != 0x00 + || inb(ioaddr+2) != 0x09) return 0; - } + + /* Good enough, we will assume everything works. */ + ethdev_init(dev); ei_status.tx_start_page = HP_START_PG; ei_status.rx_start_page = HP_START_PG + TX_PAGES; /* Set up the rest of the parameters. */ - if ((tmp = inb_p(ioaddr + HP_ID)) & 0x80) { + if ((tmp = inb(ioaddr + HP_ID)) & 0x80) { ei_status.name = "HP27247"; ei_status.word16 = 1; - ei_status.stop_page = HP_16BSTOP_PG; /* Safe for now */ + ei_status.stop_page = HP_16BSTOP_PG; /* Safe (if small) value */ } else { ei_status.name = "HP27250"; ei_status.word16 = 0; - ei_status.stop_page = HP_8BSTOP_PG; /* Safe for now */ + ei_status.stop_page = HP_8BSTOP_PG; } - /* Set the base address to point to the NIC! */ + printk("%s: %s at %#3x,", dev->name, ei_status.name, ioaddr); + + for(i = 0; i < ETHER_ADDR_LEN; i++) + printk(" %2.2x", station_addr[i] = inb(ioaddr + i)); + + + /* Set the base address to point to the NIC, not the "real" base! */ dev->base_addr = ioaddr + NIC_OFFSET; - /* Snarf the interrupt now. There's no point in waiting since we cannot - share and the board will usually be enabled. */ + /* Snarf the interrupt now. Someday this could be moved to open(). */ if (dev->irq < 2) { int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0}; int irq_8list[] = { 7, 5, 3, 4, 9, 0}; @@ -141,14 +122,13 @@ outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE); if (dev->irq == autoirq_report(0) /* It's a good IRQ line! */ && request_irq (dev->irq, &ei_interrupt) == 0) { - printk(" got IRQ %d", dev->irq); + printk(" selecting IRQ %d.\n", dev->irq); break; - } else - printk(" IRQ%d busy..", dev->irq); + } } } while (*++irqp); if (*irqp == 0) { - printk(" unable to find an free IRQ line.\n"); + printk(" no free IRQ lines.\n"); return 0; } } else { @@ -160,7 +140,6 @@ } } - printk("\n%s: %s using IRQ %d.\n", dev->name, ei_status.name, dev->irq); if (ei_debug > 1) printk(version); diff -u --recursive --new-file pl11/linux/net/inet/icmp.c linux/net/inet/icmp.c --- pl11/linux/net/inet/icmp.c Sat Jul 3 12:09:38 1993 +++ linux/net/inet/icmp.c Sat Jul 31 15:42:22 1993 @@ -23,7 +23,6 @@ #include #include #include "inet.h" -#include "timer.h" #include "dev.h" #include "ip.h" #include "route.h" diff -u --recursive --new-file pl11/linux/net/inet/iow.h linux/net/inet/iow.h --- pl11/linux/net/inet/iow.h +++ linux/net/inet/iow.h Sun Aug 8 01:13:36 1993 @@ -0,0 +1,126 @@ +#ifndef _ASM_IOW_H +#define _ASM_IOW_H +/* I added a few. Please add to the distributed files. -djb. */ +/* This file is copied 1:1 from /linux/include/asm/io.h, and changed all + al to ax, all inb to inw and all outb to outw (to get word in/out) + the four inlines here should be added to the original, and + then this one rm'd (and the #include "iow.h" in depca.c removed)... + Gruss PB +*/ +/* + * Thanks to James van Artsdalen for a better timing-fix than + * the two short jumps: using outb's to a nonexistent port seems + * to guarantee better timings even on fast machines. + * + * On the other hand, I'd like to be sure of a non-existent port: + * I feel a bit unsafe abou using 0x80. + * + * Linus + */ + +/* This is the more general version of outw.. */ +extern inline void __outw(unsigned short value, unsigned short port) +{ +__asm__ __volatile__ ("outw %w0,%w1" + : /* no outputs */ + :"a" (value),"d" (port)); +} + +/* this is used for constant port numbers < 256.. */ +extern inline void __outwc(unsigned short value, unsigned short port) +{ +__asm__ __volatile__ ("outw %w0,%1" + : /* no outputs */ + :"a" (value),"i" (port)); +} + +/* general version of inw */ +extern inline unsigned int __inw(unsigned short port) +{ + unsigned int _v; +__asm__ __volatile__ ("inw %w1,%w0" + :"=a" (_v):"d" (port),"0" (0)); + return _v; +} + +/* inw with constant port nr 0-255 */ +extern inline unsigned int __inwc(unsigned short port) +{ + unsigned int _v; +__asm__ __volatile__ ("inw %1,%w0" + :"=a" (_v):"i" (port),"0" (0)); + return _v; +} + +extern inline void __outw_p(unsigned short value, unsigned short port) +{ +__asm__ __volatile__ ("outw %w0,%w1" + : /* no outputs */ + :"a" (value),"d" (port)); + SLOW_DOWN_IO; +} + +extern inline void __outwc_p(unsigned short value, unsigned short port) +{ +__asm__ __volatile__ ("outw %w0,%1" + : /* no outputs */ + :"a" (value),"i" (port)); + SLOW_DOWN_IO; +} + +extern inline unsigned int __inw_p(unsigned short port) +{ + unsigned int _v; +__asm__ __volatile__ ("inw %w1,%w0" + :"=a" (_v):"d" (port),"0" (0)); + SLOW_DOWN_IO; + return _v; +} + +extern inline unsigned int __inwc_p(unsigned short port) +{ + unsigned int _v; +__asm__ __volatile__ ("inw %1,%w0" + :"=a" (_v):"i" (port),"0" (0)); + SLOW_DOWN_IO; + return _v; +} + +/* + * Note that due to the way __builtin_constant_p() works, you + * - can't use it inside a inlien function (it will never be true) + * - you don't have to worry about side effects within the __builtin.. + */ +#define outw(val,port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __outwc((val),(port)) : \ + __outw((val),(port))) + +#define inw(port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __inwc(port) : \ + __inw(port)) + +#define outw_p(val,port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __outwc_p((val),(port)) : \ + __outw_p((val),(port))) + +#define inw_p(port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __inwc_p(port) : \ + __inw_p(port)) + +#endif + +/* The word-wide I/O operations are more general, but require a halved + count. */ +#define port_read(port,buf,nr) \ +__asm__("cld;rep;insw": :"d" (port),"D" (buf),"c" (nr):"cx","di") +#define port_write(port,buf,nr) \ +__asm__("cld;rep;outsw": :"d" (port),"S" (buf),"c" (nr):"cx","si") + +#define port_read_b(port,buf,nr) \ +__asm__("cld;rep;insb": :"d" (port),"D" (buf),"c" (nr):"cx","di") +#define port_write_b(port,buf,nr) \ +__asm__("cld;rep;outsb": :"d" (port),"S" (buf),"c" (nr):"cx","si") diff -u --recursive --new-file pl11/linux/net/inet/ip.c linux/net/inet/ip.c --- pl11/linux/net/inet/ip.c Sat Jul 3 12:06:41 1993 +++ linux/net/inet/ip.c Fri Aug 6 20:40:18 1993 @@ -26,7 +26,6 @@ #include #include #include "inet.h" -#include "timer.h" #include "dev.h" #include "eth.h" #include "ip.h" @@ -38,6 +37,8 @@ #include "arp.h" #include "icmp.h" +#define CONFIG_IP_FORWARD + extern int last_retran; extern void sort_send(struct sock *sk); @@ -578,7 +579,9 @@ /* Do any IP forwarding required. */ if ((brd = chk_addr(iph->daddr)) == 0) { +#ifdef CONFIG_IP_FORWARD ip_forward(skb, dev); +#endif skb->sk = NULL; kfree_skb(skb, FREE_WRITE); return(0); @@ -718,9 +721,8 @@ } } sti(); - sk->time_wait.len = backoff(sk->backoff) * (2 * sk->mdev + sk->rtt); - sk->timeout = TIME_WRITE; - reset_timer ((struct timer *)&sk->time_wait); + reset_timer(sk, TIME_WRITE, + backoff(sk->backoff) * (2 * sk->mdev + sk->rtt)); } else { skb->sk = sk; } @@ -787,10 +789,7 @@ * back down reasonably quickly. */ sk->backoff++; - sk->time_wait.len = backoff(sk->backoff) * (2 * sk->mdev + sk->rtt); - sk->timeout = TIME_WRITE; - reset_timer((struct timer *)&sk->time_wait); - + reset_timer(sk, TIME_WRITE, backoff(sk->backoff) * (2 * sk->mdev + sk->rtt)); } /* Backoff function - the subject of much research */ diff -u --recursive --new-file pl11/linux/net/inet/lance.c linux/net/inet/lance.c --- pl11/linux/net/inet/lance.c +++ linux/net/inet/lance.c Fri Aug 13 04:56:54 1993 @@ -0,0 +1,630 @@ +/* lance.c: An AMD LANCE ethernet driver for linux. */ +/* + Written 1993 by Donald Becker. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. This software may be used and + distributed according to the terms of the GNU Public License, + incorporated herein by reference. + + This driver should work with the Allied Telesis 1500, and NE2100 clones. + + The author may be reached as becker@super.org or + C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 +*/ + +static char *version = "lance.c:v0.08 8/12/93 becker@super.org\n"; + +#include +#include +#include +#include +/*#include */ +#include +#include +#include +#include +/*#include */ + +#include "dev.h" +#include "iow.h" +#include "eth.h" +#include "skbuff.h" +#include "arp.h" + +/* From auto_irq.c, should be in a *.h file. */ +extern void autoirq_setup(int waittime); +extern int autoirq_report(int waittime); +extern struct device *irq2dev_map[16]; + +extern void printk_stats(struct enet_statistics *stats); + +#ifdef LANCE_DEBUG +int lance_debug = LANCE_DEBUG; +#else +int lance_debug = 1; +#endif + +#ifndef DEFAULT_DMA +#define DEFAULT_DMA 5 +#endif + +/* Bitfield in the high bits of init block ring buffer. */ +#define RING_LEN_BITS 0x80000000 +#define RING_MOD_MASK 0x0f +#define RING_SIZE 16 + + +/* Offsets from base I/O address. */ +#define LANCE_DATA 0x10 +#define LANCE_ADDR 0x12 +#define LANCE_RESET 0x14 +#define LANCE_BUS_IF 0x16 + +/* The LANCE Rx and Tx ring descriptors. */ +struct lance_rx_head { + int base; + short buf_length; /* This length is 2's complement (negative)! */ + short msg_length; /* This length is "normal". */ +}; + +struct lance_tx_head { + int base; + short length; /* Length is 2's complement (negative)! */ + short misc; +}; + +/* The LANCE initialization block, described in databook. */ +struct lance_init { + unsigned short mode; /* Pre-set mode (reg. 15) */ + unsigned char phys_addr[6]; /* Physical ethernet address */ + unsigned filter[2]; /* Multicast filter (unused). */ + /* Receive and transmit ring base, along with extra bits. */ + unsigned rx_ring; /* Tx and Rx ring base pointers */ + unsigned tx_ring; +}; + +struct lance_private { + /* These must aligned on 8-byte boundaries. */ + struct lance_rx_head rx_ring[RING_SIZE]; + struct lance_tx_head tx_ring[RING_SIZE]; + struct lance_init init_block; + int cur_rx, cur_tx; /* The next free ring entry */ + int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + int dma; + struct enet_statistics stats; + int pad0, pad1; /* Used for alignment */ +}; + +/* This is a temporary solution to the lack of a ethercard low-memory + allocation scheme. We need it for bus-master or DMA ethercards if + they are to work >16M memory systems. */ +#define PKT_BUF_SZ 1550 +static char rx_buffs[PKT_BUF_SZ][RING_SIZE]; + +int at1500_init(int ioaddr, struct device *dev); +static int lance_open(struct device *dev); +static int lance_start_xmit(struct sk_buff *skb, struct device *dev); +static int lance_rx(struct device *dev); +static void lance_interrupt(int reg_ptr); +static int lance_close(struct device *dev); +static struct enet_statistics *lance_get_stats(struct device *dev); + +#ifdef notdef +static struct sigaction lance_sigaction = { &lance_interrupt, 0, 0, NULL, }; +#endif + + + +int at1500_probe(struct device *dev) +{ + int *port, ports[] = {0x300, 0x320, 0x340, 0x360, 0}; + int ioaddr = dev->base_addr; + + if (ioaddr > 0x100) + return ! at1500_init(ioaddr, dev); + + for (port = &ports[0]; *port; port++) { + /* Probe for the Allied-Telesys vendor ID. This will not detect + other NE2100-like ethercards, which must use a hard-wired ioaddr. + There must be a better way to detect a LANCE... */ + int ioaddr = *port; + if (inb(ioaddr) != 0x00 + || inb(ioaddr+1) != 0x00 + || inb(ioaddr+2) != 0xF4) + continue; + if (at1500_init(ioaddr, dev)) + return 0; + } + return 1; /* ENODEV would be more accurate. */ +} + +int +at1500_init(int ioaddr, struct device *dev) +{ + struct lance_private *lp; + int i; + + dev->base_addr = ioaddr; + printk("%s: LANCE at %#3x, address", dev->name, ioaddr); + + /* There is a 16 byte station address PROM at the base address. + The first six bytes are the station address. */ + for (i = 0; i < 6; i++) + printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i)); + + /* Reset the LANCE */ + inw(ioaddr+LANCE_RESET); + + /* Make up a LANCE-specific-data structure. */ + dev->priv = kmalloc(sizeof(struct lance_private), GFP_KERNEL); + /* Align on 8-byte boundary. */ + dev->priv = (void *)(((int)dev->priv + 7) & ~0x07); + + if ((int)dev->priv & 0xff000000 || (int) rx_buffs & 0xff000000) { + printk(" disabled (buff %#x > 16M).\n", (int)rx_buffs); + return 0; + } + + memset(dev->priv, 0, sizeof(struct lance_private)); + lp = (struct lance_private *)dev->priv; + + /* Un-Reset the LANCE, needed only for the NE2100. */ + outw(0, ioaddr+LANCE_RESET); + + lp->init_block.mode = 0x0003; /* Disable Rx and Tx. */ + for (i = 0; i < 6; i++) + lp->init_block.phys_addr[i] = dev->dev_addr[i]; + lp->init_block.filter[0] = 0x00000000; + lp->init_block.filter[1] = 0x00000000; + lp->init_block.rx_ring = (int)lp->rx_ring | RING_LEN_BITS; + lp->init_block.tx_ring = (int)lp->tx_ring | RING_LEN_BITS; + + outw(0x0001, ioaddr+LANCE_ADDR); + outw((short) &lp->init_block, ioaddr+LANCE_DATA); + outw(0x0002, ioaddr+LANCE_ADDR); + outw(((int)&lp->init_block) >> 16, ioaddr+LANCE_DATA); + outw(0x0000, ioaddr+LANCE_ADDR); + + /* To auto-IRQ we enable the initialization-done and DMA err, + interrupts. For now we will always get a DMA error. */ + if (dev->irq < 2) { + autoirq_setup(0); + + /* Trigger an initialization just for the interrupt. */ + outw(0x0041, ioaddr+LANCE_DATA); + + dev->irq = autoirq_report(1); + if (dev->irq) + printk(", using IRQ %d.\n", dev->irq); + else { + printk(", failed to detect IRQ line.\n"); + return 0; + } + } else + printk(" assigned IRQ %d.\n", dev->irq); + +#ifndef NE2100 /* The NE2100 might not understand */ + /* Turn on auto-select of media (10baseT or BNC) so that the user + can watch the LEDs even if the board isn't opened. */ + outw(0x0002, ioaddr+LANCE_ADDR); + outw(0x0002, ioaddr+LANCE_BUS_IF); +#endif + + if ((int)(lp->rx_ring) & 0x07) + printk("%s: LANCE Rx and Tx rings not on even boundary.\n", + dev->name); + + if (lance_debug > 0) + printk(version); + + /* The LANCE-specific entries in the device structure. */ + dev->open = &lance_open; + dev->hard_start_xmit = &lance_start_xmit; + dev->stop = &lance_close; + dev->get_stats = &lance_get_stats; + + dev->mem_start = 0; + dev->rmem_end = 0x00ffffff; /* Bogus, needed for dev_rint(). */ + + /* Fill in the generic field of the device structure. */ + for (i = 0; i < DEV_NUMBUFFS; i++) + dev->buffs[i] = NULL; + + dev->hard_header = eth_header; + dev->add_arp = eth_add_arp; + dev->queue_xmit = dev_queue_xmit; + dev->rebuild_header = eth_rebuild_header; + dev->type_trans = eth_type_trans; + + dev->type = ARPHRD_ETHER; + dev->hard_header_len = ETH_HLEN; + dev->mtu = 1500; /* eth_mtu */ + dev->addr_len = ETH_ALEN; + for (i = 0; i < dev->addr_len; i++) { + dev->broadcast[i]=0xff; + } + + /* New-style flags. */ + dev->flags = IFF_BROADCAST; + dev->family = AF_INET; + dev->pa_addr = 0; + dev->pa_brdaddr = 0; + dev->pa_mask = 0; + dev->pa_alen = sizeof(unsigned long); + + return ioaddr; +} + + +static int +lance_open(struct device *dev) +{ + struct lance_private *lp = (struct lance_private *)dev->priv; + int ioaddr = dev->base_addr; + int i; + + if (request_irq(dev->irq, &lance_interrupt)) { + return -EAGAIN; + } + + if (lp->dma < 1) + lp->dma = DEFAULT_DMA; + + if (request_dma(lp->dma)) { + free_irq(dev->irq); + return -EAGAIN; + } + irq2dev_map[dev->irq] = dev; + + /* Reset the LANCE */ + inw(ioaddr+LANCE_RESET); + + /* The DMA controller is used as a no-operation slave, "cascade mode". */ + enable_dma(lp->dma); + set_dma_mode(lp->dma, DMA_MODE_CASCADE); + + /* Un-Reset the LANCE, needed only for the NE2100. */ + outw(0, ioaddr+LANCE_RESET); + +#ifndef NE2100 /* The NE2100 might not understand */ + /* Turn on auto-select of media (10baseT or BNC). */ + outw(0x0002, ioaddr+LANCE_ADDR); + outw(0x0002, ioaddr+LANCE_BUS_IF); +#endif + + if (lance_debug > 1) + printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n", + dev->name, dev->irq, lp->dma, lp->tx_ring, lp->rx_ring, + &lp->init_block); + + lp->cur_rx = lp->cur_tx = 0; + lp->dirty_rx = lp->dirty_tx = 0; + + for (i = 0; i < RING_SIZE; i++) { + lp->rx_ring[i].base = (int)rx_buffs | 0x80000000 + i*PKT_BUF_SZ; + lp->rx_ring[i].buf_length = -PKT_BUF_SZ; + lp->tx_ring[i].base = 0; + } + + lp->init_block.mode = 0x0000; + for (i = 0; i < 6; i++) + lp->init_block.phys_addr[i] = dev->dev_addr[i]; + lp->init_block.filter[0] = 0x00000000; + lp->init_block.filter[1] = 0x00000000; + lp->init_block.rx_ring = (int)lp->rx_ring | RING_LEN_BITS; + lp->init_block.tx_ring = (int)lp->tx_ring | RING_LEN_BITS; + + /* Re-initialize the LANCE, and start it when done. */ + outw(0x0001, ioaddr+LANCE_ADDR); + outw((short) &lp->init_block, ioaddr+LANCE_DATA); + outw(0x0002, ioaddr+LANCE_ADDR); + outw(((int)&lp->init_block) >> 16, ioaddr+LANCE_DATA); + + outw(0x0004, ioaddr+LANCE_ADDR); + outw(0x0d15, ioaddr+LANCE_DATA); + + outw(0x0000, ioaddr+LANCE_ADDR); + outw(0x0001, ioaddr+LANCE_DATA); + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + i = 0; + while (i++ < 100) + if (inw(ioaddr+LANCE_DATA) & 0x0100) + break; + outw(0x0142, ioaddr+LANCE_DATA); + + if (lance_debug > 2) + printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n", + dev->name, i, &lp->init_block, inw(ioaddr+LANCE_DATA)); + + return 0; /* Always succeed */ +} + +static int +lance_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct lance_private *lp = (struct lance_private *)dev->priv; + int ioaddr = dev->base_addr; + + /* Transmitter timeout, serious problems. */ + if (dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + int entry = lp->cur_tx++; + if (tickssofar < 5) + return 1; + outw(0, ioaddr+LANCE_ADDR); + printk("%s: transmit timed out, status %4.4x.\n", dev->name, + inw(ioaddr+LANCE_DATA)); + + if (lp->tx_ring[(entry+1) & RING_MOD_MASK].base >= 0) + dev->tbusy=0; + else + outw(0x00, ioaddr+LANCE_DATA), + outw(0x43, ioaddr+LANCE_DATA);; + dev->trans_start = jiffies; + + return 0; + } + + if (skb == NULL) { + dev_tint(dev); + return 0; + } + + /* Fill in the ethernet header. */ + if (!skb->arp && dev->rebuild_header(skb+1, dev)) { + skb->dev = dev; + arp_queue (skb); + return 0; + } + + if (skb->len <= 0) + return 0; + + if (lance_debug > 3) { + outw(0x0000, ioaddr+LANCE_ADDR); + printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name, + inw(ioaddr+LANCE_DATA)); + outw(0x0000, ioaddr+LANCE_DATA); + } + + /* Avoid timer-based retransmission conflicts. */ + dev->tbusy=1; + + /* This code is broken for >16M RAM systems. + There are two ways to fix it: + Keep around several static low-memory buffers, and deal with + the book-keeping (bad for small systems). + Make static Tx buffers that are optionally used. + (Even worse.) + */ + { /* Fill in a Tx ring entry */ + int entry = lp->cur_tx++; + + entry &= RING_MOD_MASK; /* Ring buffer. */ + /* Caution: the write order is important here. */ + lp->tx_ring[entry].length = -skb->len; + + /* This shouldn't be necessary... */ + lp->tx_ring[entry].length = + -(ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN); + + lp->tx_ring[entry].misc = 0x0000; + lp->tx_ring[entry].base = (int)(skb+1) | 0x83000000; + + /* Trigger an immediate send poll. */ + outw(0x0000, ioaddr+LANCE_ADDR); + outw(0x0048, ioaddr+LANCE_DATA); + + if (lance_debug > 4) { + unsigned char *pkt = + (unsigned char *)(lp->tx_ring[entry].base & 0x00ffffff); + + printk("%s: tx ring[%d], %#x, sk_buf %#x len %d.\n", + dev->name, entry, &lp->tx_ring[entry], + lp->tx_ring[entry].base, -lp->tx_ring[entry].length); + printk("%s: Tx %2.2x %2.2x %2.2x ... %2.2x %2.2x %2.2x %2.2x...%2.2x len %2.2x %2.2x %2.2x %2.2x.\n", + dev->name, pkt[0], pkt[1], pkt[2], pkt[5], pkt[6], + pkt[7], pkt[8], pkt[11], pkt[12], pkt[13], + pkt[14], pkt[15]); + } + + dev->trans_start = jiffies; + + if (lp->tx_ring[(entry+1) & RING_MOD_MASK].base >= 0) + dev->tbusy=0; + } + + return 0; +} + +/* The LANCE interrupt handler. */ +static void +lance_interrupt(int reg_ptr) +{ + int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2); + struct device *dev = irq2dev_map[irq]; + struct lance_private *lp; + int csr0, ioaddr; + + if (dev == NULL) { + printk ("lance_interrupt(): irq %d for unknown device.\n", irq); + return; + } + + ioaddr = dev->base_addr; + lp = (struct lance_private *)dev->priv; + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + + dev->interrupt = 1; + + outw(0x00, dev->base_addr + LANCE_ADDR); + csr0 = inw(dev->base_addr + LANCE_DATA); + + /* Acknowledge all of the current interrupt sources ASAP. */ + outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA); + + if (lance_debug > 5) + printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n", + dev->name, csr0, inw(dev->base_addr + LANCE_DATA)); + + if (csr0 & 0x0400) /* Rx interrupt */ + lance_rx(dev); + + if (csr0 & 0x0200) { /* Tx-done interrupt */ + int dirty_tx = lp->dirty_tx & RING_MOD_MASK; + + if (lance_debug > 5) + printk("%s: Cleaning tx ring, dirty %d clean %d.\n", + dev->name, dirty_tx, (lp->cur_tx & RING_MOD_MASK)); + + /* This code is broken for >16M RAM systems. */ + while (dirty_tx != (lp->cur_tx & RING_MOD_MASK) + && lp->tx_ring[dirty_tx].base > 0) { + sk_buff *skb = + (sk_buff *)(lp->tx_ring[dirty_tx].base & 0x00ffffff); + unsigned short *tmdp = (unsigned short *)(&lp->tx_ring[dirty_tx]); + int status = lp->tx_ring[dirty_tx].base >> 24; + + if (status & 0x40) { /* There was an major error, log it. */ + int err_status = lp->tx_ring[dirty_tx].misc; + lp->stats.tx_errors++; + if (err_status & 0x0400) lp->stats.tx_aborted_errors++; + if (err_status & 0x0800) lp->stats.tx_carrier_errors++; + if (err_status & 0x1000) lp->stats.tx_window_errors++; + if (err_status & 0x4000) lp->stats.tx_fifo_errors++; + /* We should re-init() after the FIFO error. */ + } else if (status & 0x18) + lp->stats.collisions++; + else + lp->stats.tx_packets++; + if (lance_debug > 5) + printk("%s: Tx done entry %d, %4.4x %4.4x %4.4x %4.4x.\n", + dev->name, dirty_tx, + tmdp[0], tmdp[1], tmdp[2], tmdp[3]); + if ((skb-1)->free) + kfree_skb (skb-1, FREE_WRITE); + dirty_tx = ++lp->dirty_tx & RING_MOD_MASK; + } + } + + /* Clear the interrupts we've handled. */ + outw(0x0000, dev->base_addr + LANCE_ADDR); + outw(0x7f40, dev->base_addr + LANCE_DATA); + + if (lance_debug > 4) + printk("%s: exiting interrupt, csr%d=%#4.4x.\n", + dev->name, inw(ioaddr + LANCE_ADDR), + inw(dev->base_addr + LANCE_DATA)); + + dev->interrupt = 0; + return; +} + +static int +lance_rx(struct device *dev) +{ + struct lance_private *lp = (struct lance_private *)dev->priv; + int entry = lp->cur_rx & RING_MOD_MASK; + + /* Check to see if we own this entry. */ + while (lp->rx_ring[entry].base >= 0) { + int status = lp->rx_ring[entry].base >> 24; + if (lance_debug > 5) { + unsigned char *pkt = + (unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff); + printk("%s: Rx packet at ring entry %d, len %d status %2.2x.\n", + dev->name, entry, lp->rx_ring[entry].msg_length, + lp->rx_ring[entry].base >> 24); + printk("%s: Rx %2.2x %2.2x %2.2x ... %2.2x %2.2x %2.2x %2.2x...%2.2x len %2.2x %2.2x %2.2x %2.2x.\n", + dev->name, pkt[0], pkt[1], pkt[2], pkt[5], pkt[6], + pkt[7], pkt[8], pkt[11], pkt[12], pkt[13], + pkt[14], pkt[15]); + } + /* If so, copy it to the upper layers. */ + if (status & 0x40) { /* There was an error. */ + lp->stats.rx_errors++; + if (status & 0x20) lp->stats.rx_frame_errors++; + if (status & 0x10) lp->stats.rx_over_errors++; + if (status & 0x08) lp->stats.rx_crc_errors++; + if (status & 0x04) lp->stats.rx_fifo_errors++; + } else { + if (dev_rint((unsigned char *)(lp->rx_ring[entry].base + & 0x00ffffff), + lp->rx_ring[entry].msg_length, 0, dev)) { + lp->stats.rx_dropped++; + break; + } + lp->stats.rx_packets++; + } + + lp->rx_ring[entry].base |= 0x80000000; + entry = (entry+1) & RING_MOD_MASK; + } + lp->cur_rx = entry; + + return 0; +} + +static int +lance_close(struct device *dev) +{ + int ioaddr = dev->base_addr; + struct lance_private *lp = (struct lance_private *)dev->priv; + + dev->start = 0; + dev->tbusy = 1; + + outw(112, ioaddr+LANCE_ADDR); + lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA); + + outw(0, ioaddr+LANCE_ADDR); + + if (lance_debug > 1) + printk("%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, inw(ioaddr+LANCE_DATA)); +#ifdef PRINTK_STATS + if (lance_debug > 2) + printk_stats(&lp->stats); +#endif + + /* We stop the LANCE here -- it occasionally polls + memory if we don't. */ + outw(0x0004, ioaddr+LANCE_DATA); + + disable_dma(lp->dma); + + free_irq(dev->irq); + free_dma(lp->dma); + + irq2dev_map[dev->irq] = 0; + + return 0; +} + +static struct enet_statistics * +lance_get_stats(struct device *dev) +{ + struct lance_private *lp = (struct lance_private *)dev->priv; + short ioaddr = dev->base_addr; + short saved_addr; + + cli(); + saved_addr = inw(ioaddr+LANCE_ADDR); + outw(112, ioaddr+LANCE_ADDR); + lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA); + outw(saved_addr, ioaddr+LANCE_ADDR); + sti(); + + return &lp->stats; +} + +/* + * Local variables: + * compile-command: "gcc -D__KERNEL__ -Wall -O6 -x c++ -c lance.c" + * End: + */ diff -u --recursive --new-file pl11/linux/net/inet/loopback.c linux/net/inet/loopback.c --- pl11/linux/net/inet/loopback.c Sun Jul 18 16:06:48 1993 +++ linux/net/inet/loopback.c Sat Jul 31 15:42:22 1993 @@ -33,7 +33,6 @@ #include "inet.h" #include "dev.h" #include "eth.h" -#include "timer.h" #include "ip.h" #include "protocol.h" #include "tcp.h" diff -u --recursive --new-file pl11/linux/net/inet/ne.c linux/net/inet/ne.c --- pl11/linux/net/inet/ne.c Sat Jul 3 00:07:09 1993 +++ linux/net/inet/ne.c Fri Aug 13 05:57:28 1993 @@ -1,13 +1,14 @@ /* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */ /* - Written 1992,1993 by Donald Becker. This is alpha test code. + Written 1992,1993 by Donald Becker. + Copyright 1993 United States Government as represented by the Director, National Security Agency. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. - + This driver should work with many 8390-based ethernet boards. Currently - it support the NE1000, NE2000 (and clones), and some Cabletron products. + it support the NE1000, NE2000, clones, and some Cabletron products. The Author may be reached as becker@super.org or C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 @@ -16,30 +17,22 @@ /* Routines for the NatSemi-based designs (NE[12]000). */ static char *version = - "ne.c:v0.99-10 5/28/93 Donald Becker (becker@super.org)\n"; + "ne.c:v0.99-12B 8/12/93 Donald Becker (becker@super.org)\n"; #include #include #include -#include #include +#include +#ifndef port_read +#include "iow.h" +#endif #include "dev.h" #include "8390.h" -/* These should be in someday, borrowed from blk_drv/hd.c. */ -#define port_read(port,buf,nr) \ -__asm__("cld;rep;insw": :"d" (port),"D" (buf),"c" (nr):"cx","di") -#define port_write(port,buf,nr) \ -__asm__("cld;rep;outsw": :"d" (port),"S" (buf),"c" (nr):"cx","si") - -#define port_read_b(port,buf,nr) \ -__asm__("cld;rep;insb": :"d" (port),"D" (buf),"c" (nr):"cx","di") -#define port_write_b(port,buf,nr) \ -__asm__("cld;rep;outsb": :"d" (port),"S" (buf),"c" (nr):"cx","si") - -#define EN_CMD (dev->base_addr) #define NE_BASE (dev->base_addr) +#define NE_CMD 0x00 #define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */ #define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */ @@ -48,10 +41,6 @@ #define NESM_START_PG 0x40 /* First page of TX buffer */ #define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */ -extern void NS8390_init(struct device *dev, int startp); -extern int ei_debug; -extern struct sigaction ei_sigaction; - int neprobe(int ioaddr, struct device *dev); static int neprobe1(int ioaddr, struct device *dev, int verbose); @@ -63,7 +52,7 @@ /* Probe for various non-shared-memory ethercards. - + NEx000-clone boards have a Station Address PROM (SAPROM) in the packet buffer memory space. NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of the SAPROM, while other supposed NE2000 clones must be detected by their @@ -101,15 +90,29 @@ int i; unsigned char SA_prom[32]; int wordlength = 2; + char *name; + int start_page, stop_page; int neX000, ctron, dlink; - - - if ( inb_p(ioaddr) == 0xFF) { - if (verbose) printk("8390 ethercard probe at %#3x failed.\n", ioaddr); + int reg0 = inb(ioaddr); + + if ( reg0 == 0xFF) return 0; + + /* Do a quick preliminary check that we have a 8390. */ + { int regd; + outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD); + regd = inb_p(ioaddr + 0x0d); + outb_p(0xff, ioaddr + 0x0d); + outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD); + inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */ + if (inb_p(ioaddr + EN0_COUNTER0) != 0) { + outb_p(reg0, ioaddr); + outb(regd, ioaddr + 0x0d); /* Restore the old values. */ + return 0; + } } - printk("8390 ethercard probe at %#3x:", ioaddr); + printk("NE*000 ethercard probe at %#3x:", ioaddr); /* Read the 16 bytes of station address prom, returning 1 for an eight-bit interface and 2 for a 16-bit interface. @@ -118,7 +121,7 @@ (I learned the hard way!). */ { struct {char value, offset; } program_seq[] = { - {E8390_NODMA+E8390_PAGE0+E8390_STOP, EN_CMD}, /* Select page 0 */ + {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */ {0x00, EN0_RCNTLO}, /* Clear the count regs. */ {0x00, EN0_RCNTHI}, @@ -130,7 +133,7 @@ {0x00, EN0_RCNTHI}, {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */ {0x00, EN0_RSARHI}, - {E8390_RREAD+E8390_START, EN_CMD}, + {E8390_RREAD+E8390_START, E8390_CMD}, }; for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++) outb_p(program_seq[i].value, ioaddr + program_seq[i].offset); @@ -141,19 +144,17 @@ if (SA_prom[i] != SA_prom[i+1]) wordlength = 1; } - + if (wordlength == 2) { /* We must set the 8390 for word mode, AND RESET IT. */ int tmp; outb_p(0x49, ioaddr + EN0_DCFG); tmp = inb_p(NE_BASE + NE_RESET); - ei_status.word16 = 1; outb(tmp, NE_BASE + NE_RESET); /* Un-double the SA_prom values. */ for (i = 0; i < 16; i++) SA_prom[i] = SA_prom[i+i]; - } else - ei_status.word16 = 0; + } #if defined(show_all_SAPROM) /* If your ethercard isn't detected define this to see the SA_PROM. */ @@ -165,43 +166,38 @@ printk(" %2.2x", SA_prom[i]); } #endif - + neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57); ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d); dlink = (SA_prom[0] == 0x00 && SA_prom[1] == 0xDE && SA_prom[2] == 0x01); - + /* Set up the rest of the parameters. */ - if (neX000 && wordlength == 2) { - ei_status.name = "NE2000"; - ei_status.tx_start_page = NESM_START_PG; - ei_status.stop_page = NESM_STOP_PG; - } else if (neX000 || dlink) { - ei_status.name = neX000 ? "NE1000" : "D-Link"; - ei_status.tx_start_page = NE1SM_START_PG; - ei_status.stop_page = NE1SM_STOP_PG; + if (neX000 || dlink) { + if (wordlength == 2) { + name = dlink ? "DE200" : "NE2000"; + start_page = NESM_START_PG; + stop_page = NESM_STOP_PG; + } else { + name = dlink ? "DE100" : "D-Link"; + start_page = NE1SM_START_PG; + stop_page = NE1SM_STOP_PG; + } } else if (ctron) { - ei_status.name = "Cabletron"; - ei_status.tx_start_page = 0x01; - ei_status.stop_page = (wordlength == 2) ? 0x40 : 0x20; + name = "Cabletron"; + start_page = 0x01; + stop_page = (wordlength == 2) ? 0x40 : 0x20; } else { printk(" not found.\n"); return 0; } - ei_status.rx_start_page = ei_status.tx_start_page + TX_PAGES; -#ifdef PACKETBUF_MEMSIZE - /* Allow the packet buffer size to be overridden by know-it-alls. */ - ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE; -#endif - dev->base_addr = ioaddr; - + if (dev->irq < 2) { - int nic_base = dev->base_addr; autoirq_setup(0); - outb_p(0x50, nic_base + EN0_IMR); /* Enable one interrupt. */ - outb_p(0x00, nic_base + EN0_RCNTLO); - outb_p(0x00, nic_base + EN0_RCNTHI); - outb_p(E8390_RREAD+E8390_START, nic_base); /* Trigger it... */ - outb_p(0x00, nic_base + EN0_IMR); /* Mask it again. */ + outb_p(0x50, ioaddr + EN0_IMR); /* Enable one interrupt. */ + outb_p(0x00, ioaddr + EN0_RCNTLO); + outb_p(0x00, ioaddr + EN0_RCNTHI); + outb_p(E8390_RREAD+E8390_START, ioaddr); /* Trigger it... */ + outb_p(0x00, ioaddr + EN0_IMR); /* Mask it again. */ dev->irq = autoirq_report(0); if (ei_debug > 2) printk(" autoirq is %d", dev->irq); @@ -212,17 +208,35 @@ /* Snarf the interrupt now. There's no point in waiting since we cannot share and the board will usually be enabled. */ - { int irqval = irqaction (dev->irq, &ei_sigaction); - if (irqval) { - printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval); - return 0; - } - } - - printk("\n%s: %s found, using IRQ %d.\n", - dev->name, ei_status.name, dev->irq); - if (ei_debug > 1) + { + int irqval = irqaction (dev->irq, &ei_sigaction); + if (irqval) { + printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval); + return 0; + } + } + + printk("\n%s: %s found at %#x, using IRQ %d.\n", + dev->name, name, ioaddr, dev->irq); + + dev->base_addr = ioaddr; + + ethdev_init(dev); + + if (ei_debug > 0) printk(version); + + ei_status.name = name; + ei_status.tx_start_page = start_page; + ei_status.stop_page = stop_page; + ei_status.word16 = (wordlength == 2); + + ei_status.rx_start_page = start_page + TX_PAGES; +#ifdef PACKETBUF_MEMSIZE + /* Allow the packet buffer size to be overridden by know-it-alls. */ + ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE; +#endif + ei_status.reset_8390 = &ne_reset_8390; ei_status.block_input = &ne_block_input; ei_status.block_output = &ne_block_output; @@ -269,12 +283,12 @@ return 0; } ei_status.dmaing |= 0x01; - outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, EN_CMD); + outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); outb_p(count & 0xff, nic_base + EN0_RCNTLO); outb_p(count >> 8, nic_base + EN0_RCNTHI); outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO); outb_p(ring_offset >> 8, nic_base + EN0_RSARHI); - outb_p(E8390_RREAD+E8390_START, EN_CMD); + outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD); if (ei_status.word16) { port_read(NE_BASE + NE_DATAPORT,buf,count>>1); if (count & 0x01) @@ -284,8 +298,10 @@ } /* This was for the ALPHA version only, but enough people have - encountering problems that it is still here. */ - if (ei_debug > 0) { /* DMA termination address check... */ + encountering problems that it is still here. If you see + this message you either 1) have an slightly imcompatible clone + or 2) have noise/speed problems with your bus. */ + if (ei_debug > 1) { /* DMA termination address check... */ int addr, tries = 20; do { /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here @@ -298,7 +314,7 @@ } while (--tries > 0); if (tries <= 0) printk("%s: RX transfer address mismatch," - "%#4.4x (should be) vs. %#4.4x (actual).\n", + "%#4.4x (expected) vs. %#4.4x (actual).\n", dev->name, ring_offset + xfer_count, addr); } ei_status.dmaing &= ~0x01; @@ -318,7 +334,7 @@ if (ei_status.word16 && (count & 0x01)) count++; if (ei_status.dmaing) { - if (ei_debug > 0) + if (ei_debug > 0) printk("%s: DMAing conflict in ne_block_output." "[DMAstat:%1x][irqlock:%1x]\n", dev->name, ei_status.dmaing, ei_status.irqlock); @@ -326,7 +342,7 @@ } ei_status.dmaing |= 0x02; /* We should already be in page 0, but to be safe... */ - outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, EN_CMD); + outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD); retry: #if defined(rw_bugfix) @@ -338,7 +354,7 @@ outb_p(0x00, nic_base + EN0_RCNTHI); outb_p(0x42, nic_base + EN0_RSARLO); outb_p(0x00, nic_base + EN0_RSARHI); - outb_p(E8390_RREAD+E8390_START, EN_CMD); + outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD); /* Make certain that the dummy read has occured. */ SLOW_DOWN_IO; SLOW_DOWN_IO; @@ -351,7 +367,7 @@ outb_p(0x00, nic_base + EN0_RSARLO); outb_p(start_page, nic_base + EN0_RSARHI); - outb_p(E8390_RWRITE+E8390_START, EN_CMD); + outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD); if (ei_status.word16) { port_write(NE_BASE + NE_DATAPORT, buf, count>>1); } else { @@ -360,7 +376,7 @@ /* This was for the ALPHA version only, but enough people have encountering problems that it is still here. */ - if (ei_debug > 0) { /* DMA termination address check... */ + if (ei_debug > 1) { /* DMA termination address check... */ int addr, tries = 20; do { /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here @@ -372,8 +388,8 @@ break; } while (--tries > 0); if (tries <= 0) { - printk("%s: Packet buffer transfer address mismatch on TX," - "%#4.4x vs. %#4.4x.\n", + printk("%s: Tx packet transfer address mismatch," + "%#4.4x (expected) vs. %#4.4x (actual).\n", dev->name, (start_page << 8) + count, addr); if (retries++ == 0) goto retry; diff -u --recursive --new-file pl11/linux/net/inet/packet.c linux/net/inet/packet.c --- pl11/linux/net/inet/packet.c Sat Jul 3 11:47:57 1993 +++ linux/net/inet/packet.c Sat Jul 31 15:42:22 1993 @@ -21,7 +21,6 @@ #include #include #include "inet.h" -#include "timer.h" #include "dev.h" #include "ip.h" #include "protocol.h" diff -u --recursive --new-file pl11/linux/net/inet/plip.c linux/net/inet/plip.c --- pl11/linux/net/inet/plip.c Sat Jul 3 11:59:52 1993 +++ linux/net/inet/plip.c Fri Aug 13 04:24:30 1993 @@ -1,17 +1,31 @@ -/* plip.c: A parallel port "network" driver for linux. */ +/* Plip.c: A parallel port "network" driver for linux. */ /* - Written 1993 by Donald Becker. This is unreleased software. + Written 1993 by Donald Becker and TANABE Hiroyasu. + This code is distributed under the GPL. + The current author is reached as hiro@sanpo.t.u-tokyo.ac.jp . + For more information do 'whois -h whois.nic.ad.jp HT043JP' + + The original author may be reached as becker@super.org or + C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 + This is parallel port packet pusher. It's actually more general than the "IP" in its name suggests -- but 'plip' is just such a great name! - The Author may be reached as becker@super.org or - C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 + This driver was first developed by D. Becker, when he was inspired by + Russ Nelson's parallel port packet driver. He also did the update + to 0.99.10. + + It was further developed by Tommy Thorn (tthorn@daimi.aau.dk). + + Recent versions were debugged and maintained by TANABE Hiroyasu. + + Updated for 0.99pl12 by Donald Becker. */ static char *version = - "plip.c:v0.04 Mar 19 1993 Donald Becker (becker@super.org)\n"; + "plip.c:v0.15 for 0.99pl12+, 8/11/93\n"; #include @@ -19,14 +33,17 @@ Sources: Ideas and protocols came from Russ Nelson's (nelson@crynwr.com) "parallel.asm" parallel port packet driver. + TANABE Hiroyasu changes the protocol. The "Crynwr" parallel port standard specifies the following protocol: send header nibble '8' + type octet '0xfd' or '0xfc' count-low octet count-high octet ... data octets checksum octet -Each octet is sent as - >4)&0x0F)> +Each octet is sent as + >4)&0x0F)> + The cable used is a de facto standard parallel null cable -- sold as a "LapLink" cable by various places. You'll need a 10-conductor cable to make one yourself. The wiring is: @@ -50,14 +67,14 @@ #include #include #include +#include #include #include -#include +#include #include #include "dev.h" #include "eth.h" -#include "timer.h" #include "ip.h" #include "protocol.h" #include "tcp.h" @@ -65,98 +82,125 @@ #include "sock.h" #include "arp.h" -/* use 0 for production, 1 for verification, >2 for debug */ -#ifndef PLIP_DEBUG -#define PLIP_DEBUG 9 +#ifdef PRINTK +#undef PRINTK +#endif +#ifdef PRINTK2 +#undef PRINTK2 +#endif + +#define PLIP_DEBUG /* debugging */ +#undef PLIP_DEBUG2 /* debugging with more varbose report */ + +#ifdef PLIP_DEBUG +#define PRINTK(x) printk x +#else +#define PRINTK(x) /**/ #endif -static unsigned int plip_debug = PLIP_DEBUG; +#ifdef PLIP_DEBUG2 +#define PRINTK2(x) printk x +#else +#define PRINTK2(x) /**/ +#endif /* The map from IRQ number (as passed to the interrupt handler) to 'struct device'. */ extern struct device *irq2dev_map[16]; +/* Network statistics, with the same names as 'struct enet_statistics'. */ +#define netstats enet_statistics + +/* constants */ #define PAR_DATA 0 #define PAR_STATUS 1 #define PAR_CONTROL 2 -/* Common network statistics -- these will be in *.h someday. */ -struct netstats { - int tx_packets; - int rx_packets; - int tx_errors; - int rx_errors; - int missed_packets; - int soft_tx_errors; - int soft_rx_errors; - int soft_trx_err_bits; -}; -static struct netstats *localstats; +#define PLIP_MTU 1600 +#define PLIP_HEADER_TYPE1 0xfd +#define PLIP_HEADER_TYPE2 0xfc /* Index to functions, as function prototypes. */ extern int plip_probe(int ioaddr, struct device *dev); -/* Put in the device structure. */ static int plip_open(struct device *dev); static int plip_close(struct device *dev); static int plip_tx_packet(struct sk_buff *skb, struct device *dev); +static int plip_header (unsigned char *buff, struct device *dev, + unsigned short type, unsigned long h_dest, + unsigned long h_source, unsigned len); + +/* variables used internally. */ +#define INITIALTIMEOUTFACTOR 4 +#define MAXTIMEOUTFACTOR 20 +static int timeoutfactor = INITIALTIMEOUTFACTOR; /* Routines used internally. */ -/* Dispatch from interrupts. */ -static void plip_interrupt(int reg_ptr); -static int plip_write(struct device *dev, unsigned char *buf, int length); +static void plip_device_clear(struct device *dev); +static void plip_receiver_error(struct device *dev); +static void plip_set_physicaladdr(struct device *dev, unsigned long ipaddr); +static int plip_addrcmp(struct ethhdr *eth); +static int plip_send_enethdr(struct device *dev, struct ethhdr *eth); +static int plip_rebuild_enethdr(struct device *dev, struct ethhdr *eth, + unsigned char h_dest, unsigned char h_source, + unsigned short type); +static void cold_sleep(int tics); +static void plip_interrupt(int reg_ptr); /* Dispatch from interrupts. */ +static int plip_receive_packet(struct device *dev); +static int plip_send_packet(struct device *dev, unsigned char *buf, int length); +static int plip_send_start(struct device *dev, struct ethhdr *eth); +static void double_timeoutfactor(void); +static struct enet_statistics *plip_get_stats(struct device *dev); int plip_init(struct device *dev) { + int port_base = dev->base_addr; int i; + /* Check that there is something at base_addr. */ + outb(0x00, port_base + PAR_CONTROL); + outb(0x55, port_base + PAR_DATA); + if (inb(port_base + PAR_DATA) != 0x55) + return -ENODEV; + /* Alpha testers must have the version number to report bugs. */ - if (plip_debug > 1) { +#ifdef PLIP_DEBUG + { static int version_shown = 0; if (! version_shown) printk(version), version_shown++; } - - /* We don't actually probe for anything here, although we might - someday check to see if there's bi-directional port at - dev->base_addr. */ +#endif /* Initialize the device structure. */ dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL); memset(dev->priv, 0, sizeof(struct netstats)); - localstats = (struct netstats*) dev->priv; for (i = 0; i < DEV_NUMBUFFS; i++) dev->buffs[i] = NULL; - dev->hard_header = eth_header; - dev->add_arp = eth_add_arp; - dev->queue_xmit = dev_queue_xmit; - dev->rebuild_header = eth_rebuild_header; - dev->type_trans = eth_type_trans; + dev->hard_header = &plip_header; + dev->add_arp = eth_add_arp; + dev->queue_xmit = dev_queue_xmit; + dev->rebuild_header = eth_rebuild_header; + dev->type_trans = eth_type_trans; - dev->open = &plip_open; - dev->stop = &plip_close; + dev->open = &plip_open; + dev->stop = &plip_close; dev->hard_start_xmit = &plip_tx_packet; + dev->get_stats = &plip_get_stats; /* These are ethernet specific. */ dev->type = ARPHRD_ETHER; dev->hard_header_len = ETH_HLEN; - dev->mtu = 1500; /* eth_mtu */ - dev->addr_len = ETH_ALEN; + dev->mtu = PLIP_MTU; /* PLIP may later negotiate max pkt size */ + dev->addr_len = ETH_ALEN; for (i = 0; i < dev->addr_len; i++) { dev->broadcast[i]=0xff; - dev->dev_addr[i] = i; /* The physical address is 0:1:2:3:4:5! */ + dev->dev_addr[i] = 0; } - - /* New-style flags. */ - dev->flags = IFF_BROADCAST; - dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = sizeof(unsigned long); - - printk("%s: using parallel port at %#3x, IRQ %d.\n", dev->name, - dev->base_addr, dev->irq); + printk("%s: configured for parallel port at %#3x, IRQ %d.\n", + dev->name, dev->base_addr, dev->irq); + /* initialize internal value */ + timeoutfactor = INITIALTIMEOUTFACTOR; return 0; } @@ -173,14 +217,13 @@ if (dev->irq == 0) dev->irq = 7; if (request_irq(dev->irq , &plip_interrupt) != 0) { - if (plip_debug > 2) - printk("%s: couldn't get the IRQ.\n", dev->name); - return EAGAIN; + PRINTK(("%s: couldn't get IRQ %d.\n", dev->name, dev->irq)); + return -EAGAIN; } irq2dev_map[dev->irq] = dev; - outb(0x10, dev->base_addr + PAR_CONTROL); /* Enable the rx interrupt. */ - dev->tbusy = 0; /* Transmit busy... */ + plip_device_clear(dev); + dev->tbusy = 0; dev->interrupt = 0; dev->start = 1; return 0; @@ -190,6 +233,7 @@ static int plip_close(struct device *dev) { + dev->tbusy = 1; dev->start = 0; free_irq(dev->irq); irq2dev_map[dev->irq] = NULL; @@ -201,6 +245,17 @@ plip_tx_packet(struct sk_buff *skb, struct device *dev) { int ret_val; + + if (dev->tbusy || dev->interrupt) { /* Do timeouts, to avoid hangs. */ + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 50) + return 1; + printk("%s: transmit timed out\n", dev->name); + /* Try to restart the adaptor. */ + plip_device_clear(dev); + return 0; + } + /* If some higher layer thinks we've missed an tx-done interrupt we are passed NULL. Caution: dev_tint() handles the cli()/sti() itself. */ @@ -218,7 +273,7 @@ } dev->trans_start = jiffies; - ret_val = plip_write(dev, (unsigned char *)(skb+1), skb->len); + ret_val = plip_send_packet(dev, (unsigned char *)(skb+1), skb->len); if (skb->free) kfree_skb (skb, FREE_WRITE); dev->tbusy = 0; @@ -225,27 +280,77 @@ mark_bh (INET_BH); return ret_val; } + +static int +plip_header (unsigned char *buff, struct device *dev, + unsigned short type, unsigned long h_dest, + unsigned long h_source, unsigned len) +{ + if (dev->dev_addr[0] == 0) { + /* set physical address */ + plip_set_physicaladdr(dev, h_source); + } + return eth_header(buff, dev, type, h_dest, h_source, len); +} +static void + plip_device_clear(struct device *dev) +{ + dev->interrupt = 0; + dev->tbusy = 0; + outb(0x00, dev->base_addr + PAR_DATA); + outb(0x10, dev->base_addr + PAR_CONTROL); /* Enable the rx interrupt. */ +} + +static void + plip_receiver_error(struct device *dev) +{ + dev->interrupt = 0; + dev->tbusy = 0; + outb(0x02, dev->base_addr + PAR_DATA); + outb(0x10, dev->base_addr + PAR_CONTROL); /* Enable the rx interrupt. */ +} -static inline int get_byte(struct device *dev) +static int + get_byte(struct device *dev) { - unsigned char val; + unsigned char val, oldval; unsigned char low_nibble; - int boguscount = 1500; + int timeout; + int error = 0; + val = inb(dev->base_addr + PAR_STATUS); + timeout = jiffies + timeoutfactor * 2; do { + oldval = val; val = inb(dev->base_addr + PAR_STATUS); - } while ( ! (val & 0x80) && --boguscount > 0); + if ( oldval != val ) continue; /* it's unstable */ + if ( timeout < jiffies ) { + error++; + break; + } + } while ( (val & 0x80) ); + val = inb(dev->base_addr + PAR_STATUS); low_nibble = (val >> 3) & 0x0f; - if (plip_debug > 8) - printk("%1x", low_nibble); - outb(0x10, dev->base_addr + PAR_DATA); + outb(0x11, dev->base_addr + PAR_DATA); + timeout = jiffies + timeoutfactor * 2; do { + oldval = val; val = inb(dev->base_addr + PAR_STATUS); - } while ((val & 0x80) && --boguscount > 0); - if (plip_debug > 8) - printk("%1x %s", low_nibble, - boguscount <= 0 ? "timeout":""); - outb(0x00, dev->base_addr + PAR_DATA); + if (oldval != val) continue; /* it's unstable */ + if ( timeout < jiffies ) { + error++; + break; + } + } while ( !(val & 0x80) ); + val = inb(dev->base_addr + PAR_STATUS); + PRINTK2(("%02x %s ", low_nibble | ((val << 1) & 0xf0), + error ? "t":"")); + outb(0x01, dev->base_addr + PAR_DATA); + if (error) { + /* timeout error */ + double_timeoutfactor(); + return -1; + } return low_nibble | ((val << 1) & 0xf0); } @@ -252,140 +357,458 @@ /* The typical workload of the driver: Handle the parallel port interrupts. */ static void -plip_interrupt(int reg_ptr) + plip_interrupt(int reg_ptr) { int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2); struct device *dev = irq2dev_map[irq]; - int boguscount = 1500; - unsigned length; - int sksize; - struct sk_buff *skb; + struct netstats *localstats; if (dev == NULL) { - printk ("plip_interrupt(): irq %d for unknown device.\n", irq); + PRINTK(("plip_interrupt(): irq %d for unknown device.\n", irq)); return; } + localstats = (struct netstats*) dev->priv; + if (dev->tbusy || dev->interrupt) return; dev->interrupt = 1; - outb(0x00, dev->base_addr + PAR_CONTROL); /* Disable the rx interrupt. */ + outb(0x00, dev->base_addr + PAR_CONTROL); /* Disable the rx interrupt. */ sti(); /* Allow other interrupts. */ - - if (plip_debug >= 4) - printk("%s: interrupt.\n", dev->name); - + PRINTK2(("%s: interrupt. ", dev->name)); + + { + /* check whether the interrupt is valid or not.*/ + int timeout = jiffies + timeoutfactor; + while ((inb(dev->base_addr + PAR_STATUS) & 0xf8) != 0xc0) { + if ( timeout < jiffies ) { + PRINTK2(("%s: No interrupt (status=%#02x)!\n", + dev->name, inb(dev->base_addr + PAR_STATUS))); + plip_device_clear(dev); + return; + } + } + } + if (plip_receive_packet(dev)) { + /* get some error while receiving data */ + localstats->rx_errors++; + plip_receiver_error(dev); + } else { + plip_device_clear(dev); + } +} + +static int +plip_receive_packet(struct device *dev) +{ + int plip_type; + unsigned length; + int checksum = 0; + struct sk_buff *skb; + struct netstats *localstats; + struct ethhdr eth; + localstats = (struct netstats*) dev->priv; - /* Receive the packet here. */ - if (inb(dev->base_addr + PAR_STATUS) != 0xc7) { - localstats->rx_errors++; /* No interrupt! */ - if (plip_debug > 4) - printk("%s: No interrupt (status=%#02x)!\n", - dev->name, inb(dev->base_addr + PAR_STATUS)); - return; - } outb(1, dev->base_addr + PAR_DATA); /* Ack: 'Ready' */ - length = get_byte(dev); - length |= (get_byte(dev) << 8); - if (length > dev->mtu) { - printk("%s: Bogus packet size %d, dropping it.\n", dev->name, length); - return; + + { + /* get header octet and length of packet */ + plip_type = get_byte(dev); + if (plip_type < 0) return 1; /* probably wrong interrupt */ + length = get_byte(dev) << 8; + length |= get_byte(dev); + switch ( plip_type ) { + case PLIP_HEADER_TYPE1: + { + int i; + unsigned char *eth_p = (unsigned char*)ð + for ( i = 0; i < sizeof(eth); i++, eth_p++) { + *eth_p = get_byte(dev); + } + } + break; + case PLIP_HEADER_TYPE2: + { + unsigned char h_dest, h_source; + unsigned short type; + h_dest = get_byte(dev); + h_source = get_byte(dev); + type = get_byte(dev) << 8; + type |= get_byte(dev); + plip_rebuild_enethdr(dev, ð, h_dest, h_source, type); + } + break; + default: + PRINTK(("%s: wrong header octet\n", dev->name)); + } + PRINTK2(("length = %d\n", length)); + if (length > dev->mtu || length < 8) { + PRINTK2(("%s: bogus packet size %d.\n", dev->name, length)); + return 1; + } } - boguscount = length << 5; - sksize = sizeof(struct sk_buff) + length; - skb = (struct sk_buff *) kmalloc(sksize, GFP_ATOMIC); - if (skb == NULL) { - if (plip_debug) - printk("%s: Couldn't allocate a sk_buff of size %d.\n", - dev->name, sksize); - localstats->rx_errors++; - return; + { + /* get skb area from kernel and + * set appropriate values to skb + */ + int sksize; + sksize = sizeof(struct sk_buff) + length; + skb = (struct sk_buff *)kmalloc(sksize, GFP_ATOMIC); + if (skb == NULL) { + PRINTK(("%s: Couldn't allocate a sk_buff of size %d.\n", + dev->name, sksize)); + return 1; + } + skb->lock = 0; + skb->mem_len = sksize; + skb->mem_addr = skb; } - skb->lock = 0; - skb->mem_len = sksize; - skb->mem_addr = skb; { + /* phase of receiving the data */ /* 'skb+1' points to the start of sk_buff data area. */ unsigned char *buf = (unsigned char *) (skb+1); - int checksum = 0; - - while (length--) { + unsigned char *eth_p = (unsigned char *)ð + int i; + for ( i = 0; i < sizeof(eth); i++) { + checksum += *eth_p; + *buf++ = *eth_p++; + } + for ( i = 0; i < length - sizeof(eth); i++) { unsigned char new_byte = get_byte(dev); - checksum += new_byte, *buf++ = new_byte; + checksum += new_byte; + *buf++ = new_byte; } - if (checksum != get_byte(dev)) - localstats->soft_rx_errors++; - else if(dev_rint((unsigned char *)skb, length, IN_SKBUFF, dev)) { - printk("%s: receive buffers full.\n", dev->name); - localstats->rx_errors++; - return; + checksum &= 0xff; + if (checksum != get_byte(dev)) { + localstats->rx_crc_errors++; + PRINTK(("checksum error\n")); + return 1; + } else if(dev_rint((unsigned char *)skb, length, IN_SKBUFF, dev)) { + printk("%s: rcv buff full.\n", dev->name); + localstats->rx_dropped++; + return 1; } } - /* Wait for the remote end to reset. */ - while (inb(dev->base_addr + PAR_STATUS) != 0x87) - if (boguscount-- <= 0 ) - break; - outb(0x00, dev->base_addr + PAR_DATA); - outb(0x10, dev->base_addr + PAR_CONTROL); /* Enable the rx interrupt. */ + { + /* phase of terminating this connection */ + int timeout; + + timeout = jiffies + length * timeoutfactor / 16; + outb(0x00, dev->base_addr + PAR_DATA); + /* Wait for the remote end to reset. */ + while ( (inb(dev->base_addr + PAR_STATUS) & 0xf8) != 0x80 ) { + if (timeout < jiffies ) { + double_timeoutfactor(); + PRINTK(("remote end is not reseted.\n")); + break; + } + } + } localstats->rx_packets++; - return; + return 0; } - -static inline int send_byte(struct device *dev, unsigned char val) + +static int send_byte(struct device *dev, unsigned char val) { - int boguscount = 1500; - if (plip_debug > 8) - printk("send(%02x) ", val); + int timeout; + int error = 0; + if (!(inb(dev->base_addr+PAR_STATUS) & 0x08)) { + PRINTK(("remote end become unready while sending\n")); + return -1; + } + PRINTK2((" S%02x", val)); + outb(val, dev->base_addr); /* this makes data bits more stable */ outb(0x10 | val, dev->base_addr); - while(inb(dev->base_addr+PAR_STATUS) & 0x80) - if (--boguscount <= 0) break; + timeout = jiffies + timeoutfactor; + while( inb(dev->base_addr+PAR_STATUS) & 0x80 ) + if ( timeout < jiffies ) { + error++; + break; + } + outb(0x10 | (val >> 4), dev->base_addr); outb(val >> 4, dev->base_addr); - while((inb(dev->base_addr+PAR_STATUS) & 0x80) == 0) - if (--boguscount <= 0) break; - if (plip_debug > 4 && boguscount <= 0) - printk("timeout"); + timeout = jiffies + timeoutfactor; + while( (inb(dev->base_addr+PAR_STATUS) & 0x80) == 0 ) + if ( timeout < jiffies ) { + error++; + break; + } + if (error) { + /* timeout error */ + double_timeoutfactor(); + PRINTK2(("t")); + return -1; + } + return 0; +} +/* + * plip_send_start + * trigger remoto rx interrupt and establish a connection. + * + * return value + * 0 : establish the connection + * -1 : connection failed. + */ +static int +plip_send_start(struct device *dev, struct ethhdr *eth) +{ + int timeout; + int status; + int lasttrigger; + struct netstats *localstats = (struct netstats*) dev->priv; + + /* This starts the packet protocol by triggering a remote IRQ. */ + timeout = jiffies + timeoutfactor * 16; + lasttrigger = jiffies; + while ( ((status = inb(dev->base_addr+PAR_STATUS)) & 0x08) == 0 ) { + dev->tbusy = 1; + outb(0x00, dev->base_addr + PAR_CONTROL); /* Disable my rx intr. */ + outb(0x08, dev->base_addr + PAR_DATA); /* Trigger remote rx intr. */ + if (status & 0x40) { + /* The remote end is also trying to send a packet. + * Only one end may go to the receiving phase, + * so we use the "ethernet" address (set from the IP address) + * to determine which end dominates. + */ + if ( plip_addrcmp(eth) > 0 ) { + localstats->collisions++; + PRINTK2(("both ends are trying to send a packet.\n")); + if (plip_receive_packet(dev)) { + /* get some error while receiving data */ + localstats->rx_errors++; + outb(0x02, dev->base_addr + PAR_DATA); + } else { + outb(0x00, dev->base_addr + PAR_DATA); + } + cold_sleep(2); /* make sure that remote end is ready */ + } + continue; /* restart send sequence */ + } + if (lasttrigger != jiffies) { + /* trigger again */ + outb(0x00, dev->base_addr + PAR_DATA); + cold_sleep(1); + lasttrigger = jiffies; + } + if (timeout < jiffies) { + double_timeoutfactor(); + plip_device_clear(dev); + localstats->tx_errors++; + PRINTK(("%s: Connect failed in send_packet().\n", + dev->name)); + /* We failed to send the packet. To emulate the ethernet we + should pretent the send worked fine */ + return -1; + } + } + return 0; } static int -plip_write(struct device *dev, unsigned char *buf, int length) +plip_send_packet(struct device *dev, unsigned char *buf, int length) { - int timeout = 1000; /* Approx 1 ms. */ - char checksum = 0; - int i; - if (plip_debug > 5) - printk("%s: plip_write(%d) %02x %02x %02x %02x %02x...", - dev->name, length, buf[0], buf[1], buf[2], buf[3], buf[4]); + int error = 0; + int plip_type; + struct netstats *localstats; + + PRINTK2(("%s: plip_send_packet(%d) %02x %02x %02x %02x %02x...", + dev->name, length, buf[0], buf[1], buf[2], buf[3], buf[4])); if (length > dev->mtu) { printk("%s: packet too big, %d.\n", dev->name, length); + return 0; + } + localstats = (struct netstats*) dev->priv; + + { + /* phase of checking remote status */ + int i; + int timeout = jiffies + timeoutfactor * 8; + while ( (i = (inb(dev->base_addr+PAR_STATUS) & 0xe8)) != 0x80 ) { + if (i == 0x78) { + /* probably cable is not connected */ + /* Implementation Note: + * This status should result in 'Network unreachable'. + * but I don't know the way. + */ + return 0; + } + if (timeout < jiffies) { + /* remote end is not ready */ + double_timeoutfactor(); + localstats->tx_errors++; + PRINTK(("remote end is not ready.\n")); + return 1; /* Failed to send the packet */ + } + } + } + /* phase of making a connection */ + if (plip_send_start(dev, (struct ethhdr *)buf) < 0) return 1; + + /* select plip type */ + { + /* Use stripped ethernet header if each first 5 octet of eth + * address is same. + */ + int i; + struct ethhdr *eth = (struct ethhdr *)buf; + + plip_type = PLIP_HEADER_TYPE2; + for ( i = 0; i < ETH_ALEN - 1; i++) + if (eth->h_dest[i] != eth->h_source[i]) + plip_type = PLIP_HEADER_TYPE1; } - /* This starts the packet protocol by triggering a remote IRQ. */ - outb(0x00, dev->base_addr + PAR_CONTROL); /* Disable my rx interrupt. */ - outb(0x08, dev->base_addr + PAR_DATA); /* Trigger remote rx interrupt. */ - while((inb(dev->base_addr+PAR_STATUS) & 0x08) == 0 ) - if (--timeout < 0) { - outb(0x00, dev->base_addr); - localstats->tx_errors++; - if (plip_debug > 3) - printk("%s: Connect failed during send_packet() (length=%d).\n", - dev->name, length); - /* We failed to send the packet. To emulate the ethernet we - should pretent the send worked fine, but we don't right now. */ - return 1; /* Failed to send the packet! */ + + send_byte(dev, plip_type); /* send header octet */ + + { + /* send packet's length */ + /* + * in original plip (before v0.1), it was sent with little endian. + * but in internet, network byteorder is big endian, + * so changed to use big endian. + * maybe using 'ntos()' is better. + */ + send_byte(dev, length >> 8); send_byte(dev, length); + } + { + /* phase of sending data */ + int i; + int checksum = 0; + + if (plip_type == PLIP_HEADER_TYPE2) { + plip_send_enethdr(dev, (struct ethhdr*)buf); } - send_byte(dev, length); send_byte(dev, length >> 8); - for (i = 0; i < length; i++) - checksum += buf[i], send_byte(dev, buf[i]); - send_byte(dev, checksum); - outb(0x00, dev->base_addr); - outb(0x10, dev->base_addr + PAR_CONTROL); /* Enable the rx interrupt. */ + for ( i = 0; i < sizeof(struct ethhdr); i++ ) { + if (plip_type == PLIP_HEADER_TYPE1) { + send_byte(dev, *buf); + } + checksum += *buf++; + } + + for (i = 0; i < length - sizeof(struct ethhdr); i++) { + checksum += buf[i]; + if (send_byte(dev, buf[i]) < 0) { + error++; + break; + } + } + send_byte(dev, checksum & 0xff); + } + { + /* phase of terminating this connection */ + int timeout; + + outb(0x00, dev->base_addr + PAR_DATA); + /* Wait for the remote end to reset. */ + timeout = (jiffies + length * timeoutfactor) >> 4; + while ((inb(dev->base_addr + PAR_STATUS) & 0xe8) != 0x80) { + if (timeout < jiffies ) { + double_timeoutfactor(); + PRINTK(("remote end is not reseted.\n")); + error++; + break; + } + } + if (inb(dev->base_addr + PAR_STATUS) & 0x10) { + /* receiver reports error */ + error++; + } + } + plip_device_clear(dev); localstats->tx_packets++; - if (plip_debug > 5) - printk("plip_write(%d) done.\n", length); + PRINTK2(("plip_send_packet(%d) done.\n", length)); + return error?1:0; +} + +/* + * some trivial functions + */ +static void +plip_set_physicaladdr(struct device *dev, unsigned long ipaddr) +{ + /* + * set physical address to + * 0xfd.0xfd.ipaddr + */ + + unsigned char *addr = dev->dev_addr; + int i; + + if ((ipaddr >> 24) == 0 || (ipaddr >> 24) == 0xff) return; + PRINTK2(("%s: set physical address to %08x\n", dev->name, ipaddr)); + for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++) { + addr[i] = 0xfd; + } + memcpy(&(addr[i]), &ipaddr, sizeof(unsigned long)); +} + +static int +plip_addrcmp(struct ethhdr *eth) +{ + int i; + for ( i = ETH_ALEN - 1; i >= 0; i-- ) { + if (eth->h_dest[i] > eth->h_source[i]) return -1; + if (eth->h_dest[i] < eth->h_source[i]) return 1; + } + PRINTK2(("h_dest = %08x%04x h_source = %08x%04x\n", + *(long*)ð->h_dest[2],*(short*)ð->h_dest[0], + *(long*)ð->h_source[2],*(short*)ð->h_source[0])); + return 0; +} + +static int +plip_send_enethdr(struct device *dev, struct ethhdr *eth) +{ + send_byte(dev, eth->h_dest[ETH_ALEN-1]); + send_byte(dev, eth->h_source[ETH_ALEN-1]); + send_byte(dev, eth->h_proto >> 8); + send_byte(dev, eth->h_proto); + return 0; +} + +static int +plip_rebuild_enethdr(struct device *dev, struct ethhdr *eth, + unsigned char dest, unsigned char source, + unsigned short type) +{ + eth->h_proto = type; + memcpy(eth->h_dest, dev->dev_addr, ETH_ALEN-1); + eth->h_dest[ETH_ALEN-1] = dest; + memcpy(eth->h_source, dev->dev_addr, ETH_ALEN-1); + eth->h_source[ETH_ALEN-1] = source; return 0; } + +/* This function is evil, evil, evil. This should be a + _kernel_, rescheduling sleep!. */ +static void +cold_sleep(int tics) +{ + int start = jiffies; + while(jiffies < start + tics) + ; /* do nothing */ + return; +} + +static void + double_timeoutfactor() +{ + timeoutfactor *= 2; + if (timeoutfactor >= MAXTIMEOUTFACTOR) { + timeoutfactor = MAXTIMEOUTFACTOR; + } + return; +} + +static struct enet_statistics * +plip_get_stats(struct device *dev) +{ + struct netstats *localstats = (struct netstats*) dev->priv; + return localstats; +} /* * Local variables: - * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c plip.c" + * compile-command: "gcc -D__KERNEL__ -Wall -O6 -fomit-frame-pointer -x c++ -c plip.c" * version-control: t * kept-new-versions: 5 * End: diff -u --recursive --new-file pl11/linux/net/inet/proc.c linux/net/inet/proc.c --- pl11/linux/net/inet/proc.c Fri Jul 2 20:43:17 1993 +++ linux/net/inet/proc.c Sun Aug 8 00:44:15 1993 @@ -27,7 +27,6 @@ #include #include #include "inet.h" -#include "timer.h" #include "dev.h" #include "ip.h" #include "protocol.h" @@ -37,8 +36,6 @@ #include "sock.h" #include "raw.h" -extern struct timer *timer_base; - /* * Get__netinfo returns the length of that string. * @@ -53,9 +50,9 @@ struct sock *sp; char *pos=buffer; int i; + int timer_active; unsigned long dest, src; unsigned short destp, srcp; - struct timer *tp; s_array = pro->sock_array; pos+=sprintf(pos, "sl local_address rem_address st tx_queue rx_queue tr tm->when\n"); @@ -71,21 +68,15 @@ destp = ntohs(destp); srcp = ntohs(srcp); - pos+=sprintf(pos, "%2d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08X %02X", + timer_active = del_timer(&sp->timer); + if (!timer_active) + sp->timer.expires = 0; + pos+=sprintf(pos, "%2d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08X %02X\n", i, src, srcp, dest, destp, sp->state, sp->send_seq-sp->rcv_ack_seq, sp->acked_seq-sp->copied_seq, - sp->time_wait.running, sp->time_wait.when-jiffies, sp->retransmits); - - cli(); - tp = timer_base; - while (tp) { - if (tp == &(sp->time_wait)) { - pos+=sprintf(pos, " *"); - } - tp = tp->next; - } - sti(); - pos+=sprintf(pos, "\n"); + timer_active, sp->timer.expires, sp->retransmits); + if (timer_active) + add_timer(&sp->timer); /* Is place in buffer too rare? then abort. */ if (pos > buffer+PAGE_SIZE-80) { diff -u --recursive --new-file pl11/linux/net/inet/protocol.c linux/net/inet/protocol.c --- pl11/linux/net/inet/protocol.c Sat May 29 12:29:07 1993 +++ linux/net/inet/protocol.c Sat Jul 31 15:42:22 1993 @@ -24,7 +24,6 @@ #include #include #include "inet.h" -#include "timer.h" #include "dev.h" #include "ip.h" #include "protocol.h" diff -u --recursive --new-file pl11/linux/net/inet/raw.c linux/net/inet/raw.c --- pl11/linux/net/inet/raw.c Sat Jul 3 12:08:01 1993 +++ linux/net/inet/raw.c Sat Jul 31 15:42:22 1993 @@ -27,7 +27,6 @@ #include #include #include "inet.h" -#include "timer.h" #include "dev.h" #include "ip.h" #include "protocol.h" diff -u --recursive --new-file pl11/linux/net/inet/route.c linux/net/inet/route.c --- pl11/linux/net/inet/route.c Sat Jul 3 11:46:16 1993 +++ linux/net/inet/route.c Sat Aug 7 18:36:52 1993 @@ -26,7 +26,6 @@ #include #include #include "inet.h" -#include "timer.h" #include "dev.h" #include "ip.h" #include "protocol.h" @@ -216,65 +215,12 @@ */ /* If we have a 'gateway' route here, check the correct address. */ - if (r->rt_flags & RTF_GATEWAY) { - dev = dev_check(0, - ((struct sockaddr_in *) &r->rt_gateway)->sin_addr.s_addr); - if (dev == NULL) { - dev = dev_check(1, - ((struct sockaddr_in *) &r->rt_gateway)->sin_addr.s_addr); - } - - /* - * Aii. We are using an indirect route. Check the already - * existing routing table for an entry. - */ - if (dev == NULL) { - for (rt = rt_base; rt != NULL; rt = rt->rt_next) { - if ((rt->rt_dev->flags & IFF_UP) == 0) continue; - if (ip_addr_match(rt->rt_dst, - ((struct sockaddr_in *) - &r->rt_gateway)->sin_addr.s_addr)) { - dev = rt->rt_dev; - break; - } - if (ip_addr_match(rt->rt_dev->pa_brdaddr, - ((struct sockaddr_in *) - &r->rt_gateway)->sin_addr.s_addr)) { - dev = rt->rt_dev; - break; - } - } - } - } else { - dev = dev_check(0, - ((struct sockaddr_in *) &r->rt_dst)->sin_addr.s_addr); - if (dev == NULL) { - dev = dev_check(1, - ((struct sockaddr_in *) &r->rt_dst)->sin_addr.s_addr); - } - - /* - * Aii. We are using an indirect route. Check the already - * existing routing table for an entry. - */ - if (dev == NULL) { - for (rt = rt_base; rt != NULL; rt = rt->rt_next) { - if ((rt->rt_dev->flags & IFF_UP) == 0) continue; - if (ip_addr_match(rt->rt_dst, - ((struct sockaddr_in *) - &r->rt_dst)->sin_addr.s_addr)) { - dev = rt->rt_dev; - break; - } - if (ip_addr_match(rt->rt_dev->pa_brdaddr, - ((struct sockaddr_in *) - &r->rt_dst)->sin_addr.s_addr)) { - dev = rt->rt_dev; - break; - } - } - } - } + if (!(r->rt_flags & RTF_GATEWAY)) + dev = dev_check(((struct sockaddr_in *) &r->rt_dst)->sin_addr.s_addr); + else + if ((rt = rt_route(((struct sockaddr_in *) &r->rt_gateway)->sin_addr. + s_addr,NULL))) dev = rt->rt_dev; + else dev = NULL; DPRINTF((DBG_RT, "RT: dev for %s gw ", in_ntoa((*(struct sockaddr_in *)&r->rt_dst).sin_addr.s_addr))); @@ -339,15 +285,23 @@ * at the IP options to see if we have been given a hint as * to what kind of path we should use... -FvK */ + for (rt = rt_base; rt != NULL; rt = rt->rt_next) + if ((rt->rt_flags & RTF_HOST) && rt->rt_dst == daddr) { + DPRINTF((DBG_RT, "%s (%s)\n", + rt->rt_dev->name, in_ntoa(rt->rt_gateway))); + rt->rt_use++; + return(rt); + } for (rt = rt_base; rt != NULL; rt = rt->rt_next) { DPRINTF((DBG_RT, "RT: %s via ", in_ntoa(daddr))); - if (ip_addr_match(rt->rt_dst, daddr)) { + if (!(rt->rt_flags & RTF_HOST) && ip_addr_match(rt->rt_dst, daddr)) { DPRINTF((DBG_RT, "%s (%s)\n", rt->rt_dev->name, in_ntoa(rt->rt_gateway))); rt->rt_use++; return(rt); } - if (ip_addr_match(rt->rt_dev->pa_brdaddr, daddr)) { + if ((rt->rt_dev->flags & IFF_BROADCAST) && + ip_addr_match(rt->rt_dev->pa_brdaddr, daddr)) { DPRINTF((DBG_RT, "%s (BCAST %s)\n", rt->rt_dev->name, in_ntoa(rt->rt_dev->pa_brdaddr))); rt->rt_use++; diff -u --recursive --new-file pl11/linux/net/inet/slhc.c linux/net/inet/slhc.c --- pl11/linux/net/inet/slhc.c Sat Jul 3 12:04:45 1993 +++ linux/net/inet/slhc.c Sat Jul 31 15:42:22 1993 @@ -48,7 +48,6 @@ #include #include #include "inet.h" -#include "timer.h" #include "dev.h" #include "ip.h" #include "protocol.h" diff -u --recursive --new-file pl11/linux/net/inet/slip.c linux/net/inet/slip.c --- pl11/linux/net/inet/slip.c Sun Jul 18 14:07:47 1993 +++ linux/net/inet/slip.c Sat Jul 31 15:42:22 1993 @@ -27,7 +27,6 @@ #include "inet.h" #include "dev.h" #include "eth.h" -#include "timer.h" #include "ip.h" #include "route.h" #include "protocol.h" @@ -41,12 +40,6 @@ #define SLIP_VERSION "0.7.5" -#ifdef SL_COMPRESSED -#define COMPRESSED_SLIP 1 -#else -#define COMPRESSED_SLIP 0 -#endif - /* Define some IP layer stuff. Not all systems have it. */ #ifdef SL_DUMP # define IP_VERSION 4 /* version# of our IP software */ @@ -104,6 +97,7 @@ #endif } +#if 0 void clh_dump(unsigned char *cp, int len) { if (len > 60) @@ -115,6 +109,7 @@ } printk("\n\n"); } +#endif /* Initialize a SLIP control block for use. */ static void @@ -167,11 +162,11 @@ struct slip *sl; int i; + save_flags (flags); + cli(); for (i = 0; i < SL_NRUNIT; i++) { sl = &sl_ctrl[i]; if (sl->inuse == 0) { - save_flags(flags); - cli(); sl->inuse = 1; sl->tty = NULL; restore_flags(flags); @@ -268,7 +263,7 @@ int count; count = sl->rcount; - if (COMPRESSED_SLIP) { + if (1) { if ((c = sl->rbuff[0]) & SL_TYPE_COMPRESSED_TCP) { /* make sure we've reserved enough space for uncompress to use */ save_flags(flags); @@ -285,15 +280,14 @@ if (! done) /* not enough space available */ return; - if ((count = slhc_uncompress((struct slcompress *)sl->slcomp, - sl->rbuff, count)) <= 0 ) { + count = slhc_uncompress(sl->slcomp, sl->rbuff, count); + if (count <= 0) { sl->errors++; return; } } else if (c >= SL_TYPE_UNCOMPRESSED_TCP) { sl->rbuff[0] &= 0x4f; - if ( slhc_remember((struct slcompress *)sl->slcomp, sl->rbuff, - count ) <= 0 ) { + if (slhc_remember(sl->slcomp, sl->rbuff, count) <= 0) { sl->errors++; return; } @@ -334,13 +328,14 @@ unsigned char c; int count; - DPRINTF((DBG_SLIP, "SLIP: sl_encaps(0x%X, %d) called\n", p, len)); + DPRINTF((DBG_SLIP, "SLIP: sl_encaps(0x%X, %d) called\n", icp, len)); DPRINTF((DBG_SLIP, ">> \"%s\" sent:\r\n", sl->dev->name)); ip_dump(icp, len); p = icp; - len = slhc_compress((struct slcompress *)sl->slcomp, p, len, - sl->cbuff, &p, 1); +#ifdef SL_COMPRESSED + len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1); +#endif /* * Send an initial END character to flush out any @@ -347,7 +342,7 @@ * data that may have accumulated in the receiver * due to line noise. */ - bp = (unsigned char *) sl->xbuff; + bp = sl->xbuff; *bp++ = END; count = 1; @@ -357,7 +352,7 @@ */ while(len-- > 0) { c = *p++; - switch((c & 0377)) { + switch(c) { case END: *bp++ = ESC; *bp++ = ESC_END; @@ -376,7 +371,7 @@ *bp++ = END; count++; sl->spacket++; - bp = (unsigned char *) sl->xbuff; + bp = sl->xbuff; /* Tell TTY to send it on its way. */ DPRINTF((DBG_SLIP, "SLIP: kicking TTY for %d bytes at 0x%X\n", count, bp)); @@ -566,36 +561,28 @@ /* Suck the bytes out of the TTY queues. */ do { - memset(buff, 0, 128); count = tty_read_raw_data(tty, buff, 128); if (count <= 0) break; p = buff; - while(count-- > 0) { + while (count--) { c = *p++; - switch((c & 0377)) { - case ESC: + if (sl->escape) { + if (c == ESC_ESC) + sl_enqueue(sl, ESC); + else if (c == ESC_END) + sl_enqueue(sl, END); + else + printk ("SLIP: received wrong character\n"); + sl->escape = 0; + } else { + if (c == ESC) sl->escape = 1; - break; - case ESC_ESC: - if (sl->escape) sl_enqueue(sl, ESC); - else sl_enqueue(sl, c); - sl->escape = 0; - break; - case ESC_END: - if (sl->escape) sl_enqueue(sl, END); - else sl_enqueue(sl, c); - sl->escape = 0; - break; - case END: + else if (c == END) { if (sl->rcount > 2) sl_bump(sl); sl_dequeue(sl, sl->rcount); sl->rcount = 0; - sl->escape = 0; - break; - default: - sl_enqueue(sl, c); - sl->escape = 0; + } else sl_enqueue(sl, c); } } } while(1); diff -u --recursive --new-file pl11/linux/net/inet/sock.c linux/net/inet/sock.c --- pl11/linux/net/inet/sock.c Sat Jul 3 11:44:56 1993 +++ linux/net/inet/sock.c Sat Jul 31 15:42:22 1993 @@ -9,6 +9,7 @@ * * Authors: Ross Biro, * Fred N. van Kempen, + * Florian La Roche, * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -27,7 +28,6 @@ #include #include #include "inet.h" -#include "timer.h" #include "dev.h" #include "ip.h" #include "protocol.h" @@ -50,9 +50,7 @@ #define min(a,b) ((a)<(b)?(a):(b)) -#define swap(a,b) {unsigned long c; c=a; a=b; b=c;} - extern struct proto packet_prot; @@ -318,7 +316,7 @@ remove_sock(sk); /* Now we can no longer get new packets. */ - delete_timer((struct timer *)&sk->time_wait); + delete_timer(sk); if (sk->send_tmp != NULL) kfree_skb(sk->send_tmp, FREE_WRITE); @@ -478,9 +476,7 @@ sk->destroy = 1; sk->ack_backlog = 0; sk->inuse = 0; - sk->time_wait.len = SOCK_DESTROY_TIME; - sk->timeout = TIME_DESTROY; - reset_timer((struct timer *)&sk->time_wait); + reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME); } DPRINTF((DBG_INET, "leaving destroy_sock\n")); } @@ -805,11 +801,9 @@ sk->pair = NULL; sk->send_tail = NULL; sk->send_head = NULL; - sk->time_wait.len = TCP_CONNECT_TIME; - sk->time_wait.when = 0; - sk->time_wait.sk = sk; - sk->time_wait.next = NULL; sk->timeout = 0; + sk->timer.data = (unsigned long)sk; + sk->timer.function = &net_timer; sk->back_log = NULL; sk->blog = 0; sock->data =(void *) sk; @@ -1506,7 +1500,7 @@ if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0) { DPRINTF((DBG_INET, "recovered lost memory, destroying sock = %X\n", sk)); - delete_timer((struct timer *)&sk->time_wait); + delete_timer(sk); kfree_s((void *)sk, sizeof(*sk)); } return; @@ -1523,7 +1517,7 @@ if (sk) { sk->rmem_alloc -= size; if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0) { - delete_timer((struct timer *)&sk->time_wait); + delete_timer(sk); kfree_s((void *)sk, sizeof(*sk)); } } @@ -1614,9 +1608,7 @@ sti(); if (sk->dead && sk->state == TCP_CLOSE) { /* Should be about 2 rtt's */ - sk->time_wait.len = min(sk->rtt * 2, TCP_DONE_TIME); - sk->timeout = TIME_DONE; - reset_timer((struct timer *)&sk->time_wait); + reset_timer(sk, TIME_DONE, min(sk->rtt * 2, TCP_DONE_TIME)); } } @@ -1695,7 +1687,9 @@ inet_fcntl, }; +extern unsigned long seq_offset; +/* Called by ddi.c on kernel startup. */ void inet_proto_init(struct ddi_proto *pro) { struct inet_protocol *p; @@ -1703,7 +1697,7 @@ /* Set up our UNIX VFS major device. */ if (register_chrdev(AF_INET_MAJOR, "af_inet", &inet_fops) < 0) { - printk("5s: cannot register major device %d!\n", + printk("%s: cannot register major device %d!\n", pro->name, AF_INET_MAJOR); return; } @@ -1732,5 +1726,4 @@ /* Initialize the "Buffer Head" pointers. */ bh_base[INET_BH].routine = inet_bh; - timer_table[NET_TIMER].fn = net_timer; } diff -u --recursive --new-file pl11/linux/net/inet/sock.h linux/net/inet/sock.h --- pl11/linux/net/inet/sock.h Fri Jul 2 13:23:35 1993 +++ linux/net/inet/sock.h Sat Jul 31 15:42:22 1993 @@ -10,6 +10,7 @@ * Authors: Ross Biro, * Fred N. van Kempen, * Corey Minyard + * Florian La Roche * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,6 +20,7 @@ #ifndef _SOCK_H #define _SOCK_H +#include #define SOCK_ARRAY_SIZE 64 @@ -53,7 +55,6 @@ keepopen, linger, delay_acks, - timeout, destroy, ack_timed, no_check, @@ -93,7 +94,10 @@ unsigned char max_ack_backlog; unsigned char priority; struct tcphdr dummy_th; - struct timer time_wait; + + /* This part is used for the timeout functions (timer.c). */ + int timeout; /* What are we waiting for? */ + struct timer_list timer; }; struct proto { @@ -186,5 +190,14 @@ unsigned long size); extern unsigned long sock_rspace(struct sock *sk); extern unsigned long sock_wspace(struct sock *sk); + + +/* declarations from timer.c */ +extern struct sock *timer_base; + +void delete_timer (struct sock *); +void reset_timer (struct sock *, int, unsigned long); +void net_timer (unsigned long); + #endif /* _SOCK_H */ diff -u --recursive --new-file pl11/linux/net/inet/tcp.c linux/net/inet/tcp.c --- pl11/linux/net/inet/tcp.c Sat Jul 3 12:12:57 1993 +++ linux/net/inet/tcp.c Wed Aug 11 20:41:15 1993 @@ -11,6 +11,7 @@ * Fred N. van Kempen, * Mark Evans, * Corey Minyard + * Florian La Roche, * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -27,7 +28,6 @@ #include #include #include "inet.h" -#include "timer.h" #include "dev.h" #include "ip.h" #include "protocol.h" @@ -42,12 +42,10 @@ #include #include +#define SEQ_TICK 3 +unsigned long seq_offset; -#define tmax(a,b)(before((a),(b)) ?(b) :(a)) -#define swap(a,b) {unsigned long c; c=a; a=b; b=c;} - - -static int +static __inline__ int min(unsigned int a, unsigned int b) { if (a < b) return(a); @@ -115,10 +113,9 @@ { sk->state = TCP_TIME_WAIT; sk->shutdown = SHUTDOWN_MASK; - if (!sk->dead) wake_up(sk->sleep); - sk->time_wait.len = TCP_TIMEWAIT_LEN; - sk->timeout = TIME_CLOSE; - reset_timer((struct timer *)&sk->time_wait); + if (!sk->dead) + wake_up(sk->sleep); + reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); } @@ -503,9 +500,7 @@ /* Force it to send an ack. */ sk->ack_backlog++; if (sk->timeout != TIME_WRITE && tcp_connected(sk->state)) { - sk->timeout = TIME_WRITE; - sk->time_wait.len = 10; /* got to do it quickly */ - reset_timer((struct timer *)&sk->time_wait); + reset_timer(sk, TIME_WRITE, 10); } if (inet_debug == DBG_SLIP) printk("\rtcp_ack: malloc failed\n"); return; @@ -551,8 +546,7 @@ sk->bytes_rcv = 0; sk->ack_timed = 0; if (sk->send_head == NULL && sk->wfront == NULL) { - delete_timer((struct timer *)&sk->time_wait); - sk->timeout = 0; + delete_timer(sk); } } t1->ack_seq = ntohl(ack); @@ -894,9 +888,7 @@ buff = (struct sk_buff *) sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC); if (buff == NULL) { /* Try again real soon. */ - sk->timeout = TIME_WRITE; - sk->time_wait.len = 10; - reset_timer((struct timer *) &sk->time_wait); + reset_timer(sk, TIME_WRITE, 10); return; } @@ -996,11 +988,11 @@ tcp_read_wakeup(sk); } else { /* Force it to send an ack soon. */ - if (jiffies + TCP_ACK_TIME < sk->time_wait.when) { - sk->time_wait.len = TCP_ACK_TIME; - sk->timeout = TIME_WRITE; - reset_timer((struct timer *) &sk->time_wait); - } + int was_active = del_timer(&sk->timer); + if (!was_active || TCP_ACK_TIME < sk->timer.expires) { + reset_timer(sk, TIME_WRITE, TCP_ACK_TIME); + } else + add_timer(&sk->timer); } } } @@ -1542,13 +1534,13 @@ newsk->copied_seq = skb->h.th->seq; newsk->state = TCP_SYN_RECV; newsk->timeout = 0; - newsk->send_seq = timer_seq * SEQ_TICK - seq_offset; + newsk->send_seq = jiffies * SEQ_TICK - seq_offset; newsk->rcv_ack_seq = newsk->send_seq; newsk->urg =0; newsk->retransmits = 0; newsk->destroy = 0; - newsk->time_wait.sk = newsk; - newsk->time_wait.next = NULL; + newsk->timer.data = (unsigned long)newsk; + newsk->timer.function = &net_timer; newsk->dummy_th.source = skb->h.th->dest; newsk->dummy_th.dest = skb->h.th->source; @@ -1644,9 +1636,7 @@ tcp_send_check(t1, daddr, saddr, sizeof(*t1)+4, newsk); newsk->prot->queue_xmit(newsk, dev, buff, 0); - newsk->time_wait.len = TCP_CONNECT_TIME; - DPRINTF((DBG_TCP, "newsk->time_wait.sk = %X\n", newsk->time_wait.sk)); - reset_timer((struct timer *)&newsk->time_wait); + reset_timer(newsk, TIME_WRITE /* -1 ? FIXME ??? */, TCP_CONNECT_TIME); skb->sk = newsk; /* Charge the sock_buff to newsk. */ @@ -1717,9 +1707,7 @@ case TCP_FIN_WAIT2: case TCP_LAST_ACK: /* start a timer. */ - sk->time_wait.len = 4*sk->rtt;; - sk->timeout = TIME_CLOSE; - reset_timer((struct timer *)&sk->time_wait); + reset_timer(sk, TIME_CLOSE, 4 * sk->rtt); if (timeout) tcp_time_wait(sk); release_sock(sk); break; @@ -1747,9 +1735,7 @@ /* This will force it to try again later. */ if (sk->state != TCP_CLOSE_WAIT) sk->state = TCP_ESTABLISHED; - sk->timeout = TIME_CLOSE; - sk->time_wait.len = 100; /* wait a second. */ - reset_timer((struct timer *)&sk->time_wait); + reset_timer(sk, TIME_CLOSE, 100); return; } buff->lock = 0; @@ -1791,10 +1777,8 @@ if (sk->wfront == NULL) { prot->queue_xmit(sk, dev, buff, 0); } else { - sk->time_wait.len = backoff(sk->backoff) * - (2 * sk->mdev + sk->rtt); - sk->timeout = TIME_WRITE; - reset_timer((struct timer *)&sk->time_wait); + reset_timer(sk, TIME_WRITE, + backoff(sk->backoff) * (2 * sk->mdev + sk->rtt)); buff->next = NULL; if (sk->wback == NULL) { sk->wfront=buff; @@ -1904,9 +1888,7 @@ return(0); } if (sk->keepopen) { - sk->time_wait.len = TCP_TIMEOUT_LEN; - sk->timeout = TIME_KEEPOPEN; - reset_timer((struct timer *)&sk->time_wait); + reset_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); } return(1); } @@ -2106,19 +2088,14 @@ DPRINTF((DBG_TCP, "Nothing to do, going to sleep.\n")); if (!sk->dead) wake_up(sk->sleep); - delete_timer((struct timer *)&sk->time_wait); - sk->timeout = 0; + delete_timer(sk); } else { if (sk->state != (unsigned char) sk->keepopen) { - sk->timeout = TIME_WRITE; - sk->time_wait.len = backoff(sk->backoff) * - (2 * sk->mdev + sk->rtt); - reset_timer((struct timer *)&sk->time_wait); + reset_timer(sk, TIME_WRITE, + backoff(sk->backoff) * (2 * sk->mdev + sk->rtt)); } if (sk->state == TCP_TIME_WAIT) { - sk->time_wait.len = TCP_TIMEWAIT_LEN; - reset_timer((struct timer *)&sk->time_wait); - sk->timeout = TIME_CLOSE; + reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); } } } @@ -2305,9 +2282,7 @@ /* tcp_send_ack(sk->send_seq, sk->acked_seq,sk,th, saddr); */ } else { sk->ack_backlog++; - sk->time_wait.len = TCP_ACK_TIME; - sk->timeout = TIME_WRITE; - reset_timer((struct timer *)&sk->time_wait); + reset_timer(sk, TIME_WRITE, TCP_ACK_TIME); } } } @@ -2344,9 +2319,7 @@ } tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr); sk->ack_backlog++; - sk->time_wait.len = TCP_ACK_TIME; - sk->timeout = TIME_WRITE; - reset_timer((struct timer *)&sk->time_wait); + reset_timer(sk, TIME_WRITE, TCP_ACK_TIME); } else { /* We missed a packet. Send an ack to try to resync things. */ tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr); @@ -2439,9 +2412,7 @@ sk->state = TCP_LAST_ACK; /* Start the timers. */ - sk->time_wait.len = TCP_TIMEWAIT_LEN; - sk->timeout = TIME_CLOSE; - reset_timer((struct timer *)&sk->time_wait); + reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); return(0); } sk->ack_backlog++; @@ -2529,7 +2500,7 @@ } sk->inuse = 1; sk->daddr = sin.sin_addr.s_addr; - sk->send_seq = timer_seq*SEQ_TICK-seq_offset; + sk->send_seq = jiffies * SEQ_TICK - seq_offset; sk->rcv_ack_seq = sk->send_seq -1; sk->err = 0; sk->dummy_th.dest = sin.sin_port; @@ -2588,9 +2559,8 @@ sk->prot->queue_xmit(sk, dev, buff, 0); - sk->time_wait.len = TCP_CONNECT_TIME; sk->rtt = TCP_CONNECT_TIME; - reset_timer((struct timer *)&sk->time_wait); + reset_timer(sk, TIME_WRITE /* -1 FIXME ??? */, TCP_CONNECT_TIME); sk->retransmits = TCP_RETR2 - TCP_SYN_RETRIES; release_sock(sk); return(0); diff -u --recursive --new-file pl11/linux/net/inet/timer.c linux/net/inet/timer.c --- pl11/linux/net/inet/timer.c Fri Jul 2 23:57:19 1993 +++ linux/net/inet/timer.c Tue Aug 3 11:09:46 1993 @@ -11,6 +11,7 @@ * Fred N. van Kempen, * Corey Minyard * Fred Baumgarten, + * Florian La Roche, * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -17,6 +18,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ + #include #include #include @@ -27,7 +29,6 @@ #include #include #include "inet.h" -#include "timer.h" #include "dev.h" #include "ip.h" #include "protocol.h" @@ -36,117 +37,35 @@ #include "sock.h" #include "arp.h" - -struct timer *timer_base = NULL; -unsigned long seq_offset; - - void -delete_timer(struct timer *t) +delete_timer (struct sock *t) { - struct timer *tm; + unsigned long flags; - DPRINTF((DBG_TMR, "delete_timer(t=%X)\n", t)); + save_flags (flags); cli(); - if (timer_base == NULL || t == NULL) { - t->running = 9; - sti(); - return; - } - if (t == timer_base) { - timer_base = t->next; - if (timer_base != NULL) { - timer_table[NET_TIMER].expires = timer_base->when; - timer_active |= (1 << NET_TIMER); - } else { - timer_active &= ~(1 << NET_TIMER); - } - t->running = 0; - sti(); - return; - } - tm = timer_base; - while (tm != NULL) { - if (tm->next == t) { - tm->next = t->next; - t->running = 0; - sti(); - return; - } - tm = tm->next; - } - sti(); - t->running = 9; -} + t->timeout = 0; + del_timer (&t->timer); + + restore_flags (flags); +} void -reset_timer(struct timer *t) +reset_timer (struct sock *t, int timeout, unsigned long len) { - struct timer *tm; - - DPRINTF((DBG_TMR, "reset_timer(t=%X) when = %d jiffies = %d\n", - t, t->when, jiffies)); - if (t == NULL) { - printk("*** reset timer NULL timer\n"); - __asm__ ("\t int $3\n"); - } - if (t->running) { - DPRINTF((DBG_TMR, "t->running has value of %d, len %d\n", - t->running, t->len)); - } - t->running = 1; - delete_timer(t); /* here is another race condition ! */ - cli(); /* what about a new reset_timer while being\ */ - if (!((t->running == 0) || (t->running == 9))) { /* about to install on old one ? -FB */ - printk("reset_timer(): t->running after delete_timer: %d !\n", - t->running); - sti(); - return; - } - t->running = 2; - if ((int) t->len < 0) /* prevent close to infinite timers. THEY _DO_ */ - t->len = 3; /* happen (negative values ?) - don't ask me why ! -FB */ - - delete_timer(t); - cli(); - t->when = timer_seq + t->len; - - /* First see if it goes at the beginning. */ + delete_timer (t); - if (timer_base == NULL) { - t->next = NULL; - timer_base = t; - timer_table[NET_TIMER].expires = timer_base->when; - timer_active |= (1 << NET_TIMER); - t->running = 3; - sti(); - return; - } - if (before(t->when, timer_base->when)) { - t->next = timer_base; - timer_base = t; - timer_table[NET_TIMER].expires = timer_base->when; - timer_active |= (1 << NET_TIMER); - t->running = 4; + if (timeout != -1) + t->timeout = timeout; - sti(); - return; - } - tm = timer_base; - while (tm != NULL) { - if (tm->next == NULL || t->when < tm->next->when) { - t->next = tm->next; - tm->next = t; - timer_table[NET_TIMER].expires = timer_base->when; - timer_active |= (1 << NET_TIMER); - t->running = 5; - sti(); - return; - } - tm = tm->next; - } - sti(); +#if 1 + /* FIXME: ??? */ + if ((int) len < 0) /* prevent close to infinite timers. THEY _DO_ */ + len = 3; /* happen (negative values ?) - don't ask me why ! -FB */ +#endif + t->timer.expires = len; + add_timer (&t->timer); } @@ -156,184 +75,135 @@ * sockets that need it. */ void -net_timer(void) +net_timer (unsigned long data) { - struct sock *sk; - struct timer *tm; - - cli(); /* a timer might expire and a new one with */ - /* earlier expiration could be inserted before -FB */ - tm = timer_base; - while ((tm != NULL) && (jiffies >= tm->when)) { - int why; - - sk = tm->sk; - if (sk->inuse) { - break; - } - sk->inuse = 1; - sti(); - why = sk->timeout; - - DPRINTF((DBG_TMR, "net_timer: found sk=%X why = %d\n", sk, why)); - if (sk->keepopen) { - sk->time_wait.len = TCP_TIMEOUT_LEN; - sk->timeout = TIME_KEEPOPEN; - reset_timer(tm); - } else { - sk->timeout = 0; - delete_timer(tm); - } - - /* Always see if we need to send an ack. */ - if (sk->ack_backlog) { - sk->prot->read_wakeup(sk); - if (!sk->dead) wake_up(sk->sleep); - } - - /* Now we need to figure out why the socket was on the timer. */ - switch (why) { - case TIME_DONE: - if (!sk->dead || sk->state != TCP_CLOSE) { - printk("non dead socket in time_done\n"); - release_sock(sk); - break; - } - destroy_sock(sk); - break; - case TIME_DESTROY: - /* - * We've waited for a while for all the memory - * assosiated with the socket to be freed. We - * need to print an error message. - */ - DPRINTF((DBG_TMR, "possible memory leak. sk = %X\n", sk)); - destroy_sock(sk); - sk->inuse = 0; - break; - case TIME_CLOSE: - /* We've waited long enough, close the socket. */ - sk->state = TCP_CLOSE; - delete_timer(&sk->time_wait); - - /* - * Kill the ARP entry in case the hardware - * has changed. - */ - arp_destroy(sk->daddr); - if (!sk->dead) wake_up(sk->sleep); - sk->shutdown = SHUTDOWN_MASK; - sk->time_wait.len = TCP_DONE_TIME; - sk->timeout = TIME_DESTROY; - reset_timer (&sk->time_wait); - release_sock(sk); - break; - case TIME_WRITE: /* try to retransmit. */ - /* - * It could be we got here because we - * needed to send an ack. So we need - * to check for that. - */ - if (sk->send_head != NULL) { - if (jiffies < (sk->send_head->when + - backoff(sk->backoff) * - (2 * sk->mdev + sk->rtt))) { -/* printk("timer: not yet\n"); */ - sk->time_wait.len = - (sk->send_head->when + - backoff(sk->backoff) * - (2 * sk->mdev + sk->rtt)) - jiffies; - sk->timeout = TIME_WRITE; - reset_timer(&sk->time_wait); - release_sock(sk); - break; - } -/* printk("timer: seq %d retrans %d out %d cong %d\n", sk->send_head->h.seq, - sk->retransmits, sk->packets_out, sk->cong_window); */ - DPRINTF((DBG_TMR, "retransmitting.\n")); - sk->prot->retransmit(sk, 0); - if ((sk->state == TCP_ESTABLISHED && - sk->retransmits != 0 && - (sk->retransmits & 7) == 0) || - (sk->state != TCP_ESTABLISHED && - sk->retransmits > TCP_RETR1)) { - DPRINTF((DBG_TMR, "timer.c TIME_WRITE time-out 1\n")); - arp_destroy(sk->daddr); - ip_route_check(sk->daddr); - } - if (sk->state != TCP_ESTABLISHED && - sk->retransmits > TCP_RETR2) { - DPRINTF((DBG_TMR, "timer.c TIME_WRITE time-out 2\n")); - sk->err = ETIMEDOUT; - if (sk->state == TCP_FIN_WAIT1 || - sk->state == TCP_FIN_WAIT2 || - sk->state == TCP_LAST_ACK) { - sk->state = TCP_TIME_WAIT; - sk->timeout = TIME_CLOSE; - sk->time_wait.len = - TCP_TIMEWAIT_LEN; - reset_timer(&sk->time_wait); - } else { - sk->prot->close(sk,1); - break; - } - } - } - release_sock(sk); - break; - case TIME_KEEPOPEN: - /* Send something to keep the connection open. */ - if (sk->prot->write_wakeup != NULL) - sk->prot->write_wakeup(sk); - sk->retransmits ++; - if (sk->shutdown == SHUTDOWN_MASK) { - sk->prot->close(sk,1); - sk->state = TCP_CLOSE; - } - - if ((sk->state == TCP_ESTABLISHED && - sk->retransmits != 0 && - (sk->retransmits & 7) == 0) || - (sk->state != TCP_ESTABLISHED && - sk->retransmits > TCP_RETR1)) { - DPRINTF((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 1\n")); - arp_destroy(sk->daddr); - ip_route_check(sk->daddr); - release_sock(sk); - break; - } - if (sk->state != TCP_ESTABLISHED && - sk->retransmits > TCP_RETR2) { - DPRINTF((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 2\n")); - arp_destroy (sk->daddr); - sk->err = ETIMEDOUT; - if (sk->state == TCP_FIN_WAIT1 || - sk->state == TCP_FIN_WAIT2) { - sk->state = TCP_TIME_WAIT; - if (!sk->dead) wake_up(sk->sleep); - release_sock(sk); - } else { - sk->prot->close (sk, 1); - } - break; - } - release_sock(sk); - break; - default: - printk("net timer expired - reason unknown, sk=%08X\n", - (int) sk); - release_sock(sk); - break; - } - cli(); - tm = timer_base; - } - - /* Now we need to reset the timer. */ - - if (timer_base != NULL) { - timer_table[NET_TIMER].expires = timer_base->when; - timer_active |= 1 << NET_TIMER; + struct sock *sk = (struct sock*)data; + int why = sk->timeout; + /* timeout is overwritten by 'delete_timer' and 'reset_timer' */ + + if (sk->inuse) { + sk->timer.expires = 10; + add_timer(&sk->timer); + return; + } + sk->inuse = 1; + + DPRINTF ((DBG_TMR, "net_timer: found sk=%X why = %d\n", sk, why)); + if (sk->keepopen) + reset_timer (sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); + + /* Always see if we need to send an ack. */ + if (sk->ack_backlog) { + sk->prot->read_wakeup (sk); + if (! sk->dead) + wake_up (sk->sleep); + } + + /* Now we need to figure out why the socket was on the timer. */ + switch (why) { + case TIME_DONE: + if (! sk->dead || sk->state != TCP_CLOSE) { + printk ("non dead socket in time_done\n"); + release_sock (sk); + break; + } + destroy_sock (sk); + break; + case TIME_DESTROY: + /* We've waited for a while for all the memory associated with + * the socket to be freed. We need to print an error message. + */ + DPRINTF ((DBG_TMR, "possible memory leak. sk = %X\n", sk)); + destroy_sock (sk); + sk->inuse = 0; + break; + case TIME_CLOSE: + /* We've waited long enough, close the socket. */ + sk->state = TCP_CLOSE; + delete_timer (sk); + /* Kill the ARP entry in case the hardware has changed. */ + arp_destroy (sk->daddr); + if (!sk->dead) + wake_up (sk->sleep); + sk->shutdown = SHUTDOWN_MASK; + reset_timer (sk, TIME_DESTROY, TCP_DONE_TIME); + release_sock (sk); + break; + case TIME_WRITE: /* try to retransmit. */ + /* It could be we got here because we needed to send an ack. + * So we need to check for that. + */ + if (sk->send_head) { + if (jiffies < (sk->send_head->when + backoff (sk->backoff) + * (2 * sk->mdev + sk->rtt))) { + reset_timer (sk, TIME_WRITE, (sk->send_head->when + + backoff (sk->backoff) * (2 * sk->mdev + sk->rtt)) - jiffies); + release_sock (sk); + break; + } + /* printk("timer: seq %d retrans %d out %d cong %d\n", sk->send_head->h.seq, + sk->retransmits, sk->packets_out, sk->cong_window); */ + DPRINTF ((DBG_TMR, "retransmitting.\n")); + sk->prot->retransmit (sk, 0); + if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7)) + || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) { + DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 1\n")); + arp_destroy (sk->daddr); + ip_route_check (sk->daddr); + } + if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) { + DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 2\n")); + sk->err = ETIMEDOUT; + if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 + || sk->state == TCP_LAST_ACK) { + sk->state = TCP_TIME_WAIT; + reset_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + } else { + sk->prot->close (sk, 1); + break; + } + } + } + release_sock (sk); + break; + case TIME_KEEPOPEN: + /* Send something to keep the connection open. */ + if (sk->prot->write_wakeup) + sk->prot->write_wakeup (sk); + sk->retransmits++; + if (sk->shutdown == SHUTDOWN_MASK) { + sk->prot->close (sk, 1); + sk->state = TCP_CLOSE; + } + + if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7)) + || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) { + DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 1\n")); + arp_destroy (sk->daddr); + ip_route_check (sk->daddr); + release_sock (sk); + break; + } + if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) { + DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 2\n")); + arp_destroy (sk->daddr); + sk->err = ETIMEDOUT; + if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) { + sk->state = TCP_TIME_WAIT; + if (!sk->dead) + wake_up (sk->sleep); + release_sock (sk); + } else { + sk->prot->close (sk, 1); + } + break; + } + release_sock (sk); + break; + default: + printk ("net timer expired - reason unknown, sk=%08X\n", (int)sk); + release_sock (sk); + break; } - sti(); } + diff -u --recursive --new-file pl11/linux/net/inet/timer.h linux/net/inet/timer.h --- pl11/linux/net/inet/timer.h Sat May 29 12:29:07 1993 +++ linux/net/inet/timer.h @@ -1,43 +0,0 @@ -/* - * INET An implementation of the TCP/IP protocol suite for the LINUX - * operating system. INET is implemented using the BSD Socket - * interface as the means of communication with the user level. - * - * Definitions for the TIMER module. - * - * Version: @(#)timer.h 1.0.2 05/23/93 - * - * Authors: Ross Biro, - * Fred N. van Kempen, - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ -#ifndef _TIMER_H -#define _TIMER_H - - -#define SEQ_TICK 3 -#define timer_seq jiffies - - -struct timer { - unsigned long len; - struct sock *sk; - unsigned long when; - int running; - struct timer *next; -}; - - -extern unsigned long seq_offset; - - -extern void delete_timer(struct timer *); -extern void reset_timer(struct timer *); -extern void net_timer(void); - - -#endif /* _TIMER_H */ diff -u --recursive --new-file pl11/linux/net/inet/udp.c linux/net/inet/udp.c --- pl11/linux/net/inet/udp.c Sat Jul 3 12:13:25 1993 +++ linux/net/inet/udp.c Sat Jul 31 15:42:23 1993 @@ -28,7 +28,6 @@ #include #include #include "inet.h" -#include "timer.h" #include "dev.h" #include "ip.h" #include "protocol.h" diff -u --recursive --new-file pl11/linux/net/inet/utils.c linux/net/inet/utils.c --- pl11/linux/net/inet/utils.c Sat Jul 3 11:45:57 1993 +++ linux/net/inet/utils.c Sat Jul 31 15:42:23 1993 @@ -30,12 +30,10 @@ #include "inet.h" #include "dev.h" #include "eth.h" -#include "timer.h" #include "ip.h" #include "protocol.h" #include "tcp.h" #include "skbuff.h" -#include "sock.h" #include "arp.h" diff -u --recursive --new-file pl11/linux/net/inet/wd.c linux/net/inet/wd.c --- pl11/linux/net/inet/wd.c Sat Jul 3 12:52:36 1993 +++ linux/net/inet/wd.c Fri Aug 13 04:24:28 1993 @@ -15,7 +15,7 @@ */ static char *version = - "wd.c:v0.99-10 5/28/93 Donald Becker (becker@super.org)\n"; + "wd.c:v0.99-12 8/12/93 Donald Becker (becker@super.org)\n"; #include #include @@ -27,14 +27,10 @@ #include "dev.h" #include "8390.h" - -extern void NS8390_init(struct device *dev, int startp); -extern int ei_debug; -extern struct sigaction ei_sigaction; - int wdprobe(int ioaddr, struct device *dev); int wdprobe1(int ioaddr, struct device *dev); +static int wd_open(struct device *dev); static void wd_reset_8390(struct device *dev); static int wd_block_input(struct device *dev, int count, char *buf, int ring_offset); @@ -84,121 +80,81 @@ unsigned char *station_addr = dev->dev_addr; int checksum = 0; int ancient = 0; /* An old card without config registers. */ + int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */ + char *model_name; -#if defined(EI_DEBUG) && EI_DEBUG > 2 - printk("WD80x3 ethercard at %#3x:", ioaddr); - for (i = 0; i < 16; i++) { - printk(" %2.2X", inb(ioaddr+i)); - } - printk("\n"); - printk("WD80x3 ethercard at %#3x:", ioaddr+i); - for (;i < 33; i++) { - printk(" %2.2X", inb(ioaddr+i)); - } - printk("\n"); -#endif - printk("WD80x3 ethercard probe at %#3x:", ioaddr); - for (i = 0; i < 8; i++) { - int inval = inb(ioaddr + 8 + i); - checksum += inval; - if (i < 6) - printk(" %2.2X", (station_addr[i] = inval)); - } - - if ((checksum & 0xff) != 0xFF) { - printk(" not found (%#2.2x).\n", checksum); + for (i = 0; i < 8; i++) + checksum += inb(ioaddr + 8 + i); + if ((checksum & 0xff) != 0xFF) return 0; - } - - ei_status.name = "WD8003"; - ei_status.word16 = 0; + + printk("%s: WD80x3 at %#3x, ", dev->name, ioaddr); + for (i = 0; i < 6; i++) + printk(" %2.2X", station_addr[i] = inb(ioaddr + 8 + i)); /* The following PureData probe code was contributed by - Mike Jagdis . */ - /* Puredata seem to do software configuration differently from - * others so we have to check for them. - */ + Mike Jagdis . Puredata seem to do software + configuration differently from others so we have to check for them. + This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card. + */ if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') { - ei_status.name = "PDI8023"; + unsigned char reg5 = inb(ioaddr+5); - /* We can also check whether it is an 8 bit (-8), 16 bit (-16) - * or dumb (Toshiba) card. The Toshiba version doesn't support - * anything except jumper configuration. - */ switch (inb(ioaddr+2)) { - case 0x03: - ei_status.word16 = 0; - ei_status.name = "PDI8023-8"; - break; - - case 0x05: - /* Not sure really... */ - ei_status.word16 = 0; - ei_status.name = "PDUC8023"; - break; - - case 0x0a: - ei_status.word16 = 1; - ei_status.name = "PDI8023-16"; - break; - - default: - /* Either 0x01 (dumb) or they've released a new version. */ - ei_status.word16 = 0; - ei_status.name = "PDI8023"; - break; + case 0x03: word16 = 0; model_name = "PDI8023-8"; break; + case 0x05: word16 = 0; model_name = "PDUC8023"; break; + case 0x0a: word16 = 1; model_name = "PDI8023-16"; break; + /* Either 0x01 (dumb) or they've released a new version. */ + default: word16 = 0; model_name = "PDI8023"; break; } - dev->mem_start = ((inb(ioaddr+5) & 0x1c) + 0xc0) << 12; - dev->irq = (inb(ioaddr+5) >> 5) & 0x07; - if (dev->irq == 7) - dev->irq = 10; - else - dev->irq++; - } /* End of PureData probe */ - - /* This method of checking for a 16-bit board is borrowed from the - we.c driver. A simpler method is just to look in ASIC reg. 0x03. - I'm comparing the two method in alpha test to make certain they - return the same result. */ -#ifndef FORCE_8BIT /* Same define as we.c. */ - /* Check for 16 bit board - it doesn't have register 0/8 aliasing. - Do NOT check i>=6 here -- it hangs some old 8003 boards! */ - for (i = 0; i < 6; i++) - if (inb(ioaddr+i) != inb(ioaddr+8+i)) - break; - if (i >= 6) { - ancient = 1; - ei_status.name = "WD8003-old"; - } else { - int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */ - outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */ - if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */ - && (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */ - int asic_reg5 = inb(ioaddr+WD_CMDREG5); - /* Magic to set ASIC to word-wide mode. */ - outb( ISA16 | NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5); - outb(tmp, ioaddr+1); - ei_status.name = "WD8013"; - ei_status.word16 = 1; /* We have a 16bit board here! */ + dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12; + dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1; + } else { /* End of PureData probe */ + /* This method of checking for a 16-bit board is borrowed from the + we.c driver. A simpler method is just to look in ASIC reg. 0x03. + I'm comparing the two method in alpha test to make certain they + return the same result. */ + /* Check for the old 8 bit board - it has register 0/8 aliasing. + Do NOT check i>=6 here -- it hangs the old 8003 boards! */ + for (i = 0; i < 6; i++) + if (inb(ioaddr+i) != inb(ioaddr+8+i)) + break; + if (i >= 6) { + ancient = 1; + model_name = "WD8003-old"; + word16 = 0; + } else { + int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */ + outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */ + if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */ + && (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */ + int asic_reg5 = inb(ioaddr+WD_CMDREG5); + /* Magic to set ASIC to word-wide mode. */ + outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5); + outb(tmp, ioaddr+1); + model_name = "WD8013"; + word16 = 1; /* We have a 16bit board here! */ + } else { + model_name = "WD8003"; + word16 = 0; + } + outb(tmp, ioaddr+1); /* Restore original reg1 value. */ } - outb(tmp, ioaddr+1); /* Restore original reg1 value. */ - } -#endif /* not FORCE_8BIT */ - #ifndef final_version - if ( !ancient && (inb(ioaddr+1) & 0x01) != (ei_status.word16 & 0x01)) - printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).", - ei_status.word16 ? 16:8, (inb(ioaddr+1) & 0x01) ? 16 : 8); + if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01)) + printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).", + word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8); #endif + } #if defined(WD_SHMEM) && WD_SHMEM > 0x80000 - /* Allow an override. */ + /* Allow a compile-time override. */ dev->mem_start = WD_SHMEM; #else if (dev->mem_start == 0) { /* Sanity and old 8003 check */ int reg0 = inb(ioaddr); - if (reg0 == 0xff) { + if (reg0 == 0xff || reg0 == 0) { /* Future plan: this could check a few likely locations first. */ dev->mem_start = 0xd0000; printk(" assigning address %#x", dev->mem_start); @@ -205,7 +161,7 @@ } else { int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f; /* Some boards don't have the register 5 -- it returns 0xff. */ - if (high_addr_bits == 0x1f || ei_status.word16 == 0) + if (high_addr_bits == 0x1f || word16 == 0) high_addr_bits = 0x01; dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19); } @@ -215,26 +171,15 @@ /* The 8390 isn't at the base address -- the ASIC regs are there! */ dev->base_addr = ioaddr+WD_NIC_OFFSET; - ei_status.tx_start_page = WD_START_PG; - ei_status.rx_start_page = WD_START_PG + TX_PAGES; - ei_status.stop_page = ei_status.word16 ? WD13_STOP_PG : WD03_STOP_PG; - - dev->rmem_start = dev->mem_start + TX_PAGES*256; - dev->mem_end = dev->rmem_end - = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256; - if (dev->irq < 2) { int irqmap[] = {9,3,5,7,10,11,15,4}; int reg1 = inb(ioaddr+1); int reg4 = inb(ioaddr+4); - if (reg1 == 0xff){ /* Ack!! No way to read the IRQ! */ - dev->irq = ei_status.word16 ? 10 : 5; - } else - dev->irq = irqmap[((reg4 >> 5) & 0x03) - + (reg1 & 0x04)]; - } else if (dev->irq == 2) - /* Fixup for users that don't know that IRQ 2 is really IRQ 9, - or don't know which one to set. */ + if (reg1 == 0xff) /* Ack!! No way to read the IRQ! */ + dev->irq = word16 ? 10 : 5; + else + dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)]; + } else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */ dev->irq = 9; /* Snarf the interrupt now. There's no point in waiting since we cannot @@ -246,27 +191,29 @@ } } - printk("\n%s: %s using IRQ %d with shared memory at %#x-%#x.\n", - dev->name, ei_status.name, dev->irq, dev->mem_start, dev->mem_end-1); - if (ei_debug > 1) - printk(version); + /* OK, were are certain this is going to work. Setup the device. */ + ethdev_init(dev); -#if defined(EI_DEBUG) && EI_DEBUG > 2 - printk("%s: Address read from register is %#x, setting address %#x\n", - ei_status.name, - ((inb(ioaddr+WD_CMDREG5)&0x1f)<<19) + ((inb(ioaddr)&0x3f) << 13), - dev->mem_start); -#endif + ei_status.name = model_name; + ei_status.word16 = word16; + ei_status.tx_start_page = WD_START_PG; + ei_status.rx_start_page = WD_START_PG + TX_PAGES; + ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG; - /* Map in the shared memory. Always set register 0 last to remain - compatible with very old boards. */ - if (ei_status.word16) - outb( ISA16 | NIC16 | ((dev->mem_start>>19) & 0x1f), ioaddr+WD_CMDREG5); - outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), ioaddr); /* WD_CMDREG */ + /* Don't map in the shared memory until the board is actually opened. */ + dev->rmem_start = dev->mem_start + TX_PAGES*256; + dev->mem_end = dev->rmem_end + = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256; + + printk(" %s, IRQ %d, shared memory at %#x-%#x.\n", + model_name, dev->irq, dev->mem_start, dev->mem_end-1); + if (ei_debug > 0) + printk(version); ei_status.reset_8390 = &wd_reset_8390; ei_status.block_input = &wd_block_input; ei_status.block_output = &wd_block_output; + dev->open = &wd_open; dev->stop = &wd_close_card; NS8390_init(dev, 0); @@ -273,6 +220,22 @@ return dev->base_addr; } +static int +wd_open(struct device *dev) +{ + int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ + + /* Map in the shared memory. Always set register 0 last to remain + compatible with very old boards. */ + ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB; + ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16; + + if (ei_status.word16) + outb(ISA16 | ei_status.reg5, ioaddr+WD_CMDREG5); + outb(ei_status.reg0, ioaddr); /* WD_CMDREG */ + return ei_open(dev); +} + static void wd_reset_8390(struct device *dev) { @@ -306,20 +269,7 @@ printk("%s: wd_reset_8390() did not complete.\n", dev->name); break; } -#if defined(EI_DEBUG) && EI_DEBUG > 2 - { - int i; - printk("\nWD80x3 ethercard at %#3x:", wd_cmd_port); - for (i = 0; i < 16; i++) { - printk(" %2.2X", inb(wd_cmd_port+i)); - } - printk("\nWD80x3 ethercard at %#3x:", wd_cmd_port+i); - for (;i < 33; i++) { - printk(" %2.2X", inb(wd_cmd_port+i)); - } - printk("\n"); - } -#endif + return; } /* Block input and output are easy on shared memory ethercards, and trivial @@ -328,16 +278,18 @@ static int wd_block_input(struct device *dev, int count, char *buf, int ring_offset) { - void *xfer_start = (void *)(dev->mem_start + ring_offset - (WD_START_PG<<8)); + void *xfer_start = (void *)(dev->mem_start + ring_offset + - (WD_START_PG<<8)); + + /* This mapout won't be necessary when wd_close_card is called. */ #if !defined(WD_no_mapout) int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - int reg5_val = ((dev->mem_start>>19) & 0x1f) | NIC16; - /* Map in the shared memory. */ if (ei_status.word16) - outb(ISA16 | reg5_val, wd_cmdreg + WD_CMDREG5 ); - outb((((dev->mem_start>>13) & 0x3f) | WD_MEMENB), wd_cmdreg); + outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); + outb(ei_status.reg0, wd_cmdreg); /* WD_CMDREG */ #endif + if (xfer_start + count > (void*) dev->rmem_end) { /* We must wrap the input move. */ int semi_count = (void*)dev->rmem_end - xfer_start; @@ -353,10 +305,11 @@ dev->name, count, ring_offset, xfer_start, board[-1], board[0], board[1]); } + #if !defined(WD_no_mapout) /* Turn off 16 bit access so that reboot works. */ if (ei_status.word16) - outb(reg5_val, wd_cmdreg + WD_CMDREG5 ); + outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); #endif return ring_offset + count; } @@ -369,23 +322,24 @@ { unsigned char *shmem = (unsigned char *)dev->mem_start + ((start_page - WD_START_PG)<<8); + #if !defined(WD_no_mapout) int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - int reg5_val = ((dev->mem_start>>19) & 0x1f) | NIC16; - /* Map in the shared memory. */ if (ei_status.word16) - outb(ISA16 | reg5_val, wd_cmdreg + WD_CMDREG5 ); - outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmdreg); + outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); + outb(ei_status.reg0, wd_cmdreg); /* WD_CMDREG */ #endif + memcpy(shmem, buf, count); if (ei_debug > 4) printk("%s: wd80*3 block_output(addr=%#x cnt=%d) -> %2x=%2x %2x=%2x %d...\n", shmem, count, shmem[23], buf[23], shmem[24], buf[24], memcmp(shmem,buf,count)); + #if !defined(WD_no_mapout) /* Turn off 16 bit access so that reboot works. */ if (ei_status.word16) - outb(reg5_val, wd_cmdreg + WD_CMDREG5 ); + outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); #endif } @@ -399,14 +353,16 @@ printk("%s: Shutting down ethercard.\n", dev->name); NS8390_init(dev, 0); - /* Turn off 16-bit shared memory so reboot works. */ - outb(((dev->mem_start>>19) & 0x1f) | NIC16, wd_cmdreg + WD_CMDREG5 ); - outb((((dev->mem_start>>13) & 0x3f)), wd_cmdreg); + /* Change from 16-bit to 8-bit shared memory so reboot works. */ + outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 ); + /* And disable the shared memory. */ + outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg); + return 0; } - + /* * Local variables: * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c wd.c" diff -u --recursive --new-file pl11/linux/net/unix/sock.c linux/net/unix/sock.c --- pl11/linux/net/unix/sock.c Wed Jul 14 12:21:09 1993 +++ linux/net/unix/sock.c Mon Aug 9 18:02:33 1993 @@ -282,7 +282,7 @@ printk("UNIX: create: can't allocate buffer\n"); return(-ENOMEM); } - if (!(upd->buf =(char *)get_free_page(GFP_USER))) { + if (!(upd->buf = (char*) get_free_page(GFP_USER))) { printk("UNIX: create: can't get page!\n"); unix_data_deref(upd); return(-ENOMEM); @@ -369,7 +369,7 @@ fname[sockaddr_len-UN_PATH_OFFSET] = '\0'; old_fs = get_fs(); set_fs(get_ds()); - i = do_mknod(fname, S_IFSOCK | 0777, 0); + i = do_mknod(fname, S_IFSOCK | S_IRWXUGO, 0); if (i == 0) i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL); set_fs(old_fs); if (i < 0) { diff -u --recursive --new-file pl11/linux/zBoot/misc.c linux/zBoot/misc.c --- pl11/linux/zBoot/misc.c Sat Jun 5 01:09:25 1993 +++ linux/zBoot/misc.c Sun Aug 15 01:04:40 1993 @@ -4,7 +4,8 @@ * This is a collection of several routines from gzip-1.0.3 * adapted for Linux. * - * malloc and puts by Hannu Savolainen 1993 + * malloc by Hannu Savolainen 1993 + * puts by Nick Holloway 1993 */ #include "gzip.h" @@ -75,7 +76,6 @@ local int get_method(int); char *vidmem = (char *)0xb8000; -int vidp = 0; int lines, cols; void *malloc(int size) @@ -101,20 +101,44 @@ { /* Don't care */ } +static void scroll() +{ + int i; + + memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 ); + for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 ) + vidmem[i] = ' '; +} + static void puts(char *s) { - int i,n; - for (n = 0; s [n] != '\0'; n++); - if (!n) n = 10; - - for (i=0;i= lines ) { + scroll(); + y--; + } + } else { + vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } + } } + + SCREEN_INFO.orig_x = x; + SCREEN_INFO.orig_y = y; } __ptr_t memset(__ptr_t s, int c, size_t n) @@ -279,8 +303,6 @@ vidmem = (char *) 0xb0000; else vidmem = (char *) 0xb8000; - vidp = 0; - vidmem[0] = '0'; lines = SCREEN_INFO.orig_video_lines; cols = SCREEN_INFO.orig_video_cols; @@ -304,7 +326,7 @@ work(0, 0); - puts("done.\n\n"); + puts("done.\n"); puts("Now booting the kernel\n"); }