home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-03-03 | 1.5 MB | 56,931 lines |
Text Truncated. Only the first 1MB is shown below. Download the file for the complete contents.
- diff -urNwbB linux/Makefile linux.arm/Makefile
- --- linux/Makefile Sun Mar 3 11:42:06 1996
- +++ linux.arm/Makefile Sun Feb 11 13:47:34 1996
- @@ -2,7 +2,7 @@
- PATCHLEVEL = 3
- SUBLEVEL = 35
-
- -ARCH = i386
- +ARCH = arm
-
- .EXPORT_ALL_VARIABLES:
-
- @@ -86,13 +86,19 @@
- # Include the make variables (CC, etc...)
- #
-
- +# Modified 11/02/96 by Russell King
- +# - Doesn't include the drivers subdirectory as standard.
- +# - Modified the 'nm' line in vmlinux so that we remove local and absolute symbols.
- +
- ARCHIVES =kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o net/network.a
- FILESYSTEMS =fs/filesystems.a
- -DRIVERS =drivers/block/block.a \
- +DRIVERS =
- +LIBS =$(TOPDIR)/lib/lib.a
- +SUBDIRS =kernel mm fs net ipc lib
- +
- +DRIVERS :=drivers/block/block.a \
- drivers/char/char.a \
- drivers/net/net.a
- -LIBS =$(TOPDIR)/lib/lib.a
- -SUBDIRS =kernel drivers mm fs net ipc lib
-
- ifdef CONFIG_SCSI
- DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
- @@ -108,6 +114,10 @@
-
- include arch/$(ARCH)/Makefile
-
- +ifndef CONFIG_ARM
- +SUBDIRS := drivers $(SUBDIRS)
- +endif
- +
- .S.s:
- $(CC) -D__ASSEMBLY__ -traditional -E -o $*.s $<
- .S.o:
- @@ -125,7 +135,7 @@
- $(FILESYSTEMS) \
- $(DRIVERS) \
- $(LIBS) -o vmlinux
- - $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | sort > System.map
- + $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)\|\( A \)\|\( t L\)' | sort > System.map
-
- symlinks:
- rm -f include/asm
- diff -urNwbB linux/arch/arm/Makefile linux.arm/arch/arm/Makefile
- --- linux/arch/arm/Makefile Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/Makefile Sat Feb 24 13:08:03 1996
- @@ -0,0 +1,70 @@
- +#
- +# arch/arm/Makefile
- +#
- +# This file is included by the global makefile so that you can add your own
- +# architecture-specific flags and dependencies. Remember to do have actions
- +# for "archclean" and "archdep" for cleaning up and making dependencies for
- +# this architecture
- +#
- +# This file is subject to the terms and conditions of the GNU General Public
- +# License. See the file "COPYING" in the main directory of this archive
- +# for more details.
- +#
- +# Copyright (C) 1995 by Russell King
- +#
- +
- +#
- +# Set these to indicate how to link it..
- +# -qmagic (we need to remove the 32 byte header for bootup purposes)
- +#
- +
- +SUBARCH = -m3
- +
- +CPP = $(CC) -E $(SUBARCH)
- +CFLAGS := $(CFLAGS:-fomit-frame-pointer=) $(SUBARCH)
- +LINKFLAGS = -Ttext 0x01800000
- +ZLINKFLAGS = -N -Ttext 0x01800000
- +
- +HEAD := arch/arm/kernel/head.o
- +SUBDIRS := arch/arm/drivers arch/arm/kernel arch/arm/mm arch/arm/lib $(SUBDIRS)
- +ARCHIVES := arch/arm/kernel/kernel.o arch/arm/mm/mm.o arch/arm/lib/lib.o $(ARCHIVES)
- +LIBS := $(LIBS) `gcc --print-libgcc-file-name`
- +
- +DRIVERS := arch/arm/drivers/block/block.a arch/arm/drivers/char/char.a \
- + arch/arm/drivers/net/net.a
- +
- +ifdef CONFIG_SOUND
- +DRIVERS := $(DRIVERS) arch/arm/drivers/sound/sound.a
- +endif
- +
- +ifdef CONFIG_SCSI
- +DRIVERS := $(DRIVERS) arch/arm/drivers/scsi/scsi.a
- +endif
- +
- +
- +arch/arm/kernel: dummy
- + $(MAKE) linuxsubdirs SUBDIRS=arch/arm/kernel
- +
- +arch/arm/mm: dummy
- + $(MAKE) linuxsubdirs SUBDIRS=arch/arm/mm
- +
- +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
- +
- +install:
- + @$(MAKEBOOT) install
- +
- +zinstall:
- + @$(MAKEBOOT) zinstall
- +
- +zImage:
- + @$(MAKEBOOT) zImage
- +
- +archclean:
- + @$(MAKEBOOT) clean
- + @$(MAKE) -C arch/$(ARCH)/drivers/block LINKCLEAN
- + @$(MAKE) -C arch/$(ARCH)/drivers/char LINKCLEAN
- + @$(MAKE) -C arch/$(ARCH)/drivers/net LINKCLEAN
- + @$(MAKE) -C arch/$(ARCH)/drivers/scsi LINKCLEAN
- +
- +archdep:
- + @$(MAKEBOOT) dep
- diff -urNwbB linux/arch/arm/boot/Makefile linux.arm/arch/arm/boot/Makefile
- --- linux/arch/arm/boot/Makefile Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/Makefile Sun Feb 11 21:59:54 1996
- @@ -0,0 +1,47 @@
- +#
- +# arch/i386/boot/Makefile
- +#
- +# This file is subject to the terms and conditions of the GNU General Public
- +# License. See the file "COPYING" in the main directory of this archive
- +# for more details.
- +#
- +# Copyright (C) 1994 by Linus Torvalds
- +#
- +
- +Image: $(CONFIGURE) tools/build $(TOPDIR)/vmlinux
- + tools/build $(TOPDIR)/vmlinux > Image
- + sync
- +
- +zImage: $(CONFIGURE) tools/build compressed/vmlinux
- + tools/build compressed/vmlinux > zImage
- +
- +compressed/vmlinux: $(TOPDIR)/vmlinux dep
- + @$(MAKE) -C compressed vmlinux
- +
- +install: $(CONFIGURE) Image
- + sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) Image $(TOPDIR)/System.map "$(INSTALL_PATH)"
- +
- +zinstall: $(CONFIGURE) zImage
- + sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
- +
- +tools/build: tools/build.c
- + $(HOSTCC) $(CFLAGS) -o $@ $< -I$(TOPDIR)/include
- +
- +clean:
- + rm -f Image zImage tools/build
- + @$(MAKE) -C compressed clean
- +
- +dep:
- +
- +#zdisk: zImage
- +# dd bs=8192 if=zImage of=/dev/fd0
- +
- +#zlilo: $(CONFIGURE) zImage
- +# if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi
- +# if [ -f $(INSTALL_PATH)/System.map ]; then mv $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi
- +# cat zImage > $(INSTALL_PATH)/vmlinuz
- +# cp $(TOPDIR)/System.map $(INSTALL_PATH)/
- +# if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
- +
- +#install: $(CONFIGURE) zImage
- +# sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
- diff -urNwbB linux/arch/arm/boot/compressed/Makefile linux.arm/arch/arm/boot/compressed/Makefile
- --- linux/arch/arm/boot/compressed/Makefile Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/compressed/Makefile Sun Mar 3 13:35:37 1996
- @@ -0,0 +1,56 @@
- +#
- +# linux/arch/arm/boot/compressed/Makefile
- +#
- +# create a compressed vmlinux image from the original vmlinux
- +#
- +
- +CFLAGS = -O2 -DSTDC_HEADERS
- +LOADADDR = 0x01800000
- +RELADDR = 0x01960000
- +
- +SYSTEM = $(TOPDIR)/vmlinux
- +DECOMP_OBJS = inflate.o unzip.o misc.o ../../lib/ll_char_wr.o
- +
- +
- +all: vmlinux
- +
- +vmlinux: head.o piggy.o
- + $(LD) -Ttext=$(LOADADDR) -o vmlinux head.o piggy.o
- +
- +head.o: head.S
- + $(CC) -traditional -DRELADDR=$(RELADDR) -c head.S
- +
- +piggy.o: decomphead.o decompress.a compressed.o
- + $(LD) -s -Ttext=$(RELADDR) -o a.out decomphead.o compressed.o decompress.a
- + strip a.out
- + ./piggyback < a.out > piggy.o
- + rm -f a.out
- +
- +compressed.o: $(SYSTEM) xtract piggyback
- + ./xtract $(SYSTEM) | gzip -9 | ./piggyback > compressed.o
- +
- +
- +# rules for decompression part of kernel
- +
- +decompress.a: $(DECOMP_OBJS)
- + $(AR) $(ARFLAGS) decompress.a $(DECOMP_OBJS)
- +
- +decomphead.o: decomphead.S
- + $(CC) -traditional -DLOADADDR=$(LOADADDR) -c decomphead.S
- +
- +../../lib/ll_char_wr.o:
- + make -C ../../lib ll_char_wr.o
- +
- +
- +# rules for extracting & piggybacking the kernel
- +
- +xtract: xtract.c
- + $(HOSTCC) $(CFLAGS) -o xtract xtract.c
- +
- +piggyback: piggyback.c
- + $(HOSTCC) $(CFLAGS) -o piggyback piggyback.c
- +
- +
- +clean:
- + rm -f xtract piggyback vmlinux decompress.a a.out
- +
- diff -urNwbB linux/arch/arm/boot/compressed/crypt.h linux.arm/arch/arm/boot/compressed/crypt.h
- --- linux/arch/arm/boot/compressed/crypt.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/compressed/crypt.h Sun Feb 11 09:32:10 1996
- @@ -0,0 +1,12 @@
- +/* crypt.h (dummy version) -- do not perform encryption
- + * Hardly worth copyrighting :-)
- + */
- +
- +#ifdef CRYPT
- +# undef CRYPT /* dummy version */
- +#endif
- +
- +#define RAND_HEAD_LEN 12 /* length of encryption random header */
- +
- +#define zencode
- +#define zdecode
- diff -urNwbB linux/arch/arm/boot/compressed/decomphead.S linux.arm/arch/arm/boot/compressed/decomphead.S
- --- linux/arch/arm/boot/compressed/decomphead.S Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/compressed/decomphead.S Sun Feb 11 09:32:10 1996
- @@ -0,0 +1,25 @@
- +/*
- + * linux/arch/arm/boot/decomphead.S
- + */
- +
- +.text
- + .global __stext
- +__stext:
- + b start
- +LC0:
- + .word __edata
- + .word __end
- + .word LOADADDR + 0x23
- + .word __stack_start+16384
- +start:
- + adr r1, LC0
- + ldmia r1, {r2, r3, r4, sp}
- + mov r1, #0
- +clrzlp: str r1, [r2], #4
- + cmp r2, r3
- + blt clrzlp
- + bic r0, r4, #3
- + bl _decompress_kernel
- + mov r0, #0
- + mov pc, r4
- +
- diff -urNwbB linux/arch/arm/boot/compressed/gzip.h linux.arm/arch/arm/boot/compressed/gzip.h
- --- linux/arch/arm/boot/compressed/gzip.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/compressed/gzip.h Sun Feb 11 09:32:10 1996
- @@ -0,0 +1,284 @@
- +/* gzip.h -- common declarations for all gzip modules
- + * Copyright (C) 1992-1993 Jean-loup Gailly.
- + * This is free software; you can redistribute it and/or modify it under the
- + * terms of the GNU General Public License, see the file COPYING.
- + */
- +
- +#if defined(__STDC__) || defined(PROTO)
- +# define OF(args) args
- +#else
- +# define OF(args) ()
- +#endif
- +
- +#ifdef __STDC__
- + typedef void *voidp;
- +#else
- + typedef char *voidp;
- +#endif
- +
- +/* I don't like nested includes, but the string functions are used too often */
- +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
- +# include <string.h>
- +# define memzero(s, n) memset ((s), 0, (n))
- +#else
- +# include <strings.h>
- +# define strchr index
- +# define strrchr rindex
- +# define memcpy(d, s, n) bcopy((s), (d), (n))
- +# define memcmp(s1, s2, n) bcmp((s1), (s2), (n))
- +# define memzero(s, n) bzero((s), (n))
- +#endif
- +
- +#if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H)
- +# include <memory.h>
- +#endif
- +
- +#ifndef RETSIGTYPE
- +# define RETSIGTYPE void
- +#endif
- +
- +#define local static
- +
- +typedef unsigned char uch;
- +typedef unsigned short ush;
- +typedef unsigned long ulg;
- +
- +/* Return codes from gzip */
- +#define OK 0
- +#define ERROR 1
- +#define WARNING 2
- +
- +/* Compression methods (see algorithm.doc) */
- +#define STORED 0
- +#define COMPRESSED 1
- +#define PACKED 2
- +/* methods 3 to 7 reserved */
- +#define DEFLATED 8
- +extern int method; /* compression method */
- +
- +/* To save memory for 16 bit systems, some arrays are overlayed between
- + * the various modules:
- + * deflate: prev+head window d_buf l_buf outbuf
- + * unlzw: tab_prefix tab_suffix stack inbuf outbuf
- + * inflate: window inbuf
- + * unpack: window inbuf
- + * For compression, input is done in window[]. For decompression, output
- + * is done in window except for unlzw.
- + */
- +
- +#ifndef INBUFSIZ
- +# define INBUFSIZ 0x8000 /* input buffer size */
- +#endif
- +#define INBUF_EXTRA 64 /* required by unlzw() */
- +
- +#ifndef OUTBUFSIZ
- +# define OUTBUFSIZ 16384 /* output buffer size */
- +#endif
- +#define OUTBUF_EXTRA 2048 /* required by unlzw() */
- +
- +#define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */
- +
- +#ifdef DYN_ALLOC
- +# define EXTERN(type, array) extern type * near array
- +# define DECLARE(type, array, size) type * near array
- +# define ALLOC(type, array, size) { \
- + array = (type*)fcalloc((unsigned)(((size)+1L)/2), 2*sizeof(type)); \
- + if (array == NULL) error("insufficient memory"); \
- + }
- +# define FREE(array) {if (array != NULL) fcfree(array), array=NULL;}
- +#else
- +# define EXTERN(type, array) extern type array[]
- +# define DECLARE(type, array, size) type array[size]
- +# define ALLOC(type, array, size)
- +# define FREE(array)
- +#endif
- +
- +EXTERN(uch, inbuf); /* input buffer */
- +EXTERN(uch, outbuf); /* output buffer */
- +EXTERN(ush, d_buf); /* buffer for distances, see trees.c */
- +EXTERN(uch, window); /* Sliding window and suffix table (unlzw) */
- +#define tab_suffix window
- +#ifndef MAXSEG_64K
- +# define tab_prefix prev /* hash link (see deflate.c) */
- +# define head (prev+WSIZE) /* hash head (see deflate.c) */
- + EXTERN(ush, tab_prefix); /* prefix code (see unlzw.c) */
- +#else
- +# define tab_prefix0 prev
- +# define head tab_prefix1
- + EXTERN(ush, tab_prefix0); /* prefix for even codes */
- + EXTERN(ush, tab_prefix1); /* prefix for odd codes */
- +#endif
- +
- +extern unsigned insize; /* valid bytes in inbuf */
- +extern unsigned inptr; /* index of next byte to be processed in inbuf */
- +extern unsigned outcnt; /* bytes in output buffer */
- +
- +extern long bytes_in; /* number of input bytes */
- +extern long bytes_out; /* number of output bytes */
- +extern long overhead; /* number of bytes in gzip header */
- +
- +#define isize bytes_in
- +/* for compatibility with old zip sources (to be cleaned) */
- +
- +extern int ifd; /* input file descriptor */
- +extern int ofd; /* output file descriptor */
- +extern char ifname[]; /* input filename or "stdin" */
- +extern char ofname[]; /* output filename or "stdout" */
- +
- +extern ulg time_stamp; /* original time stamp (modification time) */
- +extern long ifile_size; /* input file size, -1 for devices (debug only) */
- +
- +extern int exit_code; /* program exit code */
- +
- +typedef int file_t; /* Do not use stdio */
- +#define NO_FILE (-1) /* in memory compression */
- +
- +
- +#define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */
- +#define OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */
- +#define PKZIP_MAGIC "PK\003\004" /* Magic header for pkzip files */
- +#define PACK_MAGIC "\037\036" /* Magic header for packed files */
- +
- +/* gzip flag byte */
- +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
- +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
- +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
- +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
- +#define COMMENT 0x10 /* bit 4 set: file comment present */
- +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
- +#define RESERVED 0xC0 /* bit 6,7: reserved */
- +
- +/* internal file attribute */
- +#define UNKNOWN (-1)
- +#define BINARY 0
- +#define ASCII 1
- +
- +#ifndef WSIZE
- +# define WSIZE 0x8000 /* window size--must be a power of two, and */
- +#endif /* at least 32K for zip's deflate method */
- +
- +#define MIN_MATCH 3
- +#define MAX_MATCH 258
- +/* The minimum and maximum match lengths */
- +
- +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
- +/* Minimum amount of lookahead, except at the end of the input file.
- + * See deflate.c for comments about the MIN_MATCH+1.
- + */
- +
- +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD)
- +/* In order to simplify the code, particularly on 16 bit machines, match
- + * distances are limited to MAX_DIST instead of WSIZE.
- + */
- +
- +extern int decrypt; /* flag to turn on decryption */
- +extern int save_orig_name; /* set if original name must be saved */
- +extern int verbose; /* be verbose (-v) */
- +extern int level; /* compression level */
- +extern int test; /* check .z file integrity */
- +extern int to_stdout; /* output to stdout (-c) */
- +
- +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
- +
- +/* put_byte is used for the compressed output, put_char for the
- + * uncompressed output. However unlzw() uses window for its
- + * suffix table instead of its output buffer, so it does not use put_char.
- + * (to be cleaned up).
- + */
- +#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\
- + flush_outbuf();}
- +#define put_char(c) {window[outcnt++]=(uch)(c); if (outcnt==WSIZE)\
- + flush_window();}
- +
- +/* Output a 16 bit value, lsb first */
- +#define put_short(w) \
- +{ if (outcnt < OUTBUFSIZ-2) { \
- + outbuf[outcnt++] = (uch) ((w) & 0xff); \
- + outbuf[outcnt++] = (uch) ((ush)(w) >> 8); \
- + } else { \
- + put_byte((uch)((w) & 0xff)); \
- + put_byte((uch)((ush)(w) >> 8)); \
- + } \
- +}
- +
- +/* Output a 32 bit value to the bit stream, lsb first */
- +#define put_long(n) { \
- + put_short((n) & 0xffff); \
- + put_short(((ulg)(n)) >> 16); \
- +}
- +
- +#define seekable() 0 /* force sequential output */
- +#define translate_eol 0 /* no option -a yet */
- +
- +#define tolow(c) (isupper(c) ? (c)-'A'+'a' : (c)) /* force to lower case */
- +
- +/* Macros for getting two-byte and four-byte header values */
- +#define SH(p) ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8))
- +#define LG(p) ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16))
- +
- +/* Diagnostic functions */
- +#ifdef DEBUG
- +# define Assert(cond,msg) {if(!(cond)) error(msg);}
- +# define Trace(x) fprintf x
- +# define Tracev(x) {if (verbose) fprintf x ;}
- +# define Tracevv(x) {if (verbose>1) fprintf x ;}
- +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
- +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
- +#else
- +# define Assert(cond,msg)
- +# define Trace(x)
- +# define Tracev(x)
- +# define Tracevv(x)
- +# define Tracec(c,x)
- +# define Tracecv(c,x)
- +#endif
- +
- + /* in zip.c: */
- +extern void zip OF((int in, int out));
- +extern int file_read OF((char *buf, unsigned size));
- +
- + /* in unzip.c */
- +extern void unzip OF((int in, int out));
- +extern int check_zipfile OF((int in));
- +
- + /* in unpack.c */
- +extern void unpack OF((int in, int out));
- +
- + /* in gzip.c */
- +RETSIGTYPE abort_gzip OF((void));
- +
- + /* in deflate.c */
- +void lm_init OF((int pack_level, ush *flags));
- +ulg deflate OF((void));
- +
- + /* in trees.c */
- +void ct_init OF((ush *attr, int *method));
- +int ct_tally OF((int dist, int lc));
- +ulg flush_block OF((char *buf, ulg stored_len, int eof));
- +
- + /* in bits.c */
- +void bi_init OF((file_t zipfile));
- +void send_bits OF((int value, int length));
- +unsigned bi_reverse OF((unsigned value, int length));
- +void bi_windup OF((void));
- +void copy_block OF((char *buf, unsigned len, int header));
- +extern int (*read_buf) OF((char *buf, unsigned size));
- +
- + /* in util.c: */
- +extern ulg updcrc OF((uch *s, unsigned n));
- +extern void clear_bufs OF((void));
- +extern int fill_inbuf OF((void));
- +extern void flush_outbuf OF((void));
- +extern void flush_window OF((void));
- +extern char *strlwr OF((char *s));
- +extern char *basename OF((char *fname));
- +extern char *add_envopt OF((int *argcp, char ***argvp, char *env));
- +extern void error OF((char *m));
- +extern void warn OF((char *a, char *b));
- +extern void read_error OF((void));
- +extern void write_error OF((void));
- +extern void display_ratio OF((long num, long den));
- +extern voidp xmalloc OF((unsigned int size));
- +
- + /* in inflate.c */
- +extern int inflate OF((void));
- diff -urNwbB linux/arch/arm/boot/compressed/head.S linux.arm/arch/arm/boot/compressed/head.S
- --- linux/arch/arm/boot/compressed/head.S Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/compressed/head.S Sun Feb 11 09:32:10 1996
- @@ -0,0 +1,36 @@
- + .text
- + .align
- +
- +@ Entry point
- +@ r0 = kernel info page
- +@ r1 = kernel length (length of this code+data)
- +
- + .global __stext
- +__stext:
- + b start
- +LC0:
- + .word _input_data
- + .word _input_end
- + .word RELADDR
- +start:
- + teq r0, #0
- + beq newparams
- + mov r4, #0x02000000
- + add r4, r4, #0x0007C000 @ = 0x0207C000 [new param location]
- + mov r3, #0x4000
- + sub r3, r3, #4
- +lp2: ldmia r0!, {r5, r6, r7, r8, r9, r10, r11, r12}
- + stmia r4!, {r5, r6, r7, r8, r9, r10, r11, r12}
- + subs r3, r3, #32
- + bpl lp2
- +newparams: adr r3, LC0
- + ldmia r3, {r2, r3, r4}
- + sub r3, r3, r2
- + mov r1, r4
- +lp: ldmia r2!, {r5, r6, r7, r8, r9, r10, r11, r12}
- + stmia r4!, {r5, r6, r7, r8, r9, r10, r11, r12}
- + subs r3, r3, #32
- + bpl lp
- + ldr r1, [r1, #0x14]
- + mov pc, r1
- +
- diff -urNwbB linux/arch/arm/boot/compressed/inflate.c linux.arm/arch/arm/boot/compressed/inflate.c
- --- linux/arch/arm/boot/compressed/inflate.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/compressed/inflate.c Sat Feb 24 09:36:43 1996
- @@ -0,0 +1,810 @@
- +#define DEBG(x)
- +#define DEBG1(x)
- +/* inflate.c -- Not copyrighted 1992 by Mark Adler
- + version c10p1, 10 January 1993 */
- +
- +/*
- + * Adapted for booting Linux by Hannu Savolainen 1993
- + * based on gzip-1.0.3
- + */
- +
- +#ifndef lint
- +static char rcsid[] = "$Id: inflate.c,v 0.10 1993/02/04 13:21:06 jloup Exp $";
- +#endif
- +
- +#include "gzip.h"
- +#define slide window
- +
- +#if defined(STDC_HEADERS) || defined(HAVE_STDLIB_H)
- +# include <sys/types.h>
- +# include <stdlib.h>
- +#endif
- +
- +struct huft {
- + uch e; /* number of extra bits or operation */
- + uch b; /* number of bits in this code or subcode */
- + union {
- + ush n; /* literal, length base, or distance base */
- + struct huft *t; /* pointer to next level of table */
- + } v;
- +};
- +
- +
- +/* Function prototypes */
- +int huft_build OF((unsigned *, unsigned, unsigned, ush *, ush *,
- + struct huft **, int *));
- +int huft_free OF((struct huft *));
- +int inflate_codes OF((struct huft *, struct huft *, int, int));
- +int inflate_stored OF((void));
- +int inflate_fixed OF((void));
- +int inflate_dynamic OF((void));
- +int inflate_block OF((int *));
- +int inflate OF((void));
- +
- +
- +#define wp outcnt
- +#define flush_output(w) (wp=(w),flush_window())
- +
- +/* Tables for deflate from PKZIP's appnote.txt. */
- +static unsigned border[] = { /* Order of the bit length code lengths */
- + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
- +static ush cplens[] = { /* Copy lengths for literal codes 257..285 */
- + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
- + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
- + /* note: see note #13 above about the 258 in this list. */
- +static ush cplext[] = { /* Extra bits for literal codes 257..285 */
- + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
- + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */
- +static ush cpdist[] = { /* Copy offsets for distance codes 0..29 */
- + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
- + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
- + 8193, 12289, 16385, 24577};
- +static ush cpdext[] = { /* Extra bits for distance codes */
- + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
- + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
- + 12, 12, 13, 13};
- +
- +
- +ulg bb; /* bit buffer */
- +unsigned bk; /* bits in bit buffer */
- +
- +ush mask_bits[] = {
- + 0x0000,
- + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
- + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
- +};
- +
- +#ifdef CRYPT
- + uch cc;
- +# define NEXTBYTE() \
- + (decrypt ? (cc = get_byte(), zdecode(cc), cc) : get_byte())
- +#else
- +# define NEXTBYTE() (uch)get_byte()
- +#endif
- +#define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE())<<k;k+=8;}}
- +#define DUMPBITS(n) {b>>=(n);k-=(n);}
- +
- +int lbits = 9; /* bits in base literal/length lookup table */
- +int dbits = 6; /* bits in base distance lookup table */
- +
- +
- +/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
- +#define BMAX 16 /* maximum bit length of any code (16 for explode) */
- +#define N_MAX 288 /* maximum number of codes in any set */
- +
- +
- +unsigned hufts; /* track memory usage */
- +
- +
- +int huft_build(b, n, s, d, e, t, m)
- +unsigned *b; /* code lengths in bits (all assumed <= BMAX) */
- +unsigned n; /* number of codes (assumed <= N_MAX) */
- +unsigned s; /* number of simple-valued codes (0..s-1) */
- +ush *d; /* list of base values for non-simple codes */
- +ush *e; /* list of extra bits for non-simple codes */
- +struct huft **t; /* result: starting table */
- +int *m; /* maximum lookup bits, returns actual */
- +/* Given a list of code lengths and a maximum table size, make a set of
- + tables to decode that set of codes. Return zero on success, one if
- + the given code set is incomplete (the tables are still built in this
- + case), two if the input is invalid (all zero length codes or an
- + oversubscribed set of lengths), and three if not enough memory. */
- +{
- + unsigned a; /* counter for codes of length k */
- + unsigned c[BMAX+1]; /* bit length count table */
- + unsigned f; /* i repeats in table every f entries */
- + int g; /* maximum code length */
- + int h; /* table level */
- + register unsigned i; /* counter, current code */
- + register unsigned j; /* counter */
- + register int k; /* number of bits in current code */
- + int l; /* bits per table (returned in m) */
- + register unsigned *p; /* pointer into c[], b[], or v[] */
- + register struct huft *q; /* points to current table */
- + struct huft r; /* table entry for structure assignment */
- + struct huft *u[BMAX]; /* table stack */
- + unsigned v[N_MAX]; /* values in order of bit length */
- + register int w; /* bits before this table == (l * h) */
- + unsigned x[BMAX+1]; /* bit offsets, then code stack */
- + unsigned *xp; /* pointer into x */
- + int y; /* number of dummy codes added */
- + unsigned z; /* number of entries in current table */
- +
- +DEBG("huft1 ");
- +
- + /* Generate counts for each bit length */
- + memzero(c, sizeof(c));
- + p = b; i = n;
- + do {
- + c[*p++]++; /* assume all entries <= BMAX */
- + } while (--i);
- + if (c[0] == n) /* null input--all zero length codes */
- + {
- + *t = (struct huft *)NULL;
- + *m = 0;
- + return 0;
- + }
- +
- +DEBG("huft2 ");
- +
- + /* Find minimum and maximum length, bound *m by those */
- + l = *m;
- + for (j = 1; j <= BMAX; j++)
- + if (c[j])
- + break;
- + k = j; /* minimum code length */
- + if ((unsigned)l < j)
- + l = j;
- + for (i = BMAX; i; i--)
- + if (c[i])
- + break;
- + g = i; /* maximum code length */
- + if ((unsigned)l > i)
- + l = i;
- + *m = l;
- +
- +DEBG("huft3 ");
- +
- + /* Adjust last length count to fill out codes, if needed */
- + for (y = 1 << j; j < i; j++, y <<= 1)
- + if ((y -= c[j]) < 0)
- + return 2; /* bad input: more codes than bits */
- + if ((y -= c[i]) < 0)
- + return 2;
- + c[i] += y;
- +
- +DEBG("huft4 ");
- +
- + /* Generate starting offsets into the value table for each length */
- + x[1] = j = 0;
- + p = c + 1; xp = x + 2;
- + while (--i) { /* note that i == g from above */
- + *xp++ = (j += *p++);
- + }
- +
- +DEBG("huft5 ");
- +
- + /* Make a table of values in order of bit lengths */
- + p = b; i = 0;
- + do {
- + if ((j = *p++) != 0)
- + v[x[j]++] = i;
- + } while (++i < n);
- +
- +DEBG("h6 ");
- +
- + /* Generate the Huffman codes and for each, make the table entries */
- + x[0] = i = 0; /* first Huffman code is zero */
- + p = v; /* grab values in bit order */
- + h = -1; /* no tables yet--level -1 */
- + w = -l; /* bits decoded == (l * h) */
- + u[0] = (struct huft *)NULL; /* just to keep compilers happy */
- + q = (struct huft *)NULL; /* ditto */
- + z = 0; /* ditto */
- +DEBG("h6a ");
- +
- + /* go through the bit lengths (k already is bits in shortest code) */
- + for (; k <= g; k++)
- + {
- +DEBG("h6b ");
- + a = c[k];
- + while (a--)
- + {
- +DEBG("h6b1 ");
- + /* here i is the Huffman code of length k bits for value *p */
- + /* make tables up to required level */
- + while (k > w + l)
- + {
- +DEBG1("1 ");
- + h++;
- + w += l; /* previous table always l bits */
- +
- + /* compute minimum size table less than or equal to l bits */
- + z = (z = g - w) > (unsigned)l ? l : z; /* upper limit on table size */
- + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
- + { /* too few codes for k-w bit table */
- +DEBG1("2 ");
- + f -= a + 1; /* deduct codes from patterns left */
- + xp = c + k;
- + while (++j < z) /* try smaller tables up to z bits */
- + {
- + if ((f <<= 1) <= *++xp)
- + break; /* enough codes to use up j bits */
- + f -= *xp; /* else deduct codes from patterns */
- + }
- + }
- +DEBG1("3 ");
- + z = 1 << j; /* table entries for j-bit table */
- +
- + /* allocate and link in new table */
- + q = (struct huft *)malloc((z + 1)*sizeof(struct huft));
- +DEBG1("4 ");
- + hufts += z + 1; /* track memory usage */
- + *t = q + 1; /* link to list for huft_free() */
- + *(t = &(q->v.t)) = (struct huft *)NULL;
- + u[h] = ++q; /* table starts after link */
- +
- +DEBG1("5 ");
- + /* connect to last table, if there is one */
- + if (h)
- + {
- + x[h] = i; /* save pattern for backing up */
- + r.b = (uch)l; /* bits to dump before this table */
- + r.e = (uch)(16 + j); /* bits in this table */
- + r.v.t = q; /* pointer to this table */
- + j = i >> (w - l); /* (get around Turbo C bug) */
- + u[h-1][j] = r; /* connect to last table */
- + }
- +DEBG1("6 ");
- + }
- +DEBG("h6c ");
- +
- + /* set up table entry in r */
- + r.b = (uch)(k - w);
- + if (p >= v + n)
- + r.e = 99; /* out of values--invalid code */
- + else if (*p < s)
- + {
- + r.e = (uch)(*p < 256 ? 16 : 15); /* 256 is end-of-block code */
- + r.v.n = *p++; /* simple code is just the value */
- + }
- + else
- + {
- + r.e = (uch)e[*p - s]; /* non-simple--look up in lists */
- + r.v.n = d[*p++ - s];
- + }
- +DEBG("h6d ");
- +
- + /* fill code-like entries with r */
- + f = 1 << (k - w);
- + for (j = i >> w; j < z; j += f)
- + q[j] = r;
- +
- + /* backwards increment the k-bit code i */
- + for (j = 1 << (k - 1); i & j; j >>= 1)
- + i ^= j;
- + i ^= j;
- +
- + /* backup over finished tables */
- + while ((i & ((1 << w) - 1)) != x[h])
- + {
- + h--; /* don't need to update q */
- + w -= l;
- + }
- +DEBG("h6e ");
- + }
- +DEBG("h6f ");
- + }
- +
- +DEBG("huft7 ");
- +
- + /* Return true (1) if we were given an incomplete table */
- + return y != 0 && g != 1;
- +}
- +
- +
- +
- +int huft_free(t)
- +struct huft *t; /* table to free */
- +/* Free the malloc'ed tables built by huft_build(), which makes a linked
- + list of the tables it made, with the links in a dummy first entry of
- + each table. */
- +{
- + register struct huft *p, *q;
- +
- +
- + /* Go through linked list, freeing from the malloced (t[-1]) address. */
- + p = t;
- + while (p != (struct huft *)NULL)
- + {
- + q = (--p)->v.t;
- + free(p);
- + p = q;
- + }
- + return 0;
- +}
- +
- +
- +int inflate_codes(tl, td, bl, bd)
- +struct huft *tl, *td; /* literal/length and distance decoder tables */
- +int bl, bd; /* number of bits decoded by tl[] and td[] */
- +/* inflate (decompress) the codes in a deflated (compressed) block.
- + Return an error code or zero if it all goes ok. */
- +{
- + register unsigned e; /* table entry flag/number of extra bits */
- + unsigned n, d; /* length and index for copy */
- + unsigned w; /* current window position */
- + struct huft *t; /* pointer to table entry */
- + unsigned ml, md; /* masks for bl and bd bits */
- + register ulg b; /* bit buffer */
- + register unsigned k; /* number of bits in bit buffer */
- +
- +
- + /* make local copies of globals */
- + b = bb; /* initialize bit buffer */
- + k = bk;
- + w = wp; /* initialize window position */
- +
- + /* inflate the coded data */
- + ml = mask_bits[bl]; /* precompute masks for speed */
- + md = mask_bits[bd];
- + for (;;) /* do until end of block */
- + {
- + NEEDBITS((unsigned)bl)
- + if ((e = (t = tl + ((unsigned)b & ml))->e) > 16)
- + do {
- + if (e == 99)
- + return 1;
- + DUMPBITS(t->b)
- + e -= 16;
- + NEEDBITS(e)
- + } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16);
- + DUMPBITS(t->b)
- + if (e == 16) /* then it's a literal */
- + {
- + slide[w++] = (uch)t->v.n;
- + if (w == WSIZE)
- + {
- + flush_output(w);
- + w = 0;
- + }
- + }
- + else /* it's an EOB or a length */
- + {
- + /* exit if end of block */
- + if (e == 15)
- + break;
- +
- + /* get length of block to copy */
- + NEEDBITS(e)
- + n = t->v.n + ((unsigned)b & mask_bits[e]);
- + DUMPBITS(e);
- +
- + /* decode distance of block to copy */
- + NEEDBITS((unsigned)bd)
- + if ((e = (t = td + ((unsigned)b & md))->e) > 16)
- + do {
- + if (e == 99)
- + return 1;
- + DUMPBITS(t->b)
- + e -= 16;
- + NEEDBITS(e)
- + } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16);
- + DUMPBITS(t->b)
- + NEEDBITS(e)
- + d = w - t->v.n - ((unsigned)b & mask_bits[e]);
- + DUMPBITS(e)
- +
- + /* do the copy */
- + do {
- + n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e);
- +#if !defined(NOMEMCPY) && !defined(DEBUG)
- + if (w - d >= e) /* (this test assumes unsigned comparison) */
- + {
- + memcpy(slide + w, slide + d, e);
- + w += e;
- + d += e;
- + }
- + else /* do it slow to avoid memcpy() overlap */
- +#endif /* !NOMEMCPY */
- + do {
- + slide[w++] = slide[d++];
- + } while (--e);
- + if (w == WSIZE)
- + {
- + flush_output(w);
- + w = 0;
- + }
- + } while (n);
- + }
- + }
- +
- +
- + /* restore the globals from the locals */
- + wp = w; /* restore global window pointer */
- + bb = b; /* restore global bit buffer */
- + bk = k;
- +
- + /* done */
- + return 0;
- +}
- +
- +
- +
- +int inflate_stored()
- +/* "decompress" an inflated type 0 (stored) block. */
- +{
- + unsigned n; /* number of bytes in block */
- + unsigned w; /* current window position */
- + register ulg b; /* bit buffer */
- + register unsigned k; /* number of bits in bit buffer */
- +
- +DEBG("<stor");
- +
- + /* make local copies of globals */
- + b = bb; /* initialize bit buffer */
- + k = bk;
- + w = wp; /* initialize window position */
- +
- +
- + /* go to byte boundary */
- + n = k & 7;
- + DUMPBITS(n);
- +
- +
- + /* get the length and its complement */
- + NEEDBITS(16)
- + n = ((unsigned)b & 0xffff);
- + DUMPBITS(16)
- + NEEDBITS(16)
- + if (n != (unsigned)((~b) & 0xffff))
- + return 1; /* error in compressed data */
- + DUMPBITS(16)
- +
- +
- + /* read and output the compressed data */
- + while (n--)
- + {
- + NEEDBITS(8)
- + slide[w++] = (uch)b;
- + if (w == WSIZE)
- + {
- + flush_output(w);
- + w = 0;
- + }
- + DUMPBITS(8)
- + }
- +
- +
- + /* restore the globals from the locals */
- + wp = w; /* restore global window pointer */
- + bb = b; /* restore global bit buffer */
- + bk = k;
- +
- + DEBG(">");
- + return 0;
- +}
- +
- +
- +
- +int inflate_fixed()
- +/* decompress an inflated type 1 (fixed Huffman codes) block. We should
- + either replace this with a custom decoder, or at least precompute the
- + Huffman tables. */
- +{
- + int i; /* temporary variable */
- + struct huft *tl; /* literal/length code table */
- + struct huft *td; /* distance code table */
- + int bl; /* lookup bits for tl */
- + int bd; /* lookup bits for td */
- + unsigned l[288]; /* length list for huft_build */
- +
- +DEBG("<fix");
- +
- + /* set up literal table */
- + for (i = 0; i < 144; i++)
- + l[i] = 8;
- + for (; i < 256; i++)
- + l[i] = 9;
- + for (; i < 280; i++)
- + l[i] = 7;
- + for (; i < 288; i++) /* make a complete, but wrong code set */
- + l[i] = 8;
- + bl = 7;
- + if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0)
- + return i;
- +
- +
- + /* set up distance table */
- + for (i = 0; i < 30; i++) /* make an incomplete code set */
- + l[i] = 5;
- + bd = 5;
- + if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1)
- + {
- + huft_free(tl);
- +
- + DEBG(">");
- + return i;
- + }
- +
- +
- + /* decompress until an end-of-block code */
- + if (inflate_codes(tl, td, bl, bd))
- + return 1;
- +
- +
- + /* free the decoding tables, return */
- + huft_free(tl);
- + huft_free(td);
- + return 0;
- +}
- +
- +
- +
- +int inflate_dynamic()
- +/* decompress an inflated type 2 (dynamic Huffman codes) block. */
- +{
- + int i; /* temporary variables */
- + unsigned j;
- + unsigned l; /* last length */
- + unsigned m; /* mask for bit lengths table */
- + unsigned n; /* number of lengths to get */
- + struct huft *tl; /* literal/length code table */
- + struct huft *td; /* distance code table */
- + int bl; /* lookup bits for tl */
- + int bd; /* lookup bits for td */
- + unsigned nb; /* number of bit length codes */
- + unsigned nl; /* number of literal/length codes */
- + unsigned nd; /* number of distance codes */
- +#ifdef PKZIP_BUG_WORKAROUND
- + unsigned ll[288+32]; /* literal/length and distance code lengths */
- +#else
- + unsigned ll[286+30]; /* literal/length and distance code lengths */
- +#endif
- + register ulg b; /* bit buffer */
- + register unsigned k; /* number of bits in bit buffer */
- +
- +DEBG("<dyn");
- +
- + /* make local bit buffer */
- + b = bb;
- + k = bk;
- +
- +
- + /* read in table lengths */
- + NEEDBITS(5)
- + nl = 257 + ((unsigned)b & 0x1f); /* number of literal/length codes */
- + DUMPBITS(5)
- + NEEDBITS(5)
- + nd = 1 + ((unsigned)b & 0x1f); /* number of distance codes */
- + DUMPBITS(5)
- + NEEDBITS(4)
- + nb = 4 + ((unsigned)b & 0xf); /* number of bit length codes */
- + DUMPBITS(4)
- +#ifdef PKZIP_BUG_WORKAROUND
- + if (nl > 288 || nd > 32)
- +#else
- + if (nl > 286 || nd > 30)
- +#endif
- + return 1; /* bad lengths */
- +
- +DEBG("dyn1 ");
- +
- + /* read in bit-length-code lengths */
- + for (j = 0; j < nb; j++)
- + {
- + NEEDBITS(3)
- + ll[border[j]] = (unsigned)b & 7;
- + DUMPBITS(3)
- + }
- + for (; j < 19; j++)
- + ll[border[j]] = 0;
- +
- +DEBG("dyn2 ");
- +
- + /* build decoding table for trees--single level, 7 bit lookup */
- + bl = 7;
- + if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0)
- + {
- + if (i == 1)
- + huft_free(tl);
- + return i; /* incomplete code set */
- + }
- +
- +DEBG("dyn3 ");
- +
- + /* read in literal and distance code lengths */
- + n = nl + nd;
- + m = mask_bits[bl];
- + i = l = 0;
- + while ((unsigned)i < n)
- + {
- + NEEDBITS((unsigned)bl)
- + j = (td = tl + ((unsigned)b & m))->b;
- + DUMPBITS(j)
- + j = td->v.n;
- + if (j < 16) /* length of code in bits (0..15) */
- + ll[i++] = l = j; /* save last length in l */
- + else if (j == 16) /* repeat last length 3 to 6 times */
- + {
- + NEEDBITS(2)
- + j = 3 + ((unsigned)b & 3);
- + DUMPBITS(2)
- + if ((unsigned)i + j > n)
- + return 1;
- + while (j--)
- + ll[i++] = l;
- + }
- + else if (j == 17) /* 3 to 10 zero length codes */
- + {
- + NEEDBITS(3)
- + j = 3 + ((unsigned)b & 7);
- + DUMPBITS(3)
- + if ((unsigned)i + j > n)
- + return 1;
- + while (j--)
- + ll[i++] = 0;
- + l = 0;
- + }
- + else /* j == 18: 11 to 138 zero length codes */
- + {
- + NEEDBITS(7)
- + j = 11 + ((unsigned)b & 0x7f);
- + DUMPBITS(7)
- + if ((unsigned)i + j > n)
- + return 1;
- + while (j--)
- + ll[i++] = 0;
- + l = 0;
- + }
- + }
- +
- +DEBG("dyn4 ");
- +
- + /* free decoding table for trees */
- + huft_free(tl);
- +
- +DEBG("dyn5 ");
- +
- + /* restore the global bit buffer */
- + bb = b;
- + bk = k;
- +
- +DEBG("dyn5a ");
- +
- + /* build the decoding tables for literal/length and distance codes */
- + bl = lbits;
- + if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0)
- + {
- +DEBG("dyn5b ");
- + if (i == 1) {
- + error(" incomplete literal tree\n");
- + huft_free(tl);
- + }
- + return i; /* incomplete code set */
- + }
- +DEBG("dyn5c ");
- + bd = dbits;
- + if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0)
- + {
- +DEBG("dyn5d ");
- + if (i == 1) {
- + error(" incomplete distance tree\n");
- +#ifdef PKZIP_BUG_WORKAROUND
- + i = 0;
- + }
- +#else
- + huft_free(td);
- + }
- + huft_free(tl);
- + return i; /* incomplete code set */
- +#endif
- + }
- +
- +DEBG("dyn6 ");
- +
- + /* decompress until an end-of-block code */
- + if (inflate_codes(tl, td, bl, bd))
- + return 1;
- +
- +DEBG("dyn7 ");
- +
- + /* free the decoding tables, return */
- + huft_free(tl);
- + huft_free(td);
- +
- + DEBG(">");
- + return 0;
- +}
- +
- +
- +
- +int inflate_block(e)
- +int *e; /* last block flag */
- +/* decompress an inflated block */
- +{
- + unsigned t; /* block type */
- + register ulg b; /* bit buffer */
- + register unsigned k; /* number of bits in bit buffer */
- +
- + DEBG("<blk");
- +
- + /* make local bit buffer */
- + b = bb;
- + k = bk;
- +
- +
- + /* read in last block bit */
- + NEEDBITS(1)
- + *e = (int)b & 1;
- + DUMPBITS(1)
- +
- +
- + /* read in block type */
- + NEEDBITS(2)
- + t = (unsigned)b & 3;
- + DUMPBITS(2)
- +
- +
- + /* restore the global bit buffer */
- + bb = b;
- + bk = k;
- +
- + /* inflate that block type */
- + if (t == 2)
- + return inflate_dynamic();
- + if (t == 0)
- + return inflate_stored();
- + if (t == 1)
- + return inflate_fixed();
- +
- + DEBG(">");
- +
- + /* bad block type */
- + return 2;
- +}
- +
- +
- +
- +int inflate()
- +/* decompress an inflated entry */
- +{
- + int e; /* last block flag */
- + int r; /* result code */
- + unsigned h; /* maximum struct huft's malloc'ed */
- +
- +
- + /* initialize window, bit buffer */
- + wp = 0;
- + bk = 0;
- + bb = 0;
- +
- +
- + /* decompress until the last block */
- + h = 0;
- + do {
- + hufts = 0;
- + if ((r = inflate_block(&e)) != 0)
- + return r;
- + if (hufts > h)
- + h = hufts;
- + } while (!e);
- +
- + /* Undo too much lookahead. The next read will be byte aligned so we
- + * can discard unused bits in the last meaningful byte.
- + */
- + while (bk >= 8) {
- + bk -= 8;
- + inptr--;
- + }
- +
- + /* flush out slide */
- + flush_output(wp);
- +
- +
- + /* return success */
- +#ifdef DEBUG
- + fprintf(stderr, "<%u> ", h);
- +#endif /* DEBUG */
- + return 0;
- +}
- diff -urNwbB linux/arch/arm/boot/compressed/lzw.h linux.arm/arch/arm/boot/compressed/lzw.h
- --- linux/arch/arm/boot/compressed/lzw.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/compressed/lzw.h Sun Feb 11 09:32:11 1996
- @@ -0,0 +1,42 @@
- +/* lzw.h -- define the lzw functions.
- + * Copyright (C) 1992-1993 Jean-loup Gailly.
- + * This is free software; you can redistribute it and/or modify it under the
- + * terms of the GNU General Public License, see the file COPYING.
- + */
- +
- +#if !defined(OF) && defined(lint)
- +# include "gzip.h"
- +#endif
- +
- +#ifndef BITS
- +# define BITS 16
- +#endif
- +#define INIT_BITS 9 /* Initial number of bits per code */
- +
- +#define LZW_MAGIC "\037\235" /* Magic header for lzw files, 1F 9D */
- +
- +#define BIT_MASK 0x1f /* Mask for 'number of compression bits' */
- +/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free.
- + * It's a pity that old uncompress does not check bit 0x20. That makes
- + * extension of the format actually undesirable because old compress
- + * would just crash on the new format instead of giving a meaningful
- + * error message. It does check the number of bits, but it's more
- + * helpful to say "unsupported format, get a new version" than
- + * "can only handle 16 bits".
- + */
- +
- +#define BLOCK_MODE 0x80
- +/* Block compression: if table is full and compression rate is dropping,
- + * clear the dictionary.
- + */
- +
- +#define LZW_RESERVED 0x60 /* reserved bits */
- +
- +#define CLEAR 256 /* flush the dictionary */
- +#define FIRST (CLEAR+1) /* first free entry */
- +
- +extern int maxbits; /* max bits per code for LZW */
- +extern int block_mode; /* block compress mode -C compatible with 2.0 */
- +
- +extern void lzw OF((int in, int out));
- +extern void unlzw OF((int in, int out));
- diff -urNwbB linux/arch/arm/boot/compressed/misc.c linux.arm/arch/arm/boot/compressed/misc.c
- --- linux/arch/arm/boot/compressed/misc.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/compressed/misc.c Sun Feb 11 09:32:12 1996
- @@ -0,0 +1,534 @@
- +/*
- + * misc.c
- + *
- + * This is a collection of several routines from gzip-1.0.3
- + * adapted for Linux.
- + *
- + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
- + * puts by Nick Holloway 1993
- + */
- +
- +#include "gzip.h"
- +#include "lzw.h"
- +
- +/*#include <asm/segment.h>*/
- +
- +/*
- + * These are set up by the setup-routine at boot-time:
- + */
- +
- +struct param_struct
- +{
- + unsigned long page_size;
- + unsigned long nr_pages;
- + unsigned long ramdisk_size;
- + unsigned long mountrootrdonly;
- + unsigned long rootdev;
- + unsigned long video_num_cols;
- + unsigned long video_num_rows;
- + unsigned long video_x;
- + unsigned long video_y;
- + unsigned long memc_control_reg;
- + unsigned char sounddefault;
- + unsigned char adfsdrives;
- + unsigned char bytes_per_char_h;
- + unsigned char bytes_per_char_v;
- + unsigned long unused[256/4-11];
- + char paths[8][128];
- + char commandline[256];
- +};
- +
- +static struct param_struct *params=(struct param_struct *)0x0207C000;
- +
- +#define EOF -1
- +
- +DECLARE(uch, inbuf, INBUFSIZ);
- +DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
- +DECLARE(uch, window, WSIZE);
- +
- +unsigned outcnt;
- +unsigned insize;
- +unsigned inptr;
- +
- +extern char input_data[], input_end[];
- +int input_len;
- +
- +int input_ptr;
- +
- +int method, exit_code, part_nb, last_member;
- +int test = 0;
- +int force = 0;
- +int verbose = 1;
- +long bytes_in, bytes_out;
- +
- +char *output_data;
- +unsigned long output_ptr;
- +
- +extern int end;
- +long free_mem_ptr = (long)&end;
- +
- +int to_stdout = 0;
- +int hard_math = 0;
- +
- +void (*work)(int inf, int outf);
- +void makecrc(void);
- +
- +local int get_method(int);
- +
- +char *vidmem;
- +int video_num_lines, video_num_columns;
- +int bytes_per_char_h;
- +int white;
- +
- +static void puts(const char *);
- +
- +const char cmap_80[][8]=
- +{
- + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* */
- + {0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x00}, /* ! */
- + {0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00}, /* " */
- + {0x36,0x36,0x7F,0x36,0x7F,0x36,0x36,0x00}, /* # */
- + {0x0C,0x3F,0x68,0x3E,0x0B,0x7E,0x18,0x00}, /* $ */
- + {0x60,0x66,0x0C,0x18,0x30,0x66,0x06,0x00}, /* % */
- + {0x38,0x6C,0x6C,0x38,0x6D,0x66,0x3B,0x00}, /* & */
- + {0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00}, /* ' */
- + {0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00}, /* ( */
- + {0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00}, /* ) */
- + {0x00,0x18,0x7E,0x3C,0x7E,0x18,0x00,0x00}, /* * */
- + {0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00}, /* + */
- + {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30}, /* , */
- + {0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00}, /* - */
- + {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00}, /* . */
- + {0x00,0x06,0x0C,0x18,0x30,0x60,0x00,0x00}, /* / */
- + {0x3C,0x66,0x6E,0x7E,0x76,0x66,0x3C,0x00}, /* 0 */
- + {0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00}, /* 1 */
- + {0x3C,0x66,0x06,0x0C,0x18,0x30,0x7E,0x00}, /* 2 */
- + {0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00}, /* 3 */
- + {0x0C,0x1C,0x3C,0x6C,0x7E,0x0C,0x0C,0x00}, /* 4 */
- + {0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00}, /* 5 */
- + {0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00}, /* 6 */
- + {0x7E,0x06,0x0C,0x18,0x30,0x30,0x30,0x00}, /* 7 */
- + {0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00}, /* 8 */
- + {0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00}, /* 9 */
- + {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x00}, /* : */
- + {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x30}, /* ; */
- + {0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x00}, /* < */
- + {0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00}, /* = */
- + {0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x00}, /* > */
- + {0x3C,0x66,0x0C,0x18,0x18,0x00,0x18,0x00}, /* ? */
- + {0x3C,0x66,0x6E,0x6A,0x6E,0x60,0x3C,0x00}, /* @ */
- + {0x3C,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* A */
- + {0x7C,0x66,0x66,0x7C,0x66,0x66,0x7C,0x00}, /* B */
- + {0x3C,0x66,0x60,0x60,0x60,0x66,0x3C,0x00}, /* C */
- + {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}, /* D */
- + {0x7E,0x60,0x60,0x7C,0x60,0x60,0x7E,0x00}, /* E */
- + {0x7E,0x60,0x60,0x7C,0x60,0x60,0x60,0x00}, /* F */
- + {0x3C,0x66,0x60,0x6E,0x66,0x66,0x3C,0x00}, /* G */
- + {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* H */
- + {0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00}, /* I */
- + {0x3E,0x0C,0x0C,0x0C,0x0C,0x6C,0x38,0x00}, /* J */
- + {0x66,0x6C,0x78,0x70,0x78,0x6C,0x66,0x00}, /* K */
- + {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00}, /* L */
- + {0x63,0x77,0x7F,0x6B,0x6B,0x63,0x63,0x00}, /* M */
- + {0x66,0x66,0x76,0x7E,0x6E,0x66,0x66,0x00}, /* N */
- + {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}, /* O */
- + {0x7C,0x66,0x66,0x7C,0x60,0x60,0x60,0x00}, /* P */
- + {0x3C,0x66,0x66,0x66,0x6A,0x6C,0x36,0x00}, /* Q */
- + {0x7C,0x66,0x66,0x7C,0x6C,0x66,0x66,0x00}, /* R */
- + {0x3C,0x66,0x60,0x3C,0x06,0x66,0x3C,0x00}, /* S */
- + {0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, /* T */
- + {0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}, /* U */
- + {0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00}, /* V */
- + {0x63,0x63,0x6B,0x6B,0x7F,0x77,0x63,0x00}, /* W */
- + {0x66,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00}, /* X */
- + {0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00}, /* Y */
- + {0x7E,0x06,0x0C,0x18,0x30,0x60,0x7E,0x00}, /* Z */
- + {0x7C,0x60,0x60,0x60,0x60,0x60,0x7C,0x00}, /* [ */
- + {0x00,0x60,0x30,0x18,0x0C,0x06,0x00,0x00}, /* \ */
- + {0x3E,0x06,0x06,0x06,0x06,0x06,0x3E,0x00}, /* ] */
- + {0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00}, /* ^ */
- + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF}, /* _ */
- + {0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00}, /* ` */
- + {0x00,0x00,0x3C,0x06,0x3E,0x66,0x3E,0x00}, /* a */
- + {0x60,0x60,0x7C,0x66,0x66,0x66,0x7C,0x00}, /* b */
- + {0x00,0x00,0x3C,0x66,0x60,0x66,0x3C,0x00}, /* c */
- + {0x06,0x06,0x3E,0x66,0x66,0x66,0x3E,0x00}, /* d */
- + {0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00}, /* e */
- + {0x1C,0x30,0x30,0x7C,0x30,0x30,0x30,0x00}, /* f */
- + {0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x3C}, /* g */
- + {0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x00}, /* h */
- + {0x18,0x00,0x38,0x18,0x18,0x18,0x3C,0x00}, /* i */
- + {0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x70}, /* j */
- + {0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00}, /* k */
- + {0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00}, /* l */
- + {0x00,0x00,0x36,0x7F,0x6B,0x6B,0x63,0x00}, /* m */
- + {0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x00}, /* n */
- + {0x00,0x00,0x3C,0x66,0x66,0x66,0x3C,0x00}, /* o */
- + {0x00,0x00,0x7C,0x66,0x66,0x7C,0x60,0x60}, /* p */
- + {0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x07}, /* q */
- + {0x00,0x00,0x6C,0x76,0x60,0x60,0x60,0x00}, /* r */
- + {0x00,0x00,0x3E,0x60,0x3C,0x06,0x7C,0x00}, /* s */
- + {0x30,0x30,0x7C,0x30,0x30,0x30,0x1C,0x00}, /* t */
- + {0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x00}, /* u */
- + {0x00,0x00,0x66,0x66,0x66,0x3C,0x18,0x00}, /* v */
- + {0x00,0x00,0x63,0x6B,0x6B,0x7F,0x36,0x00}, /* w */
- + {0x00,0x00,0x66,0x3C,0x18,0x3C,0x66,0x00}, /* x */
- + {0x00,0x00,0x66,0x66,0x66,0x3E,0x06,0x3C}, /* y */
- + {0x00,0x00,0x7E,0x0C,0x18,0x30,0x7E,0x00}, /* z */
- + {0x0C,0x18,0x18,0x70,0x18,0x18,0x0C,0x00}, /* { */
- + {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, /* | */
- + {0x30,0x18,0x18,0x0E,0x18,0x18,0x30,0x00}, /* } */
- + {0x31,0x6B,0x46,0x00,0x00,0x00,0x00,0x00}, /* ~ */
- + {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF} /* */
- +};
- +
- +void *malloc(int size)
- +{
- + void *p;
- +
- + if (size <0) error("Malloc error\n");
- + if (free_mem_ptr <= 0) error("Memory error\n");
- +
- + while(1) {
- + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */
- +
- + p = (void *)free_mem_ptr;
- + free_mem_ptr += size;
- +
- + /*
- + * The part of the compressed kernel which has already been expanded
- + * is no longer needed. Therefore we can reuse it for malloc.
- + * With bigger kernels, this is necessary.
- + */
- +
- + if (free_mem_ptr < (long)&end) {
- + if (free_mem_ptr > (long)&input_data[input_ptr])
- + error("\nOut of memory\n");
- +
- + return p;
- + }
- + if (free_mem_ptr < 0x01a00000)
- + return p;
- + puts("large kernel, memory tight...");
- + free_mem_ptr = (long)input_data;
- + }
- +}
- +
- +void free(void *where)
- +{ /* Don't care */
- +}
- +
- +static void scroll()
- +{
- +#if 0
- + 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] = ' ';
- +#endif
- +}
- +
- +static void puts(const char *s)
- +{
- + extern void ll_char_write(char *, char, char, char, char);
- + int x,y;
- + char c;
- + char *ptr;
- +
- + x = params->video_x;
- + y = params->video_y;
- +
- + while ( ( c = *s++ ) != '\0' ) {
- + if ( c == '\n' ) {
- + x = 0;
- + if ( ++y >= video_num_lines ) {
- + scroll();
- + y--;
- + }
- + } else {
- + ptr = vidmem + (y*video_num_columns*params->bytes_per_char_v+x)*bytes_per_char_h;
- + ll_char_write(ptr, c, white, 0, 0);
- + if ( ++x >= video_num_columns ) {
- + x = 0;
- + if ( ++y >= video_num_lines ) {
- + scroll();
- + y--;
- + }
- + }
- + }
- + }
- +
- + params->video_x = x;
- + params->video_y = y;
- +}
- +
- +__ptr_t memset(__ptr_t s, int c, size_t n)
- +{
- + int i;
- + char *ss = (char*)s;
- +
- + for (i=0;i<n;i++) ss[i] = c;
- +}
- +
- +__ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src,
- + size_t __n)
- +{
- + int i;
- + char *d = (char *)__dest, *s = (char *)__src;
- +
- + for (i=0;i<__n;i++) d[i] = s[i];
- +}
- +
- +extern ulg crc_32_tab[]; /* crc table, defined below */
- +
- +/* ===========================================================================
- + * Run a set of bytes through the crc shift register. If s is a NULL
- + * pointer, then initialize the crc shift register contents instead.
- + * Return the current crc in either case.
- + */
- +ulg updcrc(s, n)
- + uch *s; /* pointer to bytes to pump through */
- + unsigned n; /* number of bytes in s[] */
- +{
- + register ulg c; /* temporary variable */
- +
- + static ulg crc = (ulg)0xffffffffL; /* shift register contents */
- +
- + if (s == NULL) {
- + c = 0xffffffffL;
- + } else {
- + c = crc;
- + while (n--) {
- + c = crc_32_tab[((int)c ^ (*s++)) & 0xff] ^ (c >> 8);
- + }
- + }
- + crc = c;
- + return c ^ 0xffffffffL; /* (instead of ~c for 64-bit machines) */
- +}
- +
- +/* ===========================================================================
- + * Clear input and output buffers
- + */
- +void clear_bufs()
- +{
- + outcnt = 0;
- + insize = inptr = 0;
- + bytes_in = bytes_out = 0L;
- +}
- +
- +/* ===========================================================================
- + * Fill the input buffer. This is called only when the buffer is empty
- + * and at least one byte is really needed.
- + */
- +int fill_inbuf()
- +{
- + int len, i;
- +
- + /* Read as much as possible */
- + insize = 0;
- + do {
- + len = INBUFSIZ-insize;
- + if (len > (input_len-input_ptr+1)) len=input_len-input_ptr+1;
- + if (len == 0 || len == EOF) break;
- +
- + for (i=0;i<len;i++) inbuf[insize+i] = input_data[input_ptr+i];
- + insize += len;
- + input_ptr += len;
- + } while (insize < INBUFSIZ);
- +
- + if (insize == 0) {
- + error("unable to fill buffer\n");
- + }
- + bytes_in += (ulg)insize;
- + inptr = 1;
- + return inbuf[0];
- +}
- +
- +/* ===========================================================================
- + * Write the output window window[0..outcnt-1] and update crc and bytes_out.
- + * (Used for the decompressed data only.)
- + */
- +void flush_window()
- +{
- + if (outcnt == 0) return;
- + updcrc(window, outcnt);
- +
- + memcpy(&output_data[output_ptr], (char *)window, outcnt);
- +
- + bytes_out += (ulg)outcnt;
- + output_ptr += (ulg)outcnt;
- + outcnt = 0;
- +}
- +
- +/*
- + * Code to compute the CRC-32 table. Borrowed from
- + * gzip-1.0.3/makecrc.c.
- + */
- +
- +ulg crc_32_tab[256];
- +
- +void
- +makecrc(void)
- +{
- +/* Not copyrighted 1990 Mark Adler */
- +
- + unsigned long c; /* crc shift register */
- + unsigned long e; /* polynomial exclusive-or pattern */
- + int i; /* counter for all possible eight bit values */
- + int k; /* byte being shifted into crc apparatus */
- +
- + /* terms of polynomial defining this crc (except x^32): */
- + static int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
- +
- + /* Make exclusive-or pattern from polynomial */
- + e = 0;
- + for (i = 0; i < sizeof(p)/sizeof(int); i++)
- + e |= 1L << (31 - p[i]);
- +
- + crc_32_tab[0] = 0;
- +
- + for (i = 1; i < 256; i++)
- + {
- + c = 0;
- + for (k = i | 256; k != 1; k >>= 1)
- + {
- + c = c & 1 ? (c >> 1) ^ e : c >> 1;
- + if (k & 1)
- + c ^= e;
- + }
- + crc_32_tab[i] = c;
- + }
- +}
- +
- +void error(char *x)
- +{
- + puts("\n\n");
- + puts(x);
- + puts("\n\n -- System halted");
- +
- + while(1); /* Halt */
- +}
- +
- +#define STACK_SIZE (4096)
- +
- +long _stack_start [STACK_SIZE];
- +
- +void decompress_kernel(int output_start)
- +{
- + input_len = input_end - input_data;
- + vidmem = (char *)0x02000000;
- +
- + video_num_lines = params->video_num_rows;
- + video_num_columns = params->video_num_cols;
- + bytes_per_char_h = params->bytes_per_char_h;
- + white = bytes_per_char_h == 8 ? 0xfc : 7;
- +
- + if (params->nr_pages * params->page_size < 2048*1024) error("<2M of mem\n");
- +
- + output_data = (char *)output_start; /* Points to kernel start */
- + output_ptr = 0;
- +
- + exit_code = 0;
- + test = 0;
- + input_ptr = 0;
- + part_nb = 0;
- +
- + clear_bufs();
- + makecrc();
- +
- + puts("Uncompressing Linux...");
- +
- + method = get_method(0);
- +
- + work(0, 0);
- +
- + puts("done.\n");
- +
- + puts("Now booting the kernel\n");
- +}
- +
- +/* ========================================================================
- + * Check the magic number of the input file and update ofname if an
- + * original name was given and to_stdout is not set.
- + * Return the compression method, -1 for error, -2 for warning.
- + * Set inptr to the offset of the next byte to be processed.
- + * This function may be called repeatedly for an input file consisting
- + * of several contiguous gzip'ed members.
- + * IN assertions: there is at least one remaining compressed member.
- + * If the member is a zip file, it must be the only one.
- + */
- +
- +union magic
- +{
- + char magic[2];
- + short s_magic;
- +};
- +
- +local int get_method(in)
- + int in; /* input file descriptor */
- +{
- + uch flags;
- + union magic magic; /* magic header */
- + union magic gzip_magic = { GZIP_MAGIC };
- + union magic old_gzip_magic = { OLD_GZIP_MAGIC };
- +
- + magic.magic[0] = (char)get_byte();
- + magic.magic[1] = (char)get_byte();
- +
- + method = -1; /* unknown yet */
- + part_nb++; /* number of parts in gzip file */
- + last_member = 0;
- + /* assume multiple members in gzip file except for record oriented I/O */
- +
- + if (magic.s_magic == gzip_magic.s_magic
- + || magic.s_magic == old_gzip_magic.s_magic) {
- +
- + work = unzip;
- + method = (int)get_byte();
- + flags = (uch)get_byte();
- +
- + if ((flags & ENCRYPTED) != 0)
- + error("Input is encrypted\n");
- +
- + if ((flags & CONTINUATION) != 0)
- + error("Multi part input\n");
- +
- + if ((flags & RESERVED) != 0) {
- + error("Input has invalid flags\n");
- + exit_code = ERROR;
- + if (force <= 1)
- + return -1;
- + }
- +
- + (ulg)get_byte(); /* Get timestamp */
- + ((ulg)get_byte()) << 8;
- + ((ulg)get_byte()) << 16;
- + ((ulg)get_byte()) << 24;
- +
- + (void)get_byte(); /* Ignore extra flags for the moment */
- + (void)get_byte(); /* Ignore OS type for the moment */
- +
- + if ((flags & EXTRA_FIELD) != 0) {
- + unsigned len = (unsigned)get_byte();
- + len |= ((unsigned)get_byte())<<8;
- + while (len--) (void)get_byte();
- + }
- +
- + /* Get original file name if it was truncated */
- + if ((flags & ORIG_NAME) != 0) {
- + if (to_stdout || part_nb > 1) {
- + /* Discard the old name */
- + while (get_byte() != 0) /* null */ ;
- + } else {
- + } /* to_stdout */
- + } /* orig_name */
- +
- + /* Discard file comment if any */
- + if ((flags & COMMENT) != 0) {
- + while (get_byte() != 0) /* null */ ;
- + }
- + } else
- + error("unknown compression method");
- + return method;
- +}
- diff -urNwbB linux/arch/arm/boot/compressed/piggyback.c linux.arm/arch/arm/boot/compressed/piggyback.c
- --- linux/arch/arm/boot/compressed/piggyback.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/compressed/piggyback.c Sun Feb 11 09:32:12 1996
- @@ -0,0 +1,83 @@
- +/*
- + * linux/zBoot/piggyback.c
- + *
- + * (C) 1993 Hannu Savolainen
- + */
- +
- +/*
- + * This program reads the compressed system image from stdin and
- + * encapsulates it into an object file written to the stdout.
- + */
- +
- +#include <stdio.h>
- +#include <unistd.h>
- +#include <a.out.h>
- +
- +int main(int argc, char *argv[])
- +{
- + int c, n=0, len=0;
- + char tmp_buf[512*1024];
- +
- + struct exec obj = {0x00670107}; /* object header */
- + char string_names[] = {"_input_data\0_input_end\0"};
- +
- + struct nlist var_names[2] = /* Symbol table */
- + {
- + { /* _input_data */
- + (char *)4, 7, 0, 0, 0
- + },
- + { /* _input_len */
- + (char *)16, 7, 0, 0, 0
- + }
- + };
- +
- +
- + len = 0;
- + while ((n = read(0, &tmp_buf[len], sizeof(tmp_buf)-len+1)) > 0)
- + len += n;
- +
- + len = (len + 3) & ~3;
- +
- + if (n==-1)
- + {
- + perror("stdin");
- + exit(-1);
- + }
- +
- + if (len >= sizeof(tmp_buf))
- + {
- + fprintf(stderr, "%s: Input too large\n", argv[0]);
- + exit(-1);
- + }
- +
- + fprintf(stderr, "Compressed size %d.\n", len);
- +
- +/*
- + * Output object header
- + */
- + obj.a_data = len /* + sizeof(long) */;
- + obj.a_syms = sizeof(var_names);
- + write(1, (char *)&obj, sizeof(obj));
- +
- +/*
- + * Output data segment (compressed system & len)
- + */
- + write(1, tmp_buf, len);
- +/* write(1, (char *)&len, sizeof(len));*/
- +
- +/*
- + * Output symbol table
- + */
- + var_names[1].n_value = len;
- + write(1, (char *)&var_names, sizeof(var_names));
- +
- +/*
- + * Output string table
- + */
- + len = sizeof(string_names) + sizeof(len);
- + write(1, (char *)&len, sizeof(len));
- + write(1, string_names, sizeof(string_names));
- +
- + exit(0);
- +
- +}
- diff -urNwbB linux/arch/arm/boot/compressed/unzip.c linux.arm/arch/arm/boot/compressed/unzip.c
- --- linux/arch/arm/boot/compressed/unzip.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/compressed/unzip.c Sat Feb 24 09:36:43 1996
- @@ -0,0 +1,180 @@
- +/* unzip.c -- decompress files in gzip or pkzip format.
- + * Copyright (C) 1992-1993 Jean-loup Gailly
- + *
- + * Adapted for Linux booting by Hannu Savolainen 1993
- + *
- + * This is free software; you can redistribute it and/or modify it under the
- + * terms of the GNU General Public License, see the file COPYING.
- + *
- + * The code in this file is derived from the file funzip.c written
- + * and put in the public domain by Mark Adler.
- + */
- +
- +/*
- + This version can extract files in gzip or pkzip format.
- + For the latter, only the first entry is extracted, and it has to be
- + either deflated or stored.
- + */
- +
- +#ifndef lint
- +static char rcsid[] = "$Id: unzip.c,v 0.9 1993/02/10 16:07:22 jloup Exp $";
- +#endif
- +
- +#include "gzip.h"
- +#include "crypt.h"
- +
- +#include <stdio.h>
- +
- +/* PKZIP header definitions */
- +#define LOCSIG 0x04034b50L /* four-byte lead-in (lsb first) */
- +#define LOCFLG 6 /* offset of bit flag */
- +#define CRPFLG 1 /* bit for encrypted entry */
- +#define EXTFLG 8 /* bit for extended local header */
- +#define LOCHOW 8 /* offset of compression method */
- +#define LOCTIM 10 /* file mod time (for decryption) */
- +#define LOCCRC 14 /* offset of crc */
- +#define LOCSIZ 18 /* offset of compressed size */
- +#define LOCLEN 22 /* offset of uncompressed length */
- +#define LOCFIL 26 /* offset of file name field length */
- +#define LOCEXT 28 /* offset of extra field length */
- +#define LOCHDR 30 /* size of local header, including sig */
- +#define EXTHDR 16 /* size of extended local header, inc sig */
- +
- +
- +/* Globals */
- +
- +int decrypt; /* flag to turn on decryption */
- +char *key; /* not used--needed to link crypt.c */
- +int pkzip = 0; /* set for a pkzip file */
- +int extended = 0; /* set if extended local header */
- +
- +/* ===========================================================================
- + * Check zip file and advance inptr to the start of the compressed data.
- + * Get ofname from the local header if necessary.
- + */
- +int check_zipfile(in)
- + int in; /* input file descriptors */
- +{
- + uch *h = inbuf + inptr; /* first local header */
- +
- + /* ifd = in; */
- +
- + /* Check validity of local header, and skip name and extra fields */
- + inptr += LOCHDR + SH(h + LOCFIL) + SH(h + LOCEXT);
- +
- + if (inptr > insize || LG(h) != LOCSIG) {
- + error("input not a zip");
- + }
- + method = h[LOCHOW];
- + if (method != STORED && method != DEFLATED) {
- + error("first entry not deflated or stored--can't extract");
- + }
- +
- + /* If entry encrypted, decrypt and validate encryption header */
- + if ((decrypt = h[LOCFLG] & CRPFLG) != 0) {
- + error("encrypted file\n");
- + exit_code = ERROR;
- + return -1;
- + }
- +
- + /* Save flags for unzip() */
- + extended = (h[LOCFLG] & EXTFLG) != 0;
- + pkzip = 1;
- +
- + /* Get ofname and time stamp from local header (to be done) */
- + return 0;
- +}
- +
- +/* ===========================================================================
- + * Unzip in to out. This routine works on both gzip and pkzip files.
- + *
- + * IN assertions: the buffer inbuf contains already the beginning of
- + * the compressed data, from offsets inptr to insize-1 included.
- + * The magic header has already been checked. The output buffer is cleared.
- + */
- +void unzip(in, out)
- + int in, out; /* input and output file descriptors */
- +{
- + ulg orig_crc = 0; /* original crc */
- + ulg orig_len = 0; /* original uncompressed length */
- + int n;
- + uch buf[EXTHDR]; /* extended local header */
- +
- + /* ifd = in;
- + ofd = out; */
- +
- + updcrc(NULL, 0); /* initialize crc */
- +
- + if (pkzip && !extended) { /* crc and length at the end otherwise */
- + orig_crc = LG(inbuf + LOCCRC);
- + orig_len = LG(inbuf + LOCLEN);
- + }
- +
- + /* Decompress */
- + if (method == DEFLATED) {
- +
- + int res = inflate();
- +
- + if (res == 3) {
- + error("out of memory");
- + } else if (res != 0) {
- + error("invalid compressed format");
- + }
- +
- + } else if (pkzip && method == STORED) {
- +
- + register ulg n = LG(inbuf + LOCLEN);
- +
- + if (n != LG(inbuf + LOCSIZ) - (decrypt ? RAND_HEAD_LEN : 0)) {
- +
- + error("length mismatch");
- + }
- + while (n--) {
- + uch c = (uch)get_byte();
- +#ifdef CRYPT
- + if (decrypt) zdecode(c);
- +#endif
- + if (!test) put_char(c);
- + }
- + } else {
- + error("internal error, invalid method");
- + }
- +
- + /* Get the crc and original length */
- + if (!pkzip) {
- + /* crc32 (see algorithm.doc)
- + * uncompressed input size modulo 2^32
- + */
- + for (n = 0; n < 8; n++) {
- + buf[n] = (uch)get_byte(); /* may cause an error if EOF */
- + }
- + orig_crc = LG(buf);
- + orig_len = LG(buf+4);
- +
- + } else if (extended) { /* If extended header, check it */
- + /* signature - 4bytes: 0x50 0x4b 0x07 0x08
- + * CRC-32 value
- + * compressed size 4-bytes
- + * uncompressed size 4-bytes
- + */
- + for (n = 0; n < EXTHDR; n++) {
- + buf[n] = (uch)get_byte(); /* may cause an error if EOF */
- + }
- + orig_crc = LG(buf+4);
- + orig_len = LG(buf+12);
- + }
- +
- + /* Validate decompression */
- + if (orig_crc != updcrc(outbuf, 0)) {
- + error("crc error");
- + }
- + if (orig_len != bytes_out) {
- + error("length error");
- + }
- +
- + /* Check if there are more entries in a pkzip file */
- + if (pkzip && inptr + 4 < insize && LG(inbuf+inptr) == LOCSIG) {
- + error("zip file has more than one entry");
- + }
- + extended = pkzip = 0; /* for next file */
- +}
- diff -urNwbB linux/arch/arm/boot/compressed/xtract.c linux.arm/arch/arm/boot/compressed/xtract.c
- --- linux/arch/arm/boot/compressed/xtract.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/compressed/xtract.c Sun Feb 11 09:32:12 1996
- @@ -0,0 +1,86 @@
- +/*
- + * linux/zBoot/xtract.c
- + *
- + * Copyright (C) 1993 Hannu Savolainen
- + *
- + * Extracts the system image and writes it to the stdout.
- + * based on tools/build.c by Linus Torvalds
- + */
- +
- +#include <stdio.h> /* fprintf */
- +#include <string.h>
- +#include <stdlib.h> /* contains exit */
- +#include <sys/types.h> /* unistd.h needs this */
- +#include <sys/stat.h>
- +#include <sys/sysmacros.h>
- +#include <unistd.h> /* contains read/write */
- +#include <fcntl.h>
- +#include <a.out.h>
- +
- +#define N_MAGIC_OFFSET 1024
- +
- +static int GCC_HEADER = sizeof(struct exec);
- +
- +#define STRINGIFY(x) #x
- +
- +void die(char * str)
- +{
- + fprintf(stderr,"%s\n",str);
- + exit(1);
- +}
- +
- +void usage(void)
- +{
- + die("Usage: xtract system [ | gzip | piggyback > piggy.s]");
- +}
- +
- +int main(int argc, char ** argv)
- +{
- + int i,c,id, sz;
- + char buf[1024];
- + char major_root, minor_root;
- + struct stat sb;
- +
- + struct exec *ex = (struct exec *)buf;
- +
- + if (argc != 2)
- + usage();
- +
- + if ((id=open(argv[1],O_RDONLY,0))<0)
- + die("Unable to open 'system'");
- + if (read(id,buf,GCC_HEADER) != GCC_HEADER)
- + die("Unable to read header of 'system'");
- + if (N_MAGIC(*ex) == ZMAGIC) {
- + GCC_HEADER = N_MAGIC_OFFSET;
- + lseek(id, GCC_HEADER, SEEK_SET);
- + } else if (N_MAGIC(*ex) != QMAGIC)
- + die("Non-GCC header of 'system'");
- +
- + sz = N_SYMOFF(*ex) - GCC_HEADER + 4; /* +4 to get the same result than tools/build */
- +
- + fprintf(stderr, "System size is %d\n", sz);
- +
- + while (sz)
- + {
- + int l, n;
- +
- + l = sz;
- + if (l > sizeof(buf)) l = sizeof(buf);
- +
- + if ((n=read(id, buf, l)) !=l)
- + {
- + if (n == -1)
- + perror(argv[1]);
- + else
- + fprintf(stderr, "Unexpected EOF\n");
- +
- + die("Can't read system");
- + }
- +
- + write(1, buf, l);
- + sz -= l;
- + }
- +
- + close(id);
- + return(0);
- +}
- diff -urNwbB linux/arch/arm/boot/install.sh linux.arm/arch/arm/boot/install.sh
- --- linux/arch/arm/boot/install.sh Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/install.sh Sun Mar 3 12:16:58 1996
- @@ -0,0 +1,60 @@
- +#!/bin/sh
- +#
- +# arch/i386/boot/install.sh
- +#
- +# This file is subject to the terms and conditions of the GNU General Public
- +# License. See the file "COPYING" in the main directory of this archive
- +# for more details.
- +#
- +# Copyright (C) 1995 by Linus Torvalds
- +#
- +# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin
- +#
- +# "make install" script for i386 architecture
- +#
- +# Arguments:
- +# $1 - kernel version
- +# $2 - kernel image file
- +# $3 - kernel map file
- +# $4 - default install path (blank if root directory)
- +#
- +
- +# User may have a custom install script
- +
- +if [ -x /sbin/installkernel ]; then
- + exec /sbin/installkernel "$@"
- +fi
- +
- +if [ "$2" = "zImage" ]; then
- +# Compressed install
- + echo "Installing compressed kernel"
- + if [ -f $4/vmlinuz-$1 ]; then
- + mv $4/vmlinuz-$1 $4/vmlinuz.old
- + fi
- +
- + if [ -f $4/System.map-$1 ]; then
- + mv $4/System.map-$1 $4/System.old
- + fi
- +
- + cat $2 > $4/vmlinuz-$1
- + cp $3 $4/System.map-$1
- +else
- +# Normal install
- + echo "Installing normal kernel"
- + if [ -f $4/vmlinux-$1 ]; then
- + mv $4/vmlinux-$1 $4/vmlinux.old
- + fi
- +
- + if [ -f $4/System.map ]; then
- + mv $4/System.map $4/System.old
- + fi
- +
- + cat $2 > $4/vmlinux-$1
- + cp $3 $4/System.map
- +fi
- +
- +if [ -x /sbin/loadmap ]; then
- + /sbin/loadmap
- +else
- + echo "You have to install it yourself"
- +fi
- diff -urNwbB linux/arch/arm/boot/tools/build.c linux.arm/arch/arm/boot/tools/build.c
- --- linux/arch/arm/boot/tools/build.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/tools/build.c Sun Feb 11 22:01:19 1996
- @@ -0,0 +1,56 @@
- +#include <stdio.h>
- +#include <stdlib.h>
- +#include <a.out.h>
- +#include <string.h>
- +
- +int main(int argc, char **argv)
- +{
- + void *data;
- + struct exec ex;
- + FILE *f;
- + int totlen;
- +
- + if (argc < 2) {
- + fprintf(stderr, "Usage: build kernel-name\n");
- + exit(1);
- + }
- +
- + f = fopen(argv[1], "rb");
- +
- + fread(&ex, 1, sizeof(ex), f);
- +
- + if(N_MAGIC(ex) == ZMAGIC) {
- + fseek(f, 4096, SEEK_SET);
- + totlen = ex.a_text + ex.a_data + ex.a_bss;
- + } else
- + if(N_MAGIC(ex) == QMAGIC) {
- + unsigned long my_header[8];
- +
- + fseek(f, 0x20, SEEK_SET);
- +
- + memset(my_header, 0, 0x20);
- +
- + my_header[0] = 0xea000006;
- +
- + fwrite(my_header, 4, 8, stdout);
- +
- + totlen = ex.a_text + ex.a_data + ex.a_bss - 0x20;
- + } else {
- + fprintf(stderr, "Unacceptable a.out header on kernel\n");
- + fclose(f);
- + exit(1);
- + }
- +
- + fprintf(stderr, "Kernel is %dk (%dk text, %dk data, %dk bss)\n",
- + (ex.a_text + ex.a_data + ex.a_bss)/1024,
- + ex.a_text/1024, ex.a_data/1024, ex.a_bss/1024);
- +
- + data = malloc(totlen);
- + fread(data, 1, totlen, f);
- + fwrite(data, 1, totlen, stdout);
- +
- + free(data);
- + fclose(f);
- + fflush(stdout);
- + return 0;
- +}
- diff -urNwbB linux/arch/arm/config.in linux.arm/arch/arm/config.in
- --- linux/arch/arm/config.in Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/config.in Sat Feb 24 21:32:58 1996
- @@ -0,0 +1,202 @@
- +#
- +# For a description of the syntax of this configuration file,
- +# see the Configure script.
- +#
- +# This is to tell configure not to execute the sound configuration.
- +#
- +CONFIG_ARM=1
- +
- +mainmenu_name "Linux kernel configuration"
- +
- +echo "/* Automatically generated by configure. Use Make config to change */" >> $CONFIG_H
- +#
- +# These are to alter the behaviour of various Makefiles and init/main.c
- +# in the architecture-independent areas.
- +#
- +echo "#define CONFIG_ARM 1" >> $CONFIG_H
- +echo "CONFIG_ARM=1" >> $CONFIG
- +
- +mainmenu_option next_comment
- +comment 'General setup'
- +bool 'Compile for arm3' CONFIG_ARM3 y
- +if [ "$CONFIG_ARM3" = "n" ]; then
- + bool 'Compile for arm610/710' CONFIG_ARM610 y
- + if [ "$CONFIG_ARM610" = "n" ]; then
- + bool 'Compile for StrongARM' CONFIG_ARMSTRONG y
- + fi
- +else
- + bool 'Page size as 32K' CONFIG_PAGE_32K y
- + if [ "$CONFIG_PAGE_32K" = "n" ]; then
- + bool 'Page size as 16K' CONFIG_PAGE_16K y
- + if [ "$CONFIG_PAGE_16K" = "n" ]; then
- + echo "Auto detected page size"
- + fi
- + fi
- +fi
- +
- +comment 'Math emulation is provided by a run-time loadable module'
- +
- +tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD y
- +bool 'Normal IDE disk/cdrom support' CONFIG_ST506 y
- +if [ "$CONFIG_ST506" = "y" ]; then
- + comment 'Please see drivers/block/README.ide for help/info on IDE drives'
- + bool ' Use old disk-only driver for primary i/f' CONFIG_BLK_DEV_HD y
- +# if [ "$CONFIG_BLK_DEV_HD" = "y" ]; then
- +# bool ' Include new IDE driver for secondary i/f support' CONFIG_BLK_DEV_IDE n
- +# else
- +# bool ' Use new IDE driver for primary/secondary i/f' CONFIG_BLK_DEV_IDE y
- +# fi
- +# if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
- +# bool ' Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD n
- +# fi
- +fi
- +tristate 'MFM harddisk support' CONFIG_BLK_DEV_XD n
- +
- +bool 'Networking support' CONFIG_NET y
- +bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
- +bool 'System V IPC' CONFIG_SYSVIPC y
- +#tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF n
- +
- +mainmenu_option next_comment
- +comment 'Loadable module support'
- +bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS y
- +
- +if [ "$CONFIG_NET" = "y" ]; then
- + mainmenu_option next_comment
- + comment 'Networking options'
- + bool 'TCP/IP networking' CONFIG_INET y
- + if [ "$CONFIG_INET" = "y" ]; then
- + bool 'IP: forwarding/gatewaying' CONFIG_IP_FORWARD n
- + bool 'IP: multicasting' CONFIG_IP_MULTICAST n
- + bool 'IP: firewalling' CONFIG_IP_FIREWALL n
- + bool 'IP: accounting' CONFIG_IP_ACCT n
- + tristate 'IP: tunneling' CONFIG_NET_IPIP n
- + if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_FIREWALL" = "y" ]; then
- + bool 'IP: firewall packet logging' CONFIG_IP_FIREWALL_VERBOSE y
- + bool 'IP: masquerading (ALPHA)' CONFIG_IP_MASQUERADE n
- + fi
- + comment '(it is safe to leave these untouched)'
- + bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP n
- + tristate 'IP: Reverse ARP' CONFIG_INET_RARP n
- + bool 'IP: Assume subnets are local' CONFIG_INET_SNARL y
- + bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n
- + bool 'IP: Drop source routed frames' CONFIG_IP_NOSR y
- + bool 'IP: Allow large windows (not recommended if <16MB of memory)' CONFIG_SKB_LARGE n
- + fi
- + bool 'The IPX protocol' CONFIG_IPX n
- + bool 'Appletalk DDP' CONFIG_ATALK n
- + bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n
- + if [ "$CONFIG_AX25" = "y" ]; then
- + bool 'Amateur Radio NET/ROM' CONFIG_NETROM n
- + fi
- + bool 'Kernel/User network link driver(ALPHA)' CONFIG_NETLINK n
- + if [ "$CONFIG_NETLINK" = "y" ]; then
- + bool 'Routing messages' CONFIG_RTNETLINK y
- + fi
- +fi
- +
- +mainmenu_option next_comment
- +comment 'SCSI support'
- +
- +tristate 'SCSI support?' CONFIG_SCSI n
- +
- +if [ "$CONFIG_SCSI" = "n" ]; then
- +
- + comment 'Skipping SCSI configuration options...'
- +
- +else
- +
- + comment 'SCSI support type (disk, tape, CDrom)'
- +
- + dep_tristate 'SCSI disk support' CONFIG_BLK_DEV_SD y $CONFIG_SCSI
- + dep_tristate 'SCSI tape support' CONFIG_CHR_DEV_ST y $CONFIG_SCSI
- + dep_tristate 'SCSI CDROM support' CONFIG_BLK_DEV_SR y $CONFIG_SCSI
- + dep_tristate 'SCSI generic support' CONFIG_CHR_DEV_SG n $CONFIG_SCSI
- +
- + comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs'
- +
- + bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN n
- +
- + mainmenu_option next_comment
- + comment 'SCSI low-level drivers'
- +
- + dep_tristate 'Acorn SCSI card (aka30) support' CONFIG_SCSI_ACORNSCSI_3 y $CONFIG_SCSI
- + dep_tristate 'Cumana 1 support' CONFIG_SCSI_CUMANA_1 n $CONFIG_SCSI
- + dep_tristate 'EcoScsi support' CONFIG_SCSI_ECOSCSI n $CONFIG_SCSI
- + dep_tristate 'Oak SCSI support' CONFIG_SCSI_OAK n $CONFIG_SCSI
- + dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n $CONFIG_SCSI
- +fi
- +
- +if [ "$CONFIG_NET" = "y" ]; then
- +
- + mainmenu_option next_comment
- + comment 'Network device support'
- +
- + bool 'Network device support?' CONFIG_NETDEVICES y
- + if [ "$CONFIG_NETDEVICES" = "n" ]; then
- +
- + comment 'Skipping network driver configuration options...'
- +
- + else
- + tristate 'Dummy net driver support' CONFIG_DUMMY y
- + tristate 'SLIP (serial line) support' CONFIG_SLIP y
- + if [ "$CONFIG_SLIP" = "y" ]; then
- + bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED y
- + fi
- + tristate 'PPP (point-to-point) support' CONFIG_PPP y
- + if [ ! "$CONFIG_PPP" = "n" ]; then
- + comment 'CCP compressors for PPP are only built as modules.'
- + fi
- + tristate 'PLIP (parallel port) support' CONFIG_PLIP y
- + tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER n
- + tristate 'Ether1 (82586) support' CONFIG_ETHER1 n
- + tristate 'Ether3 (NQ8005) support' CONFIG_ETHER3 n
- + fi
- +fi
- +
- +mainmenu_option next_comment
- +comment 'Filesystems'
- +
- +tristate 'Standard (minix) fs support' CONFIG_MINIX_FS n
- +tristate 'Extended fs support' CONFIG_EXT_FS n
- +tristate 'Second extended fs support' CONFIG_EXT2_FS y
- +tristate 'xiafs filesystem support' CONFIG_XIA_FS n
- +tristate 'msdos fs support' CONFIG_MSDOS_FS y
- +if [ "$CONFIG_MSDOS_FS" = "y" ]; then
- + tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n
- +fi
- +tristate '/proc filesystem support' CONFIG_PROC_FS y
- +tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y
- +tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
- +tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
- +if [ "$CONFIG_INET" = "y" ]; then
- + tristate 'NFS filesystem support' CONFIG_NFS_FS y
- + tristate 'SMB filesystem (to mount WfW shares etc...) support' CONFIG_SMB_FS n
- +fi
- +
- +mainmenu_option next_comment
- +comment 'Character devices'
- +
- +tristate 'Parallel printer support' CONFIG_PRINTER y
- +tristate 'Mouse support' CONFIG_MOUSE y
- +if [ "$CONFIG_MOUSE" = "n" ]; then
- + comment 'Skipping mouse options...'
- +else
- + tristate 'Keyboard mouse [all but RiscPC]' CONFIG_KBDMOUSE y
- +fi
- +
- +mainmenu_option next_comment
- +comment 'Sound'
- +
- +tristate 'Sound support' CONFIG_SOUND n
- +
- +mainmenu_option next_comment
- +comment 'Kernel hacking'
- +
- +bool 'Kernel profiling support' CONFIG_PROFILE n
- +if [ "$CONFIG_PROFILE" = "y" ]; then
- + int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
- +fi
- +if [ "$CONFIG_SCSI" = "y" ]; then
- +bool 'Verbose scsi error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y
- +fi
- diff -urNwbB linux/arch/arm/drivers/Makefile linux.arm/arch/arm/drivers/Makefile
- --- linux/arch/arm/drivers/Makefile Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/Makefile Sun Feb 11 09:32:13 1996
- @@ -0,0 +1,33 @@
- +#
- +# Makefile for the linux kernel device drivers.
- +#
- +# Note! Dependencies are done automagically by 'make dep', which also
- +# removes any old dependencies. DON'T put your own dependencies here
- +# unless it's something special (ie not a .c file).
- +#
- +# Note 2! The CFLAGS definitions are now in the main makefile...
- +
- +SUB_DIRS := block char net #streams
- +MOD_SUB_DIRS := $(SUB_DIRS)
- +ALL_SUB_DIRS := $(SUB_DIRS) scsi sound
- +
- +# If CONFIG_SCSI is set, the core of scsi support will be added to the kernel,
- +# but some of the low-level things may also be modules.
- +ifeq ($(CONFIG_SCSI),y)
- +SUB_DIRS += scsi
- +MOD_SUB_DIRS += scsi
- +else
- + ifeq ($(CONFIG_SCSI),m)
- + MOD_SUB_DIRS += scsi
- + endif
- +endif
- +
- +ifeq ($(CONFIG_SOUND),y)
- +SUB_DIRS += sound
- +else
- + ifeq ($(CONFIG_SOUND),m)
- + MOD_SUB_DIRS += sound
- + endif
- +endif
- +
- +include $(TOPDIR)/Rules.make
- diff -urNwbB linux/arch/arm/drivers/block/Makefile linux.arm/arch/arm/drivers/block/Makefile
- --- linux/arch/arm/drivers/block/Makefile Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/Makefile Sat Feb 24 13:17:05 1996
- @@ -0,0 +1,63 @@
- +#
- +# Makefile for the kernel block device drivers.
- +#
- +# Note! Dependencies are done automagically by 'make dep', which also
- +# removes any old dependencies. DON'T put your own dependencies here
- +# unless it's something special (ie not a .c file).
- +#
- +# Note 2! The CFLAGS definition is now inherited from the
- +# parent makefile.
- +#
- +
- +all: links first_rule
- +
- +L_TARGET := block.a
- +L_OBJS := ll_rw_blk.o ramdisk.o genhd.o hdsrch.o
- +M_OBJS :=
- +MOD_LIST_NAME := BLOCK_MODULES
- +
- +ifeq ($(CONFIG_BLK_DEV_FD),y)
- +L_OBJS += floppy.o
- +else
- + ifeq ($(CONFIG_BLK_DEV_FD),m)
- + M_OBJS += floppy.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_BLK_DEV_HD),y)
- +L_OBJS += hd.o
- +endif
- +
- +ifeq ($(CONFIG_BLK_DEV_IDE),y)
- +L_OBJS += ide.o
- +endif
- +
- +ifeq ($(CONFIG_BLK_DEV_MFM),y)
- +L_OBJS += mfmhd.o
- +else
- + ifeq ($(CONFIG_BLK_DEV_MFM),m)
- + M_OBJS += mfmhd_mod.o
- + endif
- +endif
- +
- +include $(TOPDIR)/Rules.make
- +
- +fastdep: links
- +
- +mfmhd_mod.o: mfmhd.o
- + ld -r -o $@ mfmhd.o `gcc --print-libgcc-file-name`
- +
- +LK = blk.h ll_rw_blk.c ramdisk.c README.fd README.hd
- +
- +links:
- + -@for f in $(LK); do \
- + ln -s ../../../../drivers/block/$$f .; \
- + done
- + touch links
- +
- +LINKCLEAN:
- + -@for f in $(LK); do \
- + if [ -L $$f ]; then rm -f $$f; fi; \
- + done
- + rm -f links
- +
- diff -urNwbB linux/arch/arm/drivers/block/floppy.c linux.arm/arch/arm/drivers/block/floppy.c
- --- linux/arch/arm/drivers/block/floppy.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/floppy.c Sun Mar 3 12:20:41 1996
- @@ -0,0 +1,4120 @@
- +/*
- + * linux/arch/arm/drivers/block/floppy.c
- + * [ was linux/drivers/block/floppy.c ]
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + * Copyright (C) 1993, 1994 Alain Knaff
- + * Modifications Copyright (C) 1995 Russell King
- + */
- +/*
- + * 02.12.91 - Changed to static variables to indicate need for reset
- + * and recalibrate. This makes some things easier (output_byte reset
- + * checking etc), and means less interrupt jumping in case of errors,
- + * so the code is hopefully easier to understand.
- + */
- +
- +/*
- + * This file is certainly a mess. I've tried my best to get it working,
- + * but I don't like programming floppies, and I have only one anyway.
- + * Urgel. I should check for more errors, and do more graceful error
- + * recovery. Seems there are problems with several drives. I've tried to
- + * correct them. No promises.
- + */
- +
- +/*
- + * As with hd.c, all routines within this file can (and will) be called
- + * by interrupts, so extreme caution is needed. A hardware interrupt
- + * handler may not sleep, or a kernel panic will happen. Thus I cannot
- + * call "floppy-on" directly, but have to set a special timer interrupt
- + * etc.
- + */
- +
- +/*
- + * 28.02.92 - made track-buffering routines, based on the routines written
- + * by entropy@wintermute.wpi.edu (Lawrence Foard). Linus.
- + */
- +
- +/*
- + * Automatic floppy-detection and formatting written by Werner Almesberger
- + * (almesber@nessie.cs.id.ethz.ch), who also corrected some problems with
- + * the floppy-change signal detection.
- + */
- +
- +/*
- + * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed
- + * FDC data overrun bug, added some preliminary stuff for vertical
- + * recording support.
- + *
- + * 1992/9/17: Added DMA allocation & DMA functions. -- hhb.
- + *
- + * TODO: Errors are still not counted properly.
- + */
- +
- +/* 1992/9/20
- + * Modifications for ``Sector Shifting'' by Rob Hooft (hooft@chem.ruu.nl)
- + * modelled after the freeware MS/DOS program fdformat/88 V1.8 by
- + * Christoph H. Hochst\"atter.
- + * I have fixed the shift values to the ones I always use. Maybe a new
- + * ioctl() should be created to be able to modify them.
- + * There is a bug in the driver that makes it impossible to format a
- + * floppy as the first thing after bootup.
- + */
- +
- +/*
- + * 1993/4/29 -- Linus -- cleaned up the timer handling in the kernel, and
- + * this helped the floppy driver as well. Much cleaner, and still seems to
- + * work.
- + */
- +
- +/* 1994/6/24 --bbroad-- added the floppy table entries and made
- + * minor modifications to allow 2.88 floppies to be run.
- + */
- +
- +/* 1994/7/13 -- Paul Vojta -- modified the probing code to allow three or more
- + * disk types.
- + */
- +
- +/*
- + * 1994/8/8 -- Alain Knaff -- Switched to fdpatch driver: Support for bigger
- + * format bug fixes, but unfortunately some new bugs too...
- + */
- +
- +/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
- + * errors to allow safe writing by specialized programs.
- + */
- +
- +/* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks
- + * by defining bit 1 of the "stretch" parameter to mean put sectors on the
- + * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's
- + * drives are "upside-down").
- + */
- +
- +/*
- + * 1995/8/16 -- Russell King -- altered method for turning floppy drives on
- + * for the arm.
- + */
- +#define CONFIG_FLOPPY_SANITY
- +#undef CONFIG_FLOPPY_SILENT_DCL_CLEAR
- +
- +#define DEBUGT 2
- +#define DCL_DEBUG /* debug disk change line */
- +
- +#include <linux/config.h>
- +
- +/* do print messages for unexpected interrupts */
- +static int print_unex=1;
- +
- +#ifdef MODULE
- +#define FD_MODULE
- +
- +#include <linux/module.h>
- +/*
- + * NB. we must include the kernel identification string to install the module.
- + */
- +#include <linux/version.h>
- +char kernel_version[] = UTS_RELEASE;
- +
- +int FLOPPY_IRQ=12;
- +int FLOPPY_DMA=2;
- +int ALLOWED_DRIVE_MASK = 0x33;
- +int FDC1 = 0x3f0;
- +int FDC2 = -1;
- +
- +#endif
- +
- +#ifndef FD_MODULE
- +/* the following is the mask of allowed drives. By default units 2 and
- + * 3 of both floppy controllers are disabled, because switching on the
- + * motor of these drives causes system hangs on some PCI computers. drive
- + * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if
- + * a drive is allowed. */
- +static int ALLOWED_DRIVE_MASK=0x33;
- +
- +#define FLOPPY_IRQ 12
- +#define FLOPPY_DMA 2
- +#define FDC1 0x3f0
- +static int FDC2=-1;
- +#endif
- +
- +#define MODULE_AWARE_DRIVER
- +
- +#include <linux/sched.h>
- +#include <linux/fs.h>
- +#include <linux/kernel.h>
- +#include <linux/timer.h>
- +#include <linux/tqueue.h>
- +#define FDPATCHES
- +#include <linux/fdreg.h>
- +
- +/* ============================================ *
- + * old fd.h *
- + * =========================================== */
- +
- +#define OLDFDCLRPRM 0 /* clear user-defined parameters */
- +#define OLDFDSETPRM 1 /* set user-defined parameters for current media */
- +#define OLDFDSETMEDIAPRM 1
- +#define OLDFDDEFPRM 2 /* set user-defined parameters until explicitly cleared */
- +#define OLDFDDEFMEDIAPRM 2
- +#define OLDFDGETPRM 3 /* get disk parameters */
- +#define OLDFDGETMEDIAPRM 3
- +#define OLDFDMSGON 4 /* issue kernel messages on media type change */
- +#define OLDFDMSGOFF 5 /* don't issue kernel messages on media type change */
- +#define OLDFDFMTBEG 6 /* begin formatting a disk */
- +#define OLDFDFMTTRK 7 /* format the specified track */
- +#define OLDFDFMTEND 8 /* end formatting a disk */
- +#define OLDFDSETEMSGTRESH 10 /* set fdc error reporting threshold */
- +#define OLDFDFLUSH 11 /* flush buffers for media; either for verifying media, or for
- + handling a media change without closing the file
- + descriptor */
- +#define OLDFDSETMAXERRS 12 /* set abortion and read_track threshold */
- +#define OLDFDGETMAXERRS 14 /* get abortion and read_track threshold */
- +#define OLDFDGETDRVTYP 16 /* get drive type: 5 1/4 or 3 1/2 */
- +
- +#define OLDFDSETDRVPRM 20 /* set drive parameters */
- +#define OLDFDGETDRVPRM 21 /* get drive parameters */
- +#define OLDFDGETDRVSTAT 22 /* get drive state */
- +#define OLDFDPOLLDRVSTAT 23 /* get drive state */
- +#define OLDFDRESET 24 /* reset FDC */
- +
- +#define OLDFD_RESET_IF_NEEDED 0
- +#define OLDFD_RESET_IF_RAWCMD 1
- +#define OLDFD_RESET_ALWAYS 2
- +
- +#define OLDFDGETFDCSTAT 25 /* get fdc state */
- +#define OLDFDWERRORCLR 27 /* clear write error and badness information */
- +#define OLDFDWERRORGET 28 /* get write error and badness information */
- +
- +#define OLDFDRAWCMD 30 /* send a raw command to the fdc */
- +
- +#define OLDFDTWADDLE 40 /* flicker motor-on bit before reading a sector */
- +
- +struct old_floppy_raw_cmd {
- + void *data;
- + long length;
- +
- + unsigned char rate;
- + unsigned char flags;
- + unsigned char cmd_count;
- + unsigned char cmd[9];
- + unsigned char reply_count;
- + unsigned char reply[7];
- + int track;
- +};
- +
- +struct old_floppy_fdc_state {
- + int spec1; /* spec1 value last used */
- + int spec2; /* spec2 value last used */
- + int dtr;
- + unsigned char version; /* FDC version code */
- + unsigned char dor;
- + int address; /* io address */
- + unsigned int rawcmd:2;
- + unsigned int reset:1;
- + unsigned int need_configure:1;
- + unsigned int perp_mode:2;
- + unsigned int has_fifo:1;
- +};
- +
- +
- +#include <linux/fd.h>
- +#include <linux/errno.h>
- +#include <linux/malloc.h>
- +#include <linux/mm.h>
- +#include <linux/string.h>
- +#include <linux/fcntl.h>
- +#include <linux/delay.h>
- +
- +#include <linux/ioport.h>
- +
- +#include <asm/dma.h>
- +#include <asm/irq.h>
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include <asm/segment.h>
- +
- +#define MAJOR_NR FLOPPY_MAJOR
- +#include "blk.h"
- +
- +
- +/* Dma Memory related stuff */
- +
- +/* Pure 2^n version of get_order */
- +static inline int __get_order (int size)
- +{
- + int order;
- +
- + for (order = 0; order < NR_MEM_LISTS; ++order)
- + if (size <= (PAGE_SIZE << order))
- + return order;
- + return NR_MEM_LISTS;
- +}
- +
- +static unsigned long dma_mem_alloc(int size)
- +{
- + int order = __get_order(size);
- +
- + if (order >= NR_MEM_LISTS)
- + return(0);
- + return __get_dma_pages(GFP_KERNEL,order);
- +}
- +
- +/* End dma memory related stuff */
- +
- +static unsigned int fake_change = 0;
- +static int initialising=1;
- +
- +#define FLOPPY0_TYPE 4
- +#define FLOPPY1_TYPE 0
- +
- +#define N_FDC 2
- +#define N_DRIVE 8
- +
- +static inline int TYPE(kdev_t x) {
- + return (MINOR(x)>>2) & 0x1f;
- +}
- +static inline int DRIVE(kdev_t x) {
- + return (MINOR(x)&0x03) | ((MINOR(x)&0x80)>>5);
- +}
- +#define TOMINOR(x) ((x & 3) | ((x & 4) << 5))
- +#define UNIT(x) ( (x) & 0x03 ) /* drive on fdc */
- +#define FDC(x) ( ((x) & 0x04) >> 2 ) /* fdc of drive */
- +#define REVDRIVE(fdc, unit) ( (unit) + ((fdc) << 2 ))
- + /* reverse mapping from unit and fdc to drive */
- +#define DP (&drive_params[current_drive])
- +#define DRS (&drive_state[current_drive])
- +#define DRWE (&write_errors[current_drive])
- +#define FDCS (&fdc_state[fdc])
- +#define CLEARF(x) (clear_bit(x##_BIT, &DRS->flags))
- +#define SETF(x) (set_bit(x##_BIT, &DRS->flags))
- +#define TESTF(x) (test_bit(x##_BIT, &DRS->flags))
- +
- +#define UDP (&drive_params[drive])
- +#define UDRS (&drive_state[drive])
- +#define UDRWE (&write_errors[drive])
- +#define UFDCS (&fdc_state[FDC(drive)])
- +#define UCLEARF(x) (clear_bit(x##_BIT, &UDRS->flags))
- +#define USETF(x) (set_bit(x##_BIT, &UDRS->flags))
- +#define UTESTF(x) (test_bit(x##_BIT, &UDRS->flags))
- +
- +#define DPRINT(x) printk(DEVICE_NAME "%d: " x,current_drive)
- +
- +#define DPRINT1(x,x1) printk(DEVICE_NAME "%d: " x,current_drive,(x1))
- +
- +#define DPRINT2(x,x1,x2) printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2))
- +
- +#define DPRINT3(x,x1,x2,x3) printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2),(x3))
- +
- +#define PH_HEAD(floppy,head) (((((floppy)->stretch & 2) >>1) ^ head) << 2)
- +#define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH)
- +
- +/* read/write */
- +#define COMMAND raw_cmd->cmd[0]
- +#define DR_SELECT raw_cmd->cmd[1]
- +#define TRACK raw_cmd->cmd[2]
- +#define HEAD raw_cmd->cmd[3]
- +#define SECTOR raw_cmd->cmd[4]
- +#define SIZECODE raw_cmd->cmd[5]
- +#define SECT_PER_TRACK raw_cmd->cmd[6]
- +#define GAP raw_cmd->cmd[7]
- +#define SIZECODE2 raw_cmd->cmd[8]
- +#define NR_RW 9
- +
- +/* format */
- +#define F_SIZECODE raw_cmd->cmd[2]
- +#define F_SECT_PER_TRACK raw_cmd->cmd[3]
- +#define F_GAP raw_cmd->cmd[4]
- +#define F_FILL raw_cmd->cmd[5]
- +#define NR_F 6
- +
- +/*
- + * Maximum disk size (in kilobytes). This default is used whenever the
- + * current disk size is unknown.
- + * [Now it is rather a minimum]
- + */
- +#define MAX_DISK_SIZE 2 /* 3984*/
- +
- +
- +#define K_64 0x10000 /* 64kB */
- +
- +/*
- + * 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 CROSS_64KB(a,s) (0)
- +#undef LAST_DMA_ADDR
- +#define LAST_DMA_ADDR (0x1)
- +
- +/*
- + * globals used by 'result()'
- + */
- +#define MAX_REPLIES 10
- +static unsigned char reply_buffer[MAX_REPLIES];
- +static int inr; /* size of reply buffer, when called from interrupt */
- +#define ST0 (reply_buffer[0])
- +#define ST1 (reply_buffer[1])
- +#define ST2 (reply_buffer[2])
- +#define ST3 (reply_buffer[0]) /* result of GETSTATUS */
- +#define R_TRACK (reply_buffer[3])
- +#define R_HEAD (reply_buffer[4])
- +#define R_SECTOR (reply_buffer[5])
- +#define R_SIZECODE (reply_buffer[6])
- +
- +#define SEL_DLY (2*HZ/100)
- +
- +#define ARRAY_SIZE(x) (sizeof(x) / sizeof( (x)[0] ))
- +/*
- + * this struct defines the different floppy drive types.
- + */
- +static struct {
- + struct floppy_drive_params params;
- + const char *name; /* name printed while booting */
- +} default_drive_params[]= {
- +/* NOTE: the time values in jiffies should be in msec!
- + CMOS drive type
- + | Maximum data rate supported by drive type
- + | | Head load time, msec
- + | | | Head unload time, msec (not used)
- + | | | | Step rate interval, usec
- + | | | | | Time needed for spinup time (jiffies)
- + | | | | | | Timeout for spinning down (jiffies)
- + | | | | | | | Spindown offset (where disk stops)
- + | | | | | | | | Select delay
- + | | | | | | | | | RPS
- + | | | | | | | | | | Max number of tracks
- + | | | | | | | | | | | Interrupt timeout
- + | | | | | | | | | | | | Max nonintlv. sectors
- + | | | | | | | | | | | | | -Max Errors- flags */
- +{{0, 500, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0,
- + 0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" },
- +
- +{{1, 300, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0,
- + 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
- +
- +{{2, 500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0,
- + 0, { 2, 5, 6,23,10,20,11, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/
- +
- +{{3, 250, 16, 16, 3000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
- + 0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/
- +#if 0
- +{{4, 500, 16, 16, 4000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
- + 0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
- +#else
- +{{4, 500, 16, 16, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,0}, FTD_MSG,
- + 0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
- +#endif
- +{{5, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
- + 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
- +
- +{{6, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
- + 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M" } /*3 1/2 ED*/
- +/* | --autodetected formats--- | | |
- + * read_track | | Name printed when booting
- + * | Native format
- + * Frequency of disk change checks */
- +};
- +
- +static struct floppy_drive_params drive_params[N_DRIVE];
- +static struct floppy_drive_struct drive_state[N_DRIVE];
- +static struct floppy_write_errors write_errors[N_DRIVE];
- +static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
- +
- +/*
- + * This struct defines the different floppy types.
- + *
- + * Bit 0 of 'stretch' tells if the tracks need to be doubled for some
- + * types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch'
- + * tells if the disk is in Commodore 1581 format, which means side 0 sectors
- + * are located on side 1 of the disk but with a side 0 ID, and vice-versa.
- + * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the
- + * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical
- + * side 0 is on physical side 0 (but with the misnamed sector IDs).
- + * 'stretch' should probably be renamed to something more general, like
- + * 'options'. Other parameters should be self-explanatory (see also
- + * setfdprm(8)).
- + */
- +/*
- + Size
- + | Sectors per track
- + | | Head
- + | | | Tracks
- + | | | | Stretch
- + | | | | | Gap 1 size
- + | | | | | | Data rate, | 0x40 for perp
- + | | | | | | | Spec1 (stepping rate, head unload
- + | | | | | | | | /fmt gap (gap2) */
- +static struct floppy_struct floppy_type[32] = {
- + { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */
- + { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360" }, /* 1 360KB PC */
- + { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" }, /* 2 1.2MB AT */
- + { 720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360" }, /* 3 360KB SS 3.5" */
- + { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720" }, /* 4 720KB 3.5" */
- + { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360" }, /* 5 360KB AT */
- + { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720KB AT */
- + { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 1.44MB 3.5" */
- + { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 2.88MB 3.5" */
- + { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"CompaQ"}, /* 9 2.88MB 3.5" */
- +
- + { 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25" */
- + { 3360,21,2,80,0,0x1C,0x00,0xCF,0x0C,"H1680" }, /* 11 1.68MB 3.5" */
- + { 820,10,2,41,1,0x25,0x01,0xDF,0x2E,"h410" }, /* 12 410KB 5.25" */
- + { 1640,10,2,82,0,0x25,0x02,0xDF,0x2E,"H820" }, /* 13 820KB 3.5" */
- + { 2952,18,2,82,0,0x25,0x00,0xDF,0x02,"h1476" }, /* 14 1.48MB 5.25" */
- + { 3444,21,2,82,0,0x25,0x00,0xDF,0x0C,"H1722" }, /* 15 1.72MB 3.5" */
- + { 840,10,2,42,1,0x25,0x01,0xDF,0x2E,"h420" }, /* 16 420KB 5.25" */
- + { 1660,10,2,83,0,0x25,0x02,0xDF,0x2E,"H830" }, /* 17 830KB 3.5" */
- + { 2988,18,2,83,0,0x25,0x00,0xDF,0x02,"h1494" }, /* 18 1.49MB 5.25" */
- + { 3486,21,2,83,0,0x25,0x00,0xDF,0x0C,"H1743" }, /* 19 1.74 MB 3.5" */
- +
- + { 1760,11,2,80,0,0x1C,0x09,0xCF,0x00,"h880" }, /* 20 880KB 5.25" */
- + { 2080,13,2,80,0,0x1C,0x01,0xCF,0x00,"D1040" }, /* 21 1.04MB 3.5" */
- + { 2240,14,2,80,0,0x1C,0x19,0xCF,0x00,"D1120" }, /* 22 1.12MB 3.5" */
- + { 3200,20,2,80,0,0x1C,0x20,0xCF,0x2C,"h1600" }, /* 23 1.6MB 5.25" */
- + { 3520,22,2,80,0,0x1C,0x08,0xCF,0x2e,"H1760" }, /* 24 1.76MB 3.5" */
- + { 3840,24,2,80,0,0x1C,0x20,0xCF,0x00,"H1920" }, /* 25 1.92MB 3.5" */
- + { 6400,40,2,80,0,0x25,0x5B,0xCF,0x00,"E3200" }, /* 26 3.20MB 3.5" */
- + { 7040,44,2,80,0,0x25,0x5B,0xCF,0x00,"E3520" }, /* 27 3.52MB 3.5" */
- + { 7680,48,2,80,0,0x25,0x63,0xCF,0x00,"E3840" }, /* 28 3.84MB 3.5" */
- +
- + { 3680,23,2,80,0,0x1C,0x10,0xCF,0x00,"H1840" }, /* 29 1.84MB 3.5" */
- + { 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"D800" }, /* 30 800KB 3.5" */
- + { 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5" */
- +};
- +
- +#define NUMBER(x) (sizeof(x) / sizeof(*(x)))
- +#define SECTSIZE ( _FD_SECTSIZE(*floppy))
- +
- +/* Auto-detection: Disk type used until the next media change occurs. */
- +struct floppy_struct *current_type[N_DRIVE] = {
- + NULL, NULL, NULL, NULL,
- + NULL, NULL, NULL, NULL
- +};
- +
- +/*
- + * User-provided type information. current_type points to
- + * the respective entry of this array.
- + */
- +struct floppy_struct user_params[N_DRIVE];
- +
- +static int floppy_sizes[256];
- +static int floppy_blocksizes[256] = { 0, };
- +
- +/*
- + * The driver is trying to determine the correct media format
- + * while probing is set. rw_interrupt() clears it after a
- + * successful access.
- + */
- +static int probing = 0;
- +
- +/* Synchronization of FDC access. */
- +#define FD_COMMAND_DETECT -2
- +#define FD_COMMAND_NONE -1
- +#define FD_COMMAND_ERROR 2
- +#define FD_COMMAND_OKAY 3
- +
- +static volatile int command_status = FD_COMMAND_NONE, fdc_busy = 0;
- +static struct wait_queue *fdc_wait = NULL, *command_done = NULL;
- +#define NO_SIGNAL (!(current->signal & ~current->blocked) || !interruptible)
- +#define CALL(x) if( (x) == -EINTR) return -EINTR
- +#define ECALL(x) if ((ret = (x))) return ret;
- +#define _WAIT(x,i) CALL(ret=wait_til_done((x),i))
- +#define WAIT(x) _WAIT((x),interruptible)
- +#define IWAIT(x) _WAIT((x),1)
- +
- +/* Errors during formatting are counted here. */
- +static int format_errors;
- +
- +/* Format request descriptor. */
- +static struct format_descr format_req;
- +
- +/*
- + * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps
- + * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
- + * H is head unload time (1=16ms, 2=32ms, etc)
- + */
- +
- +/*
- + * Track buffer
- + * Because these are written to by the DMA controller, they must
- + * not contain a 64k byte boundary crossing, or data will be
- + * corrupted/lost. Alignment of these is enforced in boot/head.S.
- + * Note that you must not change the sizes below without updating head.S.
- + */
- +char *floppy_track_buffer=NULL;
- +int max_buffer_sectors=0;
- +
- +int *errors;
- +typedef void (*done_f)(int);
- +struct cont_t {
- + void (*interrupt)(void); /* this is called after the interrupt of the
- + * main command */
- + void (*redo)(void); /* this is called to retry the operation */
- + void (*error)(void); /* this is called to tally an error */
- + done_f done; /* this is called to say if the operation has
- + * succeeded/failed */
- +} *cont=NULL;
- +
- +static void floppy_ready(void);
- +static void floppy_start(void);
- +static void process_fd_request(void);
- +static void recalibrate_floppy(void);
- +static void floppy_shutdown(void);
- +
- +static int floppy_grab_irq_and_dma(void);
- +static void floppy_release_irq_and_dma(void);
- +
- +/*
- + * The "reset" variable should be tested whenever an interrupt is scheduled,
- + * after the commands have been sent. This is to ensure that the driver doesn't
- + * get wedged when the interrupt doesn't come because of a failed command.
- + * reset doesn't need to be tested before sending commands, because
- + * output_byte is automatically disabled when reset is set.
- + */
- +#define CHECK_RESET { if ( FDCS->reset ){ reset_fdc(); return ; } }
- +static void reset_fdc(void);
- +
- +/*
- + * These are global variables, as that's the easiest way to give
- + * information to interrupts. They are the data used for the current
- + * request.
- + */
- +#define NO_TRACK -1
- +#define NEED_1_RECAL -2
- +#define NEED_2_RECAL -3
- +
- +/* */
- +static int usage_count = 0;
- +
- +
- +/* buffer related variables */
- +static int buffer_track = -1;
- +static int buffer_drive = -1;
- +static int buffer_min = -1;
- +static int buffer_max = -1;
- +
- +/* fdc related variables, should end up in a struct */
- +static struct floppy_fdc_state fdc_state[N_FDC];
- +static int fdc; /* current fdc */
- +
- +static struct floppy_struct * floppy = floppy_type;
- +static unsigned char current_drive = 0;
- +static long current_count_sectors = 0;
- +static unsigned char sector_t; /* sector in track */
- +
- +#ifdef DEBUGT
- +long unsigned debugtimer;
- +#endif
- +
- +/*
- + * Floppy_selects1 is the list of DOR's to select a drive n
- + * Floppy_selects2 is the list of DOR's to select drive fd
- + * On initialisation, the floppy list is scanned, and the drives allocated
- + * in the order that they are found. This is done by seeking the drive
- + * to a non-zero track, and then restoring it to track 0. If an error occurs,
- + * then there is no floppy drive present.
- + */
- +/*extern*/ int no_floppies;
- +unsigned char floppy_selects1[]={ 0x10, 0x21, 0x23, 0x33 };
- +unsigned char floppy_selects2[]={ 0 , 0 , 0 , 0 };
- +
- +/*
- + * Debugging
- + * =========
- + */
- +static inline void set_debugt(void)
- +{
- +#ifdef DEBUGT
- + debugtimer = jiffies;
- +#endif
- +}
- +
- +static inline void debugt(const char *message)
- +{
- +#ifdef DEBUGT
- + if ( DP->flags & DEBUGT )
- + printk("%s dtime=%lu\n", message, jiffies-debugtimer );
- +#endif
- +}
- +
- +typedef void (*timeout_fn)(unsigned long);
- +static struct timer_list fd_timeout ={ NULL, NULL, 0, 0,
- + (timeout_fn) floppy_shutdown };
- +
- +static const char *timeout_message;
- +
- +#ifdef CONFIG_FLOPPY_SANITY
- +static void is_alive(const char *message)
- +{
- + /* this routine checks whether the floppy driver is "alive" */
- + if (fdc_busy && command_status < 2 && !fd_timeout.prev){
- + DPRINT1("timeout handler died: %s\n",message);
- + }
- +}
- +
- +
- +#define OLOGSIZE 20
- +
- +void (*lasthandler)(void) = NULL;
- +int interruptjiffies=0;
- +int resultjiffies=0;
- +int resultsize=0;
- +int lastredo=0;
- +
- +static struct output_log {
- + unsigned char data;
- + unsigned char status;
- + unsigned long jiffies;
- +} output_log[OLOGSIZE];
- +
- +static int output_log_pos=0;
- +#endif
- +
- +#define CURRENTD -1
- +#define MAXTIMEOUT -2
- +
- +
- +
- +static void reschedule_timeout(int drive, const char *message, int marg)
- +{
- + if (drive == CURRENTD )
- + drive = current_drive;
- + del_timer(&fd_timeout);
- + if (drive < 0 || drive > N_DRIVE) {
- + fd_timeout.expires = jiffies + 20*HZ;
- + drive = 0;
- + } else
- + fd_timeout.expires = jiffies + UDP->timeout;
- + add_timer(&fd_timeout);
- + if (UDP->flags & FD_DEBUG) {
- + DPRINT("reschedule timeout ");
- + printk(message, marg);
- + printk("\n");
- + }
- + timeout_message = message;
- +}
- +
- +/*
- + * Bottom half floppy driver.
- + * ==========================
- + *
- + * This part of the file contains the code talking directly to the hardware,
- + * and also the main service loop (seek-configure-spinup-command)
- + */
- +
- +/*
- + * disk change.
- + * This routine is responsible for maintaining the FD_DISK_CHANGE flag,
- + * and the last_checked date.
- + *
- + * last_checked is the date of the last check which showed 'no disk change'
- + * FD_DISK_CHANGE is set under two conditions:
- + * 1. The floppy has been changed after some i/o to that floppy already
- + * took place.
- + * 2. No floppy disk is in the drive. This is done in order to ensure that
- + * requests are quickly flushed in case there is no disk in the drive. It
- + * follows that FD_DISK_CHANGE can only be cleared if there is a disk in
- + * the drive.
- + *
- + * For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet.
- + * For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on
- + * each seek. If a disk is present, the disk change line should also be
- + * cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk
- + * change line is set, this means either that no disk is in the drive, or
- + * that it has been removed since the last seek.
- + *
- + * This means that we really have a third possibility too:
- + * The floppy has been changed after the last seek.
- + */
- +
- +static int disk_change(int drive)
- +{
- + int fdc=FDC(drive);
- +#ifdef CONFIG_FLOPPY_SANITY
- + if(jiffies < UDP->select_delay + UDRS->select_date)
- + DPRINT("WARNING disk change called early\n");
- + if(! (FDCS->dor & (0x10 << UNIT(drive))) ||
- + (FDCS->dor & 3) != UNIT(drive) ||
- + fdc != FDC(drive)){
- + DPRINT("probing disk change on unselected drive\n");
- + DPRINT3("drive=%d fdc=%d dor=%x\n",drive, FDC(drive),
- + FDCS->dor);
- + }
- +#endif
- +
- +#ifdef DCL_DEBUG
- + if (UDP->flags & FD_DEBUG){
- + DPRINT1("checking disk change line for drive %d\n",drive);
- + DPRINT1("jiffies=%ld\n", jiffies);
- + DPRINT1("disk change line=%x\n", inb_p(FD_DIR)&0x80);
- + DPRINT1("flags=%x\n",UDRS->flags);
- + }
- +#endif
- + if (UDP->flags & FD_BROKEN_DCL)
- + return UTESTF(FD_DISK_CHANGED);
- + if( (inb_p(FD_DIR) ^ UDP->flags) & 0x80){
- + USETF(FD_VERIFY); /* verify write protection */
- + if(UDRS->maxblock){
- + /* mark it changed */
- + USETF(FD_DISK_CHANGED);
- +
- + /* invalidate its geometry */
- + if (UDRS->keep_data >= 0) {
- + if ((UDP->flags & FTD_MSG) &&
- + current_type[drive] != NULL)
- + DPRINT("Disk type is undefined after "
- + "disk change\n");
- + current_type[drive] = NULL;
- + floppy_sizes[TOMINOR(current_drive)] = MAX_DISK_SIZE;
- + }
- + }
- + /*USETF(FD_DISK_NEWCHANGE);*/
- + return 1;
- + } else {
- + UDRS->last_checked=jiffies;
- + UCLEARF(FD_DISK_NEWCHANGE);
- + }
- + return 0;
- +}
- +
- +static inline int is_selected(int dor, int unit)
- +{
- + return ( (dor & (0x10 << unit)) && (dor &3) == unit);
- +}
- +
- +static inline void arm_set_dor(int dor)
- +{
- + if(dor & 0xf0)
- + outb_p((dor & 0x0c) | floppy_selects1[dor & 3], FD_DOR);
- + else
- + outb_p((dor & 0x0c), FD_DOR);
- +}
- +
- +static int set_dor(int fdc, char mask, char data)
- +{
- + register unsigned char drive, unit, newdor,olddor;
- +
- + if (FDCS->address == -1)
- + return -1;
- +
- + olddor = FDCS->dor;
- + newdor = (olddor & mask) | data;
- + if ( newdor != olddor ){
- + unit = olddor & 0x3;
- + if(is_selected(olddor, unit) && !is_selected(newdor, unit)){
- + drive = REVDRIVE(fdc,unit);
- +#ifdef DCL_DEBUG
- + if (UDP->flags & FD_DEBUG){
- + DPRINT("calling disk change from set_dor\n");
- + }
- +#endif
- + disk_change(drive);
- + }
- + FDCS->dor = newdor;
- + arm_set_dor(newdor);
- +
- + unit = newdor & 0x3;
- + if(!is_selected(olddor, unit) && is_selected(newdor, unit)){
- + drive = REVDRIVE(fdc,unit);
- + UDRS->select_date = jiffies;
- + }
- + }
- + if ( newdor & 0xf0 )
- + floppy_grab_irq_and_dma();
- + if( olddor & 0xf0 )
- + floppy_release_irq_and_dma();
- + return olddor;
- +}
- +
- +static void twaddle(void)
- +{
- + if (DP->select_delay)
- + return;
- + arm_set_dor(FDCS->dor & ~(0x10<<UNIT(current_drive)));
- + arm_set_dor(FDCS->dor);
- + DRS->select_date = jiffies;
- +}
- +
- +/* reset all driver information about the current fdc. This is needed after
- + * a reset, and after a raw command. */
- +static void reset_fdc_info(int mode)
- +{
- + int drive;
- +
- + FDCS->spec1 = FDCS->spec2 = -1;
- + FDCS->need_configure = 1;
- + FDCS->perp_mode = 1;
- + FDCS->rawcmd = 0;
- + for ( drive = 0; drive < N_DRIVE; drive++)
- + if (FDC(drive) == fdc &&
- + ( mode || UDRS->track != NEED_1_RECAL))
- + UDRS->track = NEED_2_RECAL;
- +}
- +
- +/* selects the fdc and drive, and enables the fdc's input/dma. */
- +static void set_fdc(int drive)
- +{
- + if ( drive >= 0 && drive < N_DRIVE){
- + fdc = FDC(drive);
- + current_drive = drive;
- + }
- + set_dor(fdc,~0,8);
- + set_dor(1-fdc, ~8, 0);
- + if ( FDCS->rawcmd == 2 )
- + reset_fdc_info(1);
- + if( inb_p(FD_STATUS) != STATUS_READY )
- + FDCS->reset = 1;
- +}
- +
- +/* locks the driver */
- +static int lock_fdc(int drive, int interruptible)
- +{
- + if(!usage_count){
- + printk("trying to lock fdc while usage count=0\n");
- + return -1;
- + }
- + floppy_grab_irq_and_dma();
- + cli();
- + while (fdc_busy && NO_SIGNAL)
- + interruptible_sleep_on(&fdc_wait);
- + if(fdc_busy){
- + sti();
- + return -EINTR;
- + }
- + fdc_busy = 1;
- + sti();
- + command_status = FD_COMMAND_NONE;
- + set_fdc(drive);
- + reschedule_timeout(drive, "lock fdc", 0);
- + return 0;
- +}
- +
- +#define LOCK_FDC(drive,interruptible) \
- +if(lock_fdc(drive,interruptible)) return -EINTR;
- +
- +
- +/* unlocks the driver */
- +static inline void unlock_fdc(void)
- +{
- + raw_cmd = NULL;
- + if (!fdc_busy)
- + DPRINT("FDC access conflict!\n");
- +
- + if ( DEVICE_INTR )
- + DPRINT1("device interrupt still active at FDC release: %p!\n",
- + DEVICE_INTR);
- + command_status = FD_COMMAND_NONE;
- + del_timer(&fd_timeout);
- + cont = NULL;
- + fdc_busy = 0;
- + floppy_release_irq_and_dma();
- + wake_up(&fdc_wait);
- +}
- +
- +/* switches the motor off after a given timeout */
- +static void motor_off_callback(unsigned long nr)
- +{
- + unsigned char mask = ~(0x10 << UNIT(nr));
- +
- + set_dor( FDC(nr), mask, 0 );
- +}
- +
- +static struct timer_list motor_off_timer[N_DRIVE] = {
- + { NULL, NULL, 0, 0, motor_off_callback },
- + { NULL, NULL, 0, 1, motor_off_callback },
- + { NULL, NULL, 0, 2, motor_off_callback },
- + { NULL, NULL, 0, 3, motor_off_callback },
- + { NULL, NULL, 0, 4, motor_off_callback },
- + { NULL, NULL, 0, 5, motor_off_callback },
- + { NULL, NULL, 0, 6, motor_off_callback },
- + { NULL, NULL, 0, 7, motor_off_callback }
- +};
- +
- +/* schedules motor off */
- +static void floppy_off(unsigned int drive)
- +{
- + unsigned long volatile delta;
- + register int fdc=FDC(drive);
- +
- + if( !(FDCS->dor & ( 0x10 << UNIT(drive))))
- + return;
- +
- + del_timer(motor_off_timer+drive);
- +
- + /* make spindle stop in a position which minimizes spinup time
- + * next time */
- + if ( UDP->rps ){
- + delta = jiffies - UDRS->first_read_date + HZ -
- + UDP->spindown_offset;
- + delta = (( delta * UDP->rps) % HZ ) / UDP->rps;
- + motor_off_timer[drive].expires = jiffies + UDP->spindown - delta;
- + }
- + add_timer(motor_off_timer+drive);
- +}
- +
- +/*
- + * cycle through all N_DRIVE floppy drives, for disk change testing.
- + * stopping at current drive. This is done before any long operation, to
- + * be sure to have up to date disk change information.
- + */
- +static void scandrives(void)
- +{
- + int i, drive, saved_drive;
- +
- + if (DP->select_delay)
- + return;
- +
- + saved_drive = current_drive;
- + for(i=0; i< N_DRIVE; i++){
- + drive = (saved_drive + i + 1 ) % N_DRIVE;
- + if ( UDRS->fd_ref == 0 || UDP->select_delay != 0)
- + continue; /* skip closed drives */
- + set_fdc(drive);
- + if(! (set_dor( fdc, ~3, UNIT(drive) | ( 0x10 << UNIT(drive))) &
- + (0x10 << UNIT(drive))))
- + /* switch the motor off again, if it was off to
- + * begin with */
- + set_dor( fdc, ~( 0x10 << UNIT(drive) ), 0 );
- + }
- + set_fdc(saved_drive);
- +}
- +
- +static struct timer_list fd_timer ={ NULL, NULL, 0, 0, 0 };
- +
- +/* this function makes sure that the disk stays in the drive during the
- + * transfer */
- +static void fd_watchdog(void)
- +{
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("calling disk change from watchdog\n");
- + }
- +#endif
- +
- + if ( disk_change(current_drive) ){
- + DPRINT("disk removed during i/o\n");
- + floppy_shutdown();
- + } else {
- + del_timer(&fd_timer);
- + fd_timer.function = (timeout_fn) fd_watchdog;
- + fd_timer.expires = jiffies + HZ / 10;
- + add_timer(&fd_timer);
- + }
- +}
- +
- +static void main_command_interrupt(void)
- +{
- + del_timer(&fd_timer);
- + cont->interrupt();
- +}
- +
- +/* waits for a delay (spinup or select) to pass */
- +static int wait_for_completion(int delay, timeout_fn function)
- +{
- + if ( FDCS->reset ){
- + reset_fdc(); /* do the reset during sleep to win time
- + * if we don't need to sleep, it's a good
- + * occasion anyways */
- + return 1;
- + }
- +
- + if ( jiffies < delay ){
- + del_timer(&fd_timer);
- + fd_timer.function = function;
- + fd_timer.expires = delay;
- + add_timer(&fd_timer);
- + return 1;
- + }
- + return 0;
- +}
- +
- +static int hlt_disabled=0;
- +static void floppy_disable_hlt(void)
- +{
- + unsigned long flags;
- + save_flags(flags);
- + cli();
- + if(!hlt_disabled){
- + hlt_disabled=1;
- +#ifdef HAVE_DISABLE_HLT
- + disable_hlt();
- +#endif
- + }
- + restore_flags(flags);
- +}
- +
- +static void floppy_enable_hlt(void)
- +{
- + unsigned long flags;
- + save_flags(flags);
- + cli();
- + if(hlt_disabled){
- + hlt_disabled=0;
- +#ifdef HAVE_DISABLE_HLT
- + enable_hlt();
- +#endif
- + }
- + restore_flags(flags);
- +}
- +
- +
- +static void setup_DMA(void)
- +{
- +#ifdef CONFIG_FLOPPY_SANITY
- + if (raw_cmd->length == 0){
- + int i;
- +
- + printk("zero dma transfer size:");
- + for(i=0; i< raw_cmd->cmd_count; i++)
- + printk("%x,", raw_cmd->cmd[i]);
- + printk("\n");
- + cont->done(0);
- + FDCS->reset = 1;
- + return;
- + }
- +#if 0
- + /* disabled because of new buffer location for raw cmd */
- + if ((!CURRENT ||
- + CURRENT->buffer != raw_cmd->kernel_data ||
- + raw_cmd->length > 512 * CURRENT->nr_sectors) &&
- + (raw_cmd->kernel_data < floppy_track_buffer ||
- + raw_cmd->kernel_data + raw_cmd->length >
- + floppy_track_buffer + 1024 * max_buffer_sectors)){
- + printk("bad address. start=%p lg=%lx tb=%p\n",
- + raw_cmd->kernel_data, raw_cmd->length,
- + floppy_track_buffer);
- + if ( CURRENT ){
- + printk("buffer=%p nr=%lx cnr=%lx\n",
- + CURRENT->buffer, CURRENT->nr_sectors,
- + CURRENT->current_nr_sectors);
- + }
- + cont->done(0);
- + FDCS->reset=1;
- + return;
- + }
- + if ((long) raw_cmd->kernel_data % 512 ){
- + printk("non aligned address: %p\n", raw_cmd->kernel_data );
- + cont->done(0);
- + FDCS->reset=1;
- + return;
- + }
- + if (CROSS_64KB(raw_cmd->kernel_data, raw_cmd->length)) {
- + printk("DMA crossing 64-K boundary %p-%p\n",
- + raw_cmd->kernel_data,
- + raw_cmd->kernel_data + raw_cmd->length);
- + cont->done(0);
- + FDCS->reset=1;
- + return;
- + }
- +#endif
- +#endif
- + cli();
- + disable_dma(FLOPPY_DMA);
- + clear_dma_ff(FLOPPY_DMA);
- + set_dma_mode(FLOPPY_DMA,
- + (raw_cmd->flags & FD_RAW_READ)?
- + DMA_MODE_READ : DMA_MODE_WRITE);
- + set_dma_addr(FLOPPY_DMA, virt_to_bus(raw_cmd->kernel_data));
- + set_dma_count(FLOPPY_DMA, raw_cmd->length);
- + enable_dma(FLOPPY_DMA);
- + sti();
- + floppy_disable_hlt();
- +}
- +
- +/* sends a command byte to the fdc */
- +static int output_byte(char byte)
- +{
- + int counter;
- + unsigned char status=0;
- + unsigned char rstatus;
- +
- + if (FDCS->reset)
- + return -1;
- + for(counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
- + rstatus = inb_p(FD_STATUS);
- + status = rstatus &(STATUS_READY|STATUS_DIR|STATUS_DMA);
- + if (!(status & STATUS_READY))
- + continue;
- + if (status == STATUS_READY){
- + outb_p(byte,FD_DATA);
- +
- +#ifdef CONFIG_FLOPPY_SANITY
- + output_log[output_log_pos].data = byte;
- + output_log[output_log_pos].status = rstatus;
- + output_log[output_log_pos].jiffies = jiffies;
- + output_log_pos = (output_log_pos + 1) % OLOGSIZE;
- +#endif
- + return 0;
- + } else
- + break;
- + }
- + FDCS->reset = 1;
- + if ( !initialising )
- + DPRINT2("Unable to send byte %x to FDC. Status=%x\n",
- + byte, status);
- + return -1;
- +}
- +#define LAST_OUT(x) if(output_byte(x)){ reset_fdc();return;}
- +
- +/* gets the response from the fdc */
- +static int result(void)
- +{
- + int i = 0, counter, status = 0;
- +
- + if (FDCS->reset)
- + return -1;
- + for (counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
- + status = inb_p(FD_STATUS)&
- + (STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA);
- + if (!(status & STATUS_READY))
- + continue;
- + if (status == STATUS_READY){
- +#ifdef CONFIG_FLOPPY_SANITY
- + resultjiffies = jiffies;
- + resultsize = i;
- +#endif
- + return i;
- + }
- + if (status & STATUS_DMA )
- + break;
- + if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
- + if (i >= MAX_REPLIES) {
- + DPRINT("floppy_stat reply overrun\n");
- + break;
- + }
- + reply_buffer[i++] = inb_p(FD_DATA);
- + }
- + }
- + FDCS->reset = 1;
- + if ( !initialising )
- + DPRINT3("Getstatus times out (%x) on fdc %d [%d]\n",
- + status, fdc, i);
- + return -1;
- +}
- +
- +/* Set perpendicular mode as required, based on data rate, if supported.
- + * 82077 Now tested. 1Mbps data rate only possible with 82077-1.
- + */
- +static inline void perpendicular_mode(void)
- +{
- + unsigned char perp_mode;
- +
- + if (!floppy)
- + return;
- + if (floppy->rate & 0x40){
- + switch(raw_cmd->rate){
- + case 0:
- + perp_mode=2;
- + break;
- + case 3:
- + perp_mode=3;
- + break;
- + default:
- + DPRINT("Invalid data rate for perpendicular mode!\n");
- + cont->done(0);
- + FDCS->reset = 1; /* convenient way to return to
- + * redo without to much hassle (deep
- + * stack et al. */
- + return;
- + }
- + } else
- + perp_mode = 0;
- +
- + if ( FDCS->perp_mode == perp_mode )
- + return;
- + if (FDCS->version >= FDC_82077_ORIG && FDCS->has_fifo) {
- + output_byte(FD_PERPENDICULAR);
- + output_byte(perp_mode);
- + FDCS->perp_mode = perp_mode;
- + } else if (perp_mode) {
- + DPRINT("perpendicular mode not supported by this FDC.\n");
- + }
- +} /* perpendicular_mode */
- +
- +#define NOMINAL_DTR 500
- +
- +/* Issue a "SPECIFY" command to set the step rate time, head unload time,
- + * head load time, and DMA disable flag to values needed by floppy.
- + *
- + * The value "dtr" is the data transfer rate in Kbps. It is needed
- + * to account for the data rate-based scaling done by the 82072 and 82077
- + * FDC types. This parameter is ignored for other types of FDCs (i.e.
- + * 8272a).
- + *
- + * Note that changing the data transfer rate has a (probably deleterious)
- + * effect on the parameters subject to scaling for 82072/82077 FDCs, so
- + * fdc_specify is called again after each data transfer rate
- + * change.
- + *
- + * srt: 1000 to 16000 in microseconds
- + * hut: 16 to 240 milliseconds
- + * hlt: 2 to 254 milliseconds
- + *
- + * These values are rounded up to the next highest available delay time.
- + */
- +static void fdc_specify(void)
- +{
- + unsigned char spec1, spec2;
- + int srt, hlt, hut;
- + unsigned long dtr = NOMINAL_DTR;
- + unsigned long scale_dtr = NOMINAL_DTR;
- + int hlt_max_code = 0x7f;
- + int hut_max_code = 0xf;
- +
- + if (FDCS->need_configure && FDCS->has_fifo) {
- + if ( FDCS->reset )
- + return;
- + /* Turn on FIFO for 82077-class FDC (improves performance) */
- + /* TODO: lock this in via LOCK during initialization */
- + output_byte(FD_CONFIGURE);
- + output_byte(0);
- + output_byte(0x2A); /* FIFO on, polling off, 10 byte threshold */
- + output_byte(0); /* precompensation from track 0 upwards */
- + if ( FDCS->reset ){
- + FDCS->has_fifo=0;
- + return;
- + }
- + FDCS->need_configure = 0;
- + /*DPRINT("FIFO enabled\n");*/
- + }
- +
- + switch (raw_cmd->rate & 0x03) {
- + case 3:
- + dtr = 1000;
- + break;
- + case 1:
- + dtr = 300;
- + break;
- + case 2:
- + dtr = 250;
- + break;
- + }
- +
- + if (FDCS->version >= FDC_82072) {
- + scale_dtr = dtr;
- + hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */
- + hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */
- + }
- +
- + /* Convert step rate from microseconds to milliseconds and 4 bits */
- + srt = 16 - (DP->srt*scale_dtr/1000 + NOMINAL_DTR - 1)/NOMINAL_DTR;
- + if (srt > 0xf)
- + srt = 0xf;
- + else if (srt < 0)
- + srt = 0;
- +
- + hlt = (DP->hlt*scale_dtr/2 + NOMINAL_DTR - 1)/NOMINAL_DTR;
- + if (hlt < 0x01)
- + hlt = 0x01;
- + else if (hlt > 0x7f)
- + hlt = hlt_max_code;
- +
- + hut = (DP->hut*scale_dtr/16 + NOMINAL_DTR - 1)/NOMINAL_DTR;
- + if (hut < 0x1)
- + hut = 0x1;
- + else if (hut > 0xf)
- + hut = hut_max_code;
- +
- + spec1 = (srt << 4) | hut;
- + spec2 = (hlt << 1);
- +
- + /* If these parameters did not change, just return with success */
- + if (FDCS->spec1 != spec1 || FDCS->spec2 != spec2) {
- + /* Go ahead and set spec1 and spec2 */
- + output_byte(FD_SPECIFY);
- + output_byte(FDCS->spec1 = spec1);
- + output_byte(FDCS->spec2 = spec2);
- + }
- +} /* fdc_specify */
- +
- +/* Set the FDC's data transfer rate on behalf of the specified drive.
- + * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
- + * of the specify command (i.e. using the fdc_specify function).
- + */
- +static int fdc_dtr(void)
- +{
- + /* If data rate not already set to desired value, set it. */
- + if ( raw_cmd->rate == FDCS->dtr)
- + return 0;
- +
- + /* Set dtr */
- + outb_p(raw_cmd->rate, FD_DCR);
- +
- + /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
- + * need a stabilization period of several milliseconds to be
- + * enforced after data rate changes before R/W operations.
- + * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
- + */
- + FDCS->dtr = raw_cmd->rate;
- + return(wait_for_completion(jiffies+2*HZ/100,
- + (timeout_fn) floppy_ready));
- +} /* fdc_dtr */
- +
- +static void tell_sector(void)
- +{
- + printk(": track %d, head %d, sector %d, size %d",
- + R_TRACK, R_HEAD, R_SECTOR, R_SIZECODE);
- +} /* tell_sector */
- +
- +
- +/*
- + * Ok, this error interpreting routine is called after a
- + * DMA read/write has succeeded
- + * or failed, so we check the results, and copy any buffers.
- + * hhb: Added better error reporting.
- + * ak: Made this into a separate routine.
- + */
- +static int interpret_errors(void)
- +{
- + char bad;
- +int res = get_dma_residue(FLOPPY_DMA);
- +if(res) {printk("\n-- DMA residue (%d)",res); tell_sector(); printk("\n");}
- + if (inr!=7) {
- + DPRINT("-- FDC reply error");
- + FDCS->reset = 1;
- + return 1;
- + }
- +
- + /* check IC to find cause of interrupt */
- + switch (ST0 & ST0_INTR) {
- + case 0x40: /* error occurred during command execution */
- + bad = 1;
- + if (ST1 & ST1_WP) {
- + DPRINT("Drive is write protected\n");
- + CLEARF(FD_DISK_WRITABLE);
- + cont->done(0);
- + bad = 2;
- + } else if (ST1 & ST1_ND) {
- + SETF(FD_NEED_TWADDLE);
- + } else if (ST1 & ST1_OR) {
- + if (DP->flags & FTD_MSG )
- + DPRINT("Over/Underrun - retrying\n");
- + bad = 0;
- + }else if(*errors >= DP->max_errors.reporting){
- + DPRINT("");
- + if (ST0 & ST0_ECE) {
- + printk("Recalibrate failed!");
- + } else if (ST2 & ST2_CRC) {
- + printk("data CRC error");
- + tell_sector();
- + } else if (ST1 & ST1_CRC) {
- + printk("CRC error");
- + tell_sector();
- + } else if ((ST1 & (ST1_MAM|ST1_ND)) || (ST2 & ST2_MAM)) {
- + if (!probing) {
- + printk("sector not found");
- + tell_sector();
- + } else
- + printk("probe failed...");
- + } else if (ST2 & ST2_WC) { /* seek error */
- + printk("wrong cylinder");
- + } else if (ST2 & ST2_BC) { /* cylinder marked as bad */
- + printk("bad cylinder");
- + } else {
- + printk("unknown error. ST[0..2] are: 0x%x 0x%x 0x%x", ST0, ST1, ST2);
- + tell_sector();
- + }
- + printk("\n");
- +
- + }
- + if ( ST2 & ST2_WC || ST2 & ST2_BC)
- + /* wrong cylinder => recal */
- + DRS->track = NEED_2_RECAL;
- + return bad;
- + case 0x80: /* invalid command given */
- + DPRINT("Invalid FDC command given!\n");
- + cont->done(0);
- + return 2;
- + case 0xc0:
- + DPRINT("Abnormal termination caused by polling\n");
- + cont->error();
- + return 2;
- + default: /* (0) Normal command termination */
- + return 0;
- + }
- +}
- +
- +/*
- + * This routine is called when everything should be correctly set up
- + * for the transfer (ie floppy motor is on, the correct floppy is
- + * selected, and the head is sitting on the right track).
- + */
- +static void setup_rw_floppy(void)
- +{
- + int i,ready_date,r, flags,dflags;
- + timeout_fn function;
- +
- + flags = raw_cmd->flags;
- + if ( flags & ( FD_RAW_READ | FD_RAW_WRITE))
- + flags |= FD_RAW_INTR;
- +
- + if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)){
- + ready_date = DRS->spinup_date + DP->spinup;
- + /* If spinup will take a long time, rerun scandrives
- + * again just before spinup completion. Beware that
- + * after scandrives, we must again wait for selection.
- + */
- + if ( ready_date > jiffies + DP->select_delay){
- + ready_date -= DP->select_delay;
- + function = (timeout_fn) floppy_start;
- + } else
- + function = (timeout_fn) setup_rw_floppy;
- +
- + /* wait until the floppy is spinning fast enough */
- + if (wait_for_completion(ready_date,function))
- + return;
- + }
- + dflags = DRS->flags;
- +
- + if ( (flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))
- + setup_DMA();
- +
- + if ( flags & FD_RAW_INTR )
- + SET_INTR(main_command_interrupt);
- +
- + r=0;
- + for(i=0; i< raw_cmd->cmd_count; i++)
- + r|=output_byte( raw_cmd->cmd[i] );
- +
- +#ifdef DEBUGT
- + debugt("rw_command: ");
- +#endif
- + if ( r ){
- + reset_fdc();
- + return;
- + }
- +
- + if ( ! ( flags & FD_RAW_INTR )){
- + inr = result();
- + cont->interrupt();
- + } else if ( flags & FD_RAW_NEED_DISK )
- + fd_watchdog();
- +}
- +
- +static int blind_seek;
- +
- +/*
- + * This is the routine called after every seek (or recalibrate) interrupt
- + * from the floppy controller.
- + */
- +static void seek_interrupt(void)
- +{
- +#ifdef DEBUGT
- + debugt("seek interrupt:");
- +#endif
- + if (inr != 2 || (ST0 & 0xF8) != 0x20 ) {
- + DPRINT("seek failed\n");
- + DRS->track = NEED_2_RECAL;
- + cont->error();
- + cont->redo();
- + return;
- + }
- + if (DRS->track >= 0 && DRS->track != ST1 && !blind_seek){
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("clearing NEWCHANGE flag because of effective seek\n");
- + DPRINT1("jiffies=%ld\n", jiffies);
- + }
- +#endif
- + CLEARF(FD_DISK_NEWCHANGE); /* effective seek */
- + DRS->select_date = jiffies;
- + }
- + DRS->track = ST1;
- + floppy_ready();
- +}
- +
- +static void check_wp(void)
- +{
- + if (TESTF(FD_VERIFY)) {
- + /* check write protection */
- + output_byte( FD_GETSTATUS );
- + output_byte( UNIT(current_drive) );
- + if ( result() != 1 ){
- + FDCS->reset = 1;
- + return;
- + }
- + CLEARF(FD_VERIFY);
- + CLEARF(FD_NEED_TWADDLE);
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("checking whether disk is write protected\n");
- + DPRINT1("wp=%x\n",ST3 & 0x40);
- + }
- +#endif
- + if (!( ST3 & 0x40))
- + SETF(FD_DISK_WRITABLE);
- + else
- + CLEARF(FD_DISK_WRITABLE);
- + }
- +}
- +
- +static void seek_floppy(void)
- +{
- + int track;
- +
- + blind_seek=0;
- +
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("calling disk change from seek\n");
- + }
- +#endif
- +
- + if (!TESTF(FD_DISK_NEWCHANGE) &&
- + disk_change(current_drive) &&
- + (raw_cmd->flags & FD_RAW_NEED_DISK)){
- + /* the media changed flag should be cleared after the seek.
- + * If it isn't, this means that there is really no disk in
- + * the drive.
- + */
- + SETF(FD_DISK_CHANGED);
- + cont->done(0);
- + cont->redo();
- + return;
- + }
- + if ( DRS->track <= NEED_1_RECAL ){
- + recalibrate_floppy();
- + return;
- + } else if (TESTF(FD_DISK_NEWCHANGE) &&
- + (raw_cmd->flags & FD_RAW_NEED_DISK) &&
- + (DRS->track <= NO_TRACK || DRS->track == raw_cmd->track)) {
- + /* we seek to clear the media-changed condition. Does anybody
- + * know a more elegant way, which works on all drives? */
- + if ( raw_cmd->track )
- + track = raw_cmd->track - 1;
- + else {
- + if(DP->flags & FD_SILENT_DCL_CLEAR){
- + set_dor(fdc, ~ (0x10 << UNIT(current_drive)), 0);
- + blind_seek = 1;
- + raw_cmd->flags |= FD_RAW_NEED_SEEK;
- + }
- + track = 1;
- + }
- + } else {
- + check_wp();
- + if (raw_cmd->track != DRS->track &&
- + (raw_cmd->flags & FD_RAW_NEED_SEEK))
- + track = raw_cmd->track;
- + else {
- + setup_rw_floppy();
- + return;
- + }
- + }
- +
- + SET_INTR(seek_interrupt);
- + output_byte(FD_SEEK);
- + output_byte(UNIT(current_drive));
- + LAST_OUT(track);
- +#ifdef DEBUGT
- + debugt("seek command:");
- +#endif
- +}
- +
- +static void recal_interrupt(void)
- +{
- +#ifdef DEBUGT
- + debugt("recal interrupt:");
- +#endif
- + if (inr !=2 )
- + FDCS->reset = 1;
- + else if (ST0 & ST0_ECE) {
- + switch(DRS->track){
- + case NEED_1_RECAL:
- +#ifdef DEBUGT
- + debugt("recal interrupt need 1 recal:");
- +#endif
- + /* after a second recalibrate, we still haven't
- + * reached track 0. Probably no drive. Raise an
- + * error, as failing immediately might upset
- + * computers possessed by the Devil :-) */
- + cont->error();
- + cont->redo();
- + return;
- + case NEED_2_RECAL:
- +#ifdef DEBUGT
- + debugt("recal interrupt need 2 recal:");
- +#endif
- + /* If we already did a recalibrate,
- + * and we are not at track 0, this
- + * means we have moved. (The only way
- + * not to move at recalibration is to
- + * be already at track 0.) Clear the
- + * new change flag */
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("clearing NEWCHANGE flag because of second recalibrate\n");
- + }
- +#endif
- +
- + CLEARF(FD_DISK_NEWCHANGE);
- + DRS->select_date = jiffies;
- + /* fall through */
- + default:
- +#ifdef DEBUGT
- + debugt("recal interrupt default:");
- +#endif
- + /* Recalibrate moves the head by at
- + * most 80 steps. If after one
- + * recalibrate we don't have reached
- + * track 0, this might mean that we
- + * started beyond track 80. Try
- + * again. */
- + DRS->track = NEED_1_RECAL;
- + break;
- + }
- + } else
- + DRS->track = ST1;
- + floppy_ready();
- +}
- +
- +/*
- + * Unexpected interrupt - Print as much debugging info as we can...
- + * All bets are off...
- + */
- +static void unexpected_floppy_interrupt(void)
- +{
- + int i;
- + if ( initialising )
- + return;
- + if(print_unex){
- + DPRINT("unexpected interrupt\n");
- + if ( inr >= 0 )
- + for(i=0; i<inr; i++)
- + printk("%d %x\n", i, reply_buffer[i] );
- + }
- + while(1){
- + output_byte(FD_SENSEI);
- + inr=result();
- + if ( inr != 2 )
- + break;
- + if(print_unex){
- + printk("sensei\n");
- + for(i=0; i<inr; i++)
- + printk("%d %x\n", i, reply_buffer[i] );
- + }
- + }
- + FDCS->reset = 1;
- +}
- +
- +struct tq_struct floppy_tq =
- +{ 0, 0, (void *) (void *) unexpected_floppy_interrupt, 0 };
- +
- +/* interrupt handler */
- +static void floppy_interrupt(int irq, struct pt_regs *regs)
- +{
- + void (*handler)(void) = DEVICE_INTR;
- +
- + lasthandler = handler;
- + interruptjiffies = jiffies;
- +
- + floppy_enable_hlt();
- + CLEAR_INTR;
- + if ( fdc >= N_FDC || FDCS->address == -1){
- + /* we don't even know which FDC is the culprit */
- + printk("DOR0=%x\n", fdc_state[0].dor);
- + printk("floppy interrupt on bizarre fdc %d\n",fdc);
- + printk("handler=%p\n", handler);
- + is_alive("bizarre fdc");
- + return;
- + }
- + inr = result();
- + if (!handler){
- + unexpected_floppy_interrupt();
- + is_alive("unexpected");
- + return;
- + }
- + if ( inr == 0 ){
- + do {
- + output_byte(FD_SENSEI);
- + inr = result();
- + } while ( (ST0 & 0x83) != UNIT(current_drive) && inr == 2);
- + }
- + floppy_tq.routine = (void *)(void *) handler;
- + queue_task_irq(&floppy_tq, &tq_timer);
- + is_alive("normal interrupt end");
- +}
- +
- +static void recalibrate_floppy(void)
- +{
- +#ifdef DEBUGT
- + debugt("recalibrate floppy:");
- +#endif
- + SET_INTR(recal_interrupt);
- + output_byte(FD_RECALIBRATE);
- + LAST_OUT(UNIT(current_drive));
- +}
- +
- +/*
- + * Must do 4 FD_SENSEIs after reset because of ``drive polling''.
- + */
- +static void reset_interrupt(void)
- +{
- +#ifdef DEBUGT
- + debugt("reset interrupt:");
- +#endif
- + /* fdc_specify(); reprogram fdc */
- + result(); /* get the status ready for set_fdc */
- + if ( FDCS->reset )
- + cont->error(); /* a reset just after a reset. BAD! */
- + cont->redo();
- +}
- +
- +/*
- + * reset is done by pulling bit 2 of DOR low for a while (old FDC's),
- + * or by setting the self clearing bit 7 of STATUS (newer FDC's)
- + */
- +static void reset_fdc(void)
- +{
- + SET_INTR(reset_interrupt);
- + FDCS->reset = 0;
- + reset_fdc_info(0);
- + if ( FDCS->version >= FDC_82077 )
- + outb_p(0x80 | ( FDCS->dtr &3), FD_STATUS);
- + else {
- + arm_set_dor(FDCS->dor & ~0x04);
- + udelay(FD_RESET_DELAY);
- + arm_set_dor(FDCS->dor);
- + }
- +}
- +
- +static void empty(void)
- +{
- +}
- +
- +void show_floppy(void)
- +{
- + int i;
- +
- + printk("\n");
- + printk("floppy driver state\n");
- + printk("-------------------\n");
- + printk("now=%ld last interrupt=%d last called handler=%p\n",
- + jiffies, interruptjiffies, lasthandler);
- +
- +
- +#ifdef CONFIG_FLOPPY_SANITY
- + printk("timeout_message=%s\n", timeout_message);
- + printk("last output bytes:\n");
- + for(i=0; i < OLOGSIZE; i++)
- + printk("%2x %2x %ld\n",
- + output_log[(i+output_log_pos) % OLOGSIZE].data,
- + output_log[(i+output_log_pos) % OLOGSIZE].status,
- + output_log[(i+output_log_pos) % OLOGSIZE].jiffies);
- + printk("last result at %d\n", resultjiffies);
- + printk("last redo_fd_request at %d\n", lastredo);
- + for(i=0; i<resultsize; i++){
- + printk("%2x ", reply_buffer[i]);
- + }
- + printk("\n");
- +#endif
- +
- +#if 0
- + for(i=0; i<N_FDC; i++){
- + if(FDCS->address != -1){
- + printk("dor %d = %x\n", i, fdc_state[i].dor );
- + outb_p(fdc_state[i].address+2, fdc_state[i].dor);
- + udelay(1000); /* maybe we'll catch an interrupt... */
- + }
- + }
- +#endif
- + printk("status=%x\n", inb_p(FD_STATUS));
- + printk("fdc_busy=%d\n", fdc_busy);
- + if( DEVICE_INTR)
- + printk("DEVICE_INTR=%p\n", DEVICE_INTR);
- + if(floppy_tq.sync)
- + printk("floppy_tq.routine=%p\n", floppy_tq.routine);
- + if(fd_timer.prev)
- + printk("fd_timer.function=%p\n", fd_timer.function);
- + if(fd_timeout.prev){
- + printk("timer_table=%p\n",fd_timeout.function);
- + printk("expires=%ld\n",fd_timeout.expires-jiffies);
- + printk("now=%ld\n",jiffies);
- + }
- + printk("cont=%p\n", cont);
- + printk("CURRENT=%p\n", CURRENT);
- + printk("command_status=%d\n", command_status);
- + printk("\n");
- +}
- +
- +static void floppy_shutdown(void)
- +{
- + if (!initialising)
- + show_floppy();
- + CLEAR_INTR;
- + floppy_tq.routine = (void *)(void *) empty;
- + del_timer( &fd_timer);
- + sti();
- +
- + floppy_enable_hlt();
- + disable_dma(FLOPPY_DMA);
- + /* avoid dma going to a random drive after shutdown */
- +
- + if(!initialising)
- + DPRINT("floppy timeout\n");
- + FDCS->reset = 1;
- + if (cont){
- + cont->done(0);
- + cont->redo(); /* this will recall reset when needed */
- + } else {
- + printk("no cont in shutdown!\n");
- + process_fd_request();
- + }
- + is_alive("floppy shutdown");
- +}
- +/*typedef void (*timeout_fn)(unsigned long);*/
- +
- +/* start motor, check media-changed condition and write protection */
- +static int start_motor( void (*function)(void) )
- +{
- + int mask, data;
- +
- + mask = 0xfc;
- + data = UNIT(current_drive);
- + if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)){
- + if(!(FDCS->dor & ( 0x10 << UNIT(current_drive) ) )){
- + set_debugt();
- + /* no read since this drive is running */
- + DRS->first_read_date = 0;
- + /* note motor start time if motor is not yet running */
- + DRS->spinup_date = jiffies;
- + data |= (0x10 << UNIT(current_drive));
- + }
- + } else
- + if (FDCS->dor & ( 0x10 << UNIT(current_drive) ) )
- + mask &= ~(0x10 << UNIT(current_drive));
- +
- + /* starts motor and selects floppy */
- + del_timer(motor_off_timer + current_drive);
- + set_dor( fdc, mask, data);
- +
- + /* wait_for_completion also schedules reset if needed. */
- + return(wait_for_completion(DRS->select_date+DP->select_delay,
- + (timeout_fn) function));
- +}
- +
- +static void floppy_ready(void)
- +{
- + CHECK_RESET;
- + if(start_motor(floppy_ready)) return;
- + if(fdc_dtr()) return;
- +
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("calling disk change from floppy_ready\n");
- + }
- +#endif
- +
- + if(!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
- + disk_change(current_drive) &&
- + !DP->select_delay)
- + twaddle(); /* this clears the dcl on certain drive/controller
- + * combinations */
- +
- + if ( raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){
- + perpendicular_mode();
- + fdc_specify(); /* must be done here because of hut, hlt ... */
- + seek_floppy();
- + } else
- + setup_rw_floppy();
- +}
- +
- +static void floppy_start(void)
- +{
- + reschedule_timeout(CURRENTD, "floppy start", 0);
- +
- + scandrives();
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("setting NEWCHANGE in floppy_start\n");
- + }
- +#endif
- + SETF(FD_DISK_NEWCHANGE);
- + floppy_ready();
- +}
- +
- +/*
- + * ========================================================================
- + * here ends the bottom half. Exported routines are:
- + * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
- + * start_motor, reset_fdc, reset_fdc_info, interpret_errors.
- + * Initialisation also uses output_byte, result, set_dor, floppy_interrupt
- + * and set_dor.
- + * ========================================================================
- + */
- +/*
- + * General purpose continuations.
- + * ==============================
- + */
- +
- +static void do_wakeup(void)
- +{
- + reschedule_timeout(MAXTIMEOUT, "do wakeup", 0);
- + cont = 0;
- + command_status += 2;
- + wake_up(&command_done);
- +}
- +
- +static struct cont_t wakeup_cont={
- + empty,
- + do_wakeup,
- + empty,
- + (done_f)empty
- +};
- +
- +static int wait_til_done( void (*handler)(void ), int interruptible )
- +{
- + int ret;
- +
- + floppy_tq.routine = (void *)(void *) handler;
- + queue_task(&floppy_tq, &tq_timer);
- +
- + cli();
- + while(command_status < 2 && NO_SIGNAL){
- + is_alive("wait_til_done");
- + if (interruptible)
- + interruptible_sleep_on(&command_done);
- + else
- + sleep_on(&command_done);
- + }
- + if(command_status < 2){
- + floppy_shutdown();
- + sti();
- + process_fd_request();
- + return -EINTR;
- + }
- + sti();
- +
- + if ( FDCS->reset )
- + command_status = FD_COMMAND_ERROR;
- + if ( command_status == FD_COMMAND_OKAY )
- + ret=0;
- + else
- + ret=-EIO;
- + command_status = FD_COMMAND_NONE;
- + return ret;
- +}
- +
- +static void generic_done(int result)
- +{
- + command_status = result;
- + cont = &wakeup_cont;
- +}
- +
- +static void generic_success(void)
- +{
- + cont->done(1);
- +}
- +
- +static void generic_failure(void)
- +{
- + cont->done(0);
- +}
- +
- +static void success_and_wakeup(void)
- +{
- + generic_success();
- + cont->redo();
- +}
- +
- +
- +/*
- + * formatting and rw support.
- + * ==========================
- + */
- +
- +static int next_valid_format(void)
- +{
- + int probed_format;
- +
- + probed_format = DRS->probed_format;
- + while(1){
- + if ( probed_format >= 8 ||
- + ! DP->autodetect[probed_format] ){
- + DRS->probed_format = 0;
- + return 1;
- + }
- + if ( floppy_type[DP->autodetect[probed_format]].sect ){
- + DRS->probed_format = probed_format;
- + return 0;
- + }
- + probed_format++;
- + }
- +}
- +
- +static void bad_flp_intr(void)
- +{
- + if ( probing ){
- + DRS->probed_format++;
- + if ( !next_valid_format())
- + return;
- + }
- + (*errors)++;
- + if (*errors > DRWE->badness)
- + DRWE->badness = *errors;
- + if (*errors > DP->max_errors.abort)
- + cont->done(0);
- + if (*errors > DP->max_errors.reset)
- + FDCS->reset = 1;
- + else if (*errors > DP->max_errors.recal)
- + DRS->track = NEED_2_RECAL;
- +}
- +
- +static void set_floppy(kdev_t device)
- +{
- + if (TYPE(device))
- + floppy = TYPE(device) + floppy_type;
- + else
- + floppy = current_type[ DRIVE(device) ];
- +}
- +
- +/*
- + * formatting and support.
- + * =======================
- + */
- +static void format_interrupt(void)
- +{
- + switch (interpret_errors()){
- + case 1:
- + cont->error();
- + case 2:
- + break;
- + case 0:
- + cont->done(1);
- + }
- + cont->redo();
- +}
- +
- +#define CODE2SIZE (ssize = ( ( 1 << SIZECODE ) + 3 ) >> 2)
- +#define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80 ) >>1))
- +#define CT(x) ( (x) | 0x40 )
- +static void setup_format_params(int track)
- +{
- + struct fparm {
- + unsigned char track,head,sect,size;
- + } *here = (struct fparm *)floppy_track_buffer;
- + int il,n;
- + int count,head_shift,track_shift;
- +
- + raw_cmd = &default_raw_cmd;
- + raw_cmd->track = track;
- +
- + raw_cmd->flags = FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN |
- + /*FD_RAW_NEED_DISK |*/ FD_RAW_NEED_SEEK;
- + raw_cmd->rate = floppy->rate & 0x3;
- + raw_cmd->cmd_count = NR_F;
- + COMMAND = FM_MODE(floppy,FD_FORMAT);
- + DR_SELECT = UNIT(current_drive) + PH_HEAD(floppy,format_req.head);
- + F_SIZECODE = FD_SIZECODE(floppy);
- + F_SECT_PER_TRACK = floppy->sect << 2 >> F_SIZECODE;
- + F_GAP = floppy->fmt_gap;
- + F_FILL = FD_FILL_BYTE;
- +
- + raw_cmd->kernel_data = floppy_track_buffer;
- + raw_cmd->length = 4 * F_SECT_PER_TRACK;
- +
- + /* allow for about 30ms for data transport per track */
- + head_shift = (F_SECT_PER_TRACK + 5) / 6;
- +
- + /* a ``cylinder'' is two tracks plus a little stepping time */
- + track_shift = 2 * head_shift + 3;
- +
- + /* position of logical sector 1 on this track */
- + n = (track_shift * format_req.track + head_shift * format_req.head )
- + % F_SECT_PER_TRACK;
- +
- + /* determine interleave */
- + il = 1;
- + if (floppy->sect > DP->interleave_sect && F_SIZECODE == 2)
- + il++;
- +
- + /* initialize field */
- + for (count = 0; count < F_SECT_PER_TRACK; ++count) {
- + here[count].track = format_req.track;
- + here[count].head = format_req.head;
- + here[count].sect = 0;
- + here[count].size = F_SIZECODE;
- + }
- + /* place logical sectors */
- + for (count = 1; count <= F_SECT_PER_TRACK; ++count) {
- + here[n].sect = count;
- + n = (n+il) % F_SECT_PER_TRACK;
- + if (here[n].sect) { /* sector busy, find next free sector */
- + ++n;
- + if (n>= F_SECT_PER_TRACK) {
- + n-=F_SECT_PER_TRACK;
- + while (here[n].sect) ++n;
- + }
- + }
- + }
- +}
- +
- +static void redo_format(void)
- +{
- + buffer_track = -1;
- + setup_format_params(format_req.track << STRETCH(floppy));
- + floppy_start();
- +#ifdef DEBUGT
- + debugt("queue format request");
- +#endif
- +}
- +
- +static struct cont_t format_cont={
- + format_interrupt,
- + redo_format,
- + bad_flp_intr,
- + generic_done };
- +
- +static int do_format(kdev_t device, struct format_descr *tmp_format_req)
- +{
- + int ret;
- + int drive=DRIVE(device);
- +
- + LOCK_FDC(drive,1);
- + set_floppy(device);
- + if (!floppy ||
- + floppy->track > DP->tracks ||
- + tmp_format_req->track >= floppy->track ||
- + tmp_format_req->head >= floppy->head ||
- + (floppy->sect << 2) % (1 << FD_SIZECODE(floppy)) ||
- + !floppy->fmt_gap) {
- + process_fd_request();
- + return -EINVAL;
- + }
- + format_req = *tmp_format_req;
- + format_errors = 0;
- + cont = &format_cont;
- + errors = &format_errors;
- + IWAIT(redo_format);
- + process_fd_request();
- + return ret;
- +}
- +
- +/*
- + * Buffer read/write and support
- + * =============================
- + */
- +
- +/* new request_done. Can handle physical sectors which are smaller than a
- + * logical buffer */
- +static void request_done(int uptodate)
- +{
- + int block;
- +
- + probing = 0;
- + reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate);
- +
- + if (!CURRENT){
- + DPRINT("request list destroyed in floppy request done\n");
- + return;
- + }
- + if (uptodate){
- + /* maintain values for invalidation on geometry
- + * change */
- + block = current_count_sectors + CURRENT->sector;
- + if (block > DRS->maxblock)
- + DRS->maxblock=block;
- + if ( block > floppy->sect)
- + DRS->maxtrack = 1;
- +
- + /* unlock chained buffers */
- + while (current_count_sectors && CURRENT &&
- + current_count_sectors >= CURRENT->current_nr_sectors ){
- + current_count_sectors -= CURRENT->current_nr_sectors;
- + CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
- + CURRENT->sector += CURRENT->current_nr_sectors;
- + end_request(1);
- + }
- + if ( current_count_sectors && CURRENT){
- + /* "unlock" last subsector */
- + CURRENT->buffer += current_count_sectors <<9;
- + CURRENT->current_nr_sectors -= current_count_sectors;
- + CURRENT->nr_sectors -= current_count_sectors;
- + CURRENT->sector += current_count_sectors;
- + return;
- + }
- +
- + if ( current_count_sectors && ! CURRENT )
- + DPRINT("request list destroyed in floppy request done\n");
- +
- + } else {
- + if(CURRENT->cmd == WRITE) {
- + /* record write error information */
- + DRWE->write_errors++;
- + if(DRWE->write_errors == 1) {
- + DRWE->first_error_sector = CURRENT->sector;
- + DRWE->first_error_generation = DRS->generation;
- + }
- + DRWE->last_error_sector = CURRENT->sector;
- + DRWE->last_error_generation = DRS->generation;
- + }
- + end_request(0);
- + }
- +}
- +
- +/* Interrupt handler evaluating the result of the r/w operation */
- +static void rw_interrupt(void)
- +{
- + int nr_sectors, ssize;
- +
- + if ( ! DRS->first_read_date )
- + DRS->first_read_date = jiffies;
- +
- + nr_sectors = 0;
- + CODE2SIZE;
- + nr_sectors = ((R_TRACK-TRACK)*floppy->head+R_HEAD-HEAD) *
- + floppy->sect + ((R_SECTOR-SECTOR) << SIZECODE >> 2) -
- + (sector_t % floppy->sect) % ssize;
- +
- +#ifdef CONFIG_FLOPPY_SANITY
- + if ( nr_sectors > current_count_sectors + ssize -
- + (current_count_sectors + sector_t) % ssize +
- + sector_t % ssize){
- + DPRINT2("long rw: %x instead of %lx\n",
- + nr_sectors, current_count_sectors);
- + printk("rs=%d s=%d\n", R_SECTOR, SECTOR);
- + printk("rh=%d h=%d\n", R_HEAD, HEAD);
- + printk("rt=%d t=%d\n", R_TRACK, TRACK);
- + printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK,
- + sector_t, ssize);
- + }
- +#endif
- + if ( nr_sectors < 0 )
- + nr_sectors = 0;
- + if ( nr_sectors < current_count_sectors )
- + current_count_sectors = nr_sectors;
- +
- + switch (interpret_errors()){
- + case 2:
- + cont->redo();
- + return;
- + case 1:
- + if ( !current_count_sectors){
- + cont->error();
- + cont->redo();
- + return;
- + }
- + break;
- + case 0:
- + if ( !current_count_sectors){
- + cont->redo();
- + return;
- + }
- + current_type[current_drive] = floppy;
- + floppy_sizes[TOMINOR(current_drive) ]= floppy->size>>1;
- + break;
- + }
- +
- + if (probing) {
- + if (DP->flags & FTD_MSG)
- + DPRINT2("Auto-detected floppy type %s in fd%d\n",
- + floppy->name,current_drive);
- + current_type[current_drive] = floppy;
- + floppy_sizes[TOMINOR(current_drive)] = floppy->size >> 1;
- + probing = 0;
- + }
- +
- + if ( CT(COMMAND) != FD_READ ||
- + raw_cmd->kernel_data == CURRENT->buffer ){
- + /* transfer directly from buffer */
- + cont->done(1);
- + } else if ( CT(COMMAND) == FD_READ){
- + buffer_track = raw_cmd->track;
- + buffer_drive = current_drive;
- + if ( nr_sectors + sector_t > buffer_max )
- + buffer_max = nr_sectors + sector_t;
- + }
- + cont->redo();
- +}
- +
- +/* Compute maximal contiguous buffer size. */
- +static int buffer_chain_size(void)
- +{
- + struct buffer_head *bh;
- + int size;
- + char *base;
- +
- + base = CURRENT->buffer;
- + size = CURRENT->current_nr_sectors << 9;
- + bh = CURRENT->bh;
- +
- + if(bh){
- + bh = bh->b_reqnext;
- + while ( bh && bh->b_data == base + size ){
- + size += bh->b_size;
- + bh = bh->b_reqnext;
- + }
- + }
- + return size >> 9;
- +}
- +
- +/* Compute the maximal transfer size */
- +static int transfer_size(int ssize, int max_sector, int max_size)
- +{
- + if ( max_sector > sector_t + max_size)
- + max_sector = sector_t + max_size;
- +
- + /* alignment */
- + max_sector -= (max_sector % floppy->sect ) % ssize;
- +
- + /* transfer size, beginning not aligned */
- + current_count_sectors = max_sector - sector_t ;
- +
- + return max_sector;
- +}
- +
- +/*
- + * Move data from/to the track buffer to/from the buffer cache.
- + */
- +static void copy_buffer(int ssize, int max_sector, int max_sector_2)
- +{
- + int remaining; /* number of transferred 512-byte sectors */
- + struct buffer_head *bh;
- + char *buffer, *dma_buffer;
- + int size;
- +
- + if ( max_sector > max_sector_2 )
- + max_sector = max_sector_2;
- +
- + max_sector = transfer_size(ssize, max_sector, CURRENT->nr_sectors);
- +
- + if (current_count_sectors <= 0 && CT(COMMAND) == FD_WRITE &&
- + buffer_max > sector_t + CURRENT->nr_sectors){
- + current_count_sectors = buffer_max - sector_t;
- + if ( current_count_sectors > CURRENT->nr_sectors )
- + current_count_sectors = CURRENT->nr_sectors;
- + }
- + remaining = current_count_sectors << 9;
- +#ifdef CONFIG_FLOPPY_SANITY
- + if ((remaining >> 9) > CURRENT->nr_sectors &&
- + CT(COMMAND) == FD_WRITE ){
- + DPRINT("in copy buffer\n");
- + printk("current_count_sectors=%ld\n", current_count_sectors);
- + printk("remaining=%d\n", remaining >> 9);
- + printk("CURRENT->nr_sectors=%ld\n",CURRENT->nr_sectors);
- + printk("CURRENT->current_nr_sectors=%ld\n",
- + CURRENT->current_nr_sectors);
- + printk("max_sector=%d\n", max_sector);
- + printk("ssize=%d\n", ssize);
- + }
- +#endif
- +
- + if ( max_sector > buffer_max )
- + buffer_max = max_sector;
- +
- + dma_buffer = floppy_track_buffer + ((sector_t - buffer_min) << 9);
- +
- + bh = CURRENT->bh;
- + size = CURRENT->current_nr_sectors << 9;
- + buffer = CURRENT->buffer;
- +
- + while ( remaining > 0){
- + if ( size > remaining )
- + size = remaining;
- +#ifdef CONFIG_FLOPPY_SANITY
- + if (dma_buffer + size >
- + floppy_track_buffer + (max_buffer_sectors << 10) ||
- + dma_buffer < floppy_track_buffer ){
- + DPRINT1("buffer overrun in copy buffer %d\n",
- + (int) ((floppy_track_buffer - dma_buffer) >>9));
- + printk("sector_t=%d buffer_min=%d\n",
- + sector_t, buffer_min);
- + printk("current_count_sectors=%ld\n",
- + current_count_sectors);
- + if ( CT(COMMAND) == FD_READ )
- + printk("read\n");
- + if ( CT(COMMAND) == FD_READ )
- + printk("write\n");
- + break;
- + }
- + if ( ((unsigned long)buffer) % 512 )
- + DPRINT1("%p buffer not aligned\n", buffer);
- +#endif
- + if ( CT(COMMAND) == FD_READ )
- + memcpy( buffer, dma_buffer, size);
- + else
- + memcpy( dma_buffer, buffer, size);
- + remaining -= size;
- + if ( !remaining)
- + break;
- +
- + dma_buffer += size;
- + bh = bh->b_reqnext;
- +#ifdef CONFIG_FLOPPY_SANITY
- + if ( !bh){
- + DPRINT("bh=null in copy buffer after copy\n");
- + break;
- + }
- +#endif
- + size = bh->b_size;
- + buffer = bh->b_data;
- + }
- +#ifdef CONFIG_FLOPPY_SANITY
- + if ( remaining ){
- + if ( remaining > 0 )
- + max_sector -= remaining >> 9;
- + DPRINT1("weirdness: remaining %d\n", remaining>>9);
- + }
- +#endif
- +}
- +
- +/*
- + * Formulate a read/write request.
- + * this routine decides where to load the data (directly to buffer, or to
- + * tmp floppy area), how much data to load (the size of the buffer, the whole
- + * track, or a single sector)
- + * All floppy_track_buffer handling goes in here. If we ever add track buffer
- + * allocation on the fly, it should be done here. No other part should need
- + * modification.
- + */
- +
- +static int make_raw_rw_request(void)
- +{
- + int aligned_sector_t;
- + int max_sector, max_size, tracksize, ssize;
- +
- + set_fdc(DRIVE(CURRENT->rq_dev));
- +
- + raw_cmd = &default_raw_cmd;
- + raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
- + FD_RAW_NEED_SEEK;
- + raw_cmd->cmd_count = NR_RW;
- + if (CURRENT->cmd == READ){
- + raw_cmd->flags |= FD_RAW_READ;
- + COMMAND = FM_MODE(floppy,FD_READ);
- + } else if (CURRENT->cmd == WRITE){
- + raw_cmd->flags |= FD_RAW_WRITE;
- + COMMAND = FM_MODE(floppy,FD_WRITE);
- + } else {
- + DPRINT("make_raw_rw_request: unknown command\n");
- + return 0;
- + }
- +
- + max_sector = floppy->sect * floppy->head;
- +
- + TRACK = CURRENT->sector / max_sector;
- + sector_t = CURRENT->sector % max_sector;
- + if ( floppy->track && TRACK >= floppy->track )
- + return 0;
- + HEAD = sector_t / floppy->sect;
- +
- + if (((floppy->stretch & FD_SWAPSIDES) || TESTF( FD_NEED_TWADDLE)) &&
- + sector_t < floppy->sect )
- + max_sector = floppy->sect;
- +
- + /* 2M disks have phantom sectors on the first track */
- + if ( (floppy->rate & FD_2M ) && (!TRACK) && (!HEAD)){
- + max_sector = 2 * floppy->sect / 3;
- + if (sector_t >= max_sector){
- + current_count_sectors = (floppy->sect - sector_t);
- + if ( current_count_sectors > CURRENT->nr_sectors )
- + current_count_sectors = CURRENT->nr_sectors;
- + return 1;
- + }
- + SIZECODE = 2;
- + } else
- + SIZECODE = FD_SIZECODE(floppy);
- + raw_cmd->rate = floppy->rate & 3;
- + if ((floppy->rate & FD_2M) &&
- + (TRACK || HEAD ) &&
- + raw_cmd->rate == 2)
- + raw_cmd->rate = 1;
- +
- + if ( SIZECODE )
- + SIZECODE2 = 0xff;
- + else
- + SIZECODE2 = 0x80;
- + raw_cmd->track = TRACK << STRETCH(floppy);
- + DR_SELECT = UNIT(current_drive) + PH_HEAD(floppy,HEAD);
- + GAP = floppy->gap;
- + CODE2SIZE;
- + SECT_PER_TRACK = floppy->sect << 2 >> SIZECODE;
- + SECTOR = ((sector_t % floppy->sect) << 2 >> SIZECODE) + 1;
- + tracksize = floppy->sect - floppy->sect % ssize;
- + if ( tracksize < floppy->sect ){
- + SECT_PER_TRACK ++;
- + if ( tracksize <= sector_t % floppy->sect)
- + SECTOR--;
- + while ( tracksize <= sector_t % floppy->sect){
- + while( tracksize + ssize > floppy->sect ){
- + SIZECODE--;
- + ssize >>= 1;
- + }
- + SECTOR++; SECT_PER_TRACK ++;
- + tracksize += ssize;
- + }
- + max_sector = HEAD * floppy->sect + tracksize;
- + } else if ( !TRACK && !HEAD && !( floppy->rate & FD_2M ) && probing)
- + max_sector = floppy->sect;
- +
- + aligned_sector_t = sector_t - ( sector_t % floppy->sect ) % ssize;
- + max_size = CURRENT->nr_sectors;
- + if ((raw_cmd->track == buffer_track) && (current_drive == buffer_drive) &&
- + (sector_t >= buffer_min) && (sector_t < buffer_max)) {
- + /* data already in track buffer */
- + if (CT(COMMAND) == FD_READ) {
- + copy_buffer(1, max_sector, buffer_max);
- + return 1;
- + }
- + } else if (aligned_sector_t != sector_t || CURRENT->nr_sectors < ssize){
- + if (CT(COMMAND) == FD_WRITE){
- + if(sector_t + CURRENT->nr_sectors > ssize &&
- + sector_t + CURRENT->nr_sectors < ssize + ssize)
- + max_size = ssize + ssize;
- + else
- + max_size = ssize;
- + }
- + raw_cmd->flags &= ~FD_RAW_WRITE;
- + raw_cmd->flags |= FD_RAW_READ;
- + COMMAND = FM_MODE(floppy,FD_READ);
- + } else if ((unsigned long)CURRENT->buffer < MAX_DMA_ADDRESS ) {
- + int direct, indirect;
- +
- + indirect= transfer_size(ssize,max_sector,max_buffer_sectors*2) -
- + sector_t;
- +
- + max_size = buffer_chain_size();
- +#if 0
- + if ( max_size > ( MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer))>>9)
- + max_size=(MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer))>>9;
- + /* 64 kb boundaries */
- + if (CROSS_64KB(CURRENT->buffer, max_size << 9))
- + max_size = ( K_64 - ((long) CURRENT->buffer) % K_64)>>9;
- +#endif
- + direct = transfer_size(ssize,max_sector,max_size) - sector_t;
- + /*
- + * We try to read tracks, but if we get too many errors, we
- + * go back to reading just one sector at a time.
- + *
- + * This means we should be able to read a sector even if there
- + * are other bad sectors on this track.
- + */
- + if (!direct ||
- + (indirect * 2 > direct * 3 &&
- + *errors < DP->max_errors.read_track &&
- + /*!TESTF( FD_NEED_TWADDLE) &&*/
- + ((!probing || (DP->read_track&(1<<DRS->probed_format)))))){
- + max_size = CURRENT->nr_sectors;
- + } else {
- + raw_cmd->kernel_data = CURRENT->buffer;
- + raw_cmd->length = current_count_sectors << 9;
- + if (raw_cmd->length == 0){
- + DPRINT("zero dma transfer attempted from make_raw_request\n");
- + DPRINT3("indirect=%d direct=%d sector_t=%d",
- + indirect, direct, sector_t);
- + return 0;
- + }
- + return 2;
- + }
- + }
- +
- + if ( CT(COMMAND) == FD_READ )
- + max_size = max_sector; /* unbounded */
- +
- + /* claim buffer track if needed */
- + if (buffer_track != raw_cmd->track || /* bad track */
- + buffer_drive !=current_drive || /* bad drive */
- + sector_t > buffer_max ||
- + sector_t < buffer_min ||
- + ((CT(COMMAND) == FD_READ ||
- + (aligned_sector_t == sector_t && CURRENT->nr_sectors >= ssize ))&&
- + max_sector > 2 * max_buffer_sectors + buffer_min &&
- + max_size + sector_t > 2 * max_buffer_sectors + buffer_min)
- + /* not enough space */ ){
- + buffer_track = -1;
- + buffer_drive = current_drive;
- + buffer_max = buffer_min = aligned_sector_t;
- + }
- + raw_cmd->kernel_data = floppy_track_buffer +
- + ((aligned_sector_t-buffer_min )<<9);
- +
- + if ( CT(COMMAND) == FD_WRITE ){
- + /* copy write buffer to track buffer.
- + * if we get here, we know that the write
- + * is either aligned or the data already in the buffer
- + * (buffer will be overwritten) */
- +#ifdef CONFIG_FLOPPY_SANITY
- + if (sector_t != aligned_sector_t && buffer_track == -1 )
- + DPRINT("internal error offset !=0 on write\n");
- +#endif
- + buffer_track = raw_cmd->track;
- + buffer_drive = current_drive;
- + copy_buffer(ssize, max_sector, 2*max_buffer_sectors+buffer_min);
- + } else
- + transfer_size(ssize, max_sector,
- + 2*max_buffer_sectors+buffer_min-aligned_sector_t);
- +
- + /* round up current_count_sectors to get dma xfer size */
- + raw_cmd->length = sector_t+current_count_sectors-aligned_sector_t;
- + raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1;
- + raw_cmd->length <<= 9;
- +#ifdef CONFIG_FLOPPY_SANITY
- + if ((raw_cmd->length < current_count_sectors << 9) ||
- + (raw_cmd->kernel_data != CURRENT->buffer &&
- + CT(COMMAND) == FD_WRITE &&
- + (aligned_sector_t + (raw_cmd->length >> 9) > buffer_max ||
- + aligned_sector_t < buffer_min )) ||
- + raw_cmd->length % ( 128 << SIZECODE ) ||
- + raw_cmd->length <= 0 || current_count_sectors <= 0){
- + DPRINT2("fractionary current count b=%lx s=%lx\n",
- + raw_cmd->length, current_count_sectors);
- + if ( raw_cmd->kernel_data != CURRENT->buffer )
- + printk("addr=%d, length=%ld\n",
- + (int) ((raw_cmd->kernel_data -
- + floppy_track_buffer ) >> 9),
- + current_count_sectors);
- + printk("st=%d ast=%d mse=%d msi=%d\n",
- + sector_t, aligned_sector_t, max_sector, max_size);
- + printk("ssize=%x SIZECODE=%d\n", ssize, SIZECODE);
- + printk("command=%x SECTOR=%d HEAD=%d, TRACK=%d\n",
- + COMMAND, SECTOR, HEAD, TRACK);
- + printk("buffer drive=%d\n", buffer_drive);
- + printk("buffer track=%d\n", buffer_track);
- + printk("buffer_min=%d\n", buffer_min );
- + printk("buffer_max=%d\n", buffer_max );
- + return 0;
- + }
- +
- + if (raw_cmd->kernel_data != CURRENT->buffer ){
- + if (raw_cmd->kernel_data < floppy_track_buffer ||
- + current_count_sectors < 0 ||
- + raw_cmd->length < 0 ||
- + raw_cmd->kernel_data + raw_cmd->length >
- + floppy_track_buffer + (max_buffer_sectors << 10)){
- + DPRINT("buffer overrun in schedule dma\n");
- + printk("sector_t=%d buffer_min=%d current_count=%ld\n",
- + sector_t, buffer_min,
- + raw_cmd->length >> 9 );
- + printk("current_count_sectors=%ld\n",
- + current_count_sectors);
- + if ( CT(COMMAND) == FD_READ )
- + printk("read\n");
- + if ( CT(COMMAND) == FD_READ )
- + printk("write\n");
- + return 0;
- + }
- + } else if (raw_cmd->length > CURRENT->nr_sectors << 9 ||
- + current_count_sectors > CURRENT->nr_sectors){
- + DPRINT("buffer overrun in direct transfer\n");
- + return 0;
- + } else if ( raw_cmd->length < current_count_sectors << 9 ){
- + DPRINT("more sectors than bytes\n");
- + printk("bytes=%ld\n", raw_cmd->length >> 9 );
- + printk("sectors=%ld\n", current_count_sectors);
- + }
- + if (raw_cmd->length == 0){
- + DPRINT("zero dma transfer attempted from make_raw_request\n");
- + return 0;
- + }
- +#endif
- + return 2;
- +}
- +
- +static void redo_fd_request(void)
- +{
- +#define REPEAT {request_done(0); continue; }
- + kdev_t device;
- + int tmp;
- +
- + lastredo = jiffies;
- + if (current_drive < N_DRIVE)
- + floppy_off(current_drive);
- +
- + if (CURRENT && CURRENT->rq_status == RQ_INACTIVE){
- + DPRINT("current not active!\n");
- + return;
- + }
- +
- + while(1){
- + if (!CURRENT) {
- + CLEAR_INTR;
- + unlock_fdc();
- + return;
- + }
- + if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
- + panic(DEVICE_NAME ": request list destroyed");
- + if (CURRENT->bh && !CURRENT->bh->b_lock)
- + panic(DEVICE_NAME ": block not locked");
- +
- + device = CURRENT->rq_dev;
- + set_fdc( DRIVE(device));
- + reschedule_timeout(CURRENTD, "redo fd request", 0);
- +
- + set_floppy(device);
- + raw_cmd = & default_raw_cmd;
- + raw_cmd->flags = 0;
- + if(start_motor(redo_fd_request)) return;
- + if(test_bit(current_drive, &fake_change) ||
- + TESTF(FD_DISK_CHANGED)){
- + DPRINT("disk absent or changed during operation\n");
- + REPEAT;
- + }
- + if (!floppy) { /* Autodetection */
- + if (!probing){
- + DRS->probed_format = 0;
- + if ( next_valid_format() ){
- + DPRINT("no autodetectable formats\n");
- + floppy = NULL;
- + REPEAT;
- + }
- + }
- + probing = 1;
- + floppy = floppy_type+DP->autodetect[DRS->probed_format];
- + } else
- + probing = 0;
- + errors = & (CURRENT->errors);
- + tmp = make_raw_rw_request();
- + if ( tmp < 2 ){
- + request_done(tmp);
- + continue;
- + }
- +
- + if (TESTF(FD_NEED_TWADDLE))
- + twaddle();
- + floppy_tq.routine = (void *)(void *) floppy_start;
- + queue_task(&floppy_tq, &tq_timer);
- +#ifdef DEBUGT
- + debugt("queue fd request");
- +#endif
- + return;
- + }
- +#undef REPEAT
- +}
- +
- +static struct cont_t rw_cont={
- + rw_interrupt,
- + redo_fd_request,
- + bad_flp_intr,
- + request_done };
- +
- +struct tq_struct request_tq =
- +{ 0, 0, (void *) (void *) redo_fd_request, 0 };
- +
- +static void process_fd_request(void)
- +{
- + cont = &rw_cont;
- + queue_task(&request_tq, &tq_timer);
- +}
- +
- +static void do_fd_request(void)
- +{
- + if (fdc_busy){
- + /* fdc busy, this new request will be treated when the
- + current one is done */
- + is_alive("do fd request, old request running");
- + return;
- + }
- + /* fdc_busy cannot be set by an interrupt or a bh */
- + floppy_grab_irq_and_dma();
- + fdc_busy=1;
- + reschedule_timeout(MAXTIMEOUT, "do fd request",0);
- + process_fd_request();
- + is_alive("do fd request");
- +}
- +
- +static struct cont_t poll_cont={
- + success_and_wakeup,
- + floppy_ready,
- + generic_failure,
- + generic_done };
- +
- +static int poll_drive(int interruptible, int flag)
- +{
- + int ret;
- + /* no auto-sense, just clear dcl */
- + raw_cmd = &default_raw_cmd;
- + raw_cmd->flags= flag;
- + raw_cmd->track=0;
- + raw_cmd->cmd_count=0;
- + cont = &poll_cont;
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("setting NEWCHANGE in poll_drive\n");
- + }
- +#endif
- + SETF(FD_DISK_NEWCHANGE);
- + WAIT(floppy_ready);
- + return ret;
- +}
- +
- +/*
- + * User triggered reset
- + * ====================
- + */
- +
- +static void reset_intr(void)
- +{
- + printk("weird, reset interrupt called\n");
- +}
- +
- +static struct cont_t reset_cont={
- + reset_intr,
- + success_and_wakeup,
- + generic_failure,
- + generic_done };
- +
- +static int user_reset_fdc(int drive, int arg, int interruptible)
- +{
- + int ret;
- +
- + ret=0;
- + if(arg == FD_RESET_IF_NEEDED && !FDCS->reset)
- + return 0;
- + LOCK_FDC(drive,interruptible);
- + if(arg == FD_RESET_ALWAYS)
- + FDCS->reset=1;
- + if ( FDCS->reset ){
- + cont = &reset_cont;
- + reschedule_timeout(CURRENTD, "user reset fdc", 0);
- + WAIT(reset_fdc);
- + }
- + process_fd_request();
- + return ret;
- +}
- +
- +/*
- + * Misc Ioctl's and support
- + * ========================
- + */
- +static int fd_copyout(void *param, volatile void *address, int size)
- +{
- + int i;
- +
- + i = verify_area(VERIFY_WRITE,param,size);
- + if (i)
- + return i;
- + memcpy_tofs(param,(void *) address, size);
- + return 0;
- +}
- +
- +static int fd_copyin(void *param, volatile void *address, int size)
- +{
- + int i;
- +
- + i = verify_area(VERIFY_READ,param,size);
- + if (i)
- + return i;
- + memcpy_fromfs((void *) address, param, size);
- + return 0;
- +}
- +
- +#define COPYOUT(x) ECALL(fd_copyout( (void *)param, &(x), sizeof(x)))
- +#define COPYIN(x) ECALL(fd_copyin( (void *)param, &(x), sizeof(x)))
- +
- +static const char *drive_name(int type, int drive )
- +{
- + struct floppy_struct *floppy;
- +
- + if ( type )
- + floppy = floppy_type + type;
- + else {
- + if ( UDP->native_format )
- + floppy = floppy_type + UDP->native_format;
- + else
- + return "(null)";
- + }
- + if ( floppy->name )
- + return floppy->name;
- + else
- + return "(null)";
- +}
- +
- +
- +/* raw commands */
- +static void raw_cmd_done(int flag)
- +{
- + int i;
- +
- + if(!flag) {
- + raw_cmd->flags = FD_RAW_FAILURE;
- + raw_cmd->flags |= FD_RAW_HARDFAILURE;
- + } else {
- + raw_cmd->reply_count = inr;
- + for( i=0; i< raw_cmd->reply_count; i++)
- + raw_cmd->reply[i] = reply_buffer[i];
- +
- + if ( raw_cmd->flags & ( FD_RAW_READ | FD_RAW_WRITE ))
- + raw_cmd->length = get_dma_residue(FLOPPY_DMA);
- +
- + if( (raw_cmd->flags & FD_RAW_SOFTFAILURE) &&
- + (!raw_cmd->reply_count || (raw_cmd->reply[0] & 0xc0)))
- + raw_cmd->flags |= FD_RAW_FAILURE;
- +
- + if( disk_change(current_drive) )
- + raw_cmd->flags |= FD_RAW_DISK_CHANGE;
- + else
- + raw_cmd->flags &= ~FD_RAW_DISK_CHANGE;
- + if(raw_cmd->flags & FD_RAW_NO_MOTOR_AFTER)
- + motor_off_callback(current_drive);
- +
- + if(raw_cmd->next &&
- + (!(raw_cmd->flags & FD_RAW_FAILURE) ||
- + !(raw_cmd->flags & FD_RAW_STOP_IF_FAILURE)) &&
- + ((raw_cmd->flags & FD_RAW_FAILURE) ||
- + !(raw_cmd->flags &FD_RAW_STOP_IF_SUCCESS))) {
- + raw_cmd = raw_cmd->next;
- + return;
- + }
- + }
- + generic_done(flag);
- +}
- +
- +
- +static struct cont_t raw_cmd_cont={
- + success_and_wakeup,
- + floppy_start,
- + generic_failure,
- + raw_cmd_done
- +};
- +
- +static inline int raw_cmd_copyout(int cmd, char *param,
- + struct floppy_raw_cmd *ptr)
- +{
- + struct old_floppy_raw_cmd old_raw_cmd;
- + int ret;
- +
- + while(ptr) {
- + if(cmd == OLDFDRAWCMD) {
- + old_raw_cmd.flags = ptr->flags;
- + old_raw_cmd.data = ptr->data;
- + old_raw_cmd.length = ptr->length;
- + old_raw_cmd.rate = ptr->rate;
- + old_raw_cmd.reply_count = ptr->reply_count;
- + memcpy(old_raw_cmd.reply, ptr->reply, 7);
- + COPYOUT(old_raw_cmd);
- + param += sizeof(old_raw_cmd);
- + } else {
- + COPYOUT(*ptr);
- + param += sizeof(struct floppy_raw_cmd);
- + }
- +
- + if ( (ptr->flags & FD_RAW_READ) && ptr->buffer_length){
- + if(ptr->length>=0 && ptr->length<=ptr->buffer_length)
- + ECALL(fd_copyout(ptr->data,
- + ptr->kernel_data,
- + ptr->buffer_length -
- + ptr->length));
- + }
- + ptr = ptr->next;
- + }
- + return 0;
- +}
- +
- +
- +static void raw_cmd_free(struct floppy_raw_cmd **ptr)
- +{
- + struct floppy_raw_cmd **next;
- +
- + while(*ptr) {
- + next = & (*ptr)->next;
- + if((*ptr)->buffer_length) {
- + free_pages((unsigned long)(*ptr)->kernel_data,
- + __get_order((*ptr)->buffer_length));
- + (*ptr)->buffer_length = 0;
- + }
- + kfree(*ptr);
- + *ptr = 0;
- + ptr = next;
- + }
- +}
- +
- +
- +static inline int raw_cmd_copyin(int cmd, char *param,
- + struct floppy_raw_cmd **rcmd)
- +{
- + struct floppy_raw_cmd *ptr;
- + struct old_floppy_raw_cmd old_raw_cmd;
- + int ret;
- + int i;
- +
- + *rcmd = 0;
- + while(1) {
- + ptr = (struct floppy_raw_cmd *)
- + kmalloc(sizeof (struct floppy_raw_cmd ), GFP_USER);
- + if (!ptr)
- + return -ENOMEM;
- + ptr->next = 0;
- + ptr->buffer_length = 0;
- + *rcmd = ptr;
- + if(cmd == OLDFDRAWCMD){
- + COPYIN(old_raw_cmd);
- + ptr->flags = old_raw_cmd.flags;
- + ptr->data = old_raw_cmd.data;
- + ptr->length = old_raw_cmd.length;
- + ptr->rate = old_raw_cmd.rate;
- + ptr->cmd_count = old_raw_cmd.cmd_count;
- + ptr->track = old_raw_cmd.track;
- + memcpy(ptr->cmd, old_raw_cmd.cmd, 9);
- + if(ptr->cmd_count > 9)
- + return -EINVAL;
- + ptr->next = 0;
- + ptr->phys_length = 0;
- + param += sizeof(struct old_floppy_raw_cmd);
- + } else {
- + COPYIN(*ptr);
- + param += sizeof(struct floppy_raw_cmd);
- + if(ptr->cmd_count > 16)
- + return -EINVAL;
- + }
- +
- + for(i=0; i< 16; i++)
- + ptr->reply[i] = 0;
- + ptr->resultcode = 0;
- +
- + ptr->next = 0;
- + ptr->buffer_length = 0;
- + ptr->kernel_data = 0;
- +
- + if(ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
- + if(!ptr->length)
- + return -EINVAL;
- +
- + ptr->kernel_data =(char *)dma_mem_alloc(ptr->length);
- + if(!ptr->kernel_data)
- + return -ENOMEM;
- + ptr->buffer_length = ptr->length;
- + }
- + if(ptr->flags & FD_RAW_WRITE)
- + fd_copyin(ptr->data, ptr->kernel_data, ptr->length);
- + rcmd = & (ptr->next);
- + if( ! (ptr->flags & FD_RAW_MORE))
- + return 0;
- + ptr->rate &= 0x03;
- +
- + }
- +}
- +
- +
- +static int raw_cmd_ioctl(int cmd, void *param)
- +{
- + int drive, ret, ret2;
- + struct floppy_raw_cmd *my_raw_cmd;
- +
- + if ( FDCS->rawcmd <= 1 )
- + FDCS->rawcmd = 1;
- + for ( drive= 0; drive < N_DRIVE; drive++){
- + if ( FDC(drive) != fdc)
- + continue;
- + if ( drive == current_drive ){
- + if ( UDRS->fd_ref > 1 ){
- + FDCS->rawcmd = 2;
- + break;
- + }
- + } else if ( UDRS->fd_ref ){
- + FDCS->rawcmd = 2;
- + break;
- + }
- + }
- +
- + if(FDCS->reset)
- + return -EIO;
- +
- + ret = raw_cmd_copyin(cmd, param, &my_raw_cmd);
- + if (ret) {
- + raw_cmd_free(&my_raw_cmd);
- + return ret;
- + }
- +
- + raw_cmd = my_raw_cmd;
- + cont = &raw_cmd_cont;
- + ret=wait_til_done(floppy_start,1);
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("calling disk change from raw_cmd ioctl\n");
- + }
- +#endif
- +
- + if (ret != -EINTR && FDCS->reset)
- + ret = -EINTR;
- +
- + DRS->track = NO_TRACK;
- +
- + ret2 = raw_cmd_copyout(cmd, param, my_raw_cmd);
- + if(!ret)
- + ret = ret2;
- + raw_cmd_free(&my_raw_cmd);
- + return ret;
- +}
- +
- +static int invalidate_drive(kdev_t rdev)
- +{
- + /* invalidate the buffer track to force a reread */
- + set_bit( DRIVE(rdev), &fake_change);
- + process_fd_request();
- + check_disk_change(rdev);
- + return 0;
- +}
- +
- +static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
- + unsigned long param)
- +{
- +#define IOCTL_MODE_BIT 8
- +#define OPEN_WRITE_BIT 16
- +#define IOCTL_ALLOWED (filp && (filp->f_mode & IOCTL_MODE_BIT))
- +
- + struct floppy_struct newparams;
- + struct format_descr tmp_format_req;
- + int i,drive,type,cnt;
- + kdev_t device;
- + struct floppy_struct *this_floppy;
- + const char *name;
- + int ret;
- +
- + device = inode->i_rdev;
- + switch (cmd) {
- + RO_IOCTLS(device,param);
- + }
- + type = TYPE(device);
- + drive = DRIVE(device);
- + switch (cmd) {
- + case OLDFDGETDRVTYP:
- + case FDGETDRVTYP:
- + i=verify_area(VERIFY_WRITE,(void *) param,16);
- + if (i)
- + return i;
- + name = drive_name(type,drive);
- + for ( cnt=0; cnt<16; cnt++){
- + put_user(name[cnt], ((char*)param)+cnt);
- + if ( ! *name )
- + break;
- + }
- + return 0;
- + case OLDFDGETMAXERRS:
- + case FDGETMAXERRS:
- + COPYOUT(UDP->max_errors);
- + return 0;
- + case OLDFDGETPRM:
- + case FDGETPRM:
- + if (type)
- + this_floppy = &floppy_type[type];
- + else if ((this_floppy = current_type[drive]) == NULL)
- + return -ENODEV;
- + COPYOUT(this_floppy[0]);
- + return 0;
- + case OLDFDPOLLDRVSTAT:
- + case FDPOLLDRVSTAT:
- + LOCK_FDC(drive,1);
- + CALL(poll_drive(1, FD_RAW_NEED_DISK));
- + process_fd_request();
- + /* fall through */
- + case OLDFDGETDRVSTAT:
- + case FDGETDRVSTAT:
- + COPYOUT(*UDRS);
- + return 0;
- + case OLDFDGETFDCSTAT:
- + COPYOUT(* (struct old_floppy_fdc_state *) UFDCS);
- + return 0;
- + case FDGETFDCSTAT:
- + COPYOUT(*UFDCS);
- + return 0;
- + case OLDFDGETDRVPRM:
- + case FDGETDRVPRM:
- + COPYOUT(*UDP);
- + return 0;
- + case OLDFDWERRORGET:
- + case FDWERRORGET:
- + COPYOUT(*UDRWE);
- + return 0;
- + }
- + if (!IOCTL_ALLOWED)
- + return -EPERM;
- + switch (cmd) {
- + case OLDFDWERRORCLR:
- + case FDWERRORCLR:
- + UDRWE->write_errors = 0;
- + UDRWE->first_error_sector = 0;
- + UDRWE->first_error_generation = 0;
- + UDRWE->last_error_sector = 0;
- + UDRWE->last_error_generation = 0;
- + UDRWE->badness = 0;
- + return 0;
- + case OLDFDRAWCMD:
- + case FDRAWCMD:
- + if (type)
- + return -EINVAL;
- + LOCK_FDC(drive,1);
- + set_floppy(device);
- + CALL(i = raw_cmd_ioctl(cmd,(void *) param));
- + process_fd_request();
- + return i;
- + case OLDFDFMTTRK:
- + case FDFMTTRK:
- + if (UDRS->fd_ref != 1)
- + return -EBUSY;
- + COPYIN(tmp_format_req);
- + return do_format(device, &tmp_format_req);
- + case OLDFDSETMAXERRS:
- + case FDSETMAXERRS:
- + COPYIN(UDP->max_errors);
- + return 0;
- + case OLDFDFMTBEG:
- + case FDFMTBEG:
- + return 0;
- + case OLDFDCLRPRM:
- + case FDCLRPRM:
- + LOCK_FDC(drive,1);
- + current_type[drive] = NULL;
- + floppy_sizes[drive] = MAX_DISK_SIZE;
- + UDRS->keep_data = 0;
- + return invalidate_drive(device);
- + case OLDFDFMTEND:
- + case FDFMTEND:
- + case OLDFDFLUSH:
- + case FDFLUSH:
- + LOCK_FDC(drive,1);
- + return invalidate_drive(device);
- + case OLDFDSETPRM:
- + case FDSETPRM:
- + case OLDFDDEFPRM:
- + case FDDEFPRM:
- + COPYIN(newparams);
- + /* sanity checking for parameters.*/
- + if(newparams.sect <= 0 ||
- + newparams.head <= 0 ||
- + newparams.track <= 0 ||
- + newparams.track > UDP->tracks>> STRETCH(&newparams) ||
- + /* check if reserved bits are set */
- + (newparams.stretch & ~(FD_STRETCH | FD_SWAPSIDES)) != 0)
- + return -EINVAL;
- + if ( type){
- + if ( !suser() )
- + return -EPERM;
- + LOCK_FDC(drive,1);
- + for ( cnt = 0; cnt < N_DRIVE; cnt++){
- + if (TYPE(drive_state[cnt].fd_device) == type &&
- + drive_state[cnt].fd_ref)
- + set_bit(drive, &fake_change);
- + }
- + floppy_type[type] = newparams;
- + floppy_type[type].name="user format";
- + for (cnt = type << 2 ;
- + cnt < (type << 2 ) + 4 ;
- + cnt++)
- + floppy_sizes[cnt]=
- + floppy_sizes[cnt+0x80]=
- + floppy_type[type].size>>1;
- + process_fd_request();
- + for ( cnt = 0; cnt < N_DRIVE; cnt++){
- + if (TYPE(drive_state[cnt].fd_device) == type &&
- + drive_state[cnt].fd_ref)
- + check_disk_change(drive_state[cnt].
- + fd_device);
- + }
- + return 0;
- + }
- +
- + LOCK_FDC(drive,1);
- + if ( cmd != FDDEFPRM )
- + /* notice a disk change immediately, else
- + * we loose our settings immediately*/
- + CALL(poll_drive(1,0));
- + user_params[drive] = newparams;
- + if (buffer_drive == drive &&
- + buffer_max > user_params[drive].sect)
- + buffer_max=user_params[drive].sect;
- + current_type[drive] = &user_params[drive];
- + floppy_sizes[drive] = user_params[drive].size >> 1;
- + if (cmd == FDDEFPRM)
- + DRS->keep_data = -1;
- + else
- + DRS->keep_data = 1;
- + /* invalidation. Invalidate only when needed, i.e.
- + * when there are already sectors in the buffer cache
- + * whose number will change. This is useful, because
- + * mtools often changes the geometry of the disk after
- + * looking at the boot block */
- + if (DRS->maxblock >
- + user_params[drive].sect ||
- + DRS->maxtrack )
- + invalidate_drive(device);
- + else
- + process_fd_request();
- + return 0;
- + case OLDFDRESET:
- + case FDRESET:
- + return user_reset_fdc( drive, (int)param, 1);
- + case OLDFDMSGON:
- + case FDMSGON:
- + UDP->flags |= FTD_MSG;
- + return 0;
- + case OLDFDMSGOFF:
- + case FDMSGOFF:
- + UDP->flags &= ~FTD_MSG;
- + return 0;
- + case OLDFDSETEMSGTRESH:
- + case FDSETEMSGTRESH:
- + UDP->max_errors.reporting =
- + (unsigned short) (param & 0x0f);
- + return 0;
- + case OLDFDTWADDLE:
- + case FDTWADDLE:
- + LOCK_FDC(drive,1);
- + twaddle();
- + process_fd_request();
- + }
- + if ( ! suser() )
- + return -EPERM;
- + switch(cmd){
- + case OLDFDSETDRVPRM:
- + case FDSETDRVPRM:
- + COPYIN(*UDP);
- + return 0;
- + default:
- + return -EINVAL;
- + }
- + return 0;
- +#undef IOCTL_ALLOWED
- +}
- +
- +static void config_types(void)
- +{
- + int first=1;
- + int drive;
- +
- + /* read drive info out of physical cmos */
- + drive=0;
- + if (!UDP->cmos )
- + UDP->cmos= FLOPPY0_TYPE;
- + drive=1;
- + if (!UDP->cmos && FLOPPY1_TYPE)
- + UDP->cmos = FLOPPY1_TYPE;
- +
- + /* XXX */
- + /* additional physical CMOS drive detection should go here */
- +
- + for (drive=0; drive < N_DRIVE; drive++){
- + if (UDP->cmos >= 0 && UDP->cmos <= NUMBER(default_drive_params))
- + memcpy((char *) UDP,
- + (char *) (&default_drive_params[(int)UDP->cmos].params),
- + sizeof(struct floppy_drive_params));
- + if (UDP->cmos){
- + if (first)
- + printk("Floppy drive(s): ");
- + else
- + printk(", ");
- + first=0;
- + if (UDP->cmos > 0 ){
- + ALLOWED_DRIVE_MASK |= 1 << drive;
- + printk("fd%d is %s", drive,
- + default_drive_params[(int)UDP->cmos].name);
- + } else
- + printk("fd%d is unknown type %d",drive,
- + UDP->cmos);
- + }
- + }
- + if(!first)
- + printk("\n");
- +}
- +
- +static int floppy_read(struct inode * inode, struct file * filp,
- + char * buf, int count)
- +{
- + int drive = DRIVE(inode->i_rdev);
- +
- + check_disk_change(inode->i_rdev);
- + if (UTESTF(FD_DISK_CHANGED))
- + return -ENXIO;
- + return block_read(inode, filp, buf, count);
- +}
- +
- +static int floppy_write(struct inode * inode, struct file * filp,
- + const char * buf, int count)
- +{
- + int block;
- + int ret;
- + int drive = DRIVE(inode->i_rdev);
- +
- + if (!UDRS->maxblock)
- + UDRS->maxblock=1;/* make change detectable */
- + check_disk_change(inode->i_rdev);
- + if (UTESTF(FD_DISK_CHANGED))
- + return -ENXIO;
- + if (!UTESTF(FD_DISK_WRITABLE))
- + return -EROFS;
- + block = (filp->f_pos + count) >> 9;
- + if (block > UDRS->maxblock)
- + UDRS->maxblock = block;
- + ret= block_write(inode, filp, buf, count);
- + return ret;
- +}
- +
- +static void floppy_release(struct inode * inode, struct file * filp)
- +{
- + int drive;
- +
- + drive = DRIVE(inode->i_rdev);
- +
- + if( !filp || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
- + /* if the file is mounted OR (writable now AND writable at
- + * open time) Linus: Does this cover all cases? */
- + block_fsync(inode,filp);
- +
- + if (UDRS->fd_ref < 0)
- + UDRS->fd_ref=0;
- + else if (!UDRS->fd_ref--) {
- + DPRINT("floppy_release with fd_ref == 0");
- + UDRS->fd_ref = 0;
- + }
- + floppy_release_irq_and_dma();
- +}
- +
- +/*
- + * floppy_open check for aliasing (/dev/fd0 can be the same as
- + * /dev/PS0 etc), and disallows simultaneous access to the same
- + * drive with different device numbers.
- + */
- +#define RETERR(x) do{floppy_release(inode,filp); return -(x);}while(0)
- +
- +static int floppy_open(struct inode * inode, struct file * filp)
- +{
- + int drive;
- + int old_dev;
- + int try;
- + char *tmp;
- +
- + if (!filp) {
- + DPRINT("Weird, open called with filp=0\n");
- + return -EIO;
- + }
- +
- + drive = DRIVE(inode->i_rdev);
- +
- + if (drive >= N_DRIVE ||
- + !( ALLOWED_DRIVE_MASK & ( 1 << drive)) ||
- + fdc_state[FDC(drive)].version == FDC_NONE)
- + return -ENXIO;
- +
- + if (TYPE(inode->i_rdev) >= NUMBER(floppy_type))
- + return -ENXIO;
- + old_dev = UDRS->fd_device;
- + if (UDRS->fd_ref && old_dev != MINOR(inode->i_rdev))
- + return -EBUSY;
- +
- + if(!UDRS->fd_ref && (UDP->flags & FD_BROKEN_DCL)){
- + USETF(FD_DISK_CHANGED);
- + USETF(FD_VERIFY);
- + }
- +
- + if(UDRS->fd_ref == -1 ||
- + (UDRS->fd_ref && (filp->f_flags & O_EXCL)))
- + return -EBUSY;
- +
- + if (floppy_grab_irq_and_dma())
- + return -EBUSY;
- +
- + if (filp->f_flags & O_EXCL)
- + UDRS->fd_ref = -1;
- + else
- + UDRS->fd_ref++;
- +
- + if (!floppy_track_buffer){
- + /* if opening an ED drive, reserve a big buffer,
- + * else reserve a small one */
- + if ((UDP->cmos == 6) || (UDP->cmos == 5))
- + try = 64; /* Only 48 actually useful */
- + else
- + try = 32; /* Only 24 actually useful */
- +
- + tmp=(char *)dma_mem_alloc(1024 * try);
- + if (!tmp) {
- + try >>= 1; /* buffer only one side */
- + if (try < 16)
- + try=16;
- + tmp= (char *)dma_mem_alloc(1024*try);
- + }
- + if (!tmp) {
- + DPRINT("Unable to allocate DMA memory\n");
- + RETERR(ENXIO);
- + }
- + if (floppy_track_buffer){
- + free_pages((unsigned long)tmp,__get_order(try*1024));
- + }else {
- + floppy_track_buffer = tmp;
- + max_buffer_sectors = try;
- + }
- + }
- +
- + UDRS->fd_device = MINOR(inode->i_rdev);
- +
- + if (old_dev && old_dev != inode->i_rdev) {
- + if (buffer_drive == drive)
- + buffer_track = -1;
- + invalidate_buffers(old_dev);
- + }
- +
- + /* Allow ioctls if we have write-permissions even if read-only open */
- + if ((filp->f_mode & 2) || (permission(inode,2) == 0))
- + filp->f_mode |= IOCTL_MODE_BIT;
- + if (filp->f_mode & 2)
- + filp->f_mode |= OPEN_WRITE_BIT;
- +
- + if (UFDCS->rawcmd == 1)
- + UFDCS->rawcmd = 2;
- +
- + if (filp->f_flags & O_NDELAY)
- + return 0;
- + if (filp->f_mode & 3) {
- + UDRS->last_checked = 0;
- + check_disk_change(inode->i_rdev);
- + if (UTESTF(FD_DISK_CHANGED))
- + RETERR(ENXIO);
- + }
- + if ((filp->f_mode & 2) && !(UTESTF(FD_DISK_WRITABLE)))
- + RETERR(EROFS);
- + return 0;
- +#undef RETERR
- +}
- +
- +/*
- + * Check if the disk has been changed or if a change has been faked.
- + */
- +static int check_floppy_change(kdev_t dev)
- +{
- + int drive = DRIVE( dev );
- +
- + if (MAJOR(dev) != MAJOR_NR) {
- + DPRINT("floppy_changed: not a floppy\n");
- + return 0;
- + }
- +
- + if (UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY))
- + return 1;
- +
- + if(UDRS->last_checked + UDP->checkfreq < jiffies){
- + lock_fdc(drive,0);
- + poll_drive(0,0);
- + process_fd_request();
- + }
- +
- + if(UTESTF(FD_DISK_CHANGED) ||
- + UTESTF(FD_VERIFY) ||
- + test_bit(drive, &fake_change) ||
- + (!TYPE(dev) && !current_type[drive]))
- + return 1;
- + return 0;
- +}
- +
- +/* revalidate the floppy disk, i.e. trigger format autodetection by reading
- + * the bootblock (block 0). "Autodetection" is also needed to check whether
- + * there is a disk in the drive at all... Thus we also do it for fixed
- + * geometry formats */
- +static int floppy_revalidate(kdev_t dev)
- +{
- +#define NO_GEOM (!current_type[drive] && !TYPE(dev))
- + struct buffer_head * bh;
- + int drive=DRIVE(dev);
- + int cf;
- +
- + if(UTESTF(FD_DISK_CHANGED) ||
- + UTESTF(FD_VERIFY) ||
- + test_bit(drive, &fake_change) ||
- + NO_GEOM){
- + lock_fdc(drive,0);
- + cf = UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY);
- + if(! (cf || test_bit(drive, &fake_change) || NO_GEOM)){
- + process_fd_request(); /* already done by another thread */
- + return 0;
- + }
- + UDRS->maxblock = 0;
- + UDRS->maxtrack = 0;
- + if ( buffer_drive == drive)
- + buffer_track = -1;
- + clear_bit(drive, &fake_change);
- + UCLEARF(FD_DISK_CHANGED);
- + if(cf)
- + UDRS->generation++;
- + if(NO_GEOM){
- + /* auto-sensing */
- + int size = floppy_blocksizes[MINOR(dev)];
- + if (!size)
- + size = 1024;
- + if (!(bh = getblk(dev,0,size))){
- + process_fd_request();
- + return 1;
- + }
- + if ( bh && ! bh->b_uptodate)
- + ll_rw_block(READ, 1, &bh);
- + process_fd_request();
- + wait_on_buffer(bh);
- + brelse(bh);
- + return 0;
- + }
- + if(cf)
- + poll_drive(0, FD_RAW_NEED_DISK);
- + process_fd_request();
- + }
- + return 0;
- +}
- +
- +static struct file_operations floppy_fops = {
- + NULL, /* lseek - default */
- + floppy_read, /* read - general block-dev read */
- + floppy_write, /* write - general block-dev write */
- + NULL, /* readdir - bad */
- + NULL, /* select */
- + fd_ioctl, /* ioctl */
- + NULL, /* mmap */
- + floppy_open, /* open */
- + floppy_release, /* release */
- + block_fsync, /* fsync */
- + NULL, /* fasync */
- + check_floppy_change, /* media_change */
- + floppy_revalidate, /* revalidate */
- +};
- +
- +/*
- + * Floppy Driver initialisation
- + * =============================
- + */
- +
- +/* Determine the floppy disk controller type */
- +/* This routine was written by David C. Niemi */
- +static char get_fdc_version(void)
- +{
- + int r;
- +
- + output_byte(FD_DUMPREGS); /* 82072 and better know DUMPREGS */
- + if ( FDCS->reset )
- + return FDC_NONE;
- + if ( (r = result()) <= 0x00)
- + return FDC_NONE; /* No FDC present ??? */
- + if ((r==1) && (reply_buffer[0] == 0x80)){
- + printk("FDC %d is a 8272A\n",fdc);
- + return FDC_8272A; /* 8272a/765 don't know DUMPREGS */
- + }
- + if (r != 10) {
- + printk("FDC init: DUMPREGS: unexpected return of %d bytes.\n", r);
- + return FDC_UNKNOWN;
- + }
- + output_byte(FD_VERSION);
- + r = result();
- + if ((r == 1) && (reply_buffer[0] == 0x80)){
- + printk("FDC %d is a 82072\n",fdc);
- + return FDC_82072; /* 82072 doesn't know VERSION */
- + }
- + if ((r != 1) || (reply_buffer[0] != 0x90)) {
- + printk("FDC init: VERSION: unexpected return of %d bytes.\n", r);
- + return FDC_UNKNOWN;
- + }
- + output_byte(FD_UNLOCK);
- + r = result();
- + if ((r == 1) && (reply_buffer[0] == 0x80)){
- + printk("FDC %d is a pre-1991 82077\n", fdc);
- + return FDC_82077_ORIG; /* Pre-1991 82077 doesn't know LOCK/UNLOCK */
- + }
- + if ((r != 1) || (reply_buffer[0] != 0x00)) {
- + printk("FDC init: UNLOCK: unexpected return of %d bytes.\n", r);
- + return FDC_UNKNOWN;
- + }
- + printk("FDC %d is a post-1991 82077\n",fdc);
- + return FDC_82077; /* Revised 82077AA passes all the tests */
- +} /* get_fdc_version */
- +
- +/* lilo configuration */
- +
- +/* we make the invert_dcl function global. One day, somebody might
- +want to centralize all thinkpad related options into one lilo option,
- +there are just so many thinkpad related quirks! */
- +void floppy_invert_dcl(int *ints,int param)
- +{
- + int i;
- +
- + for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
- + if (param)
- + default_drive_params[i].params.flags |= 0x80;
- + else
- + default_drive_params[i].params.flags &= ~0x80;
- + }
- + DPRINT("Configuring drives for inverted dcl\n");
- +}
- +
- +static void daring(int *ints,int param)
- +{
- + int i;
- +
- + for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
- + if (param){
- + default_drive_params[i].params.select_delay = 0;
- + default_drive_params[i].params.flags |= FD_SILENT_DCL_CLEAR;
- + } else {
- + default_drive_params[i].params.select_delay = 2*HZ/100;
- + default_drive_params[i].params.flags &= ~FD_SILENT_DCL_CLEAR;
- + }
- + }
- + DPRINT1("Assuming %s floppy hardware\n", param ? "standard" : "broken");
- +}
- +
- +static void allow_drives(int *ints, int param)
- +{
- + ALLOWED_DRIVE_MASK=param;
- + DPRINT1("setting allowed_drive_mask to 0x%x\n", param);
- +}
- +
- +static void fdc2_adr(int *ints, int param)
- +{
- + FDC2 = param;
- + if(param){
- + DPRINT1("enabling second fdc at address 0x%3x\n", FDC2);
- + } else
- + DPRINT("disabling second fdc\n");
- +}
- +
- +static void unex(int *ints,int param)
- +{
- + print_unex = param;
- + DPRINT1("%sprinting messages for unexpected interrupts\n",
- + param ? "" : "not ");
- +}
- +
- +static void set_cmos(int *ints, int dummy)
- +{
- + int current_drive=0;
- +
- + if ( ints[0] != 2 ){
- + DPRINT("wrong number of parameters for cmos\n");
- + return;
- + }
- + current_drive = ints[1];
- + if (current_drive < 0 || current_drive >= 8 ){
- + DPRINT("bad drive for set_cmos\n");
- + return;
- + }
- + if(current_drive >= 4 && !FDC2)
- + fdc2_adr(0, 0x370);
- + if(ints[2] <= 0 || ints[2] >= NUMBER(default_drive_params)){
- + DPRINT1("bad cmos code %d\n", ints[2]);
- + return;
- + }
- + DP->cmos = ints[2];
- + DPRINT1("setting cmos code to %d\n", ints[2]);
- +}
- +
- +static struct param_table {
- + const char *name;
- + void (*fn)(int *ints, int param);
- + int def_param;
- +} config_params[]={
- + { "allowed_drive_mask", allow_drives, 0xff },
- + { "all_drives", allow_drives, 0xff },
- + { "asus_pci", allow_drives, 0x33 },
- +
- + { "daring", daring, 1 },
- +
- + { "two_fdc", fdc2_adr, 0x370 },
- + { "one_fdc", fdc2_adr, 0 },
- +
- + { "thinkpad", floppy_invert_dcl, 1 },
- +
- + { "cmos", set_cmos, 0 },
- +
- + { "unexpected_interrupts", unex, 1 },
- + { "no_unexpected_interrupts", unex, 0 },
- + { "L40SX", unex, 0 } };
- +
- +#define FLOPPY_SETUP
- +void floppy_setup(char *str, int *ints)
- +{
- + int i;
- + int param;
- + if(str)
- + for(i=0; i< ARRAY_SIZE(config_params); i++){
- + if (strcmp(str,config_params[i].name) == 0 ){
- + if (ints[0] )
- + param = ints[1];
- + else
- + param = config_params[i].def_param;
- + config_params[i].fn(ints,param);
- + return;
- + }
- + }
- + if(str) {
- + DPRINT1("unknown floppy option [%s]\n", str);
- +
- + DPRINT("allowed options are:");
- + for(i=0; i< ARRAY_SIZE(config_params); i++)
- + printk(" %s",config_params[i].name);
- + printk("\n");
- + } else
- + DPRINT("botched floppy option\n");
- + DPRINT("Read linux/arch/arm/drivers/block/README.fd\n");
- +}
- +
- +int floppy_init(void)
- +{
- + int i,unit,drive;
- + int have_no_fdc=0;
- +
- + raw_cmd = NULL;
- +
- + sti();
- +
- + if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
- + printk("Unable to get major %d for floppy\n",MAJOR_NR);
- + return -EBUSY;
- + }
- +
- + for(i=0; i<256; i++)
- + if (TYPE(i))
- + floppy_sizes[i] = floppy_type[TYPE(i)].size >> 1;
- + else
- + floppy_sizes[i] = MAX_DISK_SIZE;
- +
- + blk_size[MAJOR_NR] = floppy_sizes;
- + blksize_size[MAJOR_NR] = floppy_blocksizes;
- + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- + reschedule_timeout(MAXTIMEOUT, "floppy init", MAXTIMEOUT);
- + config_types();
- +
- + fdc_state[0].address = FDC1;
- + fdc_state[0].dor = 0;
- +#if N_FDC > 1
- + fdc_state[1].address = FDC2;
- + fdc_state[1].dor = 0;
- +#endif
- +
- + for (i = 0 ; i < N_FDC ; i++) {
- + fdc = i;
- + FDCS->dtr = -1;
- + FDCS->dor = 0x4;
- + FDCS->reset = 0;
- + FDCS->version = FDC_NONE;
- + }
- +
- + if(floppy_grab_irq_and_dma()){
- + unregister_blkdev(MAJOR_NR,"fd");
- + return -EBUSY;
- + }
- +
- + /* initialise drive state */
- + for (drive = 0; drive < N_DRIVE ; drive++) {
- + UDRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE | FD_DISK_CHANGED;
- + UDRS->generation = 0;
- + UDRS->keep_data = 0;
- + UDRS->fd_ref = 0;
- + UDRS->fd_device = 0;
- + floppy_track_buffer = NULL;
- + max_buffer_sectors = 0;
- + UDRWE->write_errors = 0;
- + UDRWE->first_error_sector = 0;
- + UDRWE->first_error_generation = 0;
- + UDRWE->last_error_sector = 0;
- + UDRWE->last_error_generation = 0;
- + UDRWE->badness = 0;
- + }
- +
- + for (i = 0 ; i < N_FDC ; i++) {
- + fdc = i;
- + FDCS->driver_version = FD_DRIVER_VERSION;
- + for(unit=0; unit<4; unit++)
- + FDCS->track[unit] = 0;
- + if (FDCS->address == -1 )
- + continue;
- + FDCS->rawcmd = 2;
- + if(user_reset_fdc(-1,FD_RESET_IF_NEEDED,0)){
- + FDCS->address = -1;
- + continue;
- + }
- + /* Try to determine the floppy controller type */
- + FDCS->version = get_fdc_version();
- + if (FDCS->version == FDC_NONE){
- + FDCS->address = -1;
- + continue;
- + }
- +
- + request_region(FDCS->address, 6, "floppy");
- + request_region(FDCS->address+7, 1, "floppy DIR");
- + /* address + 6 is reserved, and may be taken by IDE.
- + * Unfortunately, Adaptec doesn't know this :-(, */
- +
- + have_no_fdc = 0;
- + /* Not all FDCs seem to be able to handle the version command
- + * properly, so force a reset for the standard FDC clones,
- + * to avoid interrupt garbage.
- + */
- + FDCS->has_fifo = FDCS->version >= FDC_82077_ORIG;
- + user_reset_fdc(-1,FD_RESET_ALWAYS,0);
- + }
- + fdc=0;
- + del_timer(&fd_timeout);
- + current_drive = 0;
- + floppy_release_irq_and_dma();
- + initialising=0;
- + if(have_no_fdc)
- + unregister_blkdev(MAJOR_NR,"fd");
- + return have_no_fdc;
- +}
- +
- +static int floppy_grab_irq_and_dma(void)
- +{
- + int i;
- + cli();
- + if (usage_count++){
- + sti();
- + return 0;
- + }
- + sti();
- +#ifdef FD_MODULE
- + MOD_INC_USE_COUNT;
- +#endif
- + for(i=0; i< N_FDC; i++){
- + if(FDCS->address != -1){
- + fdc = i;
- + reset_fdc_info(1);
- + arm_set_dor(FDCS->dor);
- + }
- + }
- + set_dor(0, ~0, 8); /* avoid immediate interrupt */
- +
- + if (request_irq(FLOPPY_IRQ, floppy_interrupt,
- + SA_INTERRUPT|SA_SAMPLE_RANDOM, "floppy")) {
- + DPRINT1("Unable to grab IRQ%d for the floppy driver\n",
- + FLOPPY_IRQ);
- + return -1;
- + }
- + if (request_dma(FLOPPY_DMA,"floppy")) {
- + DPRINT1("Unable to grab DMA%d for the floppy driver\n",
- + FLOPPY_DMA);
- + free_irq(FLOPPY_IRQ);
- + return -1;
- + }
- + for(fdc = 0; fdc < N_FDC ; fdc++)
- + if(FDCS->address != -1)
- + arm_set_dor(FDCS->dor);
- + fdc = 0;
- + enable_irq(FLOPPY_IRQ);
- + return 0;
- +}
- +
- +static void floppy_release_irq_and_dma(void)
- +{
- +#ifdef CONFIG_FLOPPY_SANITY
- + int drive;
- +#endif
- + long tmpsize;
- + void *tmpaddr;
- +
- + cli();
- + if (--usage_count){
- + sti();
- + return;
- + }
- + sti();
- +#ifdef FD_MODULE
- + MOD_DEC_USE_COUNT;
- +#endif
- + disable_dma(FLOPPY_DMA);
- + free_dma(FLOPPY_DMA);
- + disable_irq(FLOPPY_IRQ);
- + free_irq(FLOPPY_IRQ);
- +
- + set_dor(0, ~0, 8);
- +#if N_FDC > 1
- + set_dor(1, ~8, 0);
- +#endif
- + floppy_enable_hlt();
- +
- + if (floppy_track_buffer && max_buffer_sectors) {
- + tmpsize = max_buffer_sectors*1024;
- + tmpaddr = (void *)floppy_track_buffer;
- + floppy_track_buffer = NULL;
- + max_buffer_sectors = 0;
- + free_pages((unsigned long)tmpaddr, __get_order(tmpsize));
- + }
- +
- +#ifdef CONFIG_FLOPPY_SANITY
- + for(drive=0; drive < N_FDC * 4; drive++)
- + if( motor_off_timer[drive].next )
- + printk("motor off timer %d still active\n", drive);
- +
- + if(fd_timeout.next)
- + printk("floppy timer still active:%s\n", timeout_message);
- + if (fd_timer.next)
- + printk("auxillary floppy timer still active\n");
- + if(floppy_tq.sync)
- + printk("task queue still active\n");
- +#endif
- +}
- +
- +
- +#ifdef MODULE
- +
- +extern char *get_options(char *str, int *ints);
- +
- +static void mod_setup(char *pattern, void (*setup)(char *, int *))
- +{
- + int i;
- + char c;
- + int j;
- + int match;
- + char buffer[100];
- + int ints[11];
- + int length = strlen(pattern)+1;
- +
- + match=0;
- + j=1;
- +
- + for(i=current->mm->env_start; i< current->mm->env_end; i ++){
- + c= get_fs_byte(i);
- + if(match){
- + if(j==99)
- + c='\0';
- + buffer[j] = c;
- + if(!c || c == ' ' || c == '\t'){
- + if(j){
- + buffer[j] = '\0';
- + setup(get_options(buffer,ints),ints);
- + }
- + j=0;
- + } else
- + j++;
- + if(!c)
- + break;
- + continue;
- + }
- + if( (!j && !c) || ( j && c == pattern[j-1]))
- + j++;
- + else
- + j=0;
- + if(j==length){
- + match=1;
- + j=0;
- + }
- + }
- +}
- +
- +
- +#ifdef __cplusplus
- +extern "C" {
- +#endif
- +int init_module(void)
- +{
- + int ret;
- + printk("inserting floppy driver for %s\n", kernel_version);
- +
- + mod_setup("floppy=", floppy_setup);
- +
- + ret = floppy_init();
- + return 0;
- +}
- +
- +void cleanup_module(void)
- +{
- + int fdc;
- +
- + for(fdc=0; fdc<2; fdc++)
- + if (FDCS->address != -1){
- + release_region(FDCS->address, 6);
- + release_region(FDCS->address+7, 1);
- + }
- +
- + unregister_blkdev(MAJOR_NR, "fd");
- +
- + blk_dev[MAJOR_NR].request_fn = 0;
- +}
- +
- +#ifdef __cplusplus
- +}
- +#endif
- +
- +#endif
- diff -urNwbB linux/arch/arm/drivers/block/genhd.c linux.arm/arch/arm/drivers/block/genhd.c
- --- linux/arch/arm/drivers/block/genhd.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/genhd.c Sun Mar 3 12:22:14 1996
- @@ -0,0 +1,506 @@
- +/*
- + * Code extracted from
- + * linux/kernel/hd.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + *
- + * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
- + * in the early extended-partition checks and added DM partitions
- + *
- + * Support for DiskManager v6.0x added by Mark Lord (mlord@bnr.ca)
- + * with information provided by OnTrack. This now works for linux fdisk
- + * and LILO, as well as loadlin and bootln. Note that disks other than
- + * /dev/hda *must* have a "DOS" type 0x51 partition in the first slot (hda1).
- + *
- + * More flexible handling of extended partitions - aeb, 950831
- + *
- + * Modifications Copyright (C) 1995, 1996 Russell King
- + *
- + * Origional from linux/drivers/genhd.c
- + *
- + * Altered to find image files on ADFS-formatted hard drives.
- + */
- +
- +#include <linux/config.h>
- +#include <linux/fs.h>
- +#include <linux/genhd.h>
- +#include <linux/kernel.h>
- +#include <linux/major.h>
- +#include <linux/string.h>
- +
- +#include <asm/system.h>
- +
- +struct gendisk *gendisk_head = NULL;
- +
- +static int current_minor = 0;
- +extern int *blk_size[];
- +extern void rd_load(void);
- +extern int ramdisk_size;
- +
- +extern int chr_dev_init(void);
- +extern int blk_dev_init(void);
- +extern int scsi_dev_init(void);
- +extern int net_dev_init(void);
- +
- +static void print_minor_name (struct gendisk *hd, int minor)
- +{
- + unsigned int unit = minor >> hd->minor_shift;
- + unsigned int part = minor & ((1 << hd->minor_shift) - 1);
- +
- +#ifdef CONFIG_BLK_DEV_IDE
- + /*
- + * IDE devices use multiple major numbers, but the drives
- + * are named as: {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}..
- + * This requires some creative handling here to find the
- + * correct name to use, with some help from ide.c
- + */
- + if (!strcmp(hd->major_name, "ide")) {
- + char name[16]; /* more than large enough */
- + strcpy(name, hd->real_devices); /* courtesy ide.c */
- + name[strlen(name)-1] += unit;
- + printk(" %s", name);
- + } else
- +#endif
- + printk(" %s%c", hd->major_name, 'a' + unit);
- + if (part)
- + printk("%d", part);
- + else
- + printk(":");
- +}
- +
- +static void add_partition (struct gendisk *hd, int minor, int start, int size)
- +{
- + hd->part[minor].start_sect = start;
- + hd->part[minor].nr_sects = size;
- + print_minor_name(hd, minor);
- +}
- +
- +static inline unsigned long get_word(const unsigned int *addr)
- +{
- + const unsigned char *ac = (const unsigned char *)addr;
- + if ((int)addr & 3)
- + return ac[0] | ac[1]<<8 | ac[2]<<16 | ac[3]<<24;
- + else
- + return addr[0];
- +}
- +
- +#ifdef CONFIG_MSDOS_PARTITION
- +static inline int is_extended_partition(struct partition *p)
- +{
- + return (p->sys_ind == DOS_EXTENDED_PARTITION ||
- + p->sys_ind == LINUX_EXTENDED_PARTITION);
- +}
- +
- +/*
- + * Create devices for each logical partition in an extended partition.
- + * The logical partitions form a linked list, with each entry being
- + * a partition table with two entries. The first entry
- + * is the real data partition (with a start relative to the partition
- + * table start). The second is a pointer to the next logical partition
- + * (with a start relative to the entire extended partition).
- + * We do not create a Linux partition for the partition tables, but
- + * only for the actual data partitions.
- + */
- +
- +static void extended_partition(struct gendisk *hd, kdev_t dev)
- +{
- + struct buffer_head *bh;
- + struct partition *p;
- + unsigned long first_sector, first_size, this_sector, this_size;
- + unsigned long start_sect, nr_sects;
- + int mask = (1 << hd->minor_shift) - 1;
- + int i;
- +
- + first_sector = hd->part[MINOR(dev)].start_sect;
- + first_size = hd->part[MINOR(dev)].nr_sects;
- + this_sector = first_sector;
- +
- + while (1) {
- + if ((current_minor & mask) == 0)
- + return;
- + if (!(bh = bread(dev,0,1024)))
- + return;
- + /*
- + * This block is from a device that we're about to stomp on.
- + * So make sure nobody thinks this block is usable.
- + */
- + bh->b_dirt = 0;
- + bh->b_uptodate = 0;
- + bh->b_req = 0;
- +
- + if (*(unsigned short *) (bh->b_data+510) != 0xAA55)
- + goto done;
- +
- + p = (struct partition *) (0x1BE + bh->b_data);
- +
- + this_size = hd->part[MINOR(dev)].nr_sects;
- +
- + /*
- + * Usually, the first entry is the real data partition,
- + * the 2nd entry is the next extended partition, or empty,
- + * and the 3rd and 4th entries are unused.
- + * However, DRDOS sometimes has an extended partition as
- + * the first entry (when the data partition is empty),
- + * and OS/2 seems to use all four entries.
- + */
- +
- + /*
- + * First process the data partition(s)
- + */
- + for (i = 0; i < 4; i++, p++) {
- + nr_sects = get_word (&p->nr_sects);
- + if (!nr_sects || is_extended_partition(p))
- + continue;
- +
- + /*
- + * Check the 3rd and 4th entries -
- + * these sometimes contain random garbage
- + */
- + start_sect = get_word (&p->start_sect);
- + if (i >= 2
- + && start_sect + nr_sects > this_size
- + && (this_sector + start_sect < first_sector ||
- + this_sector + start_sect + first_sector >
- + first_sector + first_size))
- + continue;
- +
- + add_partition(hd, current_minor, this_sector+start_sect, nr_sects);
- + current_minor++;
- + if ((current_minor & mask) == 0)
- + goto done;
- + }
- + /*
- + * Next, process the (first) extended partition, if present/
- + * (So far, there seems to be no reason to make
- + * extended_partition() recurse and allow a tree
- + * of extended partitions.)
- + * It should be a link to the next logical partition.
- + * Create a minor for this just long enough to get the next
- + * partition table. The minor will be reused for the next
- + * data partition.
- + */
- + p -= 4;
- + for (i=0; i<4; i++) {
- + nr_sects = get_word (&p->nr_sects);
- + if (nr_sects && is_extended_partition(p))
- + break;
- + }
- +
- + if (i == 4)
- + goto done; /* nothing left to do */
- +
- + start_sect = get_word (&p->start_sect);
- + hd->part[current_minor].nr_sects = nr_sects;
- + hd->part[current_minor].start_sect = first_sector + start_sect;
- + this_sector = first_sector + start_sect;
- + dev = MKDEV(hd->major, current_minor);
- + brelse(bh);
- + }
- +done:
- + brelse(bh);
- +}
- +
- +static int msdos_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector)
- +{
- + int i, minor = current_minor;
- + struct buffer_head *bh;
- + struct partition *p;
- + unsigned char *data;
- + int mask = (1 << hd->minor_shift) - 1;
- + unsigned long start_sect, nr_sects;
- +#ifdef CONFIG_BLK_DEV_IDE
- + int tested_for_dm6 = 0;
- +
- +read_mbr:
- +#endif
- + if (!(bh = bread(dev,0,1024))) {
- + printk(" unable to read partition table\n");
- + return -1;
- + }
- + data = bh->b_data;
- + bh->b_dirt = 0; /* In some cases we modify the geometry */
- + bh->b_uptodate = 0; /* of the drive (below), so ensure that */
- + bh->b_req = 0; /* nobody else tries to re-use this data. */
- +#ifdef CONFIG_BLK_DEV_IDE
- +check_table:
- +#endif
- + if (*(unsigned short *) (0x1fe + data) != 0xAA55) {
- + brelse(bh);
- + return 0;
- + }
- + p = (struct partition *) (0x1be + data);
- +
- +#ifdef CONFIG_BLK_DEV_IDE
- + /*
- + * Check for Disk Manager v6.0x (or EZ-DRIVE) with geometry translation
- + */
- + if (!tested_for_dm6++) { /* Only check for DM6 *once* */
- + extern int ide_xlate_1024(kdev_t, int, const char *);
- + /* check for various "disk managers" which do strange things */
- + if (p->sys_ind == EZD_PARTITION) {
- + /*
- + * The remainder of the disk must be accessed using
- + * a translated geometry that reduces the number of
- + * apparant cylinders to less than 1024 if possible.
- + *
- + * ide_xlate_1024() will take care of the necessary
- + * adjustments to fool fdisk/LILO and partition check.
- + */
- + if (ide_xlate_1024(dev, -1, " [EZD]")) {
- + data += 512;
- + goto check_table;
- + }
- + } else if (p->sys_ind == DM6_PARTITION) {
- + /*
- + * Everything on the disk is offset by 63 sectors,
- + * including the "new" MBR with its own partition table,
- + * and the remainder of the disk must be accessed using
- + * a translated geometry that reduces the number of
- + * apparant cylinders to less than 1024 if possible.
- + *
- + * ide_xlate_1024() will take care of the necessary
- + * adjustments to fool fdisk/LILO and partition check.
- + */
- + if (ide_xlate_1024(dev, 1, " [DM6:DDO]")) {
- + brelse(bh);
- + goto read_mbr;
- + }
- + } else {
- + /*
- + * look for DM6 signature in MBR, courtesy of OnTrack
- + */
- + unsigned int sig = *(unsigned short *)(data + 2);
- + if (sig <= 0x1ae && *(unsigned short *)(data + sig) = 0x55AA
- + && (1 & *(unsigned char *)(data + sig + 2)))
- + ide_xlate_1024(dev, 0, " [DM6:MBR]");
- + else if (p->sys_ind == DM6_AUX1PARTITION
- + || p->sys_ind == DM6_AUX3PARTITION)
- + ide_xlate_1024(dev, 0, " [DM6:AUX]");
- + }
- + }
- +#endif /* CONFIG_BLK_DEV_IDE */
- +
- + current_minor += 4;
- + for (i=1 ; i<=4 ; minor++, i++, p++) {
- + nr_sects = get_word (&p->nr_sects);
- + if(!nr_sects)
- + continue;
- + start_sect = get_word (&p->start_sect);
- + add_partition(hd, minor, first_sector+start_sect, nr_sects);
- + if (is_extended_partition(p)) {
- + printk(" <");
- + /*
- + * If we are rereading the partition table, we need
- + * to set the size of the partition so that we will
- + * be able to bread the block containing the extended
- + * partition info.
- + */
- + hd->sizes[minor] = hd->part[minor].nr_sects >> (BLOCK_SIZE_BITS - 9);
- + extended_partition(hd, MKDEV(hd->major, minor));
- + printk(" >");
- + /*
- + * prevent someone doing mkfs or mkswap on an
- + * extended partition, but leave room for LILO
- + */
- + if (hd->part[minor].nr_sects > 2)
- + hd->part[minor].nr_sects = 2;
- + }
- + }
- + /*
- + * Check for old-style Disk Manager partition table
- + */
- + if (*(unsigned short *) (data + 0xfc) == 0x55AA) {
- + p = (struct partition *) (0x1be + data);
- + for (i = 4 ; i < 16 ; i++, current_minor++) {
- + p--;
- + if ((current_minor & mask) == 0)
- + break;
- + nr_sects = get_word (&p->nr_sects);
- + start_sect = get_word (&p->start_sect);
- + if (!(start_sect && nr_sects))
- + continue;
- + add_partition(hd, current_minor, start_sect, nr_sects);
- + }
- + }
- + printk("\n");
- + brelse(bh);
- + return 1;
- +}
- +
- +#endif /* CONFIG_MSDOS_PARTITION */
- +
- +/* 'ARM hard drives' minor allocation:
- + * physical hd = minor >> hd->minor_shift
- + * image file = (minor-1) & 3
- + * extra file = image file + n<<2
- + */
- +
- +extern int image_allocate_list(int dev,int minor,struct hd_struct *);
- +extern void image_release_dev(int dev);
- +
- +static void pc_check_hd(struct gendisk *hd, int minor)
- +{ /* minor is the whole device minor */
- + int dev = (hd->major << 8)+minor;
- + int i;
- + struct buffer_head *bh;
- + struct partition *p;
- +
- + minor+=4;
- +#define GET_WORD(n) (((unsigned char *)&n)[0] + (((unsigned char *)&n)[1] << 8) + \
- + (((unsigned char *)&n)[2] << 16) + (((unsigned char *)&n)[3] << 24))
- +
- + if (!(bh = bread(dev,0,1024))) {
- + printk(" unable to read partition table of device %04x\n",dev);
- + return;
- + }
- + if (*(unsigned short *) (bh->b_data + 510) == 0xAA55) {
- + printk(" <");
- + p = (struct partition *) (0x1BE + bh->b_data);
- + for (i=1 ; i<=4 ; minor+=4,i++,p++) {
- + if (!(hd->part[minor].nr_sects = GET_WORD(p->nr_sects)))
- + continue;
- + hd->part[minor].start_sect = GET_WORD(p->start_sect);
- + print_minor_name (hd, minor);
- + }
- + printk(" >");
- + }
- +#undef GET_WORD
- +}
- +
- +static int arm_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector)
- +{
- + int i, minor = current_minor;
- +
- + current_minor += 4;
- +
- + for (i = 1; i <= 4; minor++, i++) {
- + int len;
- + len = image_allocate_list (dev, minor, &hd->part[minor]);
- + if (len) {
- + print_minor_name(hd, minor);
- + pc_check_hd(hd, minor);
- + }
- + }
- + printk("\n");
- + image_release_dev (dev);
- + return 1;
- +}
- +
- +static void check_partition(struct gendisk *hd, kdev_t dev)
- +{
- + static int first_time = 1;
- + unsigned long first_sector;
- + struct buffer_head *bh;
- +
- + if (first_time)
- + printk("Partition check:\n");
- + first_time = 0;
- + first_sector = hd->part[MINOR(dev)].start_sect;
- +
- + /*
- + * This is a kludge to allow the partition check to be
- + * skipped for specific drives (e.g. IDE cd-rom drives)
- + */
- + if ((int)first_sector == -1) {
- + hd->part[MINOR(dev)].start_sect = 0;
- + return;
- + }
- +
- + /*
- + * kludge - read a k of data from the drive so that hd.c can
- + * print its bits
- + */
- + bh = bread (dev, 0, 1024);
- + brelse (bh);
- +
- + printk(" ");
- + print_minor_name(hd, MINOR(dev));
- +
- +#ifdef CONFIG_MSDOS_PARTITION
- + if (msdos_partition(hd, dev, first_sector))
- + return;
- +#endif
- + if (arm_partition(hd, dev, first_sector))
- + return;
- +
- + printk(" unknown partition table\n");
- +}
- +
- +/* This function is used to re-read partition tables for removable disks.
- + Much of the cleanup from the old partition tables should have already been
- + done */
- +
- +/* This function will re-read the partition tables for a given device,
- +and set things back up again. There are some important caveats,
- +however. You must ensure that no one is using the device, and no one
- +can start using the device while this function is being executed. */
- +
- +void resetup_one_dev(struct gendisk *dev, int drive)
- +{
- + int i;
- + int first_minor = drive << dev->minor_shift;
- + int end_minor = first_minor + dev->max_p;
- +
- + blk_size[dev->major] = NULL;
- + current_minor = 1 + first_minor;
- + check_partition(dev, MKDEV(dev->major, first_minor));
- +
- + /*
- + * We need to set the sizes array before we will be able to access
- + * any of the partitions on this device.
- + */
- + if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
- + for (i = first_minor; i < end_minor; i++)
- + dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
- + blk_size[dev->major] = dev->sizes;
- + }
- +}
- +
- +static void setup_dev(struct gendisk *dev)
- +{
- + int i, drive;
- + int end_minor = dev->max_nr * dev->max_p;
- +
- + blk_size[dev->major] = NULL;
- + for (i = 0 ; i < end_minor; i++) {
- + dev->part[i].start_sect = 0;
- + dev->part[i].nr_sects = 0;
- + }
- + dev->init(dev);
- + for (drive=0 ; drive < dev->nr_real ; drive++) {
- + int first_minor = drive << dev->minor_shift;
- + current_minor = 1 + first_minor;
- + check_partition(dev, MKDEV(dev->major, first_minor));
- + }
- + if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
- + for (i = 0 ; i < end_minor; i++)
- + dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
- + blk_size[dev->major] = dev->sizes;
- + }
- +}
- +
- +void device_setup(void)
- +{
- + extern void console_map_init(void);
- + struct gendisk *p;
- + int nr=0;
- +
- + chr_dev_init();
- + blk_dev_init();
- + sti();
- +#ifdef CONFIG_SCSI
- + scsi_dev_init();
- +#endif
- +#ifdef CONFIG_INET
- + net_dev_init();
- +#endif
- + console_map_init();
- +
- + for (p = gendisk_head ; p ; p=p->next) {
- + setup_dev(p);
- + nr += p->nr_real;
- + }
- +
- + if (ramdisk_size)
- + rd_load();
- +}
- diff -urNwbB linux/arch/arm/drivers/block/hd.c linux.arm/arch/arm/drivers/block/hd.c
- --- linux/arch/arm/drivers/block/hd.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/hd.c Sun Mar 3 12:24:12 1996
- @@ -0,0 +1,1238 @@
- +/*
- + * linux/arch/arm/drivers/block/hd.c
- + * [ origional file: linux/drivers/block/hd.c ]
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + * Modified 1995 Russell King for ARM.
- + */
- +
- +/*
- + * This is the low-level hd interrupt support. It traverses the
- + * request-list, using interrupts to jump between functions. As
- + * all the functions are called within interrupts, we may not
- + * sleep. Special care is recommended.
- + *
- + * modified by Drew Eckhardt to check nr of hd's from the CMOS.
- + *
- + * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
- + * in the early extended-partition checks and added DM partitions
- + *
- + * IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
- + * and general streamlining by mlord@bnr.ca (Mark Lord).
- + *
- + * Majorly hacked request routines to support image files. (RMK)
- + * I am quite confident about these changes for the ARM, so long as
- + * Multimode is not turned on...
- + */
- +
- +/* This is the maximum number of fragments accessed in one go */
- +int no_hds;
- +#define MAX_FRAGS 16
- +static unsigned long frag_start[MAX_FRAGS]; /* Start of fragments */
- +static unsigned long frag_len[MAX_FRAGS]; /* Fragment length */
- +static int frag_count; /* Number of fragments to go */
- +static int frag_pos; /* Position in fragment arrays */
- +static unsigned int frag_sectors;
- +
- +#define DEFAULT_MULT_COUNT 0 /* set to 0 to disable multiple mode at boot */
- +#define DEFAULT_UNMASK_INTR 0 /* set to 0 to *NOT* unmask irq's more often */
- +
- +#include <asm/irq.h>
- +#include <linux/errno.h>
- +#include <linux/signal.h>
- +#include <linux/sched.h>
- +#include <linux/timer.h>
- +#include <linux/fs.h>
- +#include <linux/kernel.h>
- +#include <linux/hdreg.h>
- +#include <linux/genhd.h>
- +#include <linux/config.h>
- +#include <linux/malloc.h>
- +#include <linux/string.h>
- +#include <linux/ioport.h>
- +
- +#define REALLY_SLOW_IO
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include <asm/segment.h>
- +
- +#define MAJOR_NR HD_MAJOR
- +#include "blk.h"
- +
- +void reissue_request(struct request *current_req);
- +int issue_request(int dev, unsigned int block, unsigned int nsect, struct request *current_req);
- +#undef HD_IRQ
- +#define HD_IRQ 11
- +
- +static int revalidate_hddisk(int, int);
- +
- +#define HD_DELAY 0
- +
- +#define MAX_ERRORS 16 /* Max read/write errors/sector */
- +#define RESET_FREQ 8 /* Reset controller every 8th retry */
- +#define RECAL_FREQ 4 /* Recalibrate every 4th retry */
- +#define MAX_HD 2
- +
- +#define STAT_OK (READY_STAT|SEEK_STAT)
- +#define OK_STATUS(s) (((s)&(STAT_OK|(BUSY_STAT|WRERR_STAT|ERR_STAT)))==STAT_OK)
- +
- +static void recal_intr(void);
- +static void bad_rw_intr(void);
- +
- +static char recalibrate[MAX_HD] = { 0, };
- +static char special_op[MAX_HD] = { 0, };
- +static int access_count[MAX_HD] = {0, };
- +static char busy[MAX_HD] = {0, };
- +static struct wait_queue * busy_wait = NULL;
- +
- +static int reset = 0;
- +static int hd_error = 0;
- +
- +/*
- + * This struct defines the HD's and their types.
- + */
- +struct hd_i_struct {
- + unsigned int head,sect,cyl,wpcom,lzone,ctl;
- + };
- +static struct hd_driveid *hd_ident_info[MAX_HD] = {0, };
- +
- +#ifdef HD_TYPE
- +static struct hd_i_struct hd_info[] = { HD_TYPE };
- +struct hd_i_struct bios_info[] = { HD_TYPE };
- +static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct)));
- +#else
- +static struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };
- +struct hd_i_struct bios_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };
- +static int NR_HD = 0;
- +#endif
- +
- +static struct hd_struct hd[MAX_HD<<6]={{0,0},};
- +static int hd_sizes[MAX_HD<<6] = {0, };
- +static int hd_blocksizes[MAX_HD<<6] = {0, };
- +
- +#if (HD_DELAY > 0)
- +unsigned long last_req;
- +
- +unsigned long read_timer(void)
- +{
- + unsigned long t, flags;
- + int i;
- +
- + save_flags(flags);
- + cli();
- + t = jiffies * 11932;
- + outb_p(0, 0x43);
- + i = inb_p(0x40);
- + i |= inb(0x40) << 8;
- + restore_flags(flags);
- + return(t - i);
- +}
- +#endif
- +
- +void hd_setup(char *str, int *ints)
- +{
- + int hdind = 0;
- +
- + if (ints[0] != 3)
- + return;
- + if (bios_info[0].head != 0)
- + hdind=1;
- + bios_info[hdind].head = hd_info[hdind].head = ints[2];
- + bios_info[hdind].sect = hd_info[hdind].sect = ints[3];
- + bios_info[hdind].cyl = hd_info[hdind].cyl = ints[1];
- + bios_info[hdind].wpcom = hd_info[hdind].wpcom = 0;
- + bios_info[hdind].lzone = hd_info[hdind].lzone = ints[1];
- + bios_info[hdind].ctl = hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
- + NR_HD = hdind+1;
- +}
- +
- +static void dump_status (char *msg, unsigned int stat)
- +{
- + unsigned long flags;
- + char devc;
- +
- + devc = CURRENT ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';
- + save_flags (flags);
- + sti();
- + printk("hd%c: %s: status=0x%02x { ", devc, msg, stat & 0xff);
- + if (stat & BUSY_STAT) printk("Busy ");
- + if (stat & READY_STAT) printk("DriveReady ");
- + if (stat & WRERR_STAT) printk("WriteFault ");
- + if (stat & SEEK_STAT) printk("SeekComplete ");
- + if (stat & DRQ_STAT) printk("DataRequest ");
- + if (stat & ECC_STAT) printk("CorrectedError ");
- + if (stat & INDEX_STAT) printk("Index ");
- + if (stat & ERR_STAT) printk("Error ");
- + printk("}\n");
- + if ((stat & ERR_STAT) == 0) {
- + hd_error = 0;
- + } else {
- + hd_error = inb(HD_ERROR);
- + printk("hd%c: %s: error=0x%02x { ", devc, msg, hd_error & 0xff);
- + if (hd_error & BBD_ERR) printk("BadSector ");
- + if (hd_error & ECC_ERR) printk("UncorrectableError ");
- + if (hd_error & ID_ERR) printk("SectorIdNotFound ");
- + if (hd_error & ABRT_ERR) printk("DriveStatusError ");
- + if (hd_error & TRK0_ERR) printk("TrackZeroNotFound ");
- + if (hd_error & MARK_ERR) printk("AddrMarkNotFound ");
- + printk("}");
- + if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
- + printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL),
- + inb(HD_CURRENT) & 0xf, inb(HD_SECTOR));
- + if (CURRENT)
- + printk(", sector=%ld", CURRENT->sector);
- + }
- + printk("\n");
- + }
- + restore_flags (flags);
- +}
- +
- +void check_status(void)
- +{
- + int i = inb_p(HD_STATUS);
- +
- + if (!OK_STATUS(i)) {
- + dump_status("check_status", i);
- + bad_rw_intr();
- + }
- +}
- +
- +static int controller_busy(void)
- +{
- + int retries = 100000;
- + unsigned char status;
- +
- + do {
- + status = inb_p(HD_STATUS);
- + } while ((status & BUSY_STAT) && --retries);
- + return status;
- +}
- +
- +static int status_ok(void)
- +{
- + unsigned char status = inb_p(HD_STATUS);
- +
- + if (status & BUSY_STAT)
- + return 1; /* Ancient, but does it make sense??? */
- + if (status & WRERR_STAT)
- + return 0;
- + if (!(status & READY_STAT))
- + return 0;
- + if (!(status & SEEK_STAT))
- + return 0;
- + return 1;
- +}
- +
- +static int controller_ready(unsigned int drive, unsigned int head)
- +{
- + int retry = 100;
- +
- + do {
- + if (controller_busy() & BUSY_STAT)
- + return 0;
- + outb_p(0xA0 | (drive<<4) | head, HD_CURRENT);
- + if (status_ok())
- + return 1;
- + } while (--retry);
- + return 0;
- +}
- +
- +static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
- + unsigned int head,unsigned int cyl,unsigned int cmd,
- + void (*intr_addr)(void))
- +{
- + unsigned short port;
- +
- +#if (HD_DELAY > 0)
- + while (read_timer() - last_req < HD_DELAY)
- + /* nothing */;
- +#endif
- + if (reset)
- + return;
- + if (!controller_ready(drive, head)) {
- + reset = 1;
- + return;
- + }
- + SET_INTR(intr_addr);
- + outb_p(hd_info[drive].ctl,HD_CMD);
- + port=HD_DATA;
- + outb_p(hd_info[drive].wpcom>>2,++port);
- + outb_p(nsect,++port);
- + outb_p(sect,++port);
- + outb_p(cyl,++port);
- + outb_p(cyl>>8,++port);
- + outb_p(0xA0|(drive<<4)|head,++port);
- + outb_p(cmd,++port);
- +}
- +
- +static void hd_request (void);
- +static unsigned int identified [MAX_HD] = {0,}; /* 1 = drive ID already displayed */
- +static unsigned int unmask_intr [MAX_HD] = {0,}; /* 1 = unmask IRQs during I/O */
- +static unsigned int max_mult [MAX_HD] = {0,}; /* max sectors for MultMode */
- +static unsigned int mult_req [MAX_HD] = {0,}; /* requested MultMode count */
- +static unsigned int mult_count [MAX_HD] = {0,}; /* currently enabled MultMode count */
- +static struct request WCURRENT;
- +
- +static void fixstring (unsigned char *s, int bytecount)
- +{
- + unsigned char *p, *end = &s[bytecount &= ~1]; /* bytecount must be even */
- +
- + /* convert from big-endian to little-endian */
- + for (p = end ; p != s;) {
- + unsigned short *pp = (unsigned short *) (p -= 2);
- + *pp = (*pp >> 8) | (*pp << 8);
- + }
- +
- + /* strip leading blanks */
- + while (s != end && *s == ' ')
- + ++s;
- +
- + /* compress internal blanks and strip trailing blanks */
- + while (s != end && *s) {
- + if (*s++ != ' ' || (s != end && *s && *s != ' '))
- + *p++ = *(s-1);
- + }
- +
- + /* wipe out trailing garbage */
- + while (p != end)
- + *p++ = '\0';
- +}
- +
- +static void identify_intr(void)
- +{
- + unsigned int dev = DEVICE_NR(CURRENT->rq_dev);
- + unsigned short stat = inb_p(HD_STATUS);
- + struct hd_driveid *id = hd_ident_info[dev];
- +
- + if (unmask_intr[dev])
- + sti();
- + if (stat & (BUSY_STAT|ERR_STAT)) {
- + printk (" hd%c: non-IDE device, %dMB, CHS=%d/%d/%d\n", dev+'a',
- + hd_info[dev].cyl*hd_info[dev].head*hd_info[dev].sect / 2048,
- + hd_info[dev].cyl, hd_info[dev].head, hd_info[dev].sect);
- + if (id != NULL) {
- + hd_ident_info[dev] = NULL;
- + kfree_s (id, 512);
- + }
- + } else {
- + insw(HD_DATA, id, 256); /* get ID info */
- + max_mult[dev] = id->max_multsect;
- + if ((id->field_valid&1) && id->cur_cyls && id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) {
- + /*
- + * Extract the physical drive geometry for our use.
- + * Note that we purposely do *not* update the bios_info.
- + * This way, programs that use it (like fdisk) will
- + * still have the same logical view as the BIOS does,
- + * which keeps the partition table from being screwed.
- + */
- + hd_info[dev].cyl = id->cur_cyls;
- + hd_info[dev].head = id->cur_heads;
- + hd_info[dev].sect = id->cur_sectors;
- + }
- + fixstring (id->serial_no, sizeof(id->serial_no));
- + fixstring (id->fw_rev, sizeof(id->fw_rev));
- + fixstring (id->model, sizeof(id->model));
- + printk (" hd%c: %.40s, %dMB w/%dKB Cache, CHS=%d/%d/%d, MaxMult=%d\n",
- + dev+'a', id->model, id->cyls*id->heads*id->sectors/2048,
- + id->buf_size/2, hd_info[dev].cyl, hd_info[dev].head,
- + hd_info[dev].sect, id->max_multsect);
- + /*
- + * Early model Quantum drives go weird at this point,
- + * but doing a recalibrate seems to "fix" them.
- + * (Doing a full reset confuses some other model Quantums)
- + */
- + if (!strncmp(id->model, "QUANTUM", 7))
- + special_op[dev] = recalibrate[dev] = 1;
- + }
- +#if (HD_DELAY > 0)
- + last_req = read_timer();
- +#endif
- + hd_request();
- + return;
- +}
- +
- +static void set_multmode_intr(void)
- +{
- + unsigned int dev = DEVICE_NR(CURRENT->rq_dev), stat = inb_p(HD_STATUS);
- +
- + if (unmask_intr[dev])
- + sti();
- + if (stat & (BUSY_STAT|ERR_STAT)) {
- + mult_req[dev] = mult_count[dev] = 0;
- + dump_status("set multmode failed", stat);
- + } else {
- + if ((mult_count[dev] = mult_req[dev]))
- + printk (" hd%c: enabled %d-sector multiple mode\n",
- + dev+'a', mult_count[dev]);
- + else
- + printk (" hd%c: disabled multiple mode\n", dev+'a');
- + }
- +#if (HD_DELAY > 0)
- + last_req = read_timer();
- +#endif
- + hd_request();
- + return;
- +}
- +
- +static int drive_busy(void)
- +{
- + unsigned int i;
- + unsigned char c;
- +
- + for (i = 0; i < 500000 ; i++) {
- + c = inb_p(HD_STATUS);
- + if ((c & (BUSY_STAT | READY_STAT | SEEK_STAT)) == STAT_OK)
- + return 0;
- + }
- + dump_status("reset timed out", c);
- + return 1;
- +}
- +
- +static void reset_controller(void)
- +{
- + int i;
- +
- + outb_p(4,HD_CMD);
- + for(i = 0; i < 1000; i++) nop();
- + outb_p(hd_info[0].ctl & 0x0f,HD_CMD);
- + for(i = 0; i < 1000; i++) nop();
- + if (drive_busy())
- + printk("hd: controller still busy\n");
- + else if ((hd_error = inb(HD_ERROR)) != 1)
- + printk("hd: controller reset failed: %02x\n",hd_error);
- +}
- +
- +static void reset_hd(void)
- +{
- + static int i;
- +
- +repeat:
- + if (reset) {
- + reset = 0;
- + i = -1;
- + reset_controller();
- + } else {
- + check_status();
- + if (reset)
- + goto repeat;
- + }
- + if (++i < NR_HD) {
- + special_op[i] = recalibrate[i] = 1;
- + if (unmask_intr[i]) {
- + unmask_intr[i] = DEFAULT_UNMASK_INTR;
- + printk("hd%c: reset irq-unmasking to %d\n",i+'a',
- + DEFAULT_UNMASK_INTR);
- + }
- + if (mult_req[i] || mult_count[i]) {
- + mult_count[i] = 0;
- + mult_req[i] = DEFAULT_MULT_COUNT;
- + printk("hd%c: reset multiple mode to %d\n",i+'a',
- + DEFAULT_MULT_COUNT);
- + }
- + hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
- + hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
- + if (reset)
- + goto repeat;
- + } else
- + hd_request();
- +}
- +
- +/*
- + * Ok, don't know what to do with the unexpected interrupts: on some machines
- + * doing a reset and a retry seems to result in an eternal loop. Right now I
- + * ignore it, and just set the timeout.
- + *
- + * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
- + * drive enters "idle", "standby", or "sleep" mode, so if the status looks
- + * "good", we just ignore the interrupt completely.
- + */
- +void unexpected_hd_interrupt(void)
- +{
- + unsigned int stat = inb_p(HD_STATUS);
- +
- + if (stat & (BUSY_STAT|DRQ_STAT|ECC_STAT|ERR_STAT)) {
- + dump_status ("unexpected interrupt", stat);
- + SET_TIMER;
- + }
- +}
- +
- +/*
- + * bad_rw_intr() now tries to be a bit smarter and does things
- + * according to the error returned by the controller.
- + * -Mika Liljeberg (liljeber@cs.Helsinki.FI)
- + */
- +static void bad_rw_intr(void)
- +{
- + int dev;
- +
- + if (!CURRENT)
- + return;
- + dev = DEVICE_NR(CURRENT->rq_dev);
- + if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
- + end_request(0);
- + special_op[dev] = recalibrate[dev] = 1;
- + } else if (CURRENT->errors % RESET_FREQ == 0)
- + reset = 1;
- + else if ((hd_error & TRK0_ERR) || CURRENT->errors % RECAL_FREQ == 0)
- + special_op[dev] = recalibrate[dev] = 1;
- + /* Otherwise just retry */
- +}
- +
- +static inline int wait_DRQ(void)
- +{
- + int retries = 100000, stat;
- +
- + while (--retries > 0)
- + if ((stat = inb_p(HD_STATUS)) & DRQ_STAT)
- + return 0;
- + dump_status("wait_DRQ", stat);
- + return -1;
- +}
- +
- +static void read_intr(void)
- +{
- + unsigned int dev = DEVICE_NR(CURRENT->rq_dev);
- + int i, retries = 100000, msect = mult_count[dev], nsect;
- +
- + if (unmask_intr[dev])
- + sti(); /* permit other IRQs during xfer */
- + do {
- + i = (unsigned) inb_p(HD_STATUS);
- + if (i & BUSY_STAT)
- + continue;
- + if (!OK_STATUS(i))
- + break;
- + if (i & DRQ_STAT)
- + goto ok_to_read;
- + } while (--retries > 0);
- + dump_status("read_intr", i);
- + bad_rw_intr();
- + hd_request();
- + return;
- +ok_to_read:
- + if (msect) {
- + if ((nsect = CURRENT->current_nr_sectors) > msect)
- + nsect = msect;
- + msect -= nsect;
- + } else
- + nsect = 1;
- + insw(HD_DATA,CURRENT->buffer,nsect<<8);
- + CURRENT->sector += nsect;
- + CURRENT->buffer += nsect<<9;
- + CURRENT->errors = 0;
- + i = (CURRENT->nr_sectors -= nsect);
- +
- +#ifdef DEBUG
- + printk("hd%c: read: sectors(%ld-%ld), remaining=%ld, buffer=0x%08lx\n",
- + dev+'a', CURRENT->sector, CURRENT->sector+nsect,
- + CURRENT->nr_sectors, (unsigned long) CURRENT->buffer+(nsect<<9));
- +#endif
- + if ((CURRENT->current_nr_sectors -= nsect) <= 0)
- + end_request(1);
- + if (i > 0) {
- + if (msect)
- + goto ok_to_read;
- + if(!(--frag_sectors))
- + /* Next fragment req.d */
- + reissue_request(CURRENT);
- + else
- + {
- + SET_INTR(&read_intr);
- + }
- + return;
- + }
- + (void) inb_p(HD_STATUS);
- +#if (HD_DELAY > 0)
- + last_req = read_timer();
- +#endif
- + if (CURRENT)
- + hd_request();
- + return;
- +}
- +
- +static inline void multwrite (unsigned int dev)
- +{
- + unsigned int mcount = mult_count[dev];
- +
- + while (mcount--) {
- + outsw(HD_DATA,WCURRENT.buffer,256);
- + if (!--WCURRENT.nr_sectors)
- + return;
- + WCURRENT.buffer += 512;
- + if (!--WCURRENT.current_nr_sectors) {
- + WCURRENT.bh = WCURRENT.bh->b_reqnext;
- + if (WCURRENT.bh == NULL)
- + panic("buffer list corrupted\n");
- + WCURRENT.current_nr_sectors = WCURRENT.bh->b_size>>9;
- + WCURRENT.buffer = WCURRENT.bh->b_data;
- + }
- + }
- +}
- +
- +static void multwrite_intr(void)
- +{
- + int i;
- + unsigned int dev = DEVICE_NR(WCURRENT.rq_dev);
- +
- + if (unmask_intr[dev])
- + sti();
- + if (OK_STATUS(i=inb_p(HD_STATUS))) {
- + if (i & DRQ_STAT) {
- + if (WCURRENT.nr_sectors) {
- + multwrite(dev);
- + SET_INTR(&multwrite_intr);
- + return;
- + }
- + } else {
- + if (!WCURRENT.nr_sectors) { /* all done? */
- + for (i = CURRENT->nr_sectors; i > 0;){
- + i -= CURRENT->current_nr_sectors;
- + end_request(1);
- + }
- +#if (HD_DELAY > 0)
- + last_req = read_timer();
- +#endif
- + if (CURRENT)
- + hd_request();
- + return;
- + }
- + }
- + }
- + dump_status("multwrite_intr", i);
- + bad_rw_intr();
- + hd_request();
- +}
- +
- +static void write_intr(void)
- +{
- + int i;
- + int retries = 100000;
- +
- + if (unmask_intr[DEVICE_NR(WCURRENT.rq_dev)])
- + sti();
- + do {
- + i = (unsigned) inb_p(HD_STATUS);
- + if (i & BUSY_STAT)
- + continue;
- + if (!OK_STATUS(i))
- + break;
- + if ((CURRENT->nr_sectors <= 1) || (frag_sectors <= 1) || (i & DRQ_STAT))
- + goto ok_to_write;
- + } while (--retries > 0);
- + dump_status("write_intr", i);
- + bad_rw_intr();
- + hd_request();
- + return;
- +ok_to_write:
- + CURRENT->sector++;
- + i = --CURRENT->nr_sectors;
- + --CURRENT->current_nr_sectors;
- + CURRENT->buffer += 512;
- + if (!i || (CURRENT->bh && !SUBSECTOR(i)))
- + end_request(1);
- + if (i > 0) {
- + if(!(--frag_sectors))
- + /* Next fragment req.d */
- + reissue_request(CURRENT);
- + else
- + {
- + SET_INTR(&write_intr);
- + outsw(HD_DATA,CURRENT->buffer,256);
- + sti();
- + }
- + } else {
- +#if (HD_DELAY > 0)
- + last_req = read_timer();
- +#endif
- + hd_request();
- + }
- + return;
- +}
- +
- +static void recal_intr(void)
- +{
- + check_status();
- +#if (HD_DELAY > 0)
- + last_req = read_timer();
- +#endif
- + hd_request();
- +}
- +
- +/*
- + * This is another of the error-routines I don't know what to do with. The
- + * best idea seems to just set reset, and start all over again.
- + */
- +static void hd_times_out(void)
- +{
- + unsigned int dev;
- +
- + DEVICE_INTR = NULL;
- + if (!CURRENT)
- + return;
- + disable_irq(HD_IRQ);
- + sti();
- + reset = 1;
- + dev = DEVICE_NR(CURRENT->rq_dev);
- + printk("hd%c: timeout\n", dev+'a');
- + if (++CURRENT->errors >= MAX_ERRORS) {
- +#ifdef DEBUG
- + printk("hd%c: too many errors\n", dev+'a');
- +#endif
- + end_request(0);
- + }
- + cli();
- + hd_request();
- + enable_irq(HD_IRQ);
- +}
- +
- +int do_special_op (unsigned int dev)
- +{
- + if (recalibrate[dev]) {
- + recalibrate[dev] = 0;
- + hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
- + return reset;
- + }
- + if (!identified[dev]) {
- + identified[dev] = 1;
- + unmask_intr[dev] = DEFAULT_UNMASK_INTR;
- + mult_req[dev] = DEFAULT_MULT_COUNT;
- + hd_out(dev,0,0,0,0,WIN_IDENTIFY,&identify_intr);
- + return reset;
- + }
- + if (mult_req[dev] != mult_count[dev]) {
- + hd_out(dev,mult_req[dev],0,0,0,WIN_SETMULT,&set_multmode_intr);
- + return reset;
- + }
- + if (hd_info[dev].head > 16) {
- + printk ("hd%c: cannot handle device with more than 16 heads - giving up\n", dev+'a');
- + end_request(0);
- + }
- + special_op[dev] = 0;
- + return 1;
- +}
- +
- +/*
- + * The driver enables interrupts as much as possible. In order to do this,
- + * (a) the device-interrupt is disabled before entering hd_request(),
- + * and (b) the timeout-interrupt is disabled before the sti().
- + *
- + * Interrupts are still masked (by default) whenever we are exchanging
- + * data/cmds with a drive, because some drives seem to have very poor
- + * tolerance for latency during I/O. For devices which don't suffer from
- + * that problem (most don't), the unmask_intr[] flag can be set to unmask
- + * other interrupts during data/cmd transfers (by defining DEFAULT_UNMASK_INTR
- + * to 1, or by using "hdparm -u1 /dev/hd?" from the shell).
- + */
- +static void hd_request(void)
- +{
- + unsigned int dev, block, nsect;
- +
- + if (CURRENT && CURRENT->rq_status == RQ_INACTIVE) return;
- + if (DEVICE_INTR)
- + return;
- +repeat:
- + timer_active &= ~(1<<HD_TIMER);
- + sti();
- + INIT_REQUEST;
- + if (reset) {
- + cli();
- + reset_hd();
- + return;
- + }
- + dev = MINOR(CURRENT->rq_dev);
- + block = CURRENT->sector;
- + nsect = CURRENT->nr_sectors;
- + if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects || ((block+nsect) > hd[dev].nr_sects)) {
- +#ifdef DEBUG
- + if (dev >= (NR_HD<<6))
- + printk("hd: bad minor number: device=0x%04x\n", CURRENT->rq_dev);
- + else
- + printk("hd%c: bad access: block=%d, count=%d\n",
- + (dev>>6)+'a', block, nsect);
- +#endif
- + end_request(0);
- + goto repeat;
- + }
- + block += hd[dev].start_sect;
- + if(dev & 0x3f)
- + { /* map sectors block to block+nsect to frags,
- + */
- + if(!image_file_check(CURRENT->rq_dev,CURRENT->cmd))
- + goto repeat;
- + frag_count = image_file_map(CURRENT->rq_dev, block, nsect, MAX_FRAGS,
- + frag_start, frag_len);
- + if(!frag_count)
- + {
- + goto repeat;
- + }
- +
- + block = frag_start[0];
- + frag_sectors = nsect = frag_len[0];
- + frag_pos = 0;
- + }
- + else
- + {
- + frag_pos = 0;
- + frag_count = 1;
- + frag_sectors = nsect;
- + if(CURRENT->cmd == WRITE)
- + { /* Protect normal HD from writes */
- + printk("hd%d : attempted write on protected drive\n",dev);
- + end_request(0);
- + goto repeat;
- + }
- + }
- + if(issue_request(dev,block,nsect, CURRENT))
- + goto repeat;
- +}
- +
- +void reissue_request(struct request *current_req)
- +{
- + unsigned int dev, block, nsect;
- +
- + dev = MINOR(current_req->rq_dev);
- + frag_pos++;
- + if(frag_pos >= MAX_FRAGS)
- + {
- + block = current_req->sector;
- + block += hd[dev].start_sect;
- + nsect = current_req->nr_sectors;
- + if(!image_file_check(current_req->rq_dev,current_req->cmd))
- + {
- + end_request(0);
- + hd_request();
- + return;
- + }
- + frag_count = image_file_map(current_req->rq_dev, block, nsect, MAX_FRAGS,
- + frag_start, frag_len);
- + if(!frag_count)
- + {
- + end_request(0);
- + hd_request();
- + return;
- + }
- +
- + frag_pos = 0;
- + }
- + frag_sectors = frag_len[frag_pos];
- +repeat:
- + if(issue_request(dev, frag_start[frag_pos], frag_len[frag_pos], current_req))
- + {
- + printk("EEEEK - going round the loop\n");
- + goto repeat;
- + }
- +}
- +
- +/*
- + * This routine actually issues a request to the hard disk itself
- + */
- +int issue_request(int dev, unsigned int block, unsigned int nsect, struct request *current_req)
- +{
- + unsigned int sec, track, head, cyl;
- +int d = dev;
- + dev >>= 6;
- + if (special_op[dev]) {
- + if (do_special_op(dev))
- + return 1;
- + return 0;
- + }
- + sec = block % hd_info[dev].sect + 1;
- + track = block / hd_info[dev].sect;
- + head = track % hd_info[dev].head;
- + cyl = track / hd_info[dev].head;
- +#ifdef DEBUG
- + printk("hd%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx (%p)\n",
- + dev+'a', (current_req->cmd == READ)?"read":"writ",
- + cyl, head, sec, nsect, (unsigned long) current_req->buffer,current_req);
- +#endif
- + frag_sectors = nsect;
- + if (!unmask_intr[dev])
- + cli();
- + if (current_req->cmd == READ) {
- + unsigned int cmd = mult_count[dev] > 1 ? WIN_MULTREAD : WIN_READ;
- + hd_out(dev,nsect,sec,head,cyl,cmd,&read_intr);
- + if (reset)
- + return 1;
- + return 0;
- + }
- + if (current_req->cmd == WRITE) {
- +#if 1
- +#if 0
- + if (mult_count[dev])
- + hd_out(dev,nsect,sec,head,cyl,WIN_MULTWRITE,&multwrite_intr);
- + else
- +#endif
- + hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
- + if (reset)
- + return 1;
- + if (wait_DRQ()) {
- + bad_rw_intr();
- + return 1;
- + }
- +#if 0
- + if (mult_count[dev]) {
- + WCURRENT = *current;
- + multwrite(dev);
- + } else
- +#endif
- + outsw(HD_DATA,current_req->buffer,256);
- +if(d == 1 && block < 10) while(1);
- +#else
- +if(d == 1)
- + printk("write: buffer = %p (%08lX)\n", current_req->buffer, *(unsigned long *)current_req->buffer);
- +{int i; for(i=0; i<0x003fffff; i++); }
- + end_request(0);
- + return 1;
- +#endif
- + return 0;
- + }
- + panic("unknown hd-command");
- + return 0;
- +}
- +
- +static void do_hd_request (void)
- +{
- + disable_irq(HD_IRQ);
- + hd_request();
- + enable_irq(HD_IRQ);
- +}
- +
- +static int hd_ioctl(struct inode * inode, struct file * file,
- + unsigned int cmd, unsigned long arg)
- +{
- + struct hd_geometry *loc = (struct hd_geometry *) arg;
- + int dev, err;
- + unsigned long flags;
- +
- + if ((!inode) || (!inode->i_rdev))
- + return -EINVAL;
- + dev = DEVICE_NR(inode->i_rdev);
- + if (dev >= NR_HD)
- + return -EINVAL;
- + switch (cmd) {
- + case HDIO_GETGEO:
- + if (!loc) return -EINVAL;
- + err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
- + if (err)
- + return err;
- + put_fs_byte(bios_info[dev].head,
- + (char *) &loc->heads);
- + put_fs_byte(bios_info[dev].sect,
- + (char *) &loc->sectors);
- + put_fs_word(bios_info[dev].cyl,
- + (short *) &loc->cylinders);
- + put_fs_long(hd[MINOR(inode->i_rdev)].start_sect,
- + (long *) &loc->start);
- + return 0;
- + case BLKRASET:
- + if(!suser()) return -EACCES;
- + if(arg > 0xff) return -EINVAL;
- + read_ahead[MAJOR(inode->i_rdev)] = arg;
- + return 0;
- + case BLKRAGET:
- + if (!arg) return -EINVAL;
- + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
- + if (err)
- + return err;
- + put_fs_long(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
- + return 0;
- + case BLKGETSIZE: /* Return device size */
- + if (!arg) return -EINVAL;
- + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
- + if (err)
- + return err;
- + put_fs_long(hd[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
- + return 0;
- + case BLKFLSBUF:
- + if(!suser()) return -EACCES;
- + fsync_dev(inode->i_rdev);
- + invalidate_buffers(inode->i_rdev);
- + return 0;
- +
- + case BLKRRPART: /* Re-read partition tables */
- + return revalidate_hddisk(inode->i_rdev, 1);
- +
- + case HDIO_SET_UNMASKINTR:
- + if (!suser()) return -EACCES;
- + if ((arg > 1) || (MINOR(inode->i_rdev) & 0x3F))
- + return -EINVAL;
- + unmask_intr[dev] = arg;
- + return 0;
- +
- + case HDIO_GET_UNMASKINTR:
- + if (!arg) return -EINVAL;
- + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
- + if (err)
- + return err;
- + put_fs_long(unmask_intr[dev], (long *) arg);
- + return 0;
- +
- + case HDIO_GET_MULTCOUNT:
- + if (!arg) return -EINVAL;
- + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
- + if (err)
- + return err;
- + put_fs_long(mult_count[dev], (long *) arg);
- + return 0;
- +
- + case HDIO_SET_MULTCOUNT:
- + if (!suser()) return -EACCES;
- + if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL;
- + save_flags(flags);
- + cli(); /* a prior request might still be in progress */
- + if (arg > max_mult[dev])
- + err = -EINVAL; /* out of range for device */
- + else if (mult_req[dev] != mult_count[dev]) {
- + special_op[dev] = 1;
- + err = -EBUSY; /* busy, try again */
- + } else {
- + mult_req[dev] = arg;
- + special_op[dev] = 1;
- + err = 0;
- + }
- + restore_flags(flags);
- + return err;
- +
- + case HDIO_GET_IDENTITY:
- + if (!arg) return -EINVAL;
- + if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL;
- + if (hd_ident_info[dev] == NULL) return -ENOMSG;
- + err = verify_area(VERIFY_WRITE, (char *) arg, sizeof(struct hd_driveid));
- + if (err)
- + return err;
- + memcpy_tofs((char *)arg, (char *) hd_ident_info[dev], sizeof(struct hd_driveid));
- + return 0;
- +
- + RO_IOCTLS(inode->i_rdev,arg);
- + default:
- + return -EINVAL;
- + }
- +}
- +
- +static int hd_open(struct inode * inode, struct file * filp)
- +{
- + int target;
- +
- + target = DEVICE_NR(inode->i_rdev);
- +
- + if (target >= NR_HD)
- + return -ENODEV;
- +
- + while (busy[target])
- + sleep_on(&busy_wait);
- + access_count[target]++;
- + return 0;
- +}
- +
- +/*
- + * Releasing a block device means we sync() it, so that it can safely
- + * be forgotten about...
- + */
- +static void hd_release(struct inode * inode, struct file * file)
- +{
- + int target;
- + sync_dev(inode->i_rdev);
- +
- + target = DEVICE_NR(inode->i_rdev);
- + access_count[target]--;
- +
- +}
- +
- +static void hd_geninit(struct gendisk *);
- +
- +static struct gendisk hd_gendisk = {
- + MAJOR_NR, /* Major number */
- + "hd", /* Major name */
- + 6, /* Bits to shift to get real from partition */
- + 1 << 6, /* Number of partitions per real */
- + MAX_HD, /* maximum number of real */
- + hd_geninit, /* init function */
- + hd, /* hd struct */
- + hd_sizes, /* block sizes */
- + 0, /* number */
- + (void *) bios_info, /* internal */
- + NULL /* next */
- +};
- +
- +static void hd_interrupt(int irq, struct pt_regs *regs)
- +{
- + void (*handler)(void) = DEVICE_INTR;
- +
- + DEVICE_INTR = NULL;
- + timer_active &= ~(1<<HD_TIMER);
- + if (!handler)
- + handler = unexpected_hd_interrupt;
- + handler();
- + sti();
- +}
- +
- +
- +void set_hdinfo(int dev,unsigned char secsptrack,unsigned char heads,
- + unsigned long discsize,unsigned int secsize)
- +{
- + dev=MINOR(dev);
- + if(hd_info[dev>>6].cyl==1)
- + {
- + hd_info[dev>>6].cyl=discsize/(secsptrack*heads*secsize);
- + hd_info[dev>>6].head=heads;
- + hd_info[dev>>6].wpcom=-1;
- + hd_info[dev>>6].ctl=8;
- + hd_info[dev>>6].lzone=hd_info[dev>>6].cyl-1;
- + hd_info[dev>>6].sect=secsptrack;
- + }
- + hd[dev].start_sect=0;
- + hd[dev].nr_sects=discsize/secsize;
- +#if 0
- + printk("hd%c: %d cylinders, %d heads, %d sectors (%d total)\n",'a'+(dev>>6),
- + hd_info[dev>>6].cyl, heads, secsptrack, discsize);
- +#endif
- +}
- +
- +/*
- + * This is the harddisk IRQ description. The SA_INTERRUPT in sa_flags
- + * means we run the IRQ-handler with interrupts disabled: this is bad for
- + * interrupt latency, but anything else has led to problems on some
- + * machines...
- + *
- + * We enable interrupts in some of the routines after making sure it's
- + * safe.
- + */
- +static void hd_geninit(struct gendisk *dev)
- +{
- + int drive, i;
- +
- +NR_HD = 0;
- + if (!NR_HD) {
- + /* Default settings */
- + for (drive=0 ; drive<2 ; drive++) {
- + bios_info[drive].cyl = hd_info[drive].cyl = 1;
- + bios_info[drive].head = hd_info[drive].head = 1;
- + bios_info[drive].wpcom = hd_info[drive].wpcom = -1;
- + bios_info[drive].ctl = hd_info[drive].ctl = 8;
- + bios_info[drive].lzone = hd_info[drive].lzone = 1;
- + bios_info[drive].sect = hd_info[drive].sect = 17;
- + if(hd_info[drive].cyl && NR_HD == drive)
- + NR_HD++;
- + }
- + }
- +
- + NR_HD = no_hds;
- +
- + i = NR_HD;
- + while (i-- > 0) {
- + /*
- + * The newer E-IDE BIOSs handle drives larger than 1024
- + * cylinders by increasing the number of logical heads
- + * to keep the number of logical cylinders below the
- + * sacred INT13 limit of 1024 (10 bits). If that is
- + * what's happening here, we'll find out and correct
- + * it later when "identifying" the drive.
- + */
- + hd[i<<6].nr_sects = bios_info[i].head *
- + bios_info[i].sect * bios_info[i].cyl;
- + hd_ident_info[i] = (struct hd_driveid *) kmalloc(512,GFP_KERNEL);
- + special_op[i] = 1;
- + }
- + if (NR_HD) {
- + if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd")) {
- + printk("hd: unable to get IRQ%d for the harddisk driver\n",HD_IRQ);
- + NR_HD = 0;
- + } else {
- + request_region(HD_DATA, 8, "hd");
- + request_region(HD_CMD, 1, "hd(cmd)");
- + }
- + }
- + hd_gendisk.nr_real = NR_HD;
- +
- + for(i=0;i<(MAX_HD << 6);i++) hd_blocksizes[i] = 1024;
- + blksize_size[MAJOR_NR] = hd_blocksizes;
- +}
- +
- +static struct file_operations hd_fops = {
- + NULL, /* lseek - default */
- + block_read, /* read - general block-dev read */
- + block_write, /* write - general block-dev write */
- + NULL, /* readdir - bad */
- + NULL, /* select */
- + hd_ioctl, /* ioctl */
- + NULL, /* mmap */
- + hd_open, /* open */
- + hd_release, /* release */
- + block_fsync /* fsync */
- +};
- +
- +int hd_init(void)
- +{
- + if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
- + printk("hd: unable to get major %d for harddisk\n",MAJOR_NR);
- + return -1;
- + }
- + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */
- + hd_gendisk.next = gendisk_head;
- + gendisk_head = &hd_gendisk;
- + timer_table[HD_TIMER].fn = hd_times_out;
- + return 0;
- +}
- +
- +#define DEVICE_BUSY busy[target]
- +#define USAGE access_count[target]
- +#define CAPACITY (bios_info[target].head*bios_info[target].sect*bios_info[target].cyl)
- +/* We assume that the the bios parameters do not change, so the disk capacity
- + will not change */
- +#undef MAYBE_REINIT
- +#define GENDISK_STRUCT hd_gendisk
- +
- +/*
- + * This routine is called to flush all partitions and partition tables
- + * for a changed scsi disk, and then re-read the new partition table.
- + * If we are revalidating a disk because of a media change, then we
- + * enter with usage == 0. If we are using an ioctl, we automatically have
- + * usage == 1 (we need an open channel to use an ioctl :-), so this
- + * is our limit.
- + */
- +static int revalidate_hddisk(int dev, int maxusage)
- +{
- + int target, major;
- + struct gendisk * gdev;
- + int max_p;
- + int start;
- + int i;
- + unsigned long flags;
- +
- + target = DEVICE_NR(dev);
- + gdev = &GENDISK_STRUCT;
- +
- + save_flags(flags);
- + cli();
- + if (DEVICE_BUSY || USAGE > maxusage) {
- + restore_flags(flags);
- + return -EBUSY;
- + };
- + DEVICE_BUSY = 1;
- + restore_flags(flags);
- +
- + max_p = gdev->max_p;
- + start = target << gdev->minor_shift;
- + major = MAJOR_NR << 8;
- +
- + for (i=max_p - 1; i >=0 ; i--) {
- + sync_dev(major | start | i);
- + invalidate_inodes(major | start | i);
- + invalidate_buffers(major | start | i);
- + gdev->part[start+i].start_sect = 0;
- + gdev->part[start+i].nr_sects = 0;
- + };
- +
- +#ifdef MAYBE_REINIT
- + MAYBE_REINIT;
- +#endif
- +
- + gdev->part[start].nr_sects = CAPACITY;
- + resetup_one_dev(gdev, target);
- +
- + DEVICE_BUSY = 0;
- + wake_up(&busy_wait);
- + return 0;
- +}
- +
- diff -urNwbB linux/arch/arm/drivers/block/hdsrch.c linux.arm/arch/arm/drivers/block/hdsrch.c
- --- linux/arch/arm/drivers/block/hdsrch.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/hdsrch.c Sun Mar 3 12:29:41 1996
- @@ -0,0 +1,665 @@
- +/*
- + * linux/arch/arm/drivers/block/hdsrch.c
- + *
- + * Copyright (C) 1995, 1996 Russell King
- + *
- + * Find image files on ADFS hard drives.
- + * This currently only supports new map, 77 entry directory hard disks... (E format).
- + */
- +
- +#include <linux/config.h>
- +#include <linux/ctype.h>
- +#include <linux/kernel.h>
- +#include <linux/mm.h>
- +#include <linux/malloc.h>
- +#include <linux/string.h>
- +#include <linux/genhd.h>
- +
- +#define RECSIZE 60
- +
- +#define MAX_BLK_FRAGS 64
- +
- +extern void print_minor_name (struct gendisk *hd, int minor);
- +
- +struct boot_block
- +{
- + unsigned char log2secsize;
- + unsigned char secspertrack;
- + unsigned char heads;
- + unsigned char density;
- + unsigned char idlen;
- + unsigned char log2bpmb;
- + unsigned char shew;
- + unsigned char bootoption;
- + unsigned char lowsector;
- + unsigned char nzones;
- + unsigned short zone_spare;
- + unsigned long root;
- + unsigned long disc_size;
- + unsigned short disc_id;
- + unsigned char disc_name[32-22];
- + unsigned long disctype;
- +};
- +
- +static struct boot_block *boot=NULL;
- +static unsigned int zonesize;
- +static unsigned int idsperzone;
- +static long rootaddr;
- +static long mapaddr;
- +static long maplen;
- +static void *map=NULL;
- +static char ignoring;
- +
- +static int read_sectors(int dev,void *ptr,long address,int size)
- +{
- + struct buffer_head *bh;
- + char *p=ptr;
- + while(size>0) {
- + if((bh=bread(dev,address/1024,1024))) {
- + memcpy(p,bh->b_data+((address & 0x200)?512:0),(address & 0x200 || size<1024)?512:1024);
- + if(address & 0x200) {
- + p -= 512;
- + address -= 512;
- + size += 512;
- + }
- + } else {
- + printk("hda%c: bread failed\n",'a'+((dev & 0xc0)>>6));
- + return 0;
- + }
- + brelse(bh);
- + p += 1024;
- + address += 1024;
- + size -= 1024;
- + }
- + return 1;
- +}
- +
- +/* + Read boot block at disc address &C00 (sector 6)
- + * + Calculate map address & length
- + */
- +
- +static int getdiskinfo(int dev)
- +{
- + char *ptr;
- + extern void set_hdinfo(int,unsigned char,unsigned char,unsigned long,unsigned int);
- +
- + if(boot)
- + return 1;
- +
- + boot=(struct boot_block *)kmalloc(sizeof(struct boot_block),GFP_KERNEL);
- + if(!boot) {
- + printk("hda%c: out of memory for bootblock\n",(dev>>6)+'a');
- + return 0;
- + }
- +
- + ptr=(char *)kmalloc(1024,GFP_KERNEL);
- + if(!ptr) {
- + printk("hda%c: out of memory for bootblock\n",(dev>>6)+'a');
- + return 0;
- + }
- +
- + if(!read_sectors(dev,ptr,0xC00,1024)) {
- + kfree_s(ptr,1024);
- + return 0;
- + }
- + memcpy(boot,(ptr+0x1C0),sizeof(struct boot_block));
- + kfree_s(ptr,1024);
- +
- + set_hdinfo(dev,boot->secspertrack,boot->heads,boot->disc_size,1<<boot->log2secsize);
- +
- + zonesize=(8<<boot->log2secsize)-boot->zone_spare;
- + idsperzone=zonesize/(boot->idlen+1);
- + mapaddr=((boot->nzones>>1)*zonesize-((boot->nzones>1)?RECSIZE*8:0))<<boot->log2bpmb;
- + maplen=boot->nzones<<boot->log2secsize;
- + return 1;
- +}
- +
- +static unsigned char map_cross_valid_byte(void)
- +{
- + unsigned char const *const map_base=map;
- + unsigned int check=0;
- + unsigned int i;
- +
- + for(i=0;i<boot->nzones;i++)
- + check^=map_base[(i<<boot->log2secsize)+3];
- +
- + return check;
- +}
- +
- +static int get_map(int dev)
- +{
- + if(!getdiskinfo(dev))
- + return 0;
- +
- + if(map)
- + return 1;
- +
- + map = (unsigned long *)vmalloc(maplen);
- + if(!map)
- + return 0;
- +
- + if(!read_sectors(dev,map,mapaddr,maplen))
- + return 0;
- + if(map_cross_valid_byte()!=0xFF) {
- + printk("Map 1 has invalid cross check - trying second map\n");
- + if(!read_sectors(dev,map,mapaddr+maplen,maplen))
- + return 0;
- + if(map_cross_valid_byte()!=0xFF) {
- + printk("Map 2 has invalid cross check - ignoring drive\n");
- + ignoring=dev;
- + return 0;
- + }
- + }
- +
- + memcpy(boot,(char*)map+4,sizeof(struct boot_block));
- +
- + rootaddr=mapaddr+2*maplen;
- + return 1;
- +}
- +
- +static int readmap(int sin,int *fzone,int *fstart,int *fend,int max_frags)
- +{
- + static int startzone; /* Start zone */
- + static int fragment; /* Fragment id to look for */
- + static int curzone; /* Current zone */
- + static int curmapp; /* Current map bit position */
- +
- + register unsigned int t,z,frag,czone,cmap;
- + register unsigned char const *mapaddr;
- + register int *fz,*fs,*fe;
- + register unsigned char n;
- +
- +
- + if(sin) {/* If we have new address, start from new position */
- + fragment = sin >> 8;
- + curzone = startzone = fragment / idsperzone;
- + curmapp=0;
- + }
- + if(curzone == -1)
- + return 0;
- + frag = fragment;
- + czone = curzone;
- + cmap = curmapp;
- + fz = fzone;
- + fs = fstart;
- + fe = fend;
- +
- +/* printk("readmap: internal_address: %p, fragment: %p, zone: %p\n",sin,frag, */
- +/* czone); */
- +
- + while(1) {
- + mapaddr=(unsigned char const *)map+
- + (czone<<boot->log2secsize)+4+((czone==0)?RECSIZE:0);
- + while(cmap<zonesize-((czone==0)?RECSIZE*8:0))/* ? */ {
- + *fs=cmap;
- + z=cmap>>3;
- + t=((mapaddr[z]|(mapaddr[z+1]<<8)|(mapaddr[z+2]<<16))>>(cmap & 7)) &
- + ((1<<boot->idlen)-1);
- + cmap+=boot->idlen;
- +
- + while((cmap & 7)!=0 && (mapaddr[cmap>>3] & (1<<(cmap & 7)))==0)
- + cmap+=1;
- +
- + while((n=mapaddr[cmap>>3]) == 0)
- + cmap+=8;
- +
- + while((n & (1<<(cmap & 7)))==0)
- + cmap+=1;
- + cmap+=1;
- + if(t==frag) {
- + *fz++=czone;
- + *fe++=cmap;
- + fs++;
- + if(!--max_frags) {
- +/* printf("Breaking out\n"); */
- + curmapp = cmap;
- + curzone = czone;
- + return fs - fstart;
- + }
- + }
- + }
- + czone+=1;
- + if(czone==boot->nzones)
- + czone=0;
- + if(czone==startzone)
- + break;
- + cmap=0;
- + }
- + curzone = -1;
- + return fs - fstart;
- +}
- +
- +static unsigned long calcaddr(int zone,int offset)
- +{
- + if(zone)
- + return (zone*zonesize-RECSIZE*8+offset)<<boot->log2bpmb;
- + else
- + return offset<<boot->log2bpmb;
- +}
- +
- +static void scan_name(char *name,int len)
- +{
- + int i;
- + for(i=0;i<len+1;i++) {
- + if(name[i]<32 || name[i]=='.' || i==len) {
- + name[i]=0;
- + break;
- + }
- + if(islower(name[i]))
- + name[i]=toupper(name[i]);
- + }
- +}
- +
- +static unsigned long scandir(int dev,unsigned long diraddress,char *file,int *type,
- + unsigned long *length)
- +{
- + unsigned char *dir;
- + unsigned char *dd;
- + unsigned long addr;
- +
- + scan_name(file,10);
- +
- + dir=(unsigned char *)kmalloc(2048,GFP_KERNEL);
- +
- + read_sectors(dev,dir,diraddress,2048);
- +
- + if(strncmp((char*)dir+1,"Nick",4)!=0 && strncmp((char*)dir+1,"Hugo",4)!=0) {
- + printk("Broken directory - skipping\n");
- + return 0;
- + }
- +
- + dd=dir+5-26;
- + do {
- + dd+=26;
- + scan_name((char*)dd,10);
- + if(strcmp(file,(char*)dd)==0)
- + break;
- + }
- + while(*dd!=0);
- + if(*dd==0)
- + return 0;
- + else
- + if(dd[25] & 8)
- + *type=0;
- + *length=(unsigned long)dd[18]|(dd[19]<<8)|(dd[20]<<16)|(dd[21]<<24);
- + addr=(unsigned long)dd[22]|(dd[23]<<8)|(dd[24]<<16);
- +
- + kfree_s(dir,2048);
- +
- + return addr;
- +}
- +
- +static int search_path(int dev,char *path,unsigned long *start,unsigned long *length, int maxbits)
- +{
- + unsigned long dir,iad,len;
- + int type,i,n,fzones[MAX_BLK_FRAGS],fstart[MAX_BLK_FRAGS],fend[MAX_BLK_FRAGS];
- + char *p;
- +
- + if(!get_map(dev))
- + return 0;
- +
- + dir=rootaddr;
- + while(path) {
- + type=1;
- + p=strchr(path,'.');
- + if(p!=NULL) {
- + p[0]=0;
- + }
- + if(path[0]!='\0') {
- + iad=scandir(dev,dir,path,&type,&len);
- + if(iad==0) {
- + printk("%s '%s' not found - skipping\n",p==NULL?"File":"Directory",path);
- + return 0;
- + }
- + if(type==0) {
- + n=readmap(iad,fzones,fstart,fend,1);
- + dir=calcaddr(fzones[0],fstart[0]);
- + } else
- + if(p!=NULL) {
- + printk("'%s' is a directory - skipping\n",path);
- + return 0;
- + } else {
- + int j=0;
- + unsigned int secoff;
- + secoff=(iad & 0xFF)?(unsigned int)((iad & 0xFF)-1)<<boot->log2secsize:0;
- + len=((len-1)|((1<<boot->log2secsize)-1))+1;
- + n=readmap(iad,fzones,fstart,fend,MAX_BLK_FRAGS); /* Handle 64 entries at a time */
- + do {
- + for(i=0;i<n;i++) {
- + start[j]=calcaddr(fzones[i],fstart[i]);
- + length[j]=calcaddr(fzones[i],fend[i])-start[j];
- + if(secoff!=0) {
- + if(length[j]>secoff) {
- + start[j]+=secoff;
- + length[j]-=secoff;
- + secoff=0;
- + } else {
- + secoff-=length[j];
- + continue;
- + }
- + }
- +
- + if(j > 0 && start[j]==(start[j-1]+length[j-1])) {/* Combine parts */
- + len += length[j-1];
- + length[j-1]+=length[j];
- + j--;
- + }
- +
- + if(length[j]>len) {
- + length[j]=len;
- + j++;
- + break;
- + } else
- + len-=length[j];
- + j++;
- + if(j >= maxbits) {
- +#ifdef NOT_YET_TESTED
- + unsigned long **sn,**ln;
- + sn=(unsigned long*)kmalloc(maxbits+BLK_INCREMENT,GFP_KERNEL);
- + ln=(unsigned long*)kmalloc(maxbits+BLK_INCREMENT,GFP_KERNEL);
- + if(maxbits && start && length) {
- + memcpy(sn,start,maxbits);
- + memcpy(ln,length,maxbits);
- + kfree_s((void *)start, maxbits);
- + kfree_s((void *)length, maxbits);
- + }
- + maxbits+=BLK_INCREMENT;
- +#else
- + printk("**** EEEK!!!! - Image file is in too many chunks!\n");
- + return 0;
- +#endif
- + }
- + }
- + }
- + while((n=readmap(0,fzones,fstart,fend,MAX_BLK_FRAGS))>0);
- + return j;
- + }
- + }
- + if(p) {
- + path=p+1;
- + p[0]='.';
- + } else
- + path=0;
- + }
- + printk("Pathname invalid?!?\n");
- + return 0;
- +}
- +
- +#define MAX_HD_IMAGES 8
- +#define TWO_HDS
- +#define MAX_HD_BITS 128
- +
- +static int initialised[MAX_HD_IMAGES];
- +static int maxblock[MAX_HD_IMAGES];
- +static unsigned long startchk[MAX_HD_IMAGES],lengthchk[MAX_HD_IMAGES];
- +#ifndef NOT_YET_TESTED
- +static unsigned long startaddr[MAX_HD_IMAGES][MAX_HD_BITS],
- + lengthaddr[MAX_HD_IMAGES][MAX_HD_BITS];
- +#else
- +static unsigned long *startaddr[MAX_HD_IMAGES],
- + *lengthaddr[MAX_HD_IMAGES];
- +#endif
- +
- +/* --------------------------------------------------------------------------------- */
- +/* Externally visible functions/variables
- + * --------------------------------------------------------------------------------- */
- +
- +char arc_hd_files[MAX_HD_IMAGES][128]=
- +{
- + {0,},
- + {0,},
- + {0,},
- + {0,}
- +};
- +
- +/*
- + * Get the sectors used by device 'dev'
- + */
- +int image_allocate_list(int dev,int devno,struct hd_struct *part)
- +{
- + unsigned long chk1=0,chk2=0;
- + int i,sects=0;
- +
- +#ifndef TWO_HDS
- + if(dev!=0x300)
- + {
- + printk("Can't cope with >1 hd!\n");
- + return 0;
- + }
- +#else
- + if(dev!=0x300 && dev!=0x340)
- + {
- + printk("Can't cope with >2 hds! (device %02X:%02X)\n",dev>>8,dev & 255);
- + return 0;
- + }
- + if(dev == 0x340)
- + devno=(devno & 0x3f) + 4;
- +#endif
- +
- + if(ignoring==dev)
- + return 0;
- +
- + devno--;
- +
- + if(devno > MAX_HD_IMAGES)
- + return 0;
- +
- + if(!arc_hd_files[devno][0])
- + return 0;
- +
- +#ifndef NOT_YET_TESTED
- + maxblock[devno]=search_path(dev,arc_hd_files[devno],startaddr[devno],lengthaddr[devno],MAX_HD_BITS);
- +#else
- + maxblock[devno]=search_path(dev,arc_hd_files[devno],startaddr[devno],lengthaddr[devno],0);
- +#endif
- + if(maxblock[devno]>MAX_HD_BITS)
- + panic("image allocator: image file is too fragmented\n");
- + if(!maxblock[devno])
- + return 0;
- +
- + for(i=0; i<maxblock[devno]; i++)
- + {
- + /* convert to blocks & calculate checksums */
- +#define ADDR2BLOCK(x) ((x)>>9)
- + startaddr[devno][i]=ADDR2BLOCK(startaddr[devno][i]);
- + lengthaddr[devno][i]=ADDR2BLOCK(lengthaddr[devno][i]);
- + chk1^=startaddr[devno][i];
- + chk2^=lengthaddr[devno][i];
- + sects+=lengthaddr[devno][i];
- + }
- + startchk[devno]=chk1;
- + lengthchk[devno]=chk2;
- + initialised[devno]=1;
- + part->start_sect=0;
- + part->nr_sects=sects;
- +
- +#ifdef DEBUG
- + for(i=0;i<n;i++)
- + printk("%10X : %10X\n",start[devno][i],length[devno][i]);
- +#endif
- + return 1;
- +}
- +
- +/*
- + * This is called to release all the memory with this device
- + *
- + * This must be called before accessing a new device
- + */
- +
- +void image_release_dev(int dev)
- +{
- + if(boot) {
- + kfree_s(boot,sizeof(struct boot_block));
- + boot=NULL;
- + }
- +
- + if(map) {
- + vfree(map);
- + map = NULL;
- + }
- +
- + ignoring=0;
- +}
- +
- +/*
- + * This macro maps a device major/minor to the internal image file number
- + */
- +#ifndef TWO_HDS
- +#define GET_DEV(dev) \
- + if((dev)<0x301 || (dev)>0x314) \
- + { \
- + printk("Bad device %X passed to image_file_check\n",(dev)); \
- + return 0; \
- + } \
- + (dev)=((dev)-1) & 3;
- +#else
- +#define GET_DEV(dev) \
- + if(((dev) & 0x33F)<0x301 || ((dev) & 0x33F)>0x314) \
- + { \
- + printk("Bad device %X passed to image_file_check\n",(dev)); \
- + return 0; \
- + } \
- + switch((dev) & 0xC0) \
- + { \
- + case 0x00: (dev)=((dev) -1) & 3; break;\
- + case 0x40: (dev)=(((dev) -1) & 3)+4; break;\
- + }
- +#endif
- +
- +/*
- + * This function is called to perform checks on the image file.
- + * Checks made: Image file has been initialised, and
- + * for writes, the checksums on the start/length data are correct.
- + */
- +int image_file_check(int dev,int cmd)
- +{
- + long chk1=0,chk2=0;
- + int i;
- +
- + GET_DEV(dev);
- +
- + if (!initialised[dev]) {
- + printk("hd%c%d: %s of uninitialised image file.\n",(dev>>6)+'a',dev & 0x3f,
- + cmd==WRITE?"write":"read");
- + return 0;
- + }
- +
- + if (cmd==WRITE) {
- + /*
- + * Check the table! Just in case. We don't want to write to a part of the HD that
- + * is not allocated to us! It might be the map or something!
- + */
- + for(i=0; i<maxblock[dev]; i++) {
- + chk1^=startaddr[dev][i];
- + chk2^=lengthaddr[dev][i];
- + }
- + if(chk1!=startchk[dev] || chk2!=lengthchk[dev]) {
- + printk("hda%c%d: mapping tables corrupted on write.",
- + (dev>>6)+'a',dev & 0x3f);
- + return 0;
- + }
- + }
- + return 1;
- +}
- +
- +/*
- + * Map an image file block to a physical device returns 0 on error,
- + * or the number of entries. This is called quite often when accessing
- + * image files.
- + */
- +int image_file_map(int dev,unsigned long reqblock,unsigned long reqlength,
- + unsigned long max_reqs,
- + unsigned long *block,unsigned long *length)
- +{
- + int i,num=0;
- + unsigned long blk=reqblock,totlen=0;
- +
- + GET_DEV(dev);
- +
- + for(i=0; i<maxblock[dev]; i++)
- + {
- + long remainder;
- + totlen+=lengthaddr[dev][i];
- + if(reqblock >= lengthaddr[dev][i])
- + {
- + reqblock-=lengthaddr[dev][i];
- + continue;
- + }
- + block[num]=reqblock+startaddr[dev][i];
- + remainder=reqblock+reqlength-lengthaddr[dev][i];
- + if(remainder<=0)
- + {
- + length[num]=reqlength;
- + return num+1;
- + }
- + length[num]=lengthaddr[dev][i]-reqblock;
- + if(num+1 >= max_reqs)
- + return num+1;
- + reqblock=0;
- + reqlength=remainder;
- + num++;
- + }
- +
- + printk("hd%c%d: attempted read for sector %ld past end if image file at %ld.\n",
- + (dev>>6)+'a',dev & 0x3f,blk,totlen);
- + /* Should never happen! */
- + return 0;
- +}
- +
- +
- +
- +#if 0
- +static inline int check_bb(unsigned char *ptr)
- +{
- + unsigned int result;
- +
- + __asm__("
- + adds %3, %2, %3
- + sub %3, %3, #1
- + b LC02
- +LC01: ldrb %4, [%3, #-1]!
- + adc %0, %0, %4
- + movs %0, %0, lsl #24
- + mov %0, %0, lsr #24
- +LC02: teqs %2, %3
- + bne LC01
- + " : "=r" (result) : "0" (0), "r" (ptr), "r" (512), "r" (256));
- + return result != ptr[511];
- +}
- +
- +static struct bootblock *bb;
- +
- +int arm_partition (struct gendisk *hd, kdev_t dev, unsigned long first_sector, int minor)
- +{
- + int i;
- + struct buffer_head *bh;
- +
- + current_minor = minor;
- +
- + if (!(bh = bread (dev, 3, 1024))) {
- + printk (" unable to read boot sectors\n");
- + return -1;
- + }
- +
- + if (check_bb (bh->b_data))
- + {
- + brelse (bh);
- + return 0;
- + }
- +
- + bb = (struct bootblock *)(bh->b_data + 0x1c0);
- +
- + set_hdinfo(dev,bb->secspertrack,bb->heads,bb->disc_size,1<<bb->log2secsize);
- +
- + zonesize = (8 << bb->log2secsize) - bb->zone_spare;
- + idsperzone = zonesize / (bb->idlen + 1);
- + mapaddr = ((bb->nzones >> 1) * zonesize - ((bb->nzones > 1)? RECSIZE*8 : 0)) << bb->log2bpmb;
- + maplen = bb->nzones << bb->log2secsize;
- +
- +
- +
- + brelse (bh);
- + return 1;
- +}
- +#endif
- +
- diff -urNwbB linux/arch/arm/drivers/block/mfmhd.c linux.arm/arch/arm/drivers/block/mfmhd.c
- --- linux/arch/arm/drivers/block/mfmhd.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/mfmhd.c Sun Mar 3 12:30:30 1996
- @@ -0,0 +1,778 @@
- +/*
- + * linux/arch/arm/drivers/block/mfmhd.c
- + *
- + * Copyright (C) 1995, 1996 Russell King
- + *
- + * MFM hard drive code [experimental]
- + */
- +
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/version.h>
- +#endif
- +
- +#include <linux/config.h>
- +#include <linux/sched.h>
- +#include <linux/fs.h>
- +#include <linux/kernel.h>
- +#include <linux/timer.h>
- +#include <linux/tqueue.h>
- +#include <linux/mm.h>
- +
- +#include <linux/xd.h>
- +#include <linux/errno.h>
- +#include <linux/genhd.h>
- +#include <linux/major.h>
- +
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include <asm/segment.h>
- +#include <asm/dma.h>
- +
- +#include <asm/ecard.h>
- +
- +#define MFM_DISK_MAJOR 13
- +#undef XT_DISK_MAJOR
- +#define XT_DISK_MAJOR -1
- +#define MAJOR_NR MFM_DISK_MAJOR
- +#include "blk.h"
- +
- +XD_INFO mfm_info[XD_MAXDRIVES];
- +
- +#define DEBUG
- +
- +#define MFM_COMMAND (mfm_addr + 0)
- +#define MFM_DATAOUT (mfm_addr + 1)
- +#define MFM_STATUS (mfm_addr + 8)
- +#define MFM_DATAIN (mfm_addr + 9)
- +
- +static void mfm_geninit (void);
- +static int mfm_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg);
- +static int mfm_open (struct inode *inode,struct file *file);
- +static void mfm_release (struct inode *inode, struct file *file);
- +static void mfm_interrupt_handler (int, struct pt_regs *);
- +static void mfm_seek (void);
- +static void mfm_unexpected_interrupt (void);
- +static void mfm_request (void);
- +static int mfm_reread_partitions (int dev);
- +static void mfm_request (void);
- +
- +#define mfm_init xd_init
- +
- +static struct hd_struct mfm[XD_MAXDRIVES << 6];
- +static int mfm_sizes[XD_MAXDRIVES << 6], mfm_access[XD_MAXDRIVES] = { 0, 0 };
- +static int mfm_blocksizes[XD_MAXDRIVES << 6];
- +static unsigned char mfm_valid[XD_MAXDRIVES] = { -1, };
- +static struct wait_queue *mfm_wait_open = NULL;
- +
- +static int mfm_irq; /* Interrupt number */
- +static int mfm_addr; /* Controller address */
- +static int mfm_drives = 0; /* drives available */
- +static int mfm_status = 0; /* interrupt status */
- +static char mfm_busy = 0; /* mfm busy */
- +static int *errors;
- +
- +static struct rawcmd
- +{
- + unsigned int flags;
- + unsigned int dev;
- + unsigned int cylinder;
- + unsigned int head;
- + unsigned int sector;
- + unsigned int cmdtype;
- + unsigned int cmdcode;
- + unsigned char cmddata[16];
- + unsigned int cmdlen;
- +} raw_cmd;
- +
- +#define NEED_SEEK 1
- +unsigned char result[16];
- +unsigned char resultlen;
- +
- +static struct cont
- +{
- + void (*interrupt)(void);/* interrupt handler */
- + void (*error)(void); /* error handler */
- + void (*redo)(void); /* redo handler */
- + void (*done)(int st); /* done handler */
- +} *cont = NULL;
- +
- +static struct drvparam
- +{
- + int cylinder;
- + unsigned int nocylinders;
- + unsigned int lowcurrentcylinder;
- + unsigned int precompcylinder;
- + unsigned char noheads;
- + unsigned char nosectors;
- + struct
- + {
- + char recal;
- + char report;
- + char abort;
- + } errors;
- +} mfm_drive_param[8] =
- +{
- + { -1, 616, 616, 129, 4, 32, { 0,0,1 } },
- + { -1, 616, 616, 129, 4, 32, { 0,0,1 } },
- + { -1, 616, 616, 129, 4, 32, { 0,0,1 } },
- + { -1, 616, 616, 129, 4, 32, { 0,0,1 } },
- + { -1, 616, 616, 129, 4, 32, { 0,0,1 } },
- + { -1, 616, 616, 129, 4, 32, { 0,0,1 } },
- + { -1, 616, 616, 129, 4, 32, { 0,0,1 } },
- + { -1, 616, 616, 129, 4, 32, { 0,0,1 } }
- +};
- +
- +#define MFM_DRV_PARAM mfm_drive_param[raw_cmd.dev]
- +
- +struct tq_struct mfm_tq =
- +{ 0, 0, (void (*) (void *)) mfm_unexpected_interrupt, 0 };
- +
- +#define NO_TRACK -1
- +#define NEED_1_RECAL -2
- +#define NEED_2_RECAL -3
- +
- +
- +/* ------------------------------------------------------------------------------------------ */
- +/*
- + * From the HD63463 data sheet from Hitachi Ltd.
- + */
- +
- +#define CMD_ABT 0xF0 /* Abort */
- +#define CMD_SPC 0xE8 /* Specify */
- +#define CMD_TST 0xE0 /* Test */
- +#define CMD_RCLB 0xC8 /* Recalibrate */
- +#define CMD_SEK 0xC0 /* Seek */
- +#define CMD_WFS 0xAB /* Write Format Skew */
- +#define CMD_WFM 0xA3 /* Write Format */
- +#define CMD_MTB 0x90 /* Memory to buffer */
- +#define CMD_CMPD 0x88 /* Compare data */
- +#define CMD_WD 0x87 /* Write data */
- +#define CMD_RED 0x70 /* Read erroneous data */
- +#define CMD_RIS 0x68 /* Read ID skew */
- +#define CMD_FID 0x61 /* Find ID */
- +#define CMD_RID 0x60 /* Read ID */
- +#define CMD_BTM 0x50 /* Buffer to memory */
- +#define CMD_CKD 0x48 /* Check data */
- +#define CMD_RD 0x40 /* Read data */
- +#define CMD_OPBW 0x38 /* Open buffer write */
- +#define CMD_OPBR 0x30 /* Open buffer read */
- +#define CMD_CKV 0x28 /* Check drive */
- +#define CMD_CKE 0x20 /* Check ECC */
- +#define CMD_POD 0x18 /* Polling disable */
- +#define CMD_POL 0x10 /* Polling enable */
- +#define CMD_RCAL 0x08 /* Recall */
- +
- +#define STAT_BSY 0x8000 /* Busy */
- +#define STAT_CPR 0x4000 /* Command Parameter Rejection */
- +#define STAT_CED 0x2000 /* Command end */
- +#define STAT_SED 0x1000 /* Seek end */
- +#define STAT_DER 0x0800 /* Drive error */
- +#define STAT_ABN 0x0400 /* Abnormal end */
- +#define STAT_POL 0x0200 /* Polling */
- +
- +/* ------------------------------------------------------------------------------------------ */
- +
- +static struct gendisk mfm_gendisk = {
- + MAJOR_NR, /* Major number */
- + "mfm", /* Major name */
- + 6, /* Bits to shift to get real from partition */
- + 1 << 6, /* Number of partitions per real */
- + XD_MAXDRIVES, /* maximum number of real */
- + mfm_geninit, /* init function */
- + mfm, /* hd struct */
- + mfm_sizes, /* block sizes */
- + 0, /* number */
- + (void *)mfm_info,/* internal */
- + NULL /* next */
- +};
- +
- +static struct file_operations mfm_fops = {
- + NULL, /* lseek - default */
- + block_read, /* read - general block-dev read */
- + block_write, /* write - general block-dev write */
- + NULL, /* readdir - bad */
- + NULL, /* select */
- + mfm_ioctl, /* ioctl */
- + NULL, /* mmap */
- + mfm_open, /* open */
- + mfm_release, /* release */
- + block_fsync /* fsync */
- +};
- +
- +static struct expansion_card *ecs;
- +static int mfm_prods[] = { 0x000b };
- +static int mfm_manus[] = { 0x0000 };
- +
- +unsigned long mfm_init (unsigned long mem_start, unsigned long mem_end)
- +{
- + ecs = ecard_find(0, sizeof(mfm_prods), mfm_prods, mfm_manus);
- + if(!ecs)
- + return mem_start;
- +
- + mfm_irq = ecs->irq;
- + mfm_addr= ((((int)ecs->r_podaddr) & 0x03eff000) + 0x2000)>>2;
- +
- + ecard_claim(ecs);
- +
- + printk("mfm: controller found at address %X, interrupt %d\n", mfm_addr, mfm_irq);
- +
- + if(register_blkdev(MAJOR_NR, "mfm", &mfm_fops)) {
- + printk("mfm_init: unable to get major number %d\n", MAJOR_NR);
- + return mem_start;
- + }
- + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahread */
- +#ifndef MODULE
- + mfm_gendisk.next = gendisk_head;
- + gendisk_head = & mfm_gendisk;
- +#endif
- +
- + return mem_start;
- +}
- +
- +static int mfm_initdrives (void)
- +{
- + mfm_info[0].heads = 4;
- + mfm_info[0].cylinders = 615;
- + mfm_info[0].sectors = 32;
- + mfm_info[0].control = 0;
- + return 1;
- +}
- +
- +static void mfm_geninit (void)
- +{
- + int i;
- +
- + mfm_drives = mfm_initdrives();
- + printk("mfm: detected %d hard drive%s\n", mfm_drives, mfm_drives == 1?"":"s");
- + for(i = 0; i < mfm_drives; i++)
- + printk("mfm%d: heads = %d, cylinders = %d, sectors = %d\n", i,
- + mfm_info[i].heads, mfm_info[i].cylinders, mfm_info[i].sectors);
- +
- + if(request_irq(mfm_irq, mfm_interrupt_handler, SA_INTERRUPT, "MFM harddisk"))
- + printk("mfm: unable to get IRQ%d\n", mfm_irq);
- +
- + outw(0x80, mfm_addr + (0x1000 >> 2));
- +
- + for(i = 0; i < mfm_drives; i++) {
- + mfm[i << 6].nr_sects = mfm_info[i].heads * mfm_info[i].cylinders *
- + mfm_info[i].sectors;
- + mfm[i << 6].start_sect = 0;
- + mfm_valid[i] = 1;
- + }
- + mfm_gendisk.nr_real = mfm_drives;
- +
- + for(i=0; i<(XD_MAXDRIVES << 6); i++)
- + mfm_blocksizes[i] = 1024;
- + blksize_size[MAJOR_NR] = mfm_blocksizes;
- +}
- +
- +static int mfm_open (struct inode *inode,struct file *file)
- +{
- + int dev = DEVICE_NR(MINOR(inode->i_rdev));
- +
- + if (dev < mfm_drives) {
- + while (!mfm_valid[dev])
- + sleep_on(&mfm_wait_open);
- +
- + mfm_access[dev]++;
- +
- + return (0);
- + }
- + else
- + return (-ENODEV);
- +}
- +
- +static int mfm_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg)
- +{
- + XD_GEOMETRY *geometry = (XD_GEOMETRY *) arg;
- + int dev = DEVICE_NR(MINOR(inode->i_rdev)),err;
- +
- + if (inode && (dev < mfm_drives))
- + switch (cmd) {
- + case HDIO_GETGEO:
- + if (arg) {
- + if ((err = verify_area(VERIFY_WRITE, geometry, sizeof(*geometry))))
- + return (err);
- + put_fs_byte(mfm_info[dev].heads,(char *) &geometry->heads);
- + put_fs_byte(mfm_info[dev].sectors,(char *) &geometry->sectors);
- + put_fs_word(mfm_info[dev].cylinders,(short *) &geometry->cylinders);
- + put_fs_long(mfm[MINOR(inode->i_rdev)].start_sect,(long *) &geometry->start);
- +
- + return (0);
- + }
- + break;
- + case BLKRASET:
- + if(!suser()) return -EACCES;
- + if(!inode->i_rdev) return -EINVAL;
- + if(arg > 0xff) return -EINVAL;
- + read_ahead[MAJOR(inode->i_rdev)] = arg;
- + return 0;
- + case BLKGETSIZE:
- + if (arg) {
- + if ((err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long))))
- + return (err);
- + put_fs_long(mfm[MINOR(inode->i_rdev)].nr_sects,(long *) arg);
- +
- + return (0);
- + }
- + break;
- + case BLKFLSBUF:
- + if(!suser()) return -EACCES;
- + if(!inode->i_rdev) return -EINVAL;
- + fsync_dev(inode->i_rdev);
- + invalidate_buffers(inode->i_rdev);
- + return 0;
- +
- + case BLKRRPART:
- + return (mfm_reread_partitions(inode->i_rdev));
- + RO_IOCTLS(inode->i_rdev,arg);
- + }
- + return (-EINVAL);
- +}
- +
- +static void mfm_release (struct inode *inode, struct file *file)
- +{
- + int dev = DEVICE_NR(MINOR(inode->i_rdev));
- +
- + if (dev < mfm_drives) {
- + sync_dev(dev);
- + mfm_access[dev]--;
- + }
- +}
- +
- +static int mfm_reread_partitions (int dev)
- +{
- + int target = DEVICE_NR(MINOR(dev)),start = target << mfm_gendisk.minor_shift,partition;
- +
- + cli();
- +
- + mfm_valid[target] = (mfm_access[target] != 1);
- +
- + sti();
- +
- + if (mfm_valid[target])
- + return -EBUSY;
- +
- + for (partition = mfm_gendisk.max_p - 1; partition >= 0; partition--) {
- + sync_dev(MAJOR_NR << 8 | start | partition);
- + invalidate_inodes(MAJOR_NR << 8 | start | partition);
- + invalidate_buffers(MAJOR_NR << 8 | start | partition);
- + mfm_gendisk.part[start + partition].start_sect = 0;
- + mfm_gendisk.part[start + partition].nr_sects = 0;
- + };
- +
- + mfm_gendisk.part[start].nr_sects = mfm_info[target].heads *
- + mfm_info[target].cylinders * mfm_info[target].sectors;
- + resetup_one_dev(&mfm_gendisk,target);
- +
- + mfm_valid[target] = 1;
- + wake_up(&mfm_wait_open);
- +
- + return 0;
- +}
- +
- +static void print_status (void)
- +{
- + char *error;
- + static char *errors[] =
- + { "no error",
- + "command aborted",
- + "invalid command",
- + "parameter error",
- + "not initialised",
- + "rejected TEST",
- + "no useld",
- + "write fault",
- + "not ready",
- + "no scp",
- + "in seek",
- + "invalid NCA",
- + "invalid step rate",
- + "seek error",
- + "over run",
- + "invalid PHA",
- + "data field EEC error",
- + "data field CRC error",
- + "error corrected",
- + "data field fatal error",
- + "no data am",
- + "not hit",
- + "ID field CRC error",
- + "time over",
- + "no ID am",
- + "not writable"
- + };
- + if(result[1] < 0x65)
- + error = errors[result[1] >> 2];
- + else
- + error = "unknown";
- + printk("(");
- + if(mfm_status & STAT_BSY)
- + printk("BSY ");
- + if(mfm_status & STAT_CPR)
- + printk("CPR ");
- + if(mfm_status & STAT_CED)
- + printk("CED ");
- + if(mfm_status & STAT_SED)
- + printk("SED ");
- + if(mfm_status & STAT_DER)
- + printk("DER ");
- + if(mfm_status & STAT_ABN)
- + printk("ABN ");
- + if(mfm_status & STAT_POL)
- + printk("POL ");
- + printk(") SSB = %X (%s)\n", result[1], error);
- +
- +}
- +
- +/* ------------------------------------------------------------------------------------- */
- +
- +static void issue_command (int command, unsigned char *cmdb, int len)
- +{
- + int status;
- +#ifdef DEBUG
- + int i;
- + printk("issue_command: %02X: ",command);
- + for(i=0; i<len; i++)
- + printk("%02X ", cmdb[i]);
- + printk("\n");
- +#endif
- +
- + do {
- + status = inw(MFM_STATUS);
- + } while(status & (STAT_BSY|STAT_POL));
- + if(status & STAT_CPR)
- + {
- + outw(CMD_RCAL, MFM_COMMAND);
- + while(inw(MFM_STATUS) & STAT_BSY);
- + }
- +
- + while(len > 0)
- + {
- + outw(cmdb[1] | (cmdb[0] << 8), MFM_DATAOUT);
- + len -= 2;
- + cmdb += 2;
- + }
- + outw(command, MFM_COMMAND);
- +}
- +
- +static void wait_for_completion (void)
- +{
- + while((mfm_status = inw(MFM_STATUS)) & STAT_BSY);
- +}
- +
- +/* ------------------------------------------------------------------------------------- */
- +
- +static void mfm_rw_intr (void)
- +{
- + printk("mfm_rw_intr...\n");
- + if (cont)
- + {
- + cont->done(1);
- + }
- + mfm_request();
- +}
- +
- +static void mfm_setup_rw (void)
- +{
- + printk("setting up for rw...\n");
- +#if 1
- + SET_INTR(mfm_rw_intr);
- + issue_command(raw_cmd.cmdcode, raw_cmd.cmddata, raw_cmd.cmdlen);
- +#else
- + if(cont)
- + {
- + cont->done(0);
- + cont->redo();
- + }
- +#endif
- +}
- +
- +static void mfm_recal_intr (void)
- +{
- + printk("recal intr - status = ");
- + print_status();
- + if(mfm_status & (STAT_DER|STAT_ABN)) {
- + printk("recal failed\n");
- + MFM_DRV_PARAM.cylinder = NEED_2_RECAL;
- + if(cont)
- + {
- + cont->error();
- + cont->redo();
- + }
- + return;
- + }
- + if(mfm_status & STAT_SED) {
- + issue_command(CMD_POD, NULL, 0);
- + MFM_DRV_PARAM.cylinder = 0;
- + mfm_seek();
- + return;
- + }
- + if(mfm_status & STAT_CED) {
- + SET_INTR(mfm_recal_intr);
- + issue_command(CMD_POL, NULL, 0);
- + return;
- + }
- + printk("recal: unknown status\n");
- +}
- +
- +static void mfm_seek_intr (void)
- +{
- + printk("seek intr - status = ");
- + print_status();
- + if(mfm_status & (STAT_DER|STAT_ABN)) {
- + printk("seek failed\n");
- + MFM_DRV_PARAM.cylinder = NEED_2_RECAL;
- + if(cont)
- + {
- + cont->error();
- + cont->redo();
- + }
- + return;
- + }
- + if(mfm_status & STAT_SED) {
- + issue_command(CMD_POD, NULL, 0);
- + MFM_DRV_PARAM.cylinder = raw_cmd.cylinder;
- + mfm_seek();
- + return;
- + }
- + if(mfm_status & STAT_CED) {
- + SET_INTR(mfm_seek_intr);
- + issue_command(CMD_POL, NULL, 0);
- + return;
- + }
- + printk("seek: unknown status\n");
- +}
- +
- +static void mfm_specify (void)
- +{
- + unsigned char cmdb[16];
- + printk("specify...\n");
- + cmdb[0] = 0x1F;
- + cmdb[1] = 0xC3;
- + cmdb[2] = 0x16;
- + cmdb[3] = 0x02;
- + cmdb[4] = 0xFC | ((MFM_DRV_PARAM.nocylinders - 1) >> 8);
- + cmdb[5] = MFM_DRV_PARAM.nocylinders - 1;
- + cmdb[6] = MFM_DRV_PARAM.noheads - 1;
- + cmdb[7] = MFM_DRV_PARAM.nosectors - 1;
- + cmdb[8] = 0xA9;
- + cmdb[9] = 0x0A;
- + cmdb[10]= 0x0D;
- + cmdb[11]= 0x0C;
- + cmdb[12]= (MFM_DRV_PARAM.precompcylinder - 1) >> 8;
- + cmdb[13]= MFM_DRV_PARAM.precompcylinder - 1;
- + cmdb[14]= (MFM_DRV_PARAM.lowcurrentcylinder - 1) >> 8;
- + cmdb[15]= MFM_DRV_PARAM.lowcurrentcylinder - 1;
- +
- + issue_command(CMD_SPC, cmdb, 16);
- + wait_for_completion();
- +}
- +
- +static void mfm_seek (void)
- +{
- + unsigned char cmdb[4];
- + printk("seeking...\n");
- + if(MFM_DRV_PARAM.cylinder < 0) {
- + cmdb[0] = raw_cmd.dev + 1;
- + cmdb[1] = raw_cmd.head;
- +
- + SET_INTR(mfm_recal_intr);
- + issue_command(CMD_RCLB, cmdb, 2);
- + return;
- + }
- + mfm_specify();
- + if(MFM_DRV_PARAM.cylinder != raw_cmd.cylinder)
- + {
- + cmdb[0] = raw_cmd.dev + 1;
- + cmdb[1] = raw_cmd.head;
- + cmdb[2] = raw_cmd.cylinder >> 8;
- + cmdb[3] = raw_cmd.cylinder;
- +
- + SET_INTR(mfm_seek_intr);
- + issue_command(CMD_SEK, cmdb, 4);
- + }
- + else
- + mfm_setup_rw();
- +}
- +
- +static void mfm_initialise (void)
- +{
- + printk("init...\n");
- + if(raw_cmd.flags & NEED_SEEK)
- + mfm_seek();
- + else
- + mfm_setup_rw();
- +}
- +
- +/* ------------------------------------------------------------------------------------- */
- +
- +static void request_done(int uptodate)
- +{
- + if(!CURRENT) {
- + printk("mfm: request list destroyed\n");
- + return;
- + }
- + if(uptodate) {
- + end_request (1);
- + }
- + else {
- + end_request (0);
- + }
- +}
- +
- +static void error_handler (void)
- +{
- + int i;
- + printk("error detected... status = ");
- + print_status();
- + (*errors) ++;
- + if(*errors > MFM_DRV_PARAM.errors.abort)
- + cont->done(0);
- + if(*errors > MFM_DRV_PARAM.errors.recal)
- + MFM_DRV_PARAM.cylinder = NEED_2_RECAL;
- +}
- +
- +static void rw_interrupt(void)
- +{
- +}
- +
- +static struct cont rw_cont ={
- + rw_interrupt,
- + error_handler,
- + mfm_request,
- + request_done
- +};
- +
- +static void mfm_request (void)
- +{
- + unsigned int dev, block, nsect, track;
- +
- + if(CURRENT && CURRENT->dev < 0)
- + return;
- +
- + while(1){
- + sti();
- +
- + INIT_REQUEST;
- + dev = MINOR(CURRENT->dev);
- + block = CURRENT->sector;
- + nsect = CURRENT->nr_sectors;
- +
- + if(dev >= (mfm_drives << 6) ||
- + block >= mfm[dev].nr_sects || ((block+nsect) > mfm[dev].nr_sects)) {
- +#ifdef DEBUG
- + if(dev >= (mfm_drives << 6))
- + printk("mfm: bad minor number: device=0x%04x\n", CURRENT->dev);
- + else
- + printk("mfm%c: bad access: block=%d, count=%d\n", (dev>>6)+'a',
- + block, nsect);
- +#endif
- + end_request(0);
- + continue;
- + }
- + block += mfm[dev].start_sect;
- +
- + if(CURRENT->cmd != READ && CURRENT->cmd != WRITE)
- + {
- + printk("unknown mfm-command %d\n", CURRENT->cmd);
- + end_request(0);
- + continue;
- + }
- +
- + dev >>= 6;
- +
- + track = block / mfm_info[dev].sectors;
- + raw_cmd.flags = NEED_SEEK;
- + raw_cmd.dev = dev;
- + raw_cmd.sector = block % mfm_info[dev].sectors;
- + raw_cmd.head = track % mfm_info[dev].heads;
- + raw_cmd.cylinder = track / mfm_info[dev].heads;
- + raw_cmd.cmdtype = CURRENT->cmd;
- + raw_cmd.cmdcode = CURRENT->cmd == WRITE ? CMD_WD : CMD_RD;
- + raw_cmd.cmddata[0] = dev;
- + raw_cmd.cmddata[1] = raw_cmd.head;
- + raw_cmd.cmddata[2] = raw_cmd.cylinder >> 8;
- + raw_cmd.cmddata[3] = raw_cmd.cylinder;
- + raw_cmd.cmddata[4] = raw_cmd.head;
- + raw_cmd.cmddata[5] = raw_cmd.sector;
- + raw_cmd.cmddata[6] = nsect >> 8;
- + raw_cmd.cmddata[7] = nsect;
- + raw_cmd.cmdlen = 8;
- +
- +#ifdef DEBUG
- + printk("mfm%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx (%p)\n",
- + raw_cmd.dev+'a', (CURRENT->cmd == READ)?"read":"writ",
- + raw_cmd.cylinder,
- + raw_cmd.head,
- + raw_cmd.sector, nsect, (unsigned long)CURRENT->buffer, CURRENT);
- +#endif
- + cont = &rw_cont;
- + errors = &(CURRENT->errors);
- + mfm_tq.routine = (void (*)(void *))mfm_initialise;
- + queue_task(&mfm_tq, &tq_timer);
- + break;
- + }
- +}
- +
- +static void do_mfm_request (void)
- +{
- + mfm_request();
- +}
- +
- +static void mfm_unexpected_interrupt (void)
- +{
- + printk("mfm: unexpected interrupt - status = ");
- + print_status();
- +}
- +
- +static void mfm_interrupt_handler (int unused, struct pt_regs *regs)
- +{
- + int i;
- + void (*handler)(void) = DEVICE_INTR;
- +
- + CLEAR_INTR;
- + mfm_status = inw(MFM_STATUS);
- +
- + if((mfm_status & (STAT_CPR|STAT_BSY)) == STAT_CPR)
- + {
- + int len = 0;
- + while(len <16)
- + {
- + int in;
- + in = inw(MFM_DATAIN);
- + result[len++] = in>>8;
- + result[len++] = in;
- + }
- + }
- +
- + outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
- +
- + if(!handler) {
- + mfm_unexpected_interrupt();
- + return;
- + }
- + mfm_tq.routine = (void (*)(void *))handler;
- + queue_task_irq(&mfm_tq, &tq_timer);
- +}
- +
- +#ifdef MODULE
- +
- +char kernel_version[] = UTS_RELEASE;
- +
- +int init_module (void)
- +{
- + mfm_init(0, 0);
- + mfm_geninit();
- + return 0;
- +}
- +
- +void cleanup_module (void)
- +{
- + if (ecs)
- + ecard_release(ecs);
- +}
- +
- +#endif
- diff -urNwbB linux/arch/arm/drivers/char/Makefile linux.arm/arch/arm/drivers/char/Makefile
- --- linux/arch/arm/drivers/char/Makefile Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/Makefile Sat Feb 24 14:18:57 1996
- @@ -0,0 +1,87 @@
- +#
- +# Makefile for the kernel character device drivers.
- +#
- +# Note! Dependencies are done automagically by 'make dep', which also
- +# removes any old dependencies. DON'T put your own dependencies here
- +# unless it's something special (ie not a .c file).
- +#
- +# Note 2! The CFLAGS definitions are now inherited from the
- +# parent makes..
- +#
- +
- +#
- +# This file contains the font map for the default font
- +#
- +
- +all: links first_rule
- +
- +FONTMAPFILE = cp437.uni
- +
- +L_TARGET := char.a
- +M_OBJS :=
- +L_OBJS := tty_io.o n_tty.o console.o keyboard.o serial.o \
- + tty_ioctl.o pty.o vt.o mem.o vc_screen.o random.o \
- + defkeymap.o consolemap.o iic.o
- +
- +# vesa_blank.o selection.o
- +
- +
- +ifeq ($(CONFIG_PRINTER),y)
- + L_OBJS += lp.o
- +else
- + ifeq ($(CONFIG_PRINTER),m)
- + M_OBJS += lp.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_MOUSE),y)
- + M = y
- +else
- + ifeq ($(CONFIG_MOUSE),m)
- + M = mm
- + endif
- +endif
- +
- +ifeq ($(CONFIG_KBDMOUSE),y)
- + M = y
- + L_OBJS += kbdmouse.o
- +else
- + ifeq ($(CONFIG_KBDMOUSE),m)
- + MM = m
- + M_OBJS += kbdmouse.o
- + endif
- +endif
- +
- +ifdef M
- + L_OBJS += mouse.o
- +else
- + ifdef MM
- + M_OBJS += mouse.o
- + endif
- +endif
- +
- +include $(TOPDIR)/Rules.make
- +
- +fastdep: links uni_hash.tbl
- +
- +consolemap.o:
- +
- +conmakehash: conmakehash.c
- + $(HOSTCC) -o conmakehash conmakehash.c
- +
- +uni_hash.tbl: $(FONTMAPFILE) conmakehash
- + ./conmakehash $(FONTMAPFILE) > uni_hash.tbl
- +
- +LK = conmakehash.c consolemap.c consolemap.h cp437.uni kbd_kern.h lp.c random.c
- +
- +links:
- + -@for f in $(LK); do \
- + ln -s ../../../../drivers/char/$$f .; \
- + done
- + touch links
- +
- +LINKCLEAN:
- + -@for f in $(LK); do \
- + if [ -L $$f ]; then rm -f $$f; fi; \
- + done
- + rm -f links
- diff -urNwbB linux/arch/arm/drivers/char/console.c linux.arm/arch/arm/drivers/char/console.c
- --- linux/arch/arm/drivers/char/console.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/console.c Sun Mar 3 12:31:06 1996
- @@ -0,0 +1,2722 @@
- +/*
- + * linux/arch/arm/drivers/char/console.c
- + *
- + * Modifications (C) 1995, 1996 Russell King
- + */
- +
- +/*
- + * This module exports the console io functions:
- + *
- + * 'int vc_allocate(unsigned int console)'
- + * 'int vc_cons_allocated(unsigned int console)'
- + * 'int vc_resize(unsigned long lines,unsigned long cols)'
- + * 'void vc_disallocate(unsigned int currcons)'
- + *
- + * 'unsigned long con_init(unsigned long)'
- + * S 'int con_open(struct tty_struct *tty,struct file *filp)'
- + * S 'void con_write(struct tty_struct *tty)'
- + * S 'void console_print(const char *b)'
- + * 'void update_screen(int new_console)'
- + *
- + * 'void blank_screen(void)'
- + * 'void unblank_screen(void)'
- + * 'void poke_blanked_console(void)'
- + * 'void scrollback(int lines)' *
- + * 'void scrollfront(int lines)' *
- + * 'int do_screendump(int arg)'
- + *
- + * 'int con_get_font(char *)'
- + * 'int con_set_font(char *)'
- + * 'int con_get_trans(char *)'
- + * 'int con_set_trans(char *)'
- + *
- + * 'int set_selection(const int arg)' *
- + * 'int paste_selection(struct tty_struct *tty)' *
- + * 'int sel_loadlut(const int arg)' *
- + * 'int mouse_reporting(void)' *
- + *
- + */
- +
- +#define BLANK 0x0020
- +
- +/* A bitmap for codes <32. A bit of 1 indicates that the code
- + * corresponding to that bit number invokes a special action
- + * (such as cursor movement) and should not be displayed as a
- + * glyph unless the disp_ctrl mode is explicitly enabled.
- + */
- +#define CTRL_ACTION 0x0d00ff81
- +#define CTRL_ALWAYS 0x0800f501 /* Cannot be overriden by disp_ctrl */
- +
- +#include <linux/config.h>
- +#include <linux/sched.h>
- +#include <linux/timer.h>
- +#include <linux/interrupt.h>
- +#include <linux/tty.h>
- +#include <linux/tty_flip.h>
- +#include <linux/kernel.h>
- +#include <linux/errno.h>
- +#include <linux/kd.h>
- +#include <linux/major.h>
- +#include <linux/mm.h>
- +#include <linux/malloc.h>
- +
- +#include <asm/segment.h>
- +#include <asm/irq-no.h>
- +
- +#include "kbd_kern.h"
- +#include "vt_kern.h"
- +#include "consolemap.h"
- +
- +/* ARM Extensions */
- +extern void memc_write (int reg, int val);
- +extern void vidc_write (int reg, int val);
- +extern void ll_char_write(unsigned long ps, int ch, unsigned char forec,
- + unsigned char backc, unsigned char flgs);
- +extern void memfastset (void *ptr, unsigned long word, int length);
- +extern void prints (const char *, ...);
- +extern void map_screen_mem (unsigned long vid_base, int remap);
- +static void ll_erase(int currcons,unsigned char sx,unsigned char sy,
- + unsigned char cx,unsigned char cy);
- +static void vsync_irq(int irq, struct pt_regs *regs);
- +static void put_cursor(char on_off,unsigned long cpp);
- +unsigned char bytes_per_char_h;
- +unsigned char bytes_per_char_v;
- +unsigned long video_addr_mask;
- +/* MUST get rid of these */
- +#define BOLD 0x01
- +#define ITALIC 0x02
- +#define UNDERLINE 0x04
- +#define FLASH 0x08
- +#define INVERSE 0x10
- +#define FLAGS ((underline?UNDERLINE:0)|(reverse?INVERSE:0)|(blink?FLASH:0)|intensity)
- +
- +#include <linux/ctype.h>
- +
- +/* Routines for selection control. */
- +int set_selection(const int arg, struct tty_struct *tty);
- +int paste_selection(struct tty_struct *tty);
- +static void clear_selection(void);
- +static void highlight_pointer(const int currcons,const int where);
- +
- +#define SEL_BUFFER_SIZE 4096
- +int sel_cons = 0;
- +static int sel_start = -1;
- +static int sel_end;
- +static char sel_buffer[SEL_BUFFER_SIZE] = { '\0' };
- +
- +
- +
- +#ifndef MIN
- +#define MIN(a,b) ((a) < (b) ? (a) : (b))
- +#endif
- +
- +struct tty_driver console_driver;
- +static int console_refcount;
- +static struct tty_struct *console_table[MAX_NR_CONSOLES];
- +static struct termios *console_termios[MAX_NR_CONSOLES];
- +static struct termios *console_termios_locked[MAX_NR_CONSOLES];
- +
- +#define NPAR 16
- +
- +static void con_setsize(unsigned long rows, unsigned long cols);
- +static void vc_init(unsigned int console, unsigned long rows, unsigned long cols,
- + int do_clear);
- +static void get_scrmem(int currcons);
- +static void set_scrmem(int currcons, long offset);
- +static void set_origin(int currcons);
- +static void blank_screen(void);
- +static void unblank_screen(void);
- +void poke_blanked_console(void);
- +static void gotoxy(int currcons, int new_x, int new_y);
- +static void save_cur(int currcons);
- +static inline void set_cursor(int currcons);
- +static void reset_terminal(int currcons, int do_clear);
- +extern void reset_vc(unsigned int new_console);
- +extern void vt_init(void);
- +extern void register_console(void (*proc)(const char *));
- +extern void compute_shiftstate(void);
- +extern void reset_palette (int currcons) ;
- +extern void set_palette (void) ;
- +
- +/* Description of the hardware situation */
- +static unsigned long video_mem_base; /* Base of video memory */
- +static unsigned long video_mem_term; /* End of video memory */
- + unsigned long video_num_columns; /* Number of text columns */
- +static unsigned long video_num_lines; /* Number of text lines */
- +static unsigned long video_size_row;
- +static unsigned long video_screen_size;
- +static unsigned long video_buf_size;
- +static int can_do_color = 1;
- +static int printable = 0; /* Is console ready for printing? */
- +
- +static unsigned short console_charmask = 0x0ff;
- +
- +static unsigned char *vc_scrbuf[MAX_NR_CONSOLES];
- +
- +static int console_blanked = 0;
- +static int blankinterval = 10*60*HZ;
- +
- +struct vc_data {
- + unsigned long vc_screenbuf_size;
- + unsigned short vc_video_erase_char; /* Background erase character */
- + unsigned char vc_attr; /* Current attributes */
- + unsigned char vc_def_forecol;
- + unsigned char vc_def_backcol;
- + unsigned char vc_forecol;
- + unsigned char vc_backcol;
- + unsigned char vc_s_forecol;
- + unsigned char vc_s_backcol;
- + unsigned char vc_cursoron; /* Cursor on/off */
- + unsigned char vc_ulcolor; /* Colour for underline mode */
- + unsigned char vc_halfcolor; /* Colour for half intensity mode */
- + unsigned long vc_origin; /* Used for EGA/VGA fast scroll */
- + unsigned long vc_scr_end; /* Used for EGA/VGA fast scroll */
- + unsigned long vc_pos;
- + unsigned long vc_x,vc_y;
- + unsigned long vc_top,vc_bottom;
- + unsigned long vc_cstate;
- + unsigned long vc_npar,vc_par[NPAR];
- + unsigned long vc_video_mem_start; /* Start of video RAM */
- + unsigned long vc_video_mem_end; /* End of video RAM (sort of) */
- + unsigned long vc_saved_x;
- + unsigned long vc_saved_y;
- + /* mode flags */
- + unsigned long vc_charset : 1; /* Character set G0 / G1 */
- + unsigned long vc_s_charset : 1; /* Saved character set */
- + unsigned long vc_disp_ctrl : 1; /* Display chars < 32? */
- + unsigned long vc_toggle_meta : 1; /* Toggle high bit? */
- + unsigned long vc_decscnm : 1; /* Screen Mode */
- + unsigned long vc_decom : 1; /* Origin Mode */
- + unsigned long vc_decawm : 1; /* Autowrap Mode */
- + unsigned long vc_deccm : 1; /* Cursor Visible */
- + unsigned long vc_decim : 1; /* Insert Mode */
- + unsigned long vc_deccolm : 1; /* 80/132 Column Mode */
- + /* attribute flags */
- + unsigned long vc_intensity : 2; /* 0=half-bright, 1=normal, 2=bold */
- + unsigned long vc_underline : 1;
- + unsigned long vc_blink : 1;
- + unsigned long vc_reverse : 1;
- + unsigned long vc_s_intensity : 2; /* saved rendition */
- + unsigned long vc_s_underline : 1;
- + unsigned long vc_s_blink : 1;
- + unsigned long vc_s_reverse : 1;
- + /* misc */
- + unsigned long vc_ques : 1;
- + unsigned long vc_need_wrap : 1;
- + unsigned long vc_has_scrolled : 1; /* Info for unblank_screen */
- + unsigned long vc_kmalloced : 1; /* kfree_s() needed */
- + unsigned long vc_report_mouse : 2;
- + unsigned char vc_utf : 1; /* Unicode UTF-8 encoding */
- + unsigned char vc_utf_count;
- + unsigned long vc_utf_char;
- + unsigned long vc_tab_stop[5]; /* Tab stops. 160 columns. */
- + unsigned short *vc_translate;
- + unsigned char vc_G0_charset;
- + unsigned char vc_G1_charset;
- + unsigned char vc_saved_G0;
- + unsigned char vc_saved_G1;
- + unsigned long * vc_paletteentries;
- + /* additional information is in vt_kern.h */
- +};
- +
- +static struct vc {
- + struct vc_data *d;
- +
- + /* might add scrmem, vt_struct, kbd at some time,
- + to have everything in one place - the disadvantage
- + would be that vc_cons etc can no longer be static */
- +} vc_cons [MAX_NR_CONSOLES];
- +
- +#define screenbuf_size (vc_cons[currcons].d->vc_screenbuf_size)
- +#define origin (vc_cons[currcons].d->vc_origin)
- +#define scr_end (vc_cons[currcons].d->vc_scr_end)
- +#define pos (vc_cons[currcons].d->vc_pos)
- +#define top (vc_cons[currcons].d->vc_top)
- +#define bottom (vc_cons[currcons].d->vc_bottom)
- +#define x (vc_cons[currcons].d->vc_x)
- +#define y (vc_cons[currcons].d->vc_y)
- +#define vc_state (vc_cons[currcons].d->vc_cstate)
- +#define npar (vc_cons[currcons].d->vc_npar)
- +#define par (vc_cons[currcons].d->vc_par)
- +#define ques (vc_cons[currcons].d->vc_ques)
- +#define attr (vc_cons[currcons].d->vc_attr)
- +#define saved_x (vc_cons[currcons].d->vc_saved_x)
- +#define saved_y (vc_cons[currcons].d->vc_saved_y)
- +#define translate (vc_cons[currcons].d->vc_translate)
- +#define G0_charset (vc_cons[currcons].d->vc_G0_charset)
- +#define G1_charset (vc_cons[currcons].d->vc_G1_charset)
- +#define saved_G0 (vc_cons[currcons].d->vc_saved_G0)
- +#define saved_G1 (vc_cons[currcons].d->vc_saved_G1)
- +#define utf (vc_cons[currcons].d->vc_utf)
- +#define utf_count (vc_cons[currcons].d->vc_utf_count)
- +#define utf_char (vc_cons[currcons].d->vc_utf_char)
- +#define video_mem_start (vc_cons[currcons].d->vc_video_mem_start)
- +#define video_mem_end (vc_cons[currcons].d->vc_video_mem_end)
- +#define video_erase_char (vc_cons[currcons].d->vc_video_erase_char)
- +#define disp_ctrl (vc_cons[currcons].d->vc_disp_ctrl)
- +#define toggle_meta (vc_cons[currcons].d->vc_toggle_meta)
- +#define decscnm (vc_cons[currcons].d->vc_decscnm)
- +#define decom (vc_cons[currcons].d->vc_decom)
- +#define decawm (vc_cons[currcons].d->vc_decawm)
- +#define deccm (vc_cons[currcons].d->vc_deccm)
- +#define decim (vc_cons[currcons].d->vc_decim)
- +#define deccolm (vc_cons[currcons].d->vc_deccolm)
- +#define need_wrap (vc_cons[currcons].d->vc_need_wrap)
- +#define has_scrolled (vc_cons[currcons].d->vc_has_scrolled)
- +#define kmalloced (vc_cons[currcons].d->vc_kmalloced)
- +#define report_mouse (vc_cons[currcons].d->vc_report_mouse)
- +#define forecol (vc_cons[currcons].d->vc_forecol)
- +#define backcol (vc_cons[currcons].d->vc_backcol)
- +#define s_forecol (vc_cons[currcons].d->vc_s_forecol)
- +#define s_backcol (vc_cons[currcons].d->vc_s_backcol)
- +#define def_forecol (vc_cons[currcons].d->vc_def_forecol)
- +#define def_backcol (vc_cons[currcons].d->vc_def_backcol)
- +#define cursoron (vc_cons[currcons].d->vc_cursoron)
- +#define charset (vc_cons[currcons].d->vc_charset)
- +#define s_charset (vc_cons[currcons].d->vc_s_charset)
- +#define intensity (vc_cons[currcons].d->vc_intensity)
- +#define underline (vc_cons[currcons].d->vc_underline)
- +#define blink (vc_cons[currcons].d->vc_blink)
- +#define reverse (vc_cons[currcons].d->vc_reverse)
- +#define s_intensity (vc_cons[currcons].d->vc_s_intensity)
- +#define s_underline (vc_cons[currcons].d->vc_s_underline)
- +#define s_blink (vc_cons[currcons].d->vc_s_blink)
- +#define s_reverse (vc_cons[currcons].d->vc_s_reverse)
- +#define tab_stop (vc_cons[currcons].d->vc_tab_stop)
- +#define paletteentries (vc_cons[currcons].d->vc_paletteentries)
- +
- +#define vcmode (vt_cons[currcons]->vc_mode)
- +#define structsize (sizeof(struct vc_data) + sizeof(struct vt_struct))
- +
- +/* ARC Extention */
- +char cmap_80[][8]=
- +{
- + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* */
- + {0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x00}, /* ! */
- + {0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00}, /* " */
- + {0x36,0x36,0x7F,0x36,0x7F,0x36,0x36,0x00}, /* # */
- + {0x0C,0x3F,0x68,0x3E,0x0B,0x7E,0x18,0x00}, /* $ */
- + {0x60,0x66,0x0C,0x18,0x30,0x66,0x06,0x00}, /* % */
- + {0x38,0x6C,0x6C,0x38,0x6D,0x66,0x3B,0x00}, /* & */
- + {0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00}, /* ' */
- + {0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00}, /* ( */
- + {0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00}, /* ) */
- + {0x00,0x18,0x7E,0x3C,0x7E,0x18,0x00,0x00}, /* * */
- + {0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00}, /* + */
- + {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30}, /* , */
- + {0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00}, /* - */
- + {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00}, /* . */
- + {0x00,0x06,0x0C,0x18,0x30,0x60,0x00,0x00}, /* / */
- + {0x3C,0x66,0x6E,0x7E,0x76,0x66,0x3C,0x00}, /* 0 */
- + {0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00}, /* 1 */
- + {0x3C,0x66,0x06,0x0C,0x18,0x30,0x7E,0x00}, /* 2 */
- + {0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00}, /* 3 */
- + {0x0C,0x1C,0x3C,0x6C,0x7E,0x0C,0x0C,0x00}, /* 4 */
- + {0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00}, /* 5 */
- + {0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00}, /* 6 */
- + {0x7E,0x06,0x0C,0x18,0x30,0x30,0x30,0x00}, /* 7 */
- + {0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00}, /* 8 */
- + {0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00}, /* 9 */
- + {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x00}, /* : */
- + {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x30}, /* ; */
- + {0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x00}, /* < */
- + {0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00}, /* = */
- + {0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x00}, /* > */
- + {0x3C,0x66,0x0C,0x18,0x18,0x00,0x18,0x00}, /* ? */
- + {0x3C,0x66,0x6E,0x6A,0x6E,0x60,0x3C,0x00}, /* @ */
- + {0x3C,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* A */
- + {0x7C,0x66,0x66,0x7C,0x66,0x66,0x7C,0x00}, /* B */
- + {0x3C,0x66,0x60,0x60,0x60,0x66,0x3C,0x00}, /* C */
- + {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}, /* D */
- + {0x7E,0x60,0x60,0x7C,0x60,0x60,0x7E,0x00}, /* E */
- + {0x7E,0x60,0x60,0x7C,0x60,0x60,0x60,0x00}, /* F */
- + {0x3C,0x66,0x60,0x6E,0x66,0x66,0x3C,0x00}, /* G */
- + {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* H */
- + {0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00}, /* I */
- + {0x3E,0x0C,0x0C,0x0C,0x0C,0x6C,0x38,0x00}, /* J */
- + {0x66,0x6C,0x78,0x70,0x78,0x6C,0x66,0x00}, /* K */
- + {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00}, /* L */
- + {0x63,0x77,0x7F,0x6B,0x6B,0x63,0x63,0x00}, /* M */
- + {0x66,0x66,0x76,0x7E,0x6E,0x66,0x66,0x00}, /* N */
- + {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}, /* O */
- + {0x7C,0x66,0x66,0x7C,0x60,0x60,0x60,0x00}, /* P */
- + {0x3C,0x66,0x66,0x66,0x6A,0x6C,0x36,0x00}, /* Q */
- + {0x7C,0x66,0x66,0x7C,0x6C,0x66,0x66,0x00}, /* R */
- + {0x3C,0x66,0x60,0x3C,0x06,0x66,0x3C,0x00}, /* S */
- + {0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, /* T */
- + {0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}, /* U */
- + {0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00}, /* V */
- + {0x63,0x63,0x6B,0x6B,0x7F,0x77,0x63,0x00}, /* W */
- + {0x66,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00}, /* X */
- + {0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00}, /* Y */
- + {0x7E,0x06,0x0C,0x18,0x30,0x60,0x7E,0x00}, /* Z */
- + {0x7C,0x60,0x60,0x60,0x60,0x60,0x7C,0x00}, /* [ */
- + {0x00,0x60,0x30,0x18,0x0C,0x06,0x00,0x00}, /* \ */
- + {0x3E,0x06,0x06,0x06,0x06,0x06,0x3E,0x00}, /* ] */
- + {0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00}, /* ^ */
- + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF}, /* _ */
- + {0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00}, /* ` */
- + {0x00,0x00,0x3C,0x06,0x3E,0x66,0x3E,0x00}, /* a */
- + {0x60,0x60,0x7C,0x66,0x66,0x66,0x7C,0x00}, /* b */
- + {0x00,0x00,0x3C,0x66,0x60,0x66,0x3C,0x00}, /* c */
- + {0x06,0x06,0x3E,0x66,0x66,0x66,0x3E,0x00}, /* d */
- + {0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00}, /* e */
- + {0x1C,0x30,0x30,0x7C,0x30,0x30,0x30,0x00}, /* f */
- + {0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x3C}, /* g */
- + {0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x00}, /* h */
- + {0x18,0x00,0x38,0x18,0x18,0x18,0x3C,0x00}, /* i */
- + {0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x70}, /* j */
- + {0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00}, /* k */
- + {0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00}, /* l */
- + {0x00,0x00,0x36,0x7F,0x6B,0x6B,0x63,0x00}, /* m */
- + {0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x00}, /* n */
- + {0x00,0x00,0x3C,0x66,0x66,0x66,0x3C,0x00}, /* o */
- + {0x00,0x00,0x7C,0x66,0x66,0x7C,0x60,0x60}, /* p */
- + {0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x07}, /* q */
- + {0x00,0x00,0x6C,0x76,0x60,0x60,0x60,0x00}, /* r */
- + {0x00,0x00,0x3E,0x60,0x3C,0x06,0x7C,0x00}, /* s */
- + {0x30,0x30,0x7C,0x30,0x30,0x30,0x1C,0x00}, /* t */
- + {0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x00}, /* u */
- + {0x00,0x00,0x66,0x66,0x66,0x3C,0x18,0x00}, /* v */
- + {0x00,0x00,0x63,0x6B,0x6B,0x7F,0x36,0x00}, /* w */
- + {0x00,0x00,0x66,0x3C,0x18,0x3C,0x66,0x00}, /* x */
- + {0x00,0x00,0x66,0x66,0x66,0x3E,0x06,0x3C}, /* y */
- + {0x00,0x00,0x7E,0x0C,0x18,0x30,0x7E,0x00}, /* z */
- + {0x0C,0x18,0x18,0x70,0x18,0x18,0x0C,0x00}, /* { */
- + {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, /* | */
- + {0x30,0x18,0x18,0x0E,0x18,0x18,0x30,0x00}, /* } */
- + {0x31,0x6B,0x46,0x00,0x00,0x00,0x00,0x00}, /* ~ */
- + {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF} /* */
- +};
- +
- +static unsigned long palette_4[]=
- +{
- + 0x1000, /* Black */
- + 0x100c, /* Red */
- + 0x10c0, /* Green */
- + 0x10cc, /* Yellow */
- + 0x1c00, /* Blue */
- + 0x1c0c, /* Magenta */
- + 0x1cc0, /* Cyan */
- + 0x1ccc, /* White */
- + 0x1000,
- + 0x100f,
- + 0x10f0,
- + 0x10ff,
- + 0x1f00,
- + 0x1f0F,
- + 0x1ff0,
- + 0x1fff,
- + 0x1000
- +};
- +
- +static unsigned long palette_8[]=
- +{
- + 0x1000,
- + 0x1111,
- + 0x1222,
- + 0x1333,
- + 0x1400,
- + 0x1511,
- + 0x1622,
- + 0x1733,
- + 0x1004,
- + 0x1115,
- + 0x1226,
- + 0x1337,
- + 0x1404,
- + 0x1515,
- + 0x1626,
- + 0x1737,
- + 0x1000
- +};
- +
- +static unsigned long *default_palette_entries = palette_4;
- +
- +/* -------------------------------------------------------------------------------
- + * Low level char write
- + * ------------------------------------------------------------------------------- */
- +
- +static inline void charwrite(int currcons,unsigned long ps,int ch)
- +{
- + unsigned long *buffer = (unsigned long *)vc_scrbuf[currcons];
- + unsigned int index;
- + unsigned char flgs;
- +
- + flgs=FLAGS;
- +
- + if(currcons == fg_console)
- + ll_char_write(ps, ch, forecol, backcol, flgs);
- +
- + index = (x + y * video_num_columns);
- +
- + buffer[index] = (flgs<<24)|(backcol<<16)|(forecol<<8)|ch;
- +}
- +
- +static char cursor_on=0;
- +unsigned long cp;
- +
- +static inline void remove_cursors(int currcons)
- +{
- + unsigned long flags;
- +
- + save_flags(flags);
- + cli();
- + if(--cursoron==0 && currcons == fg_console)
- + put_cursor(0, cp);
- + restore_flags(flags);
- +}
- +
- +static inline void restore_cursors(int currcons)
- +{
- + unsigned long flags;
- +
- + save_flags(flags);
- + cli();
- + if(++cursoron==1 && cursor_on && currcons == fg_console)
- + put_cursor(1,cp);
- + restore_flags(flags);
- +}
- +
- +/* -----------------------------------------------------------------------------------------
- + * VC stuff
- + * ----------------------------------------------------------------------------------------- */
- +
- +int vc_cons_allocated(unsigned int i)
- +{
- + return (i < MAX_NR_CONSOLES && vc_cons[i].d);
- +}
- +
- +int vc_allocate(unsigned int i)
- +{
- + if (i >= MAX_NR_CONSOLES)
- + return -ENODEV;
- + if (!vc_cons[i].d) {
- + long p, q;
- +
- + /* prevent users from taking too much memory */
- + if (i >= MAX_NR_USER_CONSOLES && !suser())
- + return -EPERM;
- +
- + /* due to the granularity of kmalloc, we waste some memory here */
- + /* the alloc is done in two steps, to optimize the common situation
- + of a 25x80 console (structsize=216, video_screen_size=4000) */
- + q = (long) kmalloc(video_buf_size, GFP_KERNEL);
- + if (!q)
- + return -ENOMEM;
- + p = (long) kmalloc(structsize, GFP_KERNEL);
- + if (!p) {
- + kfree_s((char *) q, video_buf_size);
- + return -ENOMEM;
- + }
- +
- + memset((void *)q, 0, video_buf_size);
- +
- + vc_cons[i].d = (struct vc_data *) p;
- + p += sizeof(struct vc_data);
- + vt_cons[i] = (struct vt_struct *) p;
- + vc_scrbuf[i] = (unsigned char *) q;
- + vc_cons[i].d->vc_kmalloced = 1;
- + vc_cons[i].d->vc_screenbuf_size = video_buf_size;
- + vc_init (i, video_num_lines, video_num_columns, 1);
- + }
- + return 0;
- +}
- +
- +int vc_resize(unsigned long lines, unsigned long cols)
- +{/* TODO */
- + return -ENOMEM;
- +}
- +
- +void vc_disallocate(unsigned int currcons)
- +{
- + if(vc_cons_allocated(currcons))
- + {
- + if(kmalloced)
- + kfree_s(vc_scrbuf[currcons], screenbuf_size);
- + if(currcons >= MIN_NR_CONSOLES)
- + kfree_s(vc_cons[currcons].d, structsize);
- + vc_cons[currcons].d = 0;
- + }
- +}
- +
- +#define set_kbd(x) set_vc_kbd_mode(kbd_table+currcons,x)
- +#define clr_kbd(x) clr_vc_kbd_mode(kbd_table+currcons,x)
- +#define is_kbd(x) vc_kbd_mode(kbd_table+currcons,x)
- +
- +#define decarm VC_REPEAT
- +#define decckm VC_CKMODE
- +#define kbdapplic VC_APPLIC
- +#define lnm VC_CRLF
- +
- +/*
- + * this is what the terminal answers to a ESC-Z or csi0c query.
- + */
- +#define VT100ID "\033[?1;2c"
- +#define VT102ID "\033[?6c"
- +
- +static unsigned char color_4[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 };
- +static unsigned char color_8[] = {0x00, 0x18, 0x60, 0x78, 0x84, 0x9C, 0xE4, 0xFC,
- + 0x00, 0x1B, 0x63, 0x7B, 0x87, 0x9F, 0xE7, 0xFF};
- +static unsigned char *color_table = color_4;
- +
- +/*
- + * gotoxy() must verify all boundaries, because the arguments
- + * might also be negative. If a given position is out of
- + * bounds, the cursor is placed at the nearest margin.
- + */
- +static void gotoxy(int currcons,int new_x,int new_y)
- +{
- + int max_y;
- +
- + if(new_x<0)
- + x = 0;
- + else
- + if(new_x>=video_num_columns)
- + x = video_num_columns-1;
- + else
- + x = new_x;
- +
- + if(decom)
- + {
- + new_y += top;
- + max_y = bottom;
- + }
- + else
- + max_y=video_num_lines;
- +
- + if(new_y < 0)
- + y = 0;
- + else
- + if(new_y >= max_y)
- + y = max_y-1;
- + else
- + y = new_y;
- +
- + pos = origin + (y*video_num_columns*bytes_per_char_v+x)*bytes_per_char_h;
- + need_wrap=0;
- +}
- +
- +static unsigned long __real_origin;
- +static unsigned long __origin; /* Offset of currently displayed screen */
- +
- +static void __set_origin(unsigned long offset)
- +{
- + unsigned long flags;
- + clear_selection();
- + save_flags(flags); cli();
- + __origin = offset;
- +
- + memc_write(0,offset>>2);
- + memc_write(1,0);
- + memc_write(2,video_addr_mask>>2);
- + restore_flags(flags);
- +}
- +
- +/*
- +void scrollback(int lines)
- +{
- +}
- +
- +void scrollfront(int lines)
- +{
- +}
- + */
- +
- +static void set_origin(int currcons)
- +{
- + if(currcons != fg_console || vcmode == KD_GRAPHICS)
- + return;
- + __real_origin = origin - video_mem_start;
- + if(__real_origin >= video_addr_mask)
- + __real_origin -= video_addr_mask;
- + __set_origin(__real_origin);
- +}
- +
- +static void hide_cursor(void)
- +{
- + put_cursor(0,-1);
- +}
- +
- +static void set_cursor(int currcons)
- +{
- + unsigned long flags;
- +
- + if(currcons != fg_console || vcmode == KD_GRAPHICS)
- + return;
- +
- + if(__real_origin != __origin)
- + __set_origin(__real_origin);
- + save_flags(flags);
- + cli();
- +
- + if(deccm)
- + cp = pos + video_num_columns * bytes_per_char_h * (bytes_per_char_v - 1);
- + else
- + hide_cursor();
- + restore_flags(flags);
- +}
- +
- +/* --------------------------------------------------------------------------------
- + * Screen scrolling
- + * -------------------------------------------------------------------------------- */
- +
- +static void scrup(int currcons,unsigned int t, unsigned int b,unsigned char l)
- +{
- + unsigned char flgs;
- +
- + if(b > video_num_columns || t >= b)
- + return;
- +
- + if(currcons == fg_console)
- + {
- + if(t || b != video_num_lines)
- + {
- + if(l<t-b)
- + memmove((void*)(origin+t*video_size_row),
- + (void*)(origin+(t+l)*video_size_row),
- + (b-t-l)*video_size_row);
- + else
- + if(l>b-t)
- + l=b-t;
- +
- + memfastset((void*)(origin+(b-l)*video_size_row),
- + 0x11111111*backcol,l*video_size_row);
- + }
- + else
- + {
- + memfastset((void*)(origin+l*video_size_row*video_num_lines),
- + 0x11111111L*backcol,l*video_size_row);
- + pos-=origin;
- + origin+=l*video_size_row;
- + if(origin>=video_mem_term)
- + origin=origin-video_mem_term+video_mem_base;
- + pos+=origin;
- + set_origin(currcons);
- + }
- + }
- + else
- + {
- + if(!t && b == video_num_lines)
- + {
- + pos-=origin;
- + origin+=l*video_size_row;
- + if(origin>=video_mem_term)
- + origin=origin-video_mem_term+video_mem_base;
- + pos+=origin;
- + }
- + }
- + if(l < t-b)
- + memmove((void*)(vc_scrbuf[currcons]+t*video_num_columns*4),
- + (void*)(vc_scrbuf[currcons]+(t+l)*video_num_columns*4),
- + (b-t-l)*video_num_columns*4);
- + else
- + if(l > b-t)
- + l=b-t;
- +
- + flgs=FLAGS;
- +
- + memfastset((void*)(vc_scrbuf[currcons]+(b-l)*video_num_columns*4),
- + (flgs << 24)|(backcol << 16)|(forecol << 8)|32,
- + l*video_num_columns*4);
- +}
- +
- +static void scrdown(int currcons,unsigned int t,unsigned int b,unsigned char l)
- +{
- + unsigned char flgs;
- +
- + if(b>video_num_columns || t >= b)
- + return;
- +
- + if(currcons == fg_console)
- + {
- + if(t || b != video_num_lines)
- + {
- + if(l<t-b)
- + memmove((void*)(origin+(t+l)*video_size_row),
- + (void*)(origin+t*video_size_row),
- + (b-t-l)*video_size_row);
- + else
- + if(l>b-t)
- + l=b-t;
- +
- + memfastset((void*)(origin+t*video_size_row),
- + 0x11111111*backcol,l*video_size_row);
- + }
- + else
- + {
- + pos-=origin;
- + origin-=l*video_size_row;
- + if(origin<video_mem_base)
- + origin=origin-video_mem_base+video_mem_term;
- + pos+=origin;
- + memfastset((void*)origin,0x11111111L*backcol,l*video_size_row);
- + set_origin(currcons);
- + }
- + }
- + else
- + {
- + if(!t && b == video_num_lines)
- + {
- + pos-=origin;
- + origin-=l*video_size_row;
- + if(origin<video_mem_base)
- + origin=origin-video_mem_base+video_mem_term;
- + pos+=origin;
- + }
- + }
- + if(l < t-b)
- + memmove((void*)(vc_scrbuf[currcons]+(t+l)*video_num_columns*4),
- + (void*)(vc_scrbuf[currcons]+t*video_num_columns*4),
- + (b-t-l)*video_num_columns*4);
- + else
- + if(l > b-t)
- + l = b-t;
- +
- + flgs=FLAGS;
- + memfastset((void*)(vc_scrbuf[currcons]+t*video_num_columns*4),
- + (flgs << 24)|(backcol << 16)|(forecol << 8)|32,
- + l*video_num_columns*4);
- +}
- +
- +static void lf(int currcons)
- +{
- + if (y + 1 < bottom) {
- + y++;
- + pos += video_size_row;
- + return;
- + }
- + scrup(currcons,top,bottom,1);
- + need_wrap=0;
- +}
- +
- +static void ri(int currcons)
- +{
- + if (y > top) {
- + y--;
- + pos -= video_size_row;
- + return;
- + }
- + scrdown(currcons,top,bottom,1);
- + need_wrap=0;
- +}
- +
- +static inline void cr(int currcons)
- +{
- + pos -= x*bytes_per_char_h;
- + need_wrap = x = 0;
- +}
- +
- +static inline void bs(int currcons)
- +{
- + if (x) {
- + if (!need_wrap) {
- + pos -= bytes_per_char_h;
- + x--;
- + }
- + need_wrap = 0;
- + }
- +}
- +
- +static inline void del(int currcons)
- +{
- + /* ignored */
- +}
- +
- +static void csi_J(int currcons, int vpar)
- +{
- + unsigned char countx,county;
- + unsigned char startx,starty;
- +
- + switch(vpar) {
- + case 0: /* erase from cursor to bottom of screen */
- + startx=x;
- + starty=y;
- + countx=video_num_columns-x;
- + county=video_num_lines-y-1;
- + break;
- + case 1: /* erase from top of screen to cursor */
- + startx=0;
- + starty=0;
- + countx=x;
- + county=y;
- + break;
- + case 2: /* erase entire screen */
- + startx=0;
- + starty=0;
- + countx=video_num_columns;
- + county=video_num_lines-1;
- + origin=video_mem_base;
- + set_origin(currcons);
- + gotoxy(currcons,x,y);
- + break;
- + default:
- + return;
- + }
- + ll_erase(currcons,startx,starty,countx,county);
- + need_wrap = 0;
- +}
- +
- +static void csi_K(int currcons, int vpar)
- +{
- + unsigned char countx;
- + unsigned char startx;
- +
- + switch(vpar) {
- + case 0: /* erase from cursor to end of line */
- + startx = x;
- + countx = video_num_columns - x;
- + break;
- + case 1: /* erase from beginning of line to cursor */
- + startx = 0;
- + countx = x;
- + break;
- + case 2: /* erase entire line */
- + startx=0;
- + countx=video_num_columns;
- + break;
- + default:
- + return;
- + }
- + ll_erase(currcons,startx,y,countx,0);
- + need_wrap = 0;
- +}
- +
- +static void csi_X(int currcons, int vpar) /* erase the following vpar positions */
- +{ /* not vt100? */
- + unsigned char countx,county;
- + unsigned char startx,starty;
- +
- + if (!vpar)
- + vpar++;
- +
- + startx = x;
- + starty = y;
- + countx = 0;
- + county = 0;
- +#if 0 /* TODO */
- + ll_erase(currcons,startx,starty,countx,county);
- +#endif
- + need_wrap = 0;
- +}
- +
- +
- +/*
- +static void update_addr(int currcons)
- +{
- +
- +}
- + */
- +static void default_attr(int currcons)
- +{
- + underline = 0;
- + reverse = 0;
- + blink = 0;
- + intensity = 0;
- +
- + forecol = def_forecol;
- + backcol = def_backcol;
- +}
- +
- +static void csi_m(int currcons)
- +{
- + int i;
- +
- + for(i=0;i<=npar;i++)
- + {
- + switch(par[i])
- + {
- + case 0: default_attr(currcons); break;
- + case 1: intensity|=BOLD; break; /* Bold */
- + case 2: intensity&=~BOLD; break; /* Feint */
- + case 3: intensity|=ITALIC; break; /* Italic */
- + case 4: underline = 1; break; /* Underline */
- + case 5:
- + case 6: blink = 1; break; /* Flash */
- + case 7: reverse = 1; break; /* Inverse chars */
- +
- + case 10: /* ANSI X3.64-1979 (SCO-ish?)
- + * Select primary font, don't display
- + * control chars if defined, don't set
- + * bit 8 on output.
- + */
- + translate = set_translate(charset == 0
- + ? G0_charset
- + : G1_charset);
- + disp_ctrl = 0;
- + toggle_meta = 0;
- + break;
- + case 11: /* ANSI X3.64-1979 (SCO-ish?)
- + * Select first alternate font, let's
- + * chars < 32 be displayed as ROM chars.
- + */
- + translate = set_translate(IBMPC_MAP);
- + disp_ctrl = 1;
- + toggle_meta = 0;
- + break;
- + case 12: /* ANSI X3.64-1979 (SCO-ish?)
- + * Select second alternate font, toggle
- + * high bit before displaying as ROM char.
- + */
- + translate = set_translate(IBMPC_MAP);
- + disp_ctrl = 1;
- + toggle_meta = 1;
- + break;
- +
- + case 21:
- + case 22: intensity = 0; break;
- + case 24: underline = 0; break;
- + case 25: blink = 0; break;
- + case 27: reverse = 0; break;
- + case 30:
- + case 31:
- + case 32:
- + case 33:
- + case 34:
- + case 35:
- + case 36:
- + case 37: forecol=color_table[par[i]-30]; break; /* Foreground colour */
- + case 38: forecol=def_forecol; underline = 1; break;
- + case 39: forecol=def_forecol; underline = 0; break; /* Default foreground colour */
- + case 40:
- + case 41:
- + case 42:
- + case 43:
- + case 44:
- + case 45:
- + case 46:
- + case 47: backcol=color_table[par[i]-40]; break; /* Background colour */
- + case 49: backcol=def_backcol; break; /* Default background colour */
- + }
- + }
- +}
- +
- +static void respond_string(char *p,struct tty_struct *tty)
- +{
- + while(*p)
- + tty_insert_flip_char(tty, *p++, 0);
- + tty_schedule_flip(tty);
- +}
- +
- +static void cursor_report(int currcons,struct tty_struct *tty)
- +{
- + char buf[40];
- +
- + sprintf(buf, "\033[%ld;%ldR",y + (decom ? top+1 : 1),
- + x + 1);
- + respond_string(buf, tty);
- +}
- +
- +static void status_report(struct tty_struct *tty)
- +{
- + respond_string("\033[0n",tty);
- +}
- +
- +static void respond_ID(struct tty_struct *tty)
- +{
- + respond_string(VT102ID,tty);
- +}
- +
- +static void mouse_report(int currcons, struct tty_struct *tty, int butt, int mrx, int mry)
- +{
- + char buf[8];
- +
- + sprintf(buf,"\033[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
- + (char)('!'+mry));
- + respond_string(buf,tty);
- +}
- +
- +/* invoked by ioctl(TIOCLINUX) */
- +int mouse_reporting(void)
- +{
- + int currcons = fg_console;
- +
- + return report_mouse;
- +}
- +
- +static void invert_screen(void)
- +{
- + /* Todo */
- +}
- +
- +unsigned long *screen_pos(int currcons, int screen_offset)
- +{
- + return (unsigned long *)(vc_scrbuf[currcons] + screen_offset * 4);
- +}
- +
- +void getconsxy(int currcons, char *p)
- +{
- + p[0] = x;
- + p[1] = y;
- +}
- +
- +void putconsxy(int currcons, char *p)
- +{
- + gotoxy(currcons, p[0], p[1]);
- + set_cursor(currcons);
- +}
- +
- +static void set_mode(int currcons, int on_off)
- +{
- + int i;
- +
- + for(i=0;i<=npar;i++)
- + if(ques) switch(par[i]) { /* DEC private modes set/reset */
- + case 1: /* Cursor keys send ^[Ox/^[[x */
- + if (on_off)
- + set_kbd(decckm);
- + else
- + clr_kbd(decckm);
- + break;
- + case 3: /* 80/132 mode switch unimplemented */
- + deccolm = on_off;
- +#if 0
- + csi_J(currcons,2);
- + gotoxy(currcons,0,0);
- +#endif
- + break;
- + case 5: /* Inverted screen on/off */
- + if (decscnm != on_off) {
- + decscnm = on_off;
- + invert_screen();
- +/* update_attr(); */
- + }
- + break;
- + case 6: /* Origin relative/absolute */
- + decom = on_off;
- + gotoxy(currcons,0,0);
- + break;
- + case 7: /* Autowrap on/off */
- + decawm = on_off;
- + break;
- + case 8: /* Autorepeat on/off */
- + if (on_off)
- + set_kbd(decarm);
- + else
- + clr_kbd(decarm);
- + break;
- + case 9:
- + report_mouse = on_off ? 1 : 0;
- + break;
- + case 25: /* Cursor on/off */
- + deccm = on_off;
- + set_cursor(currcons);
- + break;
- + case 1000:
- + report_mouse = on_off ? 2 : 0;
- + break;
- + } else switch(par[i]) { /* ANSI modes set/reset */
- + case 3: /* Monitor (display ctrls) */
- + disp_ctrl = on_off;
- + break;
- + case 4: /* Insert mode on/off */
- + decim = on_off;
- + break;
- + case 20: /* Lf, Enter = CrLf/Lf */
- + if (on_off)
- + set_kbd(lnm);
- + else
- + clr_kbd(lnm);
- + break;
- + }
- +}
- +
- +static void setterm_command(int currcons)
- +{
- + switch(par[0]) {
- + case 1: /* Set colour for underline mode (implemented as an underline) */
- + break;
- + case 2: /* set colour for half intensity mode (implemented as half) */
- + break;
- + case 8:
- + def_forecol = forecol;
- + def_backcol = backcol;
- + break;
- + case 9:
- + blankinterval=((par[1]<60)?par[1]:60)*60*HZ;
- + break;
- + }
- +}
- +
- +static void insert_char(int currcons)
- +{
- + register unsigned char *c,*cc;
- + register unsigned char row;
- + register int col;
- +
- + c = (unsigned char*)pos;
- + cc = (unsigned char*)pos + bytes_per_char_h;
- +
- + for (row = 0; row < bytes_per_char_v; row++) {
- + for(col = (video_num_columns - x - 1) * bytes_per_char_h - 1; col >= 0; col--)
- + cc[col] = c[col];
- + *c = 0x11*backcol;
- + c += video_num_columns * bytes_per_char_h;
- + cc += video_num_columns * bytes_per_char_h;
- + }
- +}
- +
- +static void insert_line(int currcons,int n)
- +{
- + scrdown(currcons,y,bottom,n);
- + need_wrap = 0;
- +}
- +
- +static void delete_char(int currcons)
- +{
- + register unsigned char *c, *cc;
- + register unsigned char row;
- + register unsigned int col;
- +
- + c = (unsigned char*)pos;
- + cc = (unsigned char*)pos + bytes_per_char_h;
- +
- + for (row=0; row < bytes_per_char_v; row++) {
- + for (col=0; col < bytes_per_char_h * (video_num_columns - x - 1); col++) {
- + c[col] = cc[col];
- + cc[col] = 0x11*backcol;
- + }
- + c += video_num_columns * bytes_per_char_h;
- + cc += video_num_columns * bytes_per_char_h;
- + }
- +}
- +
- +static void delete_line(int currcons,int n)
- +{
- + scrup(currcons,y,bottom,n);
- + need_wrap = 0;
- +}
- +
- +static void csi_at(int currcons,int nr)
- +{
- + if (nr > video_num_columns - x)
- + nr = video_num_columns-x;
- + else if (!nr)
- + nr = 1;
- + while(nr--)
- + insert_char(currcons);
- +}
- +
- +static void csi_L(int currcons,int nr)
- +{
- + if (nr > video_num_lines - y)
- + nr = video_num_lines - y;
- + else if (!nr)
- + nr = 1;
- + insert_line(currcons,nr);
- +}
- +
- +static void csi_P(int currcons,int nr)
- +{
- + if (nr > video_num_columns)
- + nr = video_num_columns;
- + else if (!nr)
- + nr = 1;
- + while(nr--)
- + delete_char(currcons);
- +}
- +
- +static void csi_M(int currcons,int nr)
- +{
- + if (nr > video_num_lines)
- + nr = video_num_lines;
- + else if (!nr)
- + nr = 1;
- + delete_line(currcons,nr);
- +}
- +
- +static void save_cur(int currcons)
- +{
- + saved_x = x;
- + saved_y = y;
- + saved_G0 = G0_charset;
- + saved_G1 = G1_charset;
- + s_intensity = intensity;
- + s_blink = blink;
- + s_underline = underline;
- + s_reverse = reverse;
- + s_forecol = forecol;
- + s_backcol = backcol;
- +}
- +
- +static void restore_cur(int currcons)
- +{
- + gotoxy(currcons,saved_x,saved_y);
- + intensity = s_intensity;
- + underline = s_underline;
- + blink = s_blink;
- + reverse = s_reverse;
- + forecol = s_forecol;
- + backcol = s_backcol;
- + G0_charset = saved_G0;
- + G1_charset = saved_G1;
- + need_wrap = 0;
- +}
- +
- +enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
- + EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
- + ESpalette };
- +
- +static void reset_terminal(int currcons,int do_clear)
- +{
- + top = 0;
- + bottom = video_num_lines;
- + vc_state = ESnormal;
- + ques = 0;
- + translate = set_translate(LAT1_MAP);
- + G0_charset = LAT1_MAP;
- + G1_charset = GRAF_MAP;
- + charset = 0;
- + need_wrap = 0;
- + report_mouse = 0;
- + utf = 0; /* ? *** */
- + utf_count = 0;
- +
- + paletteentries = 0;
- + disp_ctrl = 0;
- + toggle_meta = 0;
- +
- + def_forecol = color_table[7];
- + def_backcol = color_table[0];
- + forecol = color_table[7];
- + backcol = color_table[0];
- +
- + decscnm = 0;
- + decom = 0;
- + decawm = 1;
- + deccm = 1;
- + decim = 0;
- +
- + set_kbd(decarm);
- + clr_kbd(decckm);
- + clr_kbd(kbdapplic);
- + clr_kbd(lnm);
- + kbd_table[currcons].lockstate = 0;
- + kbd_table[currcons].ledmode = LED_SHOW_FLAGS;
- + kbd_table[currcons].ledflagstate = kbd_table[currcons].default_ledflagstate;
- + set_leds();
- +
- + default_attr(currcons);
- +
- + tab_stop[0] = 0x01010100;
- + tab_stop[1] =
- + tab_stop[2] =
- + tab_stop[3] =
- + tab_stop[4] = 0x01010101;
- +
- + gotoxy(currcons,0,0);
- + save_cur(currcons);
- + if (do_clear)
- + csi_J(currcons,2);
- +}
- +
- +/*
- + * Turn the Scroll-Lock LED on when the tty is stopped
- + */
- +static void con_stop(struct tty_struct *tty)
- +{
- + int console_num;
- + if (!tty)
- + return;
- + console_num = MINOR(tty->device) - (tty->driver.minor_start);
- + if (!vc_cons_allocated(console_num))
- + return;
- + set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
- + set_leds();
- +}
- +
- +/*
- + * Turn the Scroll-Lock LED off when the console is started
- + */
- +static void con_start(struct tty_struct *tty)
- +{
- + int console_num;
- + if(!tty)
- + return;
- + console_num = MINOR(tty->device) - (tty->driver.minor_start);
- + if (!vc_cons_allocated(console_num))
- + return;
- + clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
- + set_leds();
- +}
- +
- +static int con_write(struct tty_struct * tty, int from_user,
- + const unsigned char *buf, int count)
- +{
- + int c, tc, ok, n = 0, r = 0;
- + unsigned int currcons;
- + struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
- + unsigned char buffer[32], *p = buffer;
- +
- + currcons = vt->vc_num;
- + if (!vc_cons_allocated(currcons)) {
- + /* could this happen? */
- + static int error = 0;
- + if(!error) {
- + error = 1;
- + printk("con_write: tty %d not allocated\n", currcons+1);
- + }
- + return 0;
- + }
- +
- + remove_cursors(currcons);
- + if(currcons == sel_cons)
- + clear_selection();
- +
- + disable_bh(KEYBOARD_BH);
- + while(!tty->stopped && count) {
- + if(from_user && r == 0)
- + {
- + r = count;
- + if(r > 32) r = 32;
- + memcpy_fromfs(buffer, buf, r);
- + p = buffer;
- + }
- + c = from_user ? *p++ : *buf;
- + buf++; n++; count--;
- + if(from_user) r--;
- +
- + if (utf) {
- + /* Combine UTF-8 into Unicode */
- + /* Incomplete characters silently ignored */
- + if(c > 0x7f) {
- + if (utf_count > 0 && (c & 0xc0) == 0x80) {
- + utf_char = (utf_char << 6) | (c & 0x3f);
- + utf_count--;
- + if (utf_count == 0)
- + tc = c = utf_char;
- + else continue;
- + } else {
- + if ((c & 0xe0) == 0xc0) {
- + utf_count = 1;
- + utf_char = (c & 0x1f);
- + } else if ((c & 0xf0) == 0xe0) {
- + utf_count = 2;
- + utf_char = (c & 0x0f);
- + } else if ((c & 0xf8) == 0xf0) {
- + utf_count = 3;
- + utf_char = (c & 0x07);
- + } else if ((c & 0xfc) == 0xf8) {
- + utf_count = 4;
- + utf_char = (c & 0x03);
- + } else if ((c & 0xfe) == 0xfc) {
- + utf_count = 5;
- + utf_char = (c & 0x01);
- + } else
- + utf_count = 0;
- + continue;
- + }
- + } else {
- + tc = c;
- + utf_count = 0;
- + }
- + } else { /* no utf */
- + tc = translate[toggle_meta ? (c | 0x80) : c];
- + }
- +
- + /* If the origional code was < 32 we only allow a
- + * glyph to be displayed if the code is not normally
- + * used (such as for cursor movement) or if the
- + * disp_ctrl mode has been explicitly enabled.
- + * Note: ESC is *never* allowed to be displayed as
- + * that would disable all escape sequences!
- + * To display font position 0x1B, go into UTF mode
- + * and display character U+F01B, or change the mapping.
- + */
- + ok = (tc && (c >= 32 || (!utf && !(((disp_ctrl ? CTRL_ALWAYS
- + : CTRL_ACTION) >> c) & 1))));
- +
- + if (vc_state == ESnormal && ok) {
- + /* Now try to find out how to display it */
- + tc = conv_uni_to_pc(tc);
- + if (tc == -4) {
- + /* If we got -4 (not found) then see if we have
- + defined a replacement character (U+FFFD) */
- + tc = conv_uni_to_pc(0xfffd);
- + } else if (tc == -3) {
- + /* Bad hash table -- hope for the best */
- + tc = c;
- + }
- + if (tc & ~console_charmask)
- + continue; /* Conversion failed */
- +
- + if (need_wrap) {
- + cr(currcons);
- + lf(currcons);
- + }
- + if(decim)
- + insert_char(currcons);
- + charwrite(currcons, pos, tc);
- + if (x == video_num_columns - 1)
- + need_wrap = decawm;
- + else {
- + x++;
- + pos+=bytes_per_char_h;
- + }
- + continue;
- + }
- +
- + /*
- + * Control characters can be used in the _middle_
- + * of an escape sequence.
- + */
- + switch(c) {
- + case 7:
- + kd_mksound(0x637, HZ/8);
- + continue;
- + case 8:
- + bs(currcons);
- + continue;
- + case 9:
- + pos -= x * bytes_per_char_h;
- + while (x < video_num_columns - 1) {
- + x++;
- + if (tab_stop[x >> 5] & (1 << (x & 31)))
- + break;
- + }
- + pos += x * bytes_per_char_h;
- + continue;
- + case 10: case 11: case 12:
- + lf(currcons);
- + if(!is_kbd(lnm))
- + continue;
- + case 13:
- + cr(currcons);
- + continue;
- + case 14:
- + charset = 1;
- + translate = set_translate(G1_charset);
- + disp_ctrl = 1;
- + continue;
- + case 15:
- + charset = 0;
- + translate = set_translate(G0_charset);
- + disp_ctrl = 0;
- + continue;
- + case 24: case 26:
- + vc_state = ESnormal;
- + continue;
- + case 27:
- + vc_state = ESesc;
- + continue;
- + case 127:
- + del(currcons);
- + continue;
- + case 128+27:
- + vc_state = ESsquare;
- + continue;
- + }
- + switch(vc_state) {
- + case ESesc:
- + vc_state = ESnormal;
- + switch (c) {
- + case '[':
- + vc_state = ESsquare;
- + continue;
- + case '%':
- + vc_state = ESpercent;
- + continue;
- + case 'E':
- + cr(currcons);
- + lf(currcons);
- + continue;
- + case 'M':
- + ri(currcons);
- + continue;
- + case 'D':
- + lf(currcons);
- + continue;
- + case 'H':
- + tab_stop[x >> 5] |= (1 << (x & 31));
- + continue;
- + case 'Z':
- + respond_ID(tty);
- + continue;
- + case '7':
- + save_cur(currcons);
- + continue;
- + case '8':
- + restore_cur(currcons);
- + continue;
- + case '(':
- + vc_state = ESsetG0;
- + continue;
- + case ')':
- + vc_state = ESsetG1;
- + continue;
- + case '#':
- + vc_state = EShash;
- + continue;
- + case 'c':
- + reset_terminal(currcons,1);
- + continue;
- + case '>': /* Numeric keypad */
- + clr_kbd(kbdapplic);
- + continue;
- + case '=': /* Appl. keypad */
- + set_kbd(kbdapplic);
- + continue;
- + }
- + continue;
- + case ESnonstd:
- + if (c=='P') { /* palette escape sequence */
- + for (npar = 0; npar<NPAR; npar++)
- + par[npar] = 0;
- + npar = 0 ;
- + vc_state = ESpalette;
- + continue;
- + } else if (c=='R') { /* reset palette */
- + reset_palette (currcons);
- + vc_state = ESnormal;
- + } else
- + vc_state = ESnormal;
- + continue;
- + case ESpalette:
- + if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) {
- + par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ;
- + if(npar==7) {
- +#if 0
- + int i = par[0]*3, j = 1;
- + palette[i] = 16*par[j++];
- + palette[i++] += par[j++];
- + palette[i] = 16*par[j++];
- + palette[i++] += par[j++];
- + palette[i] = 16*par[j++];
- + palette[i++] += par[j];
- +#endif
- + set_palette() ;
- + vc_state = ESnormal;
- + }
- + } else
- + vc_state = ESnormal;
- + continue;
- + case ESsquare:
- + for(npar = 0; npar < NPAR ; npar++)
- + par[npar] = 0;
- + npar = 0;
- + vc_state = ESgetpars;
- + if (c == '[') { /* Function key */
- + vc_state=ESfunckey;
- + continue;
- + }
- + ques = (c == '?');
- + if (ques)
- + continue;
- + case ESgetpars:
- + if(c==';' && npar<NPAR-1) {
- + npar++;
- + continue;
- + } else if (c>='0' && c<='9') {
- + par[npar] = par[npar] * 10 + c - '0';
- + continue;
- + } else vc_state=ESgotpars;
- + case ESgotpars:
- + vc_state=ESnormal;
- + switch (c) {
- + case 'h':
- + set_mode(currcons,1);
- + continue;
- + case 'l':
- + set_mode(currcons,0);
- + continue;
- + case 'n':
- + if (!ques) {
- + if (par[0] == 5)
- + status_report(tty);
- + else if (par[0] == 6)
- + cursor_report(currcons,tty);
- + }
- + continue;
- + }
- + if (ques) {
- + ques = 0;
- + continue;
- + }
- + switch(c) {
- + case 'G': case '`':
- + if (par[0]) par[0]--;
- + gotoxy(currcons,par[0],y);
- + continue;
- + case 'A':
- + if (!par[0]) par[0]++;
- + gotoxy(currcons,x,y-par[0]);
- + continue;
- + case 'B': case 'e':
- + if (!par[0]) par[0]++;
- + gotoxy(currcons,x,y+par[0]);
- + continue;
- + case 'C': case 'a':
- + if (!par[0]) par[0]++;
- + gotoxy(currcons,x+par[0],y);
- + continue;
- + case 'D':
- + if (!par[0]) par[0]++;
- + gotoxy(currcons,x-par[0],y);
- + continue;
- + case 'E':
- + if (!par[0]) par[0]++;
- + gotoxy(currcons,0,y+par[0]);
- + continue;
- + case 'F':
- + if (!par[0]) par[0]++;
- + gotoxy(currcons,0,y-par[0]);
- + continue;
- + case 'd':
- + if (par[0]) par[0]--;
- + gotoxy(currcons,x,par[0]);
- + continue;
- + case 'H': case 'f':
- + if (par[0]) par[0]--;
- + if (par[1]) par[1]--;
- + gotoxy(currcons,par[1],par[0]);
- + continue;
- + case 'J':
- + csi_J(currcons,par[0]);
- + continue;
- + case 'K':
- + csi_K(currcons,par[0]);
- + continue;
- + case 'L':
- + csi_L(currcons,par[0]);
- + continue;
- + case 'M':
- + csi_M(currcons,par[0]);
- + continue;
- + case 'P':
- + csi_P(currcons,par[0]);
- + continue;
- + case 'c':
- + if (!par[0])
- + respond_ID(tty);
- + continue;
- + case 'g':
- + if (!par[0])
- + tab_stop[x >> 5] &= ~(1 << (x & 31));
- + else if (par[0] == 3) {
- + tab_stop[0] =
- + tab_stop[1] =
- + tab_stop[2] =
- + tab_stop[3] =
- + tab_stop[4] = 0;
- + }
- + continue;
- + case 'm':
- + csi_m(currcons);
- + continue;
- + case 'q': /* DECLL - but only 3 leds */
- + /* map 0,1,2,3 to 0,1,2,4 */
- + if (par[0] < 4)
- + setledstate(kbd_table + currcons,
- + (par[0] < 3 ? par[0] : 4));
- + continue;
- + case 'r':
- + if (!par[0])
- + par[0]++;
- + if (!par[1])
- + par[1] = video_num_lines;
- + if (par[0] < par[1] &&
- + par[1] <= video_num_lines) {
- + top=par[0]-1;
- + bottom=par[1];
- + gotoxy(currcons,0,0);
- + }
- + continue;
- + case 's':
- + save_cur(currcons);
- + continue;
- + case 'u':
- + restore_cur(currcons);
- + continue;
- + case 'X':
- + csi_X(currcons, par[0]);
- + continue;
- + case '@':
- + csi_at(currcons,par[0]);
- + continue;
- + case ']': /* setterm functions */
- + setterm_command(currcons);
- + continue;
- + }
- + continue;
- + case ESpercent:
- + vc_state = ESnormal;
- + switch (c) {
- + case '@': /* defined in ISO 2022 */
- + utf = 0;
- + continue;
- + case 'G': /* prelim official escape code */
- + case '8': /* retained for compatibility */
- + utf = 1;
- + continue;
- + }
- + continue;
- + case ESfunckey:
- + vc_state = ESnormal;
- + continue;
- + case EShash:
- + vc_state = ESnormal;
- + if (c == '8') {
- + /* DEC screen alignment test. kludge :-) */
- + }
- + continue;
- + case ESsetG0:
- + if (c == '0')
- + G0_charset = GRAF_MAP;
- + else if (c == 'B')
- + G0_charset = LAT1_MAP;
- + else if (c == 'U')
- + G0_charset = IBMPC_MAP;
- + else if (c == 'K')
- + G0_charset = USER_MAP;
- + if (charset == 0)
- + translate = set_translate(G0_charset);
- + vc_state = ESnormal;
- + continue;
- + case ESsetG1:
- + if (c == '0')
- + G1_charset = GRAF_MAP;
- + else if (c == 'B')
- + G1_charset = LAT1_MAP;
- + else if (c == 'U')
- + G1_charset = IBMPC_MAP;
- + else if (c == 'K')
- + G1_charset = USER_MAP;
- + if (charset == 1)
- + translate = set_translate(G1_charset);
- + vc_state = ESnormal;
- + continue;
- + default:
- + vc_state = ESnormal;
- + }
- + }
- + if (vcmode != KD_GRAPHICS)
- + set_cursor(currcons);
- + enable_bh(KEYBOARD_BH);
- + restore_cursors(currcons);
- + return n;
- +}
- +
- +static int con_write_room(struct tty_struct *tty)
- +{
- + if (tty->stopped)
- + return 0;
- + return 4096; /* No limit, really; we're not buffering */
- +}
- +
- +static int con_chars_in_buffer(struct tty_struct *tty)
- +{
- + return 0; /* we're not buffering */
- +}
- +
- +void poke_blanked_console(void)
- +{
- + timer_active&=~(1<<BLANK_TIMER);
- + if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
- + return;
- + if (console_blanked) {
- + timer_table[BLANK_TIMER].expires = 0;
- + timer_active |= 1<<BLANK_TIMER;
- + } else if(blankinterval) {
- + timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
- + timer_active |= 1<<BLANK_TIMER;
- + }
- +}
- +
- +static void console_print(const char *b)
- +{
- + int currcons = fg_console;
- + unsigned char c;
- + static int printing = 0;
- +
- + if(vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
- + {
- + prints(b);
- + return;
- + }
- +
- + if (!printable || printing)
- + return; /* console not yet initialized */
- + printing = 1;
- +
- + if(!vc_cons_allocated(currcons)) {
- + /* impossible */
- + printk("console_print: tty %d not allocated ??\n",currcons+1);
- + return;
- + }
- +
- + remove_cursors(currcons);
- +
- + while ((c = *b++) != 0) {
- + if (c == 10 || c == 13 || need_wrap) {
- + if (c != 13)
- + lf(currcons);
- + cr(currcons);
- + if(c==10 || c==13)
- + continue;
- + }
- + charwrite(currcons,pos,c);
- + if (x == video_num_columns - 1) {
- + need_wrap=1;
- + continue;
- + }
- + x++;
- + pos += bytes_per_char_h;
- + }
- + set_cursor(currcons);
- +
- + restore_cursors(currcons);
- +
- + poke_blanked_console();
- + printing = 0;
- +}
- +
- +/*
- + * con_throttle and con_unthrottle are only used for
- + * paste_selection(), which has to stuff in a large number of
- + * characters...
- + */
- +static void con_throttle(struct tty_struct *tty)
- +{
- +}
- +
- +static void con_unthrottle(struct tty_struct *tty)
- +{
- + struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
- +
- + wake_up_interruptible(&vt->paste_wait);
- +}
- +
- +static void vc_init(unsigned int currcons, unsigned long rows, unsigned long cols, int do_clear)
- +{
- + long base = (long) vc_scrbuf[currcons];
- +
- + video_num_columns = cols;
- + video_num_lines = rows;
- + video_size_row = video_num_columns*bytes_per_char_h*bytes_per_char_v;
- + video_screen_size = video_num_lines * video_size_row;
- + video_buf_size = video_num_lines * video_num_columns * 4;
- + video_mem_start = video_mem_base;
- + video_mem_end = video_mem_term;
- +
- + pos = origin = video_mem_start;
- + scr_end = video_mem_start + video_num_lines * video_size_row;
- + video_mem_end = base + video_screen_size;
- + reset_vc(currcons);
- + def_forecol = color_table[7];
- + def_backcol = color_table[0];
- + vt_cons[currcons]->paste_wait = 0;
- + cursoron = 0;
- +
- + reset_terminal(currcons,do_clear);
- +}
- +
- +static void con_setsize(unsigned long rows,unsigned long cols)
- +{
- + video_num_lines = rows;
- + video_num_columns = cols;
- + video_size_row = video_num_columns*bytes_per_char_h*bytes_per_char_v;
- + video_screen_size = video_num_lines * video_size_row;
- + video_buf_size = video_num_lines * video_num_columns * 4;
- +}
- +
- +static int con_ioctl(struct tty_struct *tty, struct file * file,
- + unsigned int cmd, unsigned long arg)
- +{
- + return vt_ioctl(tty, file, cmd, arg);
- +}
- +
- +/*
- + * unsigned long con_init(unsigned long);
- + *
- + * This routine initialises console interrupts, and does nothing
- + * else. If you want the screen to clear, call tty_write with
- + * the apropriate escape-sequence.
- + *
- + */
- +unsigned long con_init(unsigned long kmem_start)
- +{
- + int currcons = 0, i;
- +
- + memset(&console_driver, 0, sizeof(struct tty_driver));
- + console_driver.magic = TTY_DRIVER_MAGIC;
- + console_driver.name = "tty";
- + console_driver.name_base = 1;
- + console_driver.major = TTY_MAJOR;
- + console_driver.minor_start = 1;
- + console_driver.num = MAX_NR_CONSOLES;
- + console_driver.type = TTY_DRIVER_TYPE_CONSOLE;
- + console_driver.init_termios = tty_std_termios;
- + console_driver.flags = TTY_DRIVER_REAL_RAW;
- + console_driver.refcount = &console_refcount;
- + console_driver.table = console_table;
- + console_driver.termios = console_termios;
- + console_driver.termios_locked = console_termios_locked;
- +
- + console_driver.open = con_open;
- + console_driver.write = con_write;
- + console_driver.write_room = con_write_room;
- + console_driver.chars_in_buffer = con_chars_in_buffer;
- + console_driver.ioctl = con_ioctl;
- + console_driver.stop = con_stop;
- + console_driver.start = con_start;
- + console_driver.throttle = con_throttle;
- + console_driver.unthrottle = con_unthrottle;
- +
- + if(tty_register_driver(&console_driver))
- + panic("Couldn't register console driver\n");
- +
- + con_setsize(ORIG_VIDEO_LINES,ORIG_VIDEO_COLS);
- +
- + timer_table[BLANK_TIMER].fn=blank_screen;
- + timer_table[BLANK_TIMER].expires=0;
- + if(blankinterval) {
- + timer_table[BLANK_TIMER].expires=jiffies+blankinterval;
- + timer_active|=1<<BLANK_TIMER;
- + }
- +
- + for(i=0; i<17; i++)
- + vidc_write(i<<2, default_palette_entries[i]);
- +
- + /* Use ORIG_VIDEO_MODE */
- + video_addr_mask= (video_num_lines * video_num_columns * bytes_per_char_h *
- + bytes_per_char_v - 1) | (PAGE_SIZE-1);
- + video_mem_term = 0x02000000;
- + video_mem_base = video_mem_term - video_addr_mask - 1;
- +
- + map_screen_mem (video_mem_base, 1);
- +
- + can_do_color=1;
- + if(bytes_per_char_h == 8)
- + {
- + default_palette_entries = palette_8;
- + color_table = color_8;
- + }
- +
- + for(currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
- + vc_cons[currcons].d = (struct vc_data *) kmem_start;
- + kmem_start += sizeof(struct vc_data);
- + vt_cons[currcons] = (struct vt_struct *) kmem_start;
- + kmem_start += sizeof(struct vt_struct);
- + vc_scrbuf[currcons] = (unsigned char *) kmem_start;
- + memfastset(vc_scrbuf[currcons], 0x00000720, video_buf_size);
- + kmem_start += video_buf_size;
- + kmalloced = 0;
- + screenbuf_size = video_buf_size;
- + vc_init(currcons, video_num_lines, video_num_columns,currcons);
- + }
- +
- + currcons = fg_console = 0;
- +
- + video_mem_start = video_mem_base;
- + video_mem_end = video_mem_term;
- + pos=origin=cp = video_mem_start;
- + scr_end = video_mem_start + video_num_lines * video_size_row;
- + cursoron = 1;
- +
- + gotoxy (currcons,ORIG_X,ORIG_Y);
- + csi_J (currcons, 0);
- + printable=1;
- + printk ("Console: %s %s %ldx%ldx%d, %d virtual console%s (max %d)\n","colour", "A5000",
- + video_num_columns,
- + video_num_lines,
- + bytes_per_char_h == 8 ? 256:16,
- + MIN_NR_CONSOLES,
- + (MIN_NR_CONSOLES == 1) ? "":"s",
- + MAX_NR_CONSOLES);
- + register_console (console_print);
- + if(request_irq (IRQ_VSYNCPULSE, vsync_irq, 0, "console"))
- + panic ("Unable to get VSYNC irq for console\n");
- + return kmem_start;
- +}
- +
- +static void get_scrmem(int currcons)
- +{
- +}
- +
- +void update_scrmem(int currcons, int start, int length)
- +{
- + unsigned long p, pp, sx, sy, ex, ey;
- + unsigned long *buffer;
- +
- + sy = start / video_num_columns;
- + sx = start % video_num_columns;
- + length += start;
- + ey = length / video_num_columns;
- + ex = length % video_num_columns;
- +
- + if (ey > video_num_lines)
- + ey = video_num_lines;
- +
- + p = origin + sy * video_size_row;
- + buffer = ((unsigned long *)vc_scrbuf[currcons]) + start;
- +
- + if (ey > sy)
- + {
- + for (; sy < ey; sy++)
- + {
- + pp = p + sx * bytes_per_char_h;
- + for (; sx < video_num_columns; sx++)
- + {
- + ll_char_write (pp, buffer[0] & 0xff, (buffer[0] >> 8) & 0xff,
- + (buffer[0] >> 16) & 0xff, (buffer[0] >> 24) & 0xff);
- + pp += bytes_per_char_h;
- + buffer ++;
- + }
- + p += video_size_row;
- + sx = 0;
- + }
- + }
- + if (ey == sy && ex)
- + {
- + for (; sx < ex; sx++)
- + {
- + ll_char_write (p, buffer[0] & 0xff, (buffer[0] >> 8) & 0xff,
- + (buffer[0] >> 16) & 0xff, (buffer[0] >> 24) & 0xff);
- + p += bytes_per_char_h;
- + buffer ++;
- + }
- + }
- +}
- +
- +void set_scrmem(int currcons,long offset)
- +{
- + unsigned long p,pp,mx,my;
- + unsigned long *buffer;
- + int i;
- +
- + p = origin;
- + buffer = (unsigned long *)vc_scrbuf[currcons];
- +
- + for(my = 0; my < video_num_lines ; my++)
- + {
- + pp = p;
- + for(mx = 0; mx < video_num_columns; mx++)
- + {
- + ll_char_write(pp,buffer[0] & 0xff,(buffer[0] >> 8) & 0xff,
- + (buffer[0] >> 16) & 0xff,(buffer[0] >> 24) & 0xff);
- + pp+=bytes_per_char_h;
- + buffer+=1;
- + }
- + p+=video_size_row;
- + }
- + pp = origin + ((video_screen_size + 0x7FFF) & PAGE_MASK);
- + while(p<pp)
- + {
- + *(unsigned long *)p=0;
- + p+=4;
- + }
- + if(!paletteentries || vcmode != KD_GRAPHICS)
- + for(i=0; i<17; i++)
- + vidc_write(i<<2, default_palette_entries[i]);
- + else
- + for(i=0; i<17; i++)
- + vidc_write(i<<2, paletteentries[i] & 0x1fff);
- +}
- +
- +/* ------------------------------------------------------------------------------------- *
- + * Screen blanking routines
- + * ------------------------------------------------------------------------------------- */
- +
- +void do_blank_screen(int nopowersave)
- +{
- + int i;
- +
- + if (console_blanked)
- + return;
- +
- + timer_active &= ~(1<<BLANK_TIMER);
- + timer_table[BLANK_TIMER].fn = unblank_screen;
- +
- + /* DISABLE VIDEO */
- + for (i = 0; i < 17; i++)
- + vidc_write(i<<2, 0);
- +
- + console_blanked = fg_console + 1;
- +}
- +
- +void do_unblank_screen(void)
- +{
- + int i;
- + int currcons;
- +
- + if (!console_blanked)
- + return;
- + if (!vc_cons_allocated(fg_console)) {
- + /* impossible */
- + printk("unblank_screen: tty %d not allocated ??\n", fg_console+1);
- + return;
- + }
- + timer_table[BLANK_TIMER].fn = blank_screen;
- + if (blankinterval)
- + {
- + timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
- + timer_active |= 1<<BLANK_TIMER;
- + }
- +
- + currcons = fg_console;
- + console_blanked = 0;
- +
- + if(!paletteentries || vcmode != KD_GRAPHICS)
- + for(i=0; i<17; i++)
- + vidc_write(i<<2, default_palette_entries[i]);
- + else
- + for(i=0; i<17; i++)
- + vidc_write(i<<2, paletteentries[i] & 0x1fff);
- +}
- +
- +/*
- + * If a blank_screen is due to a timer, then a power save is allowed.
- + * If it is related to console_switching, then avoid power save.
- + */
- +static void blank_screen(void)
- +{
- + do_blank_screen(0);
- +}
- +
- +static void unblank_screen(void)
- +{
- + do_unblank_screen();
- +}
- +
- +void update_screen(int new_console)
- +{
- + static int lock = 0;
- +
- + if(new_console == fg_console || lock)
- + return;
- + if(!vc_cons_allocated(new_console)) {
- + /* strange ... */
- + printk("update_screen: tty %d not allocated ??\n",new_console);
- + return;
- + }
- + lock = 1;
- +
- + highlight_pointer(fg_console,-1);
- +
- + remove_cursors(fg_console);
- +
- + if(!console_blanked)
- + get_scrmem(fg_console);
- + else
- + console_blanked = -1; /* no longer of the form console+1 */
- + fg_console = new_console; /* this is the only (nonzero) assignment to fg_console */
- + /* consequently, fg_console will always be allocated */
- + set_scrmem(fg_console, 0);
- + set_origin(fg_console);
- + set_cursor(fg_console);
- + restore_cursors(fg_console);
- + set_leds();
- + compute_shiftstate();
- + lock = 0;
- +}
- +
- +/*
- + * Allocate the console screen memory
- + */
- +int con_open(struct tty_struct *tty,struct file *filp)
- +{
- + unsigned int idx;
- + int i;
- +
- + idx = MINOR(tty->device) - tty->driver.minor_start;
- +
- + i = vc_allocate(idx);
- + if(i)
- + return i;
- +
- + vt_cons[idx]->vc_num = idx;
- + tty->driver_data = vt_cons[idx];
- +
- + if(!tty->winsize.ws_row && !tty->winsize.ws_col) {
- + tty->winsize.ws_row = video_num_lines;
- + tty->winsize.ws_col = video_num_columns;
- + }
- + return 0;
- +}
- +
- +/*
- + * PIO_FONT support
- + */
- +int con_set_font (char *arg)
- +{
- + return -EINVAL;
- +}
- +
- +int con_get_font (char *arg)
- +{
- + return -EINVAL;
- +}
- +
- +void reset_palette (int currcons)
- +{
- +}
- +
- +void set_palette (void)
- +{
- +}
- +
- +/* == arm specific console code ============================================================== */
- +
- +int palette_getentries(int currcons, int offset, int size, unsigned long *entries)
- +{
- + int i;
- +
- + if(offset>18 || offset<0 || size<1)
- + return -EINVAL;
- +
- + if(size>19)
- + size=19;
- +
- + verify_area(VERIFY_WRITE,entries,size);
- +
- + if(!paletteentries)
- + for(i=offset; i<offset+size; i++)
- + *entries++=default_palette_entries[i];
- + else
- + for(i=offset; i<offset+size; i++)
- + *entries++=(paletteentries[i] & 0x1FFF);
- +
- + return size;
- +}
- +
- +int palette_setentries(int currcons, int offset, int size, unsigned long *entries)
- +{
- + int i;
- +
- + if(offset>18 || offset<0 || size<1)
- + return -EINVAL;
- +
- + if(size>19)
- + size=19;
- +
- + verify_area(VERIFY_READ, entries, size);
- +
- + if(!paletteentries)
- + {
- + paletteentries = (unsigned long *)kmalloc(sizeof(unsigned long)*19,GFP_KERNEL);
- + if(!paletteentries)
- + return -ENOMEM;
- +
- + for(i=0; i<19; i++)
- + paletteentries[i]=default_palette_entries[i];
- + }
- +
- + for(i=offset; i<offset+size; i++)
- + paletteentries[i]=(*entries++);
- +
- + if(vcmode == KD_GRAPHICS && currcons == fg_console)
- + for(i=0; i<17; i++)
- + vidc_write(i<<2, paletteentries[i] & 0x1fff);
- +
- + return size;
- +}
- +
- +void palette_update(int currcons)
- +{
- + int i;
- + if(vcmode == KD_GRAPHICS && paletteentries)
- + for(i=0; i<17; i++)
- + vidc_write(i<<2, paletteentries[i] & 0x1fff);
- + else
- + for(i=0; i<17; i++)
- + vidc_write(i<<2, default_palette_entries[i]);
- +}
- +
- +int console_getparams(int con, unsigned long *data)
- +{
- + data[0] = 0; /* version */
- + data[1] = video_num_columns * 8; /* horiz pixels */
- + data[2] = video_num_lines * 8; /* vert. pixels */
- + data[3] = bytes_per_char_h == 4 ? 4 : 8; /* bits per pix */
- + data[4] = 4; /* depth */
- + return 0;
- +}
- +
- +static void put_cursor(char on_off,unsigned long newcp)
- +{
- + static char con=0;
- + unsigned long cp_p=cp;
- + int c = bytes_per_char_h == 8 ? color_table[15] : 0x11 * color_table[15];
- + int i;
- +
- + if(vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
- + return;
- +
- + cp=newcp;
- +
- + if(con!=on_off)
- + {
- + if(cp_p!=-1)
- + for(i=0;i<bytes_per_char_h;i++)
- + ((unsigned char*)cp_p)[i]^=c;
- + con=on_off;
- + }
- +}
- +
- +static void vsync_irq(int irq, struct pt_regs *regs)
- +{
- + static char cursor_flash=0;
- + int currcons = fg_console;
- +
- + if(++cursor_flash==16)
- + {
- + cursor_flash=0;
- + cursor_on=cursor_on?0:1;
- + if(cursoron>0)
- + put_cursor(cursor_on,cp);
- + }
- +}
- +
- +int do_screendump(int arg)
- +{
- + char *buf=(char *)arg;
- + int l;
- + if(!suser())
- + return -EPERM;
- + l=verify_area(VERIFY_WRITE,buf,2);
- + if(l)
- + return l;
- + return -ENOSYS;
- +}
- +
- +/* This routine reverses the highlight on s-e position */
- +
- +static void highlight(const int currcons, int s, int e)
- +{
- + int i;
- + unsigned char *buffer = (unsigned char *)vc_scrbuf[currcons];
- +
- + for(i = s; i <= e; i++)
- + buffer[4*i+3] ^= INVERSE;
- +
- +
- + if(currcons == fg_console)
- + {
- + unsigned long p,pp;
- + int hx,hy,hex,hey,mx,my;
- +
- + buffer += (s*4);
- +
- + hx = s % video_num_columns;
- + hy = s / video_num_columns;
- + hex = e % video_num_columns;
- + hey = e / video_num_columns;
- +
- + p = origin + (hy * video_size_row);
- + for(my = hy; my <= hey ; my++)
- + {
- + pp = p + (hx * bytes_per_char_h);
- + for(mx = hx; mx < ((my == hey)?hex+1:video_num_columns); mx++)
- + {
- + ll_char_write(pp,buffer[0],buffer[1],buffer[2],buffer[3]);
- + pp+=bytes_per_char_h;
- + buffer+=4;
- + hx = 0;
- + }
- + p+=video_size_row;
- + }
- + }
- +}
- +
- +static void highlight_pointer(const int currcons,const int where)
- +{
- +}
- +
- +static unsigned long inwordLut[4]={
- + 0x00000000, /* control char */
- + 0x03FF0000, /* digits */
- + 0x87FFFFFE, /* uppercase and '_' */
- + 0x07FFFFFE /* lowercase */
- +};
- +
- +static inline int inword(const char c)
- +{
- + return ( inwordLut[(c>>5)&3] >> (c&0x1F) ) & 1;
- +}
- +
- +int sel_loadlut(const int arg)
- +{
- + memcpy_fromfs(inwordLut, (unsigned long *)(arg+4), 16);
- + return 0;
- +}
- +
- +static inline int atedge(const int p)
- +{
- + return (!(p % video_num_columns) || !((p + 1) % video_num_columns));
- +}
- +
- +/* constrain v such that l <= v <= u */
- +static inline short limit(const int v, const int l, const int u)
- +{
- + return (v < l) ? l : ((v > u) ? u : v);
- +}
- +
- +int set_selection(const int arg, struct tty_struct *tty)
- +{
- + unsigned short *args, xs, ys, xe, ye;
- + int sel_mode, new_sel_start, new_sel_end;
- + int ps, pe, i;
- + int currcons = fg_console;
- + char spc, *obp, *bp, *spos;
- +
- + char *off = (char *)vc_scrbuf[currcons];
- +
- + unblank_screen();
- + args = (unsigned short *)(arg + 1);
- + xs = limit(get_fs_word(args++) - 1, 0, video_num_columns - 1);
- + ys = limit(get_fs_word(args++) - 1, 0, video_num_lines - 1);
- + xe = limit(get_fs_word(args++) - 1, 0, video_num_columns - 1);
- + ye = limit(get_fs_word(args++) - 1, 0, video_num_lines - 1);
- + sel_mode = get_fs_word(args);
- +
- + ps = ys * video_num_columns + xs;
- + pe = ye * video_num_columns + xe;
- +
- + if (report_mouse && (sel_mode & 16))
- + {
- + mouse_report(currcons, tty, sel_mode & 15, xs, ys);
- + return 0;
- + }
- +
- + if ( ps > pe )
- + {
- + ps ^= pe;
- + pe ^= ps;
- + ps ^= pe;
- + }
- +
- + switch (sel_mode)
- + {
- + case 0: /* character-by-character selection */
- + new_sel_start = ps;
- + new_sel_end = pe;
- + break;
- + case 1: /* word-by-word selection */
- + spc = isspace(*off + (ps << 2));
- + for (new_sel_start = ps; ; ps -= 1)
- + {
- + if ((spc && !isspace(*(off + (ps << 2)))) ||
- + (!spc && !inword(*(off + (ps << 2)))))
- + break;
- + new_sel_start = ps;
- + if (!(ps % video_num_columns))
- + break;
- + }
- + spc = isspace(*(off + (pe << 2)));
- + for (new_sel_end = pe; ; pe += 1)
- + {
- + if ((spc && !isspace(*(off + (pe << 2)))) ||
- + (!spc && !inword(*(off + (pe << 2)))))
- + break;
- + new_sel_end = pe;
- + if (!((pe + 1) % video_num_columns))
- + break;
- + }
- + break;
- + case 2: /* line-by-line selection */
- + new_sel_start = ps - ps % video_num_columns;
- + new_sel_end = pe + video_num_columns - pe % video_num_columns - 1;
- + break;
- + case 3: /* pointer highlight */
- + if (sel_cons != currcons)
- + {
- + highlight_pointer(sel_cons, -1);
- + clear_selection();
- + sel_cons = currcons;
- + }
- + highlight_pointer(sel_cons, pe);
- + return 0;
- + default:
- + return -EINVAL;
- + }
- + if(new_sel_end > new_sel_start && !atedge(new_sel_end) &&
- + isspace(*(off + (new_sel_end << 2))))
- + {
- + for (pe = new_sel_end + 1; ; pe += 1)
- + {
- + if (!isspace(*(off + (pe << 2))) || atedge(pe))
- + break;
- + }
- + if (isspace(*(off + (pe << 2))))
- + new_sel_end = pe;
- + }
- + if (sel_cons != currcons)
- + {
- + clear_selection();
- + sel_cons = currcons;
- + }
- + if(sel_start == -1)
- + highlight(sel_cons, new_sel_start, new_sel_end);
- + else if (new_sel_start == sel_start)
- + {
- + if (new_sel_end == sel_end) /* no action required */
- + return 0;
- + else if (new_sel_end > sel_end) /* extend to right/down */
- + highlight(sel_cons, sel_end + 1, new_sel_end);
- + else /* contract from right/up */
- + highlight(sel_cons, new_sel_end + 1, sel_end);
- + }
- + else if (new_sel_end == sel_end)
- + {
- + if (new_sel_start < sel_start) /* extend to left */
- + highlight(sel_cons, new_sel_start, sel_start - 1);
- + else
- + highlight(sel_cons, sel_start, new_sel_start - 1);
- + }
- + else
- + {
- + clear_selection();
- + highlight(sel_cons, new_sel_start, new_sel_end);
- + }
- + sel_start = new_sel_start;
- + sel_end = new_sel_end;
- +
- + obp = bp = sel_buffer;
- +
- + for (i = sel_start; i <= sel_end; i+=1)
- + {
- + spos = (char *)off + (i << 2);
- + *bp++ = *spos;
- + if (!isspace(*spos))
- + obp = bp;
- + if (! ((i+1) % video_num_columns))
- + {
- + /* strip trailing spaces from line and add newline,
- + unless non-space at end of line. */
- + if (obp != bp)
- + {
- + bp = obp;
- + *bp++ = '\r';
- + }
- + obp = bp;
- + }
- + /* check for space, leaving room for next character, possible
- + newline, and null at end. */
- + if (bp - sel_buffer > SEL_BUFFER_SIZE - 3)
- + break;
- + }
- + *bp = '\0';
- + return 0;
- +}
- +
- +int paste_selection(struct tty_struct *tty)
- +{
- + struct wait_queue wait;
- + char *bp = sel_buffer;
- + int c,l;
- + struct vt_struct *vt = (struct vt_struct *) tty->driver_data;
- +
- + if(!sel_buffer[0])
- + return 0;
- + unblank_screen();
- + c = strlen(sel_buffer);
- + current->state = TASK_INTERRUPTIBLE;
- + add_wait_queue(&vt->paste_wait, &wait);
- + while (c) {
- + if (test_bit(TTY_THROTTLED, &tty->flags)) {
- + schedule();
- + continue;
- + }
- + l = MIN(c, tty->ldisc.receive_room(tty));
- + tty->ldisc.receive_buf(tty,(unsigned char *) bp, 0, l);
- + c -= l;
- + bp += l;
- + }
- + current->state = TASK_RUNNING;
- + return 0;
- +}
- +
- +static void clear_selection()
- +{
- + highlight_pointer(sel_cons, -1); /* hide the pointer */
- + if (sel_start != -1)
- + {
- + highlight(sel_cons, sel_start, sel_end);
- + sel_start = -1;
- + }
- +}
- +
- +/* -------------------------------------------------------------------------------
- + * erasing
- + * ------------------------------------------------------------------------------- */
- +
- +static void ll_erasebuf(int currcons,unsigned char sx,unsigned char sy,
- + unsigned char cx,unsigned char cy)
- +{
- + unsigned char *buffer = vc_scrbuf[currcons];
- + if(sx!=0) /* Erase to end of line */
- + {
- + register unsigned char c;
- + if(cy!=0)
- + c=video_num_columns-sx;
- + else
- + c=video_num_columns-sx<cx?video_num_columns-sx:cx;
- + memfastset((void*)(buffer+4*(sx+sy*video_num_columns)),
- + (backcol<<16) | (forecol<<8) | 32, 4*c);
- + cx-=c;
- + sy++;
- + if(cy>0)
- + cy--;
- + sx=0;
- + }
- + if(cy!=0)
- + {
- + memfastset((void*)(buffer+sy*video_num_columns*4),
- + (backcol<<16) | (forecol<<8) | 32,cy*video_num_columns*4);
- + }
- + if(cx!=0)
- + {
- + memfastset((void*)(buffer+(sy+cy)*video_num_columns*4),
- + (backcol<<16) | (forecol<<8) | 32,cx*4);
- + }
- +}
- +
- +static void ll_erase(int currcons,unsigned char sx,unsigned char sy,
- + unsigned char cx,unsigned char cy)
- +{
- + register unsigned char *p,cc;
- + register unsigned int i1,i2;
- +
- + ll_erasebuf(currcons,sx,sy,cx,cy);
- +
- + if(currcons==fg_console)
- + {
- + cc=0x11*backcol;
- +
- + if(sx!=0) /* Erase to end of line */
- + {
- + register unsigned char c;
- + if(cy!=0)
- + c=video_num_columns-sx;
- + else
- + c=video_num_columns-sx<cx?video_num_columns-sx:cx;
- + p=(unsigned char*)(origin+sy*video_num_columns*
- + bytes_per_char_h*bytes_per_char_v+
- + sx*bytes_per_char_h);
- + for(i1=0;i1<c*bytes_per_char_h;i1++)
- + for(i2=0;i2<bytes_per_char_v;i2++)
- + p[i1+i2*video_num_columns*bytes_per_char_h]=cc;
- + cx-=c;
- + if(cy>0)
- + cy--;
- + sy++;
- + sx=0;
- + }
- + if(cy!=0)
- + {
- + memfastset((void*)(origin+sy*video_num_columns*
- + bytes_per_char_h*bytes_per_char_v),
- + 0x11111111L*backcol,
- + cy*bytes_per_char_h*bytes_per_char_v*video_num_columns);
- + }
- + if(cx!=0)
- + {
- + p=(unsigned char*)(origin+(sy+cy)*video_num_columns*
- + bytes_per_char_h*bytes_per_char_v);
- + for(i1=0;i1<cx*bytes_per_char_h;i1++)
- + for(i2=0;i2<bytes_per_char_v;i2++)
- + p[i1+i2*video_num_columns*bytes_per_char_h]=cc;
- + }
- + }
- +}
- +
- diff -urNwbB linux/arch/arm/drivers/char/defkeymap.c linux.arm/arch/arm/drivers/char/defkeymap.c
- --- linux/arch/arm/drivers/char/defkeymap.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/defkeymap.c Sun Mar 3 12:31:45 1996
- @@ -0,0 +1,337 @@
- +/*
- + * linux/arch/arm/drivers/char/defkeymap.c
- + *
- + * Copyright (C) 1995, 1996 Russell King
- + */
- +
- +#include <linux/types.h>
- +#include <linux/keyboard.h>
- +#include <linux/kd.h>
- +
- +/* Normal (maps 1:1 with no processing) */
- +#define KTn 0xF0
- +/* Function keys */
- +#define KTf 0xF1
- +/* Special (Performs special house-keeping funcs) */
- +#define KTs 0xF2
- +#define KIGNORE K(KTs, 0) /* Ignore */
- +#define KENTER K(KTs, 1) /* Enter */
- +#define KREGS K(KTs, 2) /* Regs */
- +#define KMEM K(KTs, 3) /* Mem */
- +#define KSTAT K(KTs, 4) /* State */
- +#define KINTR K(KTs, 5) /* Intr */
- +#define Ksl 6 /* Last console */
- +#define KCAPSLK K(KTs, 7) /* Caps lock */
- +#define KNUMLK K(KTs, 8) /* Num-lock */
- +#define KSCRLLK K(KTs, 9) /* Scroll-lock */
- +#define KSCRLFOR K(KTs,10) /* Scroll forward */
- +#define KSCRLBAK K(KTs,11) /* Scroll back */
- +#define KREBOOT K(KTs,12) /* Reboot */
- +#define KCAPSON K(KTs,13) /* Caps on */
- +#define KCOMPOSE K(KTs,14) /* Compose */
- +#define KSAK K(KTs,15) /* SAK */
- +#define CONS_DEC K(KTs,16) /* Dec console */
- +#define CONS_INC K(KTs,17) /* Incr console */
- +#define KFLOPPY K(KTs,18) /* Floppy */
- +/* Key pad (0-9 = digits, 10=+, 11=-, 12=*, 13=/, 14=enter, 16=., 17=# */
- +#define KTp 0xF3
- +#define KPAD_0 K(KTp, 0 )
- +#define KPAD_1 K(KTp, 1 )
- +#define KPAD_2 K(KTp, 2 )
- +#define KPAD_3 K(KTp, 3 )
- +#define KPAD_4 K(KTp, 4 )
- +#define KPAD_5 K(KTp, 5 )
- +#define KPAD_6 K(KTp, 6 )
- +#define KPAD_7 K(KTp, 7 )
- +#define KPAD_8 K(KTp, 8 )
- +#define KPAD_9 K(KTp, 9 )
- +#define KPAD_PL K(KTp,10 )
- +#define KPAD_MI K(KTp,11 )
- +#define KPAD_ML K(KTp,12 )
- +#define KPAD_DV K(KTp,13 )
- +#define KPAD_EN K(KTp,14 )
- +#define KPAD_DT K(KTp,16 )
- +#define KPAD_HS K(KTp,18 )
- +/* Console switching */
- +#define KCn 0xF5
- +/* Cursor */
- +#define KTc 0xF6
- +#define Kcd 0 /* Cursor down */
- +#define Kcl 1 /* Cursor left */
- +#define Kcr 2 /* Cursor right */
- +#define Kcu 3 /* Cursor up */
- +/* Shift/alt modifiers etc */
- +#define KMd 0xF7
- +#define KSHIFT K(KMd, 0 )
- +#define KALTGR K(KMd, 1 )
- +#define KCTRL K(KMd, 2 )
- +#define KALT K(KMd, 3 )
- +/* Meta */
- +#define KMt 0xF8
- +#define KAs 0xF9
- +#define KPADA_0 K(KAs, 0 )
- +#define KPADA_1 K(KAs, 1 )
- +#define KPADA_2 K(KAs, 2 )
- +#define KPADA_3 K(KAs, 3 )
- +#define KPADA_4 K(KAs, 4 )
- +#define KPADA_5 K(KAs, 5 )
- +#define KPADA_6 K(KAs, 6 )
- +#define KPADA_7 K(KAs, 7 )
- +#define KPADA_8 K(KAs, 8 )
- +#define KPADA_9 K(KAs, 9 )
- +#define KPADB_0 K(KAs,10 )
- +#define KPADB_1 K(KAs,11 )
- +#define KPADB_2 K(KAs,12 )
- +#define KPADB_3 K(KAs,13 )
- +#define KPADB_4 K(KAs,14 )
- +#define KPADB_5 K(KAs,15 )
- +#define KPADB_6 K(KAs,16 )
- +#define KPADB_7 K(KAs,17 )
- +#define KPADB_8 K(KAs,18 )
- +#define KPADB_9 K(KAs,19 )
- +/* Locking keys */
- +#define KLk 0xFA
- +/* Letters */
- +#define KTl 0xFB
- +
- +u_short plain_map[]=
- +{
- + K(KTn, 27),K(KTf, 0),K(KTf, 1),K(KTf, 2 ),K(KTf, 3),K(KTf, 4),K(KTf, 5 ),K(KTf, 6),
- + K(KTf, 7),K(KTf, 8),K(KTf, 9),K(KTf, 10 ),K(KTf, 11),KIGNORE ,KSCRLLK ,KINTR ,
- + K(KTn,'`'),K(KTn,'1'),K(KTn,'2'),K(KTn,'3' ),K(KTn,'4'),K(KTn,'5'),K(KTn,'6' ),K(KTn,'7'),
- + K(KTn,'8'),K(KTn,'9'),K(KTn,'0'),K(KTn,'-' ),K(KTn,'='),K(KTn,'£'),K(KTn,127 ),K(KTf,21 ),
- + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,K(KTn, 9 ),K(KTl,'q'),
- + K(KTl,'w'),K(KTl,'e'),K(KTl,'r'),K(KTl,'t' ),K(KTl,'y'),K(KTl,'u'),K(KTl,'i' ),K(KTl,'o'),
- + K(KTl,'p'),K(KTn,'['),K(KTn,']'),K(KTn,'\\'),K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPAD_7 ,
- + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTl,'a'),K(KTl,'s'),K(KTl,'d' ),K(KTl,'f'),
- + K(KTl,'g'),K(KTl,'h'),K(KTl,'j'),K(KTl,'k' ),K(KTl,'l'),K(KTn,';'),K(KTn,'\''),KENTER ,
- + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTl,'z' ),K(KTl,'x'),
- + K(KTl,'c'),K(KTl,'v'),K(KTl,'b'),K(KTl,'n' ),K(KTl,'m'),K(KTn,','),K(KTn,'.' ),K(KTn,'/'),
- + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn,' '),
- + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN
- +};
- +
- +u_short shift_map[]=
- +{
- + K(KTn, 27),K(KTf, 10),K(KTf, 11),K(KTf, 12 ),K(KTf, 13),K(KTf, 14),K(KTf, 15 ),K(KTf, 16),
- + K(KTf, 17),K(KTf, 18),K(KTf, 19),K(KTf, 20 ),K(KTf, 21),KIGNORE ,KMEM ,KINTR ,
- + K(KTn,'~'),K(KTn,'!'),K(KTn,'@'),K(KTn,'#' ),K(KTn,'$'),K(KTn,'%'),K(KTn,'^' ),K(KTn,'&'),
- + K(KTn,'*'),K(KTn,'('),K(KTn,')'),K(KTn,'_' ),K(KTn,'+'),K(KTn,'¤'),K(KTn,127 ),K(KTf,21 ),
- + K(KTf,20 ),KSCRLBAK ,KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,K(KTn, 9 ),K(KTl,'Q'),
- + K(KTl,'W'),K(KTl,'E'),K(KTl,'R'),K(KTl,'T' ),K(KTl,'Y'),K(KTl,'U'),K(KTl,'I' ),K(KTl,'O'),
- + K(KTl,'P'),K(KTn,'{'),K(KTn,'}'),K(KTn,'|' ),K(KTf,22 ),K(KTf,23 ),KSCRLFOR ,KPAD_7 ,
- + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTl,'A'),K(KTl,'S'),K(KTl,'D' ),K(KTl,'F'),
- + K(KTl,'G'),K(KTl,'H'),K(KTl,'J'),K(KTl,'K' ),K(KTl,'L'),K(KTn,':'),K(KTn,'"' ),KENTER ,
- + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTl,'Z' ),K(KTl,'X'),
- + K(KTl,'C'),K(KTl,'V'),K(KTl,'B'),K(KTl,'N' ),K(KTl,'M'),K(KTn,'<'),K(KTn,'>' ),K(KTn,'?'),
- + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn,' '),
- + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN
- +};
- +
- +u_short altgr_map[]=
- +{
- + KIGNORE ,K(KCn,12 ),K(KCn,13 ),K(KCn,14 ),K(KCn,15 ),K(KCn,16 ),K(KCn,17 ),K(KCn, 18),
- + K(KCn, 19),K(KCn,20 ),K(KCn,21 ),K(KCn,22 ),K(KCn,23 ),KIGNORE ,KREGS ,KINTR ,
- + KIGNORE ,KIGNORE ,K(KTn,'@'),KIGNORE ,K(KTn,'$'),KIGNORE ,KIGNORE ,K(KTn,'{'),
- + K(KTn,'['),K(KTn,']'),K(KTn,'}'),K(KTn,'\\'),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ),
- + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KTl,'q'),
- + K(KTl,'w'),K(KTl,'e'),K(KTl,'r'),K(KTl,'t' ),K(KTl,'y'),K(KTl,'u'),K(KTl,'i' ),K(KTl,'o'),
- + K(KTl,'p'),KIGNORE ,K(KTn,'~'),KIGNORE ,K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPADB_7 ,
- + KPADB_8 ,KPADB_9 ,KPAD_MI ,KCTRL ,K(KAs,20 ),K(KTl,'s'),K(KAs,23 ),K(KAs,25 ),
- + K(KTl,'g'),K(KTl,'h'),K(KTl,'j'),K(KTl,'k' ),K(KTl,'l'),KIGNORE ,KIGNORE ,KENTER ,
- + KPADB_4 ,KPADB_5 ,KPADB_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTl,'z' ),K(KTl,'x'),
- + K(KAs,22 ),K(KTl,'v'),K(KTl,21 ),K(KTl,'n' ),K(KTl,'m'),KIGNORE ,KIGNORE ,KIGNORE ,
- + KSHIFT ,K(KTc,Kcu),KPADB_1 ,KPADB_2 ,KPADB_3 ,KCAPSLK ,KALT ,KIGNORE ,
- + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPADB_0 ,KPAD_DT ,KPAD_EN
- +};
- +
- +u_short ctrl_map[]=
- +{
- + KIGNORE ,K(KTf, 0),K(KTf, 1),K(KTf, 2 ),K(KTf, 3),K(KTf, 4),K(KTf, 5 ),K(KTf, 6),
- + K(KTf, 7),K(KTf, 8),K(KTf, 9),K(KTf, 10 ),K(KTf, 11),KIGNORE ,KSTAT ,KINTR ,
- + KIGNORE ,K(KTn, 1 ),K(KTn, 2 ),K(KTn, 3 ),K(KTn, 4 ),K(KTn, 5 ),K(KTn, 6 ),K(KTn, 7 ),
- + K(KTn, 8 ),K(KTn, 9 ),K(KTn, 0 ),K(KTn,31 ),KIGNORE ,KIGNORE ,K(KTn, 8 ),K(KTf,21 ),
- + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KTn,17 ),
- + K(KTn,23 ),K(KTn, 5 ),K(KTn,18 ),K(KTn,20 ),K(KTn,25 ),K(KTn,21 ),K(KTn, 9 ),K(KTn,15 ),
- + K(KTn,16 ),K(KTn,27 ),K(KTn,29 ),K(KTn,28 ),K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPAD_7 ,
- + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTn, 1 ),K(KTn,19 ),K(KTn, 4 ),K(KTn, 6 ),
- + K(KTn, 7 ),K(KTn, 8 ),K(KTn,10 ),K(KTn,11 ),K(KTn,12 ),KIGNORE ,K(KTn, 7 ),KENTER ,
- + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTn,26 ),K(KTn,24 ),
- + K(KTn, 3 ),K(KTn,22 ),K(KTn, 2 ),K(KTn,14 ),K(KTn,13 ),KIGNORE ,KCOMPOSE ,K(KTn,127),
- + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn, 0 ),
- + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN
- +};
- +
- +u_short shift_ctrl_map[]=
- +{
- + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
- + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KFLOPPY ,KINTR ,
- + KIGNORE ,KIGNORE ,K(KTn, 0 ),KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
- + KIGNORE ,KIGNORE ,KIGNORE ,K(KTn,31 ),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ),
- + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KTn,17 ),
- + K(KTn,23 ),K(KTn, 5 ),K(KTn,18 ),K(KTn,20 ),K(KTn,25 ),K(KTn,21 ),K(KTn, 9 ),K(KTn,15 ),
- + K(KTn,16 ),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPAD_7 ,
- + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTn, 1 ),K(KTn,19 ),K(KTn, 4 ),K(KTn, 6 ),
- + K(KTn, 7 ),K(KTn, 8 ),K(KTn,10 ),K(KTn,11 ),K(KTn,12 ),KIGNORE ,K(KTn, 7 ),KENTER ,
- + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTn,26 ),K(KTn,24 ),
- + K(KTn, 3 ),K(KTn,22 ),K(KTn, 2 ),K(KTn,14 ),K(KTn,13 ),KIGNORE ,KIGNORE ,KIGNORE ,
- + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn, 0 ),
- + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN
- +};
- +
- +u_short alt_map[]=
- +{
- + K(KMt,27 ),K(KCn, 0 ),K(KCn, 1 ),K(KCn, 2 ),K(KCn, 3 ),K(KCn, 4 ),K(KCn, 5 ),K(KCn, 6 ),
- + K(KCn, 7 ),K(KCn, 8 ),K(KCn, 9 ),K(KCn,10 ),K(KCn,11 ),KIGNORE ,KSCRLLK ,KINTR ,
- + K(KMt,'`'),K(KMt,'1'),K(KMt,'2'),K(KMt,'3' ),K(KMt,'4'),K(KMt,'5'),K(KMt,'6' ),K(KMt,'7'),
- + K(KMt,'8'),K(KMt,'9'),K(KMt,'0'),K(KMt,'-' ),K(KMt,'='),K(KMt,'£'),K(KMt,127 ),K(KTf,21 ),
- + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,K(KMt, 9 ),K(KMt,'q'),
- + K(KMt,'w'),K(KMt,'e'),K(KMt,'r'),K(KMt,'t' ),K(KMt,'y'),K(KMt,'u'),K(KMt,'i' ),K(KMt,'o'),
- + K(KMt,'p'),K(KMt,'['),K(KMt,']'),K(KMt,'\\'),K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPADA_7 ,
- + KPADA_8 ,KPADA_9 ,KPAD_MI ,KCTRL ,K(KMt,'a'),K(KMt,'s'),K(KMt,'d' ),K(KMt,'f'),
- + K(KMt,'g'),K(KMt,'h'),K(KMt,'j'),K(KMt,'k' ),K(KMt,'l'),K(KMt,';'),K(KMt,'\''),K(KMt,13 ),
- + KPADA_4 ,KPADA_5 ,KPADA_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KMt,'z' ),K(KMt,'x'),
- + K(KMt,'c'),K(KMt,'v'),K(KMt,'b'),K(KMt,'n' ),K(KMt,'m'),K(KMt,','),K(KMt,'.' ),KIGNORE ,
- + KSHIFT ,K(KTc,Kcu),KPADA_1 ,KPADA_2 ,KPADA_3 ,KCAPSLK ,KALT ,K(KMt,' '),
- + KALTGR ,KCTRL ,CONS_DEC ,K(KTc,Kcd ),CONS_INC ,KPADA_0 ,KPAD_DT ,KPAD_EN
- +};
- +
- +u_short ctrl_alt_map[]=
- +{
- + KIGNORE ,K(KCn, 0 ),K(KCn, 1 ),K(KCn, 2 ),K(KCn, 3 ),K(KCn, 4 ),K(KCn, 5 ),K(KCn, 6 ),
- + K(KCn, 7 ),K(KCn, 8 ),K(KCn, 9 ),K(KCn,10 ),K(KCn,11 ),KIGNORE ,KIGNORE ,KINTR ,
- + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
- + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ),
- + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KMt,17 ),
- + K(KMt,23 ),K(KMt, 5 ),K(KMt,18 ),K(KMt,20 ),K(KMt,25 ),K(KMt,21 ),K(KMt, 9 ),K(KMt,15 ),
- + K(KMt,16 ),KIGNORE ,KIGNORE ,KIGNORE ,KREBOOT ,K(KTf,23 ),K(KTf,25 ),KPAD_7 ,
- + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KMt, 1 ),K(KMt,19 ),K(KMt, 4 ),K(KMt, 6 ),
- + K(KMt, 7 ),K(KMt, 8 ),K(KMt,10 ),K(KMt,11 ),K(KMt,12 ),KIGNORE ,KIGNORE ,KENTER ,
- + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KMt,26 ),K(KMt,24 ),
- + K(KMt, 3 ),K(KMt,22 ),K(KMt, 2 ),K(KMt,14 ),K(KMt,13 ),KIGNORE ,KIGNORE ,KIGNORE ,
- + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,KIGNORE ,
- + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KREBOOT ,KPAD_EN
- +};
- +
- +ushort *key_maps[MAX_NR_KEYMAPS] = {
- + plain_map, shift_map, altgr_map, 0,
- + ctrl_map, shift_ctrl_map, 0, 0,
- + alt_map, 0, 0, 0,
- + ctrl_alt_map, 0
- +};
- +
- +unsigned int keymap_count = 7;
- +
- +/*
- + * Philosophy: most people do not define more strings, but they who do
- + * often want quite a lot of string space. So, we statically allocate
- + * the default and allocate dynamically in chunks of 512 bytes.
- + */
- +
- +char func_buf[] = {
- + '\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,
- + '\033', '[', 'M', 0,
- + '\033', '[', 'P', 0,
- +};
- +
- +char *funcbufptr = func_buf;
- +int funcbufsize = sizeof(func_buf);
- +int funcbufleft = 0; /* space left */
- +
- +char *func_table[MAX_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,
- + 0,
- + 0,
- + func_buf + 149,
- + 0,
- +};
- +
- +struct kbdiacr accent_table[MAX_DIACR] = {
- + {'`', 'A', '\300'}, {'`', 'a', '\340'},
- + {'\'', 'A', '\301'}, {'\'', 'a', '\341'},
- + {'^', 'A', '\302'}, {'^', 'a', '\342'},
- + {'~', 'A', '\303'}, {'~', 'a', '\343'},
- + {'"', 'A', '\304'}, {'"', 'a', '\344'},
- + {'O', 'A', '\305'}, {'o', 'a', '\345'},
- + {'0', 'A', '\305'}, {'0', 'a', '\345'},
- + {'A', 'A', '\305'}, {'a', 'a', '\345'},
- + {'A', 'E', '\306'}, {'a', 'e', '\346'},
- + {',', 'C', '\307'}, {',', 'c', '\347'},
- + {'`', 'E', '\310'}, {'`', 'e', '\350'},
- + {'\'', 'E', '\311'}, {'\'', 'e', '\351'},
- + {'^', 'E', '\312'}, {'^', 'e', '\352'},
- + {'"', 'E', '\313'}, {'"', 'e', '\353'},
- + {'`', 'I', '\314'}, {'`', 'i', '\354'},
- + {'\'', 'I', '\315'}, {'\'', 'i', '\355'},
- + {'^', 'I', '\316'}, {'^', 'i', '\356'},
- + {'"', 'I', '\317'}, {'"', 'i', '\357'},
- + {'-', 'D', '\320'}, {'-', 'd', '\360'},
- + {'~', 'N', '\321'}, {'~', 'n', '\361'},
- + {'`', 'O', '\322'}, {'`', 'o', '\362'},
- + {'\'', 'O', '\323'}, {'\'', 'o', '\363'},
- + {'^', 'O', '\324'}, {'^', 'o', '\364'},
- + {'~', 'O', '\325'}, {'~', 'o', '\365'},
- + {'"', 'O', '\326'}, {'"', 'o', '\366'},
- + {'/', 'O', '\330'}, {'/', 'o', '\370'},
- + {'`', 'U', '\331'}, {'`', 'u', '\371'},
- + {'\'', 'U', '\332'}, {'\'', 'u', '\372'},
- + {'^', 'U', '\333'}, {'^', 'u', '\373'},
- + {'"', 'U', '\334'}, {'"', 'u', '\374'},
- + {'\'', 'Y', '\335'}, {'\'', 'y', '\375'},
- + {'T', 'H', '\336'}, {'t', 'h', '\376'},
- + {'s', 's', '\337'}, {'"', 'y', '\377'},
- + {'s', 'z', '\337'}, {'i', 'j', '\377'},
- +};
- +
- +unsigned int accent_table_size = 68;
- diff -urNwbB linux/arch/arm/drivers/char/diacr.h linux.arm/arch/arm/drivers/char/diacr.h
- --- linux/arch/arm/drivers/char/diacr.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/diacr.h Sun Feb 11 19:43:56 1996
- @@ -0,0 +1,8 @@
- +#ifndef _DIACR_H
- +#define _DIACR_H
- +#include <linux/kd.h>
- +
- +extern struct kbdiacr accent_table[];
- +extern unsigned int accent_table_size;
- +
- +#endif /* _DIACR_H */
- diff -urNwbB linux/arch/arm/drivers/char/iic.c linux.arm/arch/arm/drivers/char/iic.c
- --- linux/arch/arm/drivers/char/iic.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/iic.c Sun Mar 3 12:33:06 1996
- @@ -0,0 +1,137 @@
- +/*
- + * linux/arch/arm/drivers/char/iic.c
- + *
- + * Copyright (C) 1995, 1996 Russell King
- + *
- + * IIC is used to get the current time from the CMOS rtc.
- + */
- +
- +#include <asm/system.h>
- +#include <asm/delay.h>
- +#include <asm/io.h>
- +
- +extern unsigned char volatile *const ioc;
- +
- +#define DELAY udelay(10)
- +
- +static void iic_start (void)
- +{
- + unsigned char out;
- +
- + out = ioc[0] | 0xc2;
- +
- + ioc[0] = out;
- + DELAY;
- +
- + ioc[0] = out ^ 1;
- + DELAY;
- +}
- +
- +static void iic_stop (void)
- +{
- + unsigned char out;
- +
- + out = ioc[0] | 0xc3;
- +
- + DELAY;
- + ioc[0] = out ^ 1;
- +
- + DELAY;
- + ioc[0] = out;
- +}
- +
- +static int iic_sendbyte (unsigned char b)
- +{
- + unsigned char out, in;
- + int i;
- +
- + out = (ioc[0] & 0xFC) | 0xC0;
- +
- + ioc[0] = out;
- + for (i = 7; i >= 0; i--) {
- + ioc[0] = out | ((b & (1 << i)) ? 1 : 0);
- + DELAY;
- +
- + ioc[0] = out | ((b & (1 << i)) ? 1 : 0) | 2;
- + DELAY;
- +
- + ioc[0] = out | ((b & (1 << i)) ? 1 : 0);
- + }
- + ioc[0] = out | 1;
- + DELAY;
- +
- + ioc[0] = out | 3;
- + DELAY;
- +
- + in = ioc[0] & 1;
- +
- + ioc[0] = out | 1;
- + DELAY;
- +
- + ioc[0] = out;
- + DELAY;
- +
- + if(in) {
- + printk("No acknowledge from RTC\n");
- + return 1;
- + } else
- + return 0;
- +}
- +
- +static unsigned char iic_recvbyte (void)
- +{
- + unsigned char out, in;
- + int i;
- +
- + out = (ioc[0] & 0xFC) | 0xC0;
- +
- + ioc[0] = out;
- + in = 0;
- + for (i = 7; i >= 0; i--) {
- + ioc[0] = out | 1;
- + DELAY;
- +
- + ioc[0] = out | 3;
- + DELAY;
- +
- + in = (in << 1) | (ioc[0] & 1);
- + ioc[0] = out | 1;
- + DELAY;
- + }
- + ioc[0] = out;
- + DELAY;
- +
- + ioc[0] = out | 2;
- + DELAY;
- +
- + return in;
- +}
- +
- +void iic_control (unsigned char addr, unsigned char loc, unsigned char *buf, int len)
- +{
- + iic_start();
- +
- + if (iic_sendbyte(addr & 0xfe))
- + goto error;
- +
- + if (iic_sendbyte(loc))
- + goto error;
- +
- + if (addr & 1) {
- + int i;
- +
- + for (i = 0; i < len; i++)
- + if (iic_sendbyte (buf[i]))
- + break;
- + } else {
- + int i;
- +
- + iic_stop();
- + iic_start();
- + iic_sendbyte(addr|1);
- + for (i = 0; i < len; i++)
- + buf[i] = iic_recvbyte ();
- + }
- +error:
- + iic_stop();
- +}
- diff -urNwbB linux/arch/arm/drivers/char/kbdmouse.c linux.arm/arch/arm/drivers/char/kbdmouse.c
- --- linux/arch/arm/drivers/char/kbdmouse.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/kbdmouse.c Sun Mar 3 12:39:09 1996
- @@ -0,0 +1,188 @@
- +/*
- + * linux/arch/arm/drivers/char/kbdmouse.c
- + *
- + * Copyright (C) 1995, 1996 Russell King
- + *
- + * Medium-level interface for quadrature mouse connected to the keyboard.
- + */
- +
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/version.h>
- +
- +char kernel_version[] = UTS_RELEASE;
- +#define arch_mouse_init init_module
- +#else
- +#define MOD_INC_USE_COUNT
- +#define MOD_DEC_USE_COUNT
- +#endif
- +
- +#include <linux/config.h>
- +#include <linux/kernel.h>
- +#include <linux/sched.h>
- +#include <linux/signal.h>
- +#include <linux/errno.h>
- +#include <linux/mm.h>
- +#include <linux/mouse.h>
- +#include <linux/random.h>
- +
- +#include <asm/system.h>
- +#include <asm/segment.h>
- +
- +char mouse_buttons;
- +int mouse_dxpos;
- +int mouse_dypos;
- +int mouse_present;
- +char mouse_ready;
- +char mouse_active;
- +struct wait_queue *mouse_wait;
- +struct fasync_struct *fasyncptr;
- +
- +static int fasync_mouse(struct inode *inode, struct file *filp, int on)
- +{
- + int retval;
- +
- + retval = fasync_helper(inode, filp, on, &fasyncptr);
- + if (retval < 0)
- + return retval;
- + return 0;
- +}
- +
- +static void close_mouse(struct inode *inode, struct file *file)
- +{
- + fasync_mouse (inode, file, 0);
- + if (--mouse_active)
- + return;
- + mouse_ready = 0;
- +
- + MOD_DEC_USE_COUNT;
- +}
- +
- +static int open_mouse(struct inode *inode,struct file *file)
- +{
- + unsigned long flags;
- +
- + if (!mouse_present)
- + return -EINVAL;
- + if (mouse_active++)
- + return 0;
- +
- + save_flags (flags);
- + cli ();
- + mouse_active = 1;
- + mouse_ready = 0;
- + mouse_dxpos = 0;
- + mouse_dypos = 0;
- + mouse_buttons = 0;
- + restore_flags (flags);
- +
- + MOD_INC_USE_COUNT;
- +
- + return 0;
- +}
- +
- +static int write_mouse(struct inode *inode,struct file *file,const char *buffer,int count)
- +{
- + return -EINVAL;
- +}
- +
- +static int read_mouse(struct inode *inode,struct file *file,char *buffer,int count)
- +{
- + unsigned long flags;
- + int dxpos, dypos, i, buttons;
- +
- + if (count < 3)
- + return -EINVAL;
- + if ((i = verify_area(VERIFY_WRITE, buffer, count)))
- + return i;
- + if (!mouse_ready)
- + return -EAGAIN;
- +
- + save_flags (flags);
- + cli ();
- +
- + dxpos = mouse_dxpos;
- + dypos = mouse_dypos;
- + buttons = mouse_buttons ^ 7;
- +
- + if (dxpos < -127)
- + dxpos =- 127;
- + if (dxpos > 127)
- + dxpos = 127;
- + if (dypos <- 127)
- + dypos =- 127;
- + if (dypos > 127)
- + dypos = 127;
- +
- + mouse_dxpos -= dxpos;
- + mouse_dypos -= dypos;
- + mouse_ready = 0;
- +
- + restore_flags (flags);
- +
- + put_fs_byte ((char) buttons | 0x80, buffer);
- + put_fs_byte ((char) dxpos, buffer + 1);
- + put_fs_byte ((char) dypos, buffer + 2);
- + for(i = 3; i < count; i++)
- + put_fs_byte (0x00, buffer + i);
- +
- + return i;
- +}
- +
- +static int select_mouse(struct inode *inode,struct file *file,int sel_type,select_table *wait)
- +{
- + if (sel_type == SEL_IN) {
- + if (mouse_ready)
- + return 1;
- + select_wait (&mouse_wait,wait);
- + }
- + return 0;
- +}
- +
- +struct file_operations kbd_mouse_fops=
- +{
- + NULL, /* mouse_seek */
- + read_mouse,
- + write_mouse,
- + NULL, /* mouse_readdir */
- + select_mouse,
- + NULL, /* mouse_ioctl */
- + NULL, /* mouse_mmap */
- + open_mouse,
- + close_mouse,
- + NULL,
- + fasync_mouse,
- +};
- +
- +static struct mouse kbd_mouse = {
- + 4, "kbdmouse", &kbd_mouse_fops
- +};
- +
- +int arch_mouse_init(void)
- +{
- + unsigned long flags;
- +
- + save_flags (flags);
- + cli ();
- +
- + mouse_buttons=0;
- + mouse_dxpos =0;
- + mouse_dypos =0;
- + mouse_present=1;
- + mouse_ready =0;
- + mouse_active =0;
- + mouse_wait =NULL;
- +
- + restore_flags (flags);
- + mouse_register (&kbd_mouse);
- + return 0;
- +}
- +
- +#ifdef MODULE
- +void cleanup_module(void)
- +{
- + if (MOD_IN_USE)
- + printk ("kcdmouse: in use - remove delayed\n");
- + mouse_deregister (&kbd_mouse);
- +}
- +#endif
- diff -urNwbB linux/arch/arm/drivers/char/keyboard.c linux.arm/arch/arm/drivers/char/keyboard.c
- --- linux/arch/arm/drivers/char/keyboard.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/keyboard.c Sun Mar 3 12:39:34 1996
- @@ -0,0 +1,1302 @@
- +/*
- + * linux/arch/arm/drivers/block/keyboard.c
- + *
- + * Keyboard driver for Linux using Latin-1.
- + *
- + * Written for linux by Johan Myreen as a translation from
- + * the assembly version by Linus (with diacriticals added)
- + *
- + * Some additional features added by Christoph Niemann (ChN), March 1993
- + *
- + * Loadable keymaps by Risto Kankkunen, May 1993
- + *
- + * Diacriticals redone & other small changes, aeb@cwi.nl, June 93
- + * Added decr/incr_console, dynamic keymaps, unicode support,
- + * dynamic function/string keys, led setting, Sept 1994
- + * `Sticky' modifier keys, 951006
- + *
- + * Majorly altered for use with arm Russell King (rmk92@ecs.soton.ac.uk).
- + */
- +
- +#define IRQ_KEYBOARDRX 15
- +#define IRQ_KEYBOARDTX 14
- +
- +#define VERSION 102
- +
- +#include <linux/config.h>
- +#include <linux/sched.h>
- +#include <linux/interrupt.h>
- +#include <linux/tty.h>
- +#include <linux/tty_flip.h>
- +#include <linux/mm.h>
- +#include <linux/ptrace.h>
- +#include <linux/signal.h>
- +#include <linux/timer.h>
- +#include <linux/random.h>
- +#include <linux/ctype.h>
- +
- +#include <asm/bitops.h>
- +#include <asm/irq.h>
- +
- +#include "kbd_kern.h"
- +#include "diacr.h"
- +#include "vt_kern.h"
- +
- +#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
- +
- +#define KBD_REPORT_ERR
- +#define KBD_REPORT_UNKN
- +
- +#ifndef KBD_DEFMODE
- +#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
- +#endif
- +
- +#ifndef KBD_DEFLEDS
- +/*
- + * Starts with NumLock off.
- + */
- +#define KBD_DEFLEDS 0
- +#endif
- +
- +#ifndef KBD_DEFLOCK
- +#define KBD_DEFLOCK 0
- +#endif
- +
- +#define REPEAT_TIMEOUT HZ*300/1000
- +#define REPEAT_RATE HZ*30/1000
- +
- +extern void poke_blanked_console (void);
- +extern void ctrl_alt_del (void);
- +extern void reset_vc (unsigned int new_console);
- +extern void change_console (unsigned int console);
- +
- +/*
- + * global state includes the following, and various static variables
- + * in this module: shift_state, diacr, npadch, dead_key_next.
- + * (last console is now a global variable).
- + */
- +
- +/* shift state counters.. */
- +static unsigned char k_down[NR_SHIFT] = {0, };
- +/*
- + * Key down counters
- + */
- +#define BITS_PER_SHORT (8*sizeof (unsigned short))
- +static unsigned short key_down[256/BITS_PER_SHORT] = {0, };
- +
- +extern int last_console;
- +static int want_console = -1;
- +static int dead_key_next = 0;
- +/*
- + * In order to retrieve the shift_state (for the mouse server), either
- + * the variable must be global, or a new procedure must be created to
- + * return the value. I chose the former way.
- + */
- +/*static*/ int shift_state = 0;
- +static int npadch = -1; /* -1 or number assembled on pad */
- +static unsigned char diacr = 0;
- +static char rep = 0; /* Flag telling character repeat */
- +static int kbd_repeatkey = -1;
- +static int kbd_repeattimeout = REPEAT_TIMEOUT;
- +static int kbd_repeatrate = REPEAT_RATE;
- +
- +struct kbd_struct kbd_table[MAX_NR_CONSOLES];
- +static struct tty_struct **ttytab;
- +static struct kbd_struct * kbd = kbd_table;
- +static struct tty_struct * tty = NULL;
- +
- +extern void compute_shiftstate (void);
- +
- +typedef void (*k_hand)(unsigned char value, char up_flag);
- +typedef void (k_handfn)(unsigned char value, char up_flag);
- +
- +static k_handfn
- + do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
- + do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_ignore;
- +
- +static k_hand key_handler[16] = {
- + do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
- + do_meta, do_ascii, do_lock, do_lowercase, do_slock,
- + do_ignore, do_ignore, do_ignore
- +};
- +
- +typedef void (*void_fnp)(void);
- +typedef void (void_fn)(void);
- +
- +static void_fn do_null, enter, show_ptregs, send_intr, lastcons, caps_toggle,
- + num, hold, scroll_forw, scroll_back, boot_it, caps_on, compose,
- + SAK, decr_console, incr_console, spawn_console, show_stack, bare_num;
- +
- +static void_fnp spec_fn_table[] = {
- + do_null, enter, show_ptregs, show_mem,
- + show_state, send_intr, lastcons, caps_toggle,
- + num, hold, scroll_forw, scroll_back,
- + boot_it, caps_on, compose, SAK,
- + decr_console, incr_console, show_stack, bare_num
- +};
- +
- +/* maximum values each key_handler can handle */
- +const int max_vals[] = {
- + 255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1,
- + NR_DEAD - 1, 255, 3, NR_SHIFT - 1,
- + 255, NR_ASCII - 1, NR_LOCK - 1, 255,
- + NR_LOCK - 1
- +};
- +
- +const int NR_TYPES = SIZE(max_vals);
- +
- +static void put_queue(int);
- +static unsigned char handle_diacr(unsigned char);
- +
- +/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
- +static struct pt_regs * pt_regs;
- +
- +/*
- + * Many other routines do put_queue, but I think either
- + * they produce ASCII, or they produce some user-assigned
- + * string, and in both cases we might assume that it is
- + * in utf-8 already.
- + */
- +void to_utf8(ushort c) {
- + if (c < 0x80)
- + put_queue(c); /* 0******* */
- + else if (c < 0x800) {
- + put_queue(0xc0 | (c >> 6)); /* 110***** 10****** */
- + put_queue(0x80 | (c & 0x3f));
- + } else {
- + put_queue(0xe0 | (c >> 12)); /* 1110**** 10****** 10****** */
- + put_queue(0x80 | ((c >> 6) & 0x3f));
- + put_queue(0x80 | (c & 0x3f));
- + }
- + /* uft-8 is defined for words of up to 36 bits,
- + but we need only 16 bits here */
- +}
- +
- +/*
- + * ARM mouse specifics stuff.
- + *
- + * Since the mouse movements and buttons are part of the
- + * keyboard protocol, we have to handle this part in this
- + * driver. The mouse specific parts are in arcmouse.c.
- + */
- +#ifdef CONFIG_KBDMOUSE
- +extern struct wait_queue *mouse_wait;
- +extern char mouse_buttons;
- +extern int mouse_dxpos;
- +extern int mouse_dypos;
- +extern char mouse_ready;
- +#endif
- +/*
- + * Protocol codes to send the keyboard.
- + */
- +/* reset keyboard */
- +#define HRST 0xff
- +/* reset response */
- +#define RAK1 0xfe
- +/* reset response */
- +#define RAK2 0xfd
- +/* Ack for first keyboard pair */
- +#define BACK 0x3f
- +/* Last data byte ack (key scanning + mouse movement scanning) */
- +#define SMAK 0x33
- +/* Last data byte ack (mouse movement scanning) */
- +#define MACK 0x32
- +/* Last data byte ack (key scanning) */
- +#define SACK 0x31
- +/* Request mouse data */
- +#define RQMP 0x22
- +/* nothing */
- +#define PRST 0x21
- +/* Request ID */
- +#define RQID 0x20
- +
- +#define UP_FLAG 0x0100
- +
- +static unsigned char kbd_sendvala[4];
- +static unsigned char kbd_sendptri;
- +static unsigned char kbd_sendptro;
- +static unsigned char ledstate = 0xff;
- +static unsigned char getleds(void);
- +extern unsigned char *ioc;
- +
- +/*
- + * This array converts the scancode that we get from the keyboard to the
- + * real rows/columns on the A5000 keyboard. This might be keyboard specific...
- + *
- + * It is these values that we use to maintain the key down array. That way, we
- + * should pick up on the ghost key presses (which is what happens when you press
- + * three keys, and the keyboard thinks you have pressed four!)
- + *
- + * Row 8 (0x80+c) is actually a column with one key per row. It is isolated from
- + * the other keys, and can't cause these problems (its used for shift, ctrl, alt etc).
- + *
- + * Illegal scancodes are denoted by an 0xff (in other words, we don't know about
- + * them, and can't process them for ghosts). This does however, cause problems with
- + * autorepeat processing...
- + */
- +static unsigned char scancode_2_colrow[256] = {
- + 0x01, 0x42, 0x32, 0x33, 0x43, 0x56, 0x5a, 0x6c, 0x7c, 0x5c, 0x5b, 0x6b, 0x7b, 0x84, 0x70, 0x60,
- + 0x11, 0x51, 0x62, 0x63, 0x44, 0x54, 0x55, 0x45, 0x46, 0x4a, 0x3c, 0x4b, 0x59, 0x49, 0x69, 0x79,
- + 0x83, 0x40, 0x30, 0x3b, 0x39, 0x38, 0x31, 0x61, 0x72, 0x73, 0x64, 0x74, 0x75, 0x65, 0x66, 0x6a,
- + 0x1c, 0x2c, 0x7a, 0x36, 0x48, 0x68, 0x78, 0x20, 0x2b, 0x29, 0x28, 0x81, 0x71, 0x22, 0x23, 0x34,
- + 0x24, 0x25, 0x35, 0x26, 0x3a, 0x0c, 0x2a, 0x76, 0x10, 0x1b, 0x19, 0x18, 0x82, 0xff, 0x21, 0x12,
- + 0x13, 0x14, 0x04, 0x05, 0x15, 0x16, 0x1a, 0x0a, 0x85, 0x77, 0x00, 0x0b, 0x09, 0x02, 0x80, 0x03,
- + 0x87, 0x86, 0x06, 0x17, 0x27, 0x07, 0x37, 0x08, 0xff,
- +};
- +
- +static unsigned short ghost_down[128/BITS_PER_SHORT];
- +
- +/*
- + * As yet, we don't support setting and getting the
- + * key codes. I'll have to find out where this is used.
- + */
- +int setkeycode(unsigned int scancode, unsigned int keycode)
- +{
- +#if 0
- + if (scancode < SC_LIM || scancode > 255 || keycode > 127)
- + return -EINVAL;
- + if (scancode < 128)
- + high_keys[scancode - SC_LIM] = keycode;
- + else
- + e0_keys[scancode - 128] = keycode;
- + return 0;
- +#else
- + return -EINVAL;
- +#endif
- +}
- +
- +int getkeycode(unsigned int scancode)
- +{
- +#if 0
- + return
- + (scancode < SC_LIM || scancode > 255) ? -EINVAL :
- + (scancode < 128) ? high_keys[scancode - SC_LIM] :
- + e0_keys[scancode - 128];
- +#else
- + return -EINVAL;
- +#endif
- +}
- +
- +
- +/* ----------------------------------------------------------------------------------------- */
- +/*
- + * This is the keyboard specific part.
- + */
- +static void key_callback(unsigned long nr)
- +{
- + rep = 1;
- + mark_bh (KEYBOARD_BH);
- +}
- +
- +static struct timer_list key_timer =
- +{
- + NULL, NULL, 0, 0, key_callback
- +};
- +
- +static void kbd_key(unsigned int raw_keycode, int repeat_timeout)
- +{
- + unsigned int up_flag;
- + unsigned int keycode;
- + unsigned char real_keycode;
- +
- + up_flag = raw_keycode & UP_FLAG;
- + keycode = raw_keycode & 255;
- +
- + /*
- + * Separate out the mouse stuff first
- + */
- + if (keycode >= 0x70 && keycode <= 0x72) {
- +#ifdef CONFIG_KBDMOUSE
- + switch (keycode) {
- + case 0x70: /* Left mouse button */
- + if (up_flag)
- + mouse_buttons &= ~4;
- + else
- + mouse_buttons |= 4;
- + break;
- +
- + case 0x71: /* Middle mouse button */
- + if (up_flag)
- + mouse_buttons &= ~2;
- + else
- + mouse_buttons |= 2;
- + break;
- +
- + case 0x72:/* Right mouse button */
- + if (up_flag)
- + mouse_buttons &= ~1;
- + else
- + mouse_buttons |= 1;
- + break;
- + default:
- +#ifdef KBD_REPORT_UNKN
- + printk ("kbd: unknown scancode 0x%04x\n", raw_keycode);
- +#endif
- + return;
- + }
- + add_mouse_randomness (mouse_buttons << 16);
- + mouse_ready = 1;
- + wake_up_interruptible (&mouse_wait);
- +#endif
- + mark_bh (KEYBOARD_BH);
- + return;
- + }
- +
- + if (keycode >= 0x70) {
- +#ifdef KBD_REPORT_UNKN
- + printk ("kbd: unknown scancode 0x%04x\n", raw_keycode);
- +#endif
- + return;
- + }
- +
- + real_keycode = scancode_2_colrow[keycode];
- +
- + /*
- + * We have to work out if we accept this key press as a real key, or
- + * if it is a ghost. IE. If you press three keys, the keyboard will think
- + * that you've pressed a fouth: (@ = key down, # = ghost)
- + *
- + * 0 1 2 3 4 5 6 7
- + * | | | | | | | |
- + * 0-+-+-+-+-+-+-+-+-
- + * | | | | | | | |
- + * 1-+-@-+-+-+-@-+-+-
- + * | | | | | | | |
- + * 2-+-+-+-+-+-+-+-+-
- + * | | | | | | | |
- + * 3-+-@-+-+-+-#-+-+-
- + * | | | | | | | |
- + *
- + * This is what happens when you have a matrix keyboard...
- + */
- +
- + if ((real_keycode & 0x80) == 0) {
- + int rr, kc = (real_keycode >> 4) & 7;
- + int cc;
- + unsigned short res, kdownkc;
- +
- + kdownkc = ghost_down[kc] | (1 << (real_keycode & 15));
- +
- + for (rr = 0; rr < 128/BITS_PER_SHORT; rr++)
- + if (rr != kc && (res = ghost_down[rr] & kdownkc)) {
- + /*
- + * we have found a second row with at least one key pressed in the
- + * same column.
- + */
- + for (cc = 0; res; res >>= 1)
- + cc += (res & 1);
- +
- + if (cc > 1)
- + return; /* ignore it */
- + }
- + if (up_flag)
- + clear_bit (real_keycode, ghost_down);
- + else
- + set_bit (real_keycode, ghost_down);
- +
- + }
- +
- + if (kbd_repeatkey == (raw_keycode & ~UP_FLAG) || !up_flag)
- + kbd_repeatkey = -1;
- +
- + add_keyboard_randomness (raw_keycode);
- +
- + /*
- + * We have to disable interrupts here to prevent the timer from
- + * being put on the list twice or a key being released during
- + * the processing of autorepeat
- + */
- + del_timer (&key_timer);
- +
- + tty = ttytab[fg_console];
- + kbd = kbd_table + fg_console;
- +
- + if (up_flag) {
- + rep = 0;
- + clear_bit (keycode, key_down);
- + /* don't report an error if key is already up - happens when a ghost key is released */
- + } else
- + rep = set_bit (keycode, key_down);
- +
- + if (kbd->kbdmode == VC_RAW || kbd->kbdmode == VC_MEDIUMRAW) {
- + put_queue (keycode | (up_flag ? 0x80 : 0));
- + return;
- + }
- +
- + /*
- + * We have to do our own auto-repeat processing...
- + */
- + if (!up_flag && repeat_timeout) {
- + kbd_repeatkey = raw_keycode;
- + if (vc_kbd_mode(kbd, VC_REPEAT)) {
- + key_timer.expires = jiffies + repeat_timeout;
- + add_timer(&key_timer);
- + }
- + }
- +
- + /*
- + * 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.
- + */
- + if (!rep || (vc_kbd_mode (kbd, VC_REPEAT) && tty &&
- + (L_ECHO(tty) || (tty->driver.chars_in_buffer (tty) == 0)))) {
- + u_short keysym;
- + u_char type;
- +
- + /* the XOR below used to be an OR */
- + int shift_final = shift_state ^ kbd->lockstate ^ kbd->slockstate;
- + u_short *key_map = key_maps[shift_final];
- +
- + if (key_map != NULL) {
- + keysym = key_map[keycode];
- + type = KTYP(keysym);
- +
- + if (type >= 0xf0) {
- + type -= 0xf0;
- +
- + if (type == KT_LETTER) {
- + type = KT_LATIN;
- + if (vc_kbd_led(kbd, VC_CAPSLOCK)) {
- + key_map = key_maps[shift_final ^ (1<<KG_SHIFT)];
- + if (key_map)
- + keysym = key_map[keycode];
- + }
- + }
- + (*key_handler[type])(keysym & 0xff, up_flag ? 0x80 : 0);
- + if (type != KT_SLOCK)
- + kbd->slockstate = 0;
- + } else {
- + if (!up_flag)
- + to_utf8(keysym);
- + }
- + } else {
- + /*
- + * maybe beep?
- + * we have at least to update shift_state
- + * how? two almost equivalent choices follow
- + */
- +#if 1
- + compute_shiftstate ();
- +#else
- + keysym = U(plain_map[keycode]);
- + type = KTYP(keysym);
- + if (type == KT_SHIFT)
- + (*key_handler[type]) (keysym & 0xff, up_flag ? 0x80 : 0);
- +#endif
- + }
- + }
- +}
- +
- +static void kbd_sendval(unsigned char val)
- +{
- + kbd_sendvala[kbd_sendptri] = val;
- + kbd_sendptri = (kbd_sendptri + 1) & 3;
- + disable_irq (15);
- + enable_irq (14);
- +}
- +
- +static void kbd_reset(void)
- +{
- + int i;
- +
- + for (i = 0; i < NR_SHIFT; i++)
- + k_down[i] = 0;
- +
- + for (i = 0; i < 256/BITS_PER_SHORT; i++)
- + key_down[i] = 0;
- +
- + for (i = 0; i < 128/BITS_PER_SHORT; i++)
- + ghost_down[i] = 0;
- +
- + shift_state = 0;
- +}
- +
- +/*
- + * Keyboard states:
- + * 0 initial reset condition, sent HRST, wait for HRST
- + * 1 Sent RAK1, wait for RAK1
- + * 2 Sent RAK2, wait for RAK2
- + * 3 Sent SMAK, wait for *
- + * 4 Wait for second keyboard nibble for key pressed
- + * 5 Wait for second keyboard nibble for key released
- + * 6 Wait for second part of mouse data
- + */
- +
- +static void kbd_rx(int irq, struct pt_regs *regs)
- +{
- + int keyval;
- + static char kbd_mousedx=0;
- + char kbd_mousedy;
- + static unsigned char kbd_state=0;
- + static unsigned char kbd_keyhigh=0;
- +
- + pt_regs = regs;
- +
- + keyval=ioc[4];
- +
- + switch(kbd_state) {
- + case 0:/* initial reset condition */
- + if (keyval == HRST) {
- + kbd_reset ();
- + kbd_sendval (RAK1);
- + kbd_state = 1;
- + } else
- + goto kbd_wontreset;
- + break;
- +
- + case 1:/* Sent RAK1 */
- + if (keyval == RAK1) {
- + kbd_sendval (RAK2);
- + kbd_state = 2;
- + } else
- + goto kbd_wontreset;
- + break;
- +
- + case 2:/* Sent RAK2 */
- + if (keyval == RAK2) {
- + kbd_sendval (SMAK);
- + kbd_state = 3;
- + ledstate = 0xff;
- + mark_bh (KEYBOARD_BH);
- + } else
- + goto kbd_wontreset;
- + break;
- +
- + case 3:/* Send SMAK, ready for any reply */
- + if (keyval == HRST) {
- + kbd_sendval (HRST);
- + kbd_state = 0;
- + } else
- + if (keyval & 0x80) {
- + switch (keyval & 0xf0) {
- + case 0xc0:
- + kbd_keyhigh = keyval;
- + kbd_state = 4;
- + kbd_sendval (BACK);
- + break;
- +
- + case 0xd0:
- + kbd_keyhigh = keyval;
- + kbd_state = 5;
- + kbd_sendval (BACK);
- + break;
- +
- + default:
- + kbd_state = 0;
- + kbd_sendval (HRST);
- + }
- + } else {
- + kbd_mousedx = keyval & 0x40 ? keyval|0x80 : keyval;
- + kbd_state = 6;
- + kbd_sendval (BACK);
- + }
- + break;
- +
- + case 4:
- + if ((keyval & 0xf0) != 0xc0)
- + goto kbd_error;
- + else {
- + kbd_state = 3;
- + kbd_sendval (SMAK);
- + if (((kbd_keyhigh ^ keyval) & 0xf0) == 0) {
- + kbd_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0), kbd_repeattimeout);
- + mark_bh (KEYBOARD_BH);
- + }
- + }
- + break;
- +
- + case 5:
- + if ((keyval & 0xf0) != 0xd0)
- + goto kbd_error;
- + else {
- + kbd_state = 3;
- + kbd_sendval (SMAK);
- + if (((kbd_keyhigh ^ keyval) & 0xf0) == 0) {
- + kbd_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0) | UP_FLAG, 0);
- + mark_bh (KEYBOARD_BH);
- + }
- + }
- + break;
- +
- + case 6:
- + if (keyval & 0x80)
- + goto kbd_error;
- + else {
- + kbd_state = 3;
- + kbd_sendval (SMAK);
- + kbd_mousedy = (char)(keyval & 0x40 ? keyval | 0x80 : keyval);
- +#ifdef CONFIG_KBDMOUSE
- + mouse_dypos += (int)kbd_mousedy;
- + mouse_dxpos += (int)kbd_mousedx;
- + add_mouse_randomness ((mouse_buttons << 16) + (kbd_mousedy << 8) + kbd_mousedx);
- + mouse_ready = 1;
- + wake_up_interruptible (&mouse_wait);
- +#endif
- + mark_bh (KEYBOARD_BH);
- + }
- + break;
- + }
- + return;
- +kbd_wontreset:
- +#ifdef KBD_REPORT_ERR
- + printk ("kbd: keyboard won't reset\n");
- +#endif
- + kbd_sendval (HRST);
- + kbd_state = 0;
- + return;
- +kbd_error:
- +#ifdef KBD_REPORT_ERR
- + printk ("kbd: keyboard out of sync - resetting\n");
- +#endif
- + kbd_sendval (HRST);
- + kbd_state = 0;
- + return;
- +}
- +
- +static void kbd_tx(int irq, struct pt_regs *regs)
- +{
- + if(kbd_sendptri!=kbd_sendptro)
- + {
- + ioc[0x04]=kbd_sendvala[kbd_sendptro];
- + kbd_sendptro=(kbd_sendptro+1)&3;
- + }
- + if(kbd_sendptri==kbd_sendptro)
- + {
- + disable_irq(14);
- + enable_irq(15);
- + }
- +}
- +
- +/* ----------------------------------------------------------------------------------------- */
- +/*
- + * Here starts a copy of the PC keyboard code from linux/drivers/char/keyboard.c
- + */
- +
- +static void put_queue (int ch)
- +{
- + wake_up (&keypress_wait);
- + if (tty) {
- + tty_insert_flip_char (tty, ch, 0);
- + tty_schedule_flip (tty);
- + }
- +}
- +
- +static void puts_queue(char *cp)
- +{
- + wake_up (&keypress_wait);
- + if (!tty)
- + return;
- +
- + while (*cp)
- + tty_insert_flip_char (tty, *cp++, 0);
- + tty_schedule_flip (tty);
- +}
- +
- +static void applkey(int key, char mode)
- +{
- + static char buf[] = { 0x1b, 'O', 0x00, 0x00};
- +
- + buf[1] = (mode ? 'O' : '[');
- + buf[2] = key;
- + puts_queue(buf);
- +}
- +
- +static void enter(void)
- +{
- + put_queue (13);
- + if (vc_kbd_mode(kbd, VC_CRLF))
- + put_queue (10);
- +}
- +
- +static void caps_toggle(void)
- +{
- + if(rep)
- + return;
- + chg_vc_kbd_led(kbd, VC_CAPSLOCK);
- +}
- +
- +static void caps_on (void)
- +{
- + if (rep)
- + return;
- + set_vc_kbd_led (kbd, VC_CAPSLOCK);
- +}
- +
- +static void show_ptregs (void)
- +{
- + extern void register_dump (struct pt_regs *);
- + if (pt_regs)
- + register_dump (pt_regs);
- +}
- +
- +static void show_stack (void)
- +{
- + unsigned long *p;
- + unsigned long *q = (unsigned long *)((int)current->kernel_stack_page + 4096);
- + int i;
- + __asm__("mov %0, sp\n\t": "=r" (p));
- +
- + for(i=0; p < q; p++, i++) {
- + if(i && !(i & 7))
- + printk("\n");
- + printk("%08lX ", *p);
- + }
- +}
- +
- +static void hold (void)
- +{
- + if(rep || !tty)
- + return;
- +
- + /*
- + * Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty);
- + * these routines are also activated by ^S/^Q.
- + * (And SCROLLOCK can also be set by the KDSKBLED.)
- + */
- + if (tty->stopped)
- + start_tty (tty);
- + else
- + stop_tty (tty);
- +}
- +
- +static void num (void)
- +{
- + if(vc_kbd_mode(kbd,VC_APPLIC))
- + applkey('P', 1);
- + else
- + bare_num ();
- +}
- +
- +/*
- + * Bind this to Shift-NumLock if you work in application keypad mode
- + * but want to be able to change the NumLock flag.
- + * Bind this to NumLock if you prefer that the NumLock key always
- + * changes the NumLock flag.
- + */
- +static void bare_num (void)
- +{
- + if (!rep)
- + chg_vc_kbd_led (kbd, VC_NUMLOCK);
- +}
- +
- +static void lastcons (void)
- +{
- + want_console = last_console;
- +}
- +
- +static void decr_console (void)
- +{
- + int i;
- +
- + for (i = fg_console - 1; i != fg_console; i--) {
- + if (i == -1)
- + i = MAX_NR_CONSOLES - 1;
- + if (vc_cons_allocated (i))
- + break;
- + }
- + want_console = i;
- +}
- +
- +static void incr_console (void)
- +{
- + int i;
- +
- + for (i = fg_console + 1; i != fg_console; i++) {
- + if (i == MAX_NR_CONSOLES)
- + i = 0;
- + if (vc_cons_allocated (i))
- + break;
- + }
- + want_console = i;
- +}
- +
- +static void send_intr (void)
- +{
- + if (!tty)
- + return;
- + tty_insert_flip_char (tty, 0, TTY_BREAK);
- + tty_schedule_flip (tty);
- +}
- +
- +static void scroll_forw (void)
- +{
- +}
- +
- +static void scroll_back (void)
- +{
- +}
- +
- +static void boot_it (void)
- +{
- + ctrl_alt_del();
- +}
- +
- +static void compose (void)
- +{
- + dead_key_next = 1;
- +}
- +
- +int spawnpid, spawnsig;
- +
- +static void spawn_console (void)
- +{
- + if (spawnpid)
- + if (kill_proc (spawnpid, spawnsig, 1))
- + spawnpid = 0;
- +}
- +
- +static void SAK (void)
- +{
- + do_SAK(tty);
- +#if 0
- + /*
- + * Need to fix SAK handling to fix up RAW/MEDIUM_RAW and
- + * vt_cons modes before we can enable RAW/MEDIUM_RAW SAK
- + * handling.
- + *
- + * We should do this some day --- the whole point of a secure
- + * attention key is that it should be guaranteed to always
- + * work.
- + */
- + reset_vc (fg_console);
- + do_unblank_screen (); /* not in interrupt routine? */
- +#endif
- +}
- +
- +static void do_ignore (unsigned char value, char up_flag)
- +{
- +}
- +
- +static void do_null (void)
- +{
- + compute_shiftstate ();
- +}
- +
- +static void do_spec (unsigned char value,char up_flag)
- +{
- + if (up_flag)
- + return;
- + if (value >= SIZE(spec_fn_table))
- + return;
- + if (!spec_fn_table[value])
- + return;
- + spec_fn_table[value] ();
- +}
- +
- +static void do_lowercase (unsigned char value, char up_flag)
- +{
- + printk("keyboard.c: do_lowercase was called - impossible\n");
- +}
- +
- +static void do_self(unsigned char value, char up_flag)
- +{
- + if (up_flag)
- + return; /* no action, if this is a key release */
- +
- + if (diacr)
- + value = handle_diacr (value);
- +
- + if (dead_key_next) {
- + dead_key_next = 0;
- + diacr = value;
- + return;
- + }
- + put_queue (value);
- +}
- +
- +#define A_GRAVE '`'
- +#define A_ACUTE '\''
- +#define A_CFLEX '^'
- +#define A_TILDE '~'
- +#define A_DIAER '"'
- +static unsigned char ret_diacr[] = {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER };
- +
- +static void do_dead(unsigned char value, char up_flag)
- +{
- + if (up_flag)
- + return;
- +
- + value = ret_diacr[value];
- + if (diacr == value) { /* pressed twice */
- + diacr = 0;
- + put_queue (value);
- + return;
- + }
- + diacr = value;
- +}
- +
- +static unsigned char handle_diacr(unsigned char ch)
- +{
- + int d = diacr;
- + int i;
- +
- + diacr = 0;
- + if (ch == ' ')
- + return d;
- +
- + for (i = 0; i < accent_table_size; i++) {
- + if (accent_table[i].diacr == d && accent_table[i].base == ch)
- + return accent_table[i].result;
- + }
- +
- + put_queue (d);
- + return ch;
- +}
- +
- +static void do_cons(unsigned char value, char up_flag)
- +{
- + if (up_flag)
- + return;
- + want_console = value;
- +}
- +
- +static void do_fn(unsigned char value, char up_flag)
- +{
- + if (up_flag)
- + return;
- +
- + if (value < SIZE(func_table)) {
- + if (func_table[value])
- + puts_queue (func_table[value]);
- + } else
- + printk ("do_fn called with value=%d\n",value);
- +}
- +
- +static void do_pad(unsigned char value, char up_flag)
- +{
- + static char *pad_chars = "0123456789+-*/\015,.?#";
- + static char *app_map = "pqrstuvwxylSRQMnn?#";
- +
- + if (up_flag)
- + return;
- +
- + if (vc_kbd_mode (kbd, VC_APPLIC) && !k_down[KG_SHIFT]) {
- + applkey (app_map[value], 1);
- + return;
- + }
- +
- + if (!vc_kbd_led (kbd,VC_NUMLOCK))
- + switch (value) {
- + case 15:
- + case 16: do_fn (22, 0); return;
- + case 7: do_fn (20, 0); return;
- + case 8: do_cur( 3, 0); return;
- + case 9: do_fn (24, 0); return;
- + case 4: do_cur( 1, 0); return;
- + case 6: do_cur( 2, 0); return;
- + case 1: do_fn (23, 0); return;
- + case 2: do_cur( 0, 0); return;
- + case 3: do_fn (25, 0); return;
- + case 5: applkey('G',vc_kbd_mode(kbd, VC_APPLIC)); return;
- + }
- +
- + put_queue (pad_chars[value]);
- + if (value == 14 && vc_kbd_mode (kbd, VC_CRLF))
- + put_queue (10);
- +}
- +
- +static void do_cur(unsigned char value, char up_flag)
- +{
- + static char *cur_chars = "BDCA";
- +
- + if (up_flag)
- + return;
- +
- + applkey (cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE));
- +}
- +
- +static void do_shift(unsigned char value, char up_flag)
- +{
- + int old_state = shift_state;
- +
- + if (rep)
- + return;
- +
- + /* Mimic typewriter: a CapsShift key acts like Shift but undoes CapsLock */
- + if (value == KVAL(K_CAPSSHIFT)) {
- + value = KVAL(K_SHIFT);
- + if (!up_flag)
- + clr_vc_kbd_led (kbd, VC_CAPSLOCK);
- + }
- +
- + if(up_flag) {
- + /* handle the case that two shift or control
- + keys are depressed simultaneously */
- + if(k_down[value])
- + k_down[value]--;
- + } else
- + k_down[value]++;
- +
- + if(k_down[value])
- + shift_state |= (1 << value);
- + else
- + shift_state &= ~ (1 << value);
- +
- + /* kludge */
- + if(up_flag && shift_state != old_state && npadch != -1) {
- + if(kbd->kbdmode == VC_UNICODE)
- + to_utf8(npadch & 0xffff);
- + else
- + put_queue(npadch & 0xff);
- + npadch = -1;
- + }
- +}
- +
- +/*
- + * Called after returning from RAW mode or when changing consoles -
- + * recompute k_down[] and shift_state from key_down[]
- + * Maybe called when keymap is undefined so that shift key release is seen
- + */
- +void compute_shiftstate(void)
- +{
- + int i, j, k, sym, val;
- +
- + shift_state = 0;
- + for (i = 0; i < SIZE(k_down); i++)
- + k_down[i] = 0;
- +
- + for (i = 0; i < SIZE(key_down); i++)
- + if (key_down[i]) { /* skip this short word if zero */
- + k = i * BITS_PER_SHORT;
- + for (j = 0; j < BITS_PER_SHORT; j++, k++)
- + if (test_bit (k, key_down)) {
- + sym = U(plain_map[k]);
- + if (KTYP(sym) == KT_SHIFT) {
- + val = KVAL (sym);
- + if (val == KVAL(K_CAPSSHIFT))
- + val = KVAL(K_SHIFT);
- + k_down[val] ++;
- + shift_state |= (1 << val);
- + }
- + }
- + }
- +}
- +
- +static void do_meta(unsigned char value, char up_flag)
- +{
- + if(up_flag)
- + return;
- +
- + if(vc_kbd_mode(kbd, VC_META)) {
- + put_queue ('\033');
- + put_queue (value);
- + } else
- + put_queue (value | 0x80);
- +}
- +
- +static void do_ascii(unsigned char value, char up_flag)
- +{
- + int base;
- +
- + if(up_flag)
- + return;
- +
- + if(value < 10)
- + base = 10;
- + else {
- + value -= 10;
- + base = 16;
- + }
- +
- + if(npadch == -1)
- + npadch = value;
- + else
- + npadch = npadch * base + value;
- +}
- +
- +static void do_lock (unsigned char value, char up_flag)
- +{
- + if (up_flag || rep)
- + return;
- + chg_vc_kbd_lock (kbd, value);
- +}
- +
- +static void do_slock (unsigned char value, char up_flag)
- +{
- + if (up_flag || rep)
- + return;
- + chg_vc_kbd_slock (kbd, value);
- +}
- +
- +/* --------------------------------------------------------------------------------------- *
- + * Led driver *
- + * --------------------------------------------------------------------------------------- */
- +
- +/*
- + * The leds display either
- + * (i) The status of NumLock, CapsLock and ScrollLock.
- + * (ii) Whatever pattern of lights people wait to show using KDSETLED.
- + * (iii) Specified bits of specified words in kernel memory.
- + */
- +
- +static unsigned char ledioctl;
- +
- +unsigned char getledstate(void)
- +{
- + return ledstate;
- +}
- +
- +void setledstate(struct kbd_struct *kbd, unsigned int led)
- +{
- + if(!(led & ~7)) {
- + ledioctl = led;
- + kbd->ledmode = LED_SHOW_IOCTL;
- + } else
- + kbd->ledmode = LED_SHOW_FLAGS;
- + set_leds();
- +}
- +
- +static struct ledptr
- +{
- + unsigned int *addr;
- + unsigned int mask;
- + unsigned char valid:1;
- +} ledptrs[3];
- +
- +void register_leds(int console,unsigned int led,unsigned int *addr,unsigned int mask)
- +{
- + struct kbd_struct *kbd = kbd_table + console;
- +
- + if(led < 3) {
- + ledptrs[led].addr = addr;
- + ledptrs[led].mask = mask;
- + ledptrs[led].valid= 1;
- + kbd->ledmode = LED_SHOW_MEM;
- + } else
- + kbd->ledmode = LED_SHOW_FLAGS;
- +}
- +
- +static inline unsigned char getleds(void)
- +{
- + struct kbd_struct *kbd = kbd_table + fg_console;
- + unsigned char leds;
- +
- + if(kbd->ledmode == LED_SHOW_IOCTL)
- + return ledioctl;
- +
- + leds = kbd->ledflagstate;
- + if (kbd->ledmode == LED_SHOW_MEM) {
- + if (ledptrs[0].valid) {
- + if (*ledptrs[0].addr & ledptrs[0].mask)
- + leds |= 1;
- + else
- + leds &= ~1;
- + }
- + if (ledptrs[1].valid) {
- + if (*ledptrs[1].addr & ledptrs[1].mask)
- + leds |= 2;
- + else
- + leds &= ~2;
- + }
- + if (ledptrs[2].valid) {
- + if(*ledptrs[2].addr & ledptrs[2].mask)
- + leds |= 4;
- + else
- + leds &= ~4;
- + }
- + }
- + return leds;
- +}
- +
- +/*
- + * This routine is the bottom half of the keyboard interrupt
- + * routine, and runs with all interrupts enabled. It does
- + * console changing, led setting, and copy_to_cooked, which can
- + * take a reasonably long time.
- + *
- + * Aside from timing (which isn't really that important for
- + * keyboard interrupts as they happen often), using the software
- + * interrupt routines for this thing allows us to easily mask
- + * this when we don't want any of the above to happen. Not yet
- + * used, but this allows for easy and efficient race-condition
- + * prevention later on.
- + */
- +static void kbd_bh(void *unused)
- +{
- + unsigned char leds;
- +
- + tty = ttytab[fg_console];
- + kbd = kbd_table + fg_console;
- +
- + if (rep && kbd_repeatkey != -1) {
- + /*
- + * This prevents the kbd_key routine from being called
- + * twice, once by this BH, and once by the interrupt
- + * routine. Maybe we should put the key press in a
- + * buffer or variable, and then call the BH...
- + */
- + disable_irq (IRQ_KEYBOARDRX);
- + kbd_key (kbd_repeatkey, kbd_repeatrate);
- + enable_irq (IRQ_KEYBOARDRX);
- + rep = 0;
- + }
- +
- + if(want_console >= 0) {
- + if(want_console != fg_console) {
- + change_console(want_console);
- + /* we only changed when the console had already
- + * been allocated - a new console is not created
- + * in an interrupt routine.
- + */
- + }
- + want_console = -1;
- + }
- +
- + leds = getleds ();
- + if (leds != ledstate) {
- + unsigned long flags;
- + ledstate = leds;
- + leds = ((leds & (1<<VC_SCROLLOCK))?4:0) | ((leds & (1<<VC_NUMLOCK))?2:0) |
- + ((leds & (1<<VC_CAPSLOCK))?1:0);
- +
- + save_flags(flags);
- + cli();
- + intr_count -= 1;
- +
- + kbd_sendval(leds);
- +
- + intr_count += 1;
- + restore_flags(flags);
- + }
- + poke_blanked_console();
- +}
- +
- +int kbd_init (void)
- +{
- + int i;
- + struct kbd_struct kbd0;
- + extern struct tty_driver console_driver;
- + unsigned long flags;
- +
- + kbd0.ledflagstate = kbd0.default_ledflagstate = KBD_DEFLEDS;
- + kbd0.ledmode = LED_SHOW_FLAGS;
- + kbd0.lockstate = KBD_DEFLOCK;
- + kbd0.slockstate = 0;
- + kbd0.modeflags = KBD_DEFMODE;
- + kbd0.kbdmode = VC_XLATE;
- +
- + for (i = 0 ; i < MAX_NR_CONSOLES ; i++)
- + kbd_table[i] = kbd0;
- +
- + ttytab = console_driver.table;
- +
- + bh_base[KEYBOARD_BH].routine = kbd_bh;
- +
- + save_flags (flags);
- + cli ();
- + if (request_irq (IRQ_KEYBOARDRX, kbd_rx, 0, "keyboard")!=0)
- + panic("Could not allocate keyboard receive IRQ!");
- + if (request_irq (IRQ_KEYBOARDTX, kbd_tx, 0, "Keyboard")!=0)
- + panic("Could not allocate keyboard transmit IRQ!");
- + disable_irq (IRQ_KEYBOARDTX);
- + restore_flags(flags);
- +
- + kbd_sendval(HRST);
- + mark_bh (KEYBOARD_BH);
- + enable_bh (KEYBOARD_BH);
- +
- + printk("Keyboard driver installed (%s) v%d.%02d.\n", "English", VERSION/100, VERSION%100);
- + return 0;
- +}
- +
- diff -urNwbB linux/arch/arm/drivers/char/mem.c linux.arm/arch/arm/drivers/char/mem.c
- --- linux/arch/arm/drivers/char/mem.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/mem.c Sun Mar 3 12:45:19 1996
- @@ -0,0 +1,383 @@
- +/*
- + * linux/arch/arm/drivers/char/mem.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + *
- + * Modifications for ARM Copyright (C) 1995, 1996 Russell King
- + */
- +
- +#include <linux/config.h>
- +#include <linux/types.h>
- +#include <linux/errno.h>
- +#include <linux/sched.h>
- +#include <linux/kernel.h>
- +#include <linux/major.h>
- +#include <linux/tty.h>
- +#include <linux/mouse.h>
- +#include <linux/tpqic02.h>
- +#include <linux/malloc.h>
- +#include <linux/mman.h>
- +#include <linux/mm.h>
- +#include <linux/random.h>
- +
- +#include <asm/segment.h>
- +#include <asm/io.h>
- +#include <asm/pgtable.h>
- +
- +#ifdef CONFIG_SOUND
- +void soundcard_init(void);
- +#endif
- +
- +static int read_ram(struct inode * inode, struct file * file, char * buf, int count)
- +{
- + return -EIO;
- +}
- +
- +static int write_ram(struct inode * inode, struct file * file, const char * buf, int count)
- +{
- + return -EIO;
- +}
- +
- +static int read_mem(struct inode * inode, struct file * file, char * buf, int count)
- +{
- + unsigned long p = file->f_pos;
- +
- + p += PAGE_OFFSET;
- + if (count < 0)
- + return -EINVAL;
- + if (MAP_NR(p) >= MAP_NR(high_memory))
- + return 0;
- + if (count > high_memory - p)
- + count = high_memory - p;
- + memcpy_tofs(buf,(void *) p,count);
- + file->f_pos += count;
- + return count;
- +}
- +
- +static int write_mem(struct inode * inode, struct file * file, const char * buf, int count)
- +{
- + unsigned long p = file->f_pos;
- +
- + p += PAGE_OFFSET;
- + if (count < 0)
- + return -EINVAL;
- + if (MAP_NR(p) >= MAP_NR(high_memory))
- + return 0;
- + if (count > high_memory - p)
- + count = high_memory - p;
- + memcpy_fromfs((void *) p,buf,count);
- + file->f_pos += count;
- + return count;
- +}
- +
- +static int mmap_mem(struct inode * inode, struct file * file, struct vm_area_struct * vma)
- +{
- + if (vma->vm_offset & ~PAGE_MASK)
- + return -ENXIO;
- +
- + if (remap_page_range(vma->vm_start, vma->vm_offset, vma->vm_end - vma->vm_start, vma->vm_page_prot))
- + return -EAGAIN;
- + vma->vm_inode = inode;
- + inode->i_count++;
- + return 0;
- +}
- +
- +static int read_kmem(struct inode *inode, struct file *file, char *buf, int count)
- +{
- + int read1, read2;
- +
- + read1 = read_mem(inode, file, buf, count);
- + if (read1 < 0)
- + return read1;
- + read2 = vread(buf + read1, (char *) ((unsigned long) file->f_pos), count - read1);
- + if (read2 < 0)
- + return read2;
- + file->f_pos += read2;
- + return read1 + read2;
- +}
- +
- +static int read_port(struct inode * inode, struct file * file,char * buf, int count)
- +{
- + unsigned int i = file->f_pos;
- + char * tmp = buf;
- +
- + while (count-- > 0 && i < 65536) {
- + put_user(inb(i),tmp);
- + i++;
- + tmp++;
- + }
- + file->f_pos = i;
- + return tmp-buf;
- +}
- +
- +static int write_port(struct inode * inode, struct file * file, const char * buf, int count)
- +{
- + unsigned int i = file->f_pos;
- + const char * tmp = buf;
- +
- + while (count-- > 0 && i < 65536) {
- + outb(get_user(tmp),i);
- + i++;
- + tmp++;
- + }
- + file->f_pos = i;
- + return tmp-buf;
- +}
- +
- +static int read_null(struct inode * node, struct file * file, char * buf, int count)
- +{
- + return 0;
- +}
- +
- +static int write_null(struct inode * inode, struct file * file, const char * buf, int count)
- +{
- + return count;
- +}
- +
- +static int read_zero(struct inode * node, struct file * file, char * buf, int count)
- +{
- + int left;
- +
- + for (left = count; left > 0; left--) {
- + put_user(0,buf);
- + buf++;
- + }
- + return count;
- +}
- +
- +static int mmap_zero(struct inode * inode, struct file * file, struct vm_area_struct * vma)
- +{
- + if (vma->vm_flags & VM_SHARED)
- + return -EINVAL;
- + if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot))
- + return -EAGAIN;
- + return 0;
- +}
- +
- +static int read_full(struct inode * node, struct file * file, char * buf,int count)
- +{
- + return count;
- +}
- +
- +static int write_full(struct inode * inode, struct file * file, const char * buf, int count)
- +{
- + return -ENOSPC;
- +}
- +
- +/*
- + * Special lseek() function for /dev/null and /dev/zero. Most notably, you can fopen()
- + * both devices with "a" now. This was previously impossible. SRB.
- + */
- +
- +static int null_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
- +{
- + return file->f_pos=0;
- +}
- +/*
- + * The memory devices use the full 32/64 bits of the offset, and so we cannot
- + * check against negative addresses: they are ok. The return value is weird,
- + * though, in that case (0).
- + *
- + * also note that seeking relative to the "end of file" isn't supported:
- + * it has no meaning, so it returns -EINVAL.
- + */
- +static int memory_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
- +{
- + switch (orig) {
- + case 0:
- + file->f_pos = offset;
- + return file->f_pos;
- + case 1:
- + file->f_pos += offset;
- + return file->f_pos;
- + default:
- + return -EINVAL;
- + }
- + if (file->f_pos < 0)
- + return 0;
- + return file->f_pos;
- +}
- +
- +#define write_kmem write_mem
- +#define mmap_kmem mmap_mem
- +#define zero_lseek null_lseek
- +#define write_zero write_null
- +
- +static struct file_operations ram_fops = {
- + memory_lseek,
- + read_ram,
- + write_ram,
- + NULL, /* ram_readdir */
- + NULL, /* ram_select */
- + NULL, /* ram_ioctl */
- + NULL, /* ram_mmap */
- + NULL, /* no special open code */
- + NULL, /* no special release code */
- + NULL /* fsync */
- +};
- +
- +static struct file_operations mem_fops = {
- + memory_lseek,
- + read_mem,
- + write_mem,
- + NULL, /* mem_readdir */
- + NULL, /* mem_select */
- + NULL, /* mem_ioctl */
- + mmap_mem,
- + NULL, /* no special open code */
- + NULL, /* no special release code */
- + NULL /* fsync */
- +};
- +
- +static struct file_operations kmem_fops = {
- + memory_lseek,
- + read_kmem,
- + write_kmem,
- + NULL, /* kmem_readdir */
- + NULL, /* kmem_select */
- + NULL, /* kmem_ioctl */
- + mmap_kmem,
- + NULL, /* no special open code */
- + NULL, /* no special release code */
- + NULL /* fsync */
- +};
- +
- +static struct file_operations null_fops = {
- + null_lseek,
- + read_null,
- + write_null,
- + NULL, /* null_readdir */
- + NULL, /* null_select */
- + NULL, /* null_ioctl */
- + NULL, /* null_mmap */
- + NULL, /* no special open code */
- + NULL, /* no special release code */
- + NULL /* fsync */
- +};
- +
- +static struct file_operations port_fops = {
- + memory_lseek,
- + read_port,
- + write_port,
- + NULL, /* port_readdir */
- + NULL, /* port_select */
- + NULL, /* port_ioctl */
- + NULL, /* port_mmap */
- + NULL, /* no special open code */
- + NULL, /* no special release code */
- + NULL /* fsync */
- +};
- +
- +static struct file_operations zero_fops = {
- + zero_lseek,
- + read_zero,
- + write_zero,
- + NULL, /* zero_readdir */
- + NULL, /* zero_select */
- + NULL, /* zero_ioctl */
- + mmap_zero,
- + NULL, /* no special open code */
- + NULL /* no special release code */
- +};
- +
- +static struct file_operations full_fops = {
- + memory_lseek,
- + read_full,
- + write_full,
- + NULL, /* full_readdir */
- + NULL, /* full_select */
- + NULL, /* full_ioctl */
- + NULL, /* full_mmap */
- + NULL, /* no special open code */
- + NULL /* no special release code */
- +};
- +
- +static struct file_operations random_fops = {
- + memory_lseek,
- + read_random,
- + write_random,
- + NULL, /* random_readdir */
- + NULL, /* random_select */
- + random_ioctl,
- + NULL, /* random_mmap */
- + NULL, /* no special open code */
- + NULL /* no special release code */
- +};
- +
- +static struct file_operations urandom_fops = {
- + memory_lseek,
- + read_random_unlimited,
- + write_random,
- + NULL, /* urandom_readdir */
- + NULL, /* urandom_select */
- + random_ioctl,
- + NULL, /* urandom_mmap */
- + NULL, /* no special open code */
- + NULL /* no special release code */
- +};
- +
- +static int memory_open(struct inode * inode, struct file * filp)
- +{
- + switch (MINOR(inode->i_rdev)) {
- + case 0:
- + filp->f_op = &ram_fops;
- + break;
- + case 1:
- + filp->f_op = &mem_fops;
- + break;
- + case 2:
- + filp->f_op = &kmem_fops;
- + break;
- + case 3:
- + filp->f_op = &null_fops;
- + break;
- + case 4:
- + filp->f_op = &port_fops;
- + break;
- + case 5:
- + filp->f_op = &zero_fops;
- + break;
- + case 7:
- + filp->f_op = &full_fops;
- + break;
- + case 8:
- + filp->f_op = &random_fops;
- + break;
- + case 9:
- + filp->f_op = &urandom_fops;
- + break;
- + default:
- + return -ENODEV;
- + }
- + if (filp->f_op && filp->f_op->open)
- + return filp->f_op->open(inode,filp);
- + return 0;
- +}
- +
- +static struct file_operations memory_fops = {
- + NULL, /* lseek */
- + NULL, /* read */
- + NULL, /* write */
- + NULL, /* readdir */
- + NULL, /* select */
- + NULL, /* ioctl */
- + NULL, /* mmap */
- + memory_open, /* just a selector for the real open */
- + NULL, /* release */
- + NULL /* fsync */
- +};
- +
- +int chr_dev_init(void)
- +{
- + if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
- + printk("unable to get major %d for memory devs\n", MEM_MAJOR);
- + rand_initialize();
- + tty_init();
- +#ifdef CONFIG_PRINTER
- + lp_init();
- +#endif
- +#ifdef CONFIG_MOUSE
- + mouse_init();
- +#endif
- + return 0;
- +}
- diff -urNwbB linux/arch/arm/drivers/char/mouse.c linux.arm/arch/arm/drivers/char/mouse.c
- --- linux/arch/arm/drivers/char/mouse.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/mouse.c Sat Feb 24 09:49:56 1996
- @@ -0,0 +1,116 @@
- +/*
- + * linux/arch/arm/drivers/char/mouse.c
- + *
- + * Generic mouse open routine by Johan Myreen
- + *
- + * Based on code from Linus
- + *
- + * Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's
- + * changes incorporated into 0.97pl4
- + * by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)
- + * See busmouse.c for particulars.
- + *
- + * Made things a lot mode modular - easy to compile in just one or two
- + * of the mouse drivers, as they are now completely independent. Linus.
- + *
- + * Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
- + */
- +
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/version.h>
- +#else
- +#define MOD_INC_USE_COUNT
- +#define MOD_DEC_USE_COUNT
- +#endif
- +
- +#include <linux/fs.h>
- +#include <linux/errno.h>
- +#include <linux/mouse.h>
- +#include <linux/config.h>
- +#include <linux/kernel.h>
- +#include <linux/major.h>
- +#include <linux/malloc.h>
- +
- +/*
- + * Head entry for the doubly linked mouse list
- + */
- +static struct mouse mouse_list = { 0, "head", NULL, &mouse_list, &mouse_list };
- +
- +extern int arch_mouse_init(void);
- +extern struct file_operations arch_mouse_fops;
- +
- +static int mouse_open(struct inode * inode, struct file * file)
- +{
- + int minor = MINOR(inode->i_rdev);
- + struct mouse *c = mouse_list.next;
- + file->f_op = NULL;
- +
- + while (c != &mouse_list) {
- + if (c->minor == minor) {
- + file->f_op = c->fops;
- + break;
- + }
- + c = c->next;
- + }
- +
- + if (file->f_op == NULL)
- + return -ENODEV;
- +
- + return file->f_op->open(inode,file);
- +}
- +
- +static struct file_operations mouse_fops = {
- + NULL, /* seek */
- + NULL, /* read */
- + NULL, /* write */
- + NULL, /* readdir */
- + NULL, /* select */
- + NULL, /* ioctl */
- + NULL, /* mmap */
- + mouse_open,
- + NULL /* release */
- +};
- +
- +int mouse_register(struct mouse * mouse)
- +{
- + if (mouse->next || mouse->prev)
- + return -EBUSY;
- + MOD_INC_USE_COUNT;
- + mouse->next = &mouse_list;
- + mouse->prev = mouse_list.prev;
- + mouse->prev->next = mouse;
- + mouse->next->prev = mouse;
- + return 0;
- +}
- +
- +int mouse_deregister(struct mouse * mouse)
- +{
- + if (!mouse->next || !mouse->prev)
- + return -EINVAL;
- + MOD_DEC_USE_COUNT;
- + mouse->prev->next = mouse->next;
- + mouse->next->prev = mouse->prev;
- + mouse->next = NULL;
- + mouse->prev = NULL;
- + return 0;
- +}
- +
- +#ifdef MODULE
- +char kernel_version[] = UTS_RELEASE;
- +#define mouse_init init_module
- +#endif
- +
- +int mouse_init(void)
- +{
- +#ifdef CONFIG_KBDMOUSE
- + arch_mouse_init();
- +#endif
- + if (register_chrdev(MOUSE_MAJOR, "mouse", &mouse_fops))
- + {
- + printk("unable to get major %d for mouse devices\n",
- + MOUSE_MAJOR);
- + return -EIO;
- + }
- + return 0;
- +}
- diff -urNwbB linux/arch/arm/drivers/char/n_tty.c linux.arm/arch/arm/drivers/char/n_tty.c
- --- linux/arch/arm/drivers/char/n_tty.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/n_tty.c Sun Mar 3 12:46:05 1996
- @@ -0,0 +1,1019 @@
- +/*
- + * n_tty.c --- implements the N_TTY line discipline.
- + *
- + * This code used to be in tty_io.c, but things are getting hairy
- + * enough that it made sense to split things off. (The N_TTY
- + * processing has changed so much that it's hardly recognizable,
- + * anyway...)
- + *
- + * Note that the open routine for N_TTY is guaranteed never to return
- + * an error. This is because Linux will fall back to setting a line
- + * to N_TTY if it can not switch to any other line discipline.
- + *
- + * Written by Theodore Ts'o, Copyright 1994.
- + *
- + * This file also contains code originally written by Linus Torvalds,
- + * Copyright 1991, 1992, 1993, and by Julian Cowley, Copyright 1994.
- + *
- + * This file may be redistributed under the terms of the GNU Public
- + * License.
- + *
- + * Altered for ARM to reduce memory usage.
- + */
- +
- +#include <linux/types.h>
- +#include <linux/major.h>
- +#include <linux/errno.h>
- +#include <linux/signal.h>
- +#include <linux/fcntl.h>
- +#include <linux/sched.h>
- +#include <linux/interrupt.h>
- +#include <linux/tty.h>
- +#include <linux/timer.h>
- +#include <linux/ctype.h>
- +#include <linux/kd.h>
- +#include <linux/mm.h>
- +#include <linux/string.h>
- +#include <linux/malloc.h>
- +
- +#include <asm/segment.h>
- +#include <asm/system.h>
- +#include <asm/bitops.h>
- +
- +#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
- +
- +#ifndef MIN
- +#define MIN(a,b) ((a) < (b) ? (a) : (b))
- +#endif
- +
- +/* number of characters left in xmit buffer before select has we have room */
- +#define WAKEUP_CHARS 256
- +
- +/*
- + * This defines the low- and high-watermarks for throttling and
- + * unthrottling the TTY driver. These watermarks are used for
- + * controlling the space in the read buffer.
- + */
- +#define TTY_THRESHOLD_THROTTLE (N_TTY_BUF_SIZE - 128)
- +#define TTY_THRESHOLD_UNTHROTTLE 128
- +
- +static inline void put_tty_queue(unsigned char c, struct tty_struct *tty)
- +{
- + if (tty->read_cnt < N_TTY_BUF_SIZE) {
- + tty->read_buf[tty->read_head] = c;
- + tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1);
- + tty->read_cnt++;
- + }
- +}
- +
- +/*
- + * Flush the input buffer
- + */
- +void n_tty_flush_buffer(struct tty_struct * tty)
- +{
- + tty->read_head = tty->read_tail = tty->read_cnt = 0;
- + tty->canon_head = tty->canon_data = tty->erasing = 0;
- + memset(&tty->read_flags, 0, sizeof tty->read_flags);
- +
- + if (!tty->link)
- + return;
- +
- + if (tty->driver.unthrottle)
- + (tty->driver.unthrottle)(tty);
- + if (tty->link->packet) {
- + tty->ctrl_status |= TIOCPKT_FLUSHREAD;
- + wake_up_interruptible(&tty->link->read_wait);
- + }
- +}
- +
- +/*
- + * Return number of characters buffered to be delivered to user
- + */
- +int n_tty_chars_in_buffer(struct tty_struct *tty)
- +{
- + return tty->read_cnt;
- +}
- +
- +/*
- + * Perform OPOST processing. Returns -1 when the output device is
- + * full and the character must be retried.
- + */
- +static int opost(unsigned char c, struct tty_struct *tty)
- +{
- + int space, spaces;
- +
- + space = tty->driver.write_room(tty);
- + if (!space)
- + return -1;
- +
- + if (O_OPOST(tty)) {
- + switch (c) {
- + case '\n':
- + if (O_ONLRET(tty))
- + tty->column = 0;
- + if (O_ONLCR(tty)) {
- + if (space < 2)
- + return -1;
- + tty->driver.put_char(tty, '\r');
- + tty->column = 0;
- + }
- + tty->canon_column = tty->column;
- + break;
- + case '\r':
- + if (O_ONOCR(tty) && tty->column == 0)
- + return 0;
- + if (O_OCRNL(tty)) {
- + c = '\n';
- + if (O_ONLRET(tty))
- + tty->canon_column = tty->column = 0;
- + break;
- + }
- + tty->canon_column = tty->column = 0;
- + break;
- + case '\t':
- + spaces = 8 - (tty->column & 7);
- + if (O_TABDLY(tty) == XTABS) {
- + if (space < spaces)
- + return -1;
- + tty->column += spaces;
- + tty->driver.write(tty, 0, " ", spaces);
- + return 0;
- + }
- + tty->column += spaces;
- + break;
- + case '\b':
- + if (tty->column > 0)
- + tty->column--;
- + break;
- + default:
- + if (O_OLCUC(tty))
- + c = toupper(c);
- + if (!iscntrl(c))
- + tty->column++;
- + break;
- + }
- + }
- + tty->driver.put_char(tty, c);
- + return 0;
- +}
- +
- +static inline void put_char(unsigned char c, struct tty_struct *tty)
- +{
- + tty->driver.put_char(tty, c);
- +}
- +
- +/* Must be called only when L_ECHO(tty) is true. */
- +
- +static void echo_char(unsigned char c, struct tty_struct *tty)
- +{
- + if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') {
- + put_char('^', tty);
- + put_char(c ^ 0100, tty);
- + tty->column += 2;
- + } else
- + opost(c, tty);
- +}
- +
- +static inline void finish_erasing(struct tty_struct *tty)
- +{
- + if (tty->erasing) {
- + put_char('/', tty);
- + tty->column += 2;
- + tty->erasing = 0;
- + }
- +}
- +
- +static void eraser(unsigned char c, struct tty_struct *tty)
- +{
- + enum { ERASE, WERASE, KILL } kill_type;
- + int head, seen_alnums;
- +
- + if (tty->read_head == tty->canon_head) {
- + /* opost('\a', tty); */ /* what do you think? */
- + return;
- + }
- + if (c == ERASE_CHAR(tty))
- + kill_type = ERASE;
- + else if (c == WERASE_CHAR(tty))
- + kill_type = WERASE;
- + else {
- + if (!L_ECHO(tty)) {
- + tty->read_cnt -= ((tty->read_head - tty->canon_head) &
- + (N_TTY_BUF_SIZE - 1));
- + tty->read_head = tty->canon_head;
- + return;
- + }
- + if (!L_ECHOK(tty) || !L_ECHOKE(tty)) {
- + tty->read_cnt -= ((tty->read_head - tty->canon_head) &
- + (N_TTY_BUF_SIZE - 1));
- + tty->read_head = tty->canon_head;
- + finish_erasing(tty);
- + echo_char(KILL_CHAR(tty), tty);
- + /* Add a newline if ECHOK is on and ECHOKE is off. */
- + if (L_ECHOK(tty))
- + opost('\n', tty);
- + return;
- + }
- + kill_type = KILL;
- + }
- +
- + seen_alnums = 0;
- + while (tty->read_head != tty->canon_head) {
- + head = (tty->read_head - 1) & (N_TTY_BUF_SIZE-1);
- + c = tty->read_buf[head];
- + if (kill_type == WERASE) {
- + /* Equivalent to BSD's ALTWERASE. */
- + if (isalnum(c) || c == '_')
- + seen_alnums++;
- + else if (seen_alnums)
- + break;
- + }
- + tty->read_head = head;
- + tty->read_cnt--;
- + if (L_ECHO(tty)) {
- + if (L_ECHOPRT(tty)) {
- + if (!tty->erasing) {
- + put_char('\\', tty);
- + tty->column++;
- + tty->erasing = 1;
- + }
- + echo_char(c, tty);
- + } else if (!L_ECHOE(tty)) {
- + echo_char(ERASE_CHAR(tty), tty);
- + } else if (c == '\t') {
- + unsigned int col = tty->canon_column;
- + unsigned long tail = tty->canon_head;
- +
- + /* Find the column of the last char. */
- + while (tail != tty->read_head) {
- + c = tty->read_buf[tail];
- + if (c == '\t')
- + col = (col | 7) + 1;
- + else if (iscntrl(c)) {
- + if (L_ECHOCTL(tty))
- + col += 2;
- + } else
- + col++;
- + tail = (tail+1) & (N_TTY_BUF_SIZE-1);
- + }
- +
- + /* Now backup to that column. */
- + while (tty->column > col) {
- + /* Can't use opost here. */
- + put_char('\b', tty);
- + tty->column--;
- + }
- + } else {
- + if (iscntrl(c) && L_ECHOCTL(tty)) {
- + put_char('\b', tty);
- + put_char(' ', tty);
- + put_char('\b', tty);
- + tty->column--;
- + }
- + if (!iscntrl(c) || L_ECHOCTL(tty)) {
- + put_char('\b', tty);
- + put_char(' ', tty);
- + put_char('\b', tty);
- + tty->column--;
- + }
- + }
- + }
- + if (kill_type == ERASE)
- + break;
- + }
- + if (tty->read_head == tty->canon_head)
- + finish_erasing(tty);
- +}
- +
- +static void isig(int sig, struct tty_struct *tty)
- +{
- + if (tty->pgrp > 0)
- + kill_pg(tty->pgrp, sig, 1);
- + if (!L_NOFLSH(tty)) {
- + n_tty_flush_buffer(tty);
- + if (tty->driver.flush_buffer)
- + tty->driver.flush_buffer(tty);
- + }
- +}
- +
- +static inline void n_tty_receive_break(struct tty_struct *tty)
- +{
- + if (I_IGNBRK(tty))
- + return;
- + if (I_BRKINT(tty)) {
- + isig(SIGINT, tty);
- + return;
- + }
- + if (I_PARMRK(tty)) {
- + put_tty_queue('\377', tty);
- + put_tty_queue('\0', tty);
- + }
- + put_tty_queue('\0', tty);
- + wake_up_interruptible(&tty->read_wait);
- +}
- +
- +static inline void n_tty_receive_overrun(struct tty_struct *tty)
- +{
- + char buf[64];
- +
- + tty->num_overrun++;
- + if (tty->overrun_time < (jiffies - HZ)) {
- + printk("%s: %d input overrun(s)\n", _tty_name(tty, buf),
- + tty->num_overrun);
- + tty->overrun_time = jiffies;
- + tty->num_overrun = 0;
- + }
- +}
- +
- +static inline void n_tty_receive_parity_error(struct tty_struct *tty,
- + unsigned char c)
- +{
- + if (I_IGNPAR(tty)) {
- + return;
- + }
- + if (I_PARMRK(tty)) {
- + put_tty_queue('\377', tty);
- + put_tty_queue('\0', tty);
- + put_tty_queue(c, tty);
- + } else
- + put_tty_queue('\0', tty);
- + wake_up_interruptible(&tty->read_wait);
- +}
- +
- +static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
- +{
- + if (tty->raw) {
- + put_tty_queue(c, tty);
- + return;
- + }
- +
- + if (tty->stopped && I_IXON(tty) && I_IXANY(tty)) {
- + start_tty(tty);
- + return;
- + }
- +
- + if (I_ISTRIP(tty))
- + c &= 0x7f;
- + if (I_IUCLC(tty) && L_IEXTEN(tty))
- + c=tolower(c);
- +
- + if (tty->closing) {
- + if (I_IXON(tty)) {
- + if (c == START_CHAR(tty))
- + start_tty(tty);
- + else if (c == STOP_CHAR(tty))
- + stop_tty(tty);
- + }
- + return;
- + }
- +
- + /*
- + * If the previous character was LNEXT, or we know that this
- + * character is not one of the characters that we'll have to
- + * handle specially, do shortcut processing to speed things
- + * up.
- + */
- + if (!test_bit(c, &tty->process_char_map) || tty->lnext) {
- + finish_erasing(tty);
- + tty->lnext = 0;
- + if (L_ECHO(tty)) {
- + if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
- + put_char('\a', tty); /* beep if no space */
- + return;
- + }
- + /* Record the column of first canon char. */
- + if (tty->canon_head == tty->read_head)
- + tty->canon_column = tty->column;
- + echo_char(c, tty);
- + }
- + if (I_PARMRK(tty) && c == (unsigned char) '\377')
- + put_tty_queue(c, tty);
- + put_tty_queue(c, tty);
- + return;
- + }
- +
- + if (c == '\r') {
- + if (I_IGNCR(tty))
- + return;
- + if (I_ICRNL(tty))
- + c = '\n';
- + } else if (c == '\n' && I_INLCR(tty))
- + c = '\r';
- + if (I_IXON(tty)) {
- + if (c == START_CHAR(tty)) {
- + start_tty(tty);
- + return;
- + }
- + if (c == STOP_CHAR(tty)) {
- + stop_tty(tty);
- + return;
- + }
- + }
- + if (L_ISIG(tty)) {
- + if (c == INTR_CHAR(tty)) {
- + isig(SIGINT, tty);
- + return;
- + }
- + if (c == QUIT_CHAR(tty)) {
- + isig(SIGQUIT, tty);
- + return;
- + }
- + if (c == SUSP_CHAR(tty)) {
- + if (!is_orphaned_pgrp(tty->pgrp))
- + isig(SIGTSTP, tty);
- + return;
- + }
- + }
- + if (L_ICANON(tty)) {
- + if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
- + (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
- + eraser(c, tty);
- + return;
- + }
- + if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
- + tty->lnext = 1;
- + if (L_ECHO(tty)) {
- + finish_erasing(tty);
- + if (L_ECHOCTL(tty)) {
- + put_char('^', tty);
- + put_char('\b', tty);
- + }
- + }
- + return;
- + }
- + if (c == REPRINT_CHAR(tty) && L_ECHO(tty) &&
- + L_IEXTEN(tty)) {
- + unsigned long tail = tty->canon_head;
- +
- + finish_erasing(tty);
- + echo_char(c, tty);
- + opost('\n', tty);
- + while (tail != tty->read_head) {
- + echo_char(tty->read_buf[tail], tty);
- + tail = (tail+1) & (N_TTY_BUF_SIZE-1);
- + }
- + return;
- + }
- + if (c == '\n') {
- + if (L_ECHO(tty) || L_ECHONL(tty)) {
- + if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
- + put_char('\a', tty);
- + return;
- + }
- + opost('\n', tty);
- + }
- + goto handle_newline;
- + }
- + if (c == EOF_CHAR(tty)) {
- + if (tty->canon_head != tty->read_head)
- + set_bit(TTY_PUSH, &tty->flags);
- + c = __DISABLED_CHAR;
- + goto handle_newline;
- + }
- + if ((c == EOL_CHAR(tty)) ||
- + (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) {
- + /*
- + * XXX are EOL_CHAR and EOL2_CHAR echoed?!?
- + */
- + if (L_ECHO(tty)) {
- + if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
- + put_char('\a', tty);
- + return;
- + }
- + /* Record the column of first canon char. */
- + if (tty->canon_head == tty->read_head)
- + tty->canon_column = tty->column;
- + echo_char(c, tty);
- + }
- + /*
- + * XXX does PARMRK doubling happen for
- + * EOL_CHAR and EOL2_CHAR?
- + */
- + if (I_PARMRK(tty) && c == (unsigned char) '\377')
- + put_tty_queue(c, tty);
- +
- + handle_newline:
- + set_bit(tty->read_head, &tty->read_flags);
- + put_tty_queue(c, tty);
- + tty->canon_head = tty->read_head;
- + tty->canon_data++;
- + if (tty->fasync)
- + kill_fasync(tty->fasync, SIGIO);
- + if (tty->read_wait)
- + wake_up_interruptible(&tty->read_wait);
- + return;
- + }
- + }
- +
- + finish_erasing(tty);
- + if (L_ECHO(tty)) {
- + if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
- + put_char('\a', tty); /* beep if no space */
- + return;
- + }
- + if (c == '\n')
- + opost('\n', tty);
- + else {
- + /* Record the column of first canon char. */
- + if (tty->canon_head == tty->read_head)
- + tty->canon_column = tty->column;
- + echo_char(c, tty);
- + }
- + }
- +
- + if (I_PARMRK(tty) && c == (unsigned char) '\377')
- + put_tty_queue(c, tty);
- +
- + put_tty_queue(c, tty);
- +}
- +
- +static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
- + char *fp, int count)
- +{
- + const unsigned char *p;
- + char *f, flags = 0;
- + int i;
- +
- + if (!tty->read_buf)
- + return;
- +
- + if (tty->real_raw) {
- + i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,
- + N_TTY_BUF_SIZE - tty->read_head));
- + memcpy(tty->read_buf + tty->read_head, cp, i);
- + tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
- + tty->read_cnt += i;
- + cp += i;
- + count -= i;
- +
- + i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,
- + N_TTY_BUF_SIZE - tty->read_head));
- + memcpy(tty->read_buf + tty->read_head, cp, i);
- + tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
- + tty->read_cnt += i;
- + } else {
- + for (i=count, p = cp, f = fp; i; i--, p++) {
- + if (f)
- + flags = *f++;
- + switch (flags) {
- + case TTY_NORMAL:
- + n_tty_receive_char(tty, *p);
- + break;
- + case TTY_BREAK:
- + n_tty_receive_break(tty);
- + break;
- + case TTY_PARITY:
- + case TTY_FRAME:
- + n_tty_receive_parity_error(tty, *p);
- + break;
- + case TTY_OVERRUN:
- + n_tty_receive_overrun(tty);
- + break;
- + default:
- + printk("%s: unknown flag %d\n", tty_name(tty),
- + flags);
- + break;
- + }
- + }
- + if (tty->driver.flush_chars)
- + tty->driver.flush_chars(tty);
- + }
- +
- + if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
- + if (tty->fasync)
- + kill_fasync(tty->fasync, SIGIO);
- + if (tty->read_wait)
- + wake_up_interruptible(&tty->read_wait);
- + }
- +
- + if ((tty->read_cnt >= TTY_THRESHOLD_THROTTLE) &&
- + tty->driver.throttle &&
- + !set_bit(TTY_THROTTLED, &tty->flags))
- + tty->driver.throttle(tty);
- +}
- +
- +static int n_tty_receive_room(struct tty_struct *tty)
- +{
- + int left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
- +
- + /*
- + * If we are doing input canonicalization, and there are no
- + * pending newlines, let characters through without limit, so
- + * that erase characters will be handled. Other excess
- + * characters will be beeped.
- + */
- + if (tty->icanon && !tty->canon_data)
- + return N_TTY_BUF_SIZE;
- +
- + if (left > 0)
- + return left;
- + return 0;
- +}
- +
- +int is_ignored(int sig)
- +{
- + return ((current->blocked & (1<<(sig-1))) ||
- + (current->sig->action[sig-1].sa_handler == SIG_IGN));
- +}
- +
- +static void n_tty_set_termios(struct tty_struct *tty, struct termios * old)
- +{
- + if (!tty)
- + return;
- +
- + tty->icanon = (L_ICANON(tty) != 0);
- + if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) ||
- + I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
- + I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
- + I_PARMRK(tty)) {
- + cli();
- + memset(tty->process_char_map, 0, 256/8);
- +
- + if (I_IGNCR(tty) || I_ICRNL(tty))
- + set_bit('\r', &tty->process_char_map);
- + if (I_INLCR(tty))
- + set_bit('\n', &tty->process_char_map);
- +
- + if (L_ICANON(tty)) {
- + set_bit(ERASE_CHAR(tty), &tty->process_char_map);
- + set_bit(KILL_CHAR(tty), &tty->process_char_map);
- + set_bit(EOF_CHAR(tty), &tty->process_char_map);
- + set_bit('\n', &tty->process_char_map);
- + set_bit(EOL_CHAR(tty), &tty->process_char_map);
- + if (L_IEXTEN(tty)) {
- + set_bit(WERASE_CHAR(tty),
- + &tty->process_char_map);
- + set_bit(LNEXT_CHAR(tty),
- + &tty->process_char_map);
- + set_bit(EOL2_CHAR(tty),
- + &tty->process_char_map);
- + if (L_ECHO(tty))
- + set_bit(REPRINT_CHAR(tty),
- + &tty->process_char_map);
- + }
- + }
- + if (I_IXON(tty)) {
- + set_bit(START_CHAR(tty), &tty->process_char_map);
- + set_bit(STOP_CHAR(tty), &tty->process_char_map);
- + }
- + if (L_ISIG(tty)) {
- + set_bit(INTR_CHAR(tty), &tty->process_char_map);
- + set_bit(QUIT_CHAR(tty), &tty->process_char_map);
- + set_bit(SUSP_CHAR(tty), &tty->process_char_map);
- + }
- + clear_bit(__DISABLED_CHAR, &tty->process_char_map);
- + sti();
- + tty->raw = 0;
- + tty->real_raw = 0;
- + } else {
- + tty->raw = 1;
- + if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) &&
- + (I_IGNPAR(tty) || !I_INPCK(tty)) &&
- + (tty->driver.flags & TTY_DRIVER_REAL_RAW))
- + tty->real_raw = 1;
- + else
- + tty->real_raw = 0;
- + }
- +}
- +
- +static void n_tty_close(struct tty_struct *tty)
- +{
- + n_tty_flush_buffer(tty);
- + if (tty->read_buf) {
- + kfree_s (tty->read_buf, N_TTY_BUF_SIZE);
- + tty->read_buf = 0;
- + }
- +}
- +
- +static int n_tty_open(struct tty_struct *tty)
- +{
- + if (!tty)
- + return -EINVAL;
- +
- + if (!tty->read_buf) {
- + tty->read_buf = (unsigned char *)
- + kmalloc (N_TTY_BUF_SIZE, intr_count ? GFP_ATOMIC : GFP_KERNEL);
- + if (!tty->read_buf)
- + return -ENOMEM;
- + }
- + memset(tty->read_buf, 0, N_TTY_BUF_SIZE);
- + tty->read_head = tty->read_tail = tty->read_cnt = 0;
- + memset(tty->read_flags, 0, sizeof(tty->read_flags));
- + n_tty_set_termios(tty, 0);
- + tty->minimum_to_wake = 1;
- + tty->closing = 0;
- + return 0;
- +}
- +
- +static inline int input_available_p(struct tty_struct *tty, int amt)
- +{
- + if (L_ICANON(tty)) {
- + if (tty->canon_data)
- + return 1;
- + } else if (tty->read_cnt >= (amt ? amt : 1))
- + return 1;
- +
- + return 0;
- +}
- +
- +/*
- + * Helper function to speed up read_chan. It is only called when
- + * ICANON is off; it copies characters straight from the tty queue to
- + * user space directly. It can be profitably called twice; once to
- + * drain the space from the tail pointer to the (physical) end of the
- + * buffer, and once to drain the space from the (physical) beginning of
- + * the buffer to head pointer.
- + */
- +static inline void copy_from_read_buf(struct tty_struct *tty,
- + unsigned char **b,
- + unsigned int *nr)
- +
- +{
- + int n;
- +
- + n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail));
- + if (!n)
- + return;
- + memcpy_tofs(*b, &tty->read_buf[tty->read_tail], n);
- + tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
- + tty->read_cnt -= n;
- + *b += n;
- + *nr -= n;
- +}
- +
- +static int read_chan(struct tty_struct *tty, struct file *file,
- + unsigned char *buf, unsigned int nr)
- +{
- + struct wait_queue wait = { current, NULL };
- + int c;
- + unsigned char *b = buf;
- + int minimum, time;
- + int retval = 0;
- + int size;
- +
- +do_it_again:
- +
- + if (!tty->read_buf) {
- + printk("n_tty_read_chan: called with read_buf == NULL?!?\n");
- + return -EIO;
- + }
- +
- + /* Job control check -- must be done at start and after
- + every sleep (POSIX.1 7.1.1.4). */
- + /* NOTE: not yet done after every sleep pending a thorough
- + check of the logic of this change. -- jlc */
- + /* don't stop on /dev/console */
- + if (file->f_inode->i_rdev != CONSOLE_DEV &&
- + current->tty == tty) {
- + if (tty->pgrp <= 0)
- + printk("read_chan: tty->pgrp <= 0!\n");
- + else if (current->pgrp != tty->pgrp) {
- + if (is_ignored(SIGTTIN) ||
- + is_orphaned_pgrp(current->pgrp))
- + return -EIO;
- + kill_pg(current->pgrp, SIGTTIN, 1);
- + return -ERESTARTSYS;
- + }
- + }
- +
- + if (L_ICANON(tty)) {
- + minimum = time = 0;
- + current->timeout = (unsigned long) -1;
- + } else {
- + time = (HZ / 10) * TIME_CHAR(tty);
- + minimum = MIN_CHAR(tty);
- + if (minimum) {
- + current->timeout = (unsigned long) -1;
- + if (time)
- + tty->minimum_to_wake = 1;
- + else if (!tty->read_wait ||
- + (tty->minimum_to_wake > minimum))
- + tty->minimum_to_wake = minimum;
- + } else {
- + if (time) {
- + current->timeout = time + jiffies;
- + time = 0;
- + } else
- + current->timeout = 0;
- + tty->minimum_to_wake = minimum = 1;
- + }
- + }
- +
- + add_wait_queue(&tty->read_wait, &wait);
- + while (1) {
- + /* First test for status change. */
- + if (tty->packet && tty->link->ctrl_status) {
- + if (b != buf)
- + break;
- + put_user(tty->link->ctrl_status, b++);
- + tty->link->ctrl_status = 0;
- + break;
- + }
- + /* This statement must be first before checking for input
- + so that any interrupt will set the state back to
- + TASK_RUNNING. */
- + current->state = TASK_INTERRUPTIBLE;
- +
- + if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
- + ((minimum - (b - buf)) >= 1))
- + tty->minimum_to_wake = (minimum - (b - buf));
- +
- + if (!input_available_p(tty, 0)) {
- + if (tty->flags & (1 << TTY_SLAVE_CLOSED)) {
- + retval = -EIO;
- + break;
- + }
- + if (tty_hung_up_p(file))
- + break;
- + if (!current->timeout)
- + break;
- + if (file->f_flags & O_NONBLOCK) {
- + retval = -EAGAIN;
- + break;
- + }
- + if (current->signal & ~current->blocked) {
- + retval = -ERESTARTSYS;
- + break;
- + }
- + schedule();
- + continue;
- + }
- + current->state = TASK_RUNNING;
- +
- + /* Deal with packet mode. */
- + if (tty->packet && b == buf) {
- + put_user(TIOCPKT_DATA, b++);
- + nr--;
- + }
- +
- + if (L_ICANON(tty)) {
- + while (1) {
- + int eol;
- +
- + disable_bh(TQUEUE_BH);
- + if (!tty->read_cnt) {
- + enable_bh(TQUEUE_BH);
- + break;
- + }
- + eol = clear_bit(tty->read_tail,
- + &tty->read_flags);
- + c = tty->read_buf[tty->read_tail];
- + tty->read_tail = ((tty->read_tail+1) &
- + (N_TTY_BUF_SIZE-1));
- + tty->read_cnt--;
- + enable_bh(TQUEUE_BH);
- + if (!eol) {
- + put_user(c, b++);
- + if (--nr)
- + continue;
- + break;
- + }
- + if (--tty->canon_data < 0) {
- + tty->canon_data = 0;
- + }
- + if (c != __DISABLED_CHAR) {
- + put_user(c, b++);
- + nr--;
- + }
- + break;
- + }
- + } else {
- + disable_bh(TQUEUE_BH);
- + copy_from_read_buf(tty, &b, &nr);
- + copy_from_read_buf(tty, &b, &nr);
- + enable_bh(TQUEUE_BH);
- + }
- +
- + /* If there is enough space in the read buffer now, let the
- + low-level driver know. */
- + if (tty->driver.unthrottle &&
- + (tty->read_cnt <= TTY_THRESHOLD_UNTHROTTLE)
- + && clear_bit(TTY_THROTTLED, &tty->flags))
- + tty->driver.unthrottle(tty);
- +
- + if (b - buf >= minimum || !nr)
- + break;
- + if (time)
- + current->timeout = time + jiffies;
- + }
- + remove_wait_queue(&tty->read_wait, &wait);
- +
- + if (!tty->read_wait)
- + tty->minimum_to_wake = minimum;
- +
- + current->state = TASK_RUNNING;
- + current->timeout = 0;
- + size = b - buf;
- + if (size && nr)
- + clear_bit(TTY_PUSH, &tty->flags);
- + if (!size && clear_bit(TTY_PUSH, &tty->flags))
- + goto do_it_again;
- + if (!size && !retval)
- + clear_bit(TTY_PUSH, &tty->flags);
- + return (size ? size : retval);
- +}
- +
- +static int write_chan(struct tty_struct * tty, struct file * file,
- + const unsigned char * buf, unsigned int nr)
- +{
- + struct wait_queue wait = { current, NULL };
- + int c;
- + const unsigned char *b = buf;
- + int retval = 0;
- +
- + /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
- + if (L_TOSTOP(tty) && file->f_inode->i_rdev != CONSOLE_DEV) {
- + retval = tty_check_change(tty);
- + if (retval)
- + return retval;
- + }
- +
- + add_wait_queue(&tty->write_wait, &wait);
- + while (1) {
- + current->state = TASK_INTERRUPTIBLE;
- + if (current->signal & ~current->blocked) {
- + retval = -ERESTARTSYS;
- + break;
- + }
- + if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
- + retval = -EIO;
- + break;
- + }
- + if (O_OPOST(tty)) {
- + while (nr > 0) {
- + c = get_user(b);
- + if (opost(c, tty) < 0)
- + break;
- + b++; nr--;
- + }
- + if (tty->driver.flush_chars)
- + tty->driver.flush_chars(tty);
- + } else {
- + c = tty->driver.write(tty, 1, b, nr);
- + b += c;
- + nr -= c;
- + }
- + if (!nr)
- + break;
- + if (file->f_flags & O_NONBLOCK) {
- + retval = -EAGAIN;
- + break;
- + }
- + schedule();
- + }
- + current->state = TASK_RUNNING;
- + remove_wait_queue(&tty->write_wait, &wait);
- + return (b - buf) ? b - buf : retval;
- +}
- +
- +static int normal_select(struct tty_struct * tty, struct inode * inode,
- + struct file * file, int sel_type, select_table *wait)
- +{
- + switch (sel_type) {
- + case SEL_IN:
- + if (input_available_p(tty, TIME_CHAR(tty) ? 0 :
- + MIN_CHAR(tty)))
- + return 1;
- + /* fall through */
- + case SEL_EX:
- + if (tty->packet && tty->link->ctrl_status)
- + return 1;
- + if (tty->flags & (1 << TTY_SLAVE_CLOSED))
- + return 1;
- + if (tty_hung_up_p(file))
- + return 1;
- + if (!tty->read_wait) {
- + if (MIN_CHAR(tty) && !TIME_CHAR(tty))
- + tty->minimum_to_wake = MIN_CHAR(tty);
- + else
- + tty->minimum_to_wake = 1;
- + }
- + select_wait(&tty->read_wait, wait);
- + return 0;
- + case SEL_OUT:
- + if (tty->driver.chars_in_buffer(tty) < WAKEUP_CHARS)
- + return 1;
- + select_wait(&tty->write_wait, wait);
- + return 0;
- + }
- + return 0;
- +}
- +
- +struct tty_ldisc tty_ldisc_N_TTY = {
- + TTY_LDISC_MAGIC, /* magic */
- + 0, /* num */
- + 0, /* flags */
- + n_tty_open, /* open */
- + n_tty_close, /* close */
- + n_tty_flush_buffer, /* flush_buffer */
- + n_tty_chars_in_buffer, /* chars_in_buffer */
- + read_chan, /* read */
- + write_chan, /* write */
- + n_tty_ioctl, /* ioctl */
- + n_tty_set_termios, /* set_termios */
- + normal_select, /* select */
- + n_tty_receive_buf, /* receive_buf */
- + n_tty_receive_room, /* receive_room */
- + 0 /* write_wakeup */
- +};
- +
- diff -urNwbB linux/arch/arm/drivers/char/pty.c linux.arm/arch/arm/drivers/char/pty.c
- --- linux/arch/arm/drivers/char/pty.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/pty.c Sun Mar 3 12:46:45 1996
- @@ -0,0 +1,265 @@
- +/*
- + * linux/arch/arm/drivers/char/pty.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + *
- + * Modifications for ARM processor Copyright (C) 1995, 1996 Russell King.
- + */
- +
- +/*
- + * pty.c
- + *
- + * This module exports the following pty function:
- + *
- + * int pty_open(struct tty_struct * tty, struct file * filp);
- + */
- +
- +#include <linux/errno.h>
- +#include <linux/sched.h>
- +#include <linux/interrupt.h>
- +#include <linux/tty.h>
- +#include <linux/tty_flip.h>
- +#include <linux/fcntl.h>
- +#include <linux/string.h>
- +#include <linux/major.h>
- +#include <linux/mm.h>
- +#include <linux/malloc.h>
- +
- +#include <asm/segment.h>
- +#include <asm/system.h>
- +#include <asm/bitops.h>
- +
- +struct pty_struct {
- + int magic;
- + struct wait_queue * open_wait;
- +};
- +
- +#define PTY_MAGIC 0x5001
- +#define PTY_BUF_SIZE 4096/2
- +/*
- + * tmp_buf is used as a temporary buffer by pty_write. We need to
- + * lock it in case the memcpy_fromfs blocks while swapping in a page,
- + * and some other program tries to do a pty write at the same time.
- + * Since the lock will only come under contention when the system is
- + * swapping and available memory is low, it makes sense to share one
- + * buffer across all the PTY's, since it significantly saves memory if
- + * large numbers of PTY's are open.
- + */
- +static unsigned char *tmp_buf;
- +static struct semaphore tmp_buf_sem = MUTEX;
- +
- +struct tty_driver pty_driver, pty_slave_driver;
- +static int pty_refcount;
- +
- +static struct tty_struct *pty_table[NR_PTYS];
- +static struct termios *pty_termios[NR_PTYS];
- +static struct termios *pty_termios_locked[NR_PTYS];
- +static struct tty_struct *ttyp_table[NR_PTYS];
- +static struct termios *ttyp_termios[NR_PTYS];
- +static struct termios *ttyp_termios_locked[NR_PTYS];
- +static struct pty_struct pty_state[NR_PTYS];
- +
- +#define MIN(a,b) ((a) < (b) ? (a) : (b))
- +
- +static void pty_close(struct tty_struct * tty, struct file * filp)
- +{
- + if (!tty)
- + return;
- + if (tty->driver.subtype == PTY_TYPE_MASTER) {
- + if (tty->count > 1)
- + printk("master pty_close: count = %d!!\n", tty->count);
- + } else {
- + if (tty->count > 2)
- + return;
- + }
- + wake_up_interruptible(&tty->read_wait);
- + wake_up_interruptible(&tty->write_wait);
- + if (!tty->link)
- + return;
- + wake_up_interruptible(&tty->link->read_wait);
- + wake_up_interruptible(&tty->link->write_wait);
- + if (tty->driver.subtype == PTY_TYPE_MASTER)
- + tty_hangup(tty->link);
- + else {
- + start_tty(tty);
- + set_bit(TTY_SLAVE_CLOSED, &tty->link->flags);
- + }
- +}
- +
- +/*
- + * The unthrottle routine is called by the line discipline to signal
- + * that it can receive more characters. For PTY's, the TTY_THROTTLED
- + * flag is always set, to force the line discipline to always call the
- + * unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE
- + * characters in the queue. This is necessary since each time this
- + * happens, we need to wake up any sleeping processes that could be
- + * (1) trying to send data to the pty, or (2) waiting in wait_until_sent()
- + * for the pty buffer to be drained.
- + */
- +static void pty_unthrottle(struct tty_struct * tty)
- +{
- + struct tty_struct *o_tty = tty->link;
- +
- + if (!o_tty)
- + return;
- +
- + if ((o_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- + o_tty->ldisc.write_wakeup)
- + (o_tty->ldisc.write_wakeup)(o_tty);
- + wake_up_interruptible(&o_tty->write_wait);
- + set_bit(TTY_THROTTLED, &tty->flags);
- +}
- +
- +static int pty_write(struct tty_struct * tty, int from_user,
- + const unsigned char *buf, int count)
- +{
- + struct tty_struct *to = tty->link;
- + int c=0, n, r;
- + char *temp_buffer;
- +
- + if (!to || tty->stopped)
- + return 0;
- +
- + if (from_user) {
- + down(&tmp_buf_sem);
- + temp_buffer = tmp_buf +
- + ((tty->driver.subtype-1) * PTY_BUF_SIZE);
- + while (count > 0) {
- + n = MIN(count, PTY_BUF_SIZE);
- + memcpy_fromfs(temp_buffer, buf, n);
- + r = to->ldisc.receive_room(to);
- + if (r <= 0)
- + break;
- + n = MIN(n, r);
- + to->ldisc.receive_buf(to, temp_buffer, 0, n);
- + buf += n; c+= n;
- + count -= n;
- + }
- + up(&tmp_buf_sem);
- + } else {
- + c = MIN(count, to->ldisc.receive_room(to));
- + to->ldisc.receive_buf(to, buf, 0, c);
- + }
- +
- + return c;
- +}
- +
- +static int pty_write_room(struct tty_struct *tty)
- +{
- + struct tty_struct *to = tty->link;
- +
- + if (!to || tty->stopped)
- + return 0;
- +
- + return to->ldisc.receive_room(to);
- +}
- +
- +static int pty_chars_in_buffer(struct tty_struct *tty)
- +{
- + struct tty_struct *to = tty->link;
- +
- + if (!to || !to->ldisc.chars_in_buffer)
- + return 0;
- +
- + return to->ldisc.chars_in_buffer(to);
- +}
- +
- +static void pty_flush_buffer(struct tty_struct *tty)
- +{
- + struct tty_struct *to = tty->link;
- +
- + if (!to)
- + return;
- +
- + if (to->ldisc.flush_buffer)
- + to->ldisc.flush_buffer(to);
- +
- + if (to->packet) {
- + tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
- + wake_up_interruptible(&to->read_wait);
- + }
- +}
- +
- +int pty_open(struct tty_struct *tty, struct file * filp)
- +{
- + int line;
- + struct pty_struct *pty;
- +
- + if (!tty || !tty->link)
- + return -ENODEV;
- + line = MINOR(tty->device) - tty->driver.minor_start;
- + if ((line < 0) || (line >= NR_PTYS))
- + return -ENODEV;
- + pty = pty_state + line;
- + tty->driver_data = pty;
- +
- + if (!tmp_buf) {
- + tmp_buf = (unsigned char *) kmalloc(PTY_BUF_SIZE*2,GFP_KERNEL);
- + if (!tmp_buf)
- + return -ENOMEM;
- + }
- +
- + if (tty->driver.subtype == PTY_TYPE_SLAVE)
- + clear_bit(TTY_SLAVE_CLOSED, &tty->link->flags);
- + wake_up_interruptible(&pty->open_wait);
- + set_bit(TTY_THROTTLED, &tty->flags);
- + if (filp->f_flags & O_NDELAY)
- + return 0;
- + while (!tty->link->count && !(current->signal & ~current->blocked))
- + interruptible_sleep_on(&pty->open_wait);
- + if (!tty->link->count)
- + return -ERESTARTSYS;
- + return 0;
- +}
- +
- +int pty_init(void)
- +{
- + memset(&pty_state, 0, sizeof(pty_state));
- + memset(&pty_driver, 0, sizeof(struct tty_driver));
- + pty_driver.magic = TTY_DRIVER_MAGIC;
- + pty_driver.name = "pty";
- + pty_driver.major = TTY_MAJOR;
- + pty_driver.minor_start = 128;
- + pty_driver.num = NR_PTYS;
- + pty_driver.type = TTY_DRIVER_TYPE_PTY;
- + pty_driver.subtype = PTY_TYPE_MASTER;
- + pty_driver.init_termios = tty_std_termios;
- + pty_driver.init_termios.c_iflag = 0;
- + pty_driver.init_termios.c_oflag = 0;
- + pty_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
- + pty_driver.init_termios.c_lflag = 0;
- + pty_driver.flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW;
- + pty_driver.refcount = &pty_refcount;
- + pty_driver.table = pty_table;
- + pty_driver.termios = pty_termios;
- + pty_driver.termios_locked = pty_termios_locked;
- + pty_driver.other = &pty_slave_driver;
- +
- + pty_driver.open = pty_open;
- + pty_driver.close = pty_close;
- + pty_driver.write = pty_write;
- + pty_driver.write_room = pty_write_room;
- + pty_driver.flush_buffer = pty_flush_buffer;
- + pty_driver.chars_in_buffer = pty_chars_in_buffer;
- + pty_driver.unthrottle = pty_unthrottle;
- +
- + pty_slave_driver = pty_driver;
- + pty_slave_driver.name = "ttyp";
- + pty_slave_driver.subtype = PTY_TYPE_SLAVE;
- + pty_slave_driver.minor_start = 192;
- + pty_slave_driver.init_termios = tty_std_termios;
- + pty_slave_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
- + pty_slave_driver.table = ttyp_table;
- + pty_slave_driver.termios = ttyp_termios;
- + pty_slave_driver.termios_locked = ttyp_termios_locked;
- + pty_slave_driver.other = &pty_driver;
- +
- + tmp_buf = 0;
- +
- + if (tty_register_driver(&pty_driver))
- + panic("Couldn't register pty driver");
- + if (tty_register_driver(&pty_slave_driver))
- + panic("Couldn't register pty slave driver");
- +
- + return 0;
- +}
- diff -urNwbB linux/arch/arm/drivers/char/selection.h linux.arm/arch/arm/drivers/char/selection.h
- --- linux/arch/arm/drivers/char/selection.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/selection.h Sun Feb 11 09:32:21 1996
- @@ -0,0 +1,20 @@
- +/*
- + * selection.h
- + *
- + * Interface between console.c, tty_io.c, vt.c, vc_screen.c and selection.c
- + */
- +extern int sel_cons;
- +
- +extern void clear_selection(void);
- +extern int set_selection(const unsigned long arg, struct tty_struct *tty);
- +extern int paste_selection(struct tty_struct *tty);
- +extern int sel_loadlut(const unsigned long arg);
- +extern int mouse_reporting(void);
- +extern void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry);
- +
- +extern unsigned long video_num_columns;
- +extern unsigned long video_num_lines;
- +extern unsigned long video_size_row;
- +
- +extern void do_unblank_screen(void);
- +
- diff -urNwbB linux/arch/arm/drivers/char/serial.c linux.arm/arch/arm/drivers/char/serial.c
- --- linux/arch/arm/drivers/char/serial.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/serial.c Sun Mar 3 12:47:09 1996
- @@ -0,0 +1,2715 @@
- +/*
- + * linux/arch/arm/drivers/char/serial.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + *
- + * Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now
- + * much more extensible to support other serial cards based on the
- + * 16450/16550A UART's. Added support for the AST FourPort and the
- + * Accent Async board.
- + *
- + * set_serial_info fixed to set the flags, custom divisor, and uart
- + * type fields. Fix suggested by Michael K. Johnson 12/12/92.
- + *
- + * This module exports the following rs232 io functions:
- + *
- + * int rs_init(void);
- + * int rs_open(struct tty_struct * tty, struct file * filp)
- + *
- + * Slight modifications for ARM Copyright (C) 1995, 1996 Russell King
- + */
- +
- +#include <linux/errno.h>
- +#include <linux/signal.h>
- +#include <linux/sched.h>
- +#include <linux/timer.h>
- +#include <linux/interrupt.h>
- +#include <linux/tty.h>
- +#include <linux/tty_flip.h>
- +#include <linux/serial.h>
- +#include <linux/serial_reg.h>
- +#include <linux/config.h>
- +#include <linux/major.h>
- +#include <linux/string.h>
- +#include <linux/fcntl.h>
- +#include <linux/ptrace.h>
- +#include <linux/ioport.h>
- +#include <linux/mm.h>
- +
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include <asm/segment.h>
- +#include <asm/bitops.h>
- +
- +DECLARE_TASK_QUEUE(tq_serial);
- +
- +struct tty_driver serial_driver, callout_driver;
- +static int serial_refcount;
- +
- +/* serial subtype definitions */
- +#define SERIAL_TYPE_NORMAL 1
- +#define SERIAL_TYPE_CALLOUT 2
- +
- +/* number of characters left in xmit buffer before we ask for more */
- +#define WAKEUP_CHARS 256
- +
- +/*
- + * Serial driver configuration section. Here are the various options:
- + *
- + * CONFIG_HUB6
- + * Enables support for the venerable Bell Technologies
- + * HUB6 card.
- + *
- + * SERIAL_PARANOIA_CHECK
- + * Check the magic number for the async_structure where
- + * ever possible.
- + */
- +
- +#define SERIAL_PARANOIA_CHECK
- +#define CONFIG_SERIAL_NOPAUSE_IO
- +#define SERIAL_DO_RESTART
- +
- +#undef SERIAL_DEBUG_INTR
- +#undef SERIAL_DEBUG_OPEN
- +#undef SERIAL_DEBUG_FLOW
- +
- +#define RS_STROBE_TIME (10*HZ)
- +#define RS_ISR_PASS_LIMIT 256
- +
- +#define _INLINE_ inline
- +
- +/*
- + * IRQ_timeout - How long the timeout should be for each IRQ
- + * should be after the IRQ has been active.
- + */
- +
- +static struct async_struct *IRQ_ports[16];
- +static struct rs_multiport_struct rs_multiport[16];
- +static int IRQ_timeout[16];
- +static volatile int rs_irq_triggered;
- +static volatile int rs_triggered;
- +static int rs_wild_int_mask;
- +
- +static void autoconfig(struct async_struct * info);
- +static void change_speed(struct async_struct *info);
- +
- +/*
- + * This assumes you have a 1.8432 MHz clock for your UART.
- + *
- + * It'd be nice if someone built a serial card with a 24.576 MHz
- + * clock, since the 16550A is capable of handling a top speed of 1.5
- + * megabits/second; but this requires the faster clock.
- + */
- +#define BASE_BAUD ( 1843200 / 16 )
- +
- +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
- +
- +struct async_struct rs_table[] = {
- + /* UART CLK PORT IRQ FLAGS */
- + { 0, BASE_BAUD, 0x3F8,10, STD_COM_FLAGS }, /* ttyS0 */
- + { 0, BASE_BAUD, 0x2F8,10, STD_COM_FLAGS }, /* ttyS1 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS2 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS3 */
- +
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS4 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS5 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS6 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS7 */
- +
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS8 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS9 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS10 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS11 */
- +
- +};
- +
- +#define NR_PORTS (sizeof(rs_table)/sizeof(struct async_struct))
- +
- +static struct tty_struct *serial_table[NR_PORTS];
- +static struct termios *serial_termios[NR_PORTS];
- +static struct termios *serial_termios_locked[NR_PORTS];
- +
- +#ifndef MIN
- +#define MIN(a,b) ((a) < (b) ? (a) : (b))
- +#endif
- +
- +/*
- + * tmp_buf is used as a temporary buffer by serial_write. We need to
- + * lock it in case the memcpy_fromfs blocks while swapping in a page,
- + * and some other program tries to do a serial write at the same time.
- + * Since the lock will only come under contention when the system is
- + * swapping and available memory is low, it makes sense to share one
- + * buffer across all the serial ports, since it significantly saves
- + * memory if large numbers of serial ports are open.
- + */
- +static unsigned char *tmp_buf = 0;
- +static struct semaphore tmp_buf_sem = MUTEX;
- +
- +static inline int serial_paranoia_check(struct async_struct *info,
- + kdev_t device, const char *routine)
- +{
- +#ifdef SERIAL_PARANOIA_CHECK
- + static const char *badmagic =
- + "Warning: bad magic number for serial struct (%s) in %s\n";
- + static const char *badinfo =
- + "Warning: null async_struct for (%s) in %s\n";
- +
- + if (!info) {
- + printk(badinfo, kdevname(device), routine);
- + return 1;
- + }
- + if (info->magic != SERIAL_MAGIC) {
- + printk(badmagic, kdevname(device), routine);
- + return 1;
- + }
- +#endif
- + return 0;
- +}
- +
- +/*
- + * This is used to figure out the divisor speeds and the timeouts
- + */
- +static int baud_table[] = {
- + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
- + 9600, 19200, 38400, 57600, 115200, 0 };
- +
- +static inline unsigned int serial_in(struct async_struct *info, int offset)
- +{
- +#ifdef CONFIG_HUB6
- + if (info->hub6) {
- + outb(info->hub6 - 1 + offset, info->port);
- + return inb(info->port+1);
- + } else
- +#endif
- + return inb(info->port + offset);
- +}
- +
- +static inline unsigned int serial_inp(struct async_struct *info, int offset)
- +{
- +#ifdef CONFIG_HUB6
- + if (info->hub6) {
- + outb(info->hub6 - 1 + offset, info->port);
- + return inb_p(info->port+1);
- + } else
- +#endif
- +#ifdef CONFIG_SERIAL_NOPAUSE_IO
- + return inb(info->port + offset);
- +#else
- + return inb_p(info->port + offset);
- +#endif
- +}
- +
- +static inline void serial_out(struct async_struct *info, int offset, int value)
- +{
- +#ifdef CONFIG_HUB6
- + if (info->hub6) {
- + outb(info->hub6 - 1 + offset, info->port);
- + outb(value, info->port+1);
- + } else
- +#endif
- + outb(value, info->port+offset);
- +}
- +
- +static inline void serial_outp(struct async_struct *info, int offset,
- + int value)
- +{
- +#ifdef CONFIG_HUB6
- + if (info->hub6) {
- + outb(info->hub6 - 1 + offset, info->port);
- + outb_p(value, info->port+1);
- + } else
- +#endif
- +#ifdef CONFIG_SERIAL_NOPAUSE_IO
- + outb(value, info->port+offset);
- +#else
- + outb_p(value, info->port+offset);
- +#endif
- +}
- +
- +/*
- + * ------------------------------------------------------------
- + * 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 = (struct async_struct *)tty->driver_data;
- + unsigned long flags;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_stop"))
- + return;
- +
- + save_flags(flags); cli();
- + if (info->IER & UART_IER_THRI) {
- + info->IER &= ~UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + }
- + restore_flags(flags);
- +}
- +
- +static void rs_start(struct tty_struct *tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- + unsigned long flags;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_start"))
- + return;
- +
- + save_flags(flags); cli();
- + if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) {
- + info->IER |= UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + }
- + restore_flags(flags);
- +}
- +
- +/*
- + * ----------------------------------------------------------------------
- + *
- + * Here starts the interrupt handling routines. All of the following
- + * subroutines are declared as inline and are folded into
- + * rs_interrupt(). They were separated out for readability's sake.
- + *
- + * Note: rs_interrupt() is a "fast" interrupt, which means that it
- + * runs with interrupts turned off. People who may want to modify
- + * rs_interrupt() should try to keep the interrupt handler as fast as
- + * possible. After you are done making modifications, it is not a bad
- + * idea to do:
- + *
- + * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
- + *
- + * and look at the resulting assemble code in serial.s.
- + *
- + * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
- + * -----------------------------------------------------------------------
- + */
- +
- +/*
- + * This is the serial driver's interrupt routine while we are probing
- + * for submarines.
- + */
- +static void rs_probe(int irq, struct pt_regs * regs)
- +{
- + rs_irq_triggered = irq;
- + rs_triggered |= 1 << irq;
- + return;
- +}
- +
- +/*
- + * This routine is used by the interrupt handler to schedule
- + * processing in the software interrupt portion of the driver.
- + */
- +static _INLINE_ void rs_sched_event(struct async_struct *info,
- + int event)
- +{
- + info->event |= 1 << event;
- + queue_task_irq_off(&info->tqueue, &tq_serial);
- + mark_bh(SERIAL_BH);
- +}
- +
- +static _INLINE_ void receive_chars(struct async_struct *info,
- + int *status)
- +{
- + struct tty_struct *tty = info->tty;
- + unsigned char ch;
- + int ignored = 0;
- +
- + do {
- + ch = serial_inp(info, UART_RX);
- + if (*status & info->ignore_status_mask) {
- + if (++ignored > 100)
- + break;
- + goto ignore_char;
- + }
- + if (tty->flip.count >= TTY_FLIPBUF_SIZE)
- + break;
- + tty->flip.count++;
- + if (*status & (UART_LSR_BI)) {
- + printk("handling break....");
- + *tty->flip.flag_buf_ptr++ = TTY_BREAK;
- + if (info->flags & ASYNC_SAK)
- + do_SAK(tty);
- + } else if (*status & UART_LSR_PE)
- + *tty->flip.flag_buf_ptr++ = TTY_PARITY;
- + else if (*status & UART_LSR_FE)
- + *tty->flip.flag_buf_ptr++ = TTY_FRAME;
- + else if (*status & UART_LSR_OE)
- + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
- + else
- + *tty->flip.flag_buf_ptr++ = 0;
- + *tty->flip.char_buf_ptr++ = ch;
- + ignore_char:
- + *status = serial_inp(info, UART_LSR) & info->read_status_mask;
- + } while (*status & UART_LSR_DR);
- +{ unsigned long flags; save_flags (flags); if (!(flags & 0x08000000)) printk ("SERIAL IRQ ON!\n"); }
- + queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
- +#ifdef SERIAL_DEBUG_INTR
- + printk("DR...");
- +#endif
- +}
- +
- +static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
- +{
- + int count;
- +
- + if (info->x_char) {
- + serial_outp(info, UART_TX, info->x_char);
- + info->x_char = 0;
- + if (intr_done)
- + *intr_done = 0;
- + return;
- + }
- + if ((info->xmit_cnt <= 0) || info->tty->stopped ||
- + info->tty->hw_stopped) {
- + info->IER &= ~UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + return;
- + }
- +
- + count = info->xmit_fifo_size;
- + do {
- + serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]);
- + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
- + if (--info->xmit_cnt <= 0)
- + break;
- + } while (--count > 0);
- +
- + if (info->xmit_cnt < WAKEUP_CHARS)
- + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
- +
- +#ifdef SERIAL_DEBUG_INTR
- + printk("THRE...");
- +#endif
- + if (intr_done)
- + *intr_done = 0;
- +
- + if (info->xmit_cnt <= 0) {
- + info->IER &= ~UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + }
- +}
- +
- +static _INLINE_ void check_modem_status(struct async_struct *info)
- +{
- + int status;
- +
- + status = serial_in(info, UART_MSR);
- +
- + if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
- +#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
- + printk("ttys%d CD now %s...", info->line,
- + (status & UART_MSR_DCD) ? "on" : "off");
- +#endif
- + if (status & UART_MSR_DCD)
- + wake_up_interruptible(&info->open_wait);
- + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
- + (info->flags & ASYNC_CALLOUT_NOHUP))) {
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("scheduling hangup...");
- +#endif
- + queue_task_irq_off(&info->tqueue_hangup,
- + &tq_scheduler);
- + }
- + }
- + if (info->flags & ASYNC_CTS_FLOW) {
- + if (info->tty->hw_stopped) {
- + if (status & UART_MSR_CTS) {
- +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
- + printk("CTS tx start...");
- +#endif
- + info->tty->hw_stopped = 0;
- + info->IER |= UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
- + return;
- + }
- + } else {
- + if (!(status & UART_MSR_CTS)) {
- +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
- + printk("CTS tx stop...");
- +#endif
- + info->tty->hw_stopped = 1;
- + info->IER &= ~UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + }
- + }
- + }
- +}
- +
- +/*
- + * This is the serial driver's generic interrupt routine
- + */
- +static void rs_interrupt(int irq, struct pt_regs * regs)
- +{
- + int status;
- + struct async_struct * info;
- + int pass_counter = 0;
- + struct async_struct *end_mark = 0;
- + int first_multi = 0;
- + struct rs_multiport_struct *multi;
- +
- +#ifdef SERIAL_DEBUG_INTR
- + printk("rs_interrupt(%d)...", irq);
- +#endif
- +
- + info = IRQ_ports[irq];
- + if (!info)
- + return;
- +
- + multi = &rs_multiport[irq];
- + if (multi->port_monitor)
- + first_multi = inb(multi->port_monitor);
- +
- + do {
- + if (!info->tty ||
- + (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) {
- + if (!end_mark)
- + end_mark = info;
- + goto next;
- + }
- + end_mark = 0;
- +
- + info->last_active = jiffies;
- +
- + status = serial_inp(info, UART_LSR) & info->read_status_mask;
- +#ifdef SERIAL_DEBUG_INTR
- + printk("status = %x...", status);
- +#endif
- + if (status & UART_LSR_DR)
- + receive_chars(info, &status);
- + check_modem_status(info);
- + if (status & UART_LSR_THRE)
- + transmit_chars(info, 0);
- +
- + next:
- + info = info->next_port;
- + if (!info) {
- + info = IRQ_ports[irq];
- + if (pass_counter++ > RS_ISR_PASS_LIMIT) {
- +#if 0
- + printk("rs loop break\n");
- +#endif
- + break; /* Prevent infinite loops */
- + }
- + continue;
- + }
- + } while (end_mark != info);
- + if (multi->port_monitor)
- + printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n",
- + info->irq, first_multi, inb(multi->port_monitor));
- +#ifdef SERIAL_DEBUG_INTR
- + printk("end.\n");
- +#endif
- +}
- +
- +/*
- + * This is the serial driver's interrupt routine for a single port
- + */
- +static void rs_interrupt_single(int irq, struct pt_regs * regs)
- +{
- + int status;
- + int pass_counter = 0;
- + int first_multi = 0;
- + struct async_struct * info;
- + struct rs_multiport_struct *multi;
- +
- +#ifdef SERIAL_DEBUG_INTR
- + printk("rs_interrupt_single(%d)...", irq);
- +#endif
- +
- + info = IRQ_ports[irq];
- + if (!info || !info->tty)
- + return;
- +
- + multi = &rs_multiport[irq];
- + if (multi->port_monitor)
- + first_multi = inb(multi->port_monitor);
- +
- + do {
- + status = serial_inp(info, UART_LSR) & info->read_status_mask;
- +#ifdef SERIAL_DEBUG_INTR
- + printk("status = %x...", status);
- +#endif
- + if (status & UART_LSR_DR)
- + receive_chars(info, &status);
- + check_modem_status(info);
- + if (status & UART_LSR_THRE)
- + transmit_chars(info, 0);
- + if (pass_counter++ > RS_ISR_PASS_LIMIT) {
- +#if 0
- + printk("rs_single loop break.\n");
- +#endif
- + break;
- + }
- + } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
- + info->last_active = jiffies;
- + if (multi->port_monitor)
- + printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n",
- + info->irq, first_multi, inb(multi->port_monitor));
- +#ifdef SERIAL_DEBUG_INTR
- + printk("end.\n");
- +#endif
- +}
- +
- +/*
- + * This is the serial driver's for multiport boards
- + */
- +static void rs_interrupt_multi(int irq, struct pt_regs * regs)
- +{
- + int status;
- + struct async_struct * info;
- + int pass_counter = 0;
- + int first_multi= 0;
- + struct rs_multiport_struct *multi;
- +
- +#ifdef SERIAL_DEBUG_INTR
- + printk("rs_interrupt_multi(%d)...", irq);
- +#endif
- +
- + info = IRQ_ports[irq];
- + if (!info)
- + return;
- + multi = &rs_multiport[irq];
- + if (!multi->port1) {
- + /* Should never happen */
- + printk("rs_interrupt_multi: NULL port1!\n");
- + return;
- + }
- + if (multi->port_monitor)
- + first_multi = inb(multi->port_monitor);
- +
- + while (1) {
- + if (!info->tty ||
- + (serial_in(info, UART_IIR) & UART_IIR_NO_INT))
- + goto next;
- +
- + info->last_active = jiffies;
- +
- + status = serial_inp(info, UART_LSR) & info->read_status_mask;
- +#ifdef SERIAL_DEBUG_INTR
- + printk("status = %x...", status);
- +#endif
- + if (status & UART_LSR_DR)
- + receive_chars(info, &status);
- + check_modem_status(info);
- + if (status & UART_LSR_THRE)
- + transmit_chars(info, 0);
- +
- + next:
- + info = info->next_port;
- + if (info)
- + continue;
- +
- + info = IRQ_ports[irq];
- + if (pass_counter++ > RS_ISR_PASS_LIMIT) {
- +#if 1
- + printk("rs_multi loop break\n");
- +#endif
- + break; /* Prevent infinite loops */
- + }
- + if (multi->port_monitor)
- + printk("rs port monitor irq %d: 0x%x, 0x%x\n",
- + info->irq, first_multi,
- + inb(multi->port_monitor));
- + if ((inb(multi->port1) & multi->mask1) != multi->match1)
- + continue;
- + if (!multi->port2)
- + break;
- + if ((inb(multi->port2) & multi->mask2) != multi->match2)
- + continue;
- + if (!multi->port3)
- + break;
- + if ((inb(multi->port3) & multi->mask3) != multi->match3)
- + continue;
- + if (!multi->port4)
- + break;
- + if ((inb(multi->port4) & multi->mask4) == multi->match4)
- + continue;
- + break;
- + }
- +#ifdef SERIAL_DEBUG_INTR
- + printk("end.\n");
- +#endif
- +}
- +
- +
- +/*
- + * -------------------------------------------------------------------
- + * Here ends the serial interrupt routines.
- + * -------------------------------------------------------------------
- + */
- +
- +/*
- + * This routine is used to handle the "bottom half" processing for the
- + * serial driver, known also the "software interrupt" processing.
- + * This processing is done at the kernel interrupt level, after the
- + * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
- + * is where time-consuming activities which can not be done in the
- + * interrupt driver proper are done; the interrupt driver schedules
- + * them using rs_sched_event(), and they get done here.
- + */
- +static void do_serial_bh(void *unused)
- +{
- + run_task_queue(&tq_serial);
- +}
- +
- +static void do_softint(void *private_)
- +{
- + struct async_struct *info = (struct async_struct *) private_;
- + struct tty_struct *tty;
- +
- + tty = info->tty;
- + if (!tty)
- + return;
- +
- + if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
- + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- + tty->ldisc.write_wakeup)
- + (tty->ldisc.write_wakeup)(tty);
- + wake_up_interruptible(&tty->write_wait);
- + }
- +}
- +
- +/*
- + * This routine is called from the scheduler tqueue when the interrupt
- + * routine has signalled that a hangup has occurred. The path of
- + * hangup processing is:
- + *
- + * serial interrupt routine -> (scheduler tqueue) ->
- + * do_serial_hangup() -> tty->hangup() -> rs_hangup()
- + *
- + */
- +static void do_serial_hangup(void *private_)
- +{
- + struct async_struct *info = (struct async_struct *) private_;
- + struct tty_struct *tty;
- +
- + tty = info->tty;
- + if (!tty)
- + return;
- +
- + tty_hangup(tty);
- +}
- +
- +
- +/*
- + * This subroutine is called when the RS_TIMER goes off. It is used
- + * by the serial driver to handle ports that do not have an interrupt
- + * (irq=0). This doesn't work very well for 16450's, but gives barely
- + * passable results for a 16550A. (Although at the expense of much
- + * CPU overhead).
- + */
- +static void rs_timer(void)
- +{
- + static unsigned long last_strobe = 0;
- + struct async_struct *info;
- + unsigned int i;
- +
- + if ((jiffies - last_strobe) >= RS_STROBE_TIME) {
- + for (i=1; i < 16; i++) {
- + info = IRQ_ports[i];
- + if (!info)
- + continue;
- + cli();
- + if (info->next_port) {
- + do {
- + serial_out(info, UART_IER, 0);
- + info->IER |= UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + info = info->next_port;
- + } while (info);
- + if (rs_multiport[i].port1)
- + rs_interrupt_multi(i, NULL);
- + else
- + rs_interrupt(i, NULL);
- + } else
- + rs_interrupt_single(i, NULL);
- + sti();
- + }
- + }
- + last_strobe = jiffies;
- + timer_table[RS_TIMER].expires = jiffies + RS_STROBE_TIME;
- + timer_active |= 1 << RS_TIMER;
- +
- + if (IRQ_ports[0]) {
- + cli();
- + rs_interrupt(0, NULL);
- + sti();
- +
- + timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0] - 2;
- + }
- +}
- +
- +/*
- + * ---------------------------------------------------------------
- + * Low level utility subroutines for the serial driver: routines to
- + * figure out the appropriate timeout for an interrupt chain, routines
- + * to initialize and startup a serial port, and routines to shutdown a
- + * serial port. Useful stuff like that.
- + * ---------------------------------------------------------------
- + */
- +
- +/*
- + * Grab all interrupts in preparation for doing an automatic irq
- + * detection. dontgrab is a mask of irq's _not_ to grab. Returns a
- + * mask of irq's which were grabbed and should therefore be freed
- + * using free_all_interrupts().
- + */
- +static int grab_all_interrupts(int dontgrab)
- +{
- + int irq_lines = 0;
- + int i, mask;
- +
- + for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
- + if (!(mask & dontgrab) && !request_irq(i, rs_probe, SA_INTERRUPT, "serial probe")) {
- + irq_lines |= mask;
- + }
- + }
- + return irq_lines;
- +}
- +
- +/*
- + * Release all interrupts grabbed by grab_all_interrupts
- + */
- +static void free_all_interrupts(int irq_lines)
- +{
- + int i;
- +
- + for (i = 0; i < 16; i++) {
- + if (irq_lines & (1 << i))
- + free_irq(i);
- + }
- +}
- +
- +/*
- + * This routine figures out the correct timeout for a particular IRQ.
- + * It uses the smallest timeout of all of the serial ports in a
- + * particular interrupt chain. Now only used for IRQ 0....
- + */
- +static void figure_IRQ_timeout(int irq)
- +{
- + struct async_struct *info;
- + int timeout = 60*HZ; /* 60 seconds === a long time :-) */
- +
- + info = IRQ_ports[irq];
- + if (!info) {
- + IRQ_timeout[irq] = 60*HZ;
- + return;
- + }
- + while (info) {
- + if (info->timeout < timeout)
- + timeout = info->timeout;
- + info = info->next_port;
- + }
- + if (!irq)
- + timeout = timeout / 2;
- + IRQ_timeout[irq] = timeout ? timeout : 1;
- +}
- +
- +static int startup(struct async_struct * info)
- +{
- + unsigned short ICP;
- + unsigned long flags;
- + int retval;
- + void (*handler)(int, struct pt_regs *);
- +
- + if (info->flags & ASYNC_INITIALIZED)
- + return 0;
- +
- + if (!info->port || !info->type) {
- + if (info->tty)
- + set_bit(TTY_IO_ERROR, &info->tty->flags);
- + return 0;
- + }
- +
- + if (!info->xmit_buf) {
- + info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
- + if (!info->xmit_buf)
- + return -ENOMEM;
- + }
- +
- + save_flags(flags); cli();
- +
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("starting up ttys%d (irq %d)...", info->line, info->irq);
- +#endif
- +
- + /*
- + * Clear the FIFO buffers and disable them
- + * (they will be reenabled in change_speed())
- + */
- + if (info->type == PORT_16650) {
- + serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
- + UART_FCR_CLEAR_XMIT));
- + info->xmit_fifo_size = 1; /* disabled for now */
- + } else if (info->type == PORT_16550A) {
- + serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
- + UART_FCR_CLEAR_XMIT));
- + info->xmit_fifo_size = 16;
- + } else
- + info->xmit_fifo_size = 1;
- +
- + /*
- + * At this point there's no way the LSR could still be 0xFF;
- + * if it is, then bail out, because there's likely no UART
- + * here.
- + */
- + if (serial_inp(info, UART_LSR) == 0xff) {
- + restore_flags(flags);
- + if (suser()) {
- + if (info->tty)
- + set_bit(TTY_IO_ERROR, &info->tty->flags);
- + return 0;
- + } else
- + return -ENODEV;
- + }
- +
- + /*
- + * Allocate the IRQ if necessary
- + */
- + if (info->irq && (!IRQ_ports[info->irq] ||
- + !IRQ_ports[info->irq]->next_port)) {
- + if (IRQ_ports[info->irq]) {
- + free_irq(info->irq);
- + if (rs_multiport[info->irq].port1)
- + handler = rs_interrupt_multi;
- + else
- + handler = rs_interrupt;
- + } else
- + handler = rs_interrupt_single;
- +
- + retval = request_irq(info->irq, handler, SA_INTERRUPT, "serial");
- + if (retval) {
- + restore_flags(flags);
- + if (suser()) {
- + if (info->tty)
- + set_bit(TTY_IO_ERROR,
- + &info->tty->flags);
- + return 0;
- + } else
- + return retval;
- + }
- + }
- +
- + /*
- + * Clear the interrupt registers.
- + */
- + /* (void) serial_inp(info, UART_LSR); */ /* (see above) */
- + (void) serial_inp(info, UART_RX);
- + (void) serial_inp(info, UART_IIR);
- + (void) serial_inp(info, UART_MSR);
- +
- + /*
- + * Now, initialize the UART
- + */
- + serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */
- + if (info->flags & ASYNC_FOURPORT) {
- + info->MCR = UART_MCR_DTR | UART_MCR_RTS;
- + info->MCR_noint = UART_MCR_DTR | UART_MCR_OUT1;
- + } else {
- + info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
- + info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS;
- + }
- +#if defined(__alpha__) && !defined (CONFIG_PCI)
- + info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
- + info->MCR_noint |= UART_MCR_OUT1 | UART_MCR_OUT2;
- +#endif
- + if (info->irq == 0)
- + info->MCR = info->MCR_noint;
- + serial_outp(info, UART_MCR, info->MCR);
- +
- + /*
- + * Finally, enable interrupts
- + */
- + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
- + serial_outp(info, UART_IER, info->IER); /* enable interrupts */
- +
- + if (info->flags & ASYNC_FOURPORT) {
- + /* Enable interrupts on the AST Fourport board */
- + ICP = (info->port & 0xFE0) | 0x01F;
- + outb_p(0x80, ICP);
- + (void) inb_p(ICP);
- + }
- +
- + /*
- + * And clear the interrupt registers again for luck.
- + */
- + (void)serial_inp(info, UART_LSR);
- + (void)serial_inp(info, UART_RX);
- + (void)serial_inp(info, UART_IIR);
- + (void)serial_inp(info, UART_MSR);
- +
- + if (info->tty)
- + clear_bit(TTY_IO_ERROR, &info->tty->flags);
- + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- +
- + /*
- + * Insert serial port into IRQ chain.
- + */
- + info->prev_port = 0;
- + info->next_port = IRQ_ports[info->irq];
- + if (info->next_port)
- + info->next_port->prev_port = info;
- + IRQ_ports[info->irq] = info;
- + figure_IRQ_timeout(info->irq);
- +
- + /*
- + * Set up serial timers...
- + */
- + timer_table[RS_TIMER].expires = jiffies + 2*HZ/100;
- + timer_active |= 1 << RS_TIMER;
- +
- + /*
- + * and set the speed of the serial port
- + */
- + change_speed(info);
- +
- + info->flags |= ASYNC_INITIALIZED;
- + restore_flags(flags);
- + return 0;
- +}
- +
- +/*
- + * This routine will shutdown a serial port; interrupts are disabled, and
- + * DTR is dropped if the hangup on close termio flag is on.
- + */
- +static void shutdown(struct async_struct * info)
- +{
- + unsigned long flags;
- + int retval;
- +
- + if (!(info->flags & ASYNC_INITIALIZED))
- + return;
- +
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("Shutting down serial port %d (irq %d)....", info->line,
- + info->irq);
- +#endif
- +
- + save_flags(flags); cli(); /* Disable interrupts */
- +
- + /*
- + * First unlink the serial port from the IRQ chain...
- + */
- + if (info->next_port)
- + info->next_port->prev_port = info->prev_port;
- + if (info->prev_port)
- + info->prev_port->next_port = info->next_port;
- + else
- + IRQ_ports[info->irq] = info->next_port;
- + figure_IRQ_timeout(info->irq);
- +
- + /*
- + * Free the IRQ, if necessary
- + */
- + if (info->irq && (!IRQ_ports[info->irq] ||
- + !IRQ_ports[info->irq]->next_port)) {
- + if (IRQ_ports[info->irq]) {
- + free_irq(info->irq);
- + retval = request_irq(info->irq, rs_interrupt_single, SA_INTERRUPT, "serial");
- +
- + if (retval)
- + printk("serial shutdown: request_irq: error %d"
- + " Couldn't reacquire IRQ.\n", retval);
- + } else
- + free_irq(info->irq);
- + }
- +
- + if (info->xmit_buf) {
- + free_page((unsigned long) info->xmit_buf);
- + info->xmit_buf = 0;
- + }
- +
- + 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)) {
- + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
- + info->MCR_noint &= ~(UART_MCR_DTR|UART_MCR_RTS);
- + }
- + serial_outp(info, UART_MCR, info->MCR_noint);
- +
- + /* disable FIFO's */
- + serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
- + UART_FCR_CLEAR_XMIT));
- + (void)serial_in(info, UART_RX); /* read data port to reset things */
- +
- + if (info->tty)
- + set_bit(TTY_IO_ERROR, &info->tty->flags);
- +
- + info->flags &= ~ASYNC_INITIALIZED;
- + restore_flags(flags);
- +}
- +
- +/*
- + * This routine is called to set the UART divisor registers to match
- + * the specified baud rate for a serial port.
- + */
- +static void change_speed(struct async_struct *info)
- +{
- + unsigned short port;
- + int quot = 0;
- + unsigned cflag,cval,fcr;
- + int i;
- +
- + if (!info->tty || !info->tty->termios)
- + return;
- + cflag = info->tty->termios->c_cflag;
- + if (!(port = info->port))
- + return;
- + i = cflag & CBAUD;
- + if (i & CBAUDEX) {
- + i &= ~CBAUDEX;
- + if (i < 1 || i > 2)
- + info->tty->termios->c_cflag &= ~CBAUDEX;
- + else
- + i += 15;
- + }
- + if (i == 15) {
- + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- + i += 1;
- + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- + i += 2;
- + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
- + quot = info->custom_divisor;
- + }
- + if (quot) {
- + info->timeout = ((info->xmit_fifo_size*HZ*15*quot) /
- + info->baud_base) + 2;
- + } else if (baud_table[i] == 134) {
- + quot = (2*info->baud_base / 269);
- + info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
- + } else if (baud_table[i]) {
- + quot = info->baud_base / baud_table[i];
- + info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
- + } else {
- + quot = 0;
- + info->timeout = 0;
- + }
- + if (quot) {
- + info->MCR |= UART_MCR_DTR;
- + info->MCR_noint |= UART_MCR_DTR;
- + cli();
- + serial_out(info, UART_MCR, info->MCR);
- + sti();
- + } else {
- + info->MCR &= ~UART_MCR_DTR;
- + info->MCR_noint &= ~UART_MCR_DTR;
- + cli();
- + serial_out(info, UART_MCR, info->MCR);
- + sti();
- + return;
- + }
- + /* byte size and parity */
- + switch (cflag & CSIZE) {
- + case CS5: cval = 0x00; break;
- + case CS6: cval = 0x01; break;
- + case CS7: cval = 0x02; break;
- + case CS8: cval = 0x03; break;
- + default: cval = 0x00; break; /* too keep GCC shut... */
- + }
- + if (cflag & CSTOPB) {
- + cval |= 0x04;
- + }
- + if (cflag & PARENB)
- + cval |= UART_LCR_PARITY;
- + if (!(cflag & PARODD))
- + cval |= UART_LCR_EPAR;
- + if (info->type == PORT_16550A) {
- + if ((info->baud_base / quot) < 2400)
- + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
- + else
- + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
- + } else if (info->type == PORT_16650) {
- + /*
- + * On the 16650, we disable the FIFOs altogether
- + * because of a design bug in how the implement
- + * things. We could support it by completely changing
- + * how we handle the interrupt driver, but not today....
- + *
- + * N.B. Because there's no way to set a FIFO trigger
- + * at 1 char, we'd probably disable at speed below
- + * 2400 baud anyway...
- + */
- + fcr = 0;
- + } else
- + fcr = 0;
- +
- + /* CTS flow control flag and modem status interrupts */
- + info->IER &= ~UART_IER_MSI;
- + if (cflag & CRTSCTS) {
- + info->flags |= ASYNC_CTS_FLOW;
- + info->IER |= UART_IER_MSI;
- + } else
- + info->flags &= ~ASYNC_CTS_FLOW;
- + if (cflag & CLOCAL)
- + info->flags &= ~ASYNC_CHECK_CD;
- + else {
- + info->flags |= ASYNC_CHECK_CD;
- + info->IER |= UART_IER_MSI;
- + }
- + serial_out(info, UART_IER, info->IER);
- +
- + /*
- + * Set up parity check flag
- + */
- + info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
- + if (I_INPCK(info->tty))
- + info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
- + if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
- + info->read_status_mask |= UART_LSR_BI;
- +
- + info->ignore_status_mask = 0;
- + if (I_IGNPAR(info->tty)) {
- + info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
- + info->read_status_mask |= UART_LSR_PE | UART_LSR_FE;
- + }
- + if (I_IGNBRK(info->tty)) {
- + info->ignore_status_mask |= UART_LSR_BI;
- + info->read_status_mask |= UART_LSR_BI;
- + /*
- + * If we're ignore parity and break indicators, ignore
- + * overruns too. (For real raw support).
- + */
- + if (I_IGNPAR(info->tty)) {
- + info->ignore_status_mask |= UART_LSR_OE;
- + info->read_status_mask |= UART_LSR_OE;
- + }
- + }
- +
- + cli();
- + serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
- + serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */
- + serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */
- + serial_outp(info, UART_LCR, cval); /* reset DLAB */
- + serial_outp(info, UART_FCR, fcr); /* set fcr */
- + sti();
- +}
- +
- +static void rs_put_char(struct tty_struct *tty, unsigned char ch)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- + unsigned long flags;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_put_char"))
- + return;
- +
- + if (!tty || !info->xmit_buf)
- + return;
- +
- + save_flags(flags); cli();
- + if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
- + restore_flags(flags);
- + return;
- + }
- +
- + info->xmit_buf[info->xmit_head++] = ch;
- + info->xmit_head &= SERIAL_XMIT_SIZE-1;
- + info->xmit_cnt++;
- + restore_flags(flags);
- +}
- +
- +static void rs_flush_chars(struct tty_struct *tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- + unsigned long flags;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
- + return;
- +
- + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
- + !info->xmit_buf)
- + return;
- +
- + save_flags(flags); cli();
- + info->IER |= UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + restore_flags(flags);
- +}
- +
- +static int rs_write(struct tty_struct * tty, int from_user,
- + const unsigned char *buf, int count)
- +{
- + int c, total = 0;
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- + unsigned long flags;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_write"))
- + return 0;
- +
- + if (!tty || !info->xmit_buf || !tmp_buf)
- + return 0;
- +
- + save_flags(flags);
- + while (1) {
- + cli();
- + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
- + SERIAL_XMIT_SIZE - info->xmit_head));
- + if (c <= 0)
- + break;
- +
- + if (from_user) {
- + down(&tmp_buf_sem);
- + memcpy_fromfs(tmp_buf, buf, c);
- + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
- + SERIAL_XMIT_SIZE - info->xmit_head));
- + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
- + up(&tmp_buf_sem);
- + } else
- + memcpy(info->xmit_buf + info->xmit_head, buf, c);
- + info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
- + info->xmit_cnt += c;
- + restore_flags(flags);
- + buf += c;
- + count -= c;
- + total += c;
- + }
- + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
- + !(info->IER & UART_IER_THRI)) {
- + info->IER |= UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + }
- + restore_flags(flags);
- + return total;
- +}
- +
- +static int rs_write_room(struct tty_struct *tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- + int ret;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_write_room"))
- + return 0;
- + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
- + if (ret < 0)
- + ret = 0;
- + return ret;
- +}
- +
- +static int rs_chars_in_buffer(struct tty_struct *tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
- + return 0;
- + return info->xmit_cnt;
- +}
- +
- +static void rs_flush_buffer(struct tty_struct *tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
- + return;
- + cli();
- + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- + sti();
- + wake_up_interruptible(&tty->write_wait);
- + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- + tty->ldisc.write_wakeup)
- + (tty->ldisc.write_wakeup)(tty);
- +}
- +
- +/*
- + * ------------------------------------------------------------
- + * rs_throttle()
- + *
- + * This routine is called by the upper-layer tty layer to signal that
- + * incoming characters should be throttled.
- + * ------------------------------------------------------------
- + */
- +static void rs_throttle(struct tty_struct * tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- +#ifdef SERIAL_DEBUG_THROTTLE
- + char buf[64];
- +
- + printk("throttle %s: %d....\n", _tty_name(tty, buf),
- + tty->ldisc.chars_in_buffer(tty));
- +#endif
- +
- + if (serial_paranoia_check(info, tty->device, "rs_throttle"))
- + return;
- +
- + if (I_IXOFF(tty))
- + info->x_char = STOP_CHAR(tty);
- +
- + info->MCR &= ~UART_MCR_RTS;
- + info->MCR_noint &= ~UART_MCR_RTS;
- + cli();
- + serial_out(info, UART_MCR, info->MCR);
- + sti();
- +}
- +
- +static void rs_unthrottle(struct tty_struct * tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- +#ifdef SERIAL_DEBUG_THROTTLE
- + char buf[64];
- +
- + printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
- + tty->ldisc.chars_in_buffer(tty));
- +#endif
- +
- + if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
- + return;
- +
- + if (I_IXOFF(tty)) {
- + if (info->x_char)
- + info->x_char = 0;
- + else
- + info->x_char = START_CHAR(tty);
- + }
- + info->MCR |= UART_MCR_RTS;
- + info->MCR_noint |= UART_MCR_RTS;
- + cli();
- + serial_out(info, UART_MCR, info->MCR);
- + sti();
- +}
- +
- +/*
- + * ------------------------------------------------------------
- + * rs_ioctl() and friends
- + * ------------------------------------------------------------
- + */
- +
- +static int get_serial_info(struct async_struct * info,
- + struct serial_struct * retinfo)
- +{
- + struct serial_struct tmp;
- +
- + if (!retinfo)
- + return -EFAULT;
- + memset(&tmp, 0, sizeof(tmp));
- + tmp.type = info->type;
- + tmp.line = info->line;
- + tmp.port = info->port;
- + tmp.irq = info->irq;
- + tmp.flags = info->flags;
- + tmp.baud_base = info->baud_base;
- + tmp.close_delay = info->close_delay;
- + tmp.closing_wait = info->closing_wait;
- + tmp.custom_divisor = info->custom_divisor;
- + tmp.hub6 = info->hub6;
- + memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
- + return 0;
- +}
- +
- +static int set_serial_info(struct async_struct * info,
- + struct serial_struct * new_info)
- +{
- + struct serial_struct new_serial;
- + struct async_struct old_info;
- + unsigned int i,change_irq,change_port;
- + int retval = 0;
- +
- + if (!new_info)
- + return -EFAULT;
- + memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));
- + old_info = *info;
- +
- + change_irq = new_serial.irq != info->irq;
- + change_port = (new_serial.port != info->port) || (new_serial.hub6 != info->hub6);
- +
- + if (!suser()) {
- + if (change_irq || change_port ||
- + (new_serial.baud_base != info->baud_base) ||
- + (new_serial.type != info->type) ||
- + (new_serial.close_delay != info->close_delay) ||
- + ((new_serial.flags & ~ASYNC_USR_MASK) !=
- + (info->flags & ~ASYNC_USR_MASK)))
- + return -EPERM;
- + info->flags = ((info->flags & ~ASYNC_USR_MASK) |
- + (new_serial.flags & ASYNC_USR_MASK));
- + info->custom_divisor = new_serial.custom_divisor;
- + goto check_and_exit;
- + }
- +
- + if (new_serial.irq == 2)
- + new_serial.irq = 9;
- +
- + if ((new_serial.irq > 15) || (new_serial.port > 0xffff) ||
- + (new_serial.type < PORT_UNKNOWN) || (new_serial.type > PORT_MAX)) {
- + return -EINVAL;
- + }
- +
- + /* Make sure address is not already in use */
- + if (new_serial.type) {
- + for (i = 0 ; i < NR_PORTS; i++)
- + if ((info != &rs_table[i]) &&
- + (rs_table[i].port == new_serial.port) &&
- + rs_table[i].type)
- + return -EADDRINUSE;
- + }
- +
- + if ((change_port || change_irq) && (info->count > 1))
- + return -EBUSY;
- +
- + /*
- + * OK, past this point, all the error checking has been done.
- + * At this point, we start making changes.....
- + */
- +
- + info->baud_base = new_serial.baud_base;
- + info->flags = ((info->flags & ~ASYNC_FLAGS) |
- + (new_serial.flags & ASYNC_FLAGS));
- + info->custom_divisor = new_serial.custom_divisor;
- + info->type = new_serial.type;
- + info->close_delay = new_serial.close_delay * HZ/100;
- + info->closing_wait = new_serial.closing_wait * HZ/100;
- +
- + release_region(info->port,8);
- + if (change_port || change_irq) {
- + /*
- + * We need to shutdown the serial port at the old
- + * port/irq combination.
- + */
- + shutdown(info);
- + info->irq = new_serial.irq;
- + info->port = new_serial.port;
- + info->hub6 = new_serial.hub6;
- + }
- + if(info->type != PORT_UNKNOWN)
- + request_region(info->port,8,"serial(set)");
- +
- +
- +check_and_exit:
- + if (!info->port || !info->type)
- + return 0;
- + if (info->flags & ASYNC_INITIALIZED) {
- + if (((old_info.flags & ASYNC_SPD_MASK) !=
- + (info->flags & ASYNC_SPD_MASK)) ||
- + (old_info.custom_divisor != info->custom_divisor))
- + change_speed(info);
- + } else
- + retval = startup(info);
- + return retval;
- +}
- +
- +
- +/*
- + * get_lsr_info - get line status register info
- + *
- + * Purpose: Let user call ioctl() to get info when the UART physically
- + * is emptied. On bus types like RS485, the transmitter must
- + * release the bus after transmitting. This must be done when
- + * the transmit shift register is empty, not be done when the
- + * transmit holding register is empty. This functionality
- + * allows RS485 driver to be written in user space.
- + */
- +static int get_lsr_info(struct async_struct * info, unsigned int *value)
- +{
- + unsigned char status;
- + unsigned int result;
- +
- + cli();
- + status = serial_in(info, UART_LSR);
- + sti();
- + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
- + put_user(result,value);
- + return 0;
- +}
- +
- +
- +static int get_modem_info(struct async_struct * info, unsigned int *value)
- +{
- + unsigned char control, status;
- + unsigned int result;
- +
- + control = info->MCR;
- + cli();
- + 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)
- + | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
- + | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
- + | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
- + put_user(result,value);
- + return 0;
- +}
- +
- +static int set_modem_info(struct async_struct * info, unsigned int cmd,
- + unsigned int *value)
- +{
- + int error;
- + unsigned int arg;
- +
- + error = verify_area(VERIFY_READ, value, sizeof(int));
- + if (error)
- + return error;
- + arg = get_user(value);
- + switch (cmd) {
- + case TIOCMBIS:
- + if (arg & TIOCM_RTS) {
- + info->MCR |= UART_MCR_RTS;
- + info->MCR_noint |= UART_MCR_RTS;
- + }
- + if (arg & TIOCM_DTR) {
- + info->MCR |= UART_MCR_DTR;
- + info->MCR_noint |= UART_MCR_DTR;
- + }
- + break;
- + case TIOCMBIC:
- + if (arg & TIOCM_RTS) {
- + info->MCR &= ~UART_MCR_RTS;
- + info->MCR_noint &= ~UART_MCR_RTS;
- + }
- + if (arg & TIOCM_DTR) {
- + info->MCR &= ~UART_MCR_DTR;
- + info->MCR_noint &= ~UART_MCR_DTR;
- + }
- + break;
- + case TIOCMSET:
- + info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR))
- + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
- + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
- + info->MCR_noint = ((info->MCR_noint
- + & ~(UART_MCR_RTS | UART_MCR_DTR))
- + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
- + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
- + break;
- + default:
- + return -EINVAL;
- + }
- + cli();
- + serial_out(info, UART_MCR, info->MCR);
- + sti();
- + return 0;
- +}
- +
- +static int do_autoconfig(struct async_struct * info)
- +{
- + int retval;
- +
- + if (!suser())
- + return -EPERM;
- +
- + if (info->count > 1)
- + return -EBUSY;
- +
- + shutdown(info);
- +
- + cli();
- + autoconfig(info);
- + sti();
- +
- + retval = startup(info);
- + if (retval)
- + return retval;
- + return 0;
- +}
- +
- +
- +/*
- + * This routine sends a break character out the serial port.
- + */
- +static void send_break( struct async_struct * info, int duration)
- +{
- + if (!info->port)
- + 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();
- +}
- +
- +/*
- + * This routine returns a bitfield of "wild interrupts". Basically,
- + * any unclaimed interrupts which is flapping around.
- + */
- +static int check_wild_interrupts(int doprint)
- +{
- + int i, mask;
- + int wild_interrupts = 0;
- + int irq_lines;
- + unsigned long timeout;
- + unsigned long flags;
- +
- + /* Turn on interrupts (they may be off) */
- + save_flags(flags); sti();
- +
- + irq_lines = grab_all_interrupts(0);
- +
- + /*
- + * Delay for 0.1 seconds -- we use a busy loop since this may
- + * occur during the bootup sequence
- + */
- + timeout = jiffies+HZ/10;
- + while (timeout >= jiffies)
- + ;
- +
- + rs_triggered = 0; /* Reset after letting things settle */
- +
- + timeout = jiffies+HZ/10;
- + while (timeout >= jiffies)
- + ;
- +
- + for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
- + if ((rs_triggered & (1 << i)) &&
- + (irq_lines & (1 << i))) {
- + wild_interrupts |= mask;
- + if (doprint)
- + printk("Wild interrupt? (IRQ %d)\n", i);
- + }
- + }
- + free_all_interrupts(irq_lines);
- + restore_flags(flags);
- + return wild_interrupts;
- +}
- +
- +static int get_multiport_struct(struct async_struct * info,
- + struct serial_multiport_struct *retinfo)
- +{
- + struct serial_multiport_struct ret;
- + struct rs_multiport_struct *multi;
- +
- + multi = &rs_multiport[info->irq];
- +
- + ret.port_monitor = multi->port_monitor;
- +
- + ret.port1 = multi->port1;
- + ret.mask1 = multi->mask1;
- + ret.match1 = multi->match1;
- +
- + ret.port2 = multi->port2;
- + ret.mask2 = multi->mask2;
- + ret.match2 = multi->match2;
- +
- + ret.port3 = multi->port3;
- + ret.mask3 = multi->mask3;
- + ret.match3 = multi->match3;
- +
- + ret.port4 = multi->port4;
- + ret.mask4 = multi->mask4;
- + ret.match4 = multi->match4;
- +
- + ret.irq = info->irq;
- +
- + memcpy_tofs(retinfo,&ret,sizeof(*retinfo));
- + return 0;
- +
- +}
- +
- +static int set_multiport_struct(struct async_struct * info,
- + struct serial_multiport_struct *in_multi)
- +{
- + struct serial_multiport_struct new_multi;
- + struct rs_multiport_struct *multi;
- + int was_multi, now_multi;
- + int retval;
- + void (*handler)(int, struct pt_regs *);
- +
- + if (!suser())
- + return -EPERM;
- + if (!in_multi)
- + return -EFAULT;
- + memcpy_fromfs(&new_multi, in_multi,
- + sizeof(struct serial_multiport_struct));
- +
- + if (new_multi.irq != info->irq || info->irq == 0 ||
- + !IRQ_ports[info->irq])
- + return -EINVAL;
- +
- + multi = &rs_multiport[info->irq];
- + was_multi = (multi->port1 != 0);
- +
- + multi->port_monitor = new_multi.port_monitor;
- +
- + if (multi->port1)
- + release_region(multi->port1,1);
- + multi->port1 = new_multi.port1;
- + multi->mask1 = new_multi.mask1;
- + multi->match1 = new_multi.match1;
- + if (multi->port1)
- + request_region(multi->port1,1,"serial(multiport1)");
- +
- + if (multi->port2)
- + release_region(multi->port2,1);
- + multi->port2 = new_multi.port2;
- + multi->mask2 = new_multi.mask2;
- + multi->match2 = new_multi.match2;
- + if (multi->port2)
- + request_region(multi->port2,1,"serial(multiport2)");
- +
- + if (multi->port3)
- + release_region(multi->port3,1);
- + multi->port3 = new_multi.port3;
- + multi->mask3 = new_multi.mask3;
- + multi->match3 = new_multi.match3;
- + if (multi->port3)
- + request_region(multi->port3,1,"serial(multiport3)");
- +
- + if (multi->port4)
- + release_region(multi->port4,1);
- + multi->port4 = new_multi.port4;
- + multi->mask4 = new_multi.mask4;
- + multi->match4 = new_multi.match4;
- + if (multi->port4)
- + request_region(multi->port4,1,"serial(multiport4)");
- +
- + now_multi = (multi->port1 != 0);
- +
- + if (IRQ_ports[info->irq]->next_port &&
- + (was_multi != now_multi)) {
- + free_irq(info->irq);
- + if (now_multi)
- + handler = rs_interrupt_multi;
- + else
- + handler = rs_interrupt;
- +
- + retval = request_irq(info->irq, handler, SA_INTERRUPT,
- + "serial");
- + if (retval) {
- + printk("Couldn't reallocate serial interrupt "
- + "driver!!\n");
- + }
- + }
- +
- + return 0;
- +}
- +
- +static int rs_ioctl(struct tty_struct *tty, struct file * file,
- + unsigned int cmd, unsigned long arg)
- +{
- + int error;
- + struct async_struct * info = (struct async_struct *)tty->driver_data;
- + int retval;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
- + return -ENODEV;
- +
- + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
- + (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
- + if (tty->flags & (1 << TTY_IO_ERROR))
- + return -EIO;
- + }
- +
- + switch (cmd) {
- + case TCSBRK: /* SVID version: non-zero arg --> no break */
- + retval = tty_check_change(tty);
- + if (retval)
- + return retval;
- + tty_wait_until_sent(tty, 0);
- + if (!arg)
- + send_break(info, HZ/4); /* 1/4 second */
- + return 0;
- + case TCSBRKP: /* support for POSIX tcsendbreak() */
- + retval = tty_check_change(tty);
- + if (retval)
- + return retval;
- + tty_wait_until_sent(tty, 0);
- + send_break(info, arg ? arg*(HZ/10) : HZ/4);
- + return 0;
- + case TIOCGSOFTCAR:
- + error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long));
- + if (error)
- + return error;
- + put_fs_long(C_CLOCAL(tty) ? 1 : 0,
- + (unsigned long *) arg);
- + return 0;
- + case TIOCSSOFTCAR:
- + arg = get_fs_long((unsigned long *) arg);
- + tty->termios->c_cflag =
- + ((tty->termios->c_cflag & ~CLOCAL) |
- + (arg ? CLOCAL : 0));
- + return 0;
- + case TIOCMGET:
- + error = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(unsigned int));
- + if (error)
- + return error;
- + return get_modem_info(info, (unsigned int *) arg);
- + case TIOCMBIS:
- + case TIOCMBIC:
- + case TIOCMSET:
- + return set_modem_info(info, cmd, (unsigned int *) arg);
- + case TIOCGSERIAL:
- + error = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(struct serial_struct));
- + if (error)
- + return error;
- + return get_serial_info(info,
- + (struct serial_struct *) arg);
- + case TIOCSSERIAL:
- + return set_serial_info(info,
- + (struct serial_struct *) arg);
- + case TIOCSERCONFIG:
- + return do_autoconfig(info);
- +
- + case TIOCSERGWILD:
- + error = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(int));
- + if (error)
- + return error;
- + put_fs_long(rs_wild_int_mask, (unsigned long *) arg);
- + return 0;
- +
- + case TIOCSERGETLSR: /* Get line status register */
- + error = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(unsigned int));
- + if (error)
- + return error;
- + else
- + return get_lsr_info(info, (unsigned int *) arg);
- +
- + case TIOCSERSWILD:
- + if (!suser())
- + return -EPERM;
- + rs_wild_int_mask = get_fs_long((unsigned long *) arg);
- + if (rs_wild_int_mask < 0)
- + rs_wild_int_mask = check_wild_interrupts(0);
- + return 0;
- +
- + case TIOCSERGSTRUCT:
- + error = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(struct async_struct));
- + if (error)
- + return error;
- + memcpy_tofs((struct async_struct *) arg,
- + info, sizeof(struct async_struct));
- + return 0;
- +
- + case TIOCSERGETMULTI:
- + error = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(struct serial_multiport_struct));
- + if (error)
- + return error;
- + return get_multiport_struct(info,
- + (struct serial_multiport_struct *) arg);
- + case TIOCSERSETMULTI:
- + return set_multiport_struct(info,
- + (struct serial_multiport_struct *) arg);
- + default:
- + return -ENOIOCTLCMD;
- + }
- + return 0;
- +}
- +
- +static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- +
- + if (tty->termios->c_cflag == old_termios->c_cflag)
- + return;
- +
- + change_speed(info);
- +
- + if ((old_termios->c_cflag & CRTSCTS) &&
- + !(tty->termios->c_cflag & CRTSCTS)) {
- + tty->hw_stopped = 0;
- + rs_start(tty);
- + }
- +
- +#if 0
- + /*
- + * No need to wake up processes in open wait, since they
- + * sample the CLOCAL flag once, and don't recheck it.
- + * XXX It's not clear whether the current behavior is correct
- + * or not. Hence, this may change.....
- + */
- + if (!(old_termios->c_cflag & CLOCAL) &&
- + (tty->termios->c_cflag & CLOCAL))
- + wake_up_interruptible(&info->open_wait);
- +#endif
- +}
- +
- +/*
- + * ------------------------------------------------------------
- + * rs_close()
- + *
- + * This routine is called when the serial port gets closed. First, we
- + * wait for the last remaining data to be sent. Then, we unlink its
- + * async structure from the interrupt chain if necessary, and we free
- + * that IRQ if nothing is left in the chain.
- + * ------------------------------------------------------------
- + */
- +static void rs_close(struct tty_struct *tty, struct file * filp)
- +{
- + struct async_struct * info = (struct async_struct *)tty->driver_data;
- + unsigned long flags;
- + unsigned long timeout;
- +
- + if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
- + return;
- +
- + save_flags(flags); cli();
- +
- + if (tty_hung_up_p(filp)) {
- + restore_flags(flags);
- + return;
- + }
- +
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("rs_close ttys%d, count = %d\n", info->line, info->count);
- +#endif
- + if ((tty->count == 1) && (info->count != 1)) {
- + /*
- + * Uh, oh. tty->count is 1, which means that the tty
- + * structure will be freed. Info->count should always
- + * be one in these conditions. If it's greater than
- + * one, we've got real problems, since it means the
- + * serial port won't be shutdown.
- + */
- + printk("rs_close: bad serial port count; tty->count is 1, "
- + "info->count is %d\n", info->count);
- + info->count = 1;
- + }
- + if (--info->count < 0) {
- + printk("rs_close: bad serial port count for ttys%d: %d\n",
- + info->line, info->count);
- + info->count = 0;
- + }
- + if (info->count) {
- + restore_flags(flags);
- + return;
- + }
- + info->flags |= ASYNC_CLOSING;
- + /*
- + * Save the termios structure, since this port may have
- + * separate termios for callout and dialin.
- + */
- + if (info->flags & ASYNC_NORMAL_ACTIVE)
- + info->normal_termios = *tty->termios;
- + if (info->flags & ASYNC_CALLOUT_ACTIVE)
- + info->callout_termios = *tty->termios;
- + /*
- + * Now we wait for the transmit buffer to clear; and we notify
- + * the line discipline to only process XON/XOFF characters.
- + */
- + tty->closing = 1;
- + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
- + tty_wait_until_sent(tty, info->closing_wait);
- + /*
- + * At this point we stop accepting input. To do this, we
- + * disable the receive line status interrupts, and tell the
- + * interrupt driver to stop checking the data ready bit in the
- + * line status register.
- + */
- + info->IER &= ~UART_IER_RLSI;
- + info->read_status_mask &= ~UART_LSR_DR;
- + if (info->flags & ASYNC_INITIALIZED) {
- + serial_out(info, UART_IER, info->IER);
- + /*
- + * Before we drop DTR, make sure the UART transmitter
- + * has completely drained; this is especially
- + * important if there is a transmit FIFO!
- + */
- + timeout = jiffies+HZ;
- + while (!(serial_inp(info, UART_LSR) & UART_LSR_TEMT)) {
- + current->state = TASK_INTERRUPTIBLE;
- + current->timeout = jiffies + info->timeout;
- + schedule();
- + if (jiffies > timeout)
- + break;
- + }
- + }
- + shutdown(info);
- + if (tty->driver.flush_buffer)
- + tty->driver.flush_buffer(tty);
- + if (tty->ldisc.flush_buffer)
- + tty->ldisc.flush_buffer(tty);
- + tty->closing = 0;
- + info->event = 0;
- + info->tty = 0;
- + if (tty->ldisc.num != ldiscs[N_TTY].num) {
- + if (tty->ldisc.close)
- + (tty->ldisc.close)(tty);
- + tty->ldisc = ldiscs[N_TTY];
- + tty->termios->c_line = N_TTY;
- + if (tty->ldisc.open)
- + (tty->ldisc.open)(tty);
- + }
- + if (info->blocked_open) {
- + if (info->close_delay) {
- + current->state = TASK_INTERRUPTIBLE;
- + current->timeout = jiffies + info->close_delay;
- + schedule();
- + }
- + wake_up_interruptible(&info->open_wait);
- + }
- + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
- + ASYNC_CLOSING);
- + wake_up_interruptible(&info->close_wait);
- + restore_flags(flags);
- +}
- +
- +/*
- + * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
- + */
- +void rs_hangup(struct tty_struct *tty)
- +{
- + struct async_struct * info = (struct async_struct *)tty->driver_data;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_hangup"))
- + return;
- +
- + rs_flush_buffer(tty);
- + shutdown(info);
- + info->event = 0;
- + info->count = 0;
- + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
- + info->tty = 0;
- + wake_up_interruptible(&info->open_wait);
- +}
- +
- +/*
- + * ------------------------------------------------------------
- + * rs_open() and friends
- + * ------------------------------------------------------------
- + */
- +static int block_til_ready(struct tty_struct *tty, struct file * filp,
- + struct async_struct *info)
- +{
- + struct wait_queue wait = { current, NULL };
- + int retval;
- + int do_clocal = 0;
- +
- + /*
- + * If the device is in the middle of being closed, then block
- + * until it's done, and then try again.
- + */
- + if (info->flags & ASYNC_CLOSING) {
- + interruptible_sleep_on(&info->close_wait);
- +#ifdef SERIAL_DO_RESTART
- + if (info->flags & ASYNC_HUP_NOTIFY)
- + return -EAGAIN;
- + else
- + return -ERESTARTSYS;
- +#else
- + return -EAGAIN;
- +#endif
- + }
- +
- + /*
- + * If this is a callout device, then just make sure the normal
- + * device isn't being used.
- + */
- + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
- + 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;
- + }
- +
- + /*
- + * If non-blocking mode is set, or the port is not enabled,
- + * then make the check up front and then exit.
- + */
- + if ((filp->f_flags & O_NONBLOCK) ||
- + (tty->flags & (1 << TTY_IO_ERROR))) {
- + if (info->flags & ASYNC_CALLOUT_ACTIVE)
- + return -EBUSY;
- + info->flags |= ASYNC_NORMAL_ACTIVE;
- + return 0;
- + }
- +
- + if (info->flags & ASYNC_CALLOUT_ACTIVE) {
- + if (info->normal_termios.c_cflag & CLOCAL)
- + do_clocal = 1;
- + } else {
- + if (tty->termios->c_cflag & CLOCAL)
- + do_clocal = 1;
- + }
- +
- + /*
- + * Block waiting for the carrier detect and the line to become
- + * free (i.e., not in use by the callout). While we are in
- + * this loop, info->count is dropped by one, so that
- + * rs_close() knows when to free things. We restore it upon
- + * exit, either normal or abnormal.
- + */
- + retval = 0;
- + add_wait_queue(&info->open_wait, &wait);
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("block_til_ready before block: ttys%d, count = %d\n",
- + info->line, info->count);
- +#endif
- + info->count--;
- + info->blocked_open++;
- + while (1) {
- + cli();
- + if (!(info->flags & ASYNC_CALLOUT_ACTIVE))
- + serial_out(info, UART_MCR,
- + serial_inp(info, UART_MCR) |
- + (UART_MCR_DTR | UART_MCR_RTS));
- + sti();
- + current->state = TASK_INTERRUPTIBLE;
- + if (tty_hung_up_p(filp) ||
- + !(info->flags & ASYNC_INITIALIZED)) {
- +#ifdef SERIAL_DO_RESTART
- + if (info->flags & ASYNC_HUP_NOTIFY)
- + retval = -EAGAIN;
- + else
- + retval = -ERESTARTSYS;
- +#else
- + retval = -EAGAIN;
- +#endif
- + break;
- + }
- + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
- + !(info->flags & ASYNC_CLOSING) &&
- + (do_clocal || (serial_in(info, UART_MSR) &
- + UART_MSR_DCD)))
- + break;
- + if (current->signal & ~current->blocked) {
- + retval = -ERESTARTSYS;
- + break;
- + }
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("block_til_ready blocking: ttys%d, count = %d\n",
- + info->line, info->count);
- +#endif
- + schedule();
- + }
- + current->state = TASK_RUNNING;
- + remove_wait_queue(&info->open_wait, &wait);
- + if (!tty_hung_up_p(filp))
- + info->count++;
- + info->blocked_open--;
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("block_til_ready after blocking: ttys%d, count = %d\n",
- + info->line, info->count);
- +#endif
- + if (retval)
- + return retval;
- + info->flags |= ASYNC_NORMAL_ACTIVE;
- + return 0;
- +}
- +
- +/*
- + * This routine is called whenever a serial port is opened. It
- + * enables interrupts for a serial port, linking in its async structure into
- + * the IRQ chain. It also performs the serial-specific
- + * initialization for the tty structure.
- + */
- +int rs_open(struct tty_struct *tty, struct file * filp)
- +{
- + struct async_struct *info;
- + int retval, line;
- +
- + line = MINOR(tty->device) - tty->driver.minor_start;
- + if ((line < 0) || (line >= NR_PORTS))
- + return -ENODEV;
- + info = rs_table + line;
- + if (serial_paranoia_check(info, tty->device, "rs_open"))
- + return -ENODEV;
- +
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
- + info->count);
- +#endif
- + info->count++;
- + tty->driver_data = info;
- + info->tty = tty;
- +
- + if (!tmp_buf) {
- + tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL);
- + if (!tmp_buf)
- + return -ENOMEM;
- + }
- +
- + /*
- + * Start up serial port
- + */
- + retval = startup(info);
- + if (retval)
- + return retval;
- +
- + retval = block_til_ready(tty, filp, info);
- + if (retval) {
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("rs_open returning after block_til_ready with %d\n",
- + retval);
- +#endif
- + return retval;
- + }
- +
- + if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
- + if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
- + *tty->termios = info->normal_termios;
- + else
- + *tty->termios = info->callout_termios;
- + change_speed(info);
- + }
- +
- + info->session = current->session;
- + info->pgrp = current->pgrp;
- +
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("rs_open ttys%d successful...", info->line);
- +#endif
- + return 0;
- +}
- +
- +/*
- + * ---------------------------------------------------------------------
- + * rs_init() and friends
- + *
- + * rs_init() is called at boot-time to initialize the serial driver.
- + * ---------------------------------------------------------------------
- + */
- +
- +/*
- + * This routine prints out the appropriate serial driver version
- + * number, and identifies which options were configured into this
- + * driver.
- + */
- +static void show_serial_version(void)
- +{
- + printk("Serial driver version 4.11 with");
- +#ifdef CONFIG_HUB6
- + printk(" HUB-6");
- +#define SERIAL_OPT
- +#endif
- +#ifdef SERIAL_OPT
- + printk(" enabled\n");
- +#else
- + printk(" no serial options enabled\n");
- +#endif
- +#undef SERIAL_OPT
- +}
- +
- +/*
- + * This routine is called by do_auto_irq(); it attempts to determine
- + * which interrupt a serial port is configured to use. It is not
- + * fool-proof, but it works a large part of the time.
- + */
- +static int get_auto_irq(struct async_struct *info)
- +{
- + unsigned char save_MCR, save_IER, save_ICP=0;
- + unsigned short ICP=0, port = info->port;
- + unsigned long timeout;
- +
- + /*
- + * 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) {
- + serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
- + serial_outp(info, UART_IER, 0x0f); /* enable all intrs */
- + ICP = (port & 0xFE0) | 0x01F;
- + save_ICP = inb_p(ICP);
- + outb_p(0x80, ICP);
- + (void) inb_p(ICP);
- + } else {
- + serial_outp(info, UART_MCR,
- + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
- + serial_outp(info, UART_IER, 0x0f); /* enable all intrs */
- + }
- + sti();
- + /*
- + * Next, clear the interrupt registers.
- + */
- + (void)serial_inp(info, UART_LSR);
- + (void)serial_inp(info, UART_RX);
- + (void)serial_inp(info, UART_IIR);
- + (void)serial_inp(info, UART_MSR);
- +
- + timeout = jiffies+2*HZ/100;
- + while (timeout >= jiffies) {
- + if (rs_irq_triggered)
- + break;
- + }
- + /*
- + * 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);
- +}
- +
- +/*
- + * Calls get_auto_irq() multiple times, to make sure we don't get
- + * faked out by random interrupts
- + */
- +static int do_auto_irq(struct async_struct * info)
- +{
- + unsigned port = info->port;
- + int irq_lines = 0;
- + int irq_try_1 = 0, irq_try_2 = 0;
- + int retries;
- + unsigned long flags;
- +
- + if (!port)
- + return 0;
- +
- + /* Turn on interrupts (they may be off) */
- + save_flags(flags); sti();
- +
- + irq_lines = grab_all_interrupts(rs_wild_int_mask);
- +
- + for (retries = 0; retries < 5; retries++) {
- + if (!irq_try_1)
- + irq_try_1 = get_auto_irq(info);
- + if (!irq_try_2)
- + irq_try_2 = get_auto_irq(info);
- + if (irq_try_1 && irq_try_2) {
- + if (irq_try_1 == irq_try_2)
- + break;
- + irq_try_1 = irq_try_2 = 0;
- + }
- + }
- + restore_flags(flags);
- + free_all_interrupts(irq_lines);
- + return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;
- +}
- +
- +/*
- + * This routine is called by rs_init() to initialize a specific serial
- + * port. It determines what type of UART ship this serial port is
- + * using: 8250, 16450, 16550, 16550A. The important question is
- + * whether or not this UART is a 16550A or not, since this will
- + * determine whether or not we can use its FIFO features or not.
- + */
- +static void autoconfig(struct async_struct * info)
- +{
- + 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
- + * no point trying anything else.
- + *
- + * 0x80 is used as a nonsense port to prevent against false
- + * positives due to ISA bus float. The assumption is that
- + * 0x80 is a non-existent port; which should be safe since
- + * include/asm/io.h also makes this assumption.
- + */
- + scratch = serial_inp(info, UART_IER);
- + serial_outp(info, UART_IER, 0);
- + outb(0xff, 0x080);
- + scratch2 = serial_inp(info, UART_IER);
- + serial_outp(info, UART_IER, scratch);
- + if (scratch2) {
- + restore_flags(flags);
- + return; /* We failed; there's nothing here */
- + }
- +
- + /*
- + * Check to see if a UART is really there. Certain broken
- + * internal modems based on the Rockwell chipset fail this
- + * test, because they apparently don't implement the loopback
- + * test mode. So this test is skipped on the COM 1 through
- + * COM 4 ports. This *should* be safe, since no board
- + * manufacturer would be stupid enough to design a board
- + * that conflicts with COM 1-4 --- we hope!
- + */
- + if (!(info->flags & ASYNC_SKIP_TEST)) {
- + scratch = serial_inp(info, UART_MCR);
- + serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch);
- + scratch2 = serial_inp(info, UART_MSR);
- + serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A);
- + status1 = serial_inp(info, UART_MSR) & 0xF0;
- + serial_outp(info, UART_MCR, scratch);
- + serial_outp(info, UART_MSR, scratch2);
- + if (status1 != 0x90) {
- + restore_flags(flags);
- + return;
- + }
- + }
- +
- + /*
- + * If the AUTO_IRQ flag is set, try to do the automatic IRQ
- + * detection.
- + */
- + if (info->flags & ASYNC_AUTO_IRQ)
- + info->irq = do_auto_irq(info);
- +
- + scratch2 = serial_in(info, UART_LCR);
- + serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
- + serial_outp(info, UART_EFR, 0); /* EFR is the same as FCR */
- + serial_outp(info, UART_LCR, scratch2);
- + 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:
- + info->type = PORT_16450;
- + break;
- + case 1:
- + info->type = PORT_UNKNOWN;
- + break;
- + case 2:
- + info->type = PORT_16550;
- + break;
- + case 3:
- + serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
- + if (serial_in(info, UART_EFR) == 0) {
- + info->type = PORT_16650;
- + info->xmit_fifo_size = 32;
- + } else {
- + info->type = PORT_16550A;
- + info->xmit_fifo_size = 16;
- + }
- + serial_outp(info, UART_LCR, scratch2);
- + break;
- + }
- + if (info->type == PORT_16450) {
- + 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;
- + }
- + request_region(info->port,8,"serial(auto)");
- +
- + /*
- + * Reset the UART.
- + */
- +#if defined(__alpha__) && !defined(CONFIG_PCI)
- + /*
- + * I wonder what DEC did to the OUT1 and OUT2 lines?
- + * clearing them results in endless interrupts.
- + */
- + serial_outp(info, UART_MCR, 0x0c);
- +#else
- + serial_outp(info, UART_MCR, 0x00);
- +#endif
- + serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
- + UART_FCR_CLEAR_XMIT));
- + (void)serial_in(info, UART_RX);
- +
- + restore_flags(flags);
- +}
- +
- +/*
- + * The serial driver boot-time initialization code!
- + */
- +int rs_init(void)
- +{
- + int i;
- + struct async_struct * info;
- +
- + bh_base[SERIAL_BH].routine = do_serial_bh;
- + enable_bh(SERIAL_BH);
- + timer_table[RS_TIMER].fn = rs_timer;
- + timer_table[RS_TIMER].expires = 0;
- +#ifdef CONFIG_AUTO_IRQ
- + rs_wild_int_mask = check_wild_interrupts(1);
- +#endif
- +
- + for (i = 0; i < 16; i++) {
- + IRQ_ports[i] = 0;
- + IRQ_timeout[i] = 0;
- + memset(&rs_multiport[i], 0, sizeof(struct rs_multiport_struct));
- + }
- +
- + show_serial_version();
- +
- + /* Initialize the tty_driver structure */
- +
- + memset(&serial_driver, 0, sizeof(struct tty_driver));
- + serial_driver.magic = TTY_DRIVER_MAGIC;
- + serial_driver.name = "ttyS";
- + serial_driver.major = TTY_MAJOR;
- + serial_driver.minor_start = 64;
- + serial_driver.num = NR_PORTS;
- + serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
- + serial_driver.subtype = SERIAL_TYPE_NORMAL;
- + serial_driver.init_termios = tty_std_termios;
- + serial_driver.init_termios.c_cflag =
- + B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- + serial_driver.flags = TTY_DRIVER_REAL_RAW;
- + serial_driver.refcount = &serial_refcount;
- + serial_driver.table = serial_table;
- + serial_driver.termios = serial_termios;
- + serial_driver.termios_locked = serial_termios_locked;
- +
- + serial_driver.open = rs_open;
- + serial_driver.close = rs_close;
- + serial_driver.write = rs_write;
- + serial_driver.put_char = rs_put_char;
- + serial_driver.flush_chars = rs_flush_chars;
- + serial_driver.write_room = rs_write_room;
- + serial_driver.chars_in_buffer = rs_chars_in_buffer;
- + serial_driver.flush_buffer = rs_flush_buffer;
- + serial_driver.ioctl = rs_ioctl;
- + serial_driver.throttle = rs_throttle;
- + serial_driver.unthrottle = rs_unthrottle;
- + serial_driver.set_termios = rs_set_termios;
- + serial_driver.stop = rs_stop;
- + serial_driver.start = rs_start;
- + serial_driver.hangup = rs_hangup;
- +
- + /*
- + * The callout device is just like normal device except for
- + * major number and the subtype code.
- + */
- + callout_driver = serial_driver;
- + callout_driver.name = "cua";
- + callout_driver.major = TTYAUX_MAJOR;
- + callout_driver.subtype = SERIAL_TYPE_CALLOUT;
- +
- + if (tty_register_driver(&serial_driver))
- + panic("Couldn't register serial driver\n");
- + if (tty_register_driver(&callout_driver))
- + panic("Couldn't register callout driver\n");
- +
- + for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
- + info->magic = SERIAL_MAGIC;
- + info->line = i;
- + info->tty = 0;
- + info->type = PORT_UNKNOWN;
- + info->custom_divisor = 0;
- + info->close_delay = 5*HZ/10;
- + info->closing_wait = 30*HZ;
- + info->x_char = 0;
- + info->event = 0;
- + info->count = 0;
- + info->blocked_open = 0;
- + info->tqueue.routine = do_softint;
- + info->tqueue.data = info;
- + info->tqueue_hangup.routine = do_serial_hangup;
- + info->tqueue_hangup.data = info;
- + info->callout_termios =callout_driver.init_termios;
- + info->normal_termios = serial_driver.init_termios;
- + info->open_wait = 0;
- + info->close_wait = 0;
- + info->next_port = 0;
- + info->prev_port = 0;
- + if (info->irq == 2)
- + info->irq = 9;
- + if (!(info->flags & ASYNC_BOOT_AUTOCONF))
- + continue;
- + autoconfig(info);
- + if (info->type == PORT_UNKNOWN)
- + continue;
- + printk("tty%02d%s at 0x%04x (irq = %d)", info->line,
- + (info->flags & ASYNC_FOURPORT) ? " FourPort" : "",
- + info->port, info->irq);
- + switch (info->type) {
- + case PORT_8250:
- + printk(" is a 8250\n");
- + break;
- + case PORT_16450:
- + printk(" is a 16450\n");
- + break;
- + case PORT_16550:
- + printk(" is a 16550\n");
- + break;
- + case PORT_16550A:
- + printk(" is a 16550A\n");
- + break;
- + case PORT_16650:
- + printk(" is a 16650\n");
- + break;
- + default:
- + printk("\n");
- + break;
- + }
- + }
- + return 0;
- +}
- +
- +/*
- + * register_serial and unregister_serial allows for serial ports to be
- + * configured at run-time, to support PCMCIA modems.
- + */
- +int register_serial(struct serial_struct *req)
- +{
- + int i;
- + unsigned long flags;
- + struct async_struct *info;
- +
- + save_flags(flags);
- + cli();
- + for (i = 0; i < NR_PORTS; i++) {
- + if (rs_table[i].port == req->port)
- + break;
- + }
- + if (i == NR_PORTS) {
- + for (i = 0; i < NR_PORTS; i++)
- + if ((rs_table[i].type == PORT_UNKNOWN) &&
- + (rs_table[i].count == 0))
- + break;
- + }
- + if (i == NR_PORTS) {
- + restore_flags(flags);
- + return -1;
- + }
- + info = &rs_table[i];
- + if (rs_table[i].count) {
- + restore_flags(flags);
- + printk("Couldn't configure serial #%d (port=%d,irq=%d): "
- + "device already open\n", i, req->port, req->irq);
- + return -1;
- + }
- + info->irq = req->irq;
- + info->port = req->port;
- + autoconfig(info);
- + if (info->type == PORT_UNKNOWN) {
- + restore_flags(flags);
- + printk("register_serial(): autoconfig failed\n");
- + return -1;
- + }
- + printk("tty%02d at 0x%04x (irq = %d)", info->line,
- + info->port, info->irq);
- + switch (info->type) {
- + case PORT_8250:
- + printk(" is a 8250\n"); break;
- + case PORT_16450:
- + printk(" is a 16450\n"); break;
- + case PORT_16550:
- + printk(" is a 16550\n"); break;
- + case PORT_16550A:
- + printk(" is a 16550A\n"); break;
- + default:
- + printk("\n"); break;
- + }
- + restore_flags(flags);
- + return info->line;
- +}
- +
- +void unregister_serial(int line)
- +{
- + unsigned long flags;
- + struct async_struct *info = &rs_table[line];
- +
- + save_flags(flags);
- + cli();
- + if (info->tty)
- + tty_hangup(info->tty);
- + info->type = PORT_UNKNOWN;
- + printk("tty%02d unloaded\n", info->line);
- + restore_flags(flags);
- +}
- diff -urNwbB linux/arch/arm/drivers/char/tty_io.c linux.arm/arch/arm/drivers/char/tty_io.c
- --- linux/arch/arm/drivers/char/tty_io.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/tty_io.c Sun Mar 3 12:48:37 1996
- @@ -0,0 +1,1799 @@
- +/*
- + * linux/arch/arm/drivers/char/tty_io.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + *
- + * Modifications for ARM processor Copright (C) 1995, 1996 Russell King.
- + */
- +
- +/*
- + * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
- + * or rs-channels. It also implements echoing, cooked mode etc.
- + *
- + * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
- + *
- + * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the
- + * tty_struct and tty_queue structures. Previously there was a array
- + * of 256 tty_struct's which was statically allocated, and the
- + * tty_queue structures were allocated at boot time. Both are now
- + * dynamically allocated only when the tty is open.
- + *
- + * Also restructured routines so that there is more of a separation
- + * between the high-level tty routines (tty_io.c and tty_ioctl.c) and
- + * the low-level tty routines (serial.c, pty.c, console.c). This
- + * makes for cleaner and more compact code. -TYT, 9/17/92
- + *
- + * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
- + * which can be dynamically activated and de-activated by the line
- + * discipline handling modules (like SLIP).
- + *
- + * NOTE: pay no attention to the line discipline code (yet); its
- + * interface is still subject to change in this version...
- + * -- TYT, 1/31/92
- + *
- + * Added functionality to the OPOST tty handling. No delays, but all
- + * other bits should be there.
- + * -- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993.
- + *
- + * Rewrote canonical mode and added more termios flags.
- + * -- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94
- + *
- + * Reorganized FASYNC support so mouse code can share it.
- + * -- ctm@ardi.com, 9Sep 95
- + *
- + * Altered get_free_page to kmalloc's for tty structures to save memory
- + * on the ARM processor
- + * -- rmk92@ecs.soton.ac.uk
- + */
- +
- +#include <linux/config.h>
- +#include <linux/types.h>
- +#include <linux/major.h>
- +#include <linux/errno.h>
- +#include <linux/signal.h>
- +#include <linux/fcntl.h>
- +#include <linux/sched.h>
- +#include <linux/interrupt.h>
- +#include <linux/tty.h>
- +#include <linux/tty_flip.h>
- +#include <linux/timer.h>
- +#include <linux/ctype.h>
- +#include <linux/kd.h>
- +#include <linux/mm.h>
- +#include <linux/string.h>
- +#include <linux/malloc.h>
- +
- +#include <asm/segment.h>
- +#include <asm/system.h>
- +#include <asm/bitops.h>
- +
- +/*#include <linux/scc.h>*/
- +
- +#include "kbd_kern.h"
- +#include "vt_kern.h"
- +#include "selection.h"
- +
- +#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
- +#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
- +
- +#undef TTY_DEBUG_HANGUP
- +
- +#define TTY_PARANOIA_CHECK
- +#define CHECK_TTY_COUNT
- +
- +extern void do_blank_screen(int nopowersave);
- +/*extern void set_vesa_blanking(const unsigned long arg);*/
- +
- +struct termios tty_std_termios; /* for the benefit of tty drivers */
- +struct tty_driver *tty_drivers = NULL; /* linked list of tty drivers */
- +struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */
- +
- +/*
- + * fg_console is the current virtual console,
- + * last_console is the last used one
- + * redirect is the pseudo-tty that console output
- + * is redirected to if asked by TIOCCONS.
- + */
- +int fg_console = 0;
- +int last_console = 0;
- +struct tty_struct * redirect = NULL;
- +struct wait_queue * keypress_wait = NULL;
- +
- +static void initialize_tty_struct(struct tty_struct *tty);
- +
- +static int tty_read(struct inode *, struct file *, char *, int);
- +static int tty_write(struct inode *, struct file *, const char *, int);
- +static int tty_select(struct inode *, struct file *, int, select_table *);
- +static int tty_open(struct inode *, struct file *);
- +static void tty_release(struct inode *, struct file *);
- +static int tty_ioctl(struct inode * inode, struct file * file,
- + unsigned int cmd, unsigned long arg);
- +static int tty_fasync(struct inode * inode, struct file * filp, int on);
- +
- +extern void reset_palette(int currcons) ;
- +extern void set_palette(void) ;
- +
- +#ifndef MIN
- +#define MIN(a,b) ((a) < (b) ? (a) : (b))
- +#endif
- +
- +/*
- + * These two routines return the name of tty. tty_name() should NOT
- + * be used in interrupt drivers, since it's not re-entrant. Use
- + * _tty_name() instead.
- + */
- +char *_tty_name(struct tty_struct *tty, char *buf)
- +{
- + if (tty)
- + sprintf(buf, "%s%d", tty->driver.name,
- + MINOR(tty->device) - tty->driver.minor_start +
- + tty->driver.name_base);
- + else
- + strcpy(buf, "NULL tty");
- + return buf;
- +}
- +
- +char *tty_name(struct tty_struct *tty)
- +{
- + static char buf[64];
- +
- + return(_tty_name(tty, buf));
- +}
- +
- +inline int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
- + const char *routine)
- +{
- +#ifdef TTY_PARANOIA_CHECK
- + static const char *badmagic =
- + "Warning: bad magic number for tty struct (%s) in %s\n";
- + static const char *badtty =
- + "Warning: null TTY for (%s) in %s\n";
- +
- + if (!tty) {
- + printk(badtty, kdevname(device), routine);
- + return 1;
- + }
- + if (tty->magic != TTY_MAGIC) {
- + printk(badmagic, kdevname(device), routine);
- + return 1;
- + }
- +#endif
- + return 0;
- +}
- +
- +static int check_tty_count(struct tty_struct *tty, const char *routine)
- +{
- +#ifdef CHECK_TTY_COUNT
- + struct file *f;
- + int i, count = 0;
- +
- + for (f = first_file, i=0; i<nr_files; i++, f = f->f_next) {
- + if (!f->f_count)
- + continue;
- + if (f->private_data == tty) {
- + count++;
- + }
- + }
- + if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
- + tty->driver.subtype == PTY_TYPE_SLAVE &&
- + tty->link && tty->link->count)
- + count++;
- + if (tty->count != count) {
- + printk("Warning: dev (%s) tty->count(%d) != #fd's(%d) in %s\n",
- + kdevname(tty->device), tty->count, count, routine);
- + return count;
- + }
- +#endif
- + return 0;
- +}
- +
- +int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
- +{
- + if (disc < N_TTY || disc >= NR_LDISCS)
- + return -EINVAL;
- +
- + if (new_ldisc) {
- + ldiscs[disc] = *new_ldisc;
- + ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
- + ldiscs[disc].num = disc;
- + } else
- + memset(&ldiscs[disc], 0, sizeof(struct tty_ldisc));
- +
- + return 0;
- +}
- +
- +/* Set the discipline of a tty line. */
- +static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
- +{
- + int retval = 0;
- + struct tty_ldisc o_ldisc;
- +
- + if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS) ||
- + !(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
- + return -EINVAL;
- +
- + if (tty->ldisc.num == ldisc)
- + return 0; /* We are already in the desired discipline */
- + o_ldisc = tty->ldisc;
- +
- + tty_wait_until_sent(tty, 0);
- +
- + /* Shutdown the current discipline. */
- + if (tty->ldisc.close)
- + (tty->ldisc.close)(tty);
- +
- + /* Now set up the new line discipline. */
- + tty->ldisc = ldiscs[ldisc];
- + tty->termios->c_line = ldisc;
- + if (tty->ldisc.open)
- + retval = (tty->ldisc.open)(tty);
- + if (retval < 0) {
- + tty->ldisc = o_ldisc;
- + tty->termios->c_line = tty->ldisc.num;
- + if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) {
- + tty->ldisc = ldiscs[N_TTY];
- + tty->termios->c_line = N_TTY;
- + if (tty->ldisc.open) {
- + int r = tty->ldisc.open(tty);
- +
- + if (r < 0)
- + panic("Couldn't open N_TTY ldisc for "
- + "%s --- error %d.",
- + tty_name(tty), r);
- + }
- + }
- + }
- + if (tty->ldisc.num != o_ldisc.num && tty->driver.set_ldisc)
- + tty->driver.set_ldisc(tty);
- + return retval;
- +}
- +
- +/*
- + * This routine returns a tty driver structure, given a device number
- + */
- +struct tty_driver *get_tty_driver(kdev_t device)
- +{
- + int major, minor;
- + struct tty_driver *p;
- +
- + minor = MINOR(device);
- + major = MAJOR(device);
- +
- + for (p = tty_drivers; p; p = p->next) {
- + if (p->major != major)
- + continue;
- + if (minor < p->minor_start)
- + continue;
- + if (minor >= p->minor_start + p->num)
- + continue;
- + return p;
- + }
- + return NULL;
- +}
- +
- +/*
- + * If we try to write to, or set the state of, a terminal and we're
- + * not in the foreground, send a SIGTTOU. If the signal is blocked or
- + * ignored, go ahead and perform the operation. (POSIX 7.2)
- + */
- +int tty_check_change(struct tty_struct * tty)
- +{
- + if (current->tty != tty)
- + return 0;
- + if (tty->pgrp <= 0) {
- + printk("tty_check_change: tty->pgrp <= 0!\n");
- + return 0;
- + }
- + if (current->pgrp == tty->pgrp)
- + return 0;
- + if (is_ignored(SIGTTOU))
- + return 0;
- + if (is_orphaned_pgrp(current->pgrp))
- + return -EIO;
- + (void) kill_pg(current->pgrp,SIGTTOU,1);
- + return -ERESTARTSYS;
- +}
- +
- +static int hung_up_tty_read(struct inode * inode, struct file * file, char * buf, int count)
- +{
- + return 0;
- +}
- +
- +static int hung_up_tty_write(struct inode * inode, struct file * file, const char * buf, int count)
- +{
- + return -EIO;
- +}
- +
- +static int hung_up_tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait)
- +{
- + return 1;
- +}
- +
- +static int hung_up_tty_ioctl(struct inode * inode, struct file * file,
- + unsigned int cmd, unsigned long arg)
- +{
- + return -EIO;
- +}
- +
- +static int tty_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
- +{
- + return -ESPIPE;
- +}
- +
- +static struct file_operations tty_fops = {
- + tty_lseek,
- + tty_read,
- + tty_write,
- + NULL, /* tty_readdir */
- + tty_select,
- + tty_ioctl,
- + NULL, /* tty_mmap */
- + tty_open,
- + tty_release,
- + NULL, /* tty_fsync */
- + tty_fasync
- +};
- +
- +static struct file_operations hung_up_tty_fops = {
- + tty_lseek,
- + hung_up_tty_read,
- + hung_up_tty_write,
- + NULL, /* hung_up_tty_readdir */
- + hung_up_tty_select,
- + hung_up_tty_ioctl,
- + NULL, /* hung_up_tty_mmap */
- + NULL, /* hung_up_tty_open */
- + tty_release, /* hung_up_tty_release */
- + NULL, /* hung_up_tty_fsync */
- + NULL /* hung_up_tty_fasync */
- +};
- +
- +void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
- +{
- + int i;
- + struct file * filp;
- + struct task_struct *p;
- +
- + if (!tty)
- + return;
- + check_tty_count(tty, "do_tty_hangup");
- + for (filp = first_file, i=0; i<nr_files; i++, filp = filp->f_next) {
- + if (!filp->f_count)
- + continue;
- + if (filp->private_data != tty)
- + continue;
- + if (filp->f_inode
- + && filp->f_inode->i_rdev == CONSOLE_DEV)
- + continue;
- + if (filp->f_op != &tty_fops)
- + continue;
- + tty_fasync(filp->f_inode, filp, 0);
- + filp->f_op = fops;
- + }
- +
- + if (tty->ldisc.flush_buffer)
- + tty->ldisc.flush_buffer(tty);
- + if (tty->driver.flush_buffer)
- + tty->driver.flush_buffer(tty);
- + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- + tty->ldisc.write_wakeup)
- + (tty->ldisc.write_wakeup)(tty);
- + wake_up_interruptible(&tty->write_wait);
- + wake_up_interruptible(&tty->read_wait);
- +
- + /*
- + * Shutdown the current line discipline, and reset it to
- + * N_TTY.
- + */
- + if (tty->ldisc.num != ldiscs[N_TTY].num) {
- + if (tty->ldisc.close)
- + (tty->ldisc.close)(tty);
- + tty->ldisc = ldiscs[N_TTY];
- + tty->termios->c_line = N_TTY;
- + if (tty->ldisc.open) {
- + i = (tty->ldisc.open)(tty);
- + if (i < 0)
- + printk("do_tty_hangup: N_TTY open: error %d\n",
- + -i);
- + }
- + }
- +
- + for_each_task(p) {
- + if ((tty->session > 0) && (p->session == tty->session) &&
- + p->leader) {
- + send_sig(SIGHUP,p,1);
- + send_sig(SIGCONT,p,1);
- + if (tty->pgrp > 0)
- + p->tty_old_pgrp = tty->pgrp;
- + }
- + if (p->tty == tty)
- + p->tty = NULL;
- + }
- + tty->flags = 0;
- + tty->session = 0;
- + tty->pgrp = -1;
- + tty->ctrl_status = 0;
- + if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
- + *tty->termios = tty->driver.init_termios;
- + if (tty->driver.hangup)
- + (tty->driver.hangup)(tty);
- +}
- +
- +void tty_hangup(struct tty_struct * tty)
- +{
- +#ifdef TTY_DEBUG_HANGUP
- + printk("%s hangup...\n", tty_name(tty));
- +#endif
- + do_tty_hangup(tty, &hung_up_tty_fops);
- +}
- +
- +void tty_vhangup(struct tty_struct * tty)
- +{
- +#ifdef TTY_DEBUG_HANGUP
- + printk("%s vhangup...\n", tty_name(tty));
- +#endif
- + do_tty_hangup(tty, &hung_up_tty_fops);
- +}
- +
- +int tty_hung_up_p(struct file * filp)
- +{
- + return (filp->f_op == &hung_up_tty_fops);
- +}
- +
- +/*
- + * This function is typically called only by the session leader, when
- + * it wants to disassociate itself from its controlling tty.
- + *
- + * It performs the following functions:
- + * (1) Sends a SIGHUP and SIGCONT to the foreground process group
- + * (2) Clears the tty from being controlling the session
- + * (3) Clears the controlling tty for all processes in the
- + * session group.
- + */
- +void disassociate_ctty(int priv)
- +{
- + struct tty_struct *tty = current->tty;
- + struct task_struct *p;
- +
- + if (!tty) {
- + if (current->tty_old_pgrp) {
- + kill_pg(current->tty_old_pgrp, SIGHUP, priv);
- + kill_pg(current->tty_old_pgrp, SIGCONT, priv);
- + }
- + return;
- + }
- + if (tty->pgrp > 0) {
- + kill_pg(tty->pgrp, SIGHUP, priv);
- + kill_pg(tty->pgrp, SIGCONT, priv);
- + }
- +
- + current->tty_old_pgrp = 0;
- + tty->session = 0;
- + tty->pgrp = -1;
- +
- + for_each_task(p)
- + if (p->session == current->session)
- + p->tty = NULL;
- +}
- +
- +/*
- + * Sometimes we want to wait until a particular VT has been activated. We
- + * do it in a very simple manner. Everybody waits on a single queue and
- + * get woken up at once. Those that are satisfied go on with their business,
- + * while those not ready go back to sleep. Seems overkill to add a wait
- + * to each vt just for this - usually this does nothing!
- + */
- +static struct wait_queue *vt_activate_queue = NULL;
- +
- +/*
- + * Sleeps until a vt is activated, or the task is interrupted. Returns
- + * 0 if activation, -1 if interrupted.
- + */
- +int vt_waitactive(void)
- +{
- + interruptible_sleep_on(&vt_activate_queue);
- + return (current->signal & ~current->blocked) ? -1 : 0;
- +}
- +
- +#define vt_wake_waitactive() wake_up(&vt_activate_queue)
- +
- +void reset_vc(unsigned int new_console)
- +{
- + vt_cons[new_console]->vc_mode = KD_TEXT;
- + kbd_table[new_console].kbdmode = VC_XLATE;
- + vt_cons[new_console]->vt_mode.mode = VT_AUTO;
- + vt_cons[new_console]->vt_mode.waitv = 0;
- + vt_cons[new_console]->vt_mode.relsig = 0;
- + vt_cons[new_console]->vt_mode.acqsig = 0;
- + vt_cons[new_console]->vt_mode.frsig = 0;
- + vt_cons[new_console]->vt_pid = -1;
- + vt_cons[new_console]->vt_newvt = -1;
- + reset_palette (new_console) ;
- +}
- +
- +/*
- + * Performs the back end of a vt switch
- + */
- +void complete_change_console(unsigned int new_console)
- +{
- + unsigned char old_vc_mode;
- +
- + if (new_console == fg_console)
- + return;
- + if (!vc_cons_allocated(new_console))
- + return;
- + last_console = fg_console;
- +
- + /*
- + * If we're switching, we could be going from KD_GRAPHICS to
- + * KD_TEXT mode or vice versa, which means we need to blank or
- + * unblank the screen later.
- + */
- + old_vc_mode = vt_cons[fg_console]->vc_mode;
- + update_screen(new_console);
- +
- + /*
- + * If this new console is under process control, send it a signal
- + * telling it that it has acquired. Also check if it has died and
- + * clean up (similar to logic employed in change_console())
- + */
- + if (vt_cons[new_console]->vt_mode.mode == VT_PROCESS)
- + {
- + /*
- + * Send the signal as privileged - kill_proc() will
- + * tell us if the process has gone or something else
- + * is awry
- + */
- + if (kill_proc(vt_cons[new_console]->vt_pid,
- + vt_cons[new_console]->vt_mode.acqsig,
- + 1) != 0)
- + {
- + /*
- + * The controlling process has died, so we revert back to
- + * normal operation. In this case, we'll also change back
- + * to KD_TEXT mode. I'm not sure if this is strictly correct
- + * but it saves the agony when the X server dies and the screen
- + * remains blanked due to KD_GRAPHICS! It would be nice to do
- + * this outside of VT_PROCESS but there is no single process
- + * to account for and tracking tty count may be undesirable.
- + */
- + reset_vc(new_console);
- + }
- + }
- +
- + /*
- + * We do this here because the controlling process above may have
- + * gone, and so there is now a new vc_mode
- + */
- + if (old_vc_mode != vt_cons[new_console]->vc_mode)
- + {
- + if (vt_cons[new_console]->vc_mode == KD_TEXT)
- + do_unblank_screen();
- + else
- + do_blank_screen(1);
- + }
- +
- + /* Set the colour palette for this VT */
- + if (vt_cons[new_console]->vc_mode == KD_TEXT)
- + set_palette() ;
- +
- + /*
- + * Wake anyone waiting for their VT to activate
- + */
- + vt_wake_waitactive();
- + return;
- +}
- +
- +/*
- + * Performs the front-end of a vt switch
- + */
- +void change_console(unsigned int new_console)
- +{
- + if (new_console == fg_console)
- + return;
- + if (!vc_cons_allocated(new_console))
- + return;
- +
- + /*
- + * If this vt is in process mode, then we need to handshake with
- + * that process before switching. Essentially, we store where that
- + * vt wants to switch to and wait for it to tell us when it's done
- + * (via VT_RELDISP ioctl).
- + *
- + * We also check to see if the controlling process still exists.
- + * If it doesn't, we reset this vt to auto mode and continue.
- + * This is a cheap way to track process control. The worst thing
- + * that can happen is: we send a signal to a process, it dies, and
- + * the switch gets "lost" waiting for a response; hopefully, the
- + * user will try again, we'll detect the process is gone (unless
- + * the user waits just the right amount of time :-) and revert the
- + * vt to auto control.
- + */
- + if (vt_cons[fg_console]->vt_mode.mode == VT_PROCESS)
- + {
- + /*
- + * Send the signal as privileged - kill_proc() will
- + * tell us if the process has gone or something else
- + * is awry
- + */
- + if (kill_proc(vt_cons[fg_console]->vt_pid,
- + vt_cons[fg_console]->vt_mode.relsig,
- + 1) == 0)
- + {
- + /*
- + * It worked. Mark the vt to switch to and
- + * return. The process needs to send us a
- + * VT_RELDISP ioctl to complete the switch.
- + */
- + vt_cons[fg_console]->vt_newvt = new_console;
- + return;
- + }
- +
- + /*
- + * The controlling process has died, so we revert back to
- + * normal operation. In this case, we'll also change back
- + * to KD_TEXT mode. I'm not sure if this is strictly correct
- + * but it saves the agony when the X server dies and the screen
- + * remains blanked due to KD_GRAPHICS! It would be nice to do
- + * this outside of VT_PROCESS but there is no single process
- + * to account for and tracking tty count may be undesirable.
- + */
- + reset_vc(fg_console);
- +
- + /*
- + * Fall through to normal (VT_AUTO) handling of the switch...
- + */
- + }
- +
- + /*
- + * Ignore all switches in KD_GRAPHICS+VT_AUTO mode
- + */
- + if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
- + return;
- +
- + complete_change_console(new_console);
- +}
- +
- +void wait_for_keypress(void)
- +{
- + sleep_on(&keypress_wait);
- +}
- +
- +void stop_tty(struct tty_struct *tty)
- +{
- + if (tty->stopped)
- + return;
- + tty->stopped = 1;
- + if (tty->link && tty->link->packet) {
- + tty->ctrl_status &= ~TIOCPKT_START;
- + tty->ctrl_status |= TIOCPKT_STOP;
- + wake_up_interruptible(&tty->link->read_wait);
- + }
- + if (tty->driver.stop)
- + (tty->driver.stop)(tty);
- +}
- +
- +void start_tty(struct tty_struct *tty)
- +{
- + if (!tty->stopped)
- + return;
- + tty->stopped = 0;
- + if (tty->link && tty->link->packet) {
- + tty->ctrl_status &= ~TIOCPKT_STOP;
- + tty->ctrl_status |= TIOCPKT_START;
- + wake_up_interruptible(&tty->link->read_wait);
- + }
- + if (tty->driver.start)
- + (tty->driver.start)(tty);
- + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- + tty->ldisc.write_wakeup)
- + (tty->ldisc.write_wakeup)(tty);
- + wake_up_interruptible(&tty->write_wait);
- +}
- +
- +static int tty_read(struct inode * inode, struct file * file, char * buf, int count)
- +{
- + int i;
- + struct tty_struct * tty;
- +
- + tty = (struct tty_struct *)file->private_data;
- + if (tty_paranoia_check(tty, inode->i_rdev, "tty_read"))
- + return -EIO;
- + if (!tty || (tty->flags & (1 << TTY_IO_ERROR)))
- + return -EIO;
- +
- + /* This check not only needs to be done before reading, but also
- + whenever read_chan() gets woken up after sleeping, so I've
- + moved it to there. This should only be done for the N_TTY
- + line discipline, anyway. Same goes for write_chan(). -- jlc. */
- +#if 0
- + if ((inode->i_rdev != CONSOLE_DEV) && /* don't stop on /dev/console */
- + (tty->pgrp > 0) &&
- + (current->tty == tty) &&
- + (tty->pgrp != current->pgrp))
- + if (is_ignored(SIGTTIN) || is_orphaned_pgrp(current->pgrp))
- + return -EIO;
- + else {
- + (void) kill_pg(current->pgrp, SIGTTIN, 1);
- + return -ERESTARTSYS;
- + }
- +#endif
- + if (tty->ldisc.read)
- + /* XXX casts are for what kernel-wide prototypes should be. */
- + i = (tty->ldisc.read)(tty,file,(unsigned char *)buf,(unsigned int)count);
- + else
- + i = -EIO;
- + if (i > 0)
- + inode->i_atime = CURRENT_TIME;
- + return i;
- +}
- +
- +static int tty_write(struct inode * inode, struct file * file, const char * buf, int count)
- +{
- + int i, is_console;
- + struct tty_struct * tty;
- +
- + is_console = (inode->i_rdev == CONSOLE_DEV);
- +
- + if (is_console && redirect)
- + tty = redirect;
- + else
- + tty = (struct tty_struct *)file->private_data;
- + if (tty_paranoia_check(tty, inode->i_rdev, "tty_write"))
- + return -EIO;
- + if (!tty || !tty->driver.write || (tty->flags & (1 << TTY_IO_ERROR)))
- + return -EIO;
- +#if 0
- + if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) &&
- + (current->tty == tty) && (tty->pgrp != current->pgrp)) {
- + if (is_orphaned_pgrp(current->pgrp))
- + return -EIO;
- + if (!is_ignored(SIGTTOU)) {
- + (void) kill_pg(current->pgrp, SIGTTOU, 1);
- + return -ERESTARTSYS;
- + }
- + }
- +#endif
- + if (tty->ldisc.write)
- + /* XXX casts are for what kernel-wide prototypes should be. */
- + i = (tty->ldisc.write)(tty,file,(const unsigned char *)buf,(unsigned int)count);
- + else
- + i = -EIO;
- + if (i > 0)
- + inode->i_mtime = CURRENT_TIME;
- + return i;
- +}
- +
- +/*
- + * This is so ripe with races that you should *really* not touch this
- + * unless you know exactly what you are doing. All the changes have to be
- + * made atomically, or there may be incorrect pointers all over the place.
- + */
- +static int init_dev(kdev_t device, struct tty_struct **ret_tty)
- +{
- + struct tty_struct *tty, **tty_loc, *o_tty, **o_tty_loc;
- + struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;
- + struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
- + struct tty_driver *driver;
- + int retval;
- + int idx;
- +
- + driver = get_tty_driver(device);
- + if (!driver)
- + return -ENODEV;
- +
- + idx = MINOR(device) - driver->minor_start;
- + tty = o_tty = NULL;
- + tp = o_tp = NULL;
- + ltp = o_ltp = NULL;
- + o_tty_loc = NULL;
- + o_tp_loc = o_ltp_loc = NULL;
- +
- + tty_loc = &driver->table[idx];
- + tp_loc = &driver->termios[idx];
- + ltp_loc = &driver->termios_locked[idx];
- +
- +repeat:
- + retval = -EAGAIN;
- + if (driver->type == TTY_DRIVER_TYPE_PTY &&
- + driver->subtype == PTY_TYPE_MASTER &&
- + *tty_loc && (*tty_loc)->count)
- + goto end_init;
- + retval = -ENOMEM;
- + if (!*tty_loc && !tty) {
- + if (!(tty = (struct tty_struct*) kmalloc(sizeof(struct tty_struct),GFP_KERNEL)))
- + goto end_init;
- + initialize_tty_struct(tty);
- + tty->device = device;
- + tty->driver = *driver;
- + goto repeat;
- + }
- + if (!*tp_loc && !tp) {
- + tp = (struct termios *) kmalloc(sizeof(struct termios),
- + GFP_KERNEL);
- + if (!tp)
- + goto end_init;
- + *tp = driver->init_termios;
- + goto repeat;
- + }
- + if (!*ltp_loc && !ltp) {
- + ltp = (struct termios *) kmalloc(sizeof(struct termios),
- + GFP_KERNEL);
- + if (!ltp)
- + goto end_init;
- + memset(ltp, 0, sizeof(struct termios));
- + goto repeat;
- + }
- + if (driver->type == TTY_DRIVER_TYPE_PTY) {
- + o_tty_loc = &driver->other->table[idx];
- + o_tp_loc = &driver->other->termios[idx];
- + o_ltp_loc = &driver->other->termios_locked[idx];
- +
- + if (!*o_tty_loc && !o_tty) {
- + kdev_t o_device;
- +
- + o_tty = (struct tty_struct *) kmalloc (sizeof(struct tty_struct),
- + GFP_KERNEL);
- + if (!o_tty)
- + goto end_init;
- + o_device = MKDEV(driver->other->major,
- + driver->other->minor_start + idx);
- + initialize_tty_struct(o_tty);
- + o_tty->device = o_device;
- + o_tty->driver = *driver->other;
- + goto repeat;
- + }
- + if (!*o_tp_loc && !o_tp) {
- + o_tp = (struct termios *)
- + kmalloc(sizeof(struct termios), GFP_KERNEL);
- + if (!o_tp)
- + goto end_init;
- + *o_tp = driver->other->init_termios;
- + goto repeat;
- + }
- + if (!*o_ltp_loc && !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 (!*tp_loc) {
- + *tp_loc = tp;
- + tp = NULL;
- + }
- + if (!*ltp_loc) {
- + *ltp_loc = ltp;
- + ltp = NULL;
- + }
- + if (!*tty_loc) {
- + tty->termios = *tp_loc;
- + tty->termios_locked = *ltp_loc;
- + *tty_loc = tty;
- + (*driver->refcount)++;
- + (*tty_loc)->count++;
- + if (tty->ldisc.open) {
- + retval = (tty->ldisc.open)(tty);
- + if (retval < 0) {
- + (*tty_loc)->count--;
- + tty = NULL;
- + goto end_init;
- + }
- + }
- + tty = NULL;
- + } else {
- + if ((*tty_loc)->flags & (1 << TTY_CLOSING)) {
- + printk("Attempt to open closing tty %s.\n",
- + tty_name(*tty_loc));
- + printk("Ack!!!! This should never happen!!\n");
- + return -EINVAL;
- + }
- + (*tty_loc)->count++;
- + }
- + if (driver->type == TTY_DRIVER_TYPE_PTY) {
- + if (!*o_tp_loc) {
- + *o_tp_loc = o_tp;
- + o_tp = NULL;
- + }
- + if (!*o_ltp_loc) {
- + *o_ltp_loc = o_ltp;
- + o_ltp = NULL;
- + }
- + if (!*o_tty_loc) {
- + o_tty->termios = *o_tp_loc;
- + o_tty->termios_locked = *o_ltp_loc;
- + *o_tty_loc = o_tty;
- + (*driver->other->refcount)++;
- + if (o_tty->ldisc.open) {
- + retval = (o_tty->ldisc.open)(o_tty);
- + if (retval < 0) {
- + (*tty_loc)->count--;
- + o_tty = NULL;
- + goto end_init;
- + }
- + }
- + o_tty = NULL;
- + }
- + (*tty_loc)->link = *o_tty_loc;
- + (*o_tty_loc)->link = *tty_loc;
- + if (driver->subtype == PTY_TYPE_MASTER)
- + (*o_tty_loc)->count++;
- + }
- + (*tty_loc)->driver = *driver;
- + *ret_tty = *tty_loc;
- + retval = 0;
- +end_init:
- + if (tty)
- + kfree_s(tty, sizeof(struct tty_struct));
- + if (o_tty)
- + kfree_s(o_tty, sizeof(struct tty_struct));
- + if (tp)
- + 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;
- +}
- +
- +/*
- + * Even releasing the tty structures is a tricky business.. We have
- + * to be very careful that the structures are all released at the
- + * same time, as interrupts might otherwise get the wrong pointers.
- + */
- +static void release_dev(struct file * filp)
- +{
- + struct tty_struct *tty, *o_tty;
- + struct termios *tp, *o_tp, *ltp, *o_ltp;
- + struct task_struct **p;
- + int idx;
- +
- + tty = (struct tty_struct *)filp->private_data;
- + if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "release_dev"))
- + return;
- +
- + check_tty_count(tty, "release_dev");
- +
- + tty_fasync(filp->f_inode, filp, 0);
- +
- + tp = tty->termios;
- + ltp = tty->termios_locked;
- +
- + idx = MINOR(tty->device) - tty->driver.minor_start;
- +#ifdef TTY_PARANOIA_CHECK
- + if (idx < 0 || idx >= tty->driver.num) {
- + printk("release_dev: bad idx when trying to free (%s)\n",
- + kdevname(tty->device));
- + return;
- + }
- + if (tty != tty->driver.table[idx]) {
- + printk("release_dev: driver.table[%d] not tty for (%s)\n",
- + idx, kdevname(tty->device));
- + return;
- + }
- + if (tp != tty->driver.termios[idx]) {
- + printk("release_dev: driver.termios[%d] not termios for (%s)\n",
- + idx, kdevname(tty->device));
- + return;
- + }
- + if (ltp != tty->driver.termios_locked[idx]) {
- + printk("release_dev: driver.termios_locked[%d] not termios_locked for (%s)\n",
- + idx, kdevname(tty->device));
- + return;
- + }
- +#endif
- +
- +#ifdef TTY_DEBUG_HANGUP
- + printk("release_dev of %s (tty count=%d)...", tty_name(tty),
- + tty->count);
- +#endif
- +
- + o_tty = tty->link;
- + o_tp = (o_tty) ? o_tty->termios : NULL;
- + o_ltp = (o_tty) ? o_tty->termios_locked : NULL;
- +
- +#ifdef TTY_PARANOIA_CHECK
- + if (tty->driver.other) {
- + if (o_tty != tty->driver.other->table[idx]) {
- + printk("release_dev: other->table[%d] not o_tty for ("
- + "%s)\n",
- + idx, kdevname(tty->device));
- + return;
- + }
- + if (o_tp != tty->driver.other->termios[idx]) {
- + printk("release_dev: other->termios[%d] not o_termios for ("
- + "%s)\n",
- + idx, kdevname(tty->device));
- + return;
- + }
- + if (o_ltp != tty->driver.other->termios_locked[idx]) {
- + printk("release_dev: other->termios_locked[%d] not o_termios_locked for ("
- + "%s)\n",
- + idx, kdevname(tty->device));
- + return;
- + }
- +
- + if (o_tty->link != tty) {
- + printk("release_dev: bad pty pointers\n");
- + return;
- + }
- + }
- +#endif
- +
- + if (tty->driver.close)
- + tty->driver.close(tty, filp);
- + if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
- + tty->driver.subtype == PTY_TYPE_MASTER) {
- + if (--tty->link->count < 0) {
- + printk("release_dev: bad pty slave count (%d) for %s\n",
- + tty->count, tty_name(tty));
- + tty->link->count = 0;
- + }
- + }
- + if (--tty->count < 0) {
- + printk("release_dev: bad tty->count (%d) for %s\n",
- + tty->count, tty_name(tty));
- + tty->count = 0;
- + }
- + if (tty->count)
- + return;
- +
- + /*
- + * We're committed; at this point, we must not block!
- + */
- + if (o_tty) {
- + if (o_tty->count)
- + return;
- + tty->driver.other->table[idx] = NULL;
- + tty->driver.other->termios[idx] = NULL;
- + kfree_s(o_tp, sizeof(struct termios));
- + }
- +
- +#ifdef TTY_DEBUG_HANGUP
- + printk("freeing tty structure...");
- +#endif
- + tty->flags |= (1 << TTY_CLOSING);
- +
- + /*
- + * Make sure there aren't any processes that still think this
- + * tty is their controlling tty.
- + */
- + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
- + if (*p == 0)
- + continue;
- + if ((*p)->tty == tty)
- + (*p)->tty = NULL;
- + if (o_tty && (*p)->tty == o_tty)
- + (*p)->tty = NULL;
- + }
- +
- + /*
- + * Shutdown the current line discipline, and reset it to
- + * N_TTY.
- + */
- + if (tty->ldisc.close)
- + (tty->ldisc.close)(tty);
- + tty->ldisc = ldiscs[N_TTY];
- + tty->termios->c_line = N_TTY;
- + if (o_tty) {
- + if (o_tty->ldisc.close)
- + (o_tty->ldisc.close)(o_tty);
- + o_tty->ldisc = ldiscs[N_TTY];
- + }
- +
- + tty->driver.table[idx] = NULL;
- + if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
- + tty->driver.termios[idx] = NULL;
- + kfree_s(tp, sizeof(struct termios));
- + }
- + if (tty == redirect || o_tty == redirect)
- + redirect = NULL;
- + /*
- + * Make sure that the tty's task queue isn't activated. If it
- + * is, take it out of the linked list.
- + */
- + cli();
- + if (tty->flip.tqueue.sync) {
- + struct tq_struct *tq, *prev;
- +
- + for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
- + if (tq == &tty->flip.tqueue) {
- + if (prev)
- + prev->next = tq->next;
- + else
- + tq_timer = tq->next;
- + break;
- + }
- + }
- + }
- + sti();
- + tty->magic = 0;
- + (*tty->driver.refcount)--;
- + kfree_s(tty, sizeof(struct tty_struct));
- + filp->private_data = 0;
- + if (o_tty) {
- + o_tty->magic = 0;
- + (*o_tty->driver.refcount)--;
- + kfree_s(o_tty, sizeof(struct tty_struct));
- + }
- +}
- +
- +/*
- + * tty_open and tty_release keep up the tty count that contains the
- + * number of opens done on a tty. We cannot use the inode-count, as
- + * different inodes might point to the same tty.
- + *
- + * Open-counting is needed for pty masters, as well as for keeping
- + * track of serial lines: DTR is dropped when the last close happens.
- + * (This is not done solely through tty->count, now. - Ted 1/27/92)
- + *
- + * The termios state of a pty is reset on first open so that
- + * settings don't persist across reuse.
- + */
- +static int tty_open(struct inode * inode, struct file * filp)
- +{
- + struct tty_struct *tty;
- + int minor;
- + int noctty, retval;
- + kdev_t device;
- +
- +retry_open:
- + noctty = filp->f_flags & O_NOCTTY;
- + device = inode->i_rdev;
- + if (device == TTY_DEV) {
- + if (!current->tty)
- + return -ENXIO;
- + device = current->tty->device;
- + /* noctty = 1; */
- + }
- + if (device == CONSOLE_DEV) {
- + device = MKDEV(TTY_MAJOR, fg_console+1);
- + noctty = 1;
- + }
- + minor = MINOR(device);
- +
- + retval = init_dev(device, &tty);
- + if (retval)
- + return retval;
- + filp->private_data = tty;
- + check_tty_count(tty, "tty_open");
- + if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
- + tty->driver.subtype == PTY_TYPE_MASTER)
- + noctty = 1;
- +#ifdef TTY_DEBUG_HANGUP
- + printk("opening %s...", tty_name(tty));
- +#endif
- + if (tty->driver.open)
- + retval = tty->driver.open(tty, filp);
- + else
- + retval = -ENODEV;
- +
- + if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser())
- + retval = -EBUSY;
- +
- + if (retval) {
- +#ifdef TTY_DEBUG_HANGUP
- + printk("error %d in opening %s...", retval, tty_name(tty));
- +#endif
- +
- + release_dev(filp);
- + if (retval != -ERESTARTSYS)
- + return retval;
- + if (current->signal & ~current->blocked)
- + return retval;
- + schedule();
- + /*
- + * Need to reset f_op in case a hangup happened.
- + */
- + filp->f_op = &tty_fops;
- + goto retry_open;
- + }
- + if (!noctty &&
- + current->leader &&
- + !current->tty &&
- + tty->session == 0) {
- + current->tty = tty;
- + current->tty_old_pgrp = 0;
- + tty->session = current->session;
- + tty->pgrp = current->pgrp;
- + }
- + return 0;
- +}
- +
- +/*
- + * Note that releasing a pty master also releases the child, so
- + * we have to make the redirection checks after that and on both
- + * sides of a pty.
- + */
- +static void tty_release(struct inode * inode, struct file * filp)
- +{
- + release_dev(filp);
- +}
- +
- +static int tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait)
- +{
- + struct tty_struct * tty;
- +
- + tty = (struct tty_struct *)filp->private_data;
- + if (tty_paranoia_check(tty, inode->i_rdev, "tty_select"))
- + return 0;
- +
- + if (tty->ldisc.select)
- + return (tty->ldisc.select)(tty, inode, filp, sel_type, wait);
- + return 0;
- +}
- +
- +/*
- + * fasync_helper() is used by some character device drivers (mainly mice)
- + * to set up the fasync queue. It returns negative on error, 0 if it did
- + * no changes, and positive if it added/deleted the entry.
- + */
- +int fasync_helper(struct inode * inode, struct file * filp, int on, struct fasync_struct **fapp)
- +{
- + struct fasync_struct *fa, **fp;
- + unsigned long flags;
- +
- + for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
- + if (fa->fa_file == filp)
- + break;
- + }
- +
- + if (on) {
- + if (fa)
- + return 0;
- + fa = (struct fasync_struct *)kmalloc(sizeof(struct fasync_struct), GFP_KERNEL);
- + if (!fa)
- + return -ENOMEM;
- + fa->magic = FASYNC_MAGIC;
- + fa->fa_file = filp;
- + save_flags(flags);
- + cli();
- + fa->fa_next = *fapp;
- + *fapp = fa;
- + restore_flags(flags);
- + return 1;
- + }
- + if (!fa)
- + return 0;
- + save_flags(flags);
- + cli();
- + *fp = fa->fa_next;
- + restore_flags(flags);
- + kfree(fa);
- + return 1;
- +}
- +
- +static int tty_fasync(struct inode * inode, struct file * filp, int on)
- +{
- + struct tty_struct * tty;
- + int retval;
- +
- + tty = (struct tty_struct *)filp->private_data;
- + if (tty_paranoia_check(tty, inode->i_rdev, "tty_fasync"))
- + return 0;
- +
- + retval = fasync_helper(inode, filp, on, &tty->fasync);
- + if (retval <= 0)
- + return retval;
- +
- + if (on) {
- + if (!tty->read_wait)
- + tty->minimum_to_wake = 1;
- + if (filp->f_owner == 0) {
- + if (tty->pgrp)
- + filp->f_owner = -tty->pgrp;
- + else
- + filp->f_owner = current->pid;
- + }
- + } else {
- + if (!tty->fasync && !tty->read_wait)
- + tty->minimum_to_wake = N_TTY_BUF_SIZE;
- + }
- + return 0;
- +}
- +
- +#if 0
- +/*
- + * XXX does anyone use this anymore?!?
- + */
- +static int do_get_ps_info(unsigned long arg)
- +{
- + struct tstruct {
- + int flag;
- + int present[NR_TASKS];
- + struct task_struct tasks[NR_TASKS];
- + };
- + struct tstruct *ts = (struct tstruct *)arg;
- + struct task_struct **p;
- + char *c, *d;
- + int i, n = 0;
- +
- + i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct tstruct));
- + if (i)
- + return i;
- + for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++)
- + if (*p)
- + {
- + c = (char *)(*p);
- + d = (char *)(ts->tasks+n);
- + for (i=0 ; i<sizeof(struct task_struct) ; i++)
- + put_user(*c++, d++);
- + put_user(1, ts->present+n);
- + }
- + else
- + put_user(0, ts->present+n);
- + return(0);
- +}
- +#endif
- +
- +static int tty_ioctl(struct inode * inode, struct file * file,
- + unsigned int cmd, unsigned long arg)
- +{
- + int retval;
- + struct tty_struct * tty;
- + struct tty_struct * real_tty;
- + struct winsize tmp_ws;
- + pid_t pgrp;
- + unsigned char ch;
- + char mbz = 0;
- +
- + tty = (struct tty_struct *)file->private_data;
- + if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl"))
- + return -EINVAL;
- +
- + if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
- + tty->driver.subtype == PTY_TYPE_MASTER)
- + real_tty = tty->link;
- + else
- + real_tty = tty;
- +
- + switch (cmd) {
- + case TIOCSTI:
- + if ((current->tty != tty) && !suser())
- + return -EPERM;
- + retval = verify_area(VERIFY_READ, (void *) arg, 1);
- + if (retval)
- + return retval;
- + ch = get_user((char *) arg);
- + tty->ldisc.receive_buf(tty, &ch, &mbz, 1);
- + return 0;
- + case TIOCGWINSZ:
- + retval = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof (struct winsize));
- + if (retval)
- + return retval;
- + memcpy_tofs((struct winsize *) arg, &tty->winsize,
- + sizeof (struct winsize));
- + return 0;
- + case TIOCSWINSZ:
- + retval = verify_area(VERIFY_READ, (void *) arg,
- + sizeof (struct winsize));
- + if (retval)
- + return retval;
- + memcpy_fromfs(&tmp_ws, (struct winsize *) arg,
- + sizeof (struct winsize));
- + if (memcmp(&tmp_ws, &tty->winsize,
- + sizeof(struct winsize))) {
- + if (tty->pgrp > 0)
- + kill_pg(tty->pgrp, SIGWINCH, 1);
- + if ((real_tty->pgrp != tty->pgrp) &&
- + (real_tty->pgrp > 0))
- + kill_pg(real_tty->pgrp, SIGWINCH, 1);
- + }
- + tty->winsize = tmp_ws;
- + real_tty->winsize = tmp_ws;
- + return 0;
- + case TIOCCONS:
- + if (tty->driver.type == TTY_DRIVER_TYPE_CONSOLE) {
- + if (!suser())
- + return -EPERM;
- + redirect = NULL;
- + return 0;
- + }
- + if (redirect)
- + return -EBUSY;
- + redirect = real_tty;
- + return 0;
- + case FIONBIO:
- + retval = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
- + if (retval)
- + return retval;
- + arg = get_user((unsigned int *) arg);
- + if (arg)
- + file->f_flags |= O_NONBLOCK;
- + else
- + file->f_flags &= ~O_NONBLOCK;
- + return 0;
- + case TIOCEXCL:
- + set_bit(TTY_EXCLUSIVE, &tty->flags);
- + return 0;
- + case TIOCNXCL:
- + clear_bit(TTY_EXCLUSIVE, &tty->flags);
- + return 0;
- + case TIOCNOTTY:
- + if (current->tty != tty)
- + return -ENOTTY;
- + if (current->leader)
- + disassociate_ctty(0);
- + current->tty = NULL;
- + return 0;
- + case TIOCSCTTY:
- + if (current->leader &&
- + (current->session == tty->session))
- + return 0;
- + /*
- + * The process must be a session leader and
- + * not have a controlling tty already.
- + */
- + if (!current->leader || current->tty)
- + return -EPERM;
- + if (tty->session > 0) {
- + /*
- + * This tty is already the controlling
- + * tty for another session group!
- + */
- + if ((arg == 1) && suser()) {
- + /*
- + * Steal it away
- + */
- + struct task_struct *p;
- +
- + for_each_task(p)
- + if (p->tty == tty)
- + p->tty = NULL;
- + } else
- + return -EPERM;
- + }
- + current->tty = tty;
- + current->tty_old_pgrp = 0;
- + tty->session = current->session;
- + tty->pgrp = current->pgrp;
- + return 0;
- + case TIOCGPGRP:
- + /*
- + * (tty == real_tty) is a cheap way of
- + * testing if the tty is NOT a master pty.
- + */
- + if (tty == real_tty && current->tty != real_tty)
- + return -ENOTTY;
- + retval = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof (pid_t));
- + if (retval)
- + return retval;
- + put_user(real_tty->pgrp, (pid_t *) arg);
- + return 0;
- + case TIOCSPGRP:
- + retval = tty_check_change(real_tty);
- + if (retval)
- + return retval;
- + if (!current->tty ||
- + (current->tty != real_tty) ||
- + (real_tty->session != current->session))
- + return -ENOTTY;
- + pgrp = get_user((pid_t *) arg);
- + if (pgrp < 0)
- + return -EINVAL;
- + if (session_of_pgrp(pgrp) != current->session)
- + return -EPERM;
- + real_tty->pgrp = pgrp;
- + return 0;
- + case TIOCGETD:
- + retval = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof (int));
- + if (retval)
- + return retval;
- + put_user(tty->ldisc.num, (int *) arg);
- + return 0;
- + case TIOCSETD:
- + retval = tty_check_change(tty);
- + if (retval)
- + return retval;
- + arg = get_user((int *) arg);
- + return tty_set_ldisc(tty, arg);
- + case TIOCLINUX:
- + if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE)
- + return -EINVAL;
- + if (current->tty != tty && !suser())
- + return -EPERM;
- + retval = verify_area(VERIFY_READ, (void *) arg, 1);
- + if (retval)
- + return retval;
- + switch (retval = get_user((char *)arg))
- + {
- + case 0:
- + case 8:
- + case 9:
- + printk("TIOCLINUX (0/8/9) ioctl is gone - use /dev/vcs\n");
- + return -EINVAL;
- +#if 0
- + case 1:
- + printk("Deprecated TIOCLINUX (1) ioctl\n");
- + return do_get_ps_info(arg);
- +#endif
- + case 2:
- + return set_selection(arg, tty);
- + case 3:
- + return paste_selection(tty);
- + case 4:
- + do_unblank_screen();
- + return 0;
- + case 5:
- + return sel_loadlut(arg);
- + case 6:
- + /*
- + * Make it possible to react to Shift+Mousebutton.
- + * Note that 'shift_state' is an undocumented
- + * kernel-internal variable; programs not closely
- + * related to the kernel should not use this.
- + */
- + put_user(shift_state,(char *) arg);
- + return 0;
- + case 7:
- + put_user(mouse_reporting(),(char *) arg);
- + return 0;
- +/* case 10:
- + set_vesa_blanking(arg);
- + return 0;*/
- + default:
- + return -EINVAL;
- + }
- +
- + case TIOCTTYGSTRUCT:
- + retval = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(struct tty_struct));
- + if (retval)
- + return retval;
- + memcpy_tofs((struct tty_struct *) arg,
- + tty, sizeof(struct tty_struct));
- + return 0;
- + default:
- + if (tty->driver.ioctl) {
- + retval = (tty->driver.ioctl)(tty, file,
- + cmd, arg);
- + if (retval != -ENOIOCTLCMD)
- + return retval;
- + }
- + if (tty->ldisc.ioctl) {
- + retval = (tty->ldisc.ioctl)(tty, file,
- + cmd, arg);
- + if (retval != -ENOIOCTLCMD)
- + return retval;
- + }
- + return -EINVAL;
- + }
- +}
- +
- +
- +/*
- + * This implements the "Secure Attention Key" --- the idea is to
- + * prevent trojan horses by killing all processes associated with this
- + * tty when the user hits the "Secure Attention Key". Required for
- + * super-paranoid applications --- see the Orange Book for more details.
- + *
- + * This code could be nicer; ideally it should send a HUP, wait a few
- + * seconds, then send a INT, and then a KILL signal. But you then
- + * have to coordinate with the init process, since all processes associated
- + * with the current tty must be dead before the new getty is allowed
- + * to spawn.
- + */
- +void do_SAK( struct tty_struct *tty)
- +{
- +#ifdef TTY_SOFT_SAK
- + tty_hangup(tty);
- +#else
- + struct task_struct **p;
- + int session;
- + int i;
- + struct file *filp;
- +
- + if (!tty)
- + return;
- + session = tty->session;
- + if (tty->ldisc.flush_buffer)
- + tty->ldisc.flush_buffer(tty);
- + if (tty->driver.flush_buffer)
- + tty->driver.flush_buffer(tty);
- + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
- + if (!(*p))
- + continue;
- + if (((*p)->tty == tty) ||
- + ((session > 0) && ((*p)->session == session)))
- + send_sig(SIGKILL, *p, 1);
- + else {
- + for (i=0; i < NR_OPEN; i++) {
- + filp = (*p)->files->fd[i];
- + if (filp && (filp->f_op == &tty_fops) &&
- + (filp->private_data == tty)) {
- + send_sig(SIGKILL, *p, 1);
- + break;
- + }
- + }
- + }
- + }
- +#endif
- +}
- +
- +/*
- + * This routine is called out of the software interrupt to flush data
- + * from the flip buffer to the line discipline.
- + */
- +static void flush_to_ldisc(void *private_)
- +{
- + struct tty_struct *tty = (struct tty_struct *) private_;
- + unsigned char *cp;
- + char *fp;
- + int count;
- +
- + if (tty->flip.buf_num) {
- + cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
- + fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
- + tty->flip.buf_num = 0;
- +
- + cli();
- + tty->flip.char_buf_ptr = tty->flip.char_buf;
- + tty->flip.flag_buf_ptr = tty->flip.flag_buf;
- + } else {
- + cp = tty->flip.char_buf;
- + fp = tty->flip.flag_buf;
- + tty->flip.buf_num = 1;
- +
- + cli();
- + tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
- + tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
- + }
- + count = tty->flip.count;
- + tty->flip.count = 0;
- + sti();
- +
- +#if 0
- + if (count > tty->max_flip_cnt)
- + tty->max_flip_cnt = count;
- +#endif
- + tty->ldisc.receive_buf(tty, cp, fp, count);
- +}
- +
- +/*
- + * This subroutine initializes a tty structure.
- + */
- +static void initialize_tty_struct(struct tty_struct *tty)
- +{
- + memset(tty, 0, sizeof(struct tty_struct));
- + tty->magic = TTY_MAGIC;
- + tty->ldisc = ldiscs[N_TTY];
- + tty->pgrp = -1;
- + tty->flip.char_buf_ptr = tty->flip.char_buf;
- + tty->flip.flag_buf_ptr = tty->flip.flag_buf;
- + tty->flip.tqueue.routine = flush_to_ldisc;
- + tty->flip.tqueue.data = tty;
- +}
- +
- +/*
- + * The default put_char routine if the driver did not define one.
- + */
- +void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
- +{
- + tty->driver.write(tty, 0, &ch, 1);
- +}
- +
- +/*
- + * Called by a tty driver to register itself.
- + */
- +int tty_register_driver(struct tty_driver *driver)
- +{
- + int error;
- +
- + if (driver->flags & TTY_DRIVER_INSTALLED)
- + return 0;
- +
- + error = register_chrdev(driver->major, driver->name, &tty_fops);
- + if (error < 0)
- + return error;
- + else if(driver->major == 0)
- + driver->major = error;
- +
- + if (!driver->put_char)
- + driver->put_char = tty_default_put_char;
- +
- + driver->prev = 0;
- + driver->next = tty_drivers;
- + if (tty_drivers) tty_drivers->prev = driver;
- + tty_drivers = driver;
- + return error;
- +}
- +
- +/*
- + * Called by a tty driver to unregister itself.
- + */
- +int tty_unregister_driver(struct tty_driver *driver)
- +{
- + int retval;
- + struct tty_driver *p;
- + int found = 0;
- + const char *othername = NULL;
- +
- + if (*driver->refcount)
- + return -EBUSY;
- +
- + for (p = tty_drivers; p; p = p->next) {
- + if (p == driver)
- + found++;
- + else if (p->major == driver->major)
- + othername = p->name;
- + }
- +
- + if (othername == NULL) {
- + retval = unregister_chrdev(driver->major, driver->name);
- + if (retval)
- + return retval;
- + } else
- + register_chrdev(driver->major, othername, &tty_fops);
- +
- + if (driver->prev)
- + driver->prev->next = driver->next;
- + else
- + tty_drivers = driver->next;
- +
- + if (driver->next)
- + driver->next->prev = driver->prev;
- +
- + return 0;
- +}
- +
- +
- +/*
- + * Initialize the console device. This is called *early*, so
- + * we can't necessarily depend on lots of kernel help here.
- + * Just do some early initializations, and do the complex setup
- + * later.
- + */
- +long console_init(long kmem_start, long kmem_end)
- +{
- + /* Setup the default TTY line discipline. */
- + memset(ldiscs, 0, sizeof(ldiscs));
- + (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
- +
- + /*
- + * Set up the standard termios. Individual tty drivers may
- + * deviate from this; this is used as a template.
- + */
- + memset(&tty_std_termios, 0, sizeof(struct termios));
- + memcpy(tty_std_termios.c_cc, INIT_C_CC, NCCS);
- + tty_std_termios.c_iflag = ICRNL | IXON;
- + tty_std_termios.c_oflag = OPOST | ONLCR;
- + tty_std_termios.c_cflag = B38400 | CS8 | CREAD;
- + tty_std_termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
- + ECHOCTL | ECHOKE | IEXTEN;
- +
- + /*
- + * set up the console device so that later boot sequences can
- + * inform about problems etc..
- + */
- + return con_init(kmem_start);
- +}
- +
- +/*
- + * Ok, now we can initialize the rest of the tty devices and can count
- + * on memory allocations, interrupts etc..
- + */
- +int tty_init(void)
- +{
- + if (sizeof(struct tty_struct) > PAGE_SIZE)
- + panic("size of tty structure > PAGE_SIZE!");
- + if (register_chrdev(TTY_MAJOR,"tty",&tty_fops))
- + panic("unable to get major %d for tty device", TTY_MAJOR);
- + if (register_chrdev(TTYAUX_MAJOR,"cua",&tty_fops))
- + panic("unable to get major %d for tty device", TTYAUX_MAJOR);
- +
- + kbd_init();
- + rs_init();
- +
- + pty_init();
- + vcs_init();
- + return 0;
- +}
- diff -urNwbB linux/arch/arm/drivers/char/tty_ioctl.c linux.arm/arch/arm/drivers/char/tty_ioctl.c
- --- linux/arch/arm/drivers/char/tty_ioctl.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/tty_ioctl.c Sun Mar 3 12:49:03 1996
- @@ -0,0 +1,376 @@
- +/*
- + * linux/drivers/char/tty_ioctl.c
- + *
- + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
- + *
- + * Modifications for ARM processor (C) 1995, 1996 Russell King.
- + *
- + * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
- + * which can be dynamically activated and de-activated by the line
- + * discipline handling modules (like SLIP).
- + */
- +
- +#include <linux/types.h>
- +#include <linux/termios.h>
- +#include <linux/errno.h>
- +#include <linux/sched.h>
- +#include <linux/kernel.h>
- +#include <linux/major.h>
- +#include <linux/tty.h>
- +#include <linux/fcntl.h>
- +#include <linux/string.h>
- +#include <linux/mm.h>
- +
- +#include <asm/io.h>
- +#include <asm/bitops.h>
- +#include <asm/segment.h>
- +#include <asm/system.h>
- +
- +#undef TTY_DEBUG_WAIT_UNTIL_SENT
- +
- +#undef DEBUG
- +#ifdef DEBUG
- +# define PRINTK(x) printk (x)
- +#else
- +# define PRINTK(x) /**/
- +#endif
- +
- +/*
- + * Internal flag options for termios setting behavior
- + */
- +#define TERMIOS_FLUSH 1
- +#define TERMIOS_WAIT 2
- +#define TERMIOS_TERMIO 4
- +
- +void tty_wait_until_sent(struct tty_struct * tty, int timeout)
- +{
- + struct wait_queue wait = { current, NULL };
- +
- +#ifdef TTY_DEBUG_WAIT_UNTIL_SENT
- + printk("%s wait until sent...\n", tty_name(tty));
- +#endif
- + if (!tty->driver.chars_in_buffer ||
- + !tty->driver.chars_in_buffer(tty))
- + return;
- + add_wait_queue(&tty->write_wait, &wait);
- + current->counter = 0; /* make us low-priority */
- + if (timeout)
- + current->timeout = timeout + jiffies;
- + else
- + current->timeout = (unsigned) -1;
- + do {
- +#ifdef TTY_DEBUG_WAIT_UNTIL_SENT
- + printk("waiting %s...(%d)\n", tty_name(tty), tty->driver.chars_in_buffer(tty));
- +#endif
- + current->state = TASK_INTERRUPTIBLE;
- + if (current->signal & ~current->blocked)
- + break;
- + if (!tty->driver.chars_in_buffer(tty))
- + break;
- + schedule();
- + } while (current->timeout);
- + current->state = TASK_RUNNING;
- + remove_wait_queue(&tty->write_wait, &wait);
- +}
- +
- +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 set_termios(struct tty_struct * tty, unsigned long arg, int opt)
- +{
- + struct termio tmp_termio;
- + struct termios tmp_termios;
- + struct termios old_termios = *tty->termios;
- + int retval, canon_change;
- +
- + retval = tty_check_change(tty);
- + if (retval)
- + return retval;
- +
- + if (opt & TERMIOS_TERMIO) {
- + retval = verify_area(VERIFY_READ, (void *) arg, sizeof(struct termio));
- + if (retval)
- + return retval;
- + tmp_termios = *tty->termios;
- + memcpy_fromfs(&tmp_termio, (struct termio *) arg,
- + sizeof (struct termio));
- +
- +#define SET_LOW_BITS(x,y) ((x) = (0xffff0000 & (x)) | (y))
- + SET_LOW_BITS(tmp_termios.c_iflag, tmp_termio.c_iflag);
- + SET_LOW_BITS(tmp_termios.c_oflag, tmp_termio.c_oflag);
- + SET_LOW_BITS(tmp_termios.c_cflag, tmp_termio.c_cflag);
- + SET_LOW_BITS(tmp_termios.c_lflag, tmp_termio.c_lflag);
- + memcpy(&tmp_termios.c_cc, &tmp_termio.c_cc, NCC);
- +#undef SET_LOW_BITS
- + } else {
- + retval = verify_area(VERIFY_READ, (void *) arg, sizeof(struct termios));
- + if (retval)
- + return retval;
- + memcpy_fromfs(&tmp_termios, (struct termios *) arg,
- + sizeof (struct termios));
- + }
- +
- + if ((opt & TERMIOS_FLUSH) && tty->ldisc.flush_buffer)
- + tty->ldisc.flush_buffer(tty);
- +
- + if (opt & TERMIOS_WAIT)
- + tty_wait_until_sent(tty, 0);
- +
- + cli();
- + *tty->termios = tmp_termios;
- + unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
- + canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON;
- + if (canon_change) {
- + memset(&tty->read_flags, 0, sizeof tty->read_flags);
- + tty->canon_head = tty->read_tail;
- + tty->canon_data = 0;
- + tty->erasing = 0;
- + }
- + sti();
- + if (canon_change && !L_ICANON(tty) && tty->read_cnt)
- + /* Get characters left over from canonical mode. */
- + wake_up_interruptible(&tty->read_wait);
- +
- + /* see if packet mode change of state */
- +
- + if (tty->link && tty->link->packet) {
- + int old_flow = ((old_termios.c_iflag & IXON) &&
- + (old_termios.c_cc[VSTOP] == '\023') &&
- + (old_termios.c_cc[VSTART] == '\021'));
- + int new_flow = (I_IXON(tty) &&
- + STOP_CHAR(tty) == '\023' &&
- + START_CHAR(tty) == '\021');
- + if (old_flow != new_flow) {
- + tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
- + if (new_flow)
- + tty->ctrl_status |= TIOCPKT_DOSTOP;
- + else
- + tty->ctrl_status |= TIOCPKT_NOSTOP;
- + wake_up_interruptible(&tty->link->read_wait);
- + }
- + }
- +
- + if (tty->driver.set_termios)
- + (*tty->driver.set_termios)(tty, &old_termios);
- +
- + if (tty->ldisc.set_termios)
- + (*tty->ldisc.set_termios)(tty, &old_termios);
- +
- + return 0;
- +}
- +
- +static int get_termio(struct tty_struct * tty, struct termio * termio)
- +{
- + int i;
- + struct termio tmp_termio;
- +
- + i = verify_area(VERIFY_WRITE, termio, sizeof (struct termio));
- + if (i)
- + return i;
- + tmp_termio.c_iflag = tty->termios->c_iflag;
- + tmp_termio.c_oflag = tty->termios->c_oflag;
- + tmp_termio.c_cflag = tty->termios->c_cflag;
- + tmp_termio.c_lflag = tty->termios->c_lflag;
- + tmp_termio.c_line = tty->termios->c_line;
- + for(i=0 ; i < NCC ; i++)
- + tmp_termio.c_cc[i] = tty->termios->c_cc[i];
- + memcpy_tofs(termio, &tmp_termio, sizeof (struct termio));
- + return 0;
- +}
- +
- +static unsigned long inq_canon(struct tty_struct * tty)
- +{
- + int nr, head, tail;
- +
- + if (!tty->canon_data || !tty->read_buf)
- + return 0;
- + head = tty->canon_head;
- + tail = tty->read_tail;
- + nr = (head - tail) & (N_TTY_BUF_SIZE-1);
- + /* Skip EOF-chars.. */
- + while (head != tail) {
- + if (test_bit(tail, &tty->read_flags) &&
- + tty->read_buf[tail] == __DISABLED_CHAR)
- + nr--;
- + tail = (tail+1) & (N_TTY_BUF_SIZE-1);
- + }
- + return nr;
- +}
- +
- +int n_tty_ioctl(struct tty_struct * tty, struct file * file,
- + unsigned int cmd, unsigned long arg)
- +{
- + struct tty_struct * real_tty;
- + int retval;
- + int opt = 0;
- +
- + if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
- + tty->driver.subtype == PTY_TYPE_MASTER)
- + real_tty = tty->link;
- + else
- + real_tty = tty;
- +
- + switch (cmd) {
- + case TCGETS:
- + retval = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof (struct termios));
- + if (retval)
- + return retval;
- + memcpy_tofs((struct termios *) arg,
- + real_tty->termios,
- + sizeof (struct termios));
- + return 0;
- + case TCSETSF:
- + opt |= TERMIOS_FLUSH;
- + case TCSETSW:
- + opt |= TERMIOS_WAIT;
- + case TCSETS:
- + return set_termios(real_tty, arg, opt);
- + case TCGETA:
- + return get_termio(real_tty,(struct termio *) arg);
- + case TCSETAF:
- + opt |= TERMIOS_FLUSH;
- + case TCSETAW:
- + opt |= TERMIOS_WAIT;
- + case TCSETA:
- + return set_termios(real_tty, arg, opt|TERMIOS_TERMIO);
- + case TCXONC:
- + retval = tty_check_change(tty);
- + if (retval)
- + return retval;
- + switch (arg) {
- + case TCOOFF:
- + stop_tty(tty);
- + break;
- + case TCOON:
- + start_tty(tty);
- + break;
- + case TCIOFF:
- + if (STOP_CHAR(tty) != __DISABLED_CHAR)
- + tty->driver.write(tty, 0,
- + &STOP_CHAR(tty), 1);
- + break;
- + case TCION:
- + if (START_CHAR(tty) != __DISABLED_CHAR)
- + tty->driver.write(tty, 0,
- + &START_CHAR(tty), 1);
- + break;
- + default:
- + return -EINVAL;
- + }
- + return 0;
- + case TCFLSH:
- + retval = tty_check_change(tty);
- + if (retval)
- + return retval;
- + switch (arg) {
- + case TCIFLUSH:
- + if (tty->ldisc.flush_buffer)
- + tty->ldisc.flush_buffer(tty);
- + break;
- + case TCIOFLUSH:
- + if (tty->ldisc.flush_buffer)
- + tty->ldisc.flush_buffer(tty);
- + /* fall through */
- + case TCOFLUSH:
- + if (tty->driver.flush_buffer)
- + tty->driver.flush_buffer(tty);
- + break;
- + default:
- + return -EINVAL;
- + }
- + return 0;
- + case TIOCOUTQ:
- + retval = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof (unsigned long));
- + if (retval)
- + return retval;
- + if (tty->driver.chars_in_buffer)
- + put_fs_long(tty->driver.chars_in_buffer(tty),
- + (unsigned long *) arg);
- + else
- + put_fs_long(0, (unsigned long *) arg);
- + return 0;
- + case TIOCINQ:
- + retval = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof (unsigned long));
- + if (retval)
- + return retval;
- + if (L_ICANON(tty))
- + put_fs_long(inq_canon(tty),
- + (unsigned long *) arg);
- + else
- + put_fs_long(tty->read_cnt,
- + (unsigned long *) arg);
- + return 0;
- + case TIOCGLCKTRMIOS:
- + retval = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof (struct termios));
- + if (retval)
- + return retval;
- + memcpy_tofs((struct termios *) arg,
- + real_tty->termios_locked,
- + sizeof (struct termios));
- + return 0;
- + case TIOCSLCKTRMIOS:
- + if (!suser())
- + return -EPERM;
- + retval = verify_area(VERIFY_READ, (void *) arg,
- + sizeof (struct termios));
- + if (retval)
- + return retval;
- + memcpy_fromfs(real_tty->termios_locked,
- + (struct termios *) arg,
- + sizeof (struct termios));
- + return 0;
- + case TIOCPKT:
- + if (tty->driver.type != TTY_DRIVER_TYPE_PTY ||
- + tty->driver.subtype != PTY_TYPE_MASTER)
- + return -ENOTTY;
- + retval = verify_area(VERIFY_READ, (void *) arg,
- + sizeof (unsigned long));
- + if (retval)
- + return retval;
- + if (get_fs_long(arg)) {
- + if (!tty->packet) {
- + tty->packet = 1;
- + tty->link->ctrl_status = 0;
- + }
- + } else
- + tty->packet = 0;
- + return 0;
- + /* These two ioctl's always return success; even if */
- + /* the driver doesn't support them. */
- + case TCSBRK: case TCSBRKP:
- + retval = tty_check_change(tty);
- + if (retval)
- + return retval;
- + tty_wait_until_sent(tty, 0);
- + if (!tty->driver.ioctl)
- + return 0;
- + tty->driver.ioctl(tty, file, cmd, arg);
- + return 0;
- + default:
- + return -ENOIOCTLCMD;
- + }
- +}
- diff -urNwbB linux/arch/arm/drivers/char/uni_hash.tbl linux.arm/arch/arm/drivers/char/uni_hash.tbl
- --- linux/arch/arm/drivers/char/uni_hash.tbl Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/uni_hash.tbl Sun Feb 11 18:15:12 1996
- @@ -0,0 +1,88 @@
- +/*
- + * uni_hash.tbl
- + *
- + * Do not edit this file; it was automatically generated by
- + *
- + * conmakehash cp437.uni > uni_hash.tbl
- + *
- + */
- +
- +#include <linux/types.h>
- +#include <linux/kd.h>
- +
- +static u8 dfont_unicount[256] =
- +{
- + 1, 1, 1, 1, 2, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 2, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 2, 2, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 5, 1, 2, 1, 4, 1, 1,
- + 1, 5, 1, 2, 1, 1, 1, 5,
- + 1, 1, 2, 1, 1, 4, 1, 1,
- + 1, 2, 1, 1, 1, 1, 1, 2,
- + 1, 2, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 2,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 2, 2, 1, 1, 2, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 2,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 2, 1, 1, 1, 1, 2, 1,
- + 2, 1, 2, 1, 1, 2, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 2, 1
- +};
- +
- +static u16 dfont_unitable[295] =
- +{
- + 0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x25c6, 0x2663, 0x2660,
- + 0x2022, 0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b,
- + 0x263c, 0x25ba, 0x25c4, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac,
- + 0x21a8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2,
- + 0x25bc, 0x0020, 0x0021, 0x0022, 0x00a8, 0x0023, 0x0024, 0x0025,
- + 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x00b8,
- + 0x002d, 0x00ad, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033,
- + 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b,
- + 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x00c0, 0x00c1,
- + 0x00c2, 0x00c3, 0x0042, 0x0043, 0x00a9, 0x0044, 0x0045, 0x00c8,
- + 0x00ca, 0x00cb, 0x0046, 0x0047, 0x0048, 0x0049, 0x00cc, 0x00cd,
- + 0x00ce, 0x00cf, 0x004a, 0x004b, 0x212a, 0x004c, 0x004d, 0x004e,
- + 0x004f, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x0050, 0x0051, 0x0052,
- + 0x00ae, 0x0053, 0x0054, 0x0055, 0x00d9, 0x00da, 0x00db, 0x0056,
- + 0x0057, 0x0058, 0x0059, 0x00dd, 0x005a, 0x005b, 0x005c, 0x005d,
- + 0x005e, 0x005f, 0xf804, 0x0060, 0x0061, 0x00e3, 0x0062, 0x0063,
- + 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b,
- + 0x006c, 0x006d, 0x006e, 0x006f, 0x00f5, 0x0070, 0x0071, 0x0072,
- + 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x00d7, 0x0079,
- + 0x00fd, 0x007a, 0x007b, 0x007c, 0x00a5, 0x007d, 0x007e, 0x2302,
- + 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
- + 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
- + 0x212b, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb,
- + 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7,
- + 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa,
- + 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab,
- + 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562,
- + 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b,
- + 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e,
- + 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c,
- + 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553,
- + 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590,
- + 0x2580, 0x03b1, 0x03b2, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3,
- + 0x00b5, 0x03bc, 0x03c4, 0x03a6, 0x00d8, 0x0398, 0x03a9, 0x2126,
- + 0x03b4, 0x221e, 0x03c6, 0x00f8, 0x03b5, 0x2229, 0x2261, 0x00b1,
- + 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219,
- + 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0xfffd, 0x00a0
- +};
- \ No newline at end of file
- diff -urNwbB linux/arch/arm/drivers/char/vc_screen.c linux.arm/arch/arm/drivers/char/vc_screen.c
- --- linux/arch/arm/drivers/char/vc_screen.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/vc_screen.c Sat Feb 24 18:12:00 1996
- @@ -0,0 +1,232 @@
- +/*
- + * linux/arch/arm/drivers/char/vc_screen.c
- + *
- + * Provide access to virtual console memory.
- + * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled)
- + * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
- + * [minor: N]
- + *
- + * /dev/vcsaN: idem, but including attributes, and prefixed with
- + * the 4 bytes lines,columns,x,y (as screendump used to give)
- + * [minor: N+128]
- + *
- + * This replaces screendump and part of selection, so that the system
- + * administrator can control access using file system permissions.
- + *
- + * aeb@cwi.nl - efter Friedas begravelse - 950211
- + *
- + * Modified by Russell King (01/01/96) [experimental + in development]
- + */
- +
- +#include <linux/kernel.h>
- +#include <linux/major.h>
- +#include <linux/errno.h>
- +#include <linux/tty.h>
- +#include <linux/fs.h>
- +#include <asm/segment.h>
- +#include "vt_kern.h"
- +#include "selection.h"
- +
- +#define HEADER_SIZE 4
- +#define video_num_lines 75
- +#define video_num_columns 100
- +
- +static inline int
- +vcs_size(struct inode *inode)
- +{
- + int size = video_num_lines * video_num_columns;
- + if (MINOR(inode->i_rdev) & 128)
- + size = 4*size + HEADER_SIZE;
- + return size;
- +}
- +
- +static int
- +vcs_lseek(struct inode *inode, struct file *file, off_t offset, int orig)
- +{
- + int size = vcs_size(inode);
- +
- + switch (orig) {
- + case 0:
- + file->f_pos = offset;
- + break;
- + case 1:
- + file->f_pos += offset;
- + break;
- + case 2:
- + file->f_pos = size + offset;
- + break;
- + default:
- + return -EINVAL;
- + }
- + if (file->f_pos < 0 || file->f_pos > size)
- + return -EINVAL;
- + return file->f_pos;
- +}
- +
- +static int
- +vcs_read(struct inode *inode, struct file *file, char *buf, int count)
- +{
- + unsigned long p = file->f_pos;
- + unsigned int cons = MINOR(inode->i_rdev);
- + int attr, size, read;
- + char *buf0;
- + unsigned long *org, d;
- +
- + attr = (cons & 128);
- + cons = (cons & 127);
- + if (cons == 0)
- + cons = fg_console;
- + else
- + cons--;
- + if (!vc_cons_allocated(cons))
- + return -ENXIO;
- +
- + size = vcs_size(inode);
- + if (count < 0 || p > size)
- + return -EINVAL;
- + if (count > size - p)
- + count = size - p;
- +
- + buf0 = buf;
- + if (!attr) {
- + org = screen_pos(cons, p);
- + while (count-- > 0)
- + put_fs_byte(*org++ & 0xff, buf++);
- + } else {
- + if (p < HEADER_SIZE) {
- + char header[HEADER_SIZE];
- + header[0] = (char) video_num_lines;
- + header[1] = (char) video_num_columns;
- + getconsxy(cons, header+2);
- + while (p < HEADER_SIZE && count-- > 0)
- + put_fs_byte(header[p++], buf++);
- + }
- + p -= HEADER_SIZE;
- + org = screen_pos(cons, p>>2);
- + if (p & 3)
- + d = *org++;
- + if ((p & 3)>0 && count-- > 0)
- + put_fs_byte(d >> 24, buf++);
- + if ((p & 3)>1 && count-- > 0)
- + put_fs_byte((d >> 16) & 0xff, buf++);
- + if ((p & 3)>2 && count-- > 0)
- + put_fs_byte((d >> 8) & 0xff, buf++);
- + while (count > 1) {
- + put_fs_long(*org++, buf);
- + buf += 4;
- + count -= 4;
- + }
- + if (count > 0)
- + {
- + d = *org;
- + put_fs_byte(d & 0xff, buf++);
- + if (count > 1)
- + put_fs_byte((d >> 8) & 0xff, buf++);
- + if (count > 2)
- + put_fs_byte((d >> 16) & 0xff, buf++);
- + }
- + }
- + read = buf - buf0;
- + file->f_pos += read;
- + return read;
- +}
- +
- +static int
- +vcs_write(struct inode *inode, struct file *file, const char *buf, int count)
- +{
- + unsigned long p = file->f_pos;
- + unsigned int cons = MINOR(inode->i_rdev);
- + int viewed, attr, size, written;
- + const char *buf0;
- + unsigned long *org;
- +
- + attr = (cons & 128);
- + cons = (cons & 127);
- + if (cons == 0) {
- + cons = fg_console;
- + viewed = 1;
- + } else {
- + cons--;
- + viewed = 0;
- + }
- + if (!vc_cons_allocated(cons))
- + return -ENXIO;
- +
- + size = vcs_size(inode);
- + if (count < 0 || p > size)
- + return -EINVAL;
- + if (count > size - p)
- + count = size - p;
- +
- + buf0 = buf;
- + if (!attr) {
- + org = screen_pos(cons, p);
- + while (count-- > 0) {
- + *org = (*org & 0xffffff00) | get_fs_byte(buf++);
- + ll_char_write (*org);
- + org++;
- + }
- + } else {
- +#if 0
- + if (p < HEADER_SIZE) {
- + char header[HEADER_SIZE];
- + getconsxy(cons, header+2);
- + while (p < HEADER_SIZE && count-- > 0)
- + header[p++] = get_fs_byte(buf++);
- + if (!viewed)
- + putconsxy(cons, header+2);
- + }
- + p -= HEADER_SIZE;
- + org = screen_pos(cons, p>>2, viewed);
- + if ((p & 1) && count-- > 0) {
- + scr_writew((get_fs_byte(buf++) << 8) |
- + (scr_readw(org) & 0xff), org);
- + org++;
- + }
- + while (count > 1) {
- + scr_writew(get_fs_word(buf), org++);
- + buf += 2;
- + count -= 2;
- + }
- + if (count > 0)
- + scr_writew((scr_readw(org) & 0xff00) |
- + get_fs_byte(buf++), org);
- +#endif
- + }
- + written = buf - buf0;
- + update_scrmem (cons, file->f_pos, written);
- + file->f_pos += written;
- + return written;
- +}
- +
- +static int
- +vcs_open(struct inode *inode, struct file *filp)
- +{
- + unsigned int cons = (MINOR(inode->i_rdev) & 127);
- + if(cons && !vc_cons_allocated(cons-1))
- + return -ENXIO;
- + return 0;
- +}
- +
- +static struct file_operations vcs_fops = {
- + vcs_lseek, /* lseek */
- + vcs_read, /* read */
- + vcs_write, /* write */
- + NULL, /* readdir */
- + NULL, /* select */
- + NULL, /* ioctl */
- + NULL, /* mmap */
- + vcs_open, /* open */
- + NULL, /* release */
- + NULL /* fsync */
- +};
- +
- +int vcs_init(void)
- +{
- + int error;
- +
- + error = register_chrdev(VCS_MAJOR, "vcs", &vcs_fops);
- + if (error)
- + printk("unable to get major %d for vcs device", VCS_MAJOR);
- + return error;
- +}
- diff -urNwbB linux/arch/arm/drivers/char/vt.c linux.arm/arch/arm/drivers/char/vt.c
- --- linux/arch/arm/drivers/char/vt.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/vt.c Sun Mar 3 12:49:38 1996
- @@ -0,0 +1,935 @@
- +/*
- + * linux/arch/arm/drivers/char/vt.c
- + *
- + * Copyright (C) 1992 obz under the linux copyright
- + *
- + * Dynamic diacritical handling - aeb@cwi.nl - Dec 1993
- + * Dynamic keymap and string allocation - aeb@cwi.nl - May 1994
- + * Ported to ARM - rmk92@ecs.soton.ac.uk - July 1995
- + */
- +
- +#include <linux/types.h>
- +#include <linux/errno.h>
- +#include <linux/sched.h>
- +#include <linux/tty.h>
- +#include <linux/timer.h>
- +#include <linux/kernel.h>
- +#include <linux/kd.h>
- +#include <linux/vt.h>
- +#include <linux/string.h>
- +#include <linux/malloc.h>
- +
- +#include <asm/io.h>
- +#include <asm/segment.h>
- +
- +#include "kbd_kern.h"
- +#include "vt_kern.h"
- +#include "diacr.h"
- +
- +
- +extern struct tty_driver console_driver;
- +extern int sel_cons;
- +
- +#define VT_IS_IN_USE(i) (console_driver.table[i] && console_driver.table[i]->count)
- +#define VT_BUSY(i) (VT_IS_IN_USE(i) || i == fg_console || i == sel_cons)
- +
- +/*
- + * Console (vt and kd) routines, as defined by USL SVR4 manual, and by
- + * experimentation and study of X386 SYSV handling.
- + *
- + * One point of difference: SYSV vt's are /dev/vtX, which X >= 0, and
- + * /dev/console is a separate ttyp. Under Linux, /dev/tty0 is /dev/console,
- + * and the vc start at /dev/ttyX, X >= 1. We maintain that here, so we will
- + * always treat our set of vt as numbered 1..MAX_NR_CONSOLES (corresponding to
- + * ttys 0..MAX_NR_CONSOLES-1). Explicitly naming VT 0 is illegal, but using
- + * /dev/tty0 (fg_console) as a target is legal, since an implicit aliasing
- + * to the current console is done by the main ioctl code.
- + */
- +
- +struct vt_struct *vt_cons[MAX_NR_CONSOLES];
- +
- +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on);
- +
- +extern int getkeycode(unsigned int scancode);
- +extern int setkeycode(unsigned int scancode, unsigned int keycode);
- +extern void compute_shiftstate(void);
- +extern void change_console(unsigned int new_console);
- +extern void complete_change_console(unsigned int new_console);
- +extern int vt_waitactive(void);
- +extern void do_blank_screen(int nopowersave);
- +extern void do_unblank_screen(void);
- +
- +extern unsigned int keymap_count;
- +
- +/*
- + * routines to load custom translation table, EGA/VGA font and
- + * VGA colour palette from console.c
- + */
- +extern int con_set_trans_old(unsigned char * table);
- +extern int con_get_trans_old(unsigned char * table);
- +extern int con_set_trans_new(unsigned short * table);
- +extern int con_get_trans_new(unsigned short * table);
- +extern void con_clear_unimap(struct unimapinit *ui);
- +extern int con_set_unimap(ushort ct, struct unipair *list);
- +extern int con_get_unimap(ushort ct, ushort *uct, struct unipair *list);
- +extern void con_set_default_unimap(void);
- +extern int con_set_font(char * fontmap);
- +extern int con_get_font(char * fontmap);
- +
- +/*
- + * these are the valid i/o ports we're allowed to change. they map all the
- + * video ports
- + */
- +#define GPFIRST 0x3b4
- +#define GPLAST 0x3df
- +#define GPNUM (GPLAST - GPFIRST + 1)
- +
- +/*
- + * Generates sound of some count for some number of clock ticks
- + * [count = 1193180 / frequency]
- + *
- + * If freq is 0, will turn off sound, else will turn it on for that time.
- + * If msec is 0, will return immediately, else will sleep for msec time, then
- + * turn sound off.
- + *
- + * We use the BEEP_TIMER vector since we're using the same method to
- + * generate sound, and we'll overwrite any beep in progress. That may
- + * be something to fix later, if we like.
- + *
- + * We also return immediately, which is what was implied within the X
- + * comments - KDMKTONE doesn't put the process to sleep.
- + */
- +static inline void
- +kd_mksound2(unsigned int count, unsigned int vol, unsigned int ticks)
- +{
- + extern void mk_beep (unsigned int count, unsigned int vol, unsigned int ticks);
- +#ifdef CONFIG_SOUND
- + mk_beep(count, vol, ticks);
- +#endif
- +}
- +
- +void
- +kd_mksound(unsigned int count, unsigned int ticks)
- +{
- + kd_mksound2(count, 72, ticks);
- +}
- +
- +/*
- + * We handle the console-specific ioctl's here. We allow the
- + * capability to modify any console, not just the fg_console.
- + */
- +int vt_ioctl(struct tty_struct *tty, struct file * file,
- + unsigned int cmd, unsigned long arg)
- +{
- + int i, perm;
- + unsigned int console;
- + unsigned char ucval;
- + struct kbd_struct * kbd;
- + struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
- +
- + console = vt->vc_num;
- +
- + if (!vc_cons_allocated(console)) /* impossible? */
- + return -ENOIOCTLCMD;
- +
- + /*
- + * To have permissions to do most of the vt ioctls, we either have
- + * to be the owner of the tty, or super-user.
- + */
- + perm = 0;
- + if (current->tty == tty || suser())
- + perm = 1;
- +
- + kbd = kbd_table + console;
- + switch (cmd) {
- + case KIOCSOUND:
- + if (!perm)
- + return -EPERM;
- + kd_mksound((unsigned int)arg, 5);
- + return 0;
- +
- + case KDMKTONE:
- + if (!perm)
- + return -EPERM;
- + {
- + unsigned int freq = ((unsigned long *)arg)[0];
- + unsigned int vol = ((unsigned long *)arg)[1];
- + unsigned int ticks = HZ * (((unsigned long *)arg)[2] & 0xffff) / 1000;
- +
- + /*
- + * Generate the tone for the appropriate number of ticks.
- + * If the time is zero, turn off sound ourselves.
- + */
- + kd_mksound2(freq, vol, ticks);
- + return 0;
- + }
- +
- + case KDGKBTYPE:
- + /*
- + * this is naive.
- + */
- + i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
- + if (!i)
- + put_user(KB_101, (char *) arg);
- + return i;
- +
- + case KDADDIO:
- + case KDDELIO:
- + /*
- + * KDADDIO and KDDELIO may be able to add ports beyond what
- + * we reject here, but to be safe...
- + */
- + if (arg < GPFIRST || arg > GPLAST)
- + return -EINVAL;
- + return sys_ioperm(arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0;
- +
- + case KDENABIO:
- + case KDDISABIO:
- + return sys_ioperm(GPFIRST, GPNUM,
- + (cmd == KDENABIO)) ? -ENXIO : 0;
- +
- + case KDSETMODE:
- + /*
- + * currently, setting the mode from KD_TEXT to KD_GRAPHICS
- + * doesn't do a whole lot. i'm not sure if it should do any
- + * restoration of modes or what...
- + */
- + if (!perm)
- + return -EPERM;
- + switch (arg) {
- + case KD_GRAPHICS:
- + break;
- + case KD_TEXT0:
- + case KD_TEXT1:
- + arg = KD_TEXT;
- + case KD_TEXT:
- + break;
- + default:
- + return -EINVAL;
- + }
- + if (vt_cons[console]->vc_mode == (unsigned char) arg)
- + return 0;
- + vt_cons[console]->vc_mode = (unsigned char) arg;
- + if (console != fg_console)
- + return 0;
- + /*
- + * explicitly blank/unblank the screen if switching modes
- + */
- + if (arg == KD_TEXT)
- + do_unblank_screen();
- + else
- + do_blank_screen(1);
- + return 0;
- +
- + case KDGETMODE:
- + i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
- + if (!i)
- + put_user(vt_cons[console]->vc_mode, (unsigned long *) arg);
- + return i;
- +
- + case KDMAPDISP:
- + case KDUNMAPDISP:
- + /*
- + * these work like a combination of mmap and KDENABIO.
- + * this could be easily finished.
- + */
- + return -EINVAL;
- +
- + case KDSKBMODE:
- + if (!perm)
- + return -EPERM;
- + switch(arg) {
- + case K_RAW:
- + kbd->kbdmode = VC_RAW;
- + break;
- + case K_MEDIUMRAW:
- + kbd->kbdmode = VC_MEDIUMRAW;
- + break;
- + case K_XLATE:
- + kbd->kbdmode = VC_XLATE;
- + compute_shiftstate();
- + break;
- + case K_UNICODE:
- + kbd->kbdmode = VC_UNICODE;
- + compute_shiftstate();
- + break;
- + default:
- + return -EINVAL;
- + }
- + if (tty->ldisc.flush_buffer)
- + tty->ldisc.flush_buffer(tty);
- + return 0;
- +
- + case KDGKBMODE:
- + i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
- + if (!i) {
- + ucval = ((kbd->kbdmode == VC_RAW) ? K_RAW :
- + (kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW :
- + (kbd->kbdmode == VC_UNICODE) ? K_UNICODE :
- + K_XLATE);
- + put_user(ucval, (int *) arg);
- + }
- + return i;
- +
- + /* this could be folded into KDSKBMODE, but for compatibility
- + reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */
- + case KDSKBMETA:
- + switch(arg) {
- + case K_METABIT:
- + clr_vc_kbd_mode(kbd, VC_META);
- + break;
- + case K_ESCPREFIX:
- + set_vc_kbd_mode(kbd, VC_META);
- + break;
- + default:
- + return -EINVAL;
- + }
- + return 0;
- +
- + case KDGKBMETA:
- + i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
- + if (!i) {
- + ucval = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX :
- + K_METABIT);
- + put_user(ucval, (int *) arg);
- + }
- + return i;
- +
- + case KDGETKEYCODE:
- + {
- + struct kbkeycode * const a = (struct kbkeycode *)arg;
- + unsigned int sc;
- + int kc;
- +
- + i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbkeycode));
- + if (i)
- + return i;
- + sc = get_user(&a->scancode);
- + kc = getkeycode(sc);
- + if (kc < 0)
- + return kc;
- + put_user(kc, &a->keycode);
- + return 0;
- + }
- +
- + case KDSETKEYCODE:
- + {
- + struct kbkeycode * const a = (struct kbkeycode *)arg;
- + unsigned int sc, kc;
- +
- + if (!perm)
- + return -EPERM;
- + i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbkeycode));
- + if (i)
- + return i;
- + sc = get_user(&a->scancode);
- + kc = get_user(&a->keycode);
- + return setkeycode(sc, kc);
- + }
- +
- + case KDGKBENT:
- + {
- + struct kbentry * const a = (struct kbentry *)arg;
- + ushort *key_map, val;
- + u_char s;
- +
- + i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbentry));
- + if (i)
- + return i;
- + if ((i = get_user(&a->kb_index)) >= NR_KEYS)
- + return -EINVAL;
- + if ((s = get_user(&a->kb_table)) >= MAX_NR_KEYMAPS)
- + return -EINVAL;
- + key_map = key_maps[s];
- + if (key_map) {
- + val = U(key_map[i]);
- + if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
- + val = K_HOLE;
- + } else
- + val = (i ? K_HOLE : K_NOSUCHMAP);
- + put_user(val, &a->kb_value);
- + return 0;
- + }
- +
- + case KDSKBENT:
- + {
- + const struct kbentry * a = (struct kbentry *)arg;
- + ushort *key_map;
- + u_char s;
- + u_short v, ov;
- +
- + if (!perm)
- + return -EPERM;
- + i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbentry));
- + if (i)
- + return i;
- + if ((i = get_user(&a->kb_index)) >= NR_KEYS)
- + return -EINVAL;
- + if ((s = get_user(&a->kb_table)) >= MAX_NR_KEYMAPS)
- + return -EINVAL;
- + v = get_user(&a->kb_value);
- + if (!i && v == K_NOSUCHMAP) {
- + /* disallocate map */
- + key_map = key_maps[s];
- + if (s && key_map) {
- + key_maps[s] = 0;
- + if (key_map[0] == U(K_ALLOCATED)) {
- + kfree_s(key_map, sizeof(plain_map));
- + keymap_count--;
- + }
- + }
- + return 0;
- + }
- +
- + if (KTYP(v) < NR_TYPES) {
- + if (KVAL(v) > max_vals[KTYP(v)])
- + return -EINVAL;
- + } else
- + if (kbd->kbdmode != VC_UNICODE)
- + return -EINVAL;
- +
- + /* assignment to entry 0 only tests validity of args */
- + if (!i)
- + return 0;
- +
- + if (!(key_map = key_maps[s])) {
- + int j;
- +
- + if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !suser())
- + return -EPERM;
- +
- + key_map = (ushort *) kmalloc(sizeof(plain_map),
- + GFP_KERNEL);
- + if (!key_map)
- + return -ENOMEM;
- + key_maps[s] = key_map;
- + key_map[0] = U(K_ALLOCATED);
- + for (j = 1; j < NR_KEYS; j++)
- + key_map[j] = U(K_HOLE);
- + keymap_count++;
- + }
- + ov = U(key_map[i]);
- + if (v == ov)
- + return 0; /* nothing to do */
- + /*
- + * Only the Superuser can set or unset the Secure
- + * Attention Key.
- + */
- + if (((ov == K_SAK) || (v == K_SAK)) && !suser())
- + return -EPERM;
- + key_map[i] = U(v);
- + if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
- + compute_shiftstate();
- + return 0;
- + }
- +
- + case KDGKBSENT:
- + {
- + struct kbsentry *a = (struct kbsentry *)arg;
- + char *p;
- + u_char *q;
- + int sz;
- +
- + i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbsentry));
- + if (i)
- + return i;
- + if ((i = get_user(&a->kb_func)) >= MAX_NR_FUNC || i < 0)
- + return -EINVAL;
- + sz = sizeof(a->kb_string) - 1; /* sz should have been
- + a struct member */
- + q = a->kb_string;
- + p = func_table[i];
- + if(p)
- + for ( ; *p && sz; p++, sz--)
- + put_user(*p, q++);
- + put_user('\0', q);
- + return ((p && *p) ? -EOVERFLOW : 0);
- + }
- +
- + case KDSKBSENT:
- + {
- + struct kbsentry * const a = (struct kbsentry *)arg;
- + int delta;
- + char *first_free, *fj, *fnw;
- + int j, k, sz;
- + u_char *p;
- + char *q;
- +
- + if (!perm)
- + return -EPERM;
- + i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry));
- + if (i)
- + return i;
- + if ((i = get_user(&a->kb_func)) >= MAX_NR_FUNC)
- + return -EINVAL;
- + q = func_table[i];
- +
- + first_free = funcbufptr + (funcbufsize - funcbufleft);
- + for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) ;
- + if (j < MAX_NR_FUNC)
- + fj = func_table[j];
- + else
- + fj = first_free;
- +
- + delta = (q ? -strlen(q) : 1);
- + sz = sizeof(a->kb_string); /* sz should have been
- + a struct member */
- + for (p = a->kb_string; get_user(p) && sz; p++,sz--)
- + delta++;
- + if (!sz)
- + return -EOVERFLOW;
- + if (delta <= funcbufleft) { /* it fits in current buf */
- + if (j < MAX_NR_FUNC) {
- + memmove(fj + delta, fj, first_free - fj);
- + for (k = j; k < MAX_NR_FUNC; k++)
- + if (func_table[k])
- + func_table[k] += delta;
- + }
- + if (!q)
- + func_table[i] = fj;
- + funcbufleft -= delta;
- + } else { /* allocate a larger buffer */
- + sz = 256;
- + while (sz < funcbufsize - funcbufleft + delta)
- + sz <<= 1;
- + fnw = (char *) kmalloc(sz, GFP_KERNEL);
- + if(!fnw)
- + return -ENOMEM;
- +
- + if (!q)
- + func_table[i] = fj;
- + if (fj > funcbufptr)
- + memmove(fnw, funcbufptr, fj - funcbufptr);
- + for (k = 0; k < j; k++)
- + if (func_table[k])
- + func_table[k] = fnw + (func_table[k] - funcbufptr);
- +
- + if (first_free > fj) {
- + memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
- + for (k = j; k < MAX_NR_FUNC; k++)
- + if (func_table[k])
- + func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
- + }
- + if (funcbufptr != func_buf)
- + kfree_s(funcbufptr, funcbufsize);
- + funcbufptr = fnw;
- + funcbufleft = funcbufleft - delta + sz - funcbufsize;
- + funcbufsize = sz;
- + }
- + for (p = a->kb_string, q = func_table[i]; ; p++, q++)
- + if (!(*q = get_user(p)))
- + break;
- + return 0;
- + }
- +
- + case KDGKBDIACR:
- + {
- + struct kbdiacrs *a = (struct kbdiacrs *)arg;
- +
- + i = verify_area(VERIFY_WRITE, (void *) a, sizeof(struct kbdiacrs));
- + if (i)
- + return i;
- + put_user(accent_table_size, &a->kb_cnt);
- + memcpy_tofs(a->kbdiacr, accent_table,
- + accent_table_size*sizeof(struct kbdiacr));
- + return 0;
- + }
- +
- + case KDSKBDIACR:
- + {
- + struct kbdiacrs *a = (struct kbdiacrs *)arg;
- + unsigned int ct;
- +
- + if (!perm)
- + return -EPERM;
- + i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs));
- + if (i)
- + return i;
- + ct = get_user(&a->kb_cnt);
- + if (ct >= MAX_DIACR)
- + return -EINVAL;
- + accent_table_size = ct;
- + memcpy_fromfs(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr));
- + return 0;
- + }
- +
- + /* the ioctls below read/set the flags usually shown in the leds */
- + /* don't use them - they will go away without warning */
- + case KDGKBLED:
- + i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
- + if (i)
- + return i;
- + put_user(kbd->ledflagstate |
- + (kbd->default_ledflagstate << 4), (char *) arg);
- + return 0;
- +
- + case KDSKBLED:
- + if (!perm)
- + return -EPERM;
- + if (arg & ~0x77)
- + return -EINVAL;
- + kbd->ledflagstate = (arg & 7);
- + kbd->default_ledflagstate = ((arg >> 4) & 7);
- + set_leds();
- + return 0;
- +
- + /* the ioctls below only set the lights, not the functions */
- + /* for those, see KDGKBLED and KDSKBLED above */
- + case KDGETLED:
- + i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
- + if (i)
- + return i;
- + put_user(getledstate(), (char *) arg);
- + return 0;
- +
- + case KDSETLED:
- + if (!perm)
- + return -EPERM;
- + setledstate(kbd, arg);
- + return 0;
- +
- + /*
- + * A process can indicate its willingness to accept signals
- + * generated by pressing an appropriate key combination.
- + * Thus, one can have a daemon that e.g. spawns a new console
- + * upon a keypess and then changes to it.
- + * Probably init should be changed to do this (and have a
- + * field ks (`keyboard signal') in inittab describing the
- + * desired acion), so that the number of background daemons
- + * does not increase.
- + */
- + case KDSIGACCEPT:
- + {
- + extern int spawnpid, spawnsig;
- + if (!perm)
- + return -EPERM;
- + if (arg < 1 || arg > NSIG || arg == SIGKILL)
- + return -EINVAL;
- + spawnpid = current->pid;
- + spawnsig = arg;
- + return 0;
- + }
- +
- + case VT_SETMODE:
- + {
- + struct vt_mode *vtmode = (struct vt_mode *)arg;
- + char mode;
- +
- + if (!perm)
- + return -EPERM;
- + i = verify_area(VERIFY_WRITE, (void *)vtmode, sizeof(struct vt_mode));
- + if (i)
- + return i;
- + mode = get_user(&vtmode->mode);
- + if (mode != VT_AUTO && mode != VT_PROCESS)
- + return -EINVAL;
- + vt_cons[console]->vt_mode.mode = mode;
- + vt_cons[console]->vt_mode.waitv = get_user(&vtmode->waitv);
- + vt_cons[console]->vt_mode.relsig = get_user(&vtmode->relsig);
- + vt_cons[console]->vt_mode.acqsig = get_user(&vtmode->acqsig);
- + /* the frsig is ignored, so we set it to 0 */
- + vt_cons[console]->vt_mode.frsig = 0;
- + vt_cons[console]->vt_pid = current->pid;
- + vt_cons[console]->vt_newvt = 0;
- + return 0;
- + }
- +
- + case VT_GETMODE:
- + {
- + struct vt_mode *vtmode = (struct vt_mode *)arg;
- +
- + i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct vt_mode));
- + if (i)
- + return i;
- + put_user(vt_cons[console]->vt_mode.mode, &vtmode->mode);
- + put_user(vt_cons[console]->vt_mode.waitv, &vtmode->waitv);
- + put_user(vt_cons[console]->vt_mode.relsig, &vtmode->relsig);
- + put_user(vt_cons[console]->vt_mode.acqsig, &vtmode->acqsig);
- + put_user(vt_cons[console]->vt_mode.frsig, &vtmode->frsig);
- + return 0;
- + }
- +
- + /*
- + * Returns global vt state. Note that VT 0 is always open, since
- + * it's an alias for the current VT, and people can't use it here.
- + * We cannot return state for more than 16 VTs, since v_state is short.
- + */
- + case VT_GETSTATE:
- + {
- + struct vt_stat *vtstat = (struct vt_stat *)arg;
- + unsigned short state, mask;
- +
- + i = verify_area(VERIFY_WRITE,(void *)vtstat, sizeof(struct vt_stat));
- + if (i)
- + return i;
- + put_user(fg_console + 1, &vtstat->v_active);
- + state = 1; /* /dev/tty0 is always open */
- + for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask; ++i, mask <<= 1)
- + if (VT_IS_IN_USE(i))
- + state |= mask;
- + put_user(state, &vtstat->v_state);
- + return 0;
- + }
- +
- + /*
- + * Returns the first available (non-opened) console.
- + */
- + case VT_OPENQRY:
- + i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
- + if (i)
- + return i;
- + for (i = 0; i < MAX_NR_CONSOLES; ++i)
- + if (! VT_IS_IN_USE(i))
- + break;
- + put_user(i < MAX_NR_CONSOLES ? (i+1) : -1, (int *) arg);
- + return 0;
- +
- + /*
- + * ioctl(fd, VT_ACTIVATE, num) will cause us to switch to vt # num,
- + * with num >= 1 (switches to vt 0, our console, are not allowed, just
- + * to preserve sanity).
- + */
- + case VT_ACTIVATE:
- + if (!perm)
- + return -EPERM;
- + if (arg == 0 || arg > MAX_NR_CONSOLES)
- + return -ENXIO;
- + arg--;
- + i = vc_allocate(arg);
- + if (i)
- + return i;
- + change_console(arg);
- + return 0;
- +
- + /*
- + * wait until the specified VT has been activated
- + */
- + case VT_WAITACTIVE:
- + if (!perm)
- + return -EPERM;
- + if (arg == 0 || arg > MAX_NR_CONSOLES)
- + return -ENXIO;
- + arg--;
- + while (fg_console != arg)
- + {
- + if (vt_waitactive() < 0)
- + return -EINTR;
- + }
- + return 0;
- +
- + /*
- + * If a vt is under process control, the kernel will not switch to it
- + * immediately, but postpone the operation until the process calls this
- + * ioctl, allowing the switch to complete.
- + *
- + * According to the X sources this is the behavior:
- + * 0: pending switch-from not OK
- + * 1: pending switch-from OK
- + * 2: completed switch-to OK
- + */
- + case VT_RELDISP:
- + if (!perm)
- + return -EPERM;
- + if (vt_cons[console]->vt_mode.mode != VT_PROCESS)
- + return -EINVAL;
- +
- + /*
- + * Switching-from response
- + */
- + if (vt_cons[console]->vt_newvt >= 0)
- + {
- + if (arg == 0)
- + /*
- + * Switch disallowed, so forget we were trying
- + * to do it.
- + */
- + vt_cons[console]->vt_newvt = -1;
- +
- + else
- + {
- + /*
- + * The current vt has been released, so
- + * complete the switch.
- + */
- + int newvt = vt_cons[console]->vt_newvt;
- + vt_cons[console]->vt_newvt = -1;
- + i = vc_allocate(newvt);
- + if (i)
- + return i;
- + complete_change_console(newvt);
- + }
- + }
- +
- + /*
- + * Switched-to response
- + */
- + else
- + {
- + /*
- + * If it's just an ACK, ignore it
- + */
- + if (arg != VT_ACKACQ)
- + return -EINVAL;
- + }
- +
- + return 0;
- +
- + /*
- + * Disallocate memory associated to VT (but leave VT1)
- + */
- + case VT_DISALLOCATE:
- + if (arg > MAX_NR_CONSOLES)
- + return -ENXIO;
- + if (arg == 0) {
- + /* disallocate all unused consoles, but leave 0 */
- + for (i = 1; i<MAX_NR_CONSOLES; i++)
- + if (! VT_BUSY(i))
- + vc_disallocate(i);
- + } else {
- + /* disallocate a single console, if possible */
- + arg--;
- + if (VT_BUSY(arg))
- + return -EBUSY;
- + if (arg) /* leave 0 */
- + vc_disallocate(arg);
- + }
- + return 0;
- +
- + case VT_RESIZE:
- + {
- + struct vt_sizes *vtsizes = (struct vt_sizes *) arg;
- + ushort ll,cc;
- + if (!perm)
- + return -EPERM;
- + i = verify_area(VERIFY_READ, (void *)vtsizes, sizeof(struct vt_sizes));
- + if (i)
- + return i;
- + ll = get_user(&vtsizes->v_rows);
- + cc = get_user(&vtsizes->v_cols);
- + return vc_resize(ll, cc);
- + }
- +
- + case VT_GETPALETTE:
- + {
- + unsigned long pix, num, *val;
- + extern int palette_getentries (unsigned int, unsigned long,
- + unsigned long, unsigned long);
- +
- + i = verify_area(VERIFY_WRITE, (void *)arg, 12);
- + if (i)
- + return i;
- + pix = get_fs_long((unsigned long *)arg);
- + num = get_fs_long((unsigned long *)(arg + 4));
- + val = (unsigned long *)get_fs_long((unsigned long *)(arg + 8));
- + return palette_getentries(console, pix, num, val);
- + }
- +
- + case VT_SETPALETTE:
- + {
- + unsigned long pix, num, *val;
- + extern int palette_setentries (unsigned int, unsigned long,
- + unsigned long, unsigned long);
- +
- + i = verify_area(VERIFY_READ, (void *)arg, 12);
- + if (i)
- + return i;
- +
- + pix = get_fs_long((unsigned long *)arg);
- + num = get_fs_long((unsigned long *)(arg + 4));
- + val = (unsigned long *)get_fs_long((unsigned long *)(arg + 8));
- + return palette_setentries(console, pix, num, val);
- + }
- +
- + case VT_GETSCRINFO: {
- + extern int console_getparams (unsigned int, unsigned long arg);
- + i = verify_area(VERIFY_WRITE, (void *)arg, 20);
- + if (i)
- + return i;
- + return console_getparams(console, arg);
- + }
- +
- + case PIO_FONT:
- + if (!perm)
- + return -EPERM;
- + if (vt_cons[fg_console]->vc_mode != KD_TEXT)
- + return -EINVAL;
- + return con_set_font((char *)arg);
- + /* con_set_font() defined in console.c */
- +
- + case GIO_FONT:
- + if (vt_cons[fg_console]->vc_mode != KD_TEXT)
- + return -EINVAL;
- + return con_get_font((char *)arg);
- + /* con_get_font() defined in console.c */
- +
- + case PIO_SCRNMAP:
- + if (!perm)
- + return -EPERM;
- + return con_set_trans_old((char *)arg);
- +
- + case GIO_SCRNMAP:
- + return con_get_trans_old((char *)arg);
- +
- + case PIO_UNISCRNMAP:
- + if (!perm)
- + return -EPERM;
- + return con_set_trans_new((short *)arg);
- +
- + case GIO_UNISCRNMAP:
- + return con_get_trans_new((short *)arg);
- +
- + case PIO_UNIMAPCLR:
- + { struct unimapinit ui;
- + if (!perm)
- + return -EPERM;
- + i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapinit));
- + if (i)
- + return i;
- + memcpy_fromfs(&ui, (void *)arg, sizeof(struct unimapinit));
- + con_clear_unimap(&ui);
- + return 0;
- + }
- +
- + case PIO_UNIMAP:
- + { struct unimapdesc *ud;
- + u_short ct;
- + struct unipair *list;
- +
- + if (!perm)
- + return -EPERM;
- + i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapdesc));
- + if (i == 0) {
- + ud = (struct unimapdesc *) arg;
- + ct = get_fs_word(&ud->entry_ct);
- + list = (struct unipair *) get_fs_long(&ud->entries);
- + i = verify_area(VERIFY_READ, (void *) list,
- + ct*sizeof(struct unipair));
- + }
- + if (i)
- + return i;
- + return con_set_unimap(ct, list);
- + }
- +
- + case GIO_UNIMAP:
- + { struct unimapdesc *ud;
- + u_short ct;
- + struct unipair *list;
- +
- + i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct unimapdesc));
- + if (i == 0) {
- + ud = (struct unimapdesc *) arg;
- + ct = get_fs_word(&ud->entry_ct);
- + list = (struct unipair *) get_fs_long(&ud->entries);
- + if (ct)
- + i = verify_area(VERIFY_WRITE, (void *) list,
- + ct*sizeof(struct unipair));
- + }
- + if (i)
- + return i;
- + return con_get_unimap(ct, &(ud->entry_ct), list);
- + }
- +
- + default:
- + return -ENOIOCTLCMD;
- + }
- +}
- diff -urNwbB linux/arch/arm/drivers/char/vt_kern.h linux.arm/arch/arm/drivers/char/vt_kern.h
- --- linux/arch/arm/drivers/char/vt_kern.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/vt_kern.h Sun Feb 11 19:37:27 1996
- @@ -0,0 +1,29 @@
- +#ifndef _VT_KERN_H
- +#define _VT_KERN_H
- +
- +/*
- + * this really is an extension of the vc_cons structure in console.c, but
- + * with information needed by the vt package
- + */
- +
- +#include <linux/vt.h>
- +
- +extern struct vt_struct {
- + int vc_num; /* The console number */
- + unsigned char vc_mode; /* KD_TEXT, ... */
- + unsigned char vc_kbdraw;
- + unsigned char vc_kbde0;
- + unsigned char vc_kbdleds;
- + struct vt_mode vt_mode;
- + int vt_pid;
- + int vt_newvt;
- + struct wait_queue *paste_wait;
- +} *vt_cons[MAX_NR_CONSOLES];
- +
- +void kd_mksound(unsigned int count, unsigned int ticks);
- +int vc_allocate(unsigned int console);
- +int vc_cons_allocated(unsigned int console);
- +int vc_resize(unsigned long lines, unsigned long cols);
- +void vc_disallocate(unsigned int console);
- +
- +#endif /* _VT_KERN_H */
- diff -urNwbB linux/arch/arm/drivers/net/Makefile linux.arm/arch/arm/drivers/net/Makefile
- --- linux/arch/arm/drivers/net/Makefile Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/Makefile Sat Feb 24 21:37:51 1996
- @@ -0,0 +1,155 @@
- +# File: drivers/net/Makefile
- +#
- +# Makefile for the Linux network (ethercard) device drivers.
- +#
- +
- +all: links first_rule
- +
- +ifeq (${CFLAGS},)
- +CFLAGS = -D__KERNEL__ \
- + -Wall -Wstrict-prototypes -I. -I../../../../include \
- + -O2 -fomit-frame-pointer
- +
- +include ../../../../.config
- +
- +#TOPDIR = ../../../..
- +endif
- +
- +L_TARGET := net.a
- +L_OBJS := Space.o auto_irq.o net_init.o loopback.o
- +M_OBJS :=
- +SYMTAB_OBJS =
- +MOD_LIST_NAME := NET_MODULES
- +
- +# Need these to keep track of whether the SLHC module should
- +# really go in the kernel or a module
- +CONFIG_SLHC_BUILTIN :=
- +CONFIG_SLHC_MODULE :=
- +
- +ifeq ($(CONFIG_ETHER1),y)
- +L_OBJS += ether1.o
- +else
- + ifeq ($(CONFIG_ETHER1),m)
- + M_OBJS += ether1.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_ETHER3),y)
- +L_OBJS += ether3.o
- +else
- + ifeq ($(CONFIG_ETHER3),m)
- + M_OBJS += ether3.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_IPIP),y)
- +L_OBJS += tunnel.o
- +else
- + ifeq ($(CONFIG_IPIP),m)
- + M_OBJS += tunnel.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_PLIP),y)
- +L_OBJS += plip.o
- +else
- + ifeq ($(CONFIG_PLIP),m)
- + M_OBJS += plip.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_PPP),y)
- +L_OBJS += ppp.o
- +SYMTAB_OBJS += ppp.o
- +CONFIG_SLHC_BUILTIN = y
- +else
- + ifeq ($(CONFIG_PPP),m)
- + M_OBJS += ppp.o
- + SYMTAB_OBJS += ppp.o
- + CONFIG_SLHC_MODULE = y
- + endif
- +endif
- +
- +ifneq ($(CONFIG_PPP),n)
- + M_OBJS += bsd_comp.o
- +endif
- +
- +ifeq ($(CONFIG_SLIP),y)
- +L_OBJS += slip.o
- +CONFIG_SLHC_BUILTIN = y
- +else
- + ifeq ($(CONFIG_SLIP),m)
- + M_OBJS += slip.o
- + CONFIG_SLHC_MODULE = y
- + endif
- +endif
- +
- +ifeq ($(CONFIG_DUMMY),y)
- +L_OBJS += dummy.o
- +else
- + ifeq ($(CONFIG_DUMMY),m)
- + L_OBJS += dummy.o
- + endif
- +endif
- +
- +# If anything built-in uses slhc, then build it into the kernel also.
- +# If not, but a module uses it, build as a module.
- +ifdef CONFIG_SLHC_BUILTIN
- +L_OBJS += slhc.o
- +else
- + ifdef CONFIG_SLHC_MODULE
- + M_OBJS += slhc.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_EQUALIZER),y)
- +L_OBJS += eql.o
- +else
- + ifeq ($(CONFIG_EQUALIZER),m)
- + M_OBJS += eql.o
- + endif
- +endif
- +
- +fastdep: links
- +
- +ifdef CONFIG_MODVERSIONS
- +$(L_TARGET): $(SYMTAB_OBJS:.o=.ver)
- +dep: $(SYMTAB_OBJS:.o=.ver)
- +fastdep: $(SYMTAB_OBJS:.o=.ver)
- +endif
- +
- +include $(TOPDIR)/Rules.make
- +
- +include $(TOPDIR)/versions.mk
- +
- +clean:
- + rm -f core *.o *.a *.s
- +
- +Space.o: Space.c ../../../../include/linux/autoconf.h
- + $(CC) $(CFLAGS) $(OPTS) -c $<
- +
- +net_init.o: ../../../../include/linux/autoconf.h
- +
- +plip.o: plip.c
- + $(CC) $(CPPFLAGS) $(CFLAGS) $(PLIP_OPTS) -c $<
- +
- +slip.o: slip.c
- + $(CC) $(CPPFLAGS) $(CFLAGS) -c $<
- +
- +dummy.o: dummy.c
- + $(CC) $(CPPFLAGS) $(CFLAGS) -c $<
- +
- +LK = README.eql README.multicast README.tunnel README1.PLIP README2.PLIP bsd_comp.c dummy.c \
- + eql.c loopback.c net_init.c slhc.c slhc.h slip.c slip.h tunnel.c
- +
- +links:
- + -@for f in $(LK); do \
- + ln -s ../../../../drivers/net/$$f .; \
- + done
- + touch links
- +
- +LINKCLEAN:
- + -@for f in $(LK); do \
- + if [ -L $$f ]; then rm $$f; fi; \
- + done
- + rm -f links
- diff -urNwbB linux/arch/arm/drivers/net/Space.c linux.arm/arch/arm/drivers/net/Space.c
- --- linux/arch/arm/drivers/net/Space.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/Space.c Tue Feb 13 23:09:15 1996
- @@ -0,0 +1,226 @@
- +/*
- + * 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.
- + *
- + * Holds initial configuration information for devices.
- + *
- + * NOTE: This file is a nice idea, but its current format does not work
- + * well for drivers that support multiple units, like the SLIP
- + * driver. We should actually have only one pointer to a driver
- + * here, with the driver knowing how many units it supports.
- + * Currently, the SLIP driver abuses the "base_addr" integer
- + * field of the 'device' structure to store the unit number...
- + * -FvK
- + *
- + * Version: @(#)Space.c 1.0.7 08/12/93
- + *
- + * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
- + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- + * Donald J. Becker, <becker@super.org>
- + *
- + * 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.
- + */
- +#include <linux/config.h>
- +#include <linux/netdevice.h>
- +#include <linux/errno.h>
- +
- +/* The first device defaults to I/O base '0', which means autoprobe. */
- +#ifndef ETH0_ADDR
- +# define ETH0_ADDR 0
- +#endif
- +#ifndef ETH0_IRQ
- +# define ETH0_IRQ 0
- +#endif
- +/* "eth0" defaults to autoprobe (== 0), other use a base of 0xffe0 (== -0x20),
- + which means "don't probe". These entries exist to only to provide empty
- + slots which may be enabled at boot-time. */
- +
- +#define NEXT_DEV NULL
- +
- +
- +/* A unified ethernet device probe. This is the easiest way to have every
- + ethernet adaptor have the name "eth[0123...]".
- + */
- +extern int ether1_probe (struct device *dev);
- +extern int ether3_probe (struct device *dev);
- +
- +static int
- +ethif_probe(struct device *dev)
- +{
- + u_long base_addr = dev->base_addr;
- +
- + if ((base_addr == 0xffe0) || (base_addr == 1))
- + return 1; /* ENXIO */
- +
- + if (1
- +#ifdef CONFIG_ETHER1
- + && ether1_probe (dev)
- +#endif
- +#ifdef CONFIG_ETHER3
- + && ether3_probe (dev)
- +#endif
- + && 1 ) {
- + return 1; /* -ENODEV or -EAGAIN would be more accurate. */
- + }
- + return 0;
- +}
- +
- +static struct device eth3_dev = {
- + "eth3",
- + 0x0, 0x0, 0x0, 0x0,
- + 0xffe0 /* I/O base*/, 0, 0, 0, 0,
- + NEXT_DEV, ethif_probe,
- +};
- +
- +static struct device eth2_dev = {
- + "eth2",
- + 0x0, 0x0, 0x0, 0x0,
- + 0xffe0 /* I/O base*/, 0, 0, 0, 0,
- + ð3_dev, ethif_probe,
- +};
- +
- +static struct device eth1_dev = {
- + "eth1",
- + 0x0, 0x0, 0x0, 0x0,
- + 0xffe0 /* I/O base*/, 0, 0, 0, 0,
- + ð2_dev, ethif_probe,
- +};
- +
- +static struct device eth0_dev = {
- + "eth0",
- + 0x0, 0x0, 0x0, 0x0,
- + ETH0_ADDR, ETH0_IRQ, 0, 0, 0,
- + ð1_dev, ethif_probe,
- +};
- +
- +#undef NEXT_DEV
- +#define NEXT_DEV (ð0_dev)
- +
- +#if defined(PLIP) || defined(CONFIG_PLIP)
- +extern int plip_init(struct device *);
- +
- +static struct device plip2_dev = {
- + "plip2",
- + 0x0, 0x0, 0x0, 0x0,
- + 0x278, 2, 0, 0, 0,
- + NEXT_DEV, plip_init,
- +};
- +
- +static struct device plip1_dev = {
- + "plip1",
- + 0x0, 0x0, 0x0, 0x0,
- + 0x378, 7, 0, 0, 0,
- + &plip2_dev, plip_init,
- +};
- +
- +static struct device plip0_dev = {
- + "plip0",
- + 0x0, 0x0, 0x0, 0x0,
- + 0x3BC, 5, 0, 0, 0,
- + &plip1_dev, plip_init,
- +};
- +
- +#undef NEXT_DEV
- +#define NEXT_DEV (&plip0_dev)
- +#endif /* PLIP */
- +
- +#if defined(SLIP) || defined(CONFIG_SLIP)
- + /* To be exact, this node just hooks the initialization
- + routines to the device structures. */
- +extern int slip_init_ctrl_dev(struct device *);
- +
- +static struct device slip_bootstrap = {
- + "slip_proto",
- + 0x0, 0x0, 0x0, 0x0,
- + 0, 0, 0, 0, 0,
- + NEXT_DEV, slip_init_ctrl_dev,
- +};
- +
- +#undef NEXT_DEV
- +#define NEXT_DEV (&slip_bootstrap)
- +#endif /* SLIP */
- +
- +#if defined(CONFIG_PPP)
- +extern int ppp_init(struct device *);
- +
- +static struct device ppp_bootstrap = {
- + "ppp_proto",
- + 0x0, 0x0, 0x0, 0x0,
- + 0, 0, 0, 0, 0,
- + NEXT_DEV, ppp_init,
- +};
- +
- +#undef NEXT_DEV
- +#define NEXT_DEV (&ppp_bootstrap)
- +#endif /* PPP */
- +
- +#ifdef CONFIG_DUMMY
- +extern int dummy_init(struct device *dev);
- +
- +static struct device dummy_dev = {
- + "dummy",
- + 0x0, 0x0, 0x0, 0x0,
- + 0, 0, 0, 0, 0,
- + NEXT_DEV, dummy_init,
- +};
- +
- +#undef NEXT_DEV
- +#define NEXT_DEV (&dummy_dev)
- +#endif
- +
- +#ifdef CONFIG_EQUALIZER
- +extern int eql_init(struct device *dev);
- +
- +static struct device eql_dev = {
- + "eql",
- + 0x0, 0x0, 0x0, 0x0,
- + 0, 0, 0, 0, 0,
- + NEXT_DEV, eql_init,
- +};
- +
- +#undef NEXT_DEV
- +#define NEXT_DEV (&eql_dev)
- +#endif
- +
- +#ifdef CONFIG_NET_IPIP
- +#ifdef CONFIG_IP_FORWARD
- +extern int tunnel_init (struct device *dev);
- +
- +static struct device tunnel_dev1 = {
- + "tunl1",
- + 0x0, 0x0, 0x0, 0x0,
- + 0, 0, 0, 0, 0,
- + NEXT_DEV, tunnel_init,
- +};
- +
- +static struct device tunnel_dev0 = {
- + "tunl0",
- + 0x0, 0x0, 0x0, 0x0,
- + 0, 0, 0, 0, 0,
- + &tunnel_dev1, tunnel_init,
- +};
- +
- +#undef NEXT_DEV
- +#define NEXT_DEV (&tunnel_dev0)
- +#endif
- +#endif
- +
- +extern int loopback_init(struct device *dev);
- +struct device loopback_dev = {
- + "lo", /* Software Loopback interface */
- + 0x0, /* recv memory end */
- + 0x0, /* recv memory start */
- + 0x0, /* memory end */
- + 0x0, /* memory start */
- + 0, /* base I/O address */
- + 0, /* IRQ */
- + 0, 0, 0, /* flags */
- + NEXT_DEV, /* next device */
- + loopback_init /* loopback_init should set up the rest */
- +};
- +
- +struct device *dev_base = &loopback_dev;
- diff -urNwbB linux/arch/arm/drivers/net/auto_irq.c linux.arm/arch/arm/drivers/net/auto_irq.c
- --- linux/arch/arm/drivers/net/auto_irq.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/auto_irq.c Sun Feb 11 09:32:25 1996
- @@ -0,0 +1,122 @@
- +/* auto_irq.c: Auto-configure IRQ lines for linux. */
- +/*
- + Written 1994 by Donald Becker.
- +
- + The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
- + Center of Excellence in Space Data and Information Sciences
- + Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
- +
- + This code is a general-purpose IRQ line detector for devices with
- + jumpered IRQ lines. If you can make the device raise an IRQ (and
- + that IRQ line isn't already being used), these routines will tell
- + you what IRQ line it's using -- perfect for those oh-so-cool boot-time
- + device probes!
- +
- + To use this, first call autoirq_setup(timeout). TIMEOUT is how many
- + 'jiffies' (1/100 sec.) to detect other devices that have active IRQ lines,
- + and can usually be zero at boot. 'autoirq_setup()' returns the bit
- + vector of nominally-available IRQ lines (lines may be physically in-use,
- + but not yet registered to a device).
- + Next, set up your device to trigger an interrupt.
- + Finally call autoirq_report(TIMEOUT) to find out which IRQ line was
- + most recently active. The TIMEOUT should usually be zero, but may
- + be set to the number of jiffies to wait for a slow device to raise an IRQ.
- +
- + The idea of using the setup timeout to filter out bogus IRQs came from
- + the serial driver.
- +*/
- +
- +
- +#ifdef version
- +static char *version=
- +"auto_irq.c:v1.11 Donald Becker (becker@cesdis.gsfc.nasa.gov)";
- +#endif
- +
- +#include <linux/sched.h>
- +#include <linux/delay.h>
- +#include <asm/bitops.h>
- +#include <asm/io.h>
- +#include <asm/irq.h>
- +#include <linux/netdevice.h>
- +
- +struct device *irq2dev_map[32] = {0, 0, /* ... zeroed */};
- +
- +unsigned long irqs_busy = 0x2147; /* The set of fixed IRQs (keyboard, timer, etc) */
- +unsigned long irqs_used = 0x0001; /* The set of fixed IRQs sometimes enabled. */
- +unsigned long irqs_reserved = 0x0000; /* An advisory "reserved" table. */
- +unsigned long irqs_shared = 0x0000; /* IRQ lines "shared" among conforming cards.*/
- +
- +static volatile unsigned long irq_bitmap; /* The irqs we actually found. */
- +static unsigned long irq_handled; /* The irq lines we have a handler on. */
- +static volatile int irq_number; /* The latest irq number we actually found. */
- +
- +static void autoirq_probe(int irq, struct pt_regs * regs)
- +{
- + irq_number = irq;
- + set_bit(irq, (void *)&irq_bitmap); /* irq_bitmap |= 1 << irq; */
- + disable_irq(irq);
- + return;
- +}
- +
- +int autoirq_setup(int waittime)
- +{
- + int i, mask;
- + int timeout = jiffies + waittime;
- + int boguscount = (waittime*loops_per_sec) / 100;
- +
- + irq_handled = 0;
- + for (i = 0; i < 16; i++) {
- + if (test_bit(i, &irqs_busy) == 0
- + && request_irq(i, autoirq_probe, SA_INTERRUPT, "irq probe") == 0)
- + set_bit(i, (void *)&irq_handled); /* irq_handled |= 1 << i;*/
- + }
- + /* Update our USED lists. */
- + irqs_used |= ~irq_handled;
- + irq_number = 0;
- + irq_bitmap = 0;
- +
- + /* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
- + while (timeout > jiffies && --boguscount > 0)
- + ;
- +
- + for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
- + if (irq_bitmap & irq_handled & mask) {
- + irq_handled &= ~mask;
- +#ifdef notdef
- + printk(" Spurious interrupt on IRQ %d\n", i);
- +#endif
- + free_irq(i);
- + }
- + }
- + return irq_handled;
- +}
- +
- +int autoirq_report(int waittime)
- +{
- + int i;
- + int timeout = jiffies+waittime;
- + int boguscount = (waittime*loops_per_sec) / 100;
- +
- + /* Hang out at least <waittime> jiffies waiting for the IRQ. */
- +
- + while (timeout > jiffies && --boguscount > 0)
- + if (irq_number)
- + break;
- +
- + /* Retract the irq handlers that we installed. */
- + for (i = 0; i < 16; i++) {
- + if (test_bit(i, (void *)&irq_handled))
- + free_irq(i);
- + }
- + return irq_number;
- +}
- +
- +/*
- + * Local variables:
- + * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c auto_irq.c"
- + * version-control: t
- + * kept-new-versions: 5
- + * c-indent-level: 4
- + * tab-width: 4
- + * End:
- + */
- diff -urNwbB linux/arch/arm/drivers/net/ether1.c linux.arm/arch/arm/drivers/net/ether1.c
- --- linux/arch/arm/drivers/net/ether1.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/ether1.c Sun Feb 18 09:28:09 1996
- @@ -0,0 +1,1049 @@
- +/*
- + * linux/arch/arm/drivers/net/ether1.c
- + *
- + * Acorn ether1 driver (82586 chip)
- + *
- + * (c) 1996 Russell King
- + */
- +
- +/*
- + * We basically keep two queues in the cards memory - one for transmit
- + * and one for receive. Each has a head and a tail. The head is where
- + * we/the chip adds packets to be transmitted/received, and the tail
- + * is where the transmitter has got to/where the receiver will stop.
- + * Both of these queues are circular, and since the chip is running
- + * all the time, we have to be careful when we modify the pointers etc
- + * so that the buffer memory is valid all the time.
- + */
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/version.h>
- +#define CLAIM_IRQ_AT_OPEN
- +#else
- +#define MOD_INC_USE_COUNT
- +#define MOD_DEC_USE_COUNT
- +#endif
- +
- +#include <linux/config.h>
- +#include <linux/kernel.h>
- +#include <linux/sched.h>
- +#include <linux/types.h>
- +#include <linux/fcntl.h>
- +#include <linux/interrupt.h>
- +#include <linux/ptrace.h>
- +#include <linux/ioport.h>
- +#include <linux/in.h>
- +#include <linux/malloc.h>
- +#include <linux/string.h>
- +#include <asm/system.h>
- +#include <asm/bitops.h>
- +#include <asm/io.h>
- +#include <asm/dma.h>
- +#include <linux/errno.h>
- +
- +#include <linux/netdevice.h>
- +#include <linux/etherdevice.h>
- +#include <linux/skbuff.h>
- +
- +#include <asm/ecard.h>
- +
- +#define __ETHER1_C
- +#include "ether1.h"
- +
- +static unsigned int net_debug = NET_DEBUG;
- +
- +#define FUNC_PROLOGUE \
- + struct ether1_priv *priv = (struct ether1_priv *)dev->priv
- +
- +#define BUFFER_SIZE 65536
- +#define TX_AREA_START 0x0100
- +#define TX_AREA_END 0x5000
- +#define RX_AREA_START 0x5000
- +#define RX_AREA_END 0xfc00
- +
- +#define tx_done(dev) 0
- +/* ------------------------------------------------------------------------- */
- +static char *version = "ether1 ethernet driver (c) 1995 Russell King V1.00\n";
- +
- +#define BUS_16 16
- +#define BUS_8 8
- +
- +static int ether1_prods[] = { 0x0003 };
- +static int ether1_manus[] = { 0x0000 };
- +
- +/* ------------------------------------------------------------------------- */
- +#define ether1_inw(dev, addr, type, offset) ether1_inw_p (dev, addr + (int)(&((type *)0)->offset))
- +#define ether1_outw(dev, val, addr, type, offset) ether1_outw_p (dev, val, addr + (int)(&((type *)0)->offset))
- +
- +static inline unsigned short
- +ether1_inw_p (struct device *dev, int addr)
- +{
- + outb (addr >> 12, REG_PAGE);
- +
- + return inw (ETHER1_RAM + ((addr & 4095) >> 1));
- +}
- +
- +static inline void
- +ether1_outw_p (struct device *dev, unsigned short val, int addr)
- +{
- + outb (addr >> 12, REG_PAGE);
- + outw (val, ETHER1_RAM + ((addr & 4095) >> 1));
- +}
- +
- +static inline void *
- +ether1_inswb (int addr, void *data, unsigned int len)
- +{
- + int used;
- +
- + __asm__ __volatile__(
- + " subs %3, %3, #2\n"
- + " bmi 2f\n"
- + "1: ldr %0, [%1], #4\n"
- + " strb %0, [%2], #1\n"
- + " mov %0, %0, lsr #8\n"
- + " strb %0, [%2], #1\n"
- + " subs %3, %3, #2\n"
- + " bpl 1b\n"
- + "2:"
- + : "=&r" (used), "=&r" (addr), "=&r" (data), "=&r" (len)
- + : "1" (addr << 2), "2" (data), "3" (len));
- +
- + return data;
- +}
- +
- +static inline void *
- +ether1_outswb (int addr, void *data, unsigned int len)
- +{
- + int used;
- +
- + __asm__ __volatile__(
- + " subs %3, %3, #2\n"
- + " bmi 2f\n"
- + "1: ldr %0, [%2], #2\n"
- + " mov %0, %0, lsl #16\n"
- + " orr %0, %0, %0, lsr #16\n"
- + " str %0, [%1], #4\n"
- + " subs %3, %3, #2\n"
- + " bpl 1b\n"
- + "2:"
- + : "=&r" (used), "=&r" (addr), "=&r" (data), "=&r" (len)
- + : "1" (addr << 2), "2" (data), "3" (len));
- +
- + return data;
- +}
- +
- +
- +static void
- +ether1_writebuffer (struct device *dev, void *data, unsigned int start, unsigned int length)
- +{
- + unsigned int page, thislen, offset;
- +
- + offset = start & 4095;
- +
- + for (page = start >> 12; length; page++) {
- + outb (page, REG_PAGE);
- + if (offset + length > 4096) {
- + length -= 4096 - offset;
- + thislen = 4096 - offset;
- + } else {
- + thislen = length;
- + length = 0;
- + }
- +
- + data = ether1_outswb (ETHER1_RAM + (offset >> 1), data, thislen);
- + offset = 0;
- + }
- +}
- +
- +static void
- +ether1_readbuffer (struct device *dev, void *data, unsigned int start, unsigned int length)
- +{
- + unsigned int page, thislen, offset;
- +
- + offset = start & 4095;
- +
- + for (page = start >> 12; length; page++) {
- + outb (page, REG_PAGE);
- + if (offset + length > 4096) {
- + length -= 4096 - offset;
- + thislen = 4096 - offset;
- + } else {
- + thislen = length;
- + length = 0;
- + }
- +
- + data = ether1_inswb (ETHER1_RAM + (offset >> 1), data, thislen);
- + offset = 0;
- + }
- +}
- +
- +static int
- +ether1_ramtest (struct device *dev, unsigned char byte)
- +{
- + unsigned char *buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL);
- + int i, ret = BUFFER_SIZE;
- + int max_errors = 4;
- + int bad = -1;
- +
- + if (!buffer)
- + return 1;
- +
- + memset (buffer, byte, BUFFER_SIZE);
- + ether1_writebuffer (dev, buffer, 0, BUFFER_SIZE);
- + memset (buffer, byte ^ 0xff, BUFFER_SIZE);
- + ether1_readbuffer (dev, buffer, 0, BUFFER_SIZE);
- +
- + for (i = 0; i < BUFFER_SIZE; i++) {
- + if (buffer[i] != byte) {
- + if (max_errors >= 0 && bad != buffer[i]) {
- + if (bad != -1)
- + printk ("\n");
- + printk ("%s: RAM failed with (%02X instead of %02X) at 0x%04X",
- + dev->name, buffer[i], byte, i);
- + ret = -ENODEV;
- + max_errors --;
- + bad = buffer[i];
- + }
- + } else {
- + if (bad != -1) {
- + printk (" - 0x%04X\n", i);
- + bad = -1;
- + }
- + }
- + }
- +
- + if (bad != -1)
- + printk (" - 0x%04X\n", BUFFER_SIZE);
- + kfree (buffer);
- +
- + return ret;
- +}
- +
- +static int
- +ether1_reset (struct device *dev)
- +{
- + outb (CTRL_RST|CTRL_ACK, REG_CONTROL);
- + return BUS_16;
- +}
- +
- +static int
- +ether1_init_2 (struct device *dev)
- +{
- + int i;
- + dev->mem_start = 0;
- +
- + i = ether1_ramtest (dev, 0x5a);
- +
- + if (i > 0)
- + i = ether1_ramtest (dev, 0x1e);
- +
- + if (i <= 0)
- + return -ENODEV;
- +
- + dev->mem_end = i;
- +
- + return 0;
- +}
- +
- +/*
- + * These are the structures that are loaded into the ether RAM card to
- + * initialise the 82586
- + */
- +
- +/* at 0x0100 */
- +#define NOP_ADDR (TX_AREA_START)
- +#define NOP_SIZE (0x06)
- +static nop_t init_nop = {
- + 0,
- + CMD_NOP,
- + NOP_ADDR
- +};
- +
- +/* at 0x003a */
- +#define TDR_ADDR (0x003a)
- +#define TDR_SIZE (0x08)
- +static tdr_t init_tdr = {
- + 0,
- + CMD_TDR | CMD_INTR,
- + NOP_ADDR,
- + 0
- +};
- +
- +/* at 0x002e */
- +#define MC_ADDR (0x002e)
- +#define MC_SIZE (0x0c)
- +static mc_t init_mc = {
- + 0,
- + CMD_SETMULTICAST,
- + TDR_ADDR,
- + 0,
- + { { 0, } }
- +};
- +
- +/* at 0x0022 */
- +#define SA_ADDR (0x0022)
- +#define SA_SIZE (0x0c)
- +static sa_t init_sa = {
- + 0,
- + CMD_SETADDRESS,
- + MC_ADDR,
- + { 0, }
- +};
- +
- +/* at 0x0010 */
- +#define CFG_ADDR (0x0010)
- +#define CFG_SIZE (0x12)
- +static cfg_t init_cfg = {
- + 0,
- + CMD_CONFIG,
- + SA_ADDR,
- + 4,
- + 8,
- + CFG8_SRDY,
- + CFG9_PREAMB8 | CFG9_ADDRLENBUF | CFG9_ADDRLEN(6),
- + 0,
- +};
- +
- +/* at 0x0000 */
- +#define SCB_ADDR (0x0000)
- +#define SCB_SIZE (0x10)
- +static scb_t init_scb = {
- + 0,
- + 0,
- + CFG_ADDR,
- + RX_AREA_START,
- + 0,
- + 0,
- + 0,
- + 0
- +};
- +
- +/* at 0xffee */
- +#define ISCP_ADDR (0xffee)
- +#define ISCP_SIZE (0x08)
- +static iscp_t init_iscp = {
- + 1,
- + SCB_ADDR,
- + 0x0000,
- + 0x0000
- +};
- +
- +/* at 0xfff6 */
- +#define SCP_ADDR (0xfff6)
- +#define SCP_SIZE (0x0a)
- +static scp_t init_scp = {
- + SCP_SY_16BBUS,
- + { 0, 0 },
- + ISCP_ADDR,
- + 0
- +};
- +
- +#define RFD_SIZE (0x16)
- +static rfd_t init_rfd = {
- + 0,
- + 0,
- + 0,
- + 0,
- + { 0, },
- + { 0, },
- + 0
- +};
- +
- +#define RBD_SIZE (0x0a)
- +static rbd_t init_rbd = {
- + 0,
- + 0,
- + 0,
- + 0,
- + ETH_FRAME_LEN + 8
- +};
- +
- +#define TX_SIZE (0x08)
- +#define TBD_SIZE (0x08)
- +
- +static int
- +ether1_init_for_open (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + int i, status, addr, next, next2;
- +
- + for (i = 0; i < 6; i++)
- + init_sa.sa_addr[i] = dev->dev_addr[i];
- +
- + /* load data structures into ether1 RAM */
- + ether1_writebuffer (dev, &init_scp, SCP_ADDR, SCP_SIZE);
- + ether1_writebuffer (dev, &init_iscp, ISCP_ADDR, ISCP_SIZE);
- + ether1_writebuffer (dev, &init_scb, SCB_ADDR, SCB_SIZE);
- + ether1_writebuffer (dev, &init_cfg, CFG_ADDR, CFG_SIZE);
- + ether1_writebuffer (dev, &init_sa, SA_ADDR, SA_SIZE);
- + ether1_writebuffer (dev, &init_mc, MC_ADDR, MC_SIZE);
- + ether1_writebuffer (dev, &init_tdr, TDR_ADDR, TDR_SIZE);
- + ether1_writebuffer (dev, &init_nop, NOP_ADDR, NOP_SIZE);
- +
- + /*
- + * setup circularly linked list of { rfd, rbd, buffer }, with
- + * all rfds circularly linked, rbds circularly linked.
- + * First rfd is linked to scp, first rbd is linked to first
- + * rfd. Last rbd has a suspend command.
- + */
- + addr = RX_AREA_START;
- + do {
- + next = addr + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10;
- + next2 = next + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10;
- +
- + if (next2 >= RX_AREA_END) {
- + next = RX_AREA_START;
- + init_rfd.rfd_command = RFD_CMDEL | RFD_CMDSUSPEND;
- + priv->rx_tail = addr;
- + } else
- + init_rfd.rfd_command = 0;
- + if (addr == RX_AREA_START)
- + init_rfd.rfd_rbdoffset = addr + RFD_SIZE;
- + else
- + init_rfd.rfd_rbdoffset = 0;
- + init_rfd.rfd_link = next;
- + init_rbd.rbd_link = next + RFD_SIZE;
- + init_rbd.rbd_bufl = addr + RFD_SIZE + RBD_SIZE;
- +
- + ether1_writebuffer (dev, &init_rfd, addr, RFD_SIZE);
- + ether1_writebuffer (dev, &init_rbd, addr + RFD_SIZE, RBD_SIZE);
- + addr = next;
- + } while (next2 < RX_AREA_END);
- +
- + priv->tx_link = NOP_ADDR;
- + priv->tx_head = NOP_ADDR + NOP_SIZE;
- + priv->tx_tail = TDR_ADDR;
- + priv->rx_head = RX_AREA_START;
- +
- + /* release reset & give 586 a prod */
- + priv->resetting = 1;
- + outb (CTRL_RST, REG_CONTROL);
- + outb (0, REG_CONTROL);
- + outb (CTRL_CA, REG_CONTROL);
- +
- + /* 586 should now unset iscp.busy */
- + i = jiffies + HZ/2;
- + while (ether1_inw (dev, ISCP_ADDR, iscp_t, iscp_busy) == 1) {
- + if (jiffies > i) {
- + printk ("%s: can't initialise 82586: iscp is busy\n", dev->name);
- + return 1;
- + }
- + }
- +
- + /* check status of commands that we issued */
- + i += HZ/10;
- + while (((status = ether1_inw (dev, CFG_ADDR, cfg_t, cfg_status)) & STAT_COMPLETE) == 0) {
- + if (jiffies > i)
- + break;
- + }
- +
- + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
- + printk ("%s: can't initialise 82586: config status %04X\n", dev->name, status);
- + ether1_reset (dev);
- + return 1;
- + }
- +
- + i += HZ/10;
- + while (((status = ether1_inw (dev, SA_ADDR, sa_t, sa_status)) & STAT_COMPLETE) == 0) {
- + if (jiffies > i)
- + break;
- + }
- +
- + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
- + printk ("%s: can't initialise 82586: set address status %04X\n", dev->name, status);
- + ether1_reset (dev);
- + return 1;
- + }
- +
- + i += HZ/10;
- + while (((status = ether1_inw (dev, MC_ADDR, mc_t, mc_status)) & STAT_COMPLETE) == 0) {
- + if (jiffies > i)
- + break;
- + }
- +
- + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
- + printk ("%s: can't initialise 82586: set multicast status %04X\n", dev->name, status);
- + ether1_reset (dev);
- + return 1;
- + }
- +
- + i += HZ;
- + while (((status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_status)) & STAT_COMPLETE) == 0) {
- + if (jiffies > i)
- + break;
- + }
- +
- + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
- + printk ("%s: can't tdr (ignored)\n", dev->name);
- + return 0;
- + }
- +
- + status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_result);
- + if (status & TDR_XCVRPROB)
- + printk ("%s: i/f failed tdr: transceiver problem\n", dev->name);
- + else
- + if (status & (TDR_SHORT|TDR_OPEN)) {
- +#ifdef FANCY
- + printk ("%s: i/f failed tdr: cable %s %d.%d us away\n", dev->name,
- + status & TDR_SHORT ? "short" : "open", (status & TDR_TIME) / 10,
- + (status & TDR_TIME) % 10);
- +#else
- + printk ("%s: i/f failed tdr: cable %s %d clks away\n", dev->name,
- + status & TDR_SHORT ? "short" : "open", (status & TDR_TIME));
- +#endif
- + }
- +
- + return 0;
- +}
- +
- +static int
- +ether1_probe1 (struct device *dev)
- +{
- + static unsigned int version_printed = 0;
- + struct ether1_priv *priv;
- + int i;
- +
- + if (!dev->priv)
- + dev->priv = kmalloc (sizeof (struct ether1_priv), GFP_KERNEL);
- +
- + if (!dev->priv)
- + return 1;
- +
- + priv = (struct ether1_priv *)dev->priv;
- + memset (priv, 0, sizeof (struct ether1_priv));
- +
- + if ((priv->bus_type = ether1_reset (dev)) == 0) {
- + kfree (dev->priv);
- + return 1;
- + }
- +
- + if (net_debug && version_printed++ == 0)
- + printk (version);
- +
- + printk ("%s: ether1 found (bus width %d), ", dev->name, priv->bus_type);
- +
- + for (i = 0; i < 6; i++)
- + printk (i==0?" %2.2x":i==5?":%2.2x\n":":%2.2x", dev->dev_addr[i]);
- +
- + if (ether1_init_2 (dev)) {
- + kfree (dev->priv);
- + return 1;
- + }
- +
- + dev->open = ether1_open;
- + dev->stop = ether1_close;
- + dev->hard_start_xmit = ether1_sendpacket;
- + dev->get_stats = ether1_getstats;
- + dev->set_multicast_list = ether1_setmulticastlist;
- +
- + /* Fill in the fields of the device structure with ethernet values */
- + ether_setup (dev);
- +
- +#ifndef CLAIM_IRQ_AT_OPEN
- + if (request_irq (dev->irq, ether1_interrupt, 0, "ether1")) {
- + kfree (dev->priv);
- + return -EAGAIN;
- + }
- +
- + irq2dev_map[dev->irq] = dev;
- +#endif
- + return 0;
- +}
- +
- +/* ------------------------------------------------------------------------- */
- +
- +static void
- +ether1_addr (struct device *dev)
- +{
- + int i;
- +
- + for (i = 0; i < 6; i++)
- + dev->dev_addr[i] = inb (IDPROM_ADDRESS + i);
- +}
- +
- +int
- +ether1_probe (struct device *dev)
- +{
- +#ifndef MODULE
- + struct expansion_card *ec;
- +
- + if (!dev)
- + return ENODEV;
- +
- + if ((ec = ecard_find (0, sizeof (ether1_prods) / sizeof (int), ether1_prods, ether1_manus)) == NULL)
- + return ENODEV;
- +
- + dev->base_addr = (((unsigned long)ec->r_podaddr & ~0x3c0000) + 0x340000) >> 2;
- + dev->irq = ec->irq;
- +
- + ecard_claim (ec);
- +
- +#endif
- + ether1_addr (dev);
- +
- + if (ether1_probe1 (dev) == 0)
- + return 0;
- + return ENODEV;
- +}
- +
- +/* ------------------------------------------------------------------------- */
- +
- +static int
- +ether1_txalloc (struct device *dev, int size)
- +{
- + FUNC_PROLOGUE;
- + int start, tail;
- +
- + size = (size + 1) & ~1;
- + tail = priv->tx_tail;
- +
- + if (priv->tx_head + size > TX_AREA_END) {
- + if (tail > priv->tx_head)
- + return -1;
- + start = TX_AREA_START;
- + if (start + size > tail)
- + return -1;
- + priv->tx_head = start + size;
- + } else {
- + if (priv->tx_head < tail && (priv->tx_head + size) < tail)
- + return -1;
- + start = priv->tx_head;
- + priv->tx_head += size;
- + }
- +
- + return start;
- +}
- +
- +static int
- +ether1_open (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- +#ifdef CLAIM_IRQ_AT_OPEN
- + if (request_irq (dev->irq, ether1_interrupt, 0, "ether1")) {
- + kfree (dev->priv);
- + return -EAGAIN;
- + }
- +
- + irq2dev_map[dev->irq] = dev;
- +#endif
- + MOD_INC_USE_COUNT;
- +
- + memset (&priv->stats, 0, sizeof (struct enet_statistics));
- +
- + ether1_init_for_open (dev);
- +
- + dev->tbusy = 0;
- + dev->interrupt = 0;
- + dev->start = 1;
- +
- + return 0;
- +}
- +
- +static int
- +ether1_sendpacket (struct sk_buff *skb, struct device *dev)
- +{
- + FUNC_PROLOGUE;
- +
- + if (dev->tbusy) {
- + /*
- + * If we get here, some higher level has decided that we are broken.
- + * There should really be a "kick me" function call instead.
- + */
- + int tickssofar = jiffies - dev->trans_start;
- +
- + if (tickssofar < 5)
- + return 1;
- +
- + printk ("%s: transmit timeout, network cable problem?\n", dev->name);
- +
- + /* Try to restart the adapter. */
- + dev->tbusy = 0;
- + dev->trans_start = jiffies;
- + }
- +
- + /*
- + * If some higher layer thinks we've missed a tx-done interrupt
- + * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
- + * itself.
- + */
- + if (skb == NULL) {
- + dev_tint (dev);
- + return 0;
- + }
- +
- + /*
- + * Block a timer-based transmit from overlapping. This could better be
- + * done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
- + */
- + if (set_bit (0, (void *)&dev->tbusy) != 0)
- + printk ("%s: transmitter access conflict.\n", dev->name);
- + else {
- + int len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
- + int tmp, tst, nopaddr, txaddr, tbdaddr, dataddr;
- + tx_t tx;
- + tbd_t tbd;
- + nop_t nop;
- + /*
- + * insert packet followed by a nop
- + */
- + txaddr = ether1_txalloc (dev, TX_SIZE);
- + tbdaddr = ether1_txalloc (dev, TBD_SIZE);
- + dataddr = ether1_txalloc (dev, len);
- + nopaddr = ether1_txalloc (dev, NOP_SIZE);
- +
- + tx.tx_status = 0;
- + tx.tx_command = CMD_TX | CMD_INTR;
- + tx.tx_link = nopaddr;
- + tx.tx_tbdoffset = tbdaddr;
- + tbd.tbd_opts = TBD_EOL | len;
- + tbd.tbd_link = I82586_NULL;
- + tbd.tbd_bufl = dataddr;
- + tbd.tbd_bufh = 0;
- + nop.nop_status = 0;
- + nop.nop_command = CMD_NOP;
- + nop.nop_link = nopaddr;
- +
- + ether1_writebuffer (dev, &tx, txaddr, TX_SIZE);
- + ether1_writebuffer (dev, &tbd, tbdaddr, TBD_SIZE);
- + ether1_writebuffer (dev, skb->data, dataddr, len);
- + ether1_writebuffer (dev, &nop, nopaddr, NOP_SIZE);
- + tmp = priv->tx_link;
- + priv->tx_link = nopaddr;
- +
- + /* now reset the previous nop pointer */
- + ether1_outw (dev, txaddr, tmp, nop_t, nop_link);
- +
- + /* handle transmit */
- + dev->trans_start = jiffies;
- +
- + /* check to see if we have room for a full sized ether frame */
- + tmp = priv->tx_head;
- + tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN);
- + priv->tx_head = tmp;
- + if (tst != -1)
- + dev->tbusy = 0;
- + }
- + dev_kfree_skb (skb, FREE_WRITE);
- +
- + return 0;
- +}
- +
- +static void
- +ether1_xmit_done (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + nop_t nop;
- + int caddr, tst;
- +
- + caddr = priv->tx_tail;
- +
- +again:
- + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
- +
- + switch (nop.nop_command & CMD_MASK) {
- + case CMD_TDR:
- + /* special case */
- + if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset) != (unsigned short)I82586_NULL) {
- + ether1_outw (dev, SCB_CMDCUCSTART | SCB_CMDRXSTART, SCB_ADDR, scb_t, scb_command);
- + outb (CTRL_CA, REG_CONTROL);
- + }
- + priv->tx_tail = NOP_ADDR;
- + return;
- +
- + case CMD_NOP:
- + if (nop.nop_link == caddr) {
- + printk ("%s: strange command complete with no tx command!\n", dev->name);
- + return;
- + }
- + caddr = nop.nop_link;
- + goto again;
- +
- + case CMD_TX:
- + if (nop.nop_status & STAT_COMPLETE)
- + break;
- + printk ("%s: strange command complete without completed command!\n", dev->name);
- + return;
- +
- + default:
- + printk ("%s: strange command %d complete!\n", dev->name, nop.nop_command & CMD_MASK);
- + return;
- + }
- +
- + while (nop.nop_status & STAT_COMPLETE) {
- + if (nop.nop_status & STAT_OK) {
- + priv->stats.tx_packets ++;
- + priv->stats.collisions += (nop.nop_status & STAT_COLLISIONS);
- + } else {
- + priv->stats.tx_errors ++;
- +
- + if (nop.nop_status & STAT_COLLAFTERTX) {
- + priv->stats.collisions ++;
- + printk ("%s: late collision detected\n", dev->name);
- + }
- + if (nop.nop_status & STAT_NOCARRIER) {
- + priv->stats.tx_carrier_errors ++;
- + printk ("%s: no carrier\n", dev->name);
- + }
- + if (nop.nop_status & STAT_TXLOSTCTS)
- + printk ("%s: cts lost\n", dev->name);
- + if (nop.nop_status & STAT_TXSLOWDMA) {
- + priv->stats.tx_fifo_errors ++;
- + printk ("%s: fifo error - dma underrun\n", dev->name);
- + }
- + if (nop.nop_status & STAT_COLLEXCESSIVE) {
- + priv->stats.collisions += 16;
- + printk ("%s: excessive collisions\n", dev->name);
- + }
- + }
- +
- + if (nop.nop_link == caddr) {
- + printk ("%s: tx buffer chaining error: tx command points to itself\n", dev->name);
- + break;
- + }
- +
- + caddr = nop.nop_link;
- + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
- + if ((nop.nop_command & CMD_MASK) != CMD_NOP) {
- + printk ("%s: tx buffer chaining error: no nop after tx command\n", dev->name);
- + break;
- + }
- +
- + if (caddr == nop.nop_link)
- + break;
- +
- + caddr = nop.nop_link;
- + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
- + if ((nop.nop_command & CMD_MASK) != CMD_TX) {
- + printk ("%s: tx buffer chaining error: no tx command after nop\n", dev->name);
- + break;
- + }
- + }
- + priv->tx_tail = caddr;
- +
- + caddr = priv->tx_head;
- + tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN);
- + priv->tx_head = caddr;
- + if (tst != -1)
- + dev->tbusy = 0;
- +
- + mark_bh (NET_BH);
- +}
- +
- +static void
- +ether1_recv_done (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + int status;
- + int nexttail, rbdaddr;
- + rbd_t rbd;
- +
- + do {
- + status = ether1_inw (dev, priv->rx_head, rfd_t, rfd_status);
- + if ((status & RFD_COMPLETE) == 0)
- + break;
- +
- + rbdaddr = ether1_inw (dev, priv->rx_head, rfd_t, rfd_rbdoffset);
- + ether1_readbuffer (dev, &rbd, rbdaddr, RBD_SIZE);
- +
- + if ((rbd.rbd_status & (RBD_EOF | RBD_ACNTVALID)) == (RBD_EOF | RBD_ACNTVALID)) {
- + int length = rbd.rbd_status & RBD_ACNT;
- + struct sk_buff *skb;
- +
- + length = (length + 1) & ~1;
- + skb = dev_alloc_skb (length + 2);
- +
- + if (skb) {
- + skb->dev = dev;
- + skb_reserve (skb, 2);
- +
- + ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length);
- +
- + skb->protocol = eth_type_trans (skb, dev);
- + netif_rx (skb);
- + priv->stats.rx_packets ++;
- + } else
- + priv->stats.rx_dropped ++;
- + } else {
- + printk ("%s: %s\n", dev->name, (rbd.rbd_status & RBD_EOF) ? "oversized packet" :
- + "acnt not valid");
- + priv->stats.rx_dropped ++;
- + }
- +
- + nexttail = ether1_inw (dev, priv->rx_tail, rfd_t, rfd_link);
- + /* nexttail should be rx_head */
- + if (nexttail != priv->rx_head)
- + printk ("%s: receiver buffer chaining error (%04X != %04X)\n", dev->name, nexttail,
- + priv->rx_head);
- + ether1_outw (dev, RFD_CMDEL | RFD_CMDSUSPEND, nexttail, rfd_t, rfd_command);
- + ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_command);
- + ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_status);
- + ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_rbdoffset);
- +
- + priv->rx_tail = nexttail;
- + priv->rx_head = ether1_inw (dev, priv->rx_head, rfd_t, rfd_link);
- + } while (1);
- +}
- +
- +static void
- +ether1_interrupt (int irq, struct pt_regs *regs)
- +{
- + struct device *dev = irq2dev_map[irq];
- + FUNC_PROLOGUE;
- + int status;
- +
- + if (dev == NULL) {
- + printk ("ether1: interrupt %d from unknown device\n", irq);
- + return;
- + }
- +
- + dev->interrupt = 1;
- +
- + status = ether1_inw (dev, SCB_ADDR, scb_t, scb_status);
- + if (status ) {
- + ether1_outw (dev, status & (SCB_STRNR | SCB_STCNA | SCB_STFR | SCB_STCX),
- + SCB_ADDR, scb_t, scb_command);
- + outb (CTRL_CA | CTRL_ACK, REG_CONTROL);
- + if (status & SCB_STCX) {
- + ether1_xmit_done (dev);
- + }
- + if (status & SCB_STCNA) {
- + if (priv->resetting == 0)
- + printk ("%s: CU went not ready ???\n", dev->name);
- + if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset) != (unsigned short)I82586_NULL) {
- + ether1_outw (dev, SCB_CMDCUCSTART, SCB_ADDR, scb_t, scb_command);
- + outb (CTRL_CA, REG_CONTROL);
- + }
- + if (priv->resetting == 2)
- + priv->resetting = 0;
- + }
- + if (status & SCB_STFR) {
- + ether1_recv_done (dev);
- + }
- + if (status & SCB_STRNR) {
- + printk ("%s: RU went not ready\n", dev->name);
- + printk ("RU ptr = %04X\n", ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset));
- + }
- + } else
- + outb (CTRL_ACK, REG_CONTROL);
- +
- + dev->interrupt = 0;
- +}
- +
- +static int
- +ether1_close (struct device *dev)
- +{
- +#ifdef CLAIM_IRQ_AT_OPEN
- + free_irq (dev->irq);
- + irq2dev_map[dev->irq] = NULL;
- +#endif
- +
- + ether1_reset (dev);
- +
- + dev->start = 0;
- + dev->tbusy = 0;
- +
- + MOD_DEC_USE_COUNT;
- +
- + return 0;
- +}
- +
- +static struct enet_statistics *
- +ether1_getstats (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + return &priv->stats;
- +}
- +
- +/*
- + * Set or clear the multicast filter for this adaptor.
- + * num_addrs == -1 Promiscuous mode, receive all packets.
- + * num_addrs == 0 Normal mode, clear multicast list.
- + * num_addrs > 0 Multicast mode, receive normal and MC packets, and do
- + * best-effort filtering.
- + */
- +static void
- +ether1_setmulticastlist (struct device *dev, int num_addrs, void *addrs)
- +{
- +}
- +
- +/* ------------------------------------------------------------------------- */
- +
- +#ifdef MODULE
- +char kernel_version[] = UTS_RELEASE;
- +
- +static char ethernames[4][9] = {
- + " ",
- + " ",
- + " ",
- + " "
- +};
- +static struct device *my_ethers[4];
- +static struct expansion_card *ec[4];
- +
- +int
- +init_module (void)
- +{
- + int i;
- +
- + for (i = 0; i < 4; i++) {
- + my_ethers[i] = NULL;
- + ec[i] = NULL;
- + }
- +
- + i = 0;
- +
- + do {
- + if ((ec[i] = ecard_find(0, sizeof(ether1_prods) / sizeof(int), ether1_prods, ether1_manus))
- + == NULL)
- + break;
- +
- + my_ethers[i] = (struct device *)kmalloc (sizeof (struct device), GFP_KERNEL);
- + memset (my_ethers[i], 0, sizeof (struct device));
- +
- + my_ethers[i]->irq = ec[i]->irq;
- + my_ethers[i]->base_addr = (((unsigned long)ec[i]->r_podaddr & ~0x3c0000) + 0x340000) >> 2;
- + my_ethers[i]->init = ether1_probe;
- + my_ethers[i]->name = ethernames[i];
- +
- + ecard_claim (ec[i]);
- +
- + if (register_netdev (my_ethers[i]) != 0) {
- + for (i = 0; i < 4; i++) {
- + if (my_ethers[i]) {
- + kfree (my_ethers[i]);
- + my_ethers[i] = NULL;
- + }
- + if (ec[i]) {
- + ecard_release (ec[i]);
- + ec[i] = NULL;
- + }
- + }
- + return -EIO;
- + }
- + i++;
- + } while (i < 4);
- +
- + return i != 0 ? 0 : -ENODEV;
- +}
- +
- +void
- +cleanup_module (void)
- +{
- + if (MOD_IN_USE) {
- + printk ("%s: device busy, remove delayed\n", "ether1");
- + } else {
- + int i;
- +
- + for (i = 0; i < 4; i++) {
- + if (my_ethers[i]) {
- + unregister_netdev (my_ethers[i]);
- + my_ethers[i] = NULL;
- + }
- + if (ec[i]) {
- + ecard_release (ec[i]);
- + ec[i] = NULL;
- + }
- + }
- + }
- +}
- +#endif /* MODULE */
- diff -urNwbB linux/arch/arm/drivers/net/ether1.h linux.arm/arch/arm/drivers/net/ether1.h
- --- linux/arch/arm/drivers/net/ether1.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/ether1.h Sat Feb 17 10:05:58 1996
- @@ -0,0 +1,280 @@
- +/*
- + * linux/arch/arm/drivers/net/ether1.h
- + *
- + * network driver for Acorn Ether1 cards.
- + *
- + * (c) 1996 Russell King
- + */
- +
- +#ifndef _LINUX_ether1_H
- +#define _LINUX_ether1_H
- +
- +#ifdef __ETHER1_C
- +/* use 0 for production, 1 for verification, >2 for debug */
- +#ifndef NET_DEBUG
- +#define NET_DEBUG 1
- +#endif
- +
- +/* Page register */
- +#define REG_PAGE (dev->base_addr + 0x00)
- +
- +/* Control register */
- +#define REG_CONTROL (dev->base_addr + 0x01)
- +#define CTRL_RST 0x01
- +#define CTRL_LOOPBACK 0x02
- +#define CTRL_CA 0x04
- +#define CTRL_ACK 0x08
- +
- +#define ETHER1_RAM (dev->base_addr + 0x800)
- +
- +/* HW address */
- +#define IDPROM_ADDRESS (dev->base_addr + 0x09)
- +
- +struct ether1_priv {
- + struct enet_statistics stats;
- + int tx_link;
- + int tx_head;
- + volatile int tx_tail;
- + volatile int rx_head;
- + volatile int rx_tail;
- + char bus_type;
- + char resetting;
- +};
- +
- +static int ether1_open (struct device *dev);
- +static int ether1_sendpacket (struct sk_buff *skb, struct device *dev);
- +static void ether1_interrupt (int irq, struct pt_regs *regs);
- +static int ether1_close (struct device *dev);
- +static struct enet_statistics *ether1_getstats (struct device *dev);
- +static void ether1_setmulticastlist (struct device *dev, int num_addrs, void *addrs);
- +
- +#define I82586_NULL (-1)
- +
- +typedef struct { /* tdr */
- + unsigned short tdr_status;
- + unsigned short tdr_command;
- + unsigned short tdr_link;
- + unsigned short tdr_result;
- +#define TDR_TIME (0x7ff)
- +#define TDR_SHORT (1 << 12)
- +#define TDR_OPEN (1 << 13)
- +#define TDR_XCVRPROB (1 << 14)
- +#define TDR_LNKOK (1 << 15)
- +} tdr_t;
- +
- +typedef struct { /* transmit */
- + unsigned short tx_status;
- + unsigned short tx_command;
- + unsigned short tx_link;
- + unsigned short tx_tbdoffset;
- +} tx_t;
- +
- +typedef struct { /* tbd */
- + unsigned short tbd_opts;
- +#define TBD_CNT (0x3fff)
- +#define TBD_EOL (1 << 15)
- + unsigned short tbd_link;
- + unsigned short tbd_bufl;
- + unsigned short tbd_bufh;
- +} tbd_t;
- +
- +typedef struct { /* rfd */
- + unsigned short rfd_status;
- +#define RFD_NOEOF (1 << 6)
- +#define RFD_FRAMESHORT (1 << 7)
- +#define RFD_DMAOVRN (1 << 8)
- +#define RFD_NORESOURCES (1 << 9)
- +#define RFD_ALIGNERROR (1 << 10)
- +#define RFD_CRCERROR (1 << 11)
- +#define RFD_OK (1 << 13)
- +#define RFD_FDCONSUMED (1 << 14)
- +#define RFD_COMPLETE (1 << 15)
- + unsigned short rfd_command;
- +#define RFD_CMDSUSPEND (1 << 14)
- +#define RFD_CMDEL (1 << 15)
- + unsigned short rfd_link;
- + unsigned short rfd_rbdoffset;
- + unsigned char rfd_dest[6];
- + unsigned char rfd_src[6];
- + unsigned short rfd_len;
- +} rfd_t;
- +
- +typedef struct { /* rbd */
- + unsigned short rbd_status;
- +#define RBD_ACNT (0x3fff)
- +#define RBD_ACNTVALID (1 << 14)
- +#define RBD_EOF (1 << 15)
- + unsigned short rbd_link;
- + unsigned short rbd_bufl;
- + unsigned short rbd_bufh;
- + unsigned short rbd_len;
- +} rbd_t;
- +
- +typedef struct { /* nop */
- + unsigned short nop_status;
- + unsigned short nop_command;
- + unsigned short nop_link;
- +} nop_t;
- +
- +typedef struct { /* set multicast */
- + unsigned short mc_status;
- + unsigned short mc_command;
- + unsigned short mc_link;
- + unsigned short mc_cnt;
- + unsigned char mc_addrs[1][6];
- +} mc_t;
- +
- +typedef struct { /* set address */
- + unsigned short sa_status;
- + unsigned short sa_command;
- + unsigned short sa_link;
- + unsigned char sa_addr[6];
- +} sa_t;
- +
- +typedef struct { /* config command */
- + unsigned short cfg_status;
- + unsigned short cfg_command;
- + unsigned short cfg_link;
- + unsigned char cfg_bytecnt; /* size foll data: 4 - 12 */
- + unsigned char cfg_fifolim; /* FIFO threshold */
- + unsigned char cfg_byte8;
- +#define CFG8_SRDY (1 << 6)
- +#define CFG8_SAVEBADF (1 << 7)
- + unsigned char cfg_byte9;
- +#define CFG9_ADDRLEN(x) (x)
- +#define CFG9_ADDRLENBUF (1 << 3)
- +#define CFG9_PREAMB2 (0 << 4)
- +#define CFG9_PREAMB4 (1 << 4)
- +#define CFG9_PREAMB8 (2 << 4)
- +#define CFG9_PREAMB16 (3 << 4)
- +#define CFG9_ILOOPBACK (1 << 6)
- +#define CFG9_ELOOPBACK (1 << 7)
- + unsigned char cfg_byte10;
- +#define CFG10_LINPRI(x) (x)
- +#define CFG10_ACR(x) (x << 4)
- +#define CFG10_BOFMET (1 << 7)
- + unsigned char cfg_ifs;
- + unsigned char cfg_slotl;
- + unsigned char cfg_byte13;
- +#define CFG13_SLOTH(x) (x)
- +#define CFG13_RETRY(x) (x << 4)
- + unsigned char cfg_byte14;
- +#define CFG14_PROMISC (1 << 0)
- +#define CFG14_DISBRD (1 << 1)
- +#define CFG14_MANCH (1 << 2)
- +#define CFG14_TNCRS (1 << 3)
- +#define CFG14_NOCRC (1 << 4)
- +#define CFG14_CRC16 (1 << 5)
- +#define CFG14_BTSTF (1 << 6)
- +#define CFG14_FLGPAD (1 << 7)
- + unsigned char cfg_byte15;
- +#define CFG15_CSTF(x) (x)
- +#define CFG15_ICSS (1 << 3)
- +#define CFG15_CDTF(x) (x << 4)
- +#define CFG15_ICDS (1 << 7)
- + unsigned short cfg_minfrmlen;
- +} cfg_t;
- +
- +typedef struct { /* scb */
- + unsigned short scb_status; /* status of 82586 */
- +#define SCB_STRXMASK (7 << 4)
- +#define SCB_STRXIDLE (0 << 4)
- +#define SCB_STRXSUSP (1 << 4)
- +#define SCB_STRXNRES (2 << 4)
- +#define SCB_STRXRDY (4 << 4)
- +#define SCB_STCUMASK (7 << 8)
- +#define SCB_STCUIDLE (0 << 8)
- +#define SCB_STCUSUSP (1 << 8)
- +#define SCB_STCUACTV (2 << 8)
- +#define SCB_STRNR (1 << 12)
- +#define SCB_STCNA (1 << 13)
- +#define SCB_STFR (1 << 14)
- +#define SCB_STCX (1 << 15)
- + unsigned short scb_command; /* Next command */
- +#define SCB_CMDRXSTART (1 << 4)
- +#define SCB_CMDRXRESUME (2 << 4)
- +#define SCB_CMDRXSUSPEND (3 << 4)
- +#define SCB_CMDRXABORT (4 << 4)
- +#define SCB_CMDCUCSTART (1 << 8)
- +#define SCB_CMDCUCRESUME (2 << 8)
- +#define SCB_CMDCUCSUSPEND (3 << 8)
- +#define SCB_CMDCUCABORT (4 << 8)
- +#define SCB_CMDACKRNR (1 << 12)
- +#define SCB_CMDACKCNA (1 << 13)
- +#define SCB_CMDACKFR (1 << 14)
- +#define SCB_CMDACKCX (1 << 15)
- + unsigned short scb_cbl_offset; /* Offset of first command unit */
- + unsigned short scb_rfa_offset; /* Offset of first receive frame area */
- + unsigned short scb_crc_errors; /* Properly aligned frame with CRC error */
- + unsigned short scb_aln_errors; /* Misaligned frames */
- + unsigned short scb_rsc_errors; /* Frames lost due to no space */
- + unsigned short scb_ovn_errors; /* Frames lost due to slow bus */
- +} scb_t;
- +
- +typedef struct { /* iscp */
- + unsigned short iscp_busy; /* set by CPU before CA */
- + unsigned short iscp_offset; /* offset of SCB */
- + unsigned short iscp_basel; /* base of SCB */
- + unsigned short iscp_baseh;
- +} iscp_t;
- +
- + /* this address must be 0xfff6 */
- +typedef struct { /* scp */
- + unsigned short scp_sysbus; /* bus size */
- +#define SCP_SY_16BBUS 0x00
- +#define SCP_SY_8BBUS 0x01
- + unsigned short scp_junk[2]; /* junk */
- + unsigned short scp_iscpl; /* lower 16 bits of iscp */
- + unsigned short scp_iscph; /* upper 16 bits of iscp */
- +} scp_t;
- +
- +/* commands */
- +#define CMD_NOP 0
- +#define CMD_SETADDRESS 1
- +#define CMD_CONFIG 2
- +#define CMD_SETMULTICAST 3
- +#define CMD_TX 4
- +#define CMD_TDR 5
- +#define CMD_DUMP 6
- +#define CMD_DIAGNOSE 7
- +
- +#define CMD_MASK 7
- +
- +#define CMD_INTR (1 << 13)
- +#define CMD_SUSP (1 << 14)
- +#define CMD_EOL (1 << 15)
- +
- +#define STAT_COLLISIONS (15)
- +#define STAT_COLLEXCESSIVE (1 << 5)
- +#define STAT_COLLAFTERTX (1 << 6)
- +#define STAT_TXDEFERRED (1 << 7)
- +#define STAT_TXSLOWDMA (1 << 8)
- +#define STAT_TXLOSTCTS (1 << 9)
- +#define STAT_NOCARRIER (1 << 10)
- +#define STAT_FAIL (1 << 11)
- +#define STAT_ABORTED (1 << 12)
- +#define STAT_OK (1 << 13)
- +#define STAT_BUSY (1 << 14)
- +#define STAT_COMPLETE (1 << 15)
- +#endif
- +#endif
- +
- +/*
- + * Ether1 card definitions:
- + *
- + * FAST accesses:
- + * +0 Page register
- + * 16 pages
- + * +4 Control
- + * '1' = reset
- + * '2' = loopback
- + * '4' = CA
- + * '8' = int ack
- + *
- + * RAM at address + 0x2000
- + * Pod. Prod id = 3
- + * Words after ID block [base + 8 words]
- + * +0 pcb issue (0x0c and 0xf3 invalid)
- + * +1 - +6 eth hw address
- + */
- +
- diff -urNwbB linux/arch/arm/drivers/net/ether3.c linux.arm/arch/arm/drivers/net/ether3.c
- --- linux/arch/arm/drivers/net/ether3.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/ether3.c Sat Mar 2 18:11:39 1996
- @@ -0,0 +1,914 @@
- +/*
- + * linux/drivers/net/ether3.c
- + *
- + * SEEQ nq8005 ethernet driver
- + *
- + * By Russell King.
- + *
- + * Changelog:
- + * 29/02/96: RMK Won't pass packets that are from our ethernet address
- + * up to the higher levels - they're silently ignored.
- + * I/F can now be put into multicast mode. Receiver
- + * routine optimised.
- + *
- + * 30/02/96: RMK Now claims interrupt at open when part of the kernel
- + * rather than when a module.
- + *
- + * 02/03/96: RMK Various code cleanups
- + *
- + * TODO:
- + * When we detect a fatal error on the interface, we should restart it.
- + */
- +
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/version.h>
- +#else
- +#define CLAIM_IRQ_AT_OPEN
- +#define MOD_INC_USE_COUNT
- +#define MOD_DEC_USE_COUNT
- +#endif
- +
- +#include <linux/config.h>
- +#include <linux/kernel.h>
- +#include <linux/sched.h>
- +#include <linux/types.h>
- +#include <linux/fcntl.h>
- +#include <linux/interrupt.h>
- +#include <linux/ptrace.h>
- +#include <linux/ioport.h>
- +#include <linux/in.h>
- +#include <linux/malloc.h>
- +#include <linux/string.h>
- +#include <linux/errno.h>
- +#include <linux/netdevice.h>
- +#include <linux/etherdevice.h>
- +#include <linux/skbuff.h>
- +#include <asm/system.h>
- +#include <asm/bitops.h>
- +#include <asm/io.h>
- +#include <asm/dma.h>
- +#include <asm/ecard.h>
- +
- +#include "ether3.h"
- +
- +static unsigned int net_debug = NET_DEBUG;
- +static int ether3_prods[] = { 0x00A4 };
- +static int ether3_manus[] = { 0x0011 };
- +
- +static char *version = "ether3 ethernet driver (c) 1995 R.M.King v1.10\n";
- +
- +extern int inswb(int reg, void *buffer, int len);
- +extern int outswb(int reg, void *buffer, int len);
- +
- +#define FUNC_PROLOGUE \
- + struct dev_priv *priv = (struct dev_priv *)dev->priv
- +
- +#define BUS_16 16
- +#define BUS_8 8
- +
- +/*
- + * I'm not sure what address we should default to if the internal one
- + * is corrupted...
- + */
- +unsigned char def_eth_addr[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
- +
- +/* --------------------------------------------------------------------------- */
- +
- +/*
- + * write data to the buffer memory
- + */
- +static void
- +ether3_writebuffer(struct device *dev, void *data, int start, int length)
- +{
- + FUNC_PROLOGUE;
- + int timeout = 1000000;
- +
- + if (start != -1) {
- + outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- + outw (priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
- + while ((inw (REG_STATUS) & STAT_FIFOEMPTY) == 0)
- + if (!timeout--) {
- + printk ("settxlim_16 broken\n");
- + priv->broken = 1;
- + return;
- + }
- + outw (priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
- + outw (start, REG_DMAADDR);
- + }
- + if(length != 0)
- + outswb (REG_BUFWIN, data, length);
- +}
- +
- +/*
- + * read data from the buffer memory
- + */
- +static void
- +ether3_readbuffer(struct device *dev, void *data, int start, int length)
- +{
- + FUNC_PROLOGUE;
- + int timeout = 1000000;
- +
- + if (start != -1) {
- + outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- + outw (priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
- + while ((inw( REG_STATUS) & STAT_FIFOEMPTY) == 0)
- + if (!timeout--) {
- + printk ("setrxlim_16 broken\n");
- + priv->broken = 1;
- + return;
- + }
- + outw (start, REG_DMAADDR);
- + outw (priv->regs.command | CMD_FIFOREAD, REG_COMMAND);
- + }
- + if(length != 0)
- + inswb (REG_BUFWIN, data, length);
- +}
- +
- +/*
- + * Increment the use count, and switch LED on if necessary
- + */
- +static inline void
- +ether3_ledincrementuse (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- +
- + if (++ priv->use == 1) {
- + priv->regs.config2 &= ~CFG2_CTRLO;
- + outw (priv->regs.config2 , REG_CONFIG2);
- + }
- +}
- +
- +/*
- + * Decrement the use count and switch LED off if necessary
- + */
- +static inline void
- +ether3_leddecrementuse (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- +
- + if (priv->use && -- priv->use == 0) {
- + priv->regs.config2 |= CFG2_CTRLO;
- + outw (priv->regs.config2, REG_CONFIG2);
- + }
- +}
- +
- +/*
- + * Read the ethernet address string from the on board rom.
- + * This is an ascii string!!!
- + */
- +static void
- +ether3_addr(char *addr, struct expansion_card *ec)
- +{
- + struct chunk_dir cd;
- + char *s;
- +
- + if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) {
- + int i;
- + for (i = 0; i<6; i++) {
- + addr[i] = simple_strtoul(s + 1, &s, 0x10);
- + if (*s != (i==5?')' : ':' ))
- + break;
- + }
- + if (i == 6)
- + return;
- + }
- + memcpy (addr, def_eth_addr, 6);
- +}
- +
- +/* --------------------------------------------------------------------------- */
- +
- +static int
- +ether3_ramtest(struct device *dev, unsigned char byte)
- +{
- + unsigned char *buffer = kmalloc(RX_END, GFP_KERNEL);
- + unsigned long flags;
- + int i,ret=0;
- + int max_errors = 4;
- + int bad=-1;
- +
- + if (!buffer)
- + return 1;
- +
- + save_flags (flags);
- + cli ();
- +
- + memset (buffer, byte, RX_END);
- + ether3_writebuffer (dev, buffer, 0, TX_END);
- + ether3_writebuffer (dev, buffer+RX_START, RX_START, RX_LEN);
- +
- + memset (buffer, byte ^ 0xff, RX_END);
- + ether3_readbuffer (dev, buffer, 0, TX_END);
- + ether3_readbuffer (dev, buffer+RX_START, RX_START, RX_LEN);
- +
- + for (i=0; i<RX_END; i++) {
- + if (buffer[i] != byte) {
- + if (max_errors>=0 && bad!=buffer[i]) {
- + printk ("%s: RAM failed with (%02X instead of %02X) at 0x%04X",
- + dev->name, buffer[i], byte, i);
- + ret = 2;
- + max_errors--;
- + bad = buffer[i];
- + }
- + } else {
- + if (bad != -1) {
- + printk(" - 0x%04X\n", i);
- + bad = -1;
- + }
- + }
- + }
- + if (bad != -1)
- + printk (" - 0x10000\n");
- + kfree (buffer);
- + restore_flags (flags);
- +
- + return ret;
- +}
- +
- +/* ------------------------------------------------------------------------------- */
- +
- +static int
- +ether3_init_2(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + int i;
- + unsigned long txhdr = 0;
- +
- + priv->regs.config1 = CFG1_RECVCOMPSTAT0|CFG1_DMABURST8;
- + priv->regs.config2 = CFG2_CTRLO|CFG2_RECVCRC|CFG2_ERRENCRC;
- + priv->regs.command = 0;
- + /*
- + * Set up our hardware address
- + */
- + outw (priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
- + for (i = 0; i < 6; i++)
- + outb (dev->dev_addr[i], REG_BUFWIN);
- +
- + if (dev->flags & IFF_PROMISC)
- + priv->regs.config1 |= CFG1_RECVPROMISC;
- + else
- + if (dev->flags & IFF_MULTICAST)
- + priv->regs.config1 |= CFG1_RECVSPECBRMULTI;
- + else
- + priv->regs.config1 |= CFG1_RECVSPECBROAD;
- +
- + /*
- + * There is a problem with the NQ8005 in that it occasionally losses the
- + * last two bytes. To get round this problem, we receive the CRC as well.
- + * That way, if we do loose the last two, then it doesn't matter
- + */
- + outw (priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
- + outw ((TX_END>>8) - 1, REG_BUFWIN);
- + outw (priv->regs.recvptr, REG_RECVPTR);
- + outw (priv->regs.transmitptr, REG_TRANSMITPTR);
- + outw (priv->regs.recvptr >> 8, REG_RECVEND);
- + outw (priv->regs.config2, REG_CONFIG2);
- + outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- +
- + ether3_writebuffer (dev, &txhdr, priv->regs.transmitptr, 4);
- +
- + outw (priv->regs.command, REG_COMMAND);
- +
- + i = ether3_ramtest (dev, 0x5A);
- + if(i)
- + return i;
- + return ether3_ramtest (dev, 0x1E);
- +}
- +
- +static void
- +ether3_init_for_open(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + unsigned long txhdr = 0;
- + int i;
- +
- + memset (&priv->stats, 0, sizeof (struct enet_statistics));
- +
- + priv->regs.command = 0;
- + priv->txbuffered = 0;
- + outw (CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
- + while (inw (REG_STATUS) & (STAT_RXON|STAT_TXON));
- +
- + outw (priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
- + for (i = 0; i < 6; i++)
- + outb (dev->dev_addr[i], REG_BUFWIN);
- +
- + priv->regs.transmitptr = 0; /* address that transmitter has processed to */
- + priv->txinsert = 0; /* address of next available packet for tx */
- + priv->txed = 0; /* transmitted length index */
- + priv->txto = 0; /* to transmit length index */
- + priv->tx_ends[0] = 0;
- + priv->use = 0;
- +
- + priv->regs.config2 |= CFG2_CTRLO;
- +
- + outw (priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
- + outw ((TX_END>>8) - 1, REG_BUFWIN);
- +
- + priv->regs.recvptr = RX_START;
- + outw (priv->regs.recvptr, REG_RECVPTR);
- + outw (priv->regs.recvptr >> 8, REG_RECVEND);
- +
- + outw (0, REG_TRANSMITPTR);
- + outw (priv->regs.config2, REG_CONFIG2);
- + outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- +
- + ether3_writebuffer (dev, &txhdr, priv->regs.transmitptr, 4);
- +
- + priv->regs.command = CMD_ENINTRX|CMD_ENINTTX;
- + outw (priv->regs.command | CMD_RXON, REG_COMMAND);
- +}
- +
- +/*
- + * This is the real probe routine.
- + */
- +static int
- +ether3_probe1(struct device *dev)
- +{
- + static unsigned version_printed = 0;
- + struct dev_priv *priv;
- + int i;
- +
- + if (!dev->priv)
- + dev->priv = kmalloc (sizeof (struct dev_priv), GFP_KERNEL);
- +
- + if (!dev->priv)
- + return -ENOMEM;
- +
- + priv = (struct dev_priv *) dev->priv;
- + memset (priv, 0, sizeof(struct dev_priv));
- + priv->broken = 0;
- +
- + outb (0x80, REG_CONFIG2 + 1);
- + outb (0, REG_COMMAND);
- +
- + /*
- + * test to see if we have an 8 or 16-bit IO bus
- + */
- + outb (1, REG_CONFIG1);
- + if (inb (REG_CONFIG1 + 1) == 1 && inb (REG_CONFIG1) == 0)
- + priv->bus_type = BUS_8;
- + else
- + if (inw (REG_CONFIG1) == 0x0101)
- + priv->bus_type = BUS_16;
- + else {
- + kfree(dev->priv);
- + dev->priv = NULL;
- + return -ENODEV;
- + }
- +
- + if (net_debug && version_printed++ == 0)
- + printk(version);
- +
- + printk("%s: ether3 found [%d, %04lx, %d] ", dev->name, priv->bus_type,
- + dev->base_addr, dev->irq);
- +
- + if (priv->bus_type == BUS_8) {
- + printk ("*** 8-bit not supported (yet) ***\n");
- + kfree (dev->priv);
- + dev->priv = NULL;
- + return -ENODEV;
- + }
- +
- + /* Retrive and print the ethernet address. */
- + for (i = 0; i < 6; i++)
- + printk (i == 5 ? "%2.2x\n" : "%2.2x:", dev->dev_addr[i]);
- +
- + if (ether3_init_2(dev)) {
- + kfree (dev->priv);
- + dev->priv = NULL;
- + return -ENODEV;
- + }
- +
- + dev->open = ether3_open;
- + dev->stop = ether3_close;
- + dev->hard_start_xmit = ether3_send_packet;
- + dev->get_stats = ether3_get_stats;
- + dev->set_multicast_list = set_multicast_list;
- +
- + /* Fill in the fields of the device structure with ethernet values. */
- + ether_setup(dev);
- +#ifndef CLAIM_IRQ_AT_OPEN
- + if (request_irq(dev->irq, ether3_interrupt, 0, "ether3")) {
- + kfree (dev->priv);
- + dev->priv = NULL;
- + return -EAGAIN;
- + }
- +
- + irq2dev_map[dev->irq] = dev;
- +#endif
- + return 0;
- +}
- +
- +#ifndef MODULE
- +int
- +ether3_probe(struct device *dev)
- +{
- + struct expansion_card *ec;
- +
- + if (!dev)
- + return ENODEV;
- +
- + if ((ec = ecard_find (0, sizeof(ether3_prods), ether3_prods, ether3_manus)) == NULL)
- + return ENODEV;
- +
- + dev->base_addr = ((unsigned long)ec->r_podaddr & ~0x003c0000UL) >> 2;
- + dev->irq = ec->irq;
- +
- + ecard_claim (ec);
- +
- + ether3_addr (dev->dev_addr, ec);
- + return ether3_probe1(dev);
- +}
- +#endif
- +
- +/*
- + * Open/initialize the board. This is called (in the current kernel)
- + * sometime after booting when the 'ifconfig' program is run.
- + *
- + * This routine should set everything up anew at each open, even
- + * registers that "should" only need to be set once at boot, so that
- + * there is non-reboot way to recover if something goes wrong.
- + */
- +static int
- +ether3_open(struct device *dev)
- +{
- + ether3_init_for_open(dev);
- +
- +#ifdef CLAIM_IRQ_AT_OPEN
- + if (request_irq(dev->irq, ether3_interrupt, 0, "ether3"))
- + return -EAGAIN;
- +
- + irq2dev_map[dev->irq] = dev;
- +#endif
- +
- + MOD_INC_USE_COUNT;
- +
- + dev->tbusy = 0;
- + dev->interrupt = 0;
- + dev->start = 1;
- + return 0;
- +}
- +
- +/*
- + * The inverse routine to ether3_open().
- + */
- +static int
- +ether3_close(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + unsigned long flags;
- +
- + dev->tbusy = 1;
- + dev->start = 0;
- +
- + save_flags (flags);
- + cli ();
- +
- + outw (CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
- + priv->regs.command = 0;
- + while (inw (REG_STATUS) & (STAT_RXON|STAT_TXON));
- + outb (0x80, REG_CONFIG2 + 1);
- + outw (0, REG_COMMAND);
- +
- + restore_flags (flags);
- +#ifdef CLAIM_IRQ_AT_OPEN
- + free_irq (dev->irq);
- +
- + irq2dev_map[dev->irq] = NULL;
- +#endif
- +
- + MOD_DEC_USE_COUNT;
- + return 0;
- +}
- +
- +/*
- + * Get the current statistics. This may be called with the card open or
- + * closed.
- + */
- +static struct enet_statistics *
- +ether3_get_stats(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + return &priv->stats;
- +}
- +
- +/*
- + * Set or clear the multicast filter for this adaptor.
- + * num_addrs == -1 Promiscuous mode, receive all packets
- + * num_addrs == 0 Normal mode, clear multicast list
- + * num_addrs > 0 Multicast mode, receive normal and MC packets, and do
- + * best-effort filtering.
- + */
- +static void
- +set_multicast_list(struct device *dev, int num_addrs, void *addrs)
- +{
- + FUNC_PROLOGUE;
- +
- + priv->regs.config1 &= ~CFG1_RECVPROMISC;
- +
- + switch (num_addrs) {
- + case -1: priv->regs.config1 |= CFG1_RECVPROMISC; break;
- + case 0: priv->regs.config1 |= CFG1_RECVSPECBROAD; break;
- + default: priv->regs.config1 |= CFG1_RECVSPECBRMULTI; break;
- + }
- +
- + outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- +}
- +
- +/*
- + * Transmit a packet
- + */
- +static int
- +ether3_send_packet(struct sk_buff *skb, struct device *dev)
- +{
- + FUNC_PROLOGUE;
- +
- + if (dev->tbusy) {
- + /* If we get here, some higher level has decided we are broken.
- + There should really be a "kick me" function call instead. */
- + int tickssofar = jiffies - dev->trans_start;
- + if (tickssofar < 5)
- + return 1;
- + printk("%s: transmit timed out, network cable problem?\n", dev->name);
- + /* Try to restart the adaptor. */
- + dev->tbusy = 0;
- + priv->use = 0;
- + priv->regs.config2 |= CFG2_CTRLO;
- + outw (priv->regs.config2 , REG_CONFIG2);
- + dev->trans_start = jiffies;
- + }
- +
- + /* If some higher layer thinks we've missed an tx-done interrupt
- + we are passed NULL. Caution: dev_tint() handles the cli()/sti()
- + itself. */
- + if (skb == NULL) {
- + dev_tint(dev);
- + return 0;
- + }
- +
- + /* Block a timer-based transmit from overlapping. This could better be
- + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
- + if (set_bit(0, (void*)&dev->tbusy) != 0)
- + printk("%s: Transmitter access conflict.\n", dev->name);
- + else {
- + unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
- + unsigned int thisdata, thisstart, nextpacket;
- + unsigned long flags;
- + unsigned long thishdr = TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS
- + |TXHDR_ENSUCCESS;
- + unsigned long nexthdr = 0;
- +
- + if (priv->broken) {
- + dev_kfree_skb(skb, FREE_WRITE);
- + priv->stats.tx_dropped ++;
- + return 1;
- + }
- +
- + thisstart = priv->txinsert;
- + thisdata = thisstart + 4;
- + if (thisdata >= TX_END)
- + thisdata -= TX_END;
- +
- + nextpacket = thisdata + length;
- + if (nextpacket >= TX_END)
- + nextpacket -= TX_END;
- +
- + priv->txinsert = nextpacket;
- + priv->tx_ends[priv->txto++] = nextpacket;
- +
- + if (priv->txto >= MAX_TXED)
- + priv->txto = 0;
- +
- + thishdr |= htons (nextpacket);
- +
- + save_flags(flags);
- + cli();
- + ether3_ledincrementuse (dev);
- + ether3_writebuffer(dev, skb->data, thisdata, length&1?(length+1):length);
- + ether3_writebuffer(dev, &nexthdr, -1, 4);
- + ether3_writebuffer(dev, &thishdr, thisstart, 4);
- +
- + if ((inw (REG_STATUS) & STAT_TXON) == 0) {
- + outw (thisstart, REG_TRANSMITPTR);
- + outw (priv->regs.command | CMD_TXON, REG_COMMAND);
- + }
- + restore_flags(flags);
- +
- + dev->trans_start = jiffies;
- + if (priv->txbuffered ++ < MAX_TX_BUFFERED)
- + dev->tbusy = 0;
- + }
- + dev_kfree_skb (skb, FREE_WRITE);
- +
- + return 0;
- +}
- +
- +static void
- +ether3_interrupt(int irq, struct pt_regs *regs)
- +{
- + struct device *dev = (struct device *)(irq2dev_map[irq]);
- + struct dev_priv *priv;
- + int boguscount = 20, done = 0;
- +
- +#if NET_DEBUG > 1
- + if(net_debug & DEBUG_INT)
- + printk("ether3_interrupt: irq %d\n", irq);
- +#endif
- +
- + if (dev == NULL) {
- + printk ("ether3_interrupt(): irq %d for unknown device.\n", irq);
- + return;
- + }
- +
- + priv = (struct dev_priv *)dev->priv;
- +
- + dev->interrupt = 1;
- +
- + do {
- + unsigned int status;
- + status = inw(REG_STATUS);
- + outw ((status & (STAT_INTRX|STAT_INTTX|STAT_INTBUFWIN))
- + | priv->regs.command, REG_COMMAND);
- +
- + if (status & STAT_INTRX) { /* Got a packet(s). */
- + ether3_rx (dev, priv);
- + done = 1;
- + }
- + if (status & STAT_INTTX) { /* Packets transmitted */
- + ether3_tx (dev, priv);
- + done = 1;
- + }
- + } while (-- boguscount && !done) ;
- +
- + dev->interrupt = 0;
- +
- +#if NET_DEBUG > 1
- + if(net_debug & DEBUG_INT)
- + printk("ether3_interrupt: done\n", irq);
- +#endif
- +}
- +
- +/*
- + * If we have a good packet(s), get it/them out of the buffers.
- + */
- +static void
- +ether3_rx(struct device *dev, struct dev_priv *priv)
- +{
- + unsigned int current_recv_ptr;
- + unsigned int maxcnt = 10;
- +
- + ether3_ledincrementuse (dev);
- +
- + current_recv_ptr = priv->regs.recvptr;
- +
- + do {
- + struct t {
- + unsigned long rxhdr;
- + unsigned char dst_addr[6];
- + unsigned char src_addr[6];
- + } p;
- + unsigned char *buf;
- + unsigned int prev_recv_ptr;
- + unsigned int length;
- + struct sk_buff *skb;
- +
- + /*
- + * read the first 16 bytes from the buffer.
- + * This contains the status bytes etc and ethernet addresses,
- + * and we also check the source ethernet address to see if
- + * it origionated from us.
- + */
- + prev_recv_ptr = current_recv_ptr;
- + ether3_readbuffer (dev, &p, current_recv_ptr, 16);
- +
- + if ((p.rxhdr & RX_NEXT) == 0 || (p.rxhdr & RXSTAT_DONE) == 0
- + || (p.rxhdr & (RXHDR_CHAINCONTINUE|RXHDR_RECEIVE)) != RXHDR_CHAINCONTINUE)
- + break;
- +
- + current_recv_ptr = ntohs (p.rxhdr & RX_NEXT);
- +
- + /*
- + * ignore our own packets...
- + */
- + if (memcmp (dev->dev_addr, &p.src_addr, 6) == 0) {
- + outw (current_recv_ptr >> 8, REG_RECVEND);
- + continue;
- + }
- +
- + if (p.rxhdr & (RXSTAT_OVERSIZE|RXSTAT_CRCERROR|RXSTAT_DRIBBLEERROR|RXSTAT_SHORTPACKET)) {
- + priv->stats.rx_errors++;
- + if(p.rxhdr & RXSTAT_OVERSIZE) priv->stats.rx_length_errors ++;
- + if(p.rxhdr & RXSTAT_CRCERROR) priv->stats.rx_crc_errors ++;
- + if(p.rxhdr & RXSTAT_DRIBBLEERROR) priv->stats.rx_fifo_errors ++;
- + if(p.rxhdr & RXSTAT_SHORTPACKET) priv->stats.rx_length_errors ++;
- + outw (current_recv_ptr >> 8, REG_RECVEND);
- + continue;
- + }
- +
- + if (current_recv_ptr > prev_recv_ptr)
- + length = current_recv_ptr - prev_recv_ptr - 4;
- + else
- + length = (current_recv_ptr - RX_START) + (RX_END - prev_recv_ptr) - 4;
- +
- + skb = dev_alloc_skb(length + 2);
- + if (skb == NULL) {
- + printk("%s: memory squeeze, dropping packet.\n", dev->name);
- + priv->stats.rx_dropped ++;
- + break;
- + }
- +
- + skb->dev = dev;
- + skb_reserve (skb, 2);
- + buf = skb_put (skb, length);
- + /*
- + * buf isn't word aligned, and it is probably faster to
- + * copy the address this way
- + */
- + *(unsigned short *)&buf[0] = *(unsigned short *)&p.dst_addr[0];
- + *(unsigned short *)&buf[2] = *(unsigned short *)&p.dst_addr[2];
- + *(unsigned short *)&buf[4] = *(unsigned short *)&p.dst_addr[4];
- + *(unsigned short *)&buf[6] = *(unsigned short *)&p.src_addr[0];
- + *(unsigned short *)&buf[8] = *(unsigned short *)&p.src_addr[2];
- + *(unsigned short *)&buf[10] = *(unsigned short *)&p.src_addr[4];
- +
- + ether3_readbuffer (dev, buf + 12, -1, length - 12);
- +
- + outw (current_recv_ptr >> 8, REG_RECVEND);
- +
- + skb->protocol = eth_type_trans(skb, dev);
- + netif_rx (skb);
- + priv->stats.rx_packets++;
- + }
- + while (-- maxcnt);
- +
- + priv->regs.recvptr = current_recv_ptr;
- + if (!(inw (REG_STATUS) & STAT_RXON)) {
- + outw (priv->regs.recvptr, REG_RECVPTR);
- + outw (priv->regs.command | CMD_RXON, REG_COMMAND);
- + }
- +
- + /* If any worth-while packets have been received, dev_rint()
- + has done a mark_bh(NET_BH) for us and will work on them
- + when we get to the bottom-half routine. */
- +
- + ether3_leddecrementuse (dev);
- +}
- +
- +/*
- + * Update stats for the transmitted packet
- + */
- +static void
- +ether3_tx(struct device *dev, struct dev_priv *priv)
- +{
- + unsigned long txhdr;
- + unsigned char *buffer = (unsigned char *)&txhdr;
- + unsigned int txptr;
- +
- + txptr = priv->regs.transmitptr;
- +
- + do {
- + /*
- + * Read the packet header
- + */
- + if (txptr + 4 > TX_END) {
- + int len = TX_END - priv->regs.transmitptr;
- + if (len)
- + ether3_readbuffer (dev, buffer, txptr, len);
- + ether3_readbuffer (dev, buffer + len, 0, 4 - len);
- + } else
- + ether3_readbuffer (dev, buffer, txptr, 4);
- +
- + /*
- + * Check to see if this packet has been transmitted
- + */
- + if (!(txhdr & TXSTAT_DONE) || !(txhdr & TXHDR_TRANSMIT)) {
- + priv->regs.transmitptr = txptr;
- + return;
- + }
- +
- + /*
- + * Update collision count (undocumented in datasheet)
- + */
- + if (txhdr & TXSTAT_COLLISION)
- + priv->stats.collisions += (txhdr >> (3+24)) & 15;
- +
- + /*
- + * Update errors
- + */
- + if (txhdr & (TXSTAT_BABBLED | TXSTAT_16COLLISIONS)) {
- + priv->stats.tx_errors ++;
- + if (txhdr & TXSTAT_16COLLISIONS) priv->stats.collisions += 16;
- + if (txhdr & TXSTAT_BABBLED) priv->stats.tx_fifo_errors ++;
- + } else
- + priv->stats.tx_packets++;
- +
- + /*
- + * Get next packet address
- + */
- + txptr = priv->tx_ends[priv->txed++]; /* next packet address */
- +
- + if (priv->txed >= MAX_TXED)
- + priv->txed = 0;
- +
- + if (txptr != ntohs (txhdr & TX_NEXT)) {
- + printk ("%s: TX: bad next pointer - %04X instead of %04X\n",
- + dev->name, ntohs (txhdr & TX_NEXT), txptr);
- + return;
- + }
- +
- + if (priv->regs.transmitptr >= TX_END)
- + priv->regs.transmitptr -= TX_END;
- +
- + if (priv->txbuffered)
- + priv->txbuffered -= 1;
- +
- + dev->tbusy = 0;
- +
- + ether3_leddecrementuse (dev);
- +
- + mark_bh (NET_BH); /* Inform upper layers. */
- + } while (1);
- +}
- +
- +#ifdef MODULE
- +char kernel_version[] = UTS_RELEASE;
- +
- +char *ethernames[4] = {
- + " ",
- + " ",
- + " ",
- + " "
- +};
- +
- +static struct device *my_ethers[4];
- +static struct expansion_card *ec[4];
- +
- +int
- +init_module(void)
- +{
- + int i;
- +
- + for(i = 0; i < 4; i++) {
- + my_ethers[i] = NULL;
- + ec[i] = NULL;
- + }
- +
- + i = 0;
- +
- + do {
- + if ((ec[i] = ecard_find(0, sizeof(ether3_prods), ether3_prods, ether3_manus)) == NULL)
- + break;
- +
- + my_ethers[i] = (struct device *)kmalloc(sizeof(struct device), GFP_KERNEL);
- + memset(my_ethers[i], 0, sizeof(struct device));
- +
- + my_ethers[i]->irq = ec[i]->irq;
- + my_ethers[i]->base_addr= ((unsigned long)ec[i]->r_podaddr & ~0x003c0000UL)>>2;
- + my_ethers[i]->init = ether3_probe1;
- + my_ethers[i]->name = ethernames[i];
- +
- + ether3_addr(my_ethers[i]->dev_addr, ec[i]);
- +
- + ecard_claim(ec[i]);
- +
- + if(register_netdev(my_ethers[i]) != 0) {
- + for (i = 0; i < 4; i++) {
- + if(my_ethers[i]) {
- + kfree(my_ethers[i]);
- + my_ethers[i] = NULL;
- + }
- + if(ec[i]) {
- + ecard_release(ec[i]);
- + ec[i] = NULL;
- + }
- + }
- + return -EIO;
- + }
- + i++;
- + }
- + while(i < 4);
- +
- + return i != 0 ? 0 : -ENODEV;
- +}
- +
- +void
- +cleanup_module(void)
- +{
- + if (MOD_IN_USE) {
- + printk("ether3: device busy, remove delayed\n");
- + } else {
- + int i;
- + for (i = 0; i < 4; i++) {
- + if (my_ethers[i]) {
- + unregister_netdev(my_ethers[i]);
- + my_ethers[i] = NULL;
- + }
- + if (ec[i]) {
- + ecard_release(ec[i]);
- + ec[i] = NULL;
- + }
- + }
- + }
- +}
- +#endif /* MODULE */
- diff -urNwbB linux/arch/arm/drivers/net/ether3.h linux.arm/arch/arm/drivers/net/ether3.h
- --- linux/arch/arm/drivers/net/ether3.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/ether3.h Sat Mar 2 13:07:27 1996
- @@ -0,0 +1,174 @@
- +/*
- + * linux/drivers/net/ether3.h
- + *
- + * network driver for Acorn Ether3 cards
- + */
- +
- +#ifndef _LINUX_ether3_H
- +#define _LINUX_ether3_H
- +
- +/* use 0 for production, 1 for verification, >2 for debug. debug flags: */
- +#define DEBUG_TX 2
- +#define DEBUG_RX 4
- +#define DEBUG_INT 8
- +#define DEBUG_IC 16
- +#ifndef NET_DEBUG
- +#define NET_DEBUG 0
- +#endif
- +
- +/* Command register definitions & bits */
- +#define REG_COMMAND (dev->base_addr + 0x00)
- +#define CMD_ENINTDMA 0x0001
- +#define CMD_ENINTRX 0x0002
- +#define CMD_ENINTTX 0x0004
- +#define CMD_ENINTBUFWIN 0x0008
- +#define CMD_ACKINTDMA 0x0010
- +#define CMD_ACKINTRX 0x0020
- +#define CMD_ACKINTTX 0x0040
- +#define CMD_ACKINTBUFWIN 0x0080
- +#define CMD_DMAON 0x0100
- +#define CMD_RXON 0x0200
- +#define CMD_TXON 0x0400
- +#define CMD_DMAOFF 0x0800
- +#define CMD_RXOFF 0x1000
- +#define CMD_TXOFF 0x2000
- +#define CMD_FIFOREAD 0x4000
- +#define CMD_FIFOWRITE 0x8000
- +
- +/* status register */
- +#define REG_STATUS (dev->base_addr + 0x00)
- +#define STAT_ENINTSTAT 0x0001
- +#define STAT_ENINTRX 0x0002
- +#define STAT_ENINTTX 0x0004
- +#define STAT_ENINTBUFWIN 0x0008
- +#define STAT_INTDMA 0x0010
- +#define STAT_INTRX 0x0020
- +#define STAT_INTTX 0x0040
- +#define STAT_INTBUFWIN 0x0080
- +#define STAT_DMAON 0x0100
- +#define STAT_RXON 0x0200
- +#define STAT_TXON 0x0400
- +#define STAT_FIFOFULL 0x2000
- +#define STAT_FIFOEMPTY 0x4000
- +#define STAT_FIFODIR 0x8000
- +
- +/* configuration register 1 */
- +#define REG_CONFIG1 (dev->base_addr + 0x10)
- +#define CFG1_BUFSELSTAT0 0x0000
- +#define CFG1_BUFSELSTAT1 0x0001
- +#define CFG1_BUFSELSTAT2 0x0002
- +#define CFG1_BUFSELSTAT3 0x0003
- +#define CFG1_BUFSELSTAT4 0x0004
- +#define CFG1_BUFSELSTAT5 0x0005
- +#define CFG1_ADDRPROM 0x0006
- +#define CFG1_TRANSEND 0x0007
- +#define CFG1_LOCBUFMEM 0x0008
- +#define CFG1_INTVECTOR 0x0009
- +#define CFG1_DMABURSTCONT 0x0000
- +#define CFG1_DMABURST800NS 0x0010
- +#define CFG1_DMABURST1600NS 0x0020
- +#define CFG1_DMABURST3200NS 0x0030
- +#define CFG1_DMABURST1 0x0000
- +#define CFG1_DMABURST4 0x0040
- +#define CFG1_DMABURST8 0x0080
- +#define CFG1_DMABURST16 0x00C0
- +#define CFG1_RECVCOMPSTAT0 0x0100
- +#define CFG1_RECVCOMPSTAT1 0x0200
- +#define CFG1_RECVCOMPSTAT2 0x0400
- +#define CFG1_RECVCOMPSTAT3 0x0800
- +#define CFG1_RECVCOMPSTAT4 0x1000
- +#define CFG1_RECVCOMPSTAT5 0x2000
- +#define CFG1_RECVSPECONLY 0x0000
- +#define CFG1_RECVSPECBROAD 0x4000
- +#define CFG1_RECVSPECBRMULTI 0x8000
- +#define CFG1_RECVPROMISC 0xC000
- +
- +/* configuration register 2 */
- +#define REG_CONFIG2 (dev->base_addr + 0x20)
- +#define CFG2_BYTESWAP 0x0001
- +#define CFG2_ERRENCRC 0x0008
- +#define CFG2_ERRENDRIBBLE 0x0010
- +#define CFG2_ERRSHORTFRAME 0x0020
- +#define CFG2_SLOTSELECT 0x0040
- +#define CFG2_PREAMSELECT 0x0080
- +#define CFG2_ADDRLENGTH 0x0100
- +#define CFG2_RECVCRC 0x0200
- +#define CFG2_XMITNOCRC 0x0400
- +#define CFG2_LOOPBACK 0x0800
- +#define CFG2_CTRLO 0x1000
- +#define CFG2_RESET 0x8000
- +
- +#define REG_RECVEND (dev->base_addr + 0x30)
- +
- +#define REG_BUFWIN (dev->base_addr + 0x40)
- +
- +#define REG_RECVPTR (dev->base_addr + 0x50)
- +
- +#define REG_TRANSMITPTR (dev->base_addr + 0x60)
- +
- +#define REG_DMAADDR (dev->base_addr + 0x70)
- +
- +/*
- + * Cards transmit/receive headers
- + */
- +#define TX_NEXT (0xffff)
- +#define TXHDR_ENBABBLEINT (1 << 16)
- +#define TXHDR_ENCOLLISIONINT (1 << 17)
- +#define TXHDR_EN16COLLISION (1 << 18)
- +#define TXHDR_ENSUCCESS (1 << 19)
- +#define TXHDR_DATAFOLLOWS (1 << 21)
- +#define TXHDR_CHAINCONTINUE (1 << 22)
- +#define TXHDR_TRANSMIT (1 << 23)
- +#define TXSTAT_BABBLED (1 << 24)
- +#define TXSTAT_COLLISION (1 << 25)
- +#define TXSTAT_16COLLISIONS (1 << 26)
- +#define TXSTAT_DONE (1 << 31)
- +
- +#define RX_NEXT (0xffff)
- +#define RXHDR_CHAINCONTINUE (1 << 22)
- +#define RXHDR_RECEIVE (1 << 23)
- +#define RXSTAT_OVERSIZE (1 << 24)
- +#define RXSTAT_CRCERROR (1 << 25)
- +#define RXSTAT_DRIBBLEERROR (1 << 26)
- +#define RXSTAT_SHORTPACKET (1 << 27)
- +#define RXSTAT_DONE (1 << 31)
- +
- +
- +#define TX_END 0x6000
- +#define RX_START 0x6000
- +#define RX_LEN 0xA000
- +#define RX_END 0x10000
- +#define MAX_TXED 360
- +#define MAX_TX_BUFFERED 10
- +
- +struct dev_priv {
- + int bus_type; /* type of bus (not currently used) */
- + struct {
- + unsigned int command;
- + unsigned int config1;
- + unsigned int config2;
- + unsigned int transmitptr;
- + unsigned int recvptr;
- + } regs;
- + struct enet_statistics stats;
- + unsigned int txinsert; /* address to insert next packet */
- + unsigned int txed; /* transmitted index in tx_ends */
- + unsigned int txto; /* to transmit index in tx_ends */
- + unsigned int txbuffered; /* number of packets currently buffered */
- + unsigned int tx_ends[MAX_TXED]; /* addresses of transmit packets */
- + unsigned int use; /* 0 = card not in use, >0 packet transferring */
- + int broken; /* 0 = ok, 1 = something went wrong */
- +};
- +
- +extern int ether3_probe(struct device *dev);
- +static int ether3_probe1(struct device *dev);
- +static int ether3_open(struct device *dev);
- +static int ether3_send_packet(struct sk_buff *skb, struct device *dev);
- +static void ether3_interrupt(int irq, struct pt_regs *regs);
- +static void ether3_rx(struct device *dev, struct dev_priv *priv);
- +static void ether3_tx(struct device *dev, struct dev_priv *priv);
- +static int ether3_close(struct device *dev);
- +static struct enet_statistics *ether3_get_stats(struct device *dev);
- +static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
- +
- +#endif
- diff -urNwbB linux/arch/arm/drivers/net/plip.c linux.arm/arch/arm/drivers/net/plip.c
- --- linux/arch/arm/drivers/net/plip.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/plip.c Sat Feb 24 09:36:44 1996
- @@ -0,0 +1,1272 @@
- +/* $Id: plip.c,v 1.15 1995/10/03 01:47:09 gniibe Exp $ */
- +/* PLIP: A parallel port "network" driver for Linux. */
- +/* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
- +/*
- + * Authors: Donald Becker, <becker@super.org>
- + * Tommy Thorn, <thorn@daimi.aau.dk>
- + * Tanabe Hiroyasu, <hiro@sanpo.t.u-tokyo.ac.jp>
- + * Alan Cox, <gw4pts@gw4pts.ampr.org>
- + * Peter Bauer, <100136.3530@compuserve.com>
- + * Niibe Yutaka, <gniibe@mri.co.jp>
- + *
- + * Modularization and ifreq/ifmap support by Alan Cox.
- + * Rewritten by Niibe Yutaka.
- + *
- + * Fixes:
- + * 9-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
- + * - only claim 3 bytes of I/O space for port at 0x3bc
- + * - treat NULL return from register_netdev() as success in
- + * init_module()
- + * - added message if driver loaded as a module but no
- + * interfaces present.
- + * - release claimed I/O ports if malloc() fails during init.
- + *
- + * Niibe Yutaka
- + * - Module initialization. You can specify I/O addr and IRQ:
- + * # insmod plip.o io=0x3bc irq=7
- + * - MTU fix.
- + * - Make sure other end is OK, before sending a packet.
- + *
- + * 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.
- + */
- +
- +/*
- + * Original version and the name 'PLIP' from Donald Becker <becker@super.org>
- + * inspired by Russ Nelson's parallel port packet driver.
- + *
- + * NOTE:
- + * Tanabe Hiroyasu had changed the protocol, and it was in Linux v1.0.
- + * Because of the necessity to communicate to DOS machines with the
- + * Crynwr packet driver, Peter Bauer changed the protocol again
- + * back to original protocol.
- + *
- + * This version follows original PLIP protocol.
- + * So, this PLIP can't communicate the PLIP of Linux v1.0.
- + */
- +
- +/*
- + * To use with DOS box, please do (Turn on ARP switch):
- + * # ifconfig plip[0-2] arp
- + */
- +static const char *version = "NET3 PLIP version 2.1 gniibe@mri.co.jp\n";
- +
- +/*
- + Sources:
- + Ideas and protocols came from Russ Nelson's <nelson@crynwr.com>
- + "parallel.asm" parallel port packet driver.
- +
- + The "Crynwr" parallel port standard specifies the following protocol:
- + Trigger by sending '0x08' (this cause interrupt on other end)
- + count-low octet
- + count-high octet
- + ... data octets
- + checksum octet
- + Each octet is sent as <wait for rx. '0x1?'> <send 0x10+(octet&0x0F)>
- + <wait for rx. '0x0?'> <send 0x00+((octet>>4)&0x0F)>
- +
- + The packet is encapsulated as if it were ethernet.
- +
- + The cable used is a de facto standard parallel null cable -- sold as
- + a "LapLink" cable by various places. You'll need a 12-conductor cable to
- + make one yourself. The wiring is:
- + SLCTIN 17 - 17
- + GROUND 25 - 25
- + D0->ERROR 2 - 15 15 - 2
- + D1->SLCT 3 - 13 13 - 3
- + D2->PAPOUT 4 - 12 12 - 4
- + D3->ACK 5 - 10 10 - 5
- + D4->BUSY 6 - 11 11 - 6
- + Do not connect the other pins. They are
- + D5,D6,D7 are 7,8,9
- + STROBE is 1, FEED is 14, INIT is 16
- + extra grounds are 18,19,20,21,22,23,24
- +*/
- +
- +#define NO_IRQ 255
- +
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/version.h>
- +#else
- +#define MOD_INC_USE_COUNT
- +#define MOD_DEC_USE_COUNT
- +#endif
- +
- +#include <linux/kernel.h>
- +#include <linux/sched.h>
- +#include <linux/types.h>
- +#include <linux/fcntl.h>
- +#include <linux/interrupt.h>
- +#include <linux/string.h>
- +#include <linux/ptrace.h>
- +#include <linux/if_ether.h>
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include <linux/in.h>
- +#include <linux/errno.h>
- +#include <linux/delay.h>
- +#include <linux/lp.h>
- +
- +#include <linux/netdevice.h>
- +#include <linux/etherdevice.h>
- +#include <linux/skbuff.h>
- +#include <linux/if_plip.h>
- +
- +#include <linux/tqueue.h>
- +#include <linux/ioport.h>
- +#include <asm/bitops.h>
- +#include <asm/irq.h>
- +#include <asm/byteorder.h>
- +
- +/* Use 0 for production, 1 for verification, >2 for debug */
- +#ifndef NET_DEBUG
- +#define NET_DEBUG 1
- +#endif
- +static unsigned int net_debug = NET_DEBUG;
- +
- +/* In micro second */
- +#define PLIP_DELAY_UNIT 1
- +
- +/* Connection time out = PLIP_TRIGGER_WAIT * PLIP_DELAY_UNIT usec */
- +#define PLIP_TRIGGER_WAIT 500
- +
- +/* Nibble time out = PLIP_NIBBLE_WAIT * PLIP_DELAY_UNIT usec */
- +#define PLIP_NIBBLE_WAIT 3000
- +
- +#define PAR_INTR_ON (LP_PINITP|LP_PSELECP|LP_PINTEN)
- +#define PAR_INTR_OFF (LP_PINITP|LP_PSELECP)
- +#define PAR_DATA(dev) ((dev)->base_addr+0)
- +#define PAR_STATUS(dev) ((dev)->base_addr+1)
- +#define PAR_CONTROL(dev) ((dev)->base_addr+2)
- +
- +/* Bottom halfs */
- +static void plip_kick_bh(struct device *dev);
- +static void plip_bh(struct device *dev);
- +
- +/* Interrupt handler */
- +static void plip_interrupt(int irq, struct pt_regs *regs);
- +
- +/* Functions for DEV methods */
- +static int plip_rebuild_header(void *buff, struct device *dev,
- + unsigned long raddr, struct sk_buff *skb);
- +static int plip_tx_packet(struct sk_buff *skb, struct device *dev);
- +static int plip_open(struct device *dev);
- +static int plip_close(struct device *dev);
- +static struct enet_statistics *plip_get_stats(struct device *dev);
- +static int plip_config(struct device *dev, struct ifmap *map);
- +static int plip_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
- +
- +enum plip_connection_state {
- + PLIP_CN_NONE=0,
- + PLIP_CN_RECEIVE,
- + PLIP_CN_SEND,
- + PLIP_CN_CLOSING,
- + PLIP_CN_ERROR
- +};
- +
- +enum plip_packet_state {
- + PLIP_PK_DONE=0,
- + PLIP_PK_TRIGGER,
- + PLIP_PK_LENGTH_LSB,
- + PLIP_PK_LENGTH_MSB,
- + PLIP_PK_DATA,
- + PLIP_PK_CHECKSUM
- +};
- +
- +enum plip_nibble_state {
- + PLIP_NB_BEGIN,
- + PLIP_NB_1,
- + PLIP_NB_2,
- +};
- +
- +struct plip_local {
- + enum plip_packet_state state;
- + enum plip_nibble_state nibble;
- + union {
- + struct {
- +#if defined(__LITTLE_ENDIAN)
- + unsigned char lsb;
- + unsigned char msb;
- +#elif defined(__BIG_ENDIAN)
- + unsigned char msb;
- + unsigned char lsb;
- +#else
- +#error "Please fix the endianness defines in <asm/byteorder.h>"
- +#endif
- + } b;
- + unsigned short h;
- + } length;
- + unsigned short byte;
- + unsigned char checksum;
- + unsigned char data;
- + struct sk_buff *skb;
- +};
- +
- +struct net_local {
- + struct enet_statistics enet_stats;
- + struct tq_struct immediate;
- + struct tq_struct deferred;
- + struct plip_local snd_data;
- + struct plip_local rcv_data;
- + unsigned long trigger;
- + unsigned long nibble;
- + enum plip_connection_state connection;
- + unsigned short timeout_count;
- + char is_deferred;
- + char iostate;
- + int (*orig_rebuild_header)(void *eth, struct device *dev,
- + unsigned long raddr, struct sk_buff *skb);
- +};
- +
- +#define IOSTATE(x) ((x)->iostate)
- +#define RACK_STATE 0x03
- +#define RACK_UNKNOWN 0x00
- +#define RACK_ACTIVEHIGH 0x01
- +#define RACK_ACTIVELOW 0x02
- +
- +#define TACK_HIGH 0x10
- +#define TACK_UNKNOWN 0x20
- +
- +#define RESET_INTERRUPT ({ int rstirq = 0x3350058 >> 2; (void)inb (rstirq); })
- +
- +
- +/* Entry point of PLIP driver.
- + Probe the hardware, and register/initialize the driver. */
- +int
- +plip_init(struct device *dev)
- +{
- + struct net_local *nl;
- + int iosize = (PAR_DATA(dev) == 0x3bc) ? 3 : 8;
- +
- + /* Check region before the probe */
- + if (check_region(PAR_DATA(dev), iosize) < 0)
- + return -ENODEV;
- +
- + /* Check that there is something at base_addr. */
- + outb(0, PAR_DATA(dev));
- + udelay(1000);
- + if (inb(PAR_DATA(dev)) != 0)
- + return -ENODEV;
- +
- + printk(version);
- + printk("%s: Parallel port at %#3lx, ", dev->name, dev->base_addr);
- + if (dev->irq != NO_IRQ) {
- + printk("using assigned IRQ %d.\n", dev->irq);
- + } else {
- + int irq = NO_IRQ;
- +#ifdef MODULE
- + /* dev->irq==NO_IRQ means autoprobe, but we don't try to do so
- + with module. We can change it by ifconfig */
- +#else
- + unsigned int irqs = probe_irq_on();
- +
- + outb(0x00, PAR_CONTROL(dev));
- + udelay(1000);
- + outb(PAR_INTR_OFF, PAR_CONTROL(dev));
- + outb(PAR_INTR_ON, PAR_CONTROL(dev));
- + outb(PAR_INTR_OFF, PAR_CONTROL(dev));
- + udelay(1000);
- + irq = probe_irq_off(irqs);
- +#endif
- + if (irq != NO_IRQ) {
- + dev->irq = irq;
- + printk("using probed IRQ %d.\n", dev->irq);
- + } else
- + printk("failed to detect IRQ(%d) --"
- + " Please set IRQ by ifconfig.\n", irq);
- + }
- +
- + request_region(PAR_DATA(dev), iosize, dev->name);
- +
- + /* Fill in the generic fields of the device structure. */
- + ether_setup(dev);
- +
- + /* Then, override parts of it */
- + dev->hard_start_xmit = plip_tx_packet;
- + dev->open = plip_open;
- + dev->stop = plip_close;
- + dev->get_stats = plip_get_stats;
- + dev->set_config = plip_config;
- + dev->do_ioctl = plip_ioctl;
- + dev->flags = IFF_POINTOPOINT|IFF_NOARP;
- +
- + /* Set the private structure */
- + dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL);
- + if (dev->priv == NULL) {
- + printk(KERN_ERR "%s: out of memory\n", dev->name);
- + release_region(PAR_DATA(dev), iosize);
- + return -ENOMEM;
- + }
- + memset(dev->priv, 0, sizeof(struct net_local));
- + nl = (struct net_local *) dev->priv;
- +
- + nl->orig_rebuild_header = dev->rebuild_header;
- + dev->rebuild_header = plip_rebuild_header;
- +
- + /* Initialize constants */
- + nl->trigger = PLIP_TRIGGER_WAIT;
- + nl->nibble = PLIP_NIBBLE_WAIT;
- + nl->iostate = RACK_UNKNOWN | TACK_UNKNOWN | TACK_HIGH;
- +
- + /* Initialize task queue structures */
- + nl->immediate.next = &tq_last;
- + nl->immediate.sync = 0;
- + nl->immediate.routine = (void *)(void *)plip_bh;
- + nl->immediate.data = dev;
- +
- + nl->deferred.next = &tq_last;
- + nl->deferred.sync = 0;
- + nl->deferred.routine = (void *)(void *)plip_kick_bh;
- + nl->deferred.data = dev;
- +
- + return 0;
- +}
- +
- +/* Bottom half handler for the delayed request.
- + This routine is kicked by do_timer().
- + Request `plip_bh' to be invoked. */
- +static void
- +plip_kick_bh(struct device *dev)
- +{
- + struct net_local *nl = (struct net_local *)dev->priv;
- +
- + if (nl->is_deferred) {
- + queue_task(&nl->immediate, &tq_immediate);
- + mark_bh(IMMEDIATE_BH);
- + }
- +}
- +
- +/* Forward declarations of internal routines */
- +static int plip_none(struct device *, struct net_local *,
- + struct plip_local *, struct plip_local *);
- +static int plip_receive_packet(struct device *, struct net_local *,
- + struct plip_local *, struct plip_local *);
- +static int plip_send_packet(struct device *, struct net_local *,
- + struct plip_local *, struct plip_local *);
- +static int plip_connection_close(struct device *, struct net_local *,
- + struct plip_local *, struct plip_local *);
- +static int plip_error(struct device *, struct net_local *,
- + struct plip_local *, struct plip_local *);
- +static int plip_bh_timeout_error(struct device *dev, struct net_local *nl,
- + struct plip_local *snd,
- + struct plip_local *rcv,
- + int error);
- +
- +#define OK 0
- +#define TIMEOUT 1
- +#define ERROR 2
- +
- +typedef int (*plip_func)(struct device *dev, struct net_local *nl,
- + struct plip_local *snd, struct plip_local *rcv);
- +
- +static plip_func connection_state_table[] =
- +{
- + plip_none,
- + plip_receive_packet,
- + plip_send_packet,
- + plip_connection_close,
- + plip_error
- +};
- +
- +/* Bottom half handler of PLIP. */
- +static void
- +plip_bh(struct device *dev)
- +{
- + struct net_local *nl = (struct net_local *)dev->priv;
- + struct plip_local *snd = &nl->snd_data;
- + struct plip_local *rcv = &nl->rcv_data;
- + plip_func f;
- + int r;
- +
- + nl->is_deferred = 0;
- + f = connection_state_table[nl->connection];
- + if ((r = (*f)(dev, nl, snd, rcv)) != OK
- + && (r = plip_bh_timeout_error(dev, nl, snd, rcv, r)) != OK) {
- + nl->is_deferred = 1;
- + queue_task(&nl->deferred, &tq_timer);
- + }
- +}
- +
- +static int
- +plip_bh_timeout_error(struct device *dev, struct net_local *nl,
- + struct plip_local *snd, struct plip_local *rcv,
- + int error)
- +{
- + unsigned char c0;
- +
- + cli();
- + if (nl->connection == PLIP_CN_SEND) {
- +
- + if (error != ERROR) { /* Timeout */
- + nl->timeout_count++;
- + if (snd->state == PLIP_PK_TRIGGER) {
- + if (IOSTATE(nl) & TACK_UNKNOWN)
- + IOSTATE(nl) ^= TACK_HIGH;
- + }
- + if ((snd->state == PLIP_PK_TRIGGER
- + && nl->timeout_count <= 10)
- + || nl->timeout_count <= 3) {
- + sti();
- + /* Try again later */
- + return TIMEOUT;
- + }
- + c0 = inb(PAR_STATUS(dev));
- + printk("%s: transmit timeout(%d,%02x)\n",
- + dev->name, snd->state, c0);
- + }
- + nl->enet_stats.tx_errors++;
- + nl->enet_stats.tx_aborted_errors++;
- + } else if (nl->connection == PLIP_CN_RECEIVE) {
- + if (rcv->state == PLIP_PK_TRIGGER) {
- + /* Transmission was interrupted. */
- + sti();
- + return OK;
- + }
- + if (error != ERROR) { /* Timeout */
- + if (++nl->timeout_count <= 3) {
- + sti();
- + /* Try again later */
- + return TIMEOUT;
- + }
- + c0 = inb(PAR_STATUS(dev));
- + printk("%s: receive timeout(%d,%02x)\n",
- + dev->name, rcv->state, c0);
- + }
- + nl->enet_stats.rx_dropped++;
- + }
- + rcv->state = PLIP_PK_DONE;
- + if (rcv->skb) {
- + rcv->skb->free = 1;
- + kfree_skb(rcv->skb, FREE_READ);
- + rcv->skb = NULL;
- + }
- + snd->state = PLIP_PK_DONE;
- + if (snd->skb) {
- + dev_kfree_skb(snd->skb, FREE_WRITE);
- + snd->skb = NULL;
- + }
- + disable_irq(dev->irq);
- + outb(PAR_INTR_OFF, PAR_CONTROL(dev));
- + dev->tbusy = 1;
- + nl->connection = PLIP_CN_ERROR;
- + if (IOSTATE(nl) & TACK_HIGH)
- + outb(0x00, PAR_DATA(dev));
- + else
- + outb(0x08, PAR_DATA(dev));
- + sti();
- +
- + return TIMEOUT;
- +}
- +
- +static int
- +plip_none(struct device *dev, struct net_local *nl,
- + struct plip_local *snd, struct plip_local *rcv)
- +{
- + return OK;
- +}
- +
- +/* PLIP_RECEIVE --- receive a byte(two nibbles)
- + Returns OK on success, TIMEOUT on timeout */
- +inline static int
- +plip_receive(unsigned short nibble_timeout, unsigned short status_addr,
- + enum plip_nibble_state *ns_p, unsigned char *data_p)
- +{
- + unsigned char c0, c1;
- + unsigned int cx;
- +
- + switch (*ns_p) {
- + case PLIP_NB_BEGIN:
- + cx = nibble_timeout;
- + while (1) {
- + c0 = inb(status_addr);
- + udelay(PLIP_DELAY_UNIT);
- + if ((c0 & 0x80) == 0) {
- + c1 = inb(status_addr);
- + if (c0 == c1)
- + break;
- + }
- + if (--cx == 0)
- + return TIMEOUT;
- + }
- + *data_p = (c0 >> 3) & 0x0f;
- + outb(0x10, --status_addr); /* send ACK */
- + status_addr++;
- + *ns_p = PLIP_NB_1;
- +
- + case PLIP_NB_1:
- + cx = nibble_timeout;
- + while (1) {
- + c0 = inb(status_addr);
- + udelay(PLIP_DELAY_UNIT);
- + if (c0 & 0x80) {
- + c1 = inb(status_addr);
- + if (c0 == c1)
- + break;
- + }
- + if (--cx == 0)
- + return TIMEOUT;
- + }
- + *data_p |= (c0 << 1) & 0xf0;
- + outb(0x00, --status_addr); /* send ACK */
- + status_addr++;
- + *ns_p = PLIP_NB_BEGIN;
- + case PLIP_NB_2:
- + break;
- + }
- + return OK;
- +}
- +
- +/* PLIP_RECEIVE_PACKET --- receive a packet */
- +static int
- +plip_receive_packet(struct device *dev, struct net_local *nl,
- + struct plip_local *snd, struct plip_local *rcv)
- +{
- + unsigned short status_addr = PAR_STATUS(dev);
- + unsigned short nibble_timeout = nl->nibble;
- + unsigned char *lbuf;
- +
- + switch (rcv->state) {
- + case PLIP_PK_TRIGGER:
- + disable_irq(dev->irq);
- + outb(PAR_INTR_OFF, PAR_CONTROL(dev));
- + dev->interrupt = 0;
- + if (IOSTATE(nl) & TACK_HIGH)
- + outb(0x01, PAR_DATA(dev)); /* send ACK */
- + else
- + outb(0x09, PAR_DATA(dev)); /* send ACK */
- + if (net_debug > 2)
- + printk("%s: receive start\n", dev->name);
- + rcv->state = PLIP_PK_LENGTH_LSB;
- + rcv->nibble = PLIP_NB_BEGIN;
- +
- + case PLIP_PK_LENGTH_LSB:
- + if (snd->state != PLIP_PK_DONE) {
- + if (plip_receive(nl->trigger, status_addr,
- + &rcv->nibble, &rcv->length.b.lsb)) {
- + /* collision, here dev->tbusy == 1 */
- + rcv->state = PLIP_PK_DONE;
- + nl->is_deferred = 1;
- + nl->connection = PLIP_CN_SEND;
- + queue_task(&nl->deferred, &tq_timer);
- + outb(PAR_INTR_ON, PAR_CONTROL(dev));
- + RESET_INTERRUPT;
- + enable_irq(dev->irq);
- + return OK;
- + }
- + } else {
- + if (plip_receive(nibble_timeout, status_addr,
- + &rcv->nibble, &rcv->length.b.lsb))
- + return TIMEOUT;
- + }
- + rcv->state = PLIP_PK_LENGTH_MSB;
- +
- + case PLIP_PK_LENGTH_MSB:
- + if (plip_receive(nibble_timeout, status_addr,
- + &rcv->nibble, &rcv->length.b.msb))
- + return TIMEOUT;
- + if (rcv->length.h > dev->mtu + dev->hard_header_len
- + || rcv->length.h < 8) {
- + printk("%s: bogus packet size %d.\n", dev->name, rcv->length.h);
- + return ERROR;
- + }
- + /* Malloc up new buffer. */
- + rcv->skb = dev_alloc_skb(rcv->length.h);
- + if (rcv->skb == NULL) {
- + printk("%s: Memory squeeze.\n", dev->name);
- + return ERROR;
- + }
- + skb_reserve (rcv->skb, -2);
- + skb_put(rcv->skb,rcv->length.h);
- + rcv->skb->dev = dev;
- + rcv->state = PLIP_PK_DATA;
- + rcv->byte = 0;
- + rcv->checksum = 0;
- +
- + case PLIP_PK_DATA:
- + lbuf = rcv->skb->data;
- + do
- + if (plip_receive(nibble_timeout, status_addr,
- + &rcv->nibble, &lbuf[rcv->byte]))
- + return TIMEOUT;
- + while (++rcv->byte < rcv->length.h);
- + do
- + rcv->checksum += lbuf[--rcv->byte];
- + while (rcv->byte);
- + rcv->state = PLIP_PK_CHECKSUM;
- +
- + case PLIP_PK_CHECKSUM:
- + if (plip_receive(nibble_timeout, status_addr,
- + &rcv->nibble, &rcv->data))
- + return TIMEOUT;
- + if (rcv->data != rcv->checksum) {
- + nl->enet_stats.rx_crc_errors++;
- + if (net_debug)
- + printk("%s: checksum error\n", dev->name);
- + return ERROR;
- + }
- + rcv->state = PLIP_PK_DONE;
- +
- + case PLIP_PK_DONE:
- + /* Inform the upper layer for the arrival of a packet. */
- + rcv->skb->protocol=eth_type_trans(rcv->skb, dev);
- + netif_rx(rcv->skb);
- + nl->enet_stats.rx_packets++;
- + rcv->skb = NULL;
- + if (net_debug > 2)
- + printk("%s: receive end\n", dev->name);
- +
- + /* Close the connection. */
- + if (IOSTATE(nl) & TACK_HIGH)
- + outb (0x00, PAR_DATA(dev));
- + else
- + outb (0x08, PAR_DATA(dev));
- + cli();
- + if (snd->state != PLIP_PK_DONE) {
- + nl->connection = PLIP_CN_SEND;
- + sti();
- + queue_task(&nl->immediate, &tq_immediate);
- + outb(PAR_INTR_ON, PAR_CONTROL(dev));
- + RESET_INTERRUPT;
- + enable_irq(dev->irq);
- + return OK;
- + } else {
- + nl->connection = PLIP_CN_NONE;
- + sti();
- + outb(PAR_INTR_ON, PAR_CONTROL(dev));
- + RESET_INTERRUPT;
- + enable_irq(dev->irq);
- + return OK;
- + }
- + }
- + return OK;
- +}
- +
- +/* PLIP_SEND --- send a byte (two nibbles)
- + Returns OK on success, TIMEOUT when timeout */
- +inline static int
- +plip_send(unsigned short nibble_timeout, unsigned short data_addr,
- + enum plip_nibble_state *ns_p, unsigned char data)
- +{
- + unsigned char c0;
- + unsigned int cx;
- +
- + switch (*ns_p) {
- + case PLIP_NB_BEGIN:
- + outb((data & 0x0f), data_addr);
- + *ns_p = PLIP_NB_1;
- +
- + case PLIP_NB_1:
- + outb(0x10 | (data & 0x0f), data_addr);
- + cx = nibble_timeout;
- + data_addr++;
- + while (1) {
- + c0 = inb(data_addr);
- + if ((c0 & 0x80) == 0)
- + break;
- + if (--cx == 0)
- + return TIMEOUT;
- + udelay(PLIP_DELAY_UNIT);
- + }
- + outb(0x10 | (data >> 4), --data_addr);
- + *ns_p = PLIP_NB_2;
- +
- + case PLIP_NB_2:
- + outb((data >> 4), data_addr);
- + data_addr++;
- + cx = nibble_timeout;
- + while (1) {
- + c0 = inb(data_addr);
- + if (c0 & 0x80)
- + break;
- + if (--cx == 0)
- + return TIMEOUT;
- + udelay(PLIP_DELAY_UNIT);
- + }
- + data_addr--;
- + *ns_p = PLIP_NB_BEGIN;
- + return OK;
- + }
- + return OK;
- +}
- +
- +/* PLIP_SEND_PACKET --- send a packet */
- +static int
- +plip_send_packet(struct device *dev, struct net_local *nl,
- + struct plip_local *snd, struct plip_local *rcv)
- +{
- + unsigned short data_addr = PAR_DATA(dev);
- + unsigned short nibble_timeout = nl->nibble;
- + unsigned char *lbuf;
- + unsigned char c0;
- + unsigned int cx;
- +
- + if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) {
- + printk("%s: send skb lost\n", dev->name);
- + snd->state = PLIP_PK_DONE;
- + snd->skb = NULL;
- + return ERROR;
- + }
- +
- + switch (snd->state) {
- + case PLIP_PK_TRIGGER:
- + switch (IOSTATE(nl) & RACK_STATE) {
- + case RACK_UNKNOWN: /* we don't know the active edge of ACK */
- + if ((inb(PAR_STATUS(dev)) & 0xb8) != 0x80)
- + return TIMEOUT;
- + break;
- +
- + case RACK_ACTIVEHIGH: /* PC-style intr when ACK 0 -> 1 */
- + if ((inb(PAR_STATUS(dev)) & 0xf8) != 0x80)
- + return TIMEOUT;
- + break;
- +
- + case RACK_ACTIVELOW: /* ARM-style intr when ACK 1 -> 0 */
- + if ((inb(PAR_STATUS(dev)) & 0xf8) != 0xc0)
- + return TIMEOUT;
- + break;
- + }
- +
- + /* Trigger remote rx interrupt. */
- + if (IOSTATE(nl) & TACK_HIGH)
- + outb(0x08, data_addr);
- + else
- + outb(0x00, data_addr);
- + cx = nl->trigger;
- + while (1) {
- + udelay(PLIP_DELAY_UNIT);
- + cli();
- + if (nl->connection == PLIP_CN_RECEIVE) {
- + sti();
- + /* interrupted */
- + nl->enet_stats.collisions++;
- + if (net_debug > 1)
- + printk("%s: collision.\n", dev->name);
- + return OK;
- + }
- + c0 = inb(PAR_STATUS(dev));
- + if (c0 & 0x08) {
- + disable_irq(dev->irq);
- + outb(PAR_INTR_OFF, PAR_CONTROL(dev));
- + if (net_debug > 2)
- + printk("%s: send start\n", dev->name);
- + snd->state = PLIP_PK_LENGTH_LSB;
- + snd->nibble = PLIP_NB_BEGIN;
- + nl->timeout_count = 0;
- + sti();
- + break;
- + }
- + sti();
- + if (--cx == 0) {
- + if (IOSTATE(nl) & TACK_HIGH)
- + outb(0x00, data_addr);
- + else
- + outb(0x08, data_addr);
- + return TIMEOUT;
- + }
- + }
- + if (IOSTATE(nl) & TACK_UNKNOWN) {
- + if (net_debug > 1)
- + printk ("%s: remote ACK active %s\n", dev->name, IOSTATE(nl) & TACK_HIGH ? "high" : "low");
- + IOSTATE(nl) &= ~TACK_UNKNOWN;
- + }
- +
- + case PLIP_PK_LENGTH_LSB:
- + if (plip_send(nibble_timeout, data_addr,
- + &snd->nibble, snd->length.b.lsb))
- + return TIMEOUT;
- + snd->state = PLIP_PK_LENGTH_MSB;
- +
- + case PLIP_PK_LENGTH_MSB:
- + if (plip_send(nibble_timeout, data_addr,
- + &snd->nibble, snd->length.b.msb))
- + return TIMEOUT;
- + snd->state = PLIP_PK_DATA;
- + snd->byte = 0;
- + snd->checksum = 0;
- +
- + case PLIP_PK_DATA:
- + do
- + if (plip_send(nibble_timeout, data_addr,
- + &snd->nibble, lbuf[snd->byte]))
- + return TIMEOUT;
- + while (++snd->byte < snd->length.h);
- + do
- + snd->checksum += lbuf[--snd->byte];
- + while (snd->byte);
- + snd->state = PLIP_PK_CHECKSUM;
- +
- + case PLIP_PK_CHECKSUM:
- + if (plip_send(nibble_timeout, data_addr,
- + &snd->nibble, snd->checksum))
- + return TIMEOUT;
- +
- + dev_kfree_skb(snd->skb, FREE_WRITE);
- + nl->enet_stats.tx_packets++;
- + snd->state = PLIP_PK_DONE;
- +
- + case PLIP_PK_DONE:
- + /* Close the connection */
- + if (IOSTATE(nl) & TACK_HIGH)
- + outb (0x00, data_addr);
- + else
- + outb (0x08, data_addr);
- + snd->skb = NULL;
- + if (net_debug > 2)
- + printk("%s: send end\n", dev->name);
- + nl->connection = PLIP_CN_CLOSING;
- + nl->is_deferred = 1;
- + queue_task(&nl->deferred, &tq_timer);
- + outb(PAR_INTR_ON, PAR_CONTROL(dev));
- + RESET_INTERRUPT;
- + enable_irq(dev->irq);
- + return OK;
- + }
- + return OK;
- +}
- +
- +static int
- +plip_connection_close(struct device *dev, struct net_local *nl,
- + struct plip_local *snd, struct plip_local *rcv)
- +{
- + cli();
- + if (nl->connection == PLIP_CN_CLOSING) {
- + nl->connection = PLIP_CN_NONE;
- + dev->tbusy = 0;
- + mark_bh(NET_BH);
- + }
- + sti();
- + return OK;
- +}
- +
- +/* PLIP_ERROR --- wait till other end settled */
- +static int
- +plip_error(struct device *dev, struct net_local *nl,
- + struct plip_local *snd, struct plip_local *rcv)
- +{
- + unsigned char status;
- +
- + status = inb(PAR_STATUS(dev));
- + switch (IOSTATE(nl) & RACK_STATE) {
- + case RACK_UNKNOWN: /* we can't reset the interface */
- + nl->is_deferred = 1;
- + queue_task(&nl->deferred, &tq_timer);
- + return OK;
- +
- + case RACK_ACTIVEHIGH: /* PC-style */
- + if ((status & 0xf8) != 0x80) {
- + nl->is_deferred = 1;
- + queue_task(&nl->deferred, &tq_timer);
- + return OK;
- + }
- + break;
- + case RACK_ACTIVELOW: /* ARM-style */
- + if ((status & 0xf8) != 0xc0) {
- + nl->is_deferred = 1;
- + queue_task(&nl->deferred, &tq_timer);
- + return OK;
- + }
- + break;
- + }
- + if (net_debug > 2)
- + printk("%s: reset interface.\n", dev->name);
- + nl->connection = PLIP_CN_NONE;
- + dev->tbusy = 0;
- + dev->interrupt = 0;
- + outb(PAR_INTR_ON, PAR_CONTROL(dev));
- + RESET_INTERRUPT;
- + enable_irq(dev->irq);
- + mark_bh(NET_BH);
- + return OK;
- +}
- +
- +/* Handle the parallel port interrupts. */
- +static void
- +plip_interrupt(int irq, struct pt_regs * regs)
- +{
- + struct device *dev = (struct device *) irq2dev_map[irq];
- + struct net_local *nl = (struct net_local *)dev->priv;
- + struct plip_local *rcv = &nl->rcv_data;
- + unsigned char c0;
- +
- + RESET_INTERRUPT;
- +
- + if (dev == NULL) {
- + printk ("plip_interrupt: irq %d for unknown device.\n", irq);
- + return;
- + }
- +
- + if (dev->interrupt)
- + return;
- +
- + c0 = inb(PAR_STATUS(dev));
- + switch (IOSTATE(nl) & RACK_STATE) {
- + case RACK_UNKNOWN: /* don't know */
- + /*
- + * Here, we rely on the first interrupt being correct
- + * and not spurious... This is only used if we don't know
- + * which edge that the ACK interrupt triggers on.
- + */
- + if ((c0 & 0xf8) == 0xc0) {
- + IOSTATE(nl) = (IOSTATE(nl) & ~RACK_STATE) | RACK_ACTIVEHIGH;
- + printk ("%s: ACK active high\n", dev->name);
- + } else
- + if ((c0 & 0xf8) == 0x80) {
- + IOSTATE(nl) = (IOSTATE(nl) & ~RACK_STATE) | RACK_ACTIVELOW;
- + printk ("%s: ACK active low\n", dev->name);
- + } else {
- + if (net_debug > 1)
- + printk ("%s: spurious interrupt\n", dev->name);
- + return;
- + }
- + break;
- +
- + case RACK_ACTIVEHIGH: /* ACK intr when ACK 0 -> 1 */
- + if ((c0 & 0xf8) != 0xc0) {
- + if (net_debug > 1)
- + printk ("%s: spurious interrupt\n", dev->name);
- + return;
- + }
- + break;
- +
- + case RACK_ACTIVELOW: /* ACK intr when ACK 1 -> 0 */
- + if ((c0 & 0xf8) != 0x80) {
- + if (net_debug > 1)
- + printk ("%s: spurious interrupt\n", dev->name);
- + return;
- + }
- + break;
- + }
- + dev->interrupt = 1;
- + if (net_debug > 3)
- + printk("%s: interrupt.\n", dev->name);
- +
- + cli();
- + switch (nl->connection) {
- + case PLIP_CN_CLOSING:
- + dev->tbusy = 0;
- + case PLIP_CN_NONE:
- + case PLIP_CN_SEND:
- + dev->last_rx = jiffies;
- + rcv->state = PLIP_PK_TRIGGER;
- + nl->connection = PLIP_CN_RECEIVE;
- + nl->timeout_count = 0;
- + queue_task(&nl->immediate, &tq_immediate);
- + mark_bh(IMMEDIATE_BH);
- + sti();
- + break;
- +
- + case PLIP_CN_RECEIVE:
- + sti();
- + printk("%s: receive interrupt when receiving packet\n", dev->name);
- + break;
- +
- + case PLIP_CN_ERROR:
- + sti();
- + printk("%s: receive interrupt in error state\n", dev->name);
- + break;
- + }
- +}
- +
- +/* We don't need to send arp, for plip is point-to-point. */
- +static int
- +plip_rebuild_header(void *buff, struct device *dev, unsigned long dst,
- + struct sk_buff *skb)
- +{
- + struct net_local *nl = (struct net_local *)dev->priv;
- + struct ethhdr *eth = (struct ethhdr *)buff;
- + int i;
- +
- + if ((dev->flags & IFF_NOARP)==0)
- + return nl->orig_rebuild_header(buff, dev, dst, skb);
- +
- + if (eth->h_proto != htons(ETH_P_IP)) {
- + printk("plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
- + memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
- + return 0;
- + }
- +
- + for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
- + eth->h_dest[i] = 0xfc;
- + memcpy(&(eth->h_dest[i]), &dst, sizeof(unsigned long));
- + return 0;
- +}
- +
- +static int
- +plip_tx_packet(struct sk_buff *skb, struct device *dev)
- +{
- + struct net_local *nl = (struct net_local *)dev->priv;
- + struct plip_local *snd = &nl->snd_data;
- +
- + if (dev->tbusy)
- + return 1;
- +
- + /* If some higher layer thinks we've missed an tx-done interrupt
- + we are passed NULL. Caution: dev_tint() handles the cli()/sti()
- + itself. */
- + if (skb == NULL) {
- + dev_tint(dev);
- + return 0;
- + }
- +
- + if (set_bit(0, (void*)&dev->tbusy) != 0) {
- + printk("%s: Transmitter access conflict.\n", dev->name);
- + return 1;
- + }
- +
- + if (skb->len > dev->mtu + dev->hard_header_len) {
- + printk("%s: packet too big, %d.\n", dev->name, (int)skb->len);
- + dev->tbusy = 0;
- + return 0;
- + }
- +
- + if (net_debug > 2)
- + printk("%s: send request\n", dev->name);
- +
- + cli();
- + dev->trans_start = jiffies;
- + snd->skb = skb;
- + snd->length.h = skb->len;
- + snd->state = PLIP_PK_TRIGGER;
- + if (nl->connection == PLIP_CN_NONE) {
- + nl->connection = PLIP_CN_SEND;
- + nl->timeout_count = 0;
- + }
- + queue_task(&nl->immediate, &tq_immediate);
- + mark_bh(IMMEDIATE_BH);
- + sti();
- +
- + return 0;
- +}
- +
- +/* Open/initialize the board. This is called (in the current kernel)
- + sometime after booting when the 'ifconfig' program is run.
- +
- + This routine gets exclusive access to the parallel port by allocating
- + its IRQ line.
- + */
- +static int
- +plip_open(struct device *dev)
- +{
- + struct net_local *nl = (struct net_local *)dev->priv;
- + int i;
- +
- + if (dev->irq == NO_IRQ) {
- + printk("%s: IRQ is not set. Please set it by ifconfig.\n", dev->name);
- + return -EAGAIN;
- + }
- + cli();
- + if (request_irq(dev->irq , plip_interrupt, 0, dev->name) != 0) {
- + sti();
- + printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq);
- + return -EAGAIN;
- + }
- + irq2dev_map[dev->irq] = dev;
- + sti();
- +
- + /* Clear the data port. */
- + if (IOSTATE(nl) & TACK_HIGH)
- + outb (0x00, PAR_DATA(dev));
- + else
- + outb (0x08, PAR_DATA(dev));
- +
- + /* Enable rx interrupt. */
- + cli ();
- + outb(PAR_INTR_ON, PAR_CONTROL(dev));
- + RESET_INTERRUPT;
- + sti ();
- +
- + /* Initialize the state machine. */
- + nl->rcv_data.state = nl->snd_data.state = PLIP_PK_DONE;
- + nl->rcv_data.skb = nl->snd_data.skb = NULL;
- + nl->connection = PLIP_CN_NONE;
- + nl->is_deferred = 0;
- + nl->iostate = RACK_ACTIVELOW | TACK_UNKNOWN | TACK_HIGH;
- +
- + /* Fill in the MAC-level header. */
- + for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
- + dev->dev_addr[i] = 0xfc;
- + memcpy(&(dev->dev_addr[i]), &dev->pa_addr, sizeof(unsigned long));
- +
- + dev->interrupt = 0;
- + dev->start = 1;
- + dev->tbusy = 0;
- + MOD_INC_USE_COUNT;
- + return 0;
- +}
- +
- +/* The inverse routine to plip_open (). */
- +static int
- +plip_close(struct device *dev)
- +{
- + struct net_local *nl = (struct net_local *)dev->priv;
- + struct plip_local *snd = &nl->snd_data;
- + struct plip_local *rcv = &nl->rcv_data;
- +
- + dev->tbusy = 1;
- + dev->start = 0;
- + cli();
- + free_irq(dev->irq);
- + irq2dev_map[dev->irq] = NULL;
- + nl->is_deferred = 0;
- + nl->connection = PLIP_CN_NONE;
- + sti();
- + if (IOSTATE(nl) & TACK_HIGH)
- + outb(0x00, PAR_DATA(dev));
- + else
- + outb(0x08, PAR_DATA(dev));
- +
- + snd->state = PLIP_PK_DONE;
- + if (snd->skb) {
- + dev_kfree_skb(snd->skb, FREE_WRITE);
- + snd->skb = NULL;
- + }
- + rcv->state = PLIP_PK_DONE;
- + if (rcv->skb) {
- + rcv->skb->free = 1;
- + kfree_skb(rcv->skb, FREE_READ);
- + rcv->skb = NULL;
- + }
- +
- + /* Reset. */
- + outb(0x00, PAR_CONTROL(dev));
- + MOD_DEC_USE_COUNT;
- + return 0;
- +}
- +
- +static struct enet_statistics *
- +plip_get_stats(struct device *dev)
- +{
- + struct net_local *nl = (struct net_local *)dev->priv;
- + struct enet_statistics *r = &nl->enet_stats;
- +
- + return r;
- +}
- +
- +static int
- +plip_config(struct device *dev, struct ifmap *map)
- +{
- + if (dev->flags & IFF_UP)
- + return -EBUSY;
- +
- + if (map->base_addr != (unsigned long)-1
- + && map->base_addr != dev->base_addr)
- + printk("%s: You cannot change base_addr of this interface (ignored).\n", dev->name);
- +
- + if (map->irq != (unsigned char)-1)
- + dev->irq = map->irq;
- + return 0;
- +}
- +
- +static int
- +plip_ioctl(struct device *dev, struct ifreq *rq, int cmd)
- +{
- + struct net_local *nl = (struct net_local *) dev->priv;
- + struct plipconf *pc = (struct plipconf *) &rq->ifr_data;
- +
- + switch(pc->pcmd) {
- + case PLIP_GET_TIMEOUT:
- + pc->trigger = nl->trigger;
- + pc->nibble = nl->nibble;
- + break;
- + case PLIP_SET_TIMEOUT:
- + nl->trigger = pc->trigger;
- + nl->nibble = pc->nibble;
- + break;
- + default:
- + return -EOPNOTSUPP;
- + }
- + return 0;
- +}
- +
- +#ifdef MODULE
- +char kernel_version[] = UTS_RELEASE;
- +int io[] = {0, 0, 0};
- +int irq[] = {NO_IRQ, NO_IRQ, NO_IRQ};
- +
- +static struct device dev_plip[] = {
- + {
- + "plip0",
- + 0, 0, 0, 0, /* memory */
- + 0x3BC, NO_IRQ, /* base, irq */
- + 0, 0, 0, NULL, plip_init
- + },
- + {
- + "plip1",
- + 0, 0, 0, 0, /* memory */
- + 0x378, NO_IRQ, /* base, irq */
- + 0, 0, 0, NULL, plip_init
- + },
- + {
- + "plip2",
- + 0, 0, 0, 0, /* memory */
- + 0x278, 0, /* base, irq */
- + 0, 0, 0, NULL, plip_init
- + }
- +};
- +
- +int
- +init_module(void)
- +{
- + int no_parameters=1;
- + int devices=0;
- + int i;
- +
- + /* When user feeds parameters, use them */
- + for (i=0; i < 3; i++) {
- + int specified=0;
- +
- + if (io[i] != 0) {
- + dev_plip[i].base_addr = io[i];
- + specified++;
- + }
- + if (irq[i] != NO_IRQ) {
- + dev_plip[i].irq = irq[i];
- + specified++;
- + }
- + if (specified) {
- + if (register_netdev(&dev_plip[i]) != 0) {
- + printk(KERN_INFO "plip%d: Not found\n", i);
- + return -EIO;
- + }
- + no_parameters = 0;
- + }
- + }
- + if (!no_parameters)
- + return 0;
- +
- + /* No parameters. Default action is probing all interfaces. */
- + for (i=0; i < 3; i++) {
- + if (register_netdev(&dev_plip[i]) == 0)
- + devices++;
- + }
- + if (devices == 0) {
- + printk(KERN_INFO "plip: no interfaces found\n");
- + return -EIO;
- + }
- + return 0;
- +}
- +
- +void
- +cleanup_module(void)
- +{
- + int i;
- +
- + for (i=0; i < 3; i++) {
- + if (dev_plip[i].priv) {
- + unregister_netdev(&dev_plip[i]);
- + release_region(PAR_DATA(&dev_plip[i]), (PAR_DATA(&dev_plip[i]) == 0x3bc)? 3 : 8);
- + kfree_s(dev_plip[i].priv, sizeof(struct net_local));
- + dev_plip[i].priv = NULL;
- + }
- + }
- +}
- +#endif /* MODULE */
- +
- +/*
- + * Local variables:
- + * compile-command: "gcc -DMODULE -DCONFIG_MODVERSIONS -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer -pipe -m486 -c plip.c"
- + * End:
- + */
- diff -urNwbB linux/arch/arm/drivers/net/ppp.c linux.arm/arch/arm/drivers/net/ppp.c
- --- linux/arch/arm/drivers/net/ppp.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/ppp.c Sat Feb 24 09:36:53 1996
- @@ -0,0 +1,3634 @@
- +/* PPP for Linux
- + *
- + * Michael Callahan <callahan@maths.ox.ac.uk>
- + * Al Longyear <longyear@netcom.com>
- + *
- + * Dynamic PPP devices by Jim Freeman <jfree@caldera.com>.
- + * ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid <ewerlid@syscon.uu.se>
- + *
- + * ==FILEVERSION 7==
- + *
- + * NOTE TO MAINTAINERS:
- + * If you modify this file at all, increment the number above.
- + * ppp.c is shipped with a PPP distribution as well as with the kernel;
- + * if everyone increases the FILEVERSION number above, then scripts
- + * can do the right thing when deciding whether to install a new ppp.c
- + * file. Don't change the format of that line otherwise, so the
- + * installation script can recognize it.
- + */
- +
- +/*
- + Sources:
- +
- + slip.c
- +
- + RFC1331: The Point-to-Point Protocol (PPP) for the Transmission of
- + Multi-protocol Datagrams over Point-to-Point Links
- +
- + RFC1332: IPCP
- +
- + ppp-2.0
- +
- + Flags for this module (any combination is acceptable for testing.):
- +
- + OPTIMIZE_FLAG_TIME - Number of jiffies to force sending of leading flag
- + character. This is normally set to ((HZ * 3) / 2).
- + This is 1.5 seconds. If zero then the leading
- + flag is always sent.
- +
- + CHECK_CHARACTERS - Enable the checking on all received characters for
- + 8 data bits, no parity. This adds a small amount of
- + processing for each received character.
- +
- + NEW_SKBUFF - Use NET3.020 sk_buff's
- +*/
- +
- +/* #define NEW_SKBUFF 1 */
- +#define OPTIMIZE_FLAG_TIME ((HZ * 3)/2)
- +
- +#define CHECK_CHARACTERS 1
- +#define PPP_COMPRESS 1
- +#define USE_SKB_PROTOCOL 1 /* Set by the installation program! */
- +
- +#ifdef NEW_SKBUFF
- +#undef USE_SKB_PROTOCOL
- +#define USE_SKB_PROTOCOL 2
- +#endif
- +
- +#ifndef PPP_MAX_DEV
- +#define PPP_MAX_DEV 256
- +#endif
- +
- +/* $Id: ppp.c,v 1.5 1995/06/12 11:36:53 paulus Exp $
- + * Added dynamic allocation of channels to eliminate
- + * compiled-in limits on the number of channels.
- + *
- + * Dynamic channel allocation code Copyright 1995 Caldera, Inc.,
- + * released under the GNU General Public License Version 2.
- + */
- +
- +#include <linux/autoconf.h>
- +#include <linux/module.h>
- +
- +#include <endian.h>
- +#include <linux/kernel.h>
- +#include <linux/sched.h>
- +#include <linux/types.h>
- +#include <linux/fcntl.h>
- +#include <linux/interrupt.h>
- +#include <linux/ptrace.h>
- +#include <linux/ioport.h>
- +#include <linux/in.h>
- +#include <linux/malloc.h>
- +#include <linux/tty.h>
- +#include <linux/errno.h>
- +#include <linux/sched.h> /* to get the struct task_struct */
- +#include <linux/string.h> /* used in new tty drivers */
- +#include <linux/signal.h> /* used in new tty drivers */
- +#include <asm/system.h>
- +#include <asm/bitops.h>
- +#include <asm/segment.h>
- +#include <net/if.h>
- +#include <linux/if_ether.h>
- +#include <linux/netdevice.h>
- +#include <linux/skbuff.h>
- +#include <linux/inet.h>
- +#include <linux/ioctl.h>
- +
- +#ifdef NEW_SKBUFF
- +#include <linux/netprotocol.h>
- +#else
- +typedef struct sk_buff sk_buff;
- +#define skb_data(skb) ((unsigned char *) (skb)->data)
- +#endif
- +
- +#include <netinet/ip.h>
- +#include <netinet/tcp.h>
- +#include <linux/if_arp.h>
- +#include "slhc.h"
- +#include <linux/ppp_defs.h>
- +#include <linux/socket.h>
- +#include <linux/if_ppp.h>
- +#include <linux/if_pppvar.h>
- +
- +#undef PACKETPTR
- +#define PACKETPTR 1
- +#include <linux/ppp-comp.h>
- +#undef PACKETPTR
- +
- +#define bsd_decompress (*ppp->sc_rcomp->decompress)
- +#define bsd_compress (*ppp->sc_xcomp->compress)
- +
- +#ifndef PPP_IPX
- +#define PPP_IPX 0x2b /* IPX protocol over PPP */
- +#endif
- +
- +#ifndef PPP_LQR
- +#define PPP_LQR 0xc025 /* Link Quality Reporting Protocol */
- +#endif
- +
- +static int ppp_register_compressor (struct compressor *cp);
- +static void ppp_unregister_compressor (struct compressor *cp);
- +
- +/*
- + * Local functions
- + */
- +
- +static struct compressor *find_compressor (int type);
- +static void ppp_init_ctrl_blk (register struct ppp *);
- +static void ppp_kick_tty (struct ppp *, struct ppp_buffer *bfr);
- +static int ppp_doframe (struct ppp *);
- +static struct ppp *ppp_alloc (void);
- +static void ppp_print_buffer (const u_char *, const u_char *, int);
- +extern inline void ppp_stuff_char (struct ppp *ppp,
- + register struct ppp_buffer *buf,
- + register u_char chr);
- +extern inline int lock_buffer (register struct ppp_buffer *buf);
- +
- +static int rcv_proto_ip (struct ppp *, u_short, u_char *, int);
- +static int rcv_proto_ipx (struct ppp *, u_short, u_char *, int);
- +static int rcv_proto_vjc_comp (struct ppp *, u_short, u_char *, int);
- +static int rcv_proto_vjc_uncomp (struct ppp *, u_short, u_char *, int);
- +static int rcv_proto_unknown (struct ppp *, u_short, u_char *, int);
- +static int rcv_proto_lqr (struct ppp *, u_short, u_char *, int);
- +static void ppp_doframe_lower (struct ppp *, u_char *, int);
- +static int ppp_doframe (struct ppp *);
- +
- +extern int ppp_bsd_compressor_init(void);
- +static void ppp_proto_ccp (struct ppp *ppp, u_char *dp, int len, int rcvd);
- +static int rcv_proto_ccp (struct ppp *, u_short, u_char *, int);
- +
- +#define ins_char(pbuf,c) (buf_base(pbuf) [(pbuf)->count++] = (u_char)(c))
- +
- +#ifndef OPTIMIZE_FLAG_TIME
- +#define OPTIMIZE_FLAG_TIME 0
- +#endif
- +
- +#ifndef PPP_MAX_DEV
- +#define PPP_MAX_DEV 256
- +#endif
- +
- +/*
- + * Parameters which may be changed via insmod.
- + */
- +
- +static int flag_time = OPTIMIZE_FLAG_TIME;
- +static int max_dev = PPP_MAX_DEV;
- +
- +/*
- + * The "main" procedure to the ppp device
- + */
- +
- +int ppp_init (struct device *);
- +
- +/*
- + * Network device driver callback routines
- + */
- +
- +static int ppp_dev_open (struct device *);
- +static int ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd);
- +static int ppp_dev_close (struct device *);
- +static int ppp_dev_xmit (sk_buff *, struct device *);
- +static struct enet_statistics *ppp_dev_stats (struct device *);
- +
- +#if USE_SKB_PROTOCOL == 0 /* The 1.2.x kernel is here */
- +#define dev_alloc_skb(count) alloc_skb(count, GFP_ATOMIC)
- +#define skb_put(skb,count) skb_data(skb)
- +#define get_long_user(addr) get_user_long((void *) addr)
- +#define get_int_user(addr) ((int) get_user_long((void *) addr))
- +#define put_byte_user(val,addr) put_fs_byte(val,((u_char *) (addr)))
- +#define put_long_user(val,addr) put_fs_long((val),((void *) (addr)))
- +
- +static unsigned short ppp_dev_type (sk_buff *, struct device *);
- +static int ppp_dev_header (unsigned char *buff, struct device *dev,
- + unsigned short type, void *daddr, void *saddr,
- + unsigned len, struct sk_buff *skb);
- +
- +#else /* The 1.3.x kernel is here */
- +#define get_long_user(addr) get_user(((int *) addr))
- +#define get_int_user(addr) ((int) get_user(((int *) addr)))
- +#define put_byte_user(val,addr) put_user((val),((u_char *) (addr)))
- +#define put_long_user(val,addr) put_user((val),((int *) (addr)))
- +
- +static int ppp_dev_header (sk_buff *, struct device *, unsigned short,
- + void *, void *, unsigned);
- +#endif
- +
- +#ifdef NEW_SKBUFF
- +static int ppp_dev_input (struct protocol *self, struct protocol *lower,
- + sk_buff *skb, void *saddr, void *daddr);
- +static int ppp_dev_output (struct protocol *self, sk_buff *skb, int type,
- + int subid, void *saddr, void *daddr, void *opt);
- +static int ppp_dev_getkey(int protocol, int subid, unsigned char *key);
- +#else
- +static int ppp_dev_rebuild (void *, struct device *, unsigned long,
- + sk_buff *);
- +#endif
- +
- +/*
- + * TTY callbacks
- + */
- +
- +static int ppp_tty_read (struct tty_struct *, struct file *, u_char *,
- + unsigned int);
- +static int ppp_tty_write (struct tty_struct *, struct file *, const u_char *,
- + unsigned int);
- +static int ppp_tty_ioctl (struct tty_struct *, struct file *, unsigned int,
- + unsigned long);
- +static int ppp_tty_select (struct tty_struct *tty, struct inode *inode,
- + struct file *filp, int sel_type, select_table * wait);
- +static int ppp_tty_open (struct tty_struct *);
- +static void ppp_tty_close (struct tty_struct *);
- +static int ppp_tty_room (struct tty_struct *tty);
- +static void ppp_tty_receive (struct tty_struct *tty, const u_char * cp,
- + char *fp, int count);
- +static void ppp_tty_wakeup (struct tty_struct *tty);
- +
- +#define CHECK_PPP(a) if (!ppp->inuse) { printk (ppp_warning, __LINE__); return a;}
- +#define CHECK_PPP_VOID() if (!ppp->inuse) { printk (ppp_warning, __LINE__); return;}
- +
- +#define in_xmap(ppp,c) (ppp->xmit_async_map[(c) >> 5] & (1 << ((c) & 0x1f)))
- +#define in_rmap(ppp,c) ((((unsigned int) (u_char) (c)) < 0x20) && \
- + ppp->recv_async_map & (1 << (c)))
- +
- +#define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f)))
- +
- +#define tty2ppp(tty) ((struct ppp *) (tty->disc_data))
- +#define dev2ppp(dev) ((struct ppp *) (dev->priv))
- +#define ppp2tty(ppp) ((struct tty_struct *) ppp->tty)
- +#define ppp2dev(ppp) ((struct device *) ppp->dev)
- +
- +struct ppp_hdr {
- + unsigned char address;
- + unsigned char control;
- + unsigned char protocol[2];
- +};
- +
- +#define PPP_HARD_HDR_LEN (sizeof (struct ppp_hdr))
- +
- +typedef struct ppp_ctrl {
- + struct ppp_ctrl *next; /* Next structure in the list */
- + char name [8]; /* Name of the device */
- + struct ppp ppp; /* PPP control table */
- + struct device dev; /* Device information table */
- +} ppp_ctrl_t;
- +
- +static ppp_ctrl_t *ppp_list = NULL;
- +
- +#define ctl2ppp(ctl) (struct ppp *) &ctl->ppp
- +#define ctl2dev(ctl) (struct device *) &ctl->dev
- +#undef PPP_NRUNIT
- +
- +/* Buffer types */
- +#define BUFFER_TYPE_DEV_RD 0 /* ppp read buffer */
- +#define BUFFER_TYPE_TTY_WR 1 /* tty write buffer */
- +#define BUFFER_TYPE_DEV_WR 2 /* ppp write buffer */
- +#define BUFFER_TYPE_TTY_RD 3 /* tty read buffer */
- +#define BUFFER_TYPE_VJ 4 /* vj compression buffer */
- +
- +/* Define this string only once for all macro envocations */
- +static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n";
- +
- +static char szVersion[] = PPP_VERSION;
- +
- +#ifdef NEW_SKBUFF
- +static struct protocol proto_ppp;
- +#endif
- +
- +/*
- + * Information for the protocol decoder
- + */
- +
- +typedef int (*pfn_proto) (struct ppp *, u_short, u_char *, int);
- +
- +typedef struct ppp_proto_struct {
- + int proto;
- + pfn_proto func;
- +} ppp_proto_type;
- +
- +static
- +ppp_proto_type proto_list[] = {
- + { PPP_IP, rcv_proto_ip },
- + { PPP_IPX, rcv_proto_ipx },
- + { PPP_VJC_COMP, rcv_proto_vjc_comp },
- + { PPP_VJC_UNCOMP, rcv_proto_vjc_uncomp },
- + { PPP_LQR, rcv_proto_lqr },
- + { PPP_CCP, rcv_proto_ccp },
- + { 0, rcv_proto_unknown } /* !!! MUST BE LAST !!! */
- +};
- +
- +/*
- + * Values for FCS calculations.
- + */
- +
- +#define PPP_INITFCS 0xffff /* Initial FCS value */
- +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */
- +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ ppp_crc16_table[((fcs) ^ (c)) & 0xff])
- +
- +unsigned short ppp_crc16_table[256] =
- +{
- + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
- + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
- + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
- + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
- + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
- + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
- + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
- + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
- + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
- + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
- + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
- + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
- + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
- + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
- + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
- + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
- + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
- + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
- + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
- + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
- + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
- + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
- + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
- + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
- + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
- + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
- + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
- + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
- + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
- + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
- + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
- + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
- +};
- +
- +#ifdef CHECK_CHARACTERS
- +static unsigned paritytab[8] =
- +{
- + 0x96696996, 0x69969669, 0x69969669, 0x96696996,
- + 0x69969669, 0x96696996, 0x96696996, 0x69969669
- +};
- +#endif
- +
- +/* local function to store a value into the LQR frame */
- +extern inline u_char * store_long (register u_char *p, register int value) {
- + *p++ = (u_char) (value >> 24);
- + *p++ = (u_char) (value >> 16);
- + *p++ = (u_char) (value >> 8);
- + *p++ = (u_char) value;
- + return p;
- +}
- +
- +/*************************************************************
- + * INITIALIZATION
- + *************************************************************/
- +
- +/* This procedure is called once and once only to define who we are to
- + * the operating system and the various procedures that it may use in
- + * accessing the ppp protocol.
- + */
- +
- +static int
- +ppp_first_time (void)
- +{
- + static struct tty_ldisc ppp_ldisc;
- + int status;
- +
- + printk (KERN_INFO
- + "PPP: version %s (dynamic channel allocation)"
- +#ifdef NEW_SKBUFF
- + " NEW_SKBUFF"
- +#endif
- + "\n", szVersion);
- +
- +#ifndef MODULE /* slhc module logic has its own copyright announcment */
- + printk (KERN_INFO
- + "TCP compression code copyright 1989 Regents of the "
- + "University of California\n");
- +#endif
- +
- + printk (KERN_INFO
- + "PPP Dynamic channel allocation code copyright 1995 "
- + "Caldera, Inc.\n");
- +/*
- + * Register the protocol for the device
- + */
- +
- +#ifdef NEW_SKBUFF
- + memset (&proto_ppp, 0, sizeof (proto_ppp));
- +
- + proto_ppp.name = "PPP";
- + proto_ppp.output = ppp_dev_output;
- + proto_ppp.input = ppp_dev_input;
- + proto_ppp.bh_input = ppp_dev_input;
- + proto_ppp.control_event = default_protocol_control;
- + proto_ppp.get_binding = ppp_dev_getkey;
- + proto_ppp.header_space = 0; /* PPP_HARD_HDR_LEN; */
- +
- + protocol_register(&proto_ppp);
- +#endif
- +
- +/*
- + * Register the tty dicipline
- + */
- + (void) memset (&ppp_ldisc, 0, sizeof (ppp_ldisc));
- + ppp_ldisc.magic = TTY_LDISC_MAGIC;
- + ppp_ldisc.open = ppp_tty_open;
- + ppp_ldisc.close = ppp_tty_close;
- + ppp_ldisc.read = ppp_tty_read;
- + ppp_ldisc.write = ppp_tty_write;
- + ppp_ldisc.ioctl = ppp_tty_ioctl;
- + ppp_ldisc.select = ppp_tty_select;
- + ppp_ldisc.receive_room = ppp_tty_room;
- + ppp_ldisc.receive_buf = ppp_tty_receive;
- + ppp_ldisc.write_wakeup = ppp_tty_wakeup;
- +
- + status = tty_register_ldisc (N_PPP, &ppp_ldisc);
- + if (status == 0)
- + printk (KERN_INFO "PPP line discipline registered.\n");
- + else
- + printk (KERN_ERR "error registering line discipline: %d\n",
- + status);
- + return status;
- +}
- +
- +/*************************************************************
- + * INITIALIZATION
- + *************************************************************/
- +
- +/* called when the device is actually created */
- +
- +static int
- +ppp_init_dev (struct device *dev)
- +{
- + int indx;
- +#ifdef NEW_SKBUFF
- + dev->default_protocol = &proto_ppp; /* Our protocol layer is PPP */
- +#else
- + dev->hard_header = ppp_dev_header;
- +#if USE_SKB_PROTOCOL == 0
- + dev->type_trans = ppp_dev_type;
- +#endif
- + dev->rebuild_header = ppp_dev_rebuild;
- + dev->hard_header_len = 0; /* PPP_HARD_HDR_LEN; */
- +#endif
- +
- + /* device INFO */
- + dev->mtu = PPP_MTU;
- + dev->hard_start_xmit = ppp_dev_xmit;
- + dev->open = ppp_dev_open;
- + dev->stop = ppp_dev_close;
- + dev->get_stats = ppp_dev_stats;
- + dev->do_ioctl = ppp_dev_ioctl;
- + dev->addr_len = 0;
- + dev->type = ARPHRD_PPP;
- +
- + for (indx = 0; indx < DEV_NUMBUFFS; indx++)
- + skb_queue_head_init (&dev->buffs[indx]);
- +
- + /* New-style flags */
- + dev->flags = IFF_POINTOPOINT;
- + dev->family = AF_INET;
- + dev->pa_addr = 0;
- + dev->pa_brdaddr = 0;
- + dev->pa_mask = 0;
- + dev->pa_alen = 4; /* sizeof (unsigned long) */
- +
- + return 0;
- +}
- +
- +/*
- + * Local procedure to initialize the ppp structure
- + */
- +
- +static void
- +ppp_init_ctrl_blk (register struct ppp *ppp)
- +{
- + ppp->magic = PPP_MAGIC;
- + ppp->toss = 0xE0;
- + ppp->escape = 0;
- +
- + ppp->flags = 0;
- + ppp->mtu = PPP_MTU;
- + ppp->mru = PPP_MRU;
- +
- + memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map));
- + ppp->xmit_async_map[0] = 0xffffffff;
- + ppp->xmit_async_map[3] = 0x60000000;
- + ppp->recv_async_map = 0x00000000;
- +
- + ppp->rbuf = NULL;
- + ppp->wbuf = NULL;
- + ppp->ubuf = NULL;
- + ppp->cbuf = NULL;
- + ppp->slcomp = NULL;
- + ppp->read_wait = NULL;
- + ppp->write_wait = NULL;
- + ppp->last_xmit = jiffies - flag_time;
- +
- + /* clear statistics */
- + memset (&ppp->stats, '\0', sizeof (struct pppstat));
- +
- + /* Reset the demand dial information */
- + ppp->ddinfo.xmit_idle= /* time since last NP packet sent */
- + ppp->ddinfo.recv_idle=jiffies; /* time since last NP packet received */
- +
- + /* PPP compression data */
- + ppp->sc_xc_state =
- + ppp->sc_rc_state = NULL;
- +}
- +
- +static struct symbol_table ppp_syms = {
- +#include <linux/symtab_begin.h>
- + X(ppp_register_compressor),
- + X(ppp_unregister_compressor),
- + X(ppp_crc16_table),
- +#include <linux/symtab_end.h>
- +};
- +
- +/* called at boot/load time for each ppp device defined in the kernel */
- +
- +#ifndef MODULE
- +int
- +ppp_init (struct device *dev)
- +{
- + static int first_time = 1;
- + int answer = 0;
- +
- + if (first_time) {
- + first_time = 0;
- + answer = ppp_first_time();
- + if (answer == 0)
- + (void) register_symtab (&ppp_syms);
- + }
- + if (answer)
- + return answer;
- + /*
- + * Return "not found", so that dev_init() will unlink
- + * the placeholder device entry for us.
- + */
- + return ENODEV;
- +
- +}
- +#endif
- +
- +/*
- + * Routine to allocate a buffer for later use by the driver.
- + */
- +
- +static struct ppp_buffer *
- +ppp_alloc_buf (int size, int type)
- +{
- + struct ppp_buffer *buf;
- +
- + buf = (struct ppp_buffer *) kmalloc (size + sizeof (struct ppp_buffer),
- + GFP_ATOMIC);
- +
- + if (buf != NULL) {
- + buf->size = size - 1; /* Mask for the buffer size */
- + buf->type = type;
- + buf->locked = 0;
- + buf->count = 0;
- + buf->head = 0;
- + buf->tail = 0;
- + buf->fcs = PPP_INITFCS;
- + }
- + return (buf);
- +}
- +
- +/*
- + * Routine to release the allocated buffer.
- + */
- +
- +static void
- +ppp_free_buf (struct ppp_buffer *ptr)
- +{
- + if (ptr != NULL)
- + kfree (ptr);
- +}
- +
- +/*
- + * Lock the indicated transmit buffer
- + */
- +
- +extern inline int
- +lock_buffer (register struct ppp_buffer *buf)
- +{
- + register int state;
- + int flags;
- +/*
- + * Save the current state and if free then set it to the "busy" state
- + */
- + save_flags (flags);
- + cli ();
- + state = buf->locked;
- + if (state == 0)
- + buf->locked = 2;
- +/*
- + * Restore the flags and return the previous state. 0 implies success.
- + */
- + restore_flags (flags);
- + return (state);
- +}
- +
- +/*
- + * MTU has been changed by the IP layer. Unfortunately we are not told
- + * about this, but we spot it ourselves and fix things up. We could be
- + * in an upcall from the tty driver, or in an ip packet queue.
- + */
- +
- +static int
- +ppp_changedmtu (struct ppp *ppp, int new_mtu, int new_mru)
- +{
- + struct device *dev;
- +
- + struct ppp_buffer *new_rbuf;
- + struct ppp_buffer *new_wbuf;
- + struct ppp_buffer *new_cbuf;
- + struct ppp_buffer *new_tbuf;
- +
- + struct ppp_buffer *old_rbuf;
- + struct ppp_buffer *old_wbuf;
- + struct ppp_buffer *old_cbuf;
- + struct ppp_buffer *old_tbuf;
- +
- + int mtu, mru;
- +/*
- + * Allocate the buffer from the kernel for the data
- + */
- + dev = ppp2dev (ppp);
- + mru = new_mru;
- + /* allow for possible escapement of every character */
- + mtu = (new_mtu * 2) + 20;
- +
- + /* RFC 1331, section 7.2 says the minimum value is 1500 bytes */
- + if (mru < PPP_MRU)
- + mru = PPP_MRU;
- +
- + mru += 10;
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO "ppp: channel %s mtu = %d, mru = %d\n",
- + dev->name, new_mtu, new_mru);
- +
- + new_wbuf = ppp_alloc_buf (mtu+PPP_HARD_HDR_LEN, BUFFER_TYPE_DEV_WR);
- + new_tbuf = ppp_alloc_buf ((PPP_MTU * 2) + 24, BUFFER_TYPE_TTY_WR);
- + new_rbuf = ppp_alloc_buf (mru + 84, BUFFER_TYPE_DEV_RD);
- + new_cbuf = ppp_alloc_buf (mru+PPP_HARD_HDR_LEN, BUFFER_TYPE_VJ);
- +/*
- + * If the buffers failed to allocate then complain and release the partial
- + * allocations.
- + */
- + if (new_wbuf == NULL || new_tbuf == NULL ||
- + new_rbuf == NULL || new_cbuf == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp: failed to allocate new buffers\n");
- +
- + ppp_free_buf (new_wbuf);
- + ppp_free_buf (new_tbuf);
- + ppp_free_buf (new_rbuf);
- + ppp_free_buf (new_cbuf);
- + return 0;
- + }
- +/*
- + * Update the pointers to the new buffer structures.
- + */
- + cli ();
- + old_wbuf = ppp->wbuf;
- + old_rbuf = ppp->rbuf;
- + old_cbuf = ppp->cbuf;
- + old_tbuf = ppp->tbuf;
- +
- + ppp->wbuf = new_wbuf;
- + ppp->rbuf = new_rbuf;
- + ppp->cbuf = new_cbuf;
- + ppp->tbuf = new_tbuf;
- +
- + ppp->rbuf->size -= 80; /* reserve space for vj header expansion */
- +
- + dev->mem_start = (unsigned long) buf_base (new_wbuf);
- + dev->mem_end = (unsigned long) (dev->mem_start + mtu);
- + dev->rmem_start = (unsigned long) buf_base (new_rbuf);
- + dev->rmem_end = (unsigned long) (dev->rmem_start + mru);
- +/*
- + * Update the parameters for the new buffer sizes
- + */
- + ppp->toss = 0xE0; /* To ignore characters until new FLAG */
- + ppp->escape = 0; /* No pending escape character */
- +
- + dev->mtu =
- + ppp->mtu = new_mtu;
- + ppp->mru = new_mru;
- +
- + ppp->s1buf = NULL;
- + ppp->s2buf = NULL;
- + ppp->xbuf = NULL;
- +
- + ppp->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
- + ppp->flags &= ~SC_XMIT_BUSY;
- +
- + sti ();
- +/*
- + * Release old buffer pointers
- + */
- + ppp_free_buf (old_rbuf);
- + ppp_free_buf (old_wbuf);
- + ppp_free_buf (old_cbuf);
- + ppp_free_buf (old_tbuf);
- + return 1;
- +}
- +
- +/*
- + * CCP is down; free (de)compressor state if necessary.
- + */
- +
- +static void
- +ppp_ccp_closed (struct ppp *ppp)
- +{
- + if (ppp->sc_xc_state) {
- + (*ppp->sc_xcomp->comp_free) (ppp->sc_xc_state);
- + ppp->sc_xc_state = NULL;
- + }
- +
- + if (ppp->sc_rc_state) {
- + (*ppp->sc_rcomp->decomp_free) (ppp->sc_rc_state);
- + ppp->sc_rc_state = NULL;
- + }
- +}
- +
- +/*
- + * Called to release all of the information in the current PPP structure.
- + *
- + * It is called when the ppp device goes down or if it is unable to go
- + * up.
- + */
- +
- +static void
- +ppp_release (struct ppp *ppp)
- +{
- + struct tty_struct *tty;
- + struct device *dev;
- +
- + tty = ppp2tty (ppp);
- + dev = ppp2dev (ppp);
- +
- + ppp_ccp_closed (ppp);
- +
- + if (tty != NULL && tty->disc_data == ppp)
- + tty->disc_data = NULL; /* Break the tty->ppp link */
- +
- + if (dev && dev->flags & IFF_UP) {
- + dev_close (dev); /* close the device properly */
- + dev->flags = 0; /* prevent recursion */
- + }
- +
- + ppp_free_buf (ppp->rbuf);
- + ppp_free_buf (ppp->wbuf);
- + ppp_free_buf (ppp->cbuf);
- + ppp_free_buf (ppp->ubuf);
- + ppp_free_buf (ppp->tbuf);
- +
- + ppp->rbuf =
- + ppp->wbuf =
- + ppp->cbuf =
- + ppp->tbuf =
- + ppp->xbuf =
- + ppp->s1buf =
- + ppp->s2buf =
- + ppp->ubuf = NULL;
- +
- + if (ppp->slcomp) {
- + slhc_free (ppp->slcomp);
- + ppp->slcomp = NULL;
- + }
- +
- + ppp->inuse = 0;
- + ppp->tty = NULL;
- +}
- +
- +/*
- + * Device callback.
- + *
- + * Called when the PPP device goes down in response to an ifconfig request.
- + */
- +
- +static void
- +ppp_tty_close (struct tty_struct *tty)
- +{
- + struct ppp *ppp = tty2ppp (tty);
- +
- + if (ppp != NULL) {
- + if (ppp->magic != PPP_MAGIC) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING
- + "ppp: trying to close unopened tty!\n");
- + } else {
- + CHECK_PPP_VOID();
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO "ppp: channel %s closing.\n",
- + ppp2dev(ppp) -> name);
- + ppp_release (ppp);
- + MOD_DEC_USE_COUNT;
- + }
- + }
- +}
- +
- +/*
- + * TTY callback.
- + *
- + * Called when the tty discipline is switched to PPP.
- + */
- +
- +static int
- +ppp_tty_open (struct tty_struct *tty)
- +{
- + struct ppp *ppp = tty2ppp (tty);
- +/*
- + * There should not be an existing table for this slot.
- + */
- + if (ppp) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_tty_open: gack! tty already associated to %s!\n",
- + ppp->magic == PPP_MAGIC ? ppp2dev(ppp)->name
- + : "unknown");
- + return -EEXIST;
- + }
- +/*
- + * Allocate the structure from the system
- + */
- + ppp = ppp_alloc();
- + if (ppp == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_tty_open: couldn't allocate ppp channel\n");
- + return -ENFILE;
- + }
- +/*
- + * Initialize the control block
- + */
- + ppp_init_ctrl_blk (ppp);
- + ppp->tty = tty;
- + tty->disc_data = ppp;
- +/*
- + * Flush any pending characters in the driver and discipline.
- + */
- + if (tty->ldisc.flush_buffer)
- + tty->ldisc.flush_buffer (tty);
- +
- + if (tty->driver.flush_buffer)
- + tty->driver.flush_buffer (tty);
- +/*
- + * Allocate space for the default VJ header compression slots
- + */
- + ppp->slcomp = slhc_init (16, 16);
- + if (ppp->slcomp == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_tty_open: no space for compression buffers!\n");
- + ppp_release (ppp);
- + return -ENOMEM;
- + }
- +/*
- + * Allocate space for the MTU and MRU buffers
- + */
- + if (ppp_changedmtu (ppp, ppp2dev(ppp)->mtu, ppp->mru) == 0) {
- + ppp_release (ppp);
- + return -ENOMEM;
- + }
- +/*
- + * Allocate space for a user level buffer
- + */
- + ppp->ubuf = ppp_alloc_buf (RBUFSIZE, BUFFER_TYPE_TTY_RD);
- + if (ppp->ubuf == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_tty_open: no space for user receive buffer\n");
- + ppp_release (ppp);
- + return -ENOMEM;
- + }
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO "ppp: channel %s open\n",
- + ppp2dev(ppp)->name);
- +
- + MOD_INC_USE_COUNT;
- + return (ppp->line);
- +}
- +
- +/*
- + * Local function to send the next portion of the buffer.
- + *
- + * Called by the tty driver's tty_wakeup function should it be entered
- + * because the partial buffer was transmitted.
- + *
- + * Called by kick_tty to send the initial portion of the buffer.
- + *
- + * Completion processing of the buffer transmission is handled here.
- + */
- +
- +static void
- +ppp_tty_wakeup_code (struct ppp *ppp, struct tty_struct *tty,
- + struct ppp_buffer *xbuf)
- +{
- + register int count, actual;
- +/*
- + * Prevent re-entrancy by ensuring that this routine is called only once.
- + */
- + cli ();
- + if (ppp->flags & SC_XMIT_BUSY) {
- + sti ();
- + return;
- + }
- + ppp->flags |= SC_XMIT_BUSY;
- + sti ();
- +/*
- + * Send the next block of data to the modem
- + */
- + count = xbuf->count - xbuf->tail;
- + actual = tty->driver.write (tty, 0,
- + buf_base (xbuf) + xbuf->tail, count);
- +/*
- + * Terminate transmission of any block which may have an error.
- + * This could occur should the carrier drop.
- + */
- + if (actual < 0) {
- + ppp->stats.ppp_oerrors++;
- + actual = count;
- + } else
- + ppp->bytes_sent += actual;
- +/*
- + * If the buffer has been transmitted then clear the indicators.
- + */
- + xbuf->tail += actual;
- + if (actual == count) {
- + xbuf = NULL;
- + ppp->flags &= ~SC_XMIT_BUSY;
- +/*
- + * Complete the transmission on the current buffer.
- + */
- + xbuf = ppp->xbuf;
- + if (xbuf != NULL) {
- + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
- + xbuf->locked = 0;
- + ppp->xbuf = NULL;
- +/*
- + * If the completed buffer came from the device write, then complete the
- + * transmission block.
- + */
- + if (ppp2dev (ppp) -> flags & IFF_UP) {
- + if (xbuf->type == BUFFER_TYPE_DEV_WR)
- + ppp2dev (ppp)->tbusy = 0;
- + mark_bh (NET_BH);
- + }
- +/*
- + * Wake up the transmission queue for all completion events.
- + */
- + wake_up_interruptible (&ppp->write_wait);
- +/*
- + * Look at the priorities. Choose a daemon write over the device driver.
- + */
- + cli();
- + xbuf = ppp->s1buf;
- + ppp->s1buf = NULL;
- + if (xbuf == NULL) {
- + xbuf = ppp->s2buf;
- + ppp->s2buf = NULL;
- + }
- + sti();
- +/*
- + * If there is a pending buffer then transmit it now.
- + */
- + if (xbuf != NULL) {
- + ppp->flags &= ~SC_XMIT_BUSY;
- + ppp_kick_tty (ppp, xbuf);
- + return;
- + }
- + }
- + }
- +/*
- + * Clear the re-entry flag
- + */
- + ppp->flags &= ~SC_XMIT_BUSY;
- +}
- +
- +/*
- + * This function is called by the tty driver when the transmit buffer has
- + * additional space. It is used by the ppp code to continue to transmit
- + * the current buffer should the buffer have been partially sent.
- + *
- + * In addition, it is used to send the first part of the buffer since the
- + * logic and the inter-locking would be identical.
- + */
- +
- +static void
- +ppp_tty_wakeup (struct tty_struct *tty)
- +{
- + struct ppp_buffer *xbuf;
- + struct ppp *ppp = tty2ppp (tty);
- +
- + if (!ppp)
- + return;
- +
- + if (ppp->magic != PPP_MAGIC)
- + return;
- +/*
- + * Ensure that there is a transmission pending. Clear the re-entry flag if
- + * there is no pending buffer. Otherwise, send the buffer.
- + */
- + xbuf = ppp->xbuf;
- + if (xbuf == NULL)
- + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
- + else
- + ppp_tty_wakeup_code (ppp, tty, xbuf);
- +}
- +
- +/*
- + * This function is called to transmit a buffer to the remote. The buffer
- + * is placed on the pending queue if there is presently a buffer being
- + * sent or it is transmitted with the aid of ppp_tty_wakeup.
- + */
- +
- +static void
- +ppp_kick_tty (struct ppp *ppp, struct ppp_buffer *xbuf)
- +{
- + register int flags;
- +/*
- + * Hold interrupts.
- + */
- + save_flags (flags);
- + cli ();
- +/*
- + * Control the flags which are best performed with the interrupts masked.
- + */
- + xbuf->locked = 1;
- + xbuf->tail = 0;
- +/*
- + * If the transmitter is busy then place the buffer on the appropriate
- + * priority queue.
- + */
- + if (ppp->xbuf != NULL) {
- + if (xbuf->type == BUFFER_TYPE_TTY_WR)
- + ppp->s1buf = xbuf;
- + else
- + ppp->s2buf = xbuf;
- + restore_flags (flags);
- + return;
- + }
- +/*
- + * If the transmitter is not busy then this is the highest priority frame
- + */
- + ppp->flags &= ~SC_XMIT_BUSY;
- + ppp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
- + ppp->xbuf = xbuf;
- + restore_flags (flags);
- +/*
- + * Do the "tty wakeup_code" to actually send this buffer.
- + */
- + ppp_tty_wakeup_code (ppp, ppp2tty (ppp), xbuf);
- +}
- +
- +/*************************************************************
- + * TTY INPUT
- + * The following functions handle input that arrives from
- + * the TTY. It recognizes PPP frames and either hands them
- + * to the network layer or queues them for delivery to a
- + * user process reading this TTY.
- + *************************************************************/
- +
- +/*
- + * Callback function from tty driver. Return the amount of space left
- + * in the receiver's buffer to decide if remote transmitter is to be
- + * throttled.
- + */
- +
- +static int
- +ppp_tty_room (struct tty_struct *tty)
- +{
- + return 65536; /* We can handle an infinite amount of data. :-) */
- +}
- +
- +/*
- + * Callback function when data is available at the tty driver.
- + */
- +
- +static void
- +ppp_tty_receive (struct tty_struct *tty, const u_char * data,
- + char *flags, int count)
- +{
- + register struct ppp *ppp = tty2ppp (tty);
- + register struct ppp_buffer *buf = NULL;
- + u_char chr;
- +/*
- + * Fetch the pointer to the buffer. Be careful about race conditions.
- + */
- + if (ppp != NULL)
- + buf = ppp->rbuf;
- +
- + if (buf == NULL)
- + return;
- +/*
- + * Verify the table pointer and ensure that the line is
- + * still in PPP discipline.
- + */
- + if (ppp->magic != PPP_MAGIC) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "PPP: handler called but couldn't find "
- + "PPP struct.\n");
- + return;
- + }
- + CHECK_PPP_VOID ();
- +/*
- + * Print the buffer if desired
- + */
- + if (ppp->flags & SC_LOG_RAWIN)
- + ppp_print_buffer ("receive buffer", data, count);
- +/*
- + * Collect the character and error condition for the character. Set the toss
- + * flag for the first character error.
- + */
- + while (count-- > 0) {
- + ppp->bytes_rcvd++;
- + chr = *data++;
- + if (flags) {
- + if (*flags && ppp->toss == 0)
- + ppp->toss = *flags;
- + ++flags;
- + }
- +/*
- + * Set the flags for 8 data bits and no parity.
- + *
- + * Actually, it sets the flags for d7 being 0/1 and parity being even/odd
- + * so that the normal processing would have all flags set at the end of the
- + * session. A missing flag bit would denote an error condition.
- + */
- +
- +#ifdef CHECK_CHARACTERS
- + if (chr & 0x80)
- + ppp->flags |= SC_RCV_B7_1;
- + else
- + ppp->flags |= SC_RCV_B7_0;
- +
- + if (paritytab[chr >> 5] & (1 << (chr & 0x1F)))
- + ppp->flags |= SC_RCV_ODDP;
- + else
- + ppp->flags |= SC_RCV_EVNP;
- +#endif
- +/*
- + * Branch on the character. Process the escape character. The sequence ESC ESC
- + * is defined to be ESC.
- + */
- + switch (chr) {
- + case PPP_ESCAPE: /* PPP_ESCAPE: invert bit in next character */
- + ppp->escape = PPP_TRANS;
- + break;
- +/*
- + * FLAG. This is the end of the block. If the block terminated by ESC FLAG,
- + * then the block is to be ignored. In addition, characters before the very
- + * first FLAG are also tossed by this procedure.
- + */
- + case PPP_FLAG: /* PPP_FLAG: end of frame */
- + ppp->stats.ppp_ibytes += ppp->rbuf->count;
- + if (ppp->escape)
- + ppp->toss |= 0x80;
- +/*
- + * Process frames which are not to be ignored. If the processing failed,
- + * then clean up the VJ tables.
- + */
- + if ((ppp->toss & 0x80) != 0 ||
- + ppp_doframe (ppp) == 0) {
- + slhc_toss (ppp->slcomp);
- + }
- +/*
- + * Reset all indicators for the new frame to follow.
- + */
- + buf->count = 0;
- + buf->fcs = PPP_INITFCS;
- + ppp->escape = 0;
- + ppp->toss = 0;
- + break;
- +/*
- + * All other characters in the data come here. If the character is in the
- + * receive mask then ignore the character.
- + */
- + default:
- + if (in_rmap (ppp, chr))
- + break;
- +/*
- + * Adjust the character and if the frame is to be discarded then simply
- + * ignore the character until the ending FLAG is received.
- + */
- + chr ^= ppp->escape;
- + ppp->escape = 0;
- +
- + if (ppp->toss != 0)
- + break;
- +/*
- + * If the count sent is within reason then store the character, bump the
- + * count, and update the FCS for the character.
- + */
- + if (buf->count < buf->size) {
- + buf_base (buf)[buf->count++] = chr;
- + buf->fcs = PPP_FCS (buf->fcs, chr);
- + break;
- + }
- +/*
- + * The peer sent too much data. Set the flags to discard the current frame
- + * and wait for the re-synchronization FLAG to be sent.
- + */
- + ppp->stats.ppp_ierrors++;
- + ppp->toss |= 0xC0;
- + break;
- + }
- + }
- +}
- +
- +/*
- + * Put the input frame into the networking system for the indicated protocol
- + */
- +
- +static int
- +ppp_rcv_rx (struct ppp *ppp, unsigned short proto, u_char * data, int count)
- +{
- + sk_buff *skb = dev_alloc_skb (count);
- +/*
- + * Generate a skb buffer for the new frame.
- + */
- + if (skb == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_do_ip: packet dropped on %s (no memory)!\n",
- + ppp2dev (ppp)->name);
- + return 0;
- + }
- +/*
- + * Move the received data from the input buffer to the skb buffer.
- + */
- + skb->dev = ppp2dev (ppp); /* We are the device */
- +#if USE_SKB_PROTOCOL == 0
- + skb->len = count;
- +#else
- + skb->protocol = proto;
- + skb->mac.raw = skb_data(skb);
- +#endif
- + memcpy (skb_put(skb,count), data, count); /* move data */
- +/*
- + * Tag the frame and kick it to the proper receive routine
- + */
- + skb->free = 1;
- + ppp->ddinfo.recv_idle = jiffies;
- + netif_rx (skb);
- + return 1;
- +}
- +
- +/*
- + * Process the receipt of an IP frame
- + */
- +
- +static int
- +rcv_proto_ip (struct ppp *ppp, unsigned short proto, u_char * data, int count)
- +{
- + if (ppp2dev (ppp)->flags & IFF_UP) {
- + if (count > 0)
- + return ppp_rcv_rx (ppp, htons (ETH_P_IP), data, count);
- + }
- + return 0;
- +}
- +
- +/*
- + * Process the receipt of an IPX frame
- + */
- +
- +static int
- +rcv_proto_ipx (struct ppp *ppp, unsigned short proto, u_char * data, int count)
- +{
- +#ifdef NEW_SKBUFF
- + if (ppp2dev (ppp)->flags & IFF_UP) {
- + if (count > 0)
- + return ppp_rcv_rx (ppp, htons (ETH_P_IPX), data, count);
- + } else
- +#endif
- + return 0;
- +}
- +
- +/*
- + * Process the receipt of an VJ Compressed frame
- + */
- +
- +static int
- +rcv_proto_vjc_comp (struct ppp *ppp, unsigned short proto,
- + u_char *data, int count)
- +{
- + if ((ppp->flags & SC_REJ_COMP_TCP) == 0) {
- + int new_count = slhc_uncompress (ppp->slcomp, data, count);
- + if (new_count >= 0) {
- + return rcv_proto_ip (ppp, PPP_IP, data, new_count);
- + }
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_NOTICE
- + "ppp: error in VJ decompression\n");
- + }
- + return 0;
- +}
- +
- +/*
- + * Process the receipt of an VJ Un-compressed frame
- + */
- +
- +static int
- +rcv_proto_vjc_uncomp (struct ppp *ppp, unsigned short proto,
- + u_char *data, int count)
- +{
- + if ((ppp->flags & SC_REJ_COMP_TCP) == 0) {
- + if (slhc_remember (ppp->slcomp, data, count) > 0) {
- + return rcv_proto_ip (ppp, PPP_IP, data, count);
- + }
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_NOTICE
- + "ppp: error in VJ memorizing\n");
- + }
- + return 0;
- +}
- +
- +/*
- + * Receive all unclassified protocols.
- + */
- +
- +static int
- +rcv_proto_unknown (struct ppp *ppp, unsigned short proto,
- + u_char *data, int len)
- +{
- + int totlen;
- + register int current_idx;
- +
- +#define PUTC(c) \
- +{ \
- + buf_base (ppp->ubuf) [current_idx++] = (u_char) (c); \
- + current_idx &= ppp->ubuf->size; \
- + if (current_idx == ppp->ubuf->tail) \
- + goto failure; \
- +}
- +
- +/*
- + * The total length includes the protocol data.
- + * Lock the user information buffer.
- + */
- + if (set_bit (0, &ppp->ubuf->locked)) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_us_queue: can't get lock\n");
- + } else {
- + current_idx = ppp->ubuf->head;
- +/*
- + * Insert the buffer length (not counted), the protocol, and the data
- + */
- + totlen = len + 2;
- + PUTC (totlen >> 8);
- + PUTC (totlen);
- +
- + PUTC (proto >> 8);
- + PUTC (proto);
- +
- + totlen -= 2;
- + while (totlen-- > 0) {
- + PUTC (*data++);
- + }
- +#undef PUTC
- +/*
- + * The frame is complete. Update the head pointer and wakeup the pppd
- + * process.
- + */
- + ppp->ubuf->head = current_idx;
- +
- + clear_bit (0, &ppp->ubuf->locked);
- + wake_up_interruptible (&ppp->read_wait);
- + if (ppp->tty->fasync != NULL)
- + kill_fasync (ppp->tty->fasync, SIGIO);
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp: successfully queued %d bytes, flags = %x\n",
- + len + 2, ppp->flags);
- +
- + return 1;
- +/*
- + * The buffer is full. Unlock the header
- + */
- +failure:
- + clear_bit (0, &ppp->ubuf->locked);
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_us_queue: ran out of buffer space.\n");
- + }
- +/*
- + * Discard the frame. There are no takers for this protocol.
- + */
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING
- + "ppp: dropping packet on the floor.\n");
- + slhc_toss (ppp->slcomp);
- + return 0;
- +}
- +
- +/*
- + * Handle a CCP packet.
- + *
- + * The CCP packet is passed along to the pppd process just like any
- + * other PPP frame. The difference is that some processing needs to be
- + * immediate or the compressors will become confused on the peer.
- + */
- +
- +static void ppp_proto_ccp (struct ppp *ppp, u_char *dp, int len, int rcvd)
- +{
- + int slen = CCP_LENGTH(dp);
- + u_char *opt = dp + CCP_HDRLEN;
- + int opt_len = slen - CCP_HDRLEN;
- +
- + if (slen > len)
- + return;
- +
- + switch (CCP_CODE(dp)) {
- + case CCP_CONFREQ:
- + case CCP_TERMREQ:
- + case CCP_TERMACK:
- +/*
- + * CCP must be going down - disable compression
- + */
- + if (ppp->flags & SC_CCP_UP) {
- + ppp->flags &= ~(SC_CCP_UP |
- + SC_COMP_RUN |
- + SC_DECOMP_RUN);
- + }
- + break;
- +
- + case CCP_CONFACK:
- + if ((ppp->flags & SC_CCP_OPEN) == 0)
- + break;
- + if (ppp->flags & SC_CCP_UP)
- + break;
- + if (slen < (CCP_HDRLEN + CCP_OPT_MINLEN))
- + break;
- + if (slen < (CCP_OPT_LENGTH (opt) + CCP_HDRLEN))
- + break;
- +/*
- + * we're agreeing to send compressed packets.
- + */
- + if (!rcvd) {
- + if (ppp->sc_xc_state == NULL)
- + break;
- +
- + if ((*ppp->sc_xcomp->comp_init)
- + (ppp->sc_xc_state,
- + opt,
- + opt_len,
- + ppp2dev (ppp)->base_addr,
- + 0,
- + ppp->flags & SC_DEBUG))
- + ppp->flags |= SC_COMP_RUN;
- + break;
- + }
- +/*
- + * peer is agreeing to send compressed packets.
- + */
- + if (ppp->sc_rc_state == NULL)
- + break;
- +
- + if ((*ppp->sc_rcomp->decomp_init)
- + (ppp->sc_rc_state,
- + opt,
- + opt_len,
- + ppp2dev (ppp)->base_addr,
- + 0,
- + ppp->mru,
- + ppp->flags & SC_DEBUG)) {
- + ppp->flags |= SC_DECOMP_RUN;
- + ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
- + }
- + break;
- +/*
- + * The protocol sequence is complete at this end
- + */
- + case CCP_RESETACK:
- + if ((ppp->flags & SC_CCP_UP) == 0)
- + break;
- +
- + if (!rcvd) {
- + if (ppp->sc_xc_state && (ppp->flags & SC_COMP_RUN))
- + (*ppp->sc_xcomp->comp_reset)(ppp->sc_xc_state);
- + } else {
- + if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) {
- + (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state);
- + ppp->flags &= ~SC_DC_ERROR;
- + }
- + }
- + break;
- + }
- +}
- +
- +static int
- +rcv_proto_ccp (struct ppp *ppp, unsigned short proto, u_char *dp, int len)
- +{
- + ppp_proto_ccp (ppp, dp, len, 1);
- + return rcv_proto_unknown (ppp, proto, dp, len);
- +}
- +
- +/*
- + * Handle a LQR packet.
- + *
- + * The LQR packet is passed along to the pppd process just like any
- + * other PPP frame. The difference is that some processing needs to be
- + * performed to append the current data to the end of the frame.
- + */
- +
- +static int
- +rcv_proto_lqr (struct ppp *ppp, unsigned short proto, u_char * data, int len)
- +{
- +#if 0 /* until support is in the pppd process don't corrupt the reject. */
- + register u_char *p;
- + if (len > 8) {
- + if (len < 48)
- + memset (&data [len], '\0', 48 - len);
- +/*
- + * Fill in the fields from the driver data
- + */
- + p = &data [48];
- + p = store_long (p, ++ppp->stats.ppp_ilqrs);
- + p = store_long (p, ppp->stats.ppp_ipackets);
- + p = store_long (p, ppp->stats.ppp_discards);
- + p = store_long (p, ppp->stats.ppp_ierrors);
- + p = store_long (p, ppp->stats.ppp_ioctects + len);
- +
- + len = 68;
- + }
- +#endif
- +/*
- + * Pass the frame to the pppd daemon.
- + */
- + return rcv_proto_unknown (ppp, proto, data, len);
- +}
- +
- +/* on entry, a received frame is in ppp->rbuf.bufr
- + check it and dispose as appropriate */
- +
- +static void ppp_doframe_lower (struct ppp *ppp, u_char *data, int count)
- +{
- + u_short proto = PPP_PROTOCOL (data);
- + ppp_proto_type *proto_ptr;
- +/*
- + * Ignore empty frames
- + */
- + if (count <= 4)
- + return;
- +/*
- + * Count the frame and print it
- + */
- + ++ppp->stats.ppp_ipackets;
- + if (ppp->flags & SC_LOG_INPKT)
- + ppp_print_buffer ("receive frame", data, count);
- +/*
- + * Find the procedure to handle this protocol. The last one is marked
- + * as a protocol 0 which is the 'catch-all' to feed it to the pppd daemon.
- + */
- + proto_ptr = proto_list;
- + while (proto_ptr->proto != 0 && proto_ptr->proto != proto)
- + ++proto_ptr;
- +/*
- + * Update the appropriate statistic counter.
- + */
- + if ((*proto_ptr->func) (ppp, proto,
- + &data[PPP_HARD_HDR_LEN],
- + count - PPP_HARD_HDR_LEN))
- + ppp->stats.ppp_ioctects += count;
- + else
- + ++ppp->stats.ppp_discards;
- +}
- +
- +/* on entry, a received frame is in ppp->rbuf.bufr
- + check it and dispose as appropriate */
- +
- +static int
- +ppp_doframe (struct ppp *ppp)
- +{
- + u_char *data = buf_base (ppp->rbuf);
- + int count = ppp->rbuf->count;
- + int addr, ctrl, proto;
- + int new_count;
- + u_char *new_data;
- +#ifndef NO_RMK_TEMP_FIX
- + int malloced_data;
- +#endif
- +/*
- + * If there is a pending error from the receiver then log it and discard
- + * the damaged frame.
- + */
- + if (ppp->toss) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING
- + "ppp_toss: tossing frame, reason = %d\n",
- + ppp->toss);
- + ppp->stats.ppp_ierrors++;
- + return 0;
- + }
- +/*
- + * An empty frame is ignored. This occurs if the FLAG sequence precedes and
- + * follows each frame.
- + */
- + if (count == 0)
- + return 1;
- +/*
- + * Generate an error if the frame is too small.
- + */
- + if (count < PPP_HARD_HDR_LEN) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING
- + "ppp: got runt ppp frame, %d chars\n", count);
- + slhc_toss (ppp->slcomp);
- + ppp->stats.ppp_ierrors++;
- + return 1;
- + }
- +/*
- + * Verify the CRC of the frame and discard the CRC characters from the
- + * end of the buffer.
- + */
- + if (ppp->rbuf->fcs != PPP_GOODFCS) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING
- + "ppp: frame with bad fcs, excess = %x\n",
- + ppp->rbuf->fcs ^ PPP_GOODFCS);
- + ppp->stats.ppp_ierrors++;
- + return 0;
- + }
- + count -= 2; /* ignore the fcs characters */
- +/*
- + * Ignore the leading ADDRESS and CONTROL fields in the frame.
- + */
- + addr = PPP_ALLSTATIONS;
- + ctrl = PPP_UI;
- +
- + if ((data[0] == PPP_ALLSTATIONS) && (data[1] == PPP_UI)) {
- + data += 2;
- + count -= 2;
- + }
- +/*
- + * Obtain the protocol from the frame
- + */
- + proto = (u_short) *data++;
- + if ((proto & 1) == 0) {
- + proto = (proto << 8) | (u_short) *data++;
- + --count;
- + }
- +/*
- + * Rewrite the header with the full information. This may encroach upon
- + * the 'filler' area in the buffer header. This is the purpose for the
- + * filler.
- + */
- + *(--data) = proto;
- + *(--data) = proto >> 8;
- + *(--data) = ctrl;
- + *(--data) = addr;
- + count += 3;
- +#ifndef NO_RMK_TEMP_FIX
- + /*
- + * rmk: should make sure that the packet received is on a word boundary.
- + * this is a temporary fix!!!
- + */
- + if ((int)data & 3) {
- + new_data = kmalloc (ppp->mru + 4, GFP_ATOMIC); /* would like to have 'count' here */
- + memcpy (new_data, data, count);
- + data = new_data;
- + malloced_data = 1;
- + } else
- + malloced_data = 0;
- +#endif
- +/*
- + * Process the active decompressor.
- + */
- + if ((ppp->sc_rc_state != (void *) 0) &&
- + (ppp->flags & SC_DECOMP_RUN) &&
- + ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) {
- + if (proto == PPP_COMP) {
- +/*
- + * If the frame is compressed then decompress it.
- + */
- + new_data = kmalloc (ppp->mru + 4, GFP_ATOMIC);
- + if (new_data == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_doframe: no memory\n");
- + slhc_toss (ppp->slcomp);
- + (*ppp->sc_rcomp->incomp) (ppp->sc_rc_state,
- + data,
- + count);
- +#ifndef NO_RMK_TEMP_FIX
- + if (malloced_data) kfree (data);
- +#endif
- + return 1;
- + }
- +/*
- + * Decompress the frame
- + */
- + new_count = bsd_decompress (ppp->sc_rc_state,
- + data,
- + count,
- + new_data,
- + ppp->mru + 4);
- + switch (new_count) {
- + default:
- + ppp_doframe_lower (ppp, new_data, new_count);
- + kfree (new_data);
- +#ifndef NO_RMK_TEMP_FIX
- + if (malloced_data) kfree (data);
- +#endif
- + return 1;
- +
- + case DECOMP_OK:
- + break;
- +
- + case DECOMP_ERROR:
- + ppp->flags |= SC_DC_ERROR;
- + break;
- +
- + case DECOMP_FATALERROR:
- + ppp->flags |= SC_DC_FERROR;
- + break;
- + }
- +/*
- + * Log the error condition and discard the frame.
- + */
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_proto_comp: "
- + "decompress err %d\n", new_count);
- + kfree (new_data);
- + slhc_toss (ppp->slcomp);
- +#ifndef NO_RMK_TEMP_FIX
- + if (malloced_data) kfree (data);
- +#endif
- + return 1;
- + }
- +/*
- + * The frame is not special. Pass it through the compressor without
- + * actually compressing the data
- + */
- + (*ppp->sc_rcomp->incomp) (ppp->sc_rc_state,
- + data,
- + count);
- + }
- +/*
- + * Process the uncompressed frame.
- + */
- + ppp_doframe_lower (ppp, data, count);
- +#ifndef NO_RMK_TEMP_FIX
- + if (malloced_data) kfree (data);
- +#endif
- + return 1;
- +}
- +
- +/*************************************************************
- + * LINE DISCIPLINE SUPPORT
- + * The following functions form support user programs
- + * which read and write data on a TTY with the PPP line
- + * discipline. Reading is done from a circular queue,
- + * filled by the lower TTY levels.
- + *************************************************************/
- +
- +/* read a PPP frame from the us_rbuff circular buffer,
- + waiting if necessary
- +*/
- +
- +static int
- +ppp_tty_read (struct tty_struct *tty, struct file *file, u_char * buf,
- + unsigned int nr)
- +{
- + struct ppp *ppp = tty2ppp (tty);
- + u_char c;
- + int len, indx;
- +
- +#define GETC(c) \
- +{ \
- + c = buf_base (ppp->ubuf) [ppp->ubuf->tail++]; \
- + ppp->ubuf->tail &= ppp->ubuf->size; \
- +}
- +
- +/*
- + * Validate the pointer to the PPP structure
- + */
- + if (!ppp)
- + return -EIO;
- +
- + if (ppp->magic != PPP_MAGIC)
- + return -EIO;
- +
- + CHECK_PPP (-ENXIO);
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_tty_read: called buf=%p nr=%u\n",
- + buf, nr);
- +/*
- + * Acquire the read lock.
- + */
- + for (;;) {
- + ppp = tty2ppp (tty);
- + if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse)
- + return 0;
- +
- + if (set_bit (0, &ppp->ubuf->locked) != 0) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_tty_read: sleeping(ubuf)\n");
- +
- + current->timeout = 0;
- + current->state = TASK_INTERRUPTIBLE;
- + schedule ();
- +
- + if (current->signal & ~current->blocked)
- + return -EINTR;
- + continue;
- + }
- +/*
- + * Before we attempt to write the frame to the user, ensure that the
- + * user has access to the pages for the total buffer length.
- + */
- + indx = verify_area (VERIFY_WRITE, buf, nr);
- + if (indx != 0)
- + return (indx);
- +/*
- + * Fetch the length of the buffer from the first two bytes.
- + */
- + if (ppp->ubuf->head == ppp->ubuf->tail)
- + len = 0;
- + else {
- + GETC (c);
- + len = c << 8;
- + GETC (c);
- + len += c;
- + }
- +/*
- + * If there is no length then wait for the data to arrive.
- + */
- + if (len == 0) {
- + /* no data */
- + clear_bit (0, &ppp->ubuf->locked);
- + if (file->f_flags & O_NONBLOCK) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_tty_read: no data "
- + "(EAGAIN)\n");
- + return -EAGAIN;
- + }
- + current->timeout = 0;
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_tty_read: sleeping(read_wait)\n");
- +
- + interruptible_sleep_on (&ppp->read_wait);
- + if (current->signal & ~current->blocked)
- + return -EINTR;
- + continue;
- + }
- +/*
- + * Reset the time of the last read operation.
- + */
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG "ppp_tty_read: len = %d\n", len);
- +/*
- + * Ensure that the frame will fit within the caller's buffer. If not, then
- + * discard the frame from the input buffer and return an error to the caller.
- + */
- + if (len + 2 > nr) {
- + /* Can't copy it, update us_rbuff_head */
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp: read of %u bytes too small for %d "
- + "frame\n", nr, len + 2);
- + ppp->ubuf->tail += len;
- + ppp->ubuf->tail &= ppp->ubuf->size;
- + clear_bit (0, &ppp->ubuf->locked);
- + ppp->stats.ppp_ierrors++;
- + return -EOVERFLOW;
- + }
- +/*
- + * Before we attempt to write the frame to the user, ensure that the
- + * page tables are proper.
- + */
- + indx = verify_area (VERIFY_WRITE, buf, len + 2);
- + if (indx != 0) {
- + ppp->ubuf->tail += len;
- + ppp->ubuf->tail &= ppp->ubuf->size;
- + clear_bit (0, &ppp->ubuf->locked);
- + return (indx);
- + }
- +/*
- + * Fake the insertion of the ADDRESS and CONTROL information because these
- + * were not saved in the buffer.
- + */
- + put_byte_user (PPP_ALLSTATIONS, buf++);
- + put_byte_user (PPP_UI, buf++);
- +
- + indx = len;
- +/*
- + * Copy the received data from the buffer to the caller's area.
- + */
- + while (indx-- > 0) {
- + GETC (c);
- + put_byte_user (c, buf);
- + ++buf;
- + }
- +/*
- + * Release the lock and return the character count in the buffer area.
- + */
- + clear_bit (0, &ppp->ubuf->locked);
- + len += 2; /* Account for ADDRESS and CONTROL bytes */
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_tty_read: passing %d bytes up\n", len);
- + return len;
- + }
- +#undef GETC
- +}
- +
- +/* stuff a character into the transmit buffer, using PPP's way of escaping
- + special characters.
- + also, update fcs to take account of new character */
- +
- +extern inline void
- +ppp_stuff_char (struct ppp *ppp, register struct ppp_buffer *buf,
- + register u_char chr)
- +{
- +/*
- + * The buffer should not be full.
- + */
- + if (ppp->flags & SC_DEBUG) {
- + if ((buf->count < 0) || (buf->count > 3000))
- + printk (KERN_DEBUG "ppp_stuff_char: %x %d\n",
- + (unsigned int) buf->count,
- + (unsigned int) chr);
- + }
- +/*
- + * Update the FCS and if the character needs to be escaped, do it.
- + */
- + buf->fcs = PPP_FCS (buf->fcs, chr);
- + if (in_xmap (ppp, chr)) {
- + chr ^= PPP_TRANS;
- + ins_char (buf, PPP_ESCAPE);
- + }
- +/*
- + * Add the character to the buffer.
- + */
- + ins_char (buf, chr);
- +}
- +
- +/*
- + * Procedure to encode the data with the proper escapement and send the
- + * data to the remote system.
- + */
- +
- +static void
- +ppp_dev_xmit_lower (struct ppp *ppp, struct ppp_buffer *buf,
- + u_char *data, int count, int non_ip)
- +{
- + unsigned short int write_fcs;
- + int address, control;
- + int proto;
- +/*
- + * Insert the leading FLAG character
- + */
- + buf->count = 0;
- +
- + if (non_ip || flag_time == 0)
- + ins_char (buf, PPP_FLAG);
- + else {
- + if (jiffies - ppp->last_xmit > flag_time)
- + ins_char (buf, PPP_FLAG);
- + }
- + ppp->last_xmit = jiffies;
- + buf->fcs = PPP_INITFCS;
- +/*
- + * Emit the address/control information if needed
- + */
- + address = PPP_ADDRESS (data);
- + control = PPP_CONTROL (data);
- + proto = PPP_PROTOCOL (data);
- +
- + if (address != PPP_ALLSTATIONS ||
- + control != PPP_UI ||
- + (ppp->flags & SC_COMP_AC) == 0) {
- + ppp_stuff_char (ppp, buf, address);
- + ppp_stuff_char (ppp, buf, control);
- + }
- +/*
- + * Emit the protocol (compressed if possible)
- + */
- + if ((ppp->flags & SC_COMP_PROT) == 0 || (proto & 0xFF00))
- + ppp_stuff_char (ppp, buf, proto >> 8);
- +
- + ppp_stuff_char (ppp, buf, proto);
- +/*
- + * Insert the data
- + */
- + data += 4;
- + count -= 4;
- +
- + while (count-- > 0)
- + ppp_stuff_char (ppp, buf, *data++);
- +/*
- + * Add the trailing CRC and the final flag character
- + */
- + write_fcs = buf->fcs ^ 0xFFFF;
- + ppp_stuff_char (ppp, buf, write_fcs);
- + ppp_stuff_char (ppp, buf, write_fcs >> 8);
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG "ppp_dev_xmit_lower: fcs is %hx\n",
- + write_fcs);
- +/*
- + * Add the trailing flag character
- + */
- + ins_char (buf, PPP_FLAG);
- +/*
- + * Print the buffer
- + */
- + if (ppp->flags & SC_LOG_FLUSH)
- + ppp_print_buffer ("ppp flush", buf_base (buf),
- + buf->count);
- + else {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_dev_xmit: writing %d chars\n",
- + buf->count);
- + }
- +/*
- + * Send the block to the tty driver.
- + */
- + ppp->stats.ppp_obytes += buf->count;
- + ppp_kick_tty (ppp, buf);
- +}
- +
- +/*
- + * Send an frame to the remote with the proper bsd compression.
- + *
- + * Return 0 if frame was queued for transmission.
- + * 1 if frame must be re-queued for later driver support.
- + */
- +
- +static int
- +ppp_dev_xmit_frame (struct ppp *ppp, struct ppp_buffer *buf,
- + u_char *data, int count)
- +{
- + int proto;
- + int address, control;
- + u_char *new_data;
- + int new_count;
- +/*
- + * Print the buffer
- + */
- + if (ppp->flags & SC_LOG_OUTPKT)
- + ppp_print_buffer ("write frame", data, count);
- +/*
- + * Determine if the frame may be compressed. Attempt to compress the
- + * frame if possible.
- + */
- + proto = PPP_PROTOCOL (data);
- + address = PPP_ADDRESS (data);
- + control = PPP_CONTROL (data);
- +
- + if (((ppp->flags & SC_COMP_RUN) != 0) &&
- + (ppp->sc_xc_state != (void *) 0) &&
- + (address == PPP_ALLSTATIONS) &&
- + (control == PPP_UI) &&
- + (proto != PPP_LCP) &&
- + (proto != PPP_CCP)) {
- + new_data = kmalloc (count, GFP_ATOMIC);
- + if (new_data == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_dev_xmit_frame: no memory\n");
- + return 1;
- + }
- +
- + new_count = bsd_compress (ppp->sc_xc_state,
- + data,
- + new_data,
- + count,
- + count);
- +
- + if (new_count > 0) {
- + ++ppp->stats.ppp_opackets;
- + ppp->stats.ppp_ooctects += new_count;
- +
- + ppp_dev_xmit_lower (ppp, buf, new_data,
- + new_count, 0);
- + kfree (new_data);
- + return 0;
- + }
- +/*
- + * The frame could not be compressed.
- + */
- + kfree (new_data);
- + }
- +/*
- + * The frame may not be compressed. Update the statistics before the
- + * count field is destroyed. The frame will be transmitted.
- + */
- + ++ppp->stats.ppp_opackets;
- + ppp->stats.ppp_ooctects += count;
- +/*
- + * Go to the escape encoding
- + */
- + ppp_dev_xmit_lower (ppp, buf, data, count, !!(proto & 0xFF00));
- + return 0;
- +}
- +
- +/*
- + * Revise the tty frame for specific protocols.
- + */
- +
- +static int
- +send_revise_frame (register struct ppp *ppp, u_char *data, int len)
- +{
- + u_char *p;
- +
- + switch (PPP_PROTOCOL (data)) {
- +/*
- + * Update the LQR frame with the current MIB information. This saves having
- + * the daemon read old MIB data from the driver.
- + */
- + case PPP_LQR:
- + len = 48; /* total size of this frame */
- + p = (u_char *) &data [40]; /* Point to last two items. */
- + p = store_long (p, ppp->stats.ppp_opackets + 1);
- + p = store_long (p, ppp->stats.ppp_ooctects + len);
- + break;
- +/*
- + * Outbound compression frames
- + */
- + case PPP_CCP:
- + ppp_proto_ccp (ppp,
- + data + PPP_HARD_HDR_LEN,
- + len - PPP_HARD_HDR_LEN,
- + 0);
- + break;
- +/*
- + * All other frame types
- + */
- + default:
- + break;
- + }
- +
- + return len;
- +}
- +
- +/*
- + * write a frame with NR chars from BUF to TTY
- + * we have to put the FCS field on ourselves
- + */
- +
- +static int
- +ppp_tty_write (struct tty_struct *tty, struct file *file, const u_char * data,
- + unsigned int count)
- +{
- + struct ppp *ppp = tty2ppp (tty);
- + u_char *new_data;
- + int status;
- +/*
- + * Verify the pointer to the PPP data and that the tty is still in PPP mode.
- + */
- + if (!ppp)
- + return -EIO;
- +
- + if (ppp->magic != PPP_MAGIC)
- + return -EIO;
- +
- + CHECK_PPP (-ENXIO);
- +/*
- + * Ensure that the caller does not wish to send too much.
- + */
- + if (count > PPP_MTU) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING
- + "ppp_tty_write: truncating user packet "
- + "from %u to mtu %d\n", count, PPP_MTU);
- + count = PPP_MTU;
- + }
- +/*
- + * Allocate a buffer for the data and fetch it from the user space.
- + */
- + new_data = kmalloc (count, GFP_KERNEL);
- + if (new_data == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_tty_write: no memory\n");
- + return 0;
- + }
- +/*
- + * lock this PPP unit so we will be the only writer;
- + * sleep if necessary
- + */
- + while (lock_buffer (ppp->tbuf) != 0) {
- + current->timeout = 0;
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG "ppp_tty_write: sleeping\n");
- + interruptible_sleep_on (&ppp->write_wait);
- +
- + ppp = tty2ppp (tty);
- + if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse) {
- + kfree (new_data);
- + return 0;
- + }
- +
- + if (current->signal & ~current->blocked) {
- + kfree (new_data);
- + return -EINTR;
- + }
- + }
- +/*
- + * Ensure that the caller's buffer is valid.
- + */
- + status = verify_area (VERIFY_READ, data, count);
- + if (status != 0) {
- + kfree (new_data);
- + ppp->tbuf->locked = 0;
- + return status;
- + }
- +
- + memcpy_fromfs (new_data, data, count);
- +/*
- + * Change the LQR frame
- + */
- + count = send_revise_frame (ppp, new_data, count);
- +/*
- + * Send the data
- + */
- + ppp_dev_xmit_frame (ppp, ppp->tbuf, new_data, count);
- + kfree (new_data);
- + return (int) count;
- +}
- +
- +/*
- + * Process the BSD compression IOCTL event for the tty device.
- + */
- +
- +static int
- +ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
- +{
- + struct compressor *cp;
- + struct ppp_option_data data;
- + int error;
- + int nb;
- + u_char *ptr;
- + u_char ccp_option[CCP_MAX_OPTION_LENGTH];
- +/*
- + * Fetch the compression parameters
- + */
- + error = verify_area (VERIFY_READ, odp, sizeof (data));
- + if (error == 0) {
- + memcpy_fromfs (&data, odp, sizeof (data));
- + nb = data.length;
- + ptr = data.ptr;
- + if ((unsigned long) nb >= (unsigned long)CCP_MAX_OPTION_LENGTH)
- + nb = CCP_MAX_OPTION_LENGTH;
- +
- + error = verify_area (VERIFY_READ, ptr, nb);
- + }
- +
- + if (error != 0)
- + return error;
- +
- + memcpy_fromfs (ccp_option, ptr, nb);
- +
- + if (ccp_option[1] < 2) /* preliminary check on the length byte */
- + return (-EINVAL);
- +
- + cp = find_compressor ((int) (unsigned) (unsigned char) ccp_option[0]);
- + if (cp != (struct compressor *) 0) {
- + /*
- + * Found a handler for the protocol - try to allocate
- + * a compressor or decompressor.
- + */
- + error = 0;
- + if (data.transmit) {
- + if (ppp->sc_xc_state != NULL)
- + (*ppp->sc_xcomp->comp_free)(ppp->sc_xc_state);
- +
- + ppp->sc_xcomp = cp;
- + ppp->sc_xc_state = cp->comp_alloc(ccp_option, nb);
- +
- + if (ppp->sc_xc_state == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk("ppp%ld: comp_alloc failed\n",
- + ppp2dev (ppp)->base_addr);
- + error = -ENOBUFS;
- + }
- + ppp->flags &= ~SC_COMP_RUN;
- + } else {
- + if (ppp->sc_rc_state != NULL)
- + (*ppp->sc_rcomp->decomp_free)(ppp->sc_rc_state);
- + ppp->sc_rcomp = cp;
- + ppp->sc_rc_state = cp->decomp_alloc(ccp_option, nb);
- + if (ppp->sc_rc_state == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk("ppp%ld: decomp_alloc failed\n",
- + ppp2dev (ppp)->base_addr);
- + error = ENOBUFS;
- + }
- + ppp->flags &= ~SC_DECOMP_RUN;
- + }
- + return (error);
- + }
- +
- + if (ppp->flags & SC_DEBUG)
- + printk(KERN_DEBUG "ppp%ld: no compressor for [%x %x %x], %x\n",
- + ppp2dev (ppp)->base_addr, ccp_option[0], ccp_option[1],
- + ccp_option[2], nb);
- + return (-EINVAL); /* no handler found */
- +}
- +
- +/*
- + * Process the IOCTL event for the tty device.
- + */
- +
- +static int
- +ppp_tty_ioctl (struct tty_struct *tty, struct file *file, unsigned int param2,
- + unsigned long param3)
- +{
- + struct ppp *ppp = tty2ppp (tty);
- + register int temp_i = 0;
- + int error;
- +/*
- + * Verify the status of the PPP device.
- + */
- + if (!ppp)
- + return -EBADF;
- +
- + if (ppp->magic != PPP_MAGIC)
- + return -EBADF;
- +
- + CHECK_PPP (-ENXIO);
- +/*
- + * The user must have an euid of root to do these requests.
- + */
- + if (!suser ())
- + return -EPERM;
- +/*
- + * Set the MRU value
- + */
- + switch (param2) {
- + case PPPIOCSMRU:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + temp_i = get_int_user ((int *) param3);
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: set mru to %x\n", temp_i);
- +
- + if (ppp->mru != temp_i)
- + ppp_changedmtu (ppp, ppp2dev (ppp)->mtu, temp_i);
- + }
- + break;
- +/*
- + * Fetch the flags
- + */
- + case PPPIOCGFLAGS:
- + error = verify_area (VERIFY_WRITE, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + temp_i = (ppp->flags & SC_MASK);
- +#ifndef CHECK_CHARACTERS /* Don't generate errors if we don't check chars. */
- + temp_i |= SC_RCV_B7_1 | SC_RCV_B7_0 |
- + SC_RCV_ODDP | SC_RCV_EVNP;
- +#endif
- + put_long_user ((long) temp_i, param3);
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_tty_ioctl: get flags: addr %lx flags "
- + "%x\n", param3, temp_i);
- + }
- + break;
- +/*
- + * Set the flags for the various options
- + */
- + case PPPIOCSFLAGS:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + temp_i = get_int_user (param3) & SC_MASK;
- + temp_i |= (ppp->flags & ~SC_MASK);
- +
- + if ((ppp->flags & SC_CCP_OPEN) &&
- + (temp_i & SC_CCP_OPEN) == 0)
- + ppp_ccp_closed (ppp);
- +
- + if ((ppp->flags | temp_i) & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: set flags to %x\n", temp_i);
- + ppp->flags = temp_i;
- + }
- + break;
- +/*
- + * Set the compression mode
- + */
- + case PPPIOCSCOMPRESS:
- + error = ppp_set_compression (ppp,
- + (struct ppp_option_data *) param3);
- + break;
- +/*
- + * Retrieve the transmit async map
- + */
- + case PPPIOCGASYNCMAP:
- + error = verify_area (VERIFY_WRITE, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + put_long_user (ppp->xmit_async_map[0], param3);
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: get asyncmap: addr "
- + "%lx asyncmap %lx\n",
- + param3,
- + (unsigned long) ppp->xmit_async_map[0]);
- + }
- + break;
- +/*
- + * Set the transmit async map
- + */
- + case PPPIOCSASYNCMAP:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + ppp->xmit_async_map[0] = get_long_user (param3);
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: set xmit asyncmap %lx\n",
- + (unsigned long) ppp->xmit_async_map[0]);
- + }
- + break;
- +/*
- + * Set the receive async map
- + */
- + case PPPIOCSRASYNCMAP:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + ppp->recv_async_map = get_long_user (param3);
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: set rcv asyncmap %lx\n",
- + (unsigned long) ppp->recv_async_map);
- + }
- + break;
- +/*
- + * Obtain the unit number for this device.
- + */
- + case PPPIOCGUNIT:
- + error = verify_area (VERIFY_WRITE, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + put_long_user (ppp2dev (ppp)->base_addr, param3);
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: get unit: %ld",
- + ppp2dev (ppp)->base_addr);
- + }
- + break;
- +/*
- + * Set the debug level
- + */
- + case PPPIOCSDEBUG:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + temp_i = (get_int_user (param3) & 0x1F) << 16;
- + temp_i |= (ppp->flags & ~0x1F0000);
- +
- + if ((ppp->flags | temp_i) & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: set flags to %x\n", temp_i);
- + ppp->flags = temp_i;
- + }
- + break;
- +/*
- + * Get the debug level
- + */
- + case PPPIOCGDEBUG:
- + error = verify_area (VERIFY_WRITE, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + temp_i = (ppp->flags >> 16) & 0x1F;
- + put_long_user ((long) temp_i, param3);
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: get debug level %d\n",
- + temp_i);
- + }
- + break;
- +/*
- + * Get the times since the last send/receive frame operation
- + */
- + case PPPIOCGIDLE:
- + error = verify_area (VERIFY_WRITE, (void *) param3,
- + sizeof (struct ppp_idle));
- + if (error == 0) {
- + struct ppp_idle cur_ddinfo;
- + unsigned long cur_jiffies = jiffies;
- +
- + /* change absolute times to relative times. */
- + cur_ddinfo.xmit_idle = (cur_jiffies - ppp->ddinfo.xmit_idle) / HZ;
- + cur_ddinfo.recv_idle = (cur_jiffies - ppp->ddinfo.recv_idle) / HZ;
- + memcpy_tofs ((void *) param3, &cur_ddinfo,
- + sizeof (cur_ddinfo));
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: read demand dial info\n");
- + }
- + break;
- +/*
- + * Retrieve the extended async map
- + */
- + case PPPIOCGXASYNCMAP:
- + error = verify_area (VERIFY_WRITE,
- + (void *) param3,
- + sizeof (ppp->xmit_async_map));
- + if (error == 0) {
- + memcpy_tofs ((void *) param3,
- + ppp->xmit_async_map,
- + sizeof (ppp->xmit_async_map));
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: get xasyncmap: addr %lx\n",
- + param3);
- + }
- + break;
- +/*
- + * Set the async extended map
- + */
- + case PPPIOCSXASYNCMAP:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (ppp->xmit_async_map));
- + if (error == 0) {
- + __u32 temp_tbl[8];
- +
- + memcpy_fromfs (temp_tbl, (void *) param3,
- + sizeof (ppp->xmit_async_map));
- + temp_tbl[1] = 0x00000000;
- + temp_tbl[2] &= ~0x40000000;
- + temp_tbl[3] |= 0x60000000;
- +
- + if ((temp_tbl[2] & temp_tbl[3]) != 0 ||
- + (temp_tbl[4] & temp_tbl[5]) != 0 ||
- + (temp_tbl[6] & temp_tbl[7]) != 0)
- + error = -EINVAL;
- + else {
- + memcpy (ppp->xmit_async_map, temp_tbl,
- + sizeof (ppp->xmit_async_map));
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: set xasyncmap\n");
- + }
- + }
- + break;
- +/*
- + * Set the maximum VJ header compression slot number.
- + */
- + case PPPIOCSMAXCID:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + temp_i = get_int_user (param3) + 1;
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: set maxcid to %d\n",
- + temp_i);
- + if (ppp->slcomp != NULL)
- + slhc_free (ppp->slcomp);
- + ppp->slcomp = slhc_init (16, temp_i);
- +
- + if (ppp->slcomp == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp: no space for compression buffers!\n");
- + ppp_release (ppp);
- + error = -ENOMEM;
- + }
- + }
- + break;
- +/*
- + * Allow users to read, but not set, the serial port parameters
- + */
- + case TCGETS:
- + case TCGETA:
- + error = n_tty_ioctl (tty, file, param2, param3);
- + break;
- +/*
- + * All other ioctl() events will come here.
- + */
- + default:
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_tty_ioctl: invalid ioctl: %x, addr %lx\n",
- + param2,
- + param3);
- +
- + error = -ENOIOCTLCMD;
- + break;
- + }
- + return error;
- +}
- +
- +/*
- + * TTY callback.
- + *
- + * Process the select() statement for the PPP device.
- + */
- +
- +static int
- +ppp_tty_select (struct tty_struct *tty, struct inode *inode,
- + struct file *filp, int sel_type, select_table * wait)
- +{
- + struct ppp *ppp = tty2ppp (tty);
- + int result = 1;
- +/*
- + * Verify the status of the PPP device.
- + */
- + if (!ppp)
- + return -EBADF;
- +
- + if (ppp->magic != PPP_MAGIC)
- + return -EBADF;
- +
- + CHECK_PPP (0);
- +/*
- + * Branch on the type of select mode. A read request must lock the user
- + * buffer area.
- + */
- + switch (sel_type) {
- + case SEL_IN:
- + if (set_bit (0, &ppp->ubuf->locked) == 0) {
- + /* Test for the presence of data in the queue */
- + if (ppp->ubuf->head != ppp->ubuf->tail) {
- + clear_bit (0, &ppp->ubuf->locked);
- + break;
- + }
- + clear_bit (0, &ppp->ubuf->locked);
- + } /* fall through */
- + /*
- + * Exceptions or read errors.
- + */
- + case SEL_EX:
- + /* Is this a pty link and the remote disconnected? */
- + if (tty->flags & (1 << TTY_SLAVE_CLOSED))
- + break;
- +
- + /* Is this a local link and the modem disconnected? */
- + if (tty_hung_up_p (filp))
- + break;
- +
- + select_wait (&ppp->read_wait, wait);
- + result = 0;
- + break;
- +/*
- + * Write mode. A write is allowed if there is no current transmission.
- + */
- + case SEL_OUT:
- + if (ppp->tbuf->locked != 0) {
- + select_wait (&ppp->write_wait, wait);
- + result = 0;
- + }
- + break;
- + }
- + return result;
- +}
- +
- +/*************************************************************
- + * NETWORK OUTPUT
- + * This routine accepts requests from the network layer
- + * and attempts to deliver the packets.
- + * It also includes various routines we are compelled to
- + * have to make the network layer work (arp, etc...).
- + *************************************************************/
- +
- +/*
- + * Callback from the network layer when the device goes up.
- + */
- +
- +static int
- +ppp_dev_open (struct device *dev)
- +{
- + struct ppp *ppp = dev2ppp (dev);
- +
- + /* reset POINTOPOINT every time, since dev_close zaps it! */
- + dev->flags |= IFF_POINTOPOINT;
- +
- + if (ppp2tty (ppp) == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp: %s not connected to a TTY! can't go open!\n",
- + dev->name);
- + return -ENXIO;
- + }
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp: channel %s going up for IP packets!\n",
- + dev->name);
- +
- + CHECK_PPP (-ENXIO);
- + return 0;
- +}
- +
- +/*
- + * Callback from the network layer when the ppp device goes down.
- + */
- +
- +static int
- +ppp_dev_close (struct device *dev)
- +{
- + struct ppp *ppp = dev2ppp (dev);
- +
- + if (ppp2tty (ppp) == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp: %s not connected to a TTY! can't go down!\n",
- + dev->name);
- + return -ENXIO;
- + }
- +/*
- + * We don't do anything about the device going down. It is not important
- + * for us.
- + */
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp: channel %s going down for IP packets!\n",
- + dev->name);
- + CHECK_PPP (-ENXIO);
- + return 0;
- +}
- +
- +/*
- + * IOCTL operation to read the version of the driver.
- + */
- +
- +static int
- +ppp_dev_ioctl_version (struct ppp *ppp, struct ifreq *ifr)
- +{
- + int error;
- + int len;
- + char *result;
- +/*
- + * Must have write access to the buffer.
- + */
- + result = (char *) ifr->ifr_ifru.ifru_data;
- + len = strlen (szVersion) + 1;
- + error = verify_area (VERIFY_WRITE, result, len);
- +/*
- + * Move the version data
- + */
- + if (error == 0)
- + memcpy_tofs (result, szVersion, len);
- +
- + return error;
- +}
- +
- +/*
- + * IOCTL to read the statistics for the pppstats program.
- + */
- +
- +static int
- +ppp_dev_ioctl_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev)
- +{
- + struct ppp_stats *result, temp;
- + int error;
- +/*
- + * Must have write access to the buffer.
- + */
- + result = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
- + error = verify_area (VERIFY_WRITE,
- + result,
- + sizeof (temp));
- +/*
- + * Supply the information for the caller. First move the version data
- + * then move the ppp stats; and finally the vj stats.
- + */
- + memset (&temp, 0, sizeof(temp));
- + if (error == 0 && dev->flags & IFF_UP) {
- + memcpy (&temp.p, &ppp->stats, sizeof (struct pppstat));
- + if (ppp->slcomp != NULL) {
- + temp.vj.vjs_packets = ppp->slcomp->sls_o_compressed+
- + ppp->slcomp->sls_o_uncompressed;
- + temp.vj.vjs_compressed = ppp->slcomp->sls_o_compressed;
- + temp.vj.vjs_searches = ppp->slcomp->sls_o_searches;
- + temp.vj.vjs_misses = ppp->slcomp->sls_o_misses;
- + temp.vj.vjs_errorin = ppp->slcomp->sls_i_error;
- + temp.vj.vjs_tossed = ppp->slcomp->sls_i_tossed;
- + temp.vj.vjs_uncompressedin = ppp->slcomp->sls_i_uncompressed;
- + temp.vj.vjs_compressedin = ppp->slcomp->sls_i_compressed;
- + }
- + }
- +/*
- + * Move the data to the caller's buffer
- + */
- + if (error == 0)
- + memcpy_tofs (result, &temp, sizeof (temp));
- + return error;
- +}
- +
- +/*
- + * IOCTL to read the compression statistics for the pppstats program.
- + */
- +
- +static int
- +ppp_dev_ioctl_comp_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev)
- +{
- + struct ppp_comp_stats *result, temp;
- + int error;
- +/*
- + * Must have write access to the buffer.
- + */
- + result = (struct ppp_comp_stats *) ifr->ifr_ifru.ifru_data;
- + error = verify_area (VERIFY_WRITE,
- + result,
- + sizeof (temp));
- +/*
- + * Supply the information for the caller.
- + */
- + memset (&temp, 0, sizeof(temp));
- + if (error == 0 && dev->flags & IFF_UP) {
- + if (ppp->sc_xc_state != NULL)
- + (*ppp->sc_xcomp->comp_stat) (ppp->sc_xc_state,
- + &temp.c);
- +
- + if (ppp->sc_rc_state != NULL)
- + (*ppp->sc_rcomp->decomp_stat) (ppp->sc_rc_state,
- + &temp.d);
- + }
- +/*
- + * Move the data to the caller's buffer
- + */
- + if (error == 0)
- + memcpy_tofs (result, &temp, sizeof (temp));
- + return error;
- +}
- +
- +/*
- + * Callback from the network layer to process the sockioctl functions.
- + */
- +
- +static int
- +ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd)
- +{
- + struct ppp *ppp = dev2ppp (dev);
- + int error;
- +/*
- + * Process the requests
- + */
- + switch (cmd) {
- + case SIOCGPPPSTATS:
- + error = ppp_dev_ioctl_stats (ppp, ifr, dev);
- + break;
- +
- + case SIOCGPPPCSTATS:
- + error = ppp_dev_ioctl_comp_stats (ppp, ifr, dev);
- + break;
- +
- + case SIOCGPPPVER:
- + error = ppp_dev_ioctl_version (ppp, ifr);
- + break;
- +
- + default:
- + error = -EINVAL;
- + break;
- + }
- + return error;
- +}
- +
- +/*
- + * Send an IP frame to the remote with vj header compression.
- + *
- + * Return 0 if frame was queued for transmission.
- + * 1 if frame must be re-queued for later driver support.
- + */
- +
- +static int
- +ppp_dev_xmit_ip1 (struct device *dev, struct ppp *ppp, u_char *data)
- +{
- + int proto = PPP_IP;
- + int len;
- + struct ppp_hdr *hdr;
- + struct tty_struct *tty = ppp2tty (ppp);
- +/*
- + * Obtain the length from the IP header.
- + */
- + len = ((struct iphdr *)data) -> tot_len;
- + len = ntohs (len);
- +/*
- + * Validate the tty interface
- + */
- + if (tty == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_dev_xmit: %s not connected to a TTY!\n",
- + dev->name);
- + return 0;
- + }
- +/*
- + * Ensure that the PPP device is still up
- + */
- + if (!(dev->flags & IFF_UP)) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING
- + "ppp_dev_xmit: packet sent on interface %s,"
- + " which is down for IP\n",
- + dev->name);
- + return 0;
- + }
- +/*
- + * Detect a change in the transfer size
- + */
- + if (ppp->mtu != ppp2dev (ppp)->mtu) {
- + ppp_changedmtu (ppp,
- + ppp2dev (ppp)->mtu,
- + ppp->mru);
- + }
- +/*
- + * Acquire the lock on the transmission buffer. If the buffer was busy then
- + * mark the device as busy and return "failure to send, try back later" error.
- + */
- + if (lock_buffer (ppp->wbuf) != 0) {
- + dev->tbusy = 1;
- + return 1;
- + }
- +/*
- + * Print the frame being sent
- + */
- + if (ppp->flags & SC_LOG_OUTPKT)
- + ppp_print_buffer ("ppp outpkt", data, len);
- +/*
- + * At this point, the buffer will be transmitted. There is no other exit.
- + *
- + * Try to compress the header.
- + */
- + if (ppp->flags & SC_COMP_TCP) {
- + len = slhc_compress (ppp->slcomp, data, len,
- + buf_base (ppp->cbuf) + PPP_HARD_HDR_LEN,
- + &data,
- + (ppp->flags & SC_NO_TCP_CCID) == 0);
- +
- + if (data[0] & SL_TYPE_COMPRESSED_TCP) {
- + proto = PPP_VJC_COMP;
- + data[0] ^= SL_TYPE_COMPRESSED_TCP;
- + } else {
- + if (data[0] >= SL_TYPE_UNCOMPRESSED_TCP)
- + proto = PPP_VJC_UNCOMP;
- + data[0] = (data[0] & 0x0f) | 0x40;
- + }
- + }
- +/*
- + * Send the frame
- + */
- + len += PPP_HARD_HDR_LEN;
- + hdr = &((struct ppp_hdr *) data)[-1];
- +
- + hdr->address = PPP_ALLSTATIONS;
- + hdr->control = PPP_UI;
- + hdr->protocol[0] = 0;
- + hdr->protocol[1] = proto;
- +
- + return ppp_dev_xmit_frame (ppp, ppp->wbuf, (u_char *) hdr, len);
- +}
- +
- +/*
- + * This is just an interum solution until the 1.3 kernel's networking is
- + * available. The 1.2 kernel has problems with device headers before the
- + * buffers.
- + *
- + * This routine should be deleted, and the ppp_dev_xmit_ip1 routine called
- + * by this name.
- + */
- +
- +static int
- +ppp_dev_xmit_ip (struct device *dev, struct ppp *ppp, u_char *data)
- +{
- + struct ppp_hdr *hdr;
- + int len;
- + int answer;
- +
- + len = ((struct iphdr *)data) -> tot_len;
- + len = ntohs (len);
- +
- + hdr = (struct ppp_hdr *) kmalloc (len + sizeof (struct ppp_hdr),
- + GFP_ATOMIC);
- +
- + if (hdr == NULL)
- + answer = 1;
- + else {
- + memcpy (&hdr[1], data, len);
- + answer = ppp_dev_xmit_ip1 (dev, ppp, (u_char *) &hdr[1]);
- + kfree (hdr);
- + }
- +
- + return answer;
- +}
- +
- +/*
- + * Send an IPX (or any other non-IP) frame to the remote.
- + *
- + * Return 0 if frame was queued for transmission.
- + * 1 if frame must be re-queued for later driver support.
- + */
- +
- +#ifdef NEW_SKBUFF
- +static int
- +ppp_dev_xmit_ipx1 (struct device *dev, struct ppp *ppp,
- + u_char *data, int len, int proto)
- +{
- + struct tty_struct *tty = ppp2tty (ppp);
- + struct ppp_hdr *hdr;
- +/*
- + * Validate the tty interface
- + */
- + if (tty == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_dev_xmit: %s not connected to a TTY!\n",
- + dev->name);
- + return 0;
- + }
- +/*
- + * Ensure that the PPP device is still up
- + */
- + if (!(dev->flags & IFF_UP)) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING
- + "ppp_dev_xmit: packet sent on interface %s,"
- + " which is down\n",
- + dev->name);
- + return 0;
- + }
- +/*
- + * Detect a change in the transfer size
- + */
- + if (ppp->mtu != ppp2dev (ppp)->mtu) {
- + ppp_changedmtu (ppp,
- + ppp2dev (ppp)->mtu,
- + ppp->mru);
- + }
- +/*
- + * Acquire the lock on the transmission buffer. If the buffer was busy then
- + * mark the device as busy and return "failure to send, try back later" error.
- + */
- + if (lock_buffer (ppp->wbuf) != 0) {
- + dev->tbusy = 1;
- + return 1;
- + }
- +/*
- + * Print the frame being sent
- + */
- + if (ppp->flags & SC_LOG_OUTPKT)
- + ppp_print_buffer ("ppp outpkt", data, len);
- +/*
- + * Send the frame
- + */
- + len += PPP_HARD_HDR_LEN;
- + hdr = &((struct ppp_hdr *) data)[-1];
- +
- + hdr->address = PPP_ALLSTATIONS;
- + hdr->control = PPP_UI;
- + hdr->protocol[0] = proto >> 8;
- + hdr->protocol[1] = proto;
- +
- + return ppp_dev_xmit_frame (ppp, ppp->wbuf, (u_char *) hdr, len);
- +}
- +
- +/*
- + * This is just an interum solution until the 1.3 kernel's networking is
- + * available. The 1.2 kernel has problems with device headers before the
- + * buffers.
- + *
- + * This routine should be deleted, and the ppp_dev_xmit_ipx1 routine called
- + * by this name.
- + */
- +
- +static int
- +ppp_dev_xmit_ipx (struct device *dev, struct ppp *ppp,
- + u_char *data, int len, int proto)
- +{
- + struct ppp_hdr *hdr;
- + int answer;
- +
- + hdr = (struct ppp_hdr *) kmalloc (len + sizeof (struct ppp_hdr),
- + GFP_ATOMIC);
- + if (hdr == NULL)
- + answer = 1;
- + else {
- + memcpy (&hdr[1], data, len);
- + answer = ppp_dev_xmit_ipx1 (dev, ppp, (u_char *) &hdr[1],
- + len, proto);
- + kfree (hdr);
- + }
- +
- + return answer;
- +}
- +#endif
- +
- +/*
- + * Send a frame to the remote.
- + */
- +
- +static int
- +ppp_dev_xmit (sk_buff *skb, struct device *dev)
- +{
- + int answer, len;
- + u_char *data;
- + struct ppp *ppp = dev2ppp (dev);
- + struct tty_struct *tty = ppp2tty (ppp);
- +/*
- + * just a little sanity check.
- + */
- + if (skb == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING "ppp_dev_xmit: null packet!\n");
- + return 0;
- + }
- +/*
- + * Avoid timing problem should tty hangup while data is queued to be sent
- + */
- + if (!ppp->inuse) {
- + dev_kfree_skb (skb, FREE_WRITE);
- + dev_close (dev);
- + return 0;
- + }
- +/*
- + * Validate the tty linkage
- + */
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG "ppp_dev_xmit [%s]: skb %p\n",
- + dev->name, skb);
- +/*
- + * Validate the tty interface
- + */
- + if (tty == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_dev_xmit: %s not connected to a TTY!\n",
- + dev->name);
- + dev_kfree_skb (skb, FREE_WRITE);
- + return 0;
- + }
- +/*
- + * Fetch the pointer to the data
- + */
- + len = skb->len;
- + data = skb_data(skb);
- +/*
- + * Look at the protocol in the skb to determine the difference between
- + * an IP frame and an IPX frame.
- + */
- +
- +#ifdef NEW_SKBUFF
- + switch (skb->protocol) {
- + case htons (ETH_P_IPX):
- + answer = ppp_dev_xmit_ipx (dev, ppp, data, len, PPP_IPX);
- + break;
- +
- + case htons (ETH_P_IP):
- + answer = ppp_dev_xmit_ip (dev, ppp, data);
- + break;
- +
- + default: /* All others have no support at this time. */
- + dev_kfree_skb (skb, FREE_WRITE);
- + return 0;
- + }
- +#else
- + answer = ppp_dev_xmit_ip (dev, ppp, data);
- +#endif
- +
- +/*
- + * This is the end of the transmission. Release the buffer if it was sent.
- + */
- + if (answer == 0) {
- + dev_kfree_skb (skb, FREE_WRITE);
- + ppp->ddinfo.xmit_idle = jiffies;
- + }
- + return answer;
- +}
- +
- +/*
- + * Generate the statistic information for the /proc/net/dev listing.
- + */
- +
- +static struct enet_statistics *
- +ppp_dev_stats (struct device *dev)
- +{
- + struct ppp *ppp = dev2ppp (dev);
- + static struct enet_statistics ppp_stats;
- +
- + ppp_stats.rx_packets = ppp->stats.ppp_ipackets;
- + ppp_stats.rx_errors = ppp->stats.ppp_ierrors;
- + ppp_stats.rx_dropped = ppp->stats.ppp_ierrors;
- + ppp_stats.rx_fifo_errors = 0;
- + ppp_stats.rx_length_errors = 0;
- + ppp_stats.rx_over_errors = 0;
- + ppp_stats.rx_crc_errors = 0;
- + ppp_stats.rx_frame_errors = 0;
- + ppp_stats.tx_packets = ppp->stats.ppp_opackets;
- + ppp_stats.tx_errors = ppp->stats.ppp_oerrors;
- + ppp_stats.tx_dropped = 0;
- + ppp_stats.tx_fifo_errors = 0;
- + ppp_stats.collisions = 0;
- + ppp_stats.tx_carrier_errors = 0;
- + ppp_stats.tx_aborted_errors = 0;
- + ppp_stats.tx_window_errors = 0;
- + ppp_stats.tx_heartbeat_errors = 0;
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO "ppp_dev_stats called");
- + return &ppp_stats;
- +}
- +
- +#ifdef NEW_SKBUFF
- +/*
- + * The PPP protocol is currently pure IP (no IPX yet). This defines
- + * the protocol layer which is blank since the driver does all the
- + * cooking.
- + */
- +
- +static int ppp_dev_input (struct protocol *self, struct protocol *lower,
- + sk_buff *skb, void *saddr, void *daddr)
- +{
- + return protocol_pass_demultiplex(self, NULL, skb, NULL, NULL);
- +}
- +
- +static int ppp_dev_output (struct protocol *self, sk_buff *skb, int type,
- + int subid, void *saddr, void *daddr, void *opt)
- +{
- + if(skb->dev==NULL)
- + {
- + printk("ppp_dev_output: No device.\n");
- + kfree_skb(skb, FREE_WRITE);
- + return -1;
- + }
- + dev_queue_xmit(skb, skb->dev, skb->priority);
- + return 0;
- +}
- +
- +static int ppp_dev_getkey(int protocol, int subid, unsigned char *key)
- +{
- + switch (protocol)
- + {
- + case htons (ETH_P_IP):
- + case htons (ETH_P_IPX):
- + return 0;
- +
- + default:
- + break;
- + }
- +
- + return -EAFNOSUPPORT;
- +}
- +
- +#else
- +
- +#if USE_SKB_PROTOCOL == 0
- +/*
- + * Called to enquire about the type of the frame in the buffer. Return
- + * ETH_P_IP for an IP frame, ETH_P_IPX for an IPX frame.
- + */
- +
- +static unsigned short
- +ppp_dev_type (sk_buff *skb, struct device *dev)
- +{
- + return (htons (ETH_P_IP));
- +}
- +#endif
- +
- +#if USE_SKB_PROTOCOL == 0
- +static int ppp_dev_header (unsigned char *buff, struct device *dev,
- + unsigned short type, void *daddr, void *saddr,
- + unsigned len, struct sk_buff *skb)
- +#else
- +static int ppp_dev_header (sk_buff *skb, struct device *dev,
- + unsigned short type, void *daddr,
- + void *saddr, unsigned len)
- +#endif
- +{
- + return (0);
- +}
- +
- +static int
- +ppp_dev_rebuild (void *buff, struct device *dev, unsigned long raddr,
- + sk_buff *skb)
- +{
- + return (0);
- +}
- +#endif
- +
- +/*************************************************************
- + * UTILITIES
- + * Miscellany called by various functions above.
- + *************************************************************/
- +
- +/* allocate or create a PPP channel */
- +static struct ppp *
- +ppp_alloc (void)
- +{
- + int if_num;
- + int status;
- + ppp_ctrl_t *ctl;
- + struct device *dev;
- + struct ppp *ppp;
- +
- + /* try to find an free device */
- + ctl = ppp_list;
- + if_num = 0;
- +
- + while (ctl) {
- + ppp = ctl2ppp (ctl);
- + if (!set_bit(0, &ppp->inuse))
- + return (ppp);
- + ctl = ctl->next;
- + if (++if_num == max_dev)
- + return (NULL);
- + }
- +/*
- + * There are no available items. Allocate a device from the system pool
- + */
- + ctl = (ppp_ctrl_t *) kmalloc (sizeof(ppp_ctrl_t), GFP_KERNEL);
- + if (ctl) {
- + (void) memset(ctl, 0, sizeof(ppp_ctrl_t));
- + ppp = ctl2ppp (ctl);
- + dev = ctl2dev (ctl);
- +
- + /* initialize channel control data */
- + set_bit(0, &ppp->inuse);
- +
- + ppp->line = if_num;
- + ppp->tty = NULL;
- + ppp->dev = dev;
- +
- + dev->next = NULL;
- + dev->init = ppp_init_dev;
- + dev->name = ctl->name;
- + dev->base_addr = (unsigned long) if_num;
- + dev->priv = (void *) ppp;
- +
- + sprintf (dev->name, "ppp%d", if_num);
- +
- + /* link in the new channel */
- + ctl->next = ppp_list;
- + ppp_list = ctl;
- +
- +/* register device so that we can be ifconfig'd */
- +/* ppp_init_dev() will be called as a side-effect */
- +
- + status = register_netdev (dev);
- + if (status == 0) {
- + printk (KERN_INFO "registered device %s\n", dev->name);
- + return (ppp);
- + }
- +
- + printk (KERN_ERR
- + "ppp_alloc - register_netdev(%s) = %d failure.\n",
- + dev->name, status);
- + /* This one will forever be busy as it is not initialized */
- + }
- + return (NULL);
- +}
- +
- +/*
- + * Utility procedures to print a buffer in hex/ascii
- + */
- +
- +static void
- +ppp_print_hex (register u_char * out, const u_char * in, int count)
- +{
- + register u_char next_ch;
- + static char hex[] = "0123456789ABCDEF";
- +
- + while (count-- > 0) {
- + next_ch = *in++;
- + *out++ = hex[(next_ch >> 4) & 0x0F];
- + *out++ = hex[next_ch & 0x0F];
- + ++out;
- + }
- +}
- +
- +static void
- +ppp_print_char (register u_char * out, const u_char * in, int count)
- +{
- + register u_char next_ch;
- +
- + while (count-- > 0) {
- + next_ch = *in++;
- +
- + if (next_ch < 0x20 || next_ch > 0x7e)
- + *out++ = '.';
- + else {
- + *out++ = next_ch;
- + if (next_ch == '%') /* printk/syslogd has a bug !! */
- + *out++ = '%';
- + }
- + }
- + *out = '\0';
- +}
- +
- +static void
- +ppp_print_buffer (const u_char * name, const u_char * buf, int count)
- +{
- + u_char line[44];
- +
- + if (name != (u_char *) NULL)
- + printk (KERN_DEBUG "ppp: %s, count = %d\n", name, count);
- +
- + while (count > 8) {
- + memset (line, 32, 44);
- + ppp_print_hex (line, buf, 8);
- + ppp_print_char (&line[8 * 3], buf, 8);
- + printk (KERN_DEBUG "%s\n", line);
- + count -= 8;
- + buf += 8;
- + }
- +
- + if (count > 0) {
- + memset (line, 32, 44);
- + ppp_print_hex (line, buf, count);
- + ppp_print_char (&line[8 * 3], buf, count);
- + printk (KERN_DEBUG "%s\n", line);
- + }
- +}
- +
- +/*************************************************************
- + * Compressor module interface
- + *************************************************************/
- +
- +struct compressor_link {
- + struct compressor_link *next;
- + struct compressor *comp;
- +};
- +
- +static struct compressor_link *ppp_compressors = (struct compressor_link *) 0;
- +
- +static struct compressor *find_compressor (int type)
- +{
- + struct compressor_link *lnk;
- + unsigned long flags;
- +
- + save_flags(flags);
- + cli();
- +
- + lnk = ppp_compressors;
- + while (lnk != (struct compressor_link *) 0) {
- + if ((int) (unsigned char) lnk->comp->compress_proto == type) {
- + restore_flags(flags);
- + return lnk->comp;
- + }
- + lnk = lnk->next;
- + }
- +
- + restore_flags(flags);
- + return (struct compressor *) 0;
- +}
- +
- +static int ppp_register_compressor (struct compressor *cp)
- +{
- + struct compressor_link *new;
- + unsigned long flags;
- +
- + new = (struct compressor_link *) kmalloc (sizeof (struct compressor_link), GFP_KERNEL);
- +
- + if (new == (struct compressor_link *) 0)
- + return 1;
- +
- + save_flags(flags);
- + cli();
- +
- + if (find_compressor (cp->compress_proto)) {
- + restore_flags(flags);
- + kfree (new);
- + return 0;
- + }
- +
- + new->next = ppp_compressors;
- + new->comp = cp;
- + ppp_compressors = new;
- +
- + restore_flags(flags);
- + return 0;
- +}
- +
- +static void ppp_unregister_compressor (struct compressor *cp)
- +{
- + struct compressor_link *prev = (struct compressor_link *) 0;
- + struct compressor_link *lnk;
- + unsigned long flags;
- +
- + save_flags(flags);
- + cli();
- +
- + lnk = ppp_compressors;
- + while (lnk != (struct compressor_link *) 0) {
- + if (lnk->comp == cp) {
- + if (prev)
- + prev->next = lnk->next;
- + else
- + ppp_compressors = lnk->next;
- + kfree (lnk);
- + break;
- + }
- + prev = lnk;
- + lnk = lnk->next;
- + }
- + restore_flags(flags);
- +}
- +
- +/*************************************************************
- + * Module support routines
- + *************************************************************/
- +
- +#ifdef MODULE
- +
- +int
- +init_module(void)
- +{
- + int status;
- +
- + /* register our line disciplines */
- + status = ppp_first_time();
- + if (status != 0)
- + printk (KERN_INFO
- + "PPP: ppp_init() failure %d\n", status);
- + else
- + (void) register_symtab (&ppp_syms);
- + return (status);
- +}
- +
- +void
- +cleanup_module(void)
- +{
- + int status;
- + ppp_ctrl_t *ctl, *next_ctl;
- + struct device *dev;
- + struct ppp *ppp;
- + int busy_flag = 0;
- +/*
- + * Ensure that the devices are not in operation.
- + */
- + ctl = ppp_list;
- + while (ctl) {
- + ppp = ctl2ppp (ctl);
- + if (ppp->inuse && ppp->tty != NULL) {
- + busy_flag = 1;
- + break;
- + }
- +
- + dev = ctl2dev (ctl);
- + if (dev->start || dev->flags & IFF_UP) {
- + busy_flag = 1;
- + break;
- + }
- + ctl = ctl->next;
- + }
- +/*
- + * Ensure that there are no compressor modules registered
- + */
- + if (busy_flag == 0 && ppp_compressors != NULL)
- + busy_flag = 1;
- +
- + if (busy_flag) {
- + printk (KERN_INFO
- + "PPP: device busy, remove delayed\n");
- + return;
- + }
- +/*
- + * Release the tty registration of the line dicipline so that no new entries
- + * may be created.
- + */
- + status = tty_register_ldisc (N_PPP, NULL);
- + if (status != 0)
- + printk (KERN_INFO
- + "PPP: Unable to unregister ppp line discipline "
- + "(err = %d)\n", status);
- + else
- + printk (KERN_INFO
- + "PPP: ppp line discipline successfully unregistered\n");
- +/*
- + * De-register the devices so that there is no problem with them
- + */
- + next_ctl = ppp_list;
- + while (next_ctl) {
- + ctl = next_ctl;
- + next_ctl = ctl->next;
- + ppp = ctl2ppp (ctl);
- + dev = ctl2dev (ctl);
- +
- + ppp_release (ppp);
- + unregister_netdev (dev);
- + kfree (ctl);
- + }
- +}
- +#endif
- diff -urNwbB linux/arch/arm/drivers/scsi/Makefile linux.arm/arch/arm/drivers/scsi/Makefile
- --- linux/arch/arm/drivers/scsi/Makefile Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/scsi/Makefile Sat Feb 24 13:17:59 1996
- @@ -0,0 +1,147 @@
- +
- +# Makefile for kernel/blk_drv/scsi
- +#
- +# Note! Dependencies are done automagically by 'make dep', which also
- +# removes any old dependencies. DON'T put your own dependencies here
- +# unless it's something special (ie not a .c file).
- +#
- +
- +all: links first_rule
- +
- +L_TARGET := scsi.a
- +L_OBJS :=
- +M_OBJS :=
- +MOD_LIST_NAME := SCSI_MODULES
- +SCSI_SRCS := $(wildcard $(L_OBJS:%.o=%.c))
- +
- +ifeq (${CFLAGS},)
- +CFLAGS = -D__KERNEL__ \
- + -Wall -Wstrict-prototypes -I. -I../../../../include \
- + -O2 -fomit-frame-pointer
- +
- +include ../../../../.config
- +
- +TOPDIR = ../../../..
- +endif
- +
- +ifeq ($(CONFIG_SCSI),y)
- +L_OBJS += hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o
- +else
- + ifeq ($(CONFIG_SCSI),m)
- + ifdef CONFIG_MODVERSIONS
- + # Create this before we build anything else.
- + SCSI_MODULE_VER := scsi_syms.ver
- + SYMTAB_OBJS := scsi_syms.o
- + endif
- + M_OBJS += scsi_mod.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_CHR_DEV_ST),y)
- +L_OBJS += st.o
- +else
- + ifeq ($(CONFIG_CHR_DEV_ST),m)
- + M_OBJS += st.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_BLK_DEV_SD),y)
- +L_OBJS += sd.o sd_ioctl.o
- +else
- + ifeq ($(CONFIG_BLK_DEV_SD),m)
- + M_OBJS += sd_mod.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_BLK_DEV_SR),y)
- +L_OBJS += sr.o sr_ioctl.o
- +else
- + ifeq ($(CONFIG_BLK_DEV_SR),m)
- + M_OBJS += sr_mod.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_CHR_DEV_SG),y)
- +L_OBJS += sg.o
- +else
- + ifeq ($(CONFIG_CHR_DEV_SG),m)
- + M_OBJS += sg.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_SCSI_CUMANA_1),y)
- +L_OBJS += cumana_1.o
- +else
- + ifeq ($(CONFIG_SCSI_CUMANA_1),m)
- + M_OBJS += cumana_1.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_SCSI_ECOSCSI),y)
- +L_OBJS += ecoscsi.o
- +else
- + ifeq ($(CONFIG_SCSI_ECOSCSI),m)
- + M_OBJS += ecoscsi.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_SCSI_OAK1),y)
- +L_OBJS += oak.o
- +else
- + ifeq ($(CONFIG_SCSI_OAK1),m)
- + M_OBJS += oak_mod.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_SCSI_ACORNSCSI_3),y)
- +L_OBJS += acornscsi.o acornscsi-io.o
- +else
- + ifeq ($(CONFIG_SCSI_ACORNSCSI_3),m)
- + M_OBJS += acornscsi_mod.o
- + endif
- +endif
- +
- +include $(TOPDIR)/Rules.make
- +
- +ifeq ($(CONFIG_SCSI),m)
- + ifdef CONFIG_MODVERSIONS
- + include ../../../../versions.mk
- + endif
- +endif
- +
- +fastdep: links
- +
- +scsi_mod.o: $(SCSI_MODULE_VER) hosts.o scsi.o scsi_ioctl.o constants.o \
- + scsicam.o scsi_proc.o $(SYMTAB_OBJS)
- + $(LD) $(LD_RFLAG) -r -o $@ hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o $(SYMTAB_OBJS)
- +
- +sr_mod.o: $(SCSI_MODULE_VER) sr.o sr_ioctl.o
- + $(LD) $(LD_RFLAG) -r -o $@ sr.o sr_ioctl.o
- +
- +sd_mod.o: $(SCSI_MODULE_VER) sd.o sd_ioctl.o
- + $(LD) $(LD_RFLAG) -r -o $@ sd.o sd_ioctl.o
- +
- +oak_mod.o: oak.o
- + $(LD) $(LD_RFLAG) -r -o oak_mod.o oak.o `gcc --print-libgcc-file-name`
- +
- +acornscsi_mod.o: acornscsi.o acornscsi-io.o
- + $(LD) $(LD_RFLAG) -r -o acornscsi_mod.o acornscsi.o acornscsi-io.o `gcc --print-libgcc-file-name`
- +
- +$(SYMTAB_OBJS): $(SYMTAB_OBJS:.o=.c)
- +
- +modules: $(SCSI_MODULE_VER)
- +
- +LK = constants.c constants.h hosts.h scsi.h scsi_ioctl.c scsi_ioctl.h scsi_proc.c scsi_syms.c \
- + scsicam.c sd.c sd.h sd_ioctl.c sg.c sg.h sr.c sr.h sr_ioctl.c st.c st.h st_options.h
- +
- +links:
- + -@for f in $(LK); do \
- + ln -s ../../../../drivers/scsi/$$f .; \
- + done
- + touch links
- +
- +LINKCLEAN:
- + -@for f in $(LK); do \
- + if [ -L $$f ]; then rm -f $$f; fi; \
- + done
- + rm -f links
- diff -urNwbB linux/arch/arm/drivers/scsi/NCR5380.c linux.arm/arch/arm/drivers/scsi/NCR5380.c
- --- linux/arch/arm/drivers/scsi/NCR5380.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/scsi/NCR5380.c Sat Feb 24 09:36:54 1996
- @@ -0,0 +1,2786 @@
- +#define NDEBUG (NDEBUG_RESTART_SELECT)
- +/*
- + * NCR 5380 generic driver routines. These should make it *trivial*
- + * to implement 5380 SCSI drivers under Linux with a non-trantor
- + * architecture.
- + *
- + * Note that these routines also work with NR53c400 family chips.
- + *
- + * Copyright 1993, Drew Eckhardt
- + * Visionary Computing
- + * (Unix and Linux consulting and custom programming)
- + * drew@colorado.edu
- + * +1 (303) 666-5836
- + *
- + * DISTRIBUTION RELEASE 6.
- + *
- + * For more information, please consult
- + *
- + * NCR 5380 Family
- + * SCSI Protocol Controller
- + * Databook
- + *
- + * NCR Microelectronics
- + * 1635 Aeroplaza Drive
- + * Colorado Springs, CO 80916
- + * 1+ (719) 578-3400
- + * 1+ (800) 334-5454
- + */
- +
- +/*
- + * $Log: NCR5380.c,v $
- + * Revision 1.5 1994/01/19 09:14:57 drew
- + * Fixed udelay() hack that was being used on DATAOUT phases
- + * instead of a proper wait for the final handshake.
- + *
- + * Revision 1.4 1994/01/19 06:44:25 drew
- + * *** empty log message ***
- + *
- + * Revision 1.3 1994/01/19 05:24:40 drew
- + * Added support for TCR LAST_BYTE_SENT bit.
- + *
- + * Revision 1.2 1994/01/15 06:14:11 drew
- + * REAL DMA support, bug fixes.
- + *
- + * Revision 1.1 1994/01/15 06:00:54 drew
- + * Initial revision
- + *
- + */
- +
- +/*
- + * Further development / testing that should be done :
- + * 1. Cleanup the NCR5380_transfer_dma function and DMA operation complete
- + * code so that everything does the same thing that's done at the
- + * end of a pseudo-DMA read operation.
- + *
- + * 2. Fix REAL_DMA (interrupt driven, polled works fine) -
- + * basically, transfer size needs to be reduced by one
- + * and the last byte read as is done with PSEUDO_DMA.
- + *
- + * 3. Test USLEEP code
- + *
- + * 4. Test SCSI-II tagged queueing (I have no devices which support
- + * tagged queueing)
- + *
- + * 5. Test linked command handling code after Eric is ready with
- + * the high level code.
- + */
- +
- +#ifndef notyet
- +#undef LINKED
- +#undef USLEEP
- +#undef REAL_DMA
- +#endif
- +
- +#ifdef REAL_DMA_POLL
- +#undef READ_OVERRUNS
- +#define READ_OVERRUNS
- +#endif
- +
- +/*
- + * Design
- + * Issues :
- + *
- + * The other Linux SCSI drivers were written when Linux was Intel PC-only,
- + * and specifically for each board rather than each chip. This makes their
- + * adaptation to platforms like the Mac (Some of which use NCR5380's)
- + * more difficult than it has to be.
- + *
- + * Also, many of the SCSI drivers were written before the command queuing
- + * routines were implemented, meaning their implementations of queued
- + * commands were hacked on rather than designed in from the start.
- + *
- + * When I designed the Linux SCSI drivers I figured that
- + * while having two different SCSI boards in a system might be useful
- + * for debugging things, two of the same type wouldn't be used.
- + * Well, I was wrong and a number of users have mailed me about running
- + * multiple high-performance SCSI boards in a server.
- + *
- + * Finally, when I get questions from users, I have no idea what
- + * revision of my driver they are running.
- + *
- + * This driver attempts to address these problems :
- + * This is a generic 5380 driver. To use it on a different platform,
- + * one simply writes appropriate system specific macros (ie, data
- + * transfer - some PC's will use the I/O bus, 68K's must use
- + * memory mapped) and drops this file in their 'C' wrapper.
- + *
- + * As far as command queueing, two queues are maintained for
- + * each 5380 in the system - commands that haven't been issued yet,
- + * and commands that are currently executing. This means that an
- + * unlimited number of commands may be queued, letting
- + * more commands propagate from the higher driver levels giving higher
- + * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported,
- + * allowing multiple commands to propagate all the way to a SCSI-II device
- + * while a command is already executing.
- + *
- + * To solve the multiple-boards-in-the-same-system problem,
- + * there is a separate instance structure for each instance
- + * of a 5380 in the system. So, multiple NCR5380 drivers will
- + * be able to coexist with appropriate changes to the high level
- + * SCSI code.
- + *
- + * A NCR5380_PUBLIC_REVISION macro is provided, with the release
- + * number (updated for each public release) printed by the
- + * NCR5380_print_options command, which should be called from the
- + * wrapper detect function, so that I know what release of the driver
- + * users are using.
- + *
- + * Issues specific to the NCR5380 :
- + *
- + * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead
- + * piece of hardware that requires you to sit in a loop polling for
- + * the REQ signal as long as you are connected. Some devices are
- + * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect
- + * while doing long seek operations.
- + *
- + * The workaround for this is to keep track of devices that have
- + * disconnected. If the device hasn't disconnected, for commands that
- + * should disconnect, we do something like
- + *
- + * while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
- + *
- + * Some tweaking of N and M needs to be done. An algorithm based
- + * on "time to data" would give the best results as long as short time
- + * to datas (ie, on the same track) were considered, however these
- + * broken devices are the exception rather than the rule and I'd rather
- + * spend my time optimizing for the normal case.
- + *
- + * Architecture :
- + *
- + * At the heart of the design is a coroutine, NCR5380_main,
- + * which is started when not running by the interrupt handler,
- + * timer, and queue command function. It attempts to establish
- + * I_T_L or I_T_L_Q nexuses by removing the commands from the
- + * issue queue and calling NCR5380_select() if a nexus
- + * is not established.
- + *
- + * Once a nexus is established, the NCR5380_information_transfer()
- + * phase goes through the various phases as instructed by the target.
- + * if the target goes into MSG IN and sends a DISCONNECT message,
- + * the command structure is placed into the per instance disconnected
- + * queue, and NCR5380_main tries to find more work. If USLEEP
- + * was defined, and the target is idle for too long, the system
- + * will try to sleep.
- + *
- + * If a command has disconnected, eventually an interrupt will trigger,
- + * calling NCR5380_intr() which will in turn call NCR5380_reselect
- + * to reestablish a nexus. This will run main if necessary.
- + *
- + * On command termination, the done function will be called as
- + * appropriate.
- + *
- + * SCSI pointers are maintained in the SCp field of SCSI command
- + * structures, being initialized after the command is connected
- + * in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
- + * Note that in violation of the standard, an implicit SAVE POINTERS operation
- + * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
- + */
- +
- +/*
- + * Using this file :
- + * This file a skeleton Linux SCSI driver for the NCR 5380 series
- + * of chips. To use it, you write a architecture specific functions
- + * and macros and include this file in your driver.
- + *
- + * These macros control options :
- + * AUTOPROBE_IRQ - if defined, the NCR5380_probe_irq() function will be
- + * defined.
- + *
- + * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
- + * for commands that return with a CHECK CONDITION status.
- + *
- + * DIFFERENTIAL - if defined, NCR53c81 chips will use external differential
- + * transceivers.
- + *
- + * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512
- + * bytes at a time. Since interrupts are disabled by default during
- + * these transfers, we might need this to give reasonable interrupt
- + * service time if the transfer size gets too large.
- + *
- + * LINKED - if defined, linked commands are supported.
- + *
- + * PSEUDO_DMA - if defined, PSEUDO DMA is used during the data transfer phases.
- + *
- + * REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
- + *
- + * REAL_DMA_POLL - if defined, REAL DMA is used but the driver doesn't
- + * rely on phase mismatch and EOP interrupts to determine end
- + * of phase.
- + *
- + * SCSI2 - if defined, SCSI-2 tagged queuing is used where possible
- + *
- + * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. You
- + * only really want to use this if you're having a problem with
- + * dropped characters during high speed communications, and even
- + * then, you're going to be better off twiddling with transfersize
- + * in the high level code.
- + *
- + * USLEEP - if defined, on devices that aren't disconnecting from the
- + * bus, we will go to sleep so that the CPU can get real work done
- + * when we run a command that won't complete immediately.
- + *
- + * Note that if USLEEP is defined, NCR5380_TIMER *must* also be
- + * defined.
- + *
- + * Defaults for these will be provided if USLEEP is defined, although
- + * the user may want to adjust these to allocate CPU resources to
- + * the SCSI driver or "real" code.
- + *
- + * USLEEP_SLEEP - amount of time, in jiffies, to sleep
- + *
- + * USLEEP_POLL - amount of time, in jiffies, to poll
- + *
- + * These macros MUST be defined :
- + * NCR5380_local_declare() - declare any local variables needed for your transfer
- + * routines.
- + *
- + * NCR5380_setup(instance) - initialize any local variables needed from a given
- + * instance of the host adapter for NCR5380_{read,write,pread,pwrite}
- + *
- + * NCR5380_read(register) - read from the specified register
- + *
- + * NCR5380_write(register, value) - write to the specific register
- + *
- + * NCR5380_implementation_fields - additional fields needed for this
- + * specific implementation of the NCR5380
- + *
- + * Either real DMA *or* pseudo DMA may be implemented
- + * REAL functions :
- + * NCR5380_REAL_DMA should be defined if real DMA is to be used.
- + * Note that the DMA setup functions should return the number of bytes
- + * that they were able to program the controller for.
- + *
- + * Also note that generic i386/PC versions of these macros are
- + * available as NCR5380_i386_dma_write_setup,
- + * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
- + *
- + * NCR5380_dma_write_setup(instance, src, count) - initialize
- + * NCR5380_dma_read_setup(instance, dst, count) - initialize
- + * NCR5380_dma_residual(instance); - residual count
- + *
- + * PSEUDO functions :
- + * NCR5380_pwrite(instance, src, count)
- + * NCR5380_pread(instance, dst, count);
- + *
- + * If nothing specific to this implementation needs doing (ie, with external
- + * hardware), you must also define
- + *
- + * NCR5380_queue_command
- + * NCR5380_reset
- + * NCR5380_abort
- + *
- + * to be the global entry points into the specific driver, ie
- + * #define NCR5380_queue_command t128_queue_command.
- + *
- + * If this is not done, the routines will be defined as static functions
- + * with the NCR5380* names and the user must provide a globally
- + * accessible wrapper function.
- + *
- + * The generic driver is initialized by calling NCR5380_init(instance),
- + * after setting the appropriate host specific fields and ID. If the
- + * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
- + * possible) function may be used. Before the specific driver initialization
- + * code finishes, NCR5380_print_options should be called.
- + */
- +
- +static struct Scsi_Host *first_instance = NULL;
- +static Scsi_Host_Template *the_template = NULL;
- +
- +/*
- + * Function : void initialize_SCp(Scsi_Cmnd *cmd)
- + *
- + * Purpose : initialize the saved data pointers for cmd to point to the
- + * start of the buffer.
- + *
- + * Inputs : cmd - Scsi_Cmnd structure to have pointers reset.
- + */
- +
- +static __inline__ void initialize_SCp(Scsi_Cmnd *cmd) {
- + /*
- + * Initialize the Scsi Pointer field so that all of the commands in the
- + * various queues are valid.
- + */
- +
- + if (cmd->use_sg) {
- + cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
- + cmd->SCp.buffers_residual = cmd->use_sg - 1;
- + cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
- + cmd->SCp.this_residual = cmd->SCp.buffer->length;
- + } else {
- + cmd->SCp.buffer = NULL;
- + cmd->SCp.buffers_residual = 0;
- + cmd->SCp.ptr = (char *) cmd->request_buffer;
- + cmd->SCp.this_residual = cmd->request_bufflen;
- + }
- +}
- +
- +#include <linux/delay.h>
- +
- +#ifdef NDEBUG
- +static struct {
- + unsigned char mask;
- + char * name;}
- +signals[] = {{ SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" },
- + { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD, "CD" }, { SR_IO, "IO" },
- + { SR_SEL, "SEL" }, {0, NULL}},
- +basrs[] = {{BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}},
- +icrs[] = {{ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"},
- + {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"},
- + {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"},
- + {0, NULL}},
- +mrs[] = {{MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"},
- + {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR,
- + "MODE PARITY INTR"}, {MR_MONITOR_BSY, "MODE MONITOR BSY"},
- + {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"},
- + {0, NULL}};
- +
- +/*
- + * Function : void NCR5380_print(struct Scsi_Host *instance)
- + *
- + * Purpose : print the SCSI bus signals for debugging purposes
- + *
- + * Input : instance - which NCR5380
- + */
- +
- +static void NCR5380_print(struct Scsi_Host *instance) {
- + NCR5380_local_declare();
- + unsigned char status, data, basr, mr, icr, i;
- + NCR5380_setup(instance);
- + cli();
- + data = NCR5380_read(CURRENT_SCSI_DATA_REG);
- + status = NCR5380_read(STATUS_REG);
- + mr = NCR5380_read(MODE_REG);
- + icr = NCR5380_read(INITIATOR_COMMAND_REG);
- + basr = NCR5380_read(BUS_AND_STATUS_REG);
- + sti();
- + for (i = 0; signals[i].mask ; ++i)
- + if (status & signals[i].mask)
- + printk(" %s", signals[i].name);
- + for (i = 0; basrs[i].mask ; ++i)
- + if (basr & basrs[i].mask)
- + printk(" %s", basrs[i].name);
- + for (i = 0; icrs[i].mask; ++i)
- + if (icr & icrs[i].mask)
- + printk(" %s", icrs[i].name);
- + for (i = 0; mrs[i].mask; ++i)
- + if (mr & mrs[i].mask)
- + printk(" %s", mrs[i].name);
- + printk("\n");
- +}
- +
- +static struct {
- + unsigned char value;
- + char *name;
- +} phases[] = {
- +{PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
- +{PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
- +{PHASE_UNKNOWN, "UNKNOWN"}};
- +
- +/*
- + * Function : void NCR5380_print_phase(struct Scsi_Host *instance)
- + *
- + * Purpose : print the current SCSI phase for debugging purposes
- + *
- + * Input : instance - which NCR5380
- + */
- +
- +static void NCR5380_print_phase(struct Scsi_Host *instance) {
- + NCR5380_local_declare();
- + unsigned char status;
- + int i;
- + NCR5380_setup(instance);
- +
- + status = NCR5380_read(STATUS_REG);
- + if (!(status & SR_REQ))
- + printk("scsi%d: REQ not asserted, phase unknown.\n",
- + instance->host_no);
- + else {
- + for (i = 0; (phases[i].value != PHASE_UNKNOWN) &&
- + (phases[i].value != (status & PHASE_MASK)); ++i);
- + printk("scsi%d: phase %s\n", instance->host_no, phases[i].name);
- + }
- +}
- +#endif
- +
- +/*
- + * We need to have our coroutine active given these constraints :
- + * 1. The mutex flag, main_running, can only be set when the main
- + * routine can actually process data, otherwise SCSI commands
- + * will never get issued.
- + *
- + * 2. NCR5380_main() shouldn't be called before it has exited, because
- + * other drivers have had kernel stack overflows in similar
- + * situations.
- + *
- + * 3. We don't want to inline NCR5380_main() because of space concerns,
- + * even though it is only called in two places.
- + *
- + * So, the solution is to set the mutex in an inline wrapper for the
- + * main coroutine, and have the main coroutine exit with interrupts
- + * disabled after the final search through the queues so that no race
- + * conditions are possible.
- + */
- +
- +static volatile int main_running = 0;
- +
- +/*
- + * Function : run_main(void)
- + *
- + * Purpose : insure that the coroutine is running and will process our
- + * request. main_running is checked/set here (in an inline function)
- + * rather than in NCR5380_main itself to reduce the chances of stack
- + * overflow.
- + *
- + */
- +
- +static __inline__ void run_main(void) {
- + cli();
- + if (!main_running) {
- + main_running = 1;
- + NCR5380_main();
- + /*
- + * main_running is cleared in NCR5380_main once it can't do
- + * more work, and NCR5380_main exits with interrupts disabled.
- + */
- + sti();
- + } else
- + sti();
- +}
- +
- +#ifdef USLEEP
- +#ifndef NCR5380_TIMER
- +#error "NCR5380_TIMER must be defined so that this type of NCR5380 driver gets a unique timer."
- +#endif
- +
- +/*
- + * These need tweaking, and would probably work best as per-device
- + * flags initialized differently for disk, tape, cd, etc devices.
- + * People with broken devices are free to experiment as to what gives
- + * the best results for them.
- + *
- + * USLEEP_SLEEP should be a minimum seek time.
- + *
- + * USLEEP_POLL should be a maximum rotational latency.
- + */
- +#ifndef USLEEP_SLEEP
- +/* 20 ms (reasonable hard disk speed) */
- +#define USLEEP_SLEEP 2
- +#endif
- +/* 300 RPM (floppy speed) */
- +#ifndef USLEEP_POLL
- +#define USLEEP_POLL 20
- +#endif
- +
- +static struct Scsi_Host * expires_first = NULL;
- +
- +/*
- + * Function : int should_disconnect (unsigned char cmd)
- + *
- + * Purpose : decide weather a command would normally disconnect or
- + * not, since if it won't disconnect we should go to sleep.
- + *
- + * Input : cmd - opcode of SCSI command
- + *
- + * Returns : DISCONNECT_LONG if we should disconnect for a really long
- + * time (ie always, sleep, look for REQ active, sleep),
- + * DISCONNECT_TIME_TO_DATA if we would only disconnect for a normal
- + * time-to-data delay, DISCONNECT_NONE if this command would return
- + * immediately.
- + *
- + * Future sleep algorithms based on time to data can exploit
- + * something like this so they can differentiate between "normal"
- + * (ie, read, write, seek) and unusual commands (ie, * format).
- + *
- + * Note : We don't deal with commands that handle an immediate disconnect,
- + *
- + */
- +
- +static int should_disconnect (unsigned char cmd) {
- + switch (cmd) {
- + case READ_6:
- + case WRITE_6:
- + case SEEK_6:
- + case READ_10:
- + case WRITE_10:
- + case SEEK_10:
- + return DISCONNECT_TIME_TO_DATA;
- + case FORMAT_UNIT:
- + case SEARCH_HIGH:
- + case SEARCH_LOW:
- + case SEARCH_EQUAL:
- + return DISCONNECT_LONG;
- + default:
- + return DISCONNECT_NONE;
- + }
- +}
- +
- +/*
- + * Assumes instance->time_expires has been set in higher level code.
- + */
- +
- +static int NCR5380_set_timer (struct Scsi_Host *instance) {
- + struct Scsi_Host *tmp, **prev;
- +
- + cli();
- + if (((struct NCR5380_hostdata *) (instance->host_data))->next_timer) {
- + sti();
- + return -1;
- + }
- +
- + for (prev = &expires_first, tmp = expires_first; tmp;
- + prev = &(((struct NCR5380_hostdata *) tmp->host_data)->next_timer),
- + tmp = ((struct NCR5380_hostdata *) tmp->host_data)->next_timer)
- + if (instance->time_expires < tmp->time_expires)
- + break;
- +
- + instance->next_timer = tmp;
- + *prev = instance;
- + timer_table[NCR5380_TIMER].expires = expires_first->time_expires;
- + timer_active |= 1 << NCR5380_TIMER;
- + sti();
- + return 0;
- +}
- +
- +/* Doing something about unwanted reentrancy here might be useful */
- +void NCR5380_timer_fn(void) {
- + struct Scsi_Host *instance;
- + cli();
- + for (; expires_first && expires_first->time_expires >= jiffies; ) {
- + instance = ((NCR5380_hostdata *) expires_first->host_data)->
- + expires_next;
- + ((NCR5380_hostdata *) expires_first->host_data)->expires_next =
- + NULL;
- + ((NCR5380_hostdata *) expires_first->host_data)->time_expires =
- + 0;
- + expires_first = instance;
- + }
- +
- + if (expires_first) {
- + timer_table[NCR5380_TIMER].expires = ((NCR5380_hostdata *)
- + expires_first->host_data)->time_expires;
- + timer_active |= (1 << NCR5380_TIMER);
- + } else {
- + timer_table[NCR5380_TIMER].expires = 0;
- + timer_active &= ~(1 << MCR5380_TIMER);
- + }
- + sti();
- +
- + run_main();
- +}
- +#endif /* def USLEEP */
- +
- +static void NCR5380_all_init (void) {
- + static int done = 0;
- + if (!done) {
- +#if (NDEBUG & NDEBUG_INIT)
- + printk("scsi : NCR5380_all_init()\n");
- +#endif
- + done = 1;
- +#ifdef USLEEP
- + timer_table[NCR5380_TIMER].expires = 0;
- + timer_table[NCR5380_TIMER].fn = NCR5380_timer_fn;
- +#endif
- + }
- +}
- +
- +#ifdef AUTOPROBE_IRQ
- +/*
- + * Function : int NCR5380_probe_irq (struct Scsi_Host *instance, int possible)
- + *
- + * Purpose : autoprobe for the IRQ line used by the NCR5380.
- + *
- + * Inputs : instance - pointer to this instance of the NCR5380 driver,
- + * possible - bitmask of permissible interrupts.
- + *
- + * Returns : number of the IRQ selected, IRQ_NONE if no interrupt fired.
- + *
- + * XXX no effort is made to deal with spurious interrupts.
- + */
- +
- +
- +static int probe_irq;
- +static void probe_intr (int irq, struct pt_regs * regs) {
- + probe_irq = irq;
- +};
- +
- +static int NCR5380_probe_irq (struct Scsi_Host *instance, int possible) {
- + NCR5380_local_declare();
- + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
- + instance->hostdata;
- + unsigned long timeout;
- + int trying_irqs, i, mask;
- + NCR5380_setup(instance);
- +
- + for (trying_irqs = i = 0, mask = 1; i < 16; ++i, mask <<= 1)
- + if ((mask & possible) && (request_irq(i, &probe_intr, SA_INTERRUPT, "NCR-probe")
- + == 0))
- + trying_irqs |= mask;
- +
- + timeout = jiffies + 25;
- + probe_irq = IRQ_NONE;
- +
- +/*
- + * A interrupt is triggered whenever BSY = false, SEL = true
- + * and a bit set in the SELECT_ENABLE_REG is asserted on the
- + * SCSI bus.
- + *
- + * Note that the bus is only driven when the phase control signals
- + * (I/O, C/D, and MSG) match those in the TCR, so we must reset that
- + * to zero.
- + */
- +
- + NCR5380_write(TARGET_COMMAND_REG, 0);
- + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- + NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA |
- + ICR_ASSERT_SEL);
- +
- + while (probe_irq == IRQ_NONE && jiffies < timeout);
- +
- + NCR5380_write(SELECT_ENABLE_REG, 0);
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- +
- + for (i = 0, mask = 1; i < 16; ++i, mask <<= 1)
- + if (trying_irqs & mask)
- + free_irq(i);
- +
- + return probe_irq;
- +}
- +#endif /* AUTOPROBE_IRQ */
- +
- +/*
- + * Function : void NCR58380_print_options (struct Scsi_Host *instance)
- + *
- + * Purpose : called by probe code indicating the NCR5380 driver
- + * options that were selected.
- + *
- + * Inputs : instance, pointer to this instance. Unused.
- + */
- +
- +static void NCR5380_print_options (struct Scsi_Host *instance) {
- + printk(" generic options"
- +#ifdef AUTOPROBE_IRQ
- + " AUTOPROBE_IRQ"
- +#endif
- +#ifdef AUTOSENSE
- + " AUTOSENSE"
- +#endif
- +#ifdef DIFFERENTIAL
- + " DIFFERENTIAL"
- +#endif
- +#ifdef REAL_DMA
- + " REAL DMA"
- +#endif
- +#ifdef REAL_DMA_POLL
- + " REAL DMA POLL"
- +#endif
- +#ifdef PARITY
- + " PARITY"
- +#endif
- +#ifdef PSEUDO_DMA
- + " PSEUDO DMA"
- +#endif
- +#ifdef SCSI2
- + " SCSI-2"
- +#endif
- +#ifdef UNSAFE
- + " UNSAFE "
- +#endif
- + );
- +#ifdef USLEEP
- + printk(" USLEEP, USLEEP_POLL=%d USLEEP_SLEEP=%d", USLEEP_POLL, USLEEP_SLEEP);
- +#endif
- + printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
- +#ifdef NCR53C400
- + if (hostdata->flags & FLAG_NCR53C400) {
- + printk(" ncr53c400 release=%d", NCR53C400_PUBLIC_RELEASE);
- + }
- +#endif
- +}
- +
- +/*
- + * Function : void NCR5380_print_status (struct Scsi_Host *instance)
- + *
- + * Purpose : print commands in the various queues, called from
- + * NCR5380_abort and NCR5380_debug to aid debugging.
- + *
- + * Inputs : instance, pointer to this instance.
- + */
- +
- +static void NCR5380_print_status (struct Scsi_Host *instance) {
- + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
- + instance->hostdata;
- + Scsi_Cmnd *ptr;
- +
- +
- + printk("NCR5380 : coroutine is%s running.\n",
- + main_running ? "" : "n't");
- +
- +#ifdef NDEBUG
- + NCR5380_print (instance);
- + NCR5380_print_phase (instance);
- +#endif
- +
- + cli();
- + if (!hostdata->connected) {
- + printk ("scsi%d: no currently connected command\n",
- + instance->host_no);
- + } else {
- + print_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected);
- + }
- +
- + printk ("scsi%d: issue_queue\n", instance->host_no);
- +
- + for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr;
- + ptr = (Scsi_Cmnd *) ptr->host_scribble)
- + print_Scsi_Cmnd (ptr);
- +
- + printk ("scsi%d: disconnected_queue\n", instance->host_no);
- +
- + for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
- + ptr = (Scsi_Cmnd *) ptr->host_scribble)
- + print_Scsi_Cmnd (ptr);
- +
- + sti();
- +}
- +
- +
- +/*
- + * Function : void NCR5380_init (struct Scsi_Host *instance, flags)
- + *
- + * Purpose : initializes *instance and corresponding 5380 chip,
- + * with flags OR'd into the initial flags value.
- + *
- + * Inputs : instance - instantiation of the 5380 driver.
- + *
- + * Notes : I assume that the host, hostno, and id bits have been
- + * set correctly. I don't care about the irq and other fields.
- + *
- + */
- +
- +static void NCR5380_init (struct Scsi_Host *instance, int flags) {
- + NCR5380_local_declare();
- + int i;
- + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
- + instance->hostdata;
- +
- + /*
- + * On NCR53C400 boards, NCR5380 registers are mapped 8 past
- + * the base address.
- + */
- +
- + if (flags & FLAG_NCR53C400)
- + instance->io_port += 8;
- +
- + NCR5380_setup(instance);
- +
- + NCR5380_all_init();
- +
- + hostdata->aborted = 0;
- + hostdata->id_mask = 1 << instance->this_id;
- + for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
- + if (i > hostdata->id_mask)
- + hostdata->id_higher_mask |= i;
- + for (i = 0; i < 8; ++i)
- + hostdata->busy[i] = 0;
- +#ifdef REAL_DMA
- + hostdata->dmalen = 0;
- +#endif
- + hostdata->targets_present = 0;
- + hostdata->connected = NULL;
- + hostdata->issue_queue = NULL;
- + hostdata->disconnected_queue = NULL;
- + hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT | flags;
- +
- + if (!the_template) {
- + the_template = instance->hostt;
- + first_instance = instance;
- + }
- +
- +
- +#ifdef USLEEP
- + hostdata->time_expires = 0;
- + hostdata->next_timer = NULL;
- +#endif
- +
- +#ifndef AUTOSENSE
- + if ((instance->cmd_per_lun > 1) || instance->can_queue > 1))
- + printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n"
- + " without AUTOSENSE option, contingent allegiance conditions may\n"
- + " be incorrectly cleared.\n", instance->host_no);
- +#endif /* def AUTOSENSE */
- +
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- + NCR5380_write(MODE_REG, MR_BASE);
- + NCR5380_write(TARGET_COMMAND_REG, 0);
- + NCR5380_write(SELECT_ENABLE_REG, 0);
- +#ifdef NCR53C400
- + if (hostdata->flags & FLAG_NCR53C400) {
- + NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE);
- + }
- +#endif
- +}
- +
- +/*
- + * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd,
- + * void (*done)(Scsi_Cmnd *))
- + *
- + * Purpose : enqueues a SCSI command
- + *
- + * Inputs : cmd - SCSI command, done - function called on completion, with
- + * a pointer to the command descriptor.
- + *
- + * Returns : 0
- + *
- + * Side effects :
- + * cmd is added to the per instance issue_queue, with minor
- + * twiddling done to the host specific fields of cmd. If the
- + * main coroutine is not running, it is restarted.
- + *
- + */
- +
- +/* Only make static if a wrapper function is used */
- +#ifndef NCR5380_queue_command
- +static
- +#endif
- +int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) {
- + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
- + cmd->host->hostdata;
- + Scsi_Cmnd *tmp;
- +
- +#if (NDEBUG & NDEBUG_NO_WRITE)
- + switch (cmd->cmnd[0]) {
- + case WRITE:
- + case WRITE_10:
- + printk("scsi%d: WRITE attempted with NO_WRITE debugging flag set\n",
- + instance->host_no);
- + cmd->result = (DID_ERROR << 16);
- + done(cmd);
- + return 0;
- + }
- +#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
- +
- +
- + /*
- + * We use the host_scribble field as a pointer to the next command
- + * in a queue
- + */
- +
- + cmd->host_scribble = NULL;
- + cmd->scsi_done = done;
- +
- + cmd->result = 0;
- +
- +
- + /*
- + * Insert the cmd into the issue queue. Note that REQUEST SENSE
- + * commands are added to the head of the queue since any command will
- + * clear the contingent allegiance condition that exists and the
- + * sense data is only guaranteed to be valid while the condition exists.
- + */
- +
- + cli();
- + if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
- + cmd->host_scribble = (unsigned char *) hostdata->issue_queue;
- + hostdata->issue_queue = cmd;
- + } else {
- + for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble;
- + tmp = (Scsi_Cmnd *) tmp->host_scribble);
- + tmp->host_scribble = (unsigned char *) cmd;
- + }
- +#if (NDEBUG & NDEBUG_QUEUES)
- + printk("scsi%d: command added to %s of queue\n", instance->host_no,
- + (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
- +#endif
- +
- +/* Run the coroutine if it isn't already running. */
- + run_main();
- + return 0;
- +}
- +
- +/*
- + * Function : NCR5380_main (void)
- + *
- + * Purpose : NCR5380_main is a coroutine that runs as long as more work can
- + * be done on the NCR5380 host adapters in a system. Both
- + * NCR5380_queue_command() and NCR5380_intr() will try to start it
- + * in case it is not running.
- + *
- + * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should
- + * reenable them. This prevents reentrancy and kernel stack overflow.
- + */
- +
- +static void NCR5380_main (void) {
- + Scsi_Cmnd *tmp, *prev;
- + struct Scsi_Host *instance;
- + struct NCR5380_hostdata *hostdata;
- + int done;
- +
- + /*
- + * We run (with interrupts disabled) until we're sure that none of
- + * the host adapters have anything that can be done, at which point
- + * we set main_running to 0 and exit.
- + *
- + * Interrupts are enabled before doing various other internal
- + * instructions, after we've decided that we need to run through
- + * the loop again.
- + *
- + * this should prevent any race conditions.
- + */
- +
- + do {
- + cli(); /* Freeze request queues */
- + done = 1;
- + for (instance = first_instance; instance &&
- + instance->hostt == the_template; instance=instance->next) {
- + hostdata = (struct NCR5380_hostdata *) instance->hostdata;
- + cli();
- + if (!hostdata->connected) {
- +#if (NDEBUG & NDEBUG_MAIN)
- + printk("scsi%d: not connected\n", instance->host_no);
- +#endif
- + /*
- + * Search through the issue_queue for a command destined
- + * for a target that's not busy.
- + */
- + for (tmp = (Scsi_Cmnd *) hostdata->issue_queue,
- + prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *)
- + tmp->host_scribble)
- +
- + /* When we find one, remove it from the issue queue. */
- + if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) {
- + if (prev)
- + prev->host_scribble = tmp->host_scribble;
- + else
- + hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble;
- + tmp->host_scribble = NULL;
- +
- + /* reenable interrupts after finding one */
- + sti();
- +
- + /*
- + * Attempt to establish an I_T_L nexus here.
- + * On success, instance->hostdata->connected is set.
- + * On failure, we must add the command back to the
- + * issue queue so we can keep trying.
- + */
- +#if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES))
- + printk("scsi%d: main() : command for target %d lun %d removed from issue_queue\n",
- + instance->host_no, tmp->target, tmp->lun);
- +#endif
- + /*
- + * REQUEST SENSE commands are issued without tagged
- + * queueing, even on SCSI-II devices because the
- + * contingent allegiance condition exists for the
- + * entire unit.
- + */
- +
- + if (!NCR5380_select(instance, tmp,
- + (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE :
- + TAG_NEXT)) {
- + break;
- + } else {
- + cli();
- + tmp->host_scribble = (unsigned char *)
- + hostdata->issue_queue;
- + hostdata->issue_queue = tmp;
- + sti();
- +#if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES))
- + printk("scsi%d: main(): select() failed, returned to issue_queue\n",
- + instance->host_no);
- +#endif
- + }
- + } /* if target/lun is not busy */
- + } /* if (!hostdata->connected) */
- +
- + if (hostdata->connected
- +#ifdef REAL_DMA
- + && !hostdata->dmalen
- +#endif
- +#ifdef USLEEP
- + && (!hostdata->time_expires || hostdata->time_expires >= jiffies)
- +#endif
- + ) {
- + sti();
- +#if (NDEBUG & NDEBUG_MAIN)
- + printk("scsi%d: main() : performing information transfer\n",
- + instance->host_no);
- +#endif
- + NCR5380_information_transfer(instance);
- +#if (NDEBUG & NDEBUG_MAIN)
- + printk("scsi%d: main() : done set false\n", instance->host_no);
- +#endif
- + done = 0;
- + } else
- + break;
- + } /* for instance */
- + } while (!done);
- + main_running = 0;
- +}
- +
- +/*
- + * Function : void NCR5380_intr (int irq)
- + *
- + * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
- + * from the disconnected queue, and restarting NCR5380_main()
- + * as required.
- + *
- + * Inputs : int irq, irq that caused this interrupt.
- + *
- + */
- +
- +static void NCR5380_intr (int irq, struct pt_regs * regs) {
- + NCR5380_local_declare();
- + struct Scsi_Host *instance;
- + int done;
- + unsigned char basr;
- +#if (NDEBUG & NDEBUG_INTR)
- + printk("scsi : NCR5380 irq %d triggered\n", irq);
- +#endif
- + do {
- + done = 1;
- + for (instance = first_instance; instance && (instance->hostt ==
- + the_template); instance = instance->next)
- + if (instance->irq == irq) {
- +
- + /* Look for pending interrupts */
- + NCR5380_setup(instance);
- + basr = NCR5380_read(BUS_AND_STATUS_REG);
- + /* XXX dispatch to appropriate routine if found and done=0 */
- + if (basr & BASR_IRQ) {
- +#if (NDEBUG & NDEBUG_INTR)
- + NCR5380_print(instance);
- +#endif
- + if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) ==
- + (SR_SEL | SR_IO)) {
- + done = 0;
- + sti();
- +#if (NDEBUG & NDEBUG_INTR)
- + printk("scsi%d: SEL interrupt\n", instance->host_no);
- +#endif
- + NCR5380_reselect(instance);
- + (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- + } else if (basr & BASR_PARITY_ERROR) {
- +#if (NDEBUG & NDEBUG_INTR)
- + printk("scsi%d: PARITY interrupt\n", instance->host_no);
- +#endif
- + (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- + } else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) {
- +#if (NDEBUG & NDEBUG_INTR)
- + printk("scsi%d: RESET interrupt\n", instance->host_no);
- +#endif
- + (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- + } else {
- +/*
- + * XXX the rest of the interrupt conditions should *only* occur during a
- + * DMA transfer, which I haven't gotten around to fixing yet.
- + */
- +
- +#if defined(REAL_DMA)
- + /*
- + * We should only get PHASE MISMATCH and EOP interrupts
- + * if we have DMA enabled, so do a sanity check based on
- + * the current setting of the MODE register.
- + */
- +
- + if ((NCR5380_read(MODE_REG) & MR_DMA) && ((basr &
- + BASR_END_DMA_TRANSFER) ||
- + !(basr & BASR_PHASE_MATCH))) {
- + int transfered;
- +
- + if (!hostdata->connected)
- + panic("scsi%d: received end of DMA interrupt with no connected cmd\n",
- + instance->hostno);
- +
- + transfered = (hostdata->dmalen - NCR5380_dma_residual(instance));
- + hostdata->connected->SCp.this_residual -= transferred;
- + hostdata->connected->SCp.ptr += transferred;
- + hostdata->dmalen = 0;
- +
- + (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- +#if NCR_TIMEOUT
- + {
- + unsigned long timeout = jiffies + NCR_TIMEOUT;
- +
- + while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK
- + && jiffies < timeout)
- + ;
- + if (jiffies >= timeout)
- + printk("scsi: timeout at %d\n", __LINE__);
- + }
- +#else /* NCR_TIMEOUT */
- + while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK);
- +#endif
- +
- + NCR5380_write(MODE_REG, MR_BASE);
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- + }
- +#else
- +#if (NDEBUG & NDEBUG_INTR)
- + printk("scsi : unknown interrupt, BASR 0x%X, MR 0x%X, SR 0x%x\n", basr, NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
- +#endif
- + (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- +#endif
- + }
- + } /* if BASR_IRQ */
- + if (!done)
- + run_main();
- + } /* if (instance->irq == irq) */
- + } while (!done);
- +}
- +
- +/*
- + * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
- + * int tag);
- + *
- + * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
- + * including ARBITRATION, SELECTION, and initial message out for
- + * IDENTIFY and queue messages.
- + *
- + * Inputs : instance - instantiation of the 5380 driver on which this
- + * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for
- + * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for
- + * the command that is presently connected.
- + *
- + * Returns : -1 if selection could not execute for some reason,
- + * 0 if selection succeeded or failed because the target
- + * did not respond.
- + *
- + * Side effects :
- + * If bus busy, arbitration failed, etc, NCR5380_select() will exit
- + * with registers as they should have been on entry - ie
- + * SELECT_ENABLE will be set appropriately, the NCR5380
- + * will cease to drive any SCSI bus signals.
- + *
- + * If successful : I_T_L or I_T_L_Q nexus will be established,
- + * instance->connected will be set to cmd.
- + * SELECT interrupt will be disabled.
- + *
- + * If failed (no target) : cmd->scsi_done() will be called, and the
- + * cmd->result host byte set to DID_BAD_TARGET.
- + */
- +
- +static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
- + int tag) {
- + NCR5380_local_declare();
- + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata*)
- + instance->hostdata;
- + unsigned char tmp[3], phase;
- + unsigned char *data;
- + int len;
- + unsigned long timeout;
- + NCR5380_setup(instance);
- +
- + hostdata->restart_select = 0;
- +#if defined (NDEBUG) && (NDEBUG & NDEBUG_ARBITRATION)
- + NCR5380_print(instance);
- + printk("scsi%d: starting arbitration, id = %d\n", instance->host_no,
- + instance->this_id);
- +#endif
- + cli();
- +
- + /*
- + * Set the phase bits to 0, otherwise the NCR5380 won't drive the
- + * data bus during SELECTION.
- + */
- +
- + NCR5380_write(TARGET_COMMAND_REG, 0);
- +
- +
- + /*
- + * Start arbitration.
- + */
- +
- + NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
- + NCR5380_write(MODE_REG, MR_ARBITRATE);
- +
- + sti();
- +
- + /* Wait for arbitration logic to complete */
- +#if NCR_TIMEOUT
- + {
- + unsigned long timeout = jiffies + 2*NCR_TIMEOUT;
- +
- + while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
- + && jiffies < timeout)
- + ;
- + if (jiffies >= timeout)
- + {
- + printk("scsi: arbitration timeout at %d\n", __LINE__);
- + NCR5380_write(MODE_REG, MR_BASE);
- + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- + return -1;
- + }
- + }
- +#else /* NCR_TIMEOUT */
- + while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS));
- +#endif
- +
- +#if (NDEBUG & NDEBUG_ARBITRATION)
- + printk("scsi%d: arbitration complete\n", instance->host_no);
- +/* Avoid GCC 2.4.5 asm needs to many reloads error */
- + __asm__("nop");
- +#endif
- +
- + /*
- + * The arbitration delay is 2.2us, but this is a minimum and there is
- + * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate
- + * the integral nature of udelay().
- + *
- + */
- +
- + udelay(3);
- +
- + /* Check for lost arbitration */
- + if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
- + (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) ||
- + (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST)) {
- + NCR5380_write(MODE_REG, MR_BASE);
- +#if (NDEBUG & NDEBUG_ARBITRATION)
- + printk("scsi%d: lost arbitration, deasserting MR_ARBITRATE\n",
- + instance->host_no);
- +#endif
- + return -1;
- + }
- +
- +
- +
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL);
- +
- + if (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) {
- + NCR5380_write(MODE_REG, MR_BASE);
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- +#if (NDEBUG & NDEBUG_ARBITRATION)
- + printk("scsi%d: lost arbitration, deasserting ICR_ASSERT_SEL\n",
- + instance->host_no);
- +#endif
- + return -1;
- + }
- +
- + /*
- + * Again, bus clear + bus settle time is 1.2us, however, this is
- + * a minimum so we'll udelay ceil(1.2)
- + */
- +
- + udelay(2);
- +
- +#if (NDEBUG & NDEBUG_ARBITRATION)
- + printk("scsi%d: won arbitration\n", instance->host_no);
- +#endif
- +
- +
- + /*
- + * Now that we have won arbitration, start Selection process, asserting
- + * the host and target ID's on the SCSI bus.
- + */
- +
- + NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->target)));
- +
- + /*
- + * Raise ATN while SEL is true before BSY goes false from arbitration,
- + * since this is the only way to guarantee that we'll get a MESSAGE OUT
- + * phase immediately after selection.
- + */
- +
- + NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY |
- + ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL ));
- + NCR5380_write(MODE_REG, MR_BASE);
- +
- + /*
- + * Reselect interrupts must be turned off prior to the dropping of BSY,
- + * otherwise we will trigger an interrupt.
- + */
- + NCR5380_write(SELECT_ENABLE_REG, 0);
- +
- + /* Reset BSY */
- + NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA |
- + ICR_ASSERT_ATN | ICR_ASSERT_SEL));
- +
- + /*
- + * Something weird happens when we cease to drive BSY - looks
- + * like the board/chip is letting us do another read before the
- + * appropriate propagation delay has expired, and we're confusing
- + * a BSY signal from ourselves as the target's response to SELECTION.
- + *
- + * A small delay (the 'C++' frontend breaks the pipeline with an
- + * unnecessary jump, making it work on my 386-33/Trantor T128, the
- + * tighter 'C' code breaks and requires this) solves the problem -
- + * the 1 us delay is arbitrary, and only used because this delay will
- + * be the same on other platforms and since it works here, it should
- + * work there.
- + */
- +
- + udelay(1);
- +
- +#if (NDEBUG & NDEBUG_SELECTION)
- + printk("scsi%d: selecting target %d\n", instance->host_no, cmd->target);
- +#endif
- +
- + /*
- + * The SCSI specification calls for a 250 ms timeout for the actual
- + * selection.
- + */
- +
- + timeout = jiffies + 25;
- +
- + /*
- + * XXX very interesting - we're seeing a bounce where the BSY we
- + * asserted is being reflected / still asserted (propagation delay?)
- + * and it's detecting as true. Sigh.
- + */
- +
- + while ((jiffies < timeout) && !(NCR5380_read(STATUS_REG) &
- + (SR_BSY | SR_IO)));
- +
- + if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) ==
- + (SR_SEL | SR_IO)) {
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- + NCR5380_reselect(instance);
- + printk ("scsi%d: reselection after won arbitration?\n",
- + instance->host_no);
- + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- + return -1;
- + }
- +
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
- +
- + if (!(NCR5380_read(STATUS_REG) & SR_BSY)) {
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- + if (hostdata->targets_present & (1 << cmd->target)) {
- + printk("scsi%d: weirdness\n", instance->host_no);
- + if (hostdata->restart_select)
- + printk("\trestart select\n");
- +#ifdef NDEBUG
- + NCR5380_print (instance);
- +#endif
- + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- + return -1;
- + }
- + cmd->result = DID_BAD_TARGET << 16;
- + cmd->scsi_done(cmd);
- + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- +#if (NDEBUG & NDEBUG_SELECTION)
- + printk("scsi%d: target did not respond within 250ms\n",
- + instance->host_no);
- +#endif
- + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- + return 0;
- + }
- +
- + hostdata->targets_present |= (1 << cmd->target);
- +
- + /*
- + * Since we followed the SCSI spec, and raised ATN while SEL
- + * was true but before BSY was false during selection, the information
- + * transfer phase should be a MESSAGE OUT phase so that we can send the
- + * IDENTIFY message.
- + *
- + * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG
- + * message (2 bytes) with a tag ID that we increment with every command
- + * until it wraps back to 0.
- + *
- + * XXX - it turns out that there are some broken SCSI-II devices,
- + * which claim to support tagged queuing but fail when more than
- + * some number of commands are issued at once.
- + */
- +
- + /* Wait for start of REQ/ACK handshake */
- + while (!(NCR5380_read(STATUS_REG) & SR_REQ));
- +
- +#if (NDEBUG & NDEBUG_SELECTION)
- + printk("scsi%d: target %d selected, going into MESSAGE OUT phase.\n",
- + instance->host_no, cmd->target);
- +#endif
- + tmp[0] = IDENTIFY(((instance->irq == IRQ_NONE) ? 0 : 1), cmd->lun);
- +#ifdef SCSI2
- + if (cmd->device->tagged_queue && (tag != TAG_NONE)) {
- + tmp[1] = SIMPLE_QUEUE_TAG;
- + if (tag == TAG_NEXT) {
- + /* 0 is TAG_NONE, used to imply no tag for this command */
- + if (cmd->device->current_tag == 0)
- + cmd->device->current_tag = 1;
- +
- + cmd->tag = cmd->device->current_tag;
- + cmd->device->current_tag++;
- + } else
- + cmd->tag = (unsigned char) tag;
- +
- + tmp[2] = cmd->tag;
- + hostdata->last_message = SIMPLE_QUEUE_TAG;
- + len = 3;
- + } else
- +#endif /* def SCSI2 */
- + {
- + len = 1;
- + cmd->tag=0;
- + }
- +
- + /* Send message(s) */
- + data = tmp;
- + phase = PHASE_MSGOUT;
- + NCR5380_transfer_pio(instance, &phase, &len, &data);
- +#if (NDEBUG & NDEBUG_SELECTION)
- + printk("scsi%d: nexus established.\n", instance->host_no);
- +#endif
- + /* XXX need to handle errors here */
- + hostdata->connected = cmd;
- +#ifdef SCSI2
- + if (!cmd->device->tagged_queue)
- +#endif
- + hostdata->busy[cmd->target] |= (1 << cmd->lun);
- +
- + initialize_SCp(cmd);
- +
- +
- + return 0;
- +}
- +
- +/*
- + * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance,
- + * unsigned char *phase, int *count, unsigned char **data)
- + *
- + * Purpose : transfers data in given phase using polled I/O
- + *
- + * Inputs : instance - instance of driver, *phase - pointer to
- + * what phase is expected, *count - pointer to number of
- + * bytes to transfer, **data - pointer to data pointer.
- + *
- + * Returns : -1 when different phase is entered without transferring
- + * maximum number of bytes, 0 if all bytes or transfered or exit
- + * is in same phase.
- + *
- + * Also, *phase, *count, *data are modified in place.
- + *
- + * XXX Note : handling for bus free may be useful.
- + */
- +
- +/*
- + * Note : this code is not as quick as it could be, however it
- + * IS 100% reliable, and for the actual data transfer where speed
- + * counts, we will always do a pseudo DMA or DMA transfer.
- + */
- +
- +static int NCR5380_transfer_pio (struct Scsi_Host *instance,
- + unsigned char *phase, int *count, unsigned char **data) {
- + NCR5380_local_declare();
- + register unsigned char p = *phase, tmp;
- + register int c = *count;
- + register unsigned char *d = *data;
- + NCR5380_setup(instance);
- +
- + /*
- + * The NCR5380 chip will only drive the SCSI bus when the
- + * phase specified in the appropriate bits of the TARGET COMMAND
- + * REGISTER match the STATUS REGISTER
- + */
- +
- + NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
- +
- + do {
- + /*
- + * Wait for assertion of REQ, after which the phase bits will be
- + * valid
- + */
- + while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ));
- +
- +#if (NDEBUG & NDEBUG_HANDSHAKE)
- + printk("scsi%d: REQ detected\n", instance->host_no);
- +#endif
- +
- + /* Check for phase mismatch */
- + if ((tmp & PHASE_MASK) != p) {
- +#if (NDEBUG & NDEBUG_PIO)
- + printk("scsi%d: phase mismatch\n", instance->host_no);
- + NCR5380_print_phase(instance);
- +#endif
- + break;
- + }
- +
- + /* Do actual transfer from SCSI bus to / from memory */
- + if (!(p & SR_IO))
- + NCR5380_write(OUTPUT_DATA_REG, *d);
- + else
- + *d = NCR5380_read(CURRENT_SCSI_DATA_REG);
- +
- + ++d;
- +
- + /*
- + * The SCSI standard suggests that in MSGOUT phase, the initiator
- + * should drop ATN on the last byte of the message phase
- + * after REQ has been asserted for the handshake but before
- + * the initiator raises ACK.
- + */
- +
- + if (!(p & SR_IO)) {
- + if (!((p & SR_MSG) && c > 1)) {
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- + ICR_ASSERT_DATA);
- +#if (NDEBUG & NDEBUG_PIO)
- + NCR5380_print(instance);
- +#endif
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- + ICR_ASSERT_DATA | ICR_ASSERT_ACK);
- + } else {
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- + ICR_ASSERT_DATA | ICR_ASSERT_ATN);
- +#if (NDEBUG & NDEBUG_PIO)
- + NCR5380_print(instance);
- +#endif
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- + ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
- + }
- + } else {
- +#if (NDEBUG & NDEBUG_PIO)
- + NCR5380_print(instance);
- +#endif
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
- + }
- +
- + while (NCR5380_read(STATUS_REG) & SR_REQ);
- +
- +#if (NDEBUG & NDEBUG_HANDSHAKE)
- + printk("scsi%d: req false, handshake complete\n", instance->host_no);
- +#endif
- +
- +/*
- + * We have several special cases to consider during REQ/ACK handshaking :
- + * 1. We were in MSGOUT phase, and we are on the last byte of the
- + * message. ATN must be dropped as ACK is dropped.
- + *
- + * 2. We are in a MSGIN phase, and we are on the last byte of the
- + * message. We must exit with ACK asserted, so that the calling
- + * code may raise ATN before dropping ACK to reject the message.
- + *
- + * 3. ACK and ATN are clear and the target may proceed as normal.
- + */
- + if (!(p == PHASE_MSGIN && c == 1)) {
- + if (p == PHASE_MSGOUT && c > 1)
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
- + else
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- + }
- + } while (--c);
- +
- +#if (NDEBUG & NDEBUG_PIO)
- + printk("scsi%d: residual %d\n", instance->host_no, c);
- +#endif
- +
- + *count = c;
- + *data = d;
- + tmp = NCR5380_read(STATUS_REG);
- + if (tmp & SR_REQ)
- + *phase = tmp & PHASE_MASK;
- + else
- + *phase = PHASE_UNKNOWN;
- +
- + if (!c || (*phase == p))
- + return 0;
- + else
- + return -1;
- +}
- +
- +#if defined(REAL_DMA) || defined(PSEUDO_DMA) || defined (REAL_DMA_POLL)
- +/*
- + * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance,
- + * unsigned char *phase, int *count, unsigned char **data)
- + *
- + * Purpose : transfers data in given phase using either real
- + * or pseudo DMA.
- + *
- + * Inputs : instance - instance of driver, *phase - pointer to
- + * what phase is expected, *count - pointer to number of
- + * bytes to transfer, **data - pointer to data pointer.
- + *
- + * Returns : -1 when different phase is entered without transferring
- + * maximum number of bytes, 0 if all bytes or transfered or exit
- + * is in same phase.
- + *
- + * Also, *phase, *count, *data are modified in place.
- + *
- + */
- +
- +
- +static int NCR5380_transfer_dma (struct Scsi_Host *instance,
- + unsigned char *phase, int *count, unsigned char **data) {
- + NCR5380_local_declare();
- + register int c = *count;
- + register unsigned char p = *phase;
- + register unsigned char *d = *data;
- + unsigned char tmp;
- + int foo;
- +#if defined(REAL_DMA_POLL)
- + int cnt, toPIO;
- + unsigned char saved_data = 0, overrun = 0, residue;
- +#endif
- +
- + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
- + instance->hostdata;
- +
- + NCR5380_setup(instance);
- +
- + if ((tmp = (NCR5380_read(STATUS_REG) & PHASE_MASK)) != p) {
- + *phase = tmp;
- + return -1;
- + }
- +#if defined(REAL_DMA) || defined(REAL_DMA_POLL)
- +#ifdef READ_OVERRUNS
- + if (p & SR_IO) {
- + c -= 2;
- + }
- +#endif
- +#if (NDEBUG & NDEBUG_DMA)
- + printk("scsi%d: initializing DMA channel %d for %s, %d bytes %s %0x\n",
- + instance->host_no, instance->dma_channel, (p & SR_IO) ? "reading" :
- + "writing", c, (p & SR_IO) ? "to" : "from", (unsigned) d);
- +#endif
- + hostdata->dma_len = (p & SR_IO) ?
- + NCR5380_dma_read_setup(instance, d, c) :
- + NCR5380_dma_write_setup(instance, d, c);
- +#endif
- +
- + NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
- +
- +#ifdef REAL_DMA
- + NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
- +#elif defined(REAL_DMA_POLL)
- + NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
- +#else
- + /*
- + * Note : on my sample board, watch-dog timeouts occurred when interrupts
- + * were not disabled for the duration of a single DMA transfer, from
- + * before the setting of DMA mode to after transfer of the last byte.
- + */
- +
- +#if defined(PSEUDO_DMA) && !defined(UNSAFE)
- + cli();
- +#endif
- + NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
- +#endif /* def REAL_DMA */
- +
- +#if (NDEBUG & NDEBUG_DMA) & 0
- + printk("scsi%d: mode reg = 0x%X\n", instance->host_no, NCR5380_read(MODE_REG));
- +#endif
- +
- +/*
- + * FOO stuff. For some UNAPPARENT reason, I'm getting
- + * watchdog timers fired on bootup for NO APPARENT REASON, meaning it's
- + * probably a timing problem.
- + *
- + * Since this is the only place I have back-to-back writes, perhaps this
- + * is the problem?
- + */
- +
- + if (p & SR_IO) {
- +#ifndef FOO
- + udelay(1);
- +#endif
- + NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
- + } else {
- +#ifndef FOO
- + udelay(1);
- +#endif
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA);
- +#ifndef FOO
- + udelay(1);
- +#endif
- + NCR5380_write(START_DMA_SEND_REG, 0);
- +#ifndef FOO
- + udelay(1);
- +#endif
- + }
- +
- +#if defined(REAL_DMA_POLL)
- + do {
- + tmp = NCR5380_read(BUS_AND_STATUS_REG);
- + } while ((tmp & BASR_PHASE_MATCH) && !(tmp & (BASR_BUSY_ERROR |
- + BASR_END_DMA_TRANSFER)));
- +
- +/*
- + At this point, either we've completed DMA, or we have a phase mismatch,
- + or we've unexpectedly lost BUSY (which is a real error).
- +
- + For write DMAs, we want to wait until the last byte has been
- + transferred out over the bus before we turn off DMA mode. Alas, there
- + seems to be no terribly good way of doing this on a 5380 under all
- + conditions. For non-scatter-gather operations, we can wait until REQ
- + and ACK both go false, or until a phase mismatch occurs. Gather-writes
- + are nastier, since the device will be expecting more data than we
- + are prepared to send it, and REQ will remain asserted. On a 53C8[01] we
- + could test LAST BIT SENT to assure transfer (I imagine this is precisely
- + why this signal was added to the newer chips) but on the older 538[01]
- + this signal does not exist. The workaround for this lack is a watchdog;
- + we bail out of the wait-loop after a modest amount of wait-time if
- + the usual exit conditions are not met. Not a terribly clean or
- + correct solution :-%
- +
- + Reads are equally tricky due to a nasty characteristic of the NCR5380.
- + If the chip is in DMA mode for an READ, it will respond to a target's
- + REQ by latching the SCSI data into the INPUT DATA register and asserting
- + ACK, even if it has _already_ been notified by the DMA controller that
- + the current DMA transfer has completed! If the NCR5380 is then taken
- + out of DMA mode, this already-acknowledged byte is lost.
- +
- + This is not a problem for "one DMA transfer per command" reads, because
- + the situation will never arise... either all of the data is DMA'ed
- + properly, or the target switches to MESSAGE IN phase to signal a
- + disconnection (either operation bringing the DMA to a clean halt).
- + However, in order to handle scatter-reads, we must work around the
- + problem. The chosen fix is to DMA N-2 bytes, then check for the
- + condition before taking the NCR5380 out of DMA mode. One or two extra
- + bytes are transferred via PIO as necessary to fill out the original
- + request.
- +*/
- +
- + if (p & SR_IO) {
- +#ifdef READ_OVERRUNS
- + udelay(10);
- + if (((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH|BASR_ACK)) ==
- + (BASR_PHASE_MATCH | BASR_ACK))) {
- + saved_data = NCR5380_read(INPUT_DATA_REGISTER);
- + overrun = 1;
- + }
- +#endif
- + } else {
- + int limit = 100;
- + while (((tmp = NCR5380_read(BUS_AND_STATUS_REG)) & BASR_ACK) ||
- + (NCR5380_read(STATUS_REG) & SR_REQ)) {
- + if (!(tmp & BASR_PHASE_MATCH)) break;
- + if (--limit < 0) break;
- + }
- + }
- +
- +
- +#if (NDEBUG & NDEBUG_DMA)
- + printk("scsi%d: polled DMA transfer complete, basr 0x%X, sr 0x%X\n",
- + instance->host_no, tmp, NCR5380_read(STATUS_REG));
- +#endif
- +
- + NCR5380_write(MODE_REG, MR_BASE);
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- +
- + residue = NCR5380_dma_residual(instance);
- + c -= residue;
- + *count -= c;
- + *data += c;
- + *phase = NCR5380_read(STATUS_REG) & PHASE_MASK;
- +
- +#ifdef READ_OVERRUNS
- + if (*phase == p && (p & SR_IO) && residue == 0) {
- + if (overrun) {
- +#if (NDEBUG & NDEBUG_DMA)
- + printk("Got an input overrun, using saved byte\n");
- +#endif
- + **data = saved_data;
- + *data += 1;
- + *count -= 1;
- + cnt = toPIO = 1;
- + } else {
- + printk("No overrun??\n");
- + cnt = toPIO = 2;
- + }
- +#if (NDEBUG & NDEBUG_DMA)
- + printk("Doing %d-byte PIO to 0x%X\n", cnt, *data);
- +#endif
- + NCR5380_transfer_pio(instance, phase, &cnt, data);
- + *count -= toPIO - cnt;
- + }
- +#endif
- +
- +#if (NDEBUG & NDEBUG_DMA)
- + printk("Return with data ptr = 0x%X, count %d, last 0x%X, next 0x%X\n",
- + *data, *count, *(*data+*count-1), *(*data+*count));
- +#endif
- + return 0;
- +
- +#elif defined(REAL_DMA)
- + return 0;
- +#else /* defined(REAL_DMA_POLL) */
- + if (p & SR_IO) {
- +#if 0
- + if (!(foo = NCR5380_pread(instance, d, c - 1))) {
- + /*
- + * We can't disable DMA mode after successfully transferring
- + * what we plan to be the last byte, since that would open up
- + * a race condition where if the target asserted REQ before
- + * we got the DMA mode reset, the NCR5380 would have latched
- + * an additional byte into the INPUT DATA register and we'd
- + * have dropped it.
- + *
- + * The workaround was to transfer one fewer bytes than we
- + * intended to with the pseudo-DMA read function, wait for
- + * the chip to latch the last byte, read it, and then disable
- + * pseudo-DMA mode.
- + *
- + * After REQ is asserted, the NCR5380 asserts DRQ and ACK.
- + * REQ is deasserted when ACK is asserted, and not reasserted
- + * until ACK goes false. Since the NCR5380 won't lower ACK
- + * until DACK is asserted, which won't happen unless we twiddle
- + * the DMA port or we take the NCR5380 out of DMA mode, we
- + * can guarantee that we won't handshake another extra
- + * byte.
- + */
- +
- + while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
- + /* Wait for clean handshake */
- + while (NCR5380_read(STATUS_REG) & SR_REQ);
- + d[c - 1] = NCR5380_read(INPUT_DATA_REG);
- + }
- +#else
- + if (!(foo = NCR5380_pread(instance, d, c - 2))) {
- + while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
- + while (NCR5380_read(STATUS_REG) & SR_REQ);
- + d[c - 2] = NCR5380_read(INPUT_DATA_REG);
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
- + while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ACK);
- + while (NCR5380_read(STATUS_REG) & SR_REQ);
- + d[c - 2] = NCR5380_read(INPUT_DATA_REG);
- + }
- +#endif
- + } else {
- + int timeout;
- + if (!(foo = NCR5380_pwrite(instance, d, c))) {
- + /*
- + * Wait for the last byte to be sent. If REQ is being asserted for
- + * the byte we're interested, we'll ACK it and it will go false.
- + */
- + if (!(hostdata->flags & FLAG_HAS_LAST_BYTE_SENT)) {
- + timeout = 20000;
- +#if 1
- +#if 1
- + while (!(NCR5380_read(BUS_AND_STATUS_REG) &
- + BASR_DRQ) && (NCR5380_read(BUS_AND_STATUS_REG) &
- + BASR_PHASE_MATCH));
- +#else
- + if (NCR5380_read(STATUS_REG) & SR_REQ) {
- + for (; timeout &&
- + !(NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK);
- + --timeout);
- + for (; timeout && (NCR5380_read(STATUS_REG) & SR_REQ);
- + --timeout);
- + }
- +#endif
- +
- +
- +#if (NDEBUG & NDEBUG_LAST_BYTE_SENT)
- + if (!timeout)
- + printk("scsi%d: timed out on last byte\n",
- + instance->host_no);
- +#endif
- +
- +
- + if (hostdata->flags & FLAG_CHECK_LAST_BYTE_SENT) {
- + hostdata->flags &= ~FLAG_CHECK_LAST_BYTE_SENT;
- + if (NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT) {
- + hostdata->flags |= FLAG_HAS_LAST_BYTE_SENT;
- +#if (NDEBUG & NDEBUG_LAST_BYTE_SENT)
- + printk("scsi%d: last bit sent works\n",
- + instance->host_no);
- +#endif
- + }
- + }
- + } else
- + while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT));
- +#else
- + udelay (5);
- +#endif
- + }
- + }
- +
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- + NCR5380_write(MODE_REG, MR_BASE);
- +
- + *data = d + c;
- + *count = 0;
- + *phase = (NCR5380_read(STATUS_REG & PHASE_MASK));
- +#if defined(PSEUDO_DMA) && !defined(UNSAFE)
- + sti();
- +#endif /* defined(REAL_DMA_POLL) */
- + return foo;
- +#endif /* def REAL_DMA */
- +}
- +#endif /* defined(REAL_DMA) | defined(PSEUDO_DMA) */
- +
- +/*
- + * Function : NCR5380_information_transfer (struct Scsi_Host *instance)
- + *
- + * Purpose : run through the various SCSI phases and do as the target
- + * directs us to. Operates on the currently connected command,
- + * instance->connected.
- + *
- + * Inputs : instance, instance for which we are doing commands
- + *
- + * Side effects : SCSI things happen, the disconnected queue will be
- + * modified if a command disconnects, *instance->connected will
- + * change.
- + *
- + * XXX Note : we need to watch for bus free or a reset condition here
- + * to recover from an unexpected bus free condition.
- + */
- +
- +static void NCR5380_information_transfer (struct Scsi_Host *instance) {
- + NCR5380_local_declare();
- + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
- + instance->hostdata;
- + unsigned char msgout = NOP;
- + int sink = 0;
- + int len;
- +#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
- + int transfersize;
- +#endif
- + unsigned char *data;
- + unsigned char phase, tmp, extended_msg[10], old_phase=0xff;
- + Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
- + NCR5380_setup(instance);
- +
- + while (1) {
- + tmp = NCR5380_read(STATUS_REG);
- + /* We only have a valid SCSI phase when REQ is asserted */
- + if (tmp & SR_REQ) {
- + phase = (tmp & PHASE_MASK);
- + if (phase != old_phase) {
- + old_phase = phase;
- +#if (NDEBUG & NDEBUG_INFORMATION)
- + NCR5380_print_phase(instance);
- +#endif
- + }
- +
- + if (sink && (phase != PHASE_MSGOUT)) {
- + NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
- +
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
- + ICR_ASSERT_ACK);
- + while (NCR5380_read(STATUS_REG) & SR_REQ);
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- + ICR_ASSERT_ATN);
- + sink = 0;
- + continue;
- + }
- +
- + switch (phase) {
- + case PHASE_DATAIN:
- + case PHASE_DATAOUT:
- +#if (NDEBUG & NDEBUG_NO_DATAOUT)
- + printk("scsi%d: NDEBUG_NO_DATAOUT set, attempted DATAOUT aborted\n",
- + instance->host_no);
- + sink = 1;
- + msgout = ABORT;
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
- + break;
- +#endif
- + /*
- + * If there is no room left in the current buffer in the
- + * scatter-gather list, move onto the next one.
- + */
- +
- + if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
- + ++cmd->SCp.buffer;
- + --cmd->SCp.buffers_residual;
- + cmd->SCp.this_residual = cmd->SCp.buffer->length;
- + cmd->SCp.ptr = cmd->SCp.buffer->address;
- +#if (NDEBUG & NDEBUG_INFORMATION)
- + printk("scsi%d: %d bytes and %d buffers left\n",
- + instance->host_no, cmd->SCp.this_residual,
- + cmd->SCp.buffers_residual);
- +#endif
- + }
- +
- + /*
- + * The preferred transfer method is going to be
- + * PSEUDO-DMA for systems that are strictly PIO,
- + * since we can let the hardware do the handshaking.
- + *
- + * For this to work, we need to know the transfersize
- + * ahead of time, since the pseudo-DMA code will sit
- + * in an unconditional loop.
- + */
- +
- +#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
- +#ifdef NCR5380_dma_xfer_len
- + if (!cmd->device->borken &&
- + (transfersize = NCR5380_dma_xfer_len(instance, cmd)) != 0) {
- +#else
- + transfersize = cmd->transfersize;
- +
- +#ifdef LIMIT_TRANSFERSIZE /* If we have problems with interrupt service */
- + if( transfersize > 512 )
- + transfersize = 512;
- +#endif /* LIMIT_TRANSFERSIZE */
- +
- + if (!cmd->device->borken && transfersize &&
- + cmd->SCp.this_residual && !(cmd->SCp.this_residual %
- + transfersize)) {
- +#endif
- + len = transfersize;
- + if (NCR5380_transfer_dma(instance, &phase,
- + &len, (unsigned char **) &cmd->SCp.ptr)) {
- + /*
- + * If the watchdog timer fires, all future accesses to this
- + * device will use the polled-IO.
- + */
- + printk("scsi%d: switching target %d lun %d to slow handshake\n",
- + instance->host_no, cmd->target, cmd->lun);
- + cmd->device->borken = 1;
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- + ICR_ASSERT_ATN);
- + sink = 1;
- + msgout = ABORT;
- + /* XXX - need to source or sink data here, as appropriate */
- + } else
- + cmd->SCp.this_residual -= transfersize - len;
- + } else
- +#endif /* defined(PSEUDO_DMA) || defined(REAL_DMA_POLL) */
- + NCR5380_transfer_pio(instance, &phase,
- + (int *) &cmd->SCp.this_residual, (unsigned char **)
- + &cmd->SCp.ptr);
- + break;
- + case PHASE_MSGIN:
- + len = 1;
- + data = &tmp;
- + NCR5380_transfer_pio(instance, &phase, &len, &data);
- + cmd->SCp.Message = tmp;
- +
- + switch (tmp) {
- + /*
- + * Linking lets us reduce the time required to get the
- + * next command out to the device, hopefully this will
- + * mean we don't waste another revolution due to the delays
- + * required by ARBITRATION and another SELECTION.
- + *
- + * In the current implementation proposal, low level drivers
- + * merely have to start the next command, pointed to by
- + * next_link, done() is called as with unlinked commands.
- + */
- +#ifdef LINKED
- + case LINKED_CMD_COMPLETE:
- + case LINKED_FLG_CMD_COMPLETE:
- + /* Accept message by clearing ACK */
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- +
- +#if (NDEBUG & NDEBUG_LINKED)
- + printk("scsi%d: target %d lun %d linked command complete.\n",
- + instance->host_no, cmd->target, cmd->lun);
- +#endif
- + /*
- + * Sanity check : A linked command should only terminate with
- + * one of these messages if there are more linked commands
- + * available.
- + */
- +
- + if (!cmd->next_link) {
- + printk("scsi%d: target %d lun %d linked command complete, no next_link\n"
- + instance->host_no, cmd->target, cmd->lun);
- + sink = 1;
- + msgout = ABORT;
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- + ICR_ASSERT_ATN);
- + break;
- + }
- +
- + initialize_SCp(cmd->next_link);
- + /* The next command is still part of this process */
- + cmd->next_link->tag = cmd->tag;
- + cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
- +#if (NDEBUG & NDEBUG_LINKED)
- + printk("scsi%d: target %d lun %d linked request done, calling scsi_done().\n",
- + instance->host_no, cmd->target, cmd->lun);
- +#endif
- + cmd->scsi_done(cmd);
- + cmd = hostdata->connected;
- + break;
- +#endif /* def LINKED */
- + case ABORT:
- + case COMMAND_COMPLETE:
- + /* Accept message by clearing ACK */
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- + hostdata->connected = NULL;
- +#if (NDEBUG & NDEBUG_QUEUES)
- + printk("scsi%d: command for target %d, lun %d completed\n",
- + instance->host_no, cmd->target, cmd->lun);
- +#endif
- + hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
- +
- + /*
- + * I'm not sure what the correct thing to do here is :
- + *
- + * If the command that just executed is NOT a request
- + * sense, the obvious thing to do is to set the result
- + * code to the values of the stored parameters.
- + *
- + * If it was a REQUEST SENSE command, we need some way
- + * to differentiate between the failure code of the original
- + * and the failure code of the REQUEST sense - the obvious
- + * case is success, where we fall through and leave the result
- + * code unchanged.
- + *
- + * The non-obvious place is where the REQUEST SENSE failed
- + */
- +
- + if (cmd->cmnd[0] != REQUEST_SENSE)
- + cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
- + else if (cmd->SCp.Status != GOOD)
- + cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
- +
- +#ifdef AUTOSENSE
- + if ((cmd->cmnd[0] != REQUEST_SENSE) &&
- + (cmd->SCp.Status == CHECK_CONDITION)) {
- +#if (NDEBUG & NDEBUG_AUTOSENSE)
- + printk("scsi%d: performing request sense\n",
- + instance->host_no);
- +#endif
- + cmd->cmnd[0] = REQUEST_SENSE;
- + cmd->cmnd[1] &= 0xe0;
- + cmd->cmnd[2] = 0;
- + cmd->cmnd[3] = 0;
- + cmd->cmnd[4] = sizeof(cmd->sense_buffer);
- + cmd->cmnd[5] = 0;
- +
- + cmd->SCp.buffer = NULL;
- + cmd->SCp.buffers_residual = 0;
- + cmd->SCp.ptr = (char *) cmd->sense_buffer;
- + cmd->SCp.this_residual = sizeof(cmd->sense_buffer);
- +
- + cli();
- + cmd->host_scribble = (unsigned char *)
- + hostdata->issue_queue;
- + hostdata->issue_queue = (Scsi_Cmnd *) cmd;
- + sti();
- +#if (NDEBUG & NDEBUG_QUEUES)
- + printk("scsi%d: REQUEST SENSE added to head of issue queue\n");
- +#endif
- + } else
- +#endif /* def AUTOSENSE */
- + cmd->scsi_done(cmd);
- +
- + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- + /*
- + * Restore phase bits to 0 so an interrupted selection,
- + * arbitration can resume.
- + */
- + NCR5380_write(TARGET_COMMAND_REG, 0);
- +
- + while ((NCR5380_read(STATUS_REG) & SR_BSY) &&
- + !hostdata->connected);
- + return;
- + case MESSAGE_REJECT:
- + /* Accept message by clearing ACK */
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- + switch (hostdata->last_message) {
- + case HEAD_OF_QUEUE_TAG:
- + case ORDERED_QUEUE_TAG:
- + case SIMPLE_QUEUE_TAG:
- + cmd->device->tagged_queue = 0;
- + hostdata->busy[cmd->target] |= (1 << cmd->lun);
- + break;
- + default:
- + break;
- + }
- + case DISCONNECT:
- + /* Accept message by clearing ACK */
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- + cmd->device->disconnect = 1;
- + cli();
- + cmd->host_scribble = (unsigned char *)
- + hostdata->disconnected_queue;
- + hostdata->connected = NULL;
- + hostdata->disconnected_queue = cmd;
- + sti();
- +#if (NDEBUG & NDEBUG_QUEUES)
- + printk("scsi%d: command for target %d lun %d was moved from connected to"
- + " the disconnected_queue\n", instance->host_no,
- + cmd->target, cmd->lun);
- +#endif
- + /*
- + * Restore phase bits to 0 so an interrupted selection,
- + * arbitration can resume.
- + */
- + NCR5380_write(TARGET_COMMAND_REG, 0);
- +
- + /* Enable reselect interrupts */
- + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- + /* Wait for bus free to avoid nasty timeouts */
- + while ((NCR5380_read(STATUS_REG) & SR_BSY) &&
- + !hostdata->connected);
- + return;
- + /*
- + * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
- + * operation, in violation of the SCSI spec so we can safely
- + * ignore SAVE/RESTORE pointers calls.
- + *
- + * Unfortunately, some disks violate the SCSI spec and
- + * don't issue the required SAVE_POINTERS message before
- + * disconnecting, and we have to break spec to remain
- + * compatible.
- + */
- + case SAVE_POINTERS:
- + case RESTORE_POINTERS:
- + /* Accept message by clearing ACK */
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- + break;
- + case EXTENDED_MESSAGE:
- +/*
- + * Extended messages are sent in the following format :
- + * Byte
- + * 0 EXTENDED_MESSAGE == 1
- + * 1 length (includes one byte for code, doesn't
- + * include first two bytes)
- + * 2 code
- + * 3..length+1 arguments
- + *
- + * Start the extended message buffer with the EXTENDED_MESSAGE
- + * byte, since print_msg() wants the whole thing.
- + */
- + extended_msg[0] = EXTENDED_MESSAGE;
- + /* Accept first byte by clearing ACK */
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- +
- +#if (NDEBUG & NDEBUG_EXTENDED)
- + printk("scsi%d: receiving extended message\n",
- + instance->host_no);
- +#endif
- +
- + len = 2;
- + data = extended_msg + 1;
- + phase = PHASE_MSGIN;
- + NCR5380_transfer_pio(instance, &phase, &len, &data);
- +
- +#if (NDEBUG & NDEBUG_EXTENDED)
- + printk("scsi%d: length=%d, code=0x%02x\n",
- + instance->host_no, (int) extended_msg[1],
- + (int) extended_msg[2]);
- +#endif
- +
- + if (!len && extended_msg[1] <=
- + (sizeof (extended_msg) - 1)) {
- + /* Accept third byte by clearing ACK */
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- + len = extended_msg[1] - 1;
- + data = extended_msg + 3;
- + phase = PHASE_MSGIN;
- +
- + NCR5380_transfer_pio(instance, &phase, &len, &data);
- +
- +#if (NDEBUG & NDEBUG_EXTENDED)
- + printk("scsi%d: message received, residual %d\n",
- + instance->host_no, len);
- +#endif
- +
- + switch (extended_msg[2]) {
- + case EXTENDED_SDTR:
- + case EXTENDED_WDTR:
- + case EXTENDED_MODIFY_DATA_POINTER:
- + case EXTENDED_EXTENDED_IDENTIFY:
- + tmp = 0;
- + }
- + } else if (len) {
- + printk("scsi%d: error receiving extended message\n",
- + instance->host_no);
- + tmp = 0;
- + } else {
- + printk("scsi%d: extended message code %02x length %d is too long\n",
- + instance->host_no, extended_msg[2], extended_msg[1]);
- + tmp = 0;
- + }
- + /* Fall through to reject message */
- +
- + /*
- + * If we get something weird that we aren't expecting,
- + * reject it.
- + */
- + default:
- + if (!tmp) {
- + printk("scsi%d: rejecting message ", instance->host_no);
- + print_msg (extended_msg);
- + printk("\n");
- + } else if (tmp != EXTENDED_MESSAGE)
- + printk("scsi%d: rejecting unknown message %02x from target %d, lun %d\n",
- + instance->host_no, tmp, cmd->target, cmd->lun);
- + else
- + printk("scsi%d: rejecting unknown extended message code %02x, length %d from target %d, lun %d\n",
- + instance->host_no, extended_msg[1], extended_msg[0], cmd->target, cmd->lun);
- +
- + msgout = MESSAGE_REJECT;
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- + ICR_ASSERT_ATN);
- + break;
- + } /* switch (tmp) */
- + break;
- + case PHASE_MSGOUT:
- + len = 1;
- + data = &msgout;
- + hostdata->last_message = msgout;
- + NCR5380_transfer_pio(instance, &phase, &len, &data);
- + if (msgout == ABORT) {
- + hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
- + hostdata->connected = NULL;
- + cmd->result = DID_ERROR << 16;
- + cmd->scsi_done(cmd);
- + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- + return;
- + }
- + msgout = NOP;
- + break;
- + case PHASE_CMDOUT:
- + len = cmd->cmd_len;
- + data = cmd->cmnd;
- + /*
- + * XXX for performance reasons, on machines with a
- + * PSEUDO-DMA architecture we should probably
- + * use the dma transfer function.
- + */
- + NCR5380_transfer_pio(instance, &phase, &len,
- + &data);
- +#ifdef USLEEP
- + if (!disconnect && should_disconnect(cmd->cmnd[0])) {
- + hostdata->time_expires = jiffies + USLEEP_SLEEP;
- +#if (NDEBUG & NDEBUG_USLEEP)
- + printk("scsi%d: issued command, sleeping until %ul\n", instance->host_no,
- + hostdata->time_expires);
- +#endif
- + NCR5380_set_timer (instance);
- + return;
- + }
- +#endif /* def USLEEP */
- + break;
- + case PHASE_STATIN:
- + len = 1;
- + data = &tmp;
- + NCR5380_transfer_pio(instance, &phase, &len, &data);
- + cmd->SCp.Status = tmp;
- + break;
- + default:
- + printk("scsi%d: unknown phase\n", instance->host_no);
- +#ifdef NDEBUG
- + NCR5380_print(instance);
- +#endif
- + } /* switch(phase) */
- + } /* if (tmp * SR_REQ) */
- +#ifdef USLEEP
- + else {
- + if (!disconnect && hostdata->time_expires && jiffies >
- + hostdata->time_expires) {
- + hostdata->time_expires = jiffies + USLEEP_SLEEP;
- +#if (NDEBUG & NDEBUG_USLEEP)
- + printk("scsi%d: poll timed out, sleeping until %ul\n", instance->host_no,
- + hostdata->time_expires);
- +#endif
- + NCR5380_set_timer (instance);
- + return;
- + }
- + }
- +#endif
- + } /* while (1) */
- +}
- +
- +/*
- + * Function : void NCR5380_reselect (struct Scsi_Host *instance)
- + *
- + * Purpose : does reselection, initializing the instance->connected
- + * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q
- + * nexus has been reestablished,
- + *
- + * Inputs : instance - this instance of the NCR5380.
- + *
- + */
- +
- +
- +static void NCR5380_reselect (struct Scsi_Host *instance) {
- + NCR5380_local_declare();
- + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
- + instance->hostdata;
- + unsigned char target_mask;
- + unsigned char lun, phase;
- + int len;
- +#ifdef SCSI2
- + unsigned char tag;
- +#endif
- + unsigned char msg[3];
- + unsigned char *data;
- + Scsi_Cmnd *tmp = NULL, *prev;
- + int abort = 0;
- + NCR5380_setup(instance);
- +
- + /*
- + * Disable arbitration, etc. since the host adapter obviously
- + * lost, and tell an interrupted NCR5380_select() to restart.
- + */
- +
- + NCR5380_write(MODE_REG, MR_BASE);
- + hostdata->restart_select = 1;
- +
- + target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask);
- +
- +#if (NDEBUG & NDEBUG_RESELECTION)
- + printk("scsi%d: reselect\n", instance->host_no);
- +#endif
- +
- + /*
- + * At this point, we have detected that our SCSI ID is on the bus,
- + * SEL is true and BSY was false for at least one bus settle delay
- + * (400 ns).
- + *
- + * We must assert BSY ourselves, until the target drops the SEL
- + * signal.
- + */
- +
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY);
- +
- + while (NCR5380_read(STATUS_REG) & SR_SEL);
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- +
- + /*
- + * Wait for target to go into MSGIN.
- + */
- +
- + while (!(NCR5380_read(STATUS_REG) & SR_REQ));
- +
- + len = 1;
- + data = msg;
- + phase = PHASE_MSGIN;
- + NCR5380_transfer_pio(instance, &phase, &len, &data);
- +
- +
- + if (!msg[0] & 0x80) {
- + printk("scsi%d: expecting IDENTIFY message, got ",
- + instance->host_no);
- + print_msg(msg);
- + abort = 1;
- + } else {
- + /* Accept message by clearing ACK */
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- + lun = (msg[0] & 0x07);
- +
- + /*
- + * We need to add code for SCSI-II to track which devices have
- + * I_T_L_Q nexuses established, and which have simple I_T_L
- + * nexuses so we can chose to do additional data transfer.
- + */
- +
- +#ifdef SCSI2
- +#error "SCSI-II tagged queueing is not supported yet"
- +#endif
- +
- + /*
- + * Find the command corresponding to the I_T_L or I_T_L_Q nexus we
- + * just reestablished, and remove it from the disconnected queue.
- + */
- +
- +
- + for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL;
- + tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble)
- + if ((target_mask == (1 << tmp->target)) && (lun == tmp->lun)
- +#ifdef SCSI2
- + && (tag == tmp->tag)
- +#endif
- +) {
- + if (prev)
- + prev->host_scribble = tmp->host_scribble;
- + else
- + hostdata->disconnected_queue = (Scsi_Cmnd *) tmp->host_scribble;
- + tmp->host_scribble = NULL;
- + break;
- + }
- +
- + if (!tmp) {
- +#ifdef SCSI2
- + printk("scsi%d: warning : target bitmask %02x lun %d tag %d not in disconnect_queue.\n",
- + instance->host_no, target_mask, lun, tag);
- +#else
- + printk("scsi%d: warning : target bitmask %02x lun %d not in disconnect_queue.\n",
- + instance->host_no, target_mask, lun);
- +#endif
- + /*
- + * Since we have an established nexus that we can't do anything with,
- + * we must abort it.
- + */
- + abort = 1;
- + }
- + }
- +
- + if (abort) {
- + msg[0] = ABORT;
- + len = 1;
- + data = msg;
- + phase = PHASE_MSGOUT;
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
- + NCR5380_transfer_pio(instance, &phase, &len, &data);
- + } else {
- + hostdata->connected = tmp;
- +#if (NDEBUG & NDEBUG_RESELECTION)
- + printk"scsi%d: nexus established, target = %d, lun = %d, tag = %d\n",
- + instance->host_no, tmp->target, tmp->lun, tmp->tag);
- +#endif
- + }
- +}
- +
- +/*
- + * Function : void NCR5380_dma_complete (struct Scsi_Host *instance)
- + *
- + * Purpose : called by interrupt handler when DMA finishes or a phase
- + * mismatch occurs (which would finish the DMA transfer).
- + *
- + * Inputs : instance - this instance of the NCR5380.
- + *
- + * Returns : pointer to the Scsi_Cmnd structure for which the I_T_L
- + * nexus has been reestablished, on failure NULL is returned.
- + */
- +
- +#ifdef REAL_DMA
- +static void NCR5380_dma_complete (NCR5380_instance *instance) {
- + NCR5380_local_declare();
- + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *
- + instance->hostdata);
- + int transferred;
- + NCR5380_setup(instance);
- +
- + /*
- + * XXX this might not be right.
- + *
- + * Wait for final byte to transfer, ie wait for ACK to go false.
- + *
- + * We should use the Last Byte Sent bit, unfortunately this is
- + * not available on the 5380/5381 (only the various CMOS chips)
- + */
- +
- + while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK);
- +
- + NCR5380_write(MODE_REG, MR_BASE);
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- +
- + /*
- + * The only places we should see a phase mismatch and have to send
- + * data from the same set of pointers will be the data transfer
- + * phases. So, residual, requested length are only important here.
- + */
- +
- + if (!(hostdata->connected->SCp.phase & SR_CD)) {
- + transferred = instance->dmalen - NCR5380_dma_residual();
- + hostdata->connected->SCp.this_residual -= transferred;
- + hostdata->connected->SCp.ptr += transferred;
- + }
- +}
- +#endif /* def REAL_DMA */
- +
- +/*
- + * Function : int NCR5380_abort (Scsi_Cmnd *cmd)
- + *
- + * Purpose : abort a command
- + *
- + * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
- + * host byte of the result field to, if zero DID_ABORTED is
- + * used.
- + *
- + * Returns : 0 - success, -1 on failure.
- + *
- + * XXX - there is no way to abort the command that is currently
- + * connected, you have to wait for it to complete. If this is
- + * a problem, we could implement longjmp() / setjmp(), setjmp()
- + * called where the loop started in NCR5380_main().
- + */
- +
- +#ifndef NCR5380_abort
- +static
- +#endif
- +int NCR5380_abort (Scsi_Cmnd *cmd) {
- + NCR5380_local_declare();
- + struct Scsi_Host *instance = cmd->host;
- + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
- + instance->hostdata;
- + Scsi_Cmnd *tmp, **prev;
- + unsigned char msg, phase, *msgptr;
- + int len;
- +
- + printk("scsi%d: aborting command\n", instance->host_no);
- + print_Scsi_Cmnd (cmd);
- +
- + NCR5380_print_status (instance);
- +
- + cli();
- + NCR5380_setup(instance);
- +
- +#if (NDEBUG & NDEBUG_ABORT)
- + printk("scsi%d: abort called\n", instance->host_no);
- + printk(" basr 0x%X, sr 0x%X\n",
- + NCR5380_read(BUS_AND_STATUS_REG), NCR5380_read(STATUS_REG));
- +#endif
- +
- +#if 0
- +/*
- + * Case 1 : If the command is the currently executing command,
- + * we'll set the aborted flag and return control so that
- + * information transfer routine can exit cleanly.
- + */
- +
- + if (hostdata->connected == cmd) {
- +#if (NDEBUG & NDEBUG_ABORT)
- + printk("scsi%d: aborting connected command\n", instance->host_no);
- +#endif
- + hostdata->aborted = 1;
- +/*
- + * We should perform BSY checking, and make sure we haven't slipped
- + * into BUS FREE.
- + */
- +
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN);
- +/*
- + * Since we can't change phases until we've completed the current
- + * handshake, we have to source or sink a byte of data if the current
- + * phase is not MSGOUT.
- + */
- +
- +/*
- + * Return control to the executing NCR drive so we can clear the
- + * aborted flag and get back into our main loop.
- + */
- +
- + return 0;
- + }
- +#endif
- +
- +/*
- + * Case 2 : If the command hasn't been issued yet, we simply remove it
- + * from the issue queue.
- + */
- + for (prev = (Scsi_Cmnd **) &(hostdata->issue_queue),
- + tmp = (Scsi_Cmnd *) hostdata->issue_queue;
- + tmp; prev = (Scsi_Cmnd **) &(tmp->host_scribble), tmp =
- + (Scsi_Cmnd *) tmp->host_scribble)
- + if (cmd == tmp) {
- + (*prev) = (Scsi_Cmnd *) tmp->host_scribble;
- + tmp->host_scribble = NULL;
- + tmp->result = DID_ABORT << 16;
- + sti();
- +#if (NDEBUG & NDEBUG_ABORT)
- + printk("scsi%d: abort removed command from issue queue.\n",
- + instance->host_no);
- +#endif
- + tmp->done(tmp);
- + return SCSI_ABORT_SUCCESS;
- + }
- +
- +/*
- + * Case 3 : If any commands are connected, we're going to fail the abort
- + * and let the high level SCSI driver retry at a later time or
- + * issue a reset.
- + *
- + * Timeouts, and therefore aborted commands, will be highly unlikely
- + * and handling them cleanly in this situation would make the common
- + * case of noresets less efficient, and would pollute our code. So,
- + * we fail.
- + */
- +
- + if (hostdata->connected) {
- + sti();
- +#if (NDEBUG & NDEBUG_ABORT)
- + printk("scsi%d: abort failed, command connected.\n", instance->host_no);
- +#endif
- + return SCSI_ABORT_NOT_RUNNING;
- + }
- +
- +/*
- + * Case 4: If the command is currently disconnected from the bus, and
- + * there are no connected commands, we reconnect the I_T_L or
- + * I_T_L_Q nexus associated with it, go into message out, and send
- + * an abort message.
- + *
- + * This case is especially ugly. In order to reestablish the nexus, we
- + * need to call NCR5380_select(). The easiest way to implement this
- + * function was to abort if the bus was busy, and let the interrupt
- + * handler triggered on the SEL for reselect take care of lost arbitrations
- + * where necessary, meaning interrupts need to be enabled.
- + *
- + * When interrupts are enabled, the queues may change - so we
- + * can't remove it from the disconnected queue before selecting it
- + * because that could cause a failure in hashing the nexus if that
- + * device reselected.
- + *
- + * Since the queues may change, we can't use the pointers from when we
- + * first locate it.
- + *
- + * So, we must first locate the command, and if NCR5380_select()
- + * succeeds, then issue the abort, relocate the command and remove
- + * it from the disconnected queue.
- + */
- +
- + for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp;
- + tmp = (Scsi_Cmnd *) tmp->host_scribble)
- + if (cmd == tmp) {
- + sti();
- +#if (NDEBUG & NDEBUG_ABORT)
- + printk("scsi%d: aborting disconnected command.\n", instance->host_no);
- +#endif
- +
- + if (NCR5380_select (instance, cmd, (int) cmd->tag))
- + return SCSI_ABORT_BUSY;
- +
- +#if (NDEBUG & NDEBUG_ABORT)
- + printk("scsi%d: nexus reestablished.\n", instance->host_no);
- +#endif
- +
- + msg = ABORT;
- + msgptr = &msg;
- + len = 1;
- + phase = PHASE_MSGOUT;
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
- + NCR5380_transfer_pio (instance, &phase, &len, &msgptr);
- +
- + cli();
- + for (prev = (Scsi_Cmnd **) &(hostdata->disconnected_queue),
- + tmp = (Scsi_Cmnd *) hostdata->disconnected_queue;
- + tmp; prev = (Scsi_Cmnd **) &(tmp->host_scribble), tmp =
- + (Scsi_Cmnd *) tmp->host_scribble)
- + if (cmd == tmp) {
- + *prev = (Scsi_Cmnd *) tmp->host_scribble;
- + tmp->host_scribble = NULL;
- + tmp->result = DID_ABORT << 16;
- + sti();
- + tmp->done(tmp);
- + return SCSI_ABORT_SUCCESS;
- + }
- + }
- +
- +/*
- + * Case 5 : If we reached this point, the command was not found in any of
- + * the queues.
- + *
- + * We probably reached this point because of an unlikely race condition
- + * between the command completing successfully and the abortion code,
- + * so we won't panic, but we will notify the user in case something really
- + * broke.
- + */
- +
- + sti();
- + printk("scsi%d: warning : SCSI command probably completed successfully\n"
- + " before abortion\n", instance->host_no);
- + return SCSI_ABORT_NOT_RUNNING;
- +}
- +
- +
- +/*
- + * Function : int NCR5380_reset (Scsi_Cmnd *cmd)
- + *
- + * Purpose : reset the SCSI bus.
- + *
- + * Returns : SCSI_RESET_WAKEUP
- + *
- + */
- +
- +#ifndef NCR5380_reset
- +static
- +#endif
- +int NCR5380_reset (Scsi_Cmnd *cmd) {
- + NCR5380_local_declare();
- + NCR5380_setup(cmd->host);
- +
- + NCR5380_print_status (cmd->host);
- +
- + cli();
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST);
- + udelay(25);
- + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- + sti();
- +
- + return SCSI_RESET_WAKEUP;
- +}
- +
- diff -urNwbB linux/arch/arm/drivers/scsi/NCR5380.h linux.arm/arch/arm/drivers/scsi/NCR5380.h
- --- linux/arch/arm/drivers/scsi/NCR5380.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/scsi/NCR5380.h Sat Feb 24 09:36:54 1996
- @@ -0,0 +1,352 @@
- +/*
- + * NCR 5380 defines
- + *
- + * Copyright 1993, Drew Eckhardt
- + * Visionary Computing
- + * (Unix consulting and custom programming)
- + * drew@colorado.edu
- + * +1 (303) 666-5836
- + *
- + * DISTRIBUTION RELEASE 6
- + *
- + * For more information, please consult
- + *
- + * NCR 5380 Family
- + * SCSI Protocol Controller
- + * Databook
- + * NCR Microelectronics
- + * 1635 Aeroplaza Drive
- + * Colorado Springs, CO 80916
- + * 1+ (719) 578-3400
- + * 1+ (800) 334-5454
- + */
- +
- +/*
- + * $Log: NCR5380.h,v $
- + */
- +
- +#ifndef NCR5380_H
- +#define NCR5380_H
- +
- +#define NCR5380_PUBLIC_RELEASE 6
- +#ifdef NCR53C400
- +#define NCR53C400_PUBLIC_RELEASE 1
- +#endif
- +
- +#define NDEBUG_ARBITRATION 0x1
- +#define NDEBUG_AUTOSENSE 0x2
- +#define NDEBUG_DMA 0x4
- +#define NDEBUG_HANDSHAKE 0x8
- +#define NDEBUG_INFORMATION 0x10
- +#define NDEBUG_INIT 0x20
- +#define NDEBUG_INTR 0x40
- +#define NDEBUG_LINKED 0x80
- +#define NDEBUG_MAIN 0x100
- +#define NDEBUG_NO_DATAOUT 0x200
- +#define NDEBUG_NO_WRITE 0x400
- +#define NDEBUG_PIO 0x800
- +#define NDEBUG_PSEUDO_DMA 0x1000
- +#define NDEBUG_QUEUES 0x2000
- +#define NDEBUG_RESELECTION 0x4000
- +#define NDEBUG_SELECTION 0x8000
- +#define NDEBUG_USLEEP 0x10000
- +#define NDEBUG_LAST_BYTE_SENT 0x20000
- +#define NDEBUG_RESTART_SELECT 0x40000
- +#define NDEBUG_EXTENDED 0x80000
- +
- +/*
- + * The contents of the OUTPUT DATA register are asserted on the bus when
- + * either arbitration is occurring or the phase-indicating signals (
- + * IO, CD, MSG) in the TARGET COMMAND register and the ASSERT DATA
- + * bit in the INITIATOR COMMAND register is set.
- + */
- +
- +#define OUTPUT_DATA_REG 0 /* wo DATA lines on SCSI bus */
- +#define CURRENT_SCSI_DATA_REG 0 /* ro same */
- +
- +#define INITIATOR_COMMAND_REG 1 /* rw */
- +#define ICR_ASSERT_RST 0x80 /* rw Set to assert RST */
- +#define ICR_ARBITRATION_PROGRESS 0x40 /* ro Indicates arbitration complete */
- +#define ICR_TRI_STATE 0x40 /* wo Set to tri-state drivers */
- +#define ICR_ARBITRATION_LOST 0x20 /* ro Indicates arbitration lost */
- +#define ICR_DIFF_ENABLE 0x20 /* wo Set to enable diff. drivers */
- +#define ICR_ASSERT_ACK 0x10 /* rw ini Set to assert ACK */
- +#define ICR_ASSERT_BSY 0x08 /* rw Set to assert BSY */
- +#define ICR_ASSERT_SEL 0x04 /* rw Set to assert SEL */
- +#define ICR_ASSERT_ATN 0x02 /* rw Set to assert ATN */
- +#define ICR_ASSERT_DATA 0x01 /* rw SCSI_DATA_REG is asserted */
- +
- +#ifdef DIFFERENTIAL
- +#define ICR_BASE ICR_DIFF_ENABLE
- +#else
- +#define ICR_BASE 0
- +#endif
- +
- +#define MODE_REG 2
- +/*
- + * Note : BLOCK_DMA code will keep DRQ asserted for the duration of the
- + * transfer, causing the chip to hog the bus. You probably don't want
- + * this.
- + */
- +#define MR_BLOCK_DMA_MODE 0x80 /* rw block mode DMA */
- +#define MR_TARGET 0x40 /* rw target mode */
- +#define MR_ENABLE_PAR_CHECK 0x20 /* rw enable parity checking */
- +#define MR_ENABLE_PAR_INTR 0x10 /* rw enable bad parity interrupt */
- +#define MR_ENABLE_EOP_INTR 0x08 /* rw enable eop interrupt */
- +#define MR_MONITOR_BSY 0x04 /* rw enable int on unexpected bsy fail */
- +#define MR_DMA_MODE 0x02 /* rw DMA / pseudo DMA mode */
- +#define MR_ARBITRATE 0x01 /* rw start arbitration */
- +
- +#ifdef PARITY
- +#define MR_BASE MR_ENABLE_PAR_CHECK
- +#else
- +#define MR_BASE 0
- +#endif
- +
- +#define TARGET_COMMAND_REG 3
- +#define TCR_LAST_BYTE_SENT 0x80 /* ro DMA done */
- +#define TCR_ASSERT_REQ 0x08 /* tgt rw assert REQ */
- +#define TCR_ASSERT_MSG 0x04 /* tgt rw assert MSG */
- +#define TCR_ASSERT_CD 0x02 /* tgt rw assert CD */
- +#define TCR_ASSERT_IO 0x01 /* tgt rw assert IO */
- +
- +#define STATUS_REG 4 /* ro */
- +/*
- + * Note : a set bit indicates an active signal, driven by us or another
- + * device.
- + */
- +#define SR_RST 0x80
- +#define SR_BSY 0x40
- +#define SR_REQ 0x20
- +#define SR_MSG 0x10
- +#define SR_CD 0x08
- +#define SR_IO 0x04
- +#define SR_SEL 0x02
- +#define SR_DBP 0x01
- +
- +/*
- + * Setting a bit in this register will cause an interrupt to be generated when
- + * BSY is false and SEL true and this bit is asserted on the bus.
- + */
- +#define SELECT_ENABLE_REG 4 /* wo */
- +
- +#define BUS_AND_STATUS_REG 5 /* ro */
- +#define BASR_END_DMA_TRANSFER 0x80 /* ro set on end of transfer */
- +#define BASR_DRQ 0x40 /* ro mirror of DRQ pin */
- +#define BASR_PARITY_ERROR 0x20 /* ro parity error detected */
- +#define BASR_IRQ 0x10 /* ro mirror of IRQ pin */
- +#define BASR_PHASE_MATCH 0x08 /* ro Set when MSG CD IO match TCR */
- +#define BASR_BUSY_ERROR 0x04 /* ro Unexpected change to inactive state */
- +#define BASR_ATN 0x02 /* ro BUS status */
- +#define BASR_ACK 0x01 /* ro BUS status */
- +
- +/* Write any value to this register to start a DMA send */
- +#define START_DMA_SEND_REG 5 /* wo */
- +
- +/*
- + * Used in DMA transfer mode, data is latched from the SCSI bus on
- + * the falling edge of REQ (ini) or ACK (tgt)
- + */
- +#define INPUT_DATA_REG 6 /* ro */
- +
- +/* Write any value to this register to start a DMA receive */
- +#define START_DMA_TARGET_RECEIVE_REG 6 /* wo */
- +
- +/* Read this register to clear interrupt conditions */
- +#define RESET_PARITY_INTERRUPT_REG 7 /* ro */
- +
- +/* Write any value to this register to start an ini mode DMA receive */
- +#define START_DMA_INITIATOR_RECEIVE_REG 7 /* wo */
- +
- +#ifdef NCR53C400
- +#define C400_CONTROL_STATUS_REG -8 /* rw */
- +
- +#define CSR_RESET 0x80 /* wo Resets 53c400 */
- +#define CSR_53C80_REG 0x80 /* ro 5380 registers busy */
- +#define CSR_TRANS_DIR 0x40 /* rw Data transfer direction */
- +#define CSR_SCSI_BUFF_INTR 0x20 /* rw Enable int on transfer ready */
- +#define CSR_53C80_INTR 0x10 /* rw Enable 53c80 interrupts */
- +#define CSR_SHARED_INTR 0x08 /* rw Interrupt sharing */
- +#define CSR_HOST_BUF_NOT_RDY 0x04 /* ro Is Host buffer ready */
- +#define CSR_SCSI_BUF_RDY 0x02 /* ro SCSI buffer read */
- +#define CSR_GATED_53C80_IRQ 0x01 /* ro Last block xferred */
- +
- +#define CSR_BASE CSR_SCSI_BUFF_INTR | CSR_53C80_INTR
- +
- +/* Number of 128-byte blocks to be transferred */
- +#define C400_CLOCK_COUNTER_REG -7 /* rw */
- +
- +/* Resume transfer after disconnect */
- +#define C400_RESUME_TRANSFER_REG -6 /* wo */
- +
- +/* Access to host buffer stack */
- +#define C400_HOST_BUFFER -4 /* rw */
- +
- +#endif /* NCR53C400 */
- +
- +
- +/* Note : PHASE_* macros are based on the values of the STATUS register */
- +#define PHASE_MASK (SR_MSG | SR_CD | SR_IO)
- +
- +#define PHASE_DATAOUT 0
- +#define PHASE_DATAIN SR_IO
- +#define PHASE_CMDOUT SR_CD
- +#define PHASE_STATIN (SR_CD | SR_IO)
- +#define PHASE_MSGOUT (SR_MSG | SR_CD)
- +#define PHASE_MSGIN (SR_MSG | SR_CD | SR_IO)
- +#define PHASE_UNKNOWN 0xff
- +
- +/*
- + * Convert status register phase to something we can use to set phase in
- + * the target register so we can get phase mismatch interrupts on DMA
- + * transfers.
- + */
- +
- +#define PHASE_SR_TO_TCR(phase) ((phase) >> 2)
- +
- +/*
- + * The internal should_disconnect() function returns these based on the
- + * expected length of a disconnect if a device supports disconnect/
- + * reconnect.
- + */
- +
- +#define DISCONNECT_NONE 0
- +#define DISCONNECT_TIME_TO_DATA 1
- +#define DISCONNECT_LONG 2
- +
- +/*
- + * These are "special" values for the tag parameter passed to NCR5380_select.
- + */
- +
- +#define TAG_NEXT -1 /* Use next free tag */
- +#define TAG_NONE -2 /*
- + * Establish I_T_L nexus instead of I_T_L_Q
- + * even on SCSI-II devices.
- + */
- +
- +/*
- + * These are "special" values for the irq and dma_channel fields of the
- + * Scsi_Host structure
- + */
- +
- +#define IRQ_NONE 255
- +#define DMA_NONE 255
- +#define IRQ_AUTO 254
- +#define DMA_AUTO 254
- +
- +#define FLAG_HAS_LAST_BYTE_SENT 1 /* NCR53c81 or better */
- +#define FLAG_CHECK_LAST_BYTE_SENT 2 /* Only test once */
- +#define FLAG_NCR53C400 4 /* NCR53c400 */
- +
- +#ifndef ASM
- +struct NCR5380_hostdata {
- + NCR5380_implementation_fields; /* implementation specific */
- + unsigned char id_mask, id_higher_mask; /* 1 << id, all bits greater */
- + unsigned char targets_present; /* targets we have connected
- + to, so we can call a select
- + failure a retryable condition */
- + volatile unsigned char busy[8]; /* index = target, bit = lun */
- +#if defined(REAL_DMA) || defined(REAL_DMA_POLL)
- + volatile int dma_len; /* requested length of DMA */
- +#endif
- + volatile unsigned char last_message; /* last message OUT */
- + volatile Scsi_Cmnd *connected; /* currently connected command */
- + volatile Scsi_Cmnd *issue_queue; /* waiting to be issued */
- + volatile Scsi_Cmnd *disconnected_queue; /* waiting for reconnect */
- + volatile int restart_select; /* we have disconnected,
- + used to restart
- + NCR5380_select() */
- + volatile unsigned aborted:1; /* flag, says aborted */
- + int flags;
- +#ifdef USLEEP
- + unsigned long time_expires; /* in jiffies, set prior to sleeping */
- + struct Scsi_Host *next_timer;
- +#endif
- +};
- +
- +#ifdef __KERNEL__
- +static struct Scsi_Host *first_instance; /* linked list of 5380's */
- +
- +#if defined(AUTOPROBE_IRQ)
- +static int NCR5380_probe_irq (struct Scsi_Host *instance, int possible);
- +#endif
- +static void NCR5380_init (struct Scsi_Host *instance, int flags);
- +static void NCR5380_information_transfer (struct Scsi_Host *instance);
- +static void NCR5380_intr (int irq, struct pt_regs * regs);
- +static void NCR5380_main (void);
- +static void NCR5380_print_options (struct Scsi_Host *instance);
- +#ifndef NCR5380_abort
- +static
- +#endif
- +int NCR5380_abort (Scsi_Cmnd *cmd);
- +#ifndef NCR5380_reset
- +static
- +#endif
- +int NCR5380_reset (Scsi_Cmnd *cmd);
- +#ifndef NCR5380_queue_command
- +static
- +#endif
- +int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
- +
- +
- +static void NCR5380_reselect (struct Scsi_Host *instance);
- +static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag);
- +#if defined(PSEUDO_DMA) || defined(REAL_DMA) || defined(REAL_DMA_POLL)
- +static int NCR5380_transfer_dma (struct Scsi_Host *instance,
- + unsigned char *phase, int *count, unsigned char **data);
- +#endif
- +static int NCR5380_transfer_pio (struct Scsi_Host *instance,
- + unsigned char *phase, int *count, unsigned char **data);
- +
- +#if (defined(REAL_DMA) || defined(REAL_DMA_POLL)) && defined(i386)
- +static __inline__ int NCR5380_i386_dma_setup (struct Scsi_Host *instance,
- + unsigned char *ptr, unsigned int count, unsigned char mode) {
- + unsigned limit;
- +
- + if (instance->dma_channel <=3) {
- + if (count > 65536)
- + count = 65536;
- + limit = 65536 - (((unsigned) ptr) & 0xFFFF);
- + } else {
- + if (count > 65536 * 2)
- + count = 65536 * 2;
- + limit = 65536* 2 - (((unsigned) ptr) & 0x1FFFF);
- + }
- +
- + if (count > limit) count = limit;
- +
- + if ((count & 1) || (((unsigned) ptr) & 1))
- + panic ("scsi%d : attempted unaligned DMA transfer\n", instance->host_no);
- + cli();
- + disable_dma(instance->dma_channel);
- + clear_dma_ff(instance->dma_channel);
- + set_dma_addr(instance->dma_channel, (unsigned int) ptr);
- + set_dma_count(instance->dma_channel, count);
- + set_dma_mode(instance->dma_channel, mode);
- + enable_dma(instance->dma_channel);
- + sti();
- + return count;
- +}
- +
- +static __inline__ int NCR5380_i386_dma_write_setup (struct Scsi_Host *instance,
- + unsigned char *src, unsigned int count) {
- + return NCR5380_i386_dma_setup (instance, src, count, DMA_MODE_WRITE);
- +}
- +
- +static __inline__ int NCR5380_i386_dma_read_setup (struct Scsi_Host *instance,
- + unsigned char *src, unsigned int count) {
- + return NCR5380_i386_dma_setup (instance, src, count, DMA_MODE_READ);
- +}
- +
- +static __inline__ int NCR5380_i386_dma_residual (struct Scsi_Host *instance) {
- + register int tmp;
- + cli();
- + clear_dma_ff(instance->dma_channel);
- + tmp = get_dma_residue(instance->dma_channel);
- + sti();
- + return tmp;
- +}
- +#endif /* defined(REAL_DMA) && defined(i386) */
- +#endif __KERNEL_
- +#endif /* ndef ASM */
- +#endif /* NCR5380_H */
- diff -urNwbB linux/arch/arm/drivers/scsi/acornscsi-io.S linux.arm/arch/arm/drivers/scsi/acornscsi-io.S
- --- linux/arch/arm/drivers/scsi/acornscsi-io.S Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/scsi/acornscsi-io.S Sun Feb 11 09:32:30 1996
- @@ -0,0 +1,253 @@
- +@ linux/arch/arm/drivers/scsi/acornscsi-io.S: Acorn SCSI card IO
- +@
- +@ There is a reason for not using LDMs in this file - it kills the
- +@ floppy drive and the serial port! In order to keep those running
- +@ error free, we use single word operations.
- +@
- +
- +@ Purpose: transfer a block of data from the acorn scsi card to memory
- +@ Proto : void acornscsi_in(int portstrt, char *buffer, int length)
- +@ Returns: nothing
- +
- + .align
- + .global ___acornscsi_in
- +___acornscsi_in:
- + mov ip, sp
- + stmfd sp!, {r4 - r10, fp, ip, lr, pc}
- + sub fp, ip, #4
- + cmp r0,#0x00C00000
- + movge r3, #0
- + movlt r3, #0x03000000
- + orrlt r3, r3, #0x00010000
- + add r0, r3, r0, lsl #2
- + mov ip, #0xff
- + orr ip, ip, ip, lsl #8
- +acornscsi_in1lp:
- + subs r2, r2, #32
- + bmi acornscsi_in16
- + ldr r3, [r0], #4
- + and r3, r3, ip
- + ldr r4, [r0], #4
- + orr r3, r3, r4, lsl #16
- + ldr r5, [r0], #4
- + and r4, r5, ip
- + ldr r6, [r0], #4
- + orr r4, r4, r6, lsl #16
- + ldr r7, [r0], #4
- + and r5, r7, ip
- + ldr r8, [r0], #4
- + orr r5, r5, r8, lsl #16
- + ldr r9, [r0], #4
- + and r6, r9, ip
- + ldr r10, [r0], #4
- + orr r6, r6, r10, lsl #16
- + stmia r1!, {r3 - r6}
- + ldr r3, [r0], #4
- + and r3, r3, ip
- + ldr r4, [r0], #4
- + orr r3, r3, r4, lsl #16
- + ldr r5, [r0], #4
- + and r4, r5, ip
- + ldr r6, [r0], #4
- + orr r4, r4, r6, lsl #16
- + ldr r7, [r0], #4
- + and r5, r7, ip
- + ldr r8, [r0], #4
- + orr r5, r5, r8, lsl #16
- + ldr r9, [r0], #4
- + and r6, r9, ip
- + ldr r10, [r0], #4
- + orr r6, r6, r10, lsl #16
- + stmia r1!, {r3 - r6}
- + bne acornscsi_in1lp
- + ldmea fp, {r4 - r10, fp, sp, pc}^
- +acornscsi_in16: adds r2, r2, #16
- + bmi acornscsi_in8
- +acornscsi_in16lp:
- + ldr r3, [r0], #4
- + and r3, r3, ip
- + ldr r4, [r0], #4
- + orr r3, r3, r4, lsl #16
- + ldr r5, [r0], #4
- + and r4, r5, ip
- + ldr r6, [r0], #4
- + orr r4, r4, r6, lsl #16
- + ldr r7, [r0], #4
- + and r5, r7, ip
- + ldr r8, [r0], #4
- + orr r5, r5, r8, lsl #16
- + ldr r9, [r0], #4
- + and r6, r9, ip
- + ldr r10, [r0], #4
- + orr r6, r6, r10, lsl #16
- + stmia r1!, {r3 - r6}
- + ldmeqea fp, {r4 - r10, fp, sp, pc}^
- + sub r2, r2, #16
- +acornscsi_in8: adds r2, r2, #8
- + bmi acornscsi_in4
- + ldr r3, [r0], #4
- + and r3, r3, ip
- + ldr r4, [r0], #4
- + orr r3, r3, r4, lsl #16
- + ldr r5, [r0], #4
- + and r4, r5, ip
- + ldr r6, [r0], #4
- + orr r4, r4, r6, lsl #16
- + stmia r1!, {r3 - r4}
- + ldmeqea fp, {r4 - r10, fp, sp, pc}^
- + sub r2, r2, #8
- +acornscsi_in4: adds r2, r2, #4
- + bmi acornscsi_in2
- + ldr r3, [r0], #4
- + and r3, r3, ip
- + ldr r4, [r0], #4
- + orr r3, r3, r4, lsl #16
- + str r3, [r1], #4
- + ldmeqea fp, {r4 - r10, fp, sp, pc}^
- + sub r2, r2, #4
- +acornscsi_in2: adds r2, r2, #2
- + ldr r3, [r0], #4
- + and r3, r3, ip
- + strb r3, [r1], #1
- + mov r3, r3, lsr #8
- + strplb r3, [r1], #1
- + ldmea fp, {r4 - r10, fp, sp, pc}^
- +
- +@ Purpose: transfer a block of data from memory to the acorn scsi card
- +@ Proto : void acornscsi_in(int portstrt, char *buffer, int length)
- +@ Returns: nothing
- +
- + .global ___acornscsi_out
- +___acornscsi_out:
- + mov ip, sp
- + stmfd sp!, {r4 - r10, fp, ip, lr, pc}
- + sub fp, ip, #4
- + cmp r0,#0x00C00000
- + movge r3, #0
- + movlt r3, #0x03000000
- + orrlt r3, r3, #0x00010000
- + add r0, r3, r0, lsl #2
- + mov ip, #0xff
- + orr ip, ip, ip, lsl #8
- +acornscsi_out1lp:
- + subs r2, r2, #32
- + bmi acornscsi_out16
- + ldmia r1!, {r7 - r10}
- + mov r3, r7, lsl #16
- + orr r3, r3, r3, lsr #16
- + str r3, [r0], #4
- + mov r4, r7, lsr #16
- + orr r4, r4, r4, lsl #16
- + str r4, [r0], #4
- + mov r5, r8, lsl #16
- + orr r5, r5, r5, lsr #16
- + str r5, [r0], #4
- + mov r6, r8, lsr #16
- + orr r6, r6, r6, lsl #16
- + str r6, [r0], #4
- + mov r7, r9, lsl #16
- + orr r7, r7, r7, lsr #16
- + str r7, [r0], #4
- + mov r8, r9, lsr #16
- + orr r8, r8, r8, lsl #16
- + str r8, [r0], #4
- + mov r9, r10, lsl #16
- + orr r9, r9, r9, lsr #16
- + str r9, [r0], #4
- + mov r10, r10, lsr #16
- + orr r10, r10, r10, lsl #16
- + str r10, [r0], #4
- + ldmia r1!, {r7 - r10}
- + mov r3, r7, lsl #16
- + orr r3, r3, r3, lsr #16
- + str r3, [r0], #4
- + mov r4, r7, lsr #16
- + orr r4, r4, r4, lsl #16
- + str r4, [r0], #4
- + mov r5, r8, lsl #16
- + orr r5, r5, r5, lsr #16
- + str r5, [r0], #4
- + mov r6, r8, lsr #16
- + orr r6, r6, r6, lsl #16
- + str r6, [r0], #4
- + mov r7, r9, lsl #16
- + orr r7, r7, r7, lsr #16
- + str r7, [r0], #4
- + mov r8, r9, lsr #16
- + orr r8, r8, r8, lsl #16
- + str r8, [r0], #4
- + mov r9, r10, lsl #16
- + orr r9, r9, r9, lsr #16
- + str r9, [r0], #4
- + mov r10, r10, lsr #16
- + orr r10, r10, r10, lsl #16
- + str r10, [r0], #4
- + bne acornscsi_out1lp
- + ldmea fp, {r4 - r10, fp, sp, pc}^
- +acornscsi_out16:
- + adds r2, r2, #16
- + bmi acornscsi_out8
- +acornscsi_out16lp:
- + ldmia r1!, {r7 - r10}
- + mov r3, r7, lsl #16
- + orr r3, r3, r3, lsr #16
- + str r3, [r0], #4
- + mov r4, r7, lsr #16
- + orr r4, r4, r4, lsl #16
- + str r4, [r0], #4
- + mov r5, r8, lsl #16
- + orr r5, r5, r5, lsr #16
- + str r5, [r0], #4
- + mov r6, r8, lsr #16
- + orr r6, r6, r6, lsl #16
- + str r6, [r0], #4
- + mov r7, r9, lsl #16
- + orr r7, r7, r7, lsr #16
- + str r7, [r0], #4
- + mov r8, r9, lsr #16
- + orr r8, r8, r8, lsl #16
- + str r8, [r0], #4
- + mov r9, r10, lsl #16
- + orr r9, r9, r9, lsr #16
- + str r9, [r0], #4
- + mov r10, r10, lsr #16
- + orr r10, r10, r10, lsl #16
- + str r10, [r0], #4
- + ldmeqea fp, {r4 - r10, fp, sp, pc}^
- + sub r2, r2, #16
- +acornscsi_out8: adds r2, r2, #8
- + bmi acornscsi_out4
- + ldmia r1!, {r5 - r6}
- + mov r3, r5, lsl #16
- + orr r3, r3, r3, lsr #16
- + str r3, [r0], #4
- + mov r4, r5, lsr #16
- + orr r4, r4, r4, lsl #16
- + str r4, [r0], #4
- + mov r5, r6, lsl #16
- + orr r5, r5, r5, lsr #16
- + str r5, [r0], #4
- + mov r6, r6, lsr #16
- + orr r6, r6, r6, lsl #16
- + str r6, [r0], #4
- + ldmeqea fp, {r4 - r10, fp, sp, pc}^
- + sub r2, r2, #8
- +acornscsi_out4: adds r2, r2, #4
- + bmi acornscsi_out2
- + ldr r4, [r1], #4
- + mov r3, r4, lsl #16
- + orr r3, r3, r3, lsr #16
- + str r3, [r0], #4
- + mov r4, r4, lsr #16
- + orr r4, r4, r4, lsl #16
- + str r4, [r0], #4
- + ldmeqea fp, {r4 - r10, fp, sp, pc}^
- + sub r2, r2, #4
- +acornscsi_out2: adds r2, r2, #2
- + ldr r3, [r1], #2
- + and r3, r3, ip
- + strb r3, [r0], #1
- + mov r3, r3, lsr #8
- + strplb r3, [r0], #1
- + ldmea fp, {r4 - r10, fp, sp, pc}^
- +
- diff -urNwbB linux/arch/arm/drivers/scsi/acornscsi.c linux.arm/arch/arm/drivers/scsi/acornscsi.c
- --- linux/arch/arm/drivers/scsi/acornscsi.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/scsi/acornscsi.c Sun Feb 11 09:32:31 1996
- @@ -0,0 +1,1967 @@
- +/*
- + * linux/arch/arm/drivers/scsi/acornscsi.c
- + *
- + * Acorn scsi SP3 driver
- + *
- + * By R.M.King, using SP3 info supplied by Acorn.
- + *
- + * Abandoned using the Select and Transfer command since there were
- + * some nasty races between our software and the target devices that
- + * were not easy to solve, and the device specs said that there were
- + * some nasty bugs in that command...
- + *
- + */
- +
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/config.h>
- +#endif
- +
- +#include <linux/kernel.h>
- +#include <linux/sched.h>
- +#include <linux/string.h>
- +#include <linux/signal.h>
- +#include <linux/errno.h>
- +#include <linux/proc_fs.h>
- +#include <linux/stat.h>
- +#include <asm/bitops.h>
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include <asm/ecard.h>
- +#include "../block/blk.h"
- +#include "scsi.h"
- +#include "hosts.h"
- +#include "acornscsi.h"
- +#include "constants.h"
- +
- +#define DEBUG_NO_WRITE 1
- +#define DEBUG_QUEUES 2
- +#define DEBUG_DMA 4
- +#define DEBUG_ABORT 8
- +#define DEBUG_DISCON 16
- +#define DEBUG_CONNECT 32
- +#define DEBUG_PHASES 64
- +#define DEBUG_WRITE 128
- +#define DEBUG_LINK 256
- +
- +/*
- + *====================================================================================
- + *
- + * Driver Configuration
- + *
- + *
- + *
- + * SCSI-II Tagged queue support.
- + *
- + * I don't have any SCSI devices that support it, so it is totally untested (except to
- + * make sure that it doesn't interfere with any non-tagging devices). It is not fully
- + * implemented either - what happens when a tagging device reconnects???
- + */
- +#undef SCSI2_TAG
- +
- +/*
- + * SCSI-II Linked command support.
- + *
- + * The higher level code doesn't support linked commands yet.
- + */
- +#undef SCSI2_LINK
- +
- +/*
- + * Debugging information
- + *
- + * Please see debug list above
- + */
- +#define DEBUG (DEBUG_ABORT)
- +
- +/*
- + * List of devices that the driver will recognise
- + *
- + * PRODS is the list of product numbers
- + * MANUS is the list of manufacturer numbers
- + *
- + * NOTE: this is used as a pair of numbers. If you add a number in the middle of
- + * one, then you must add a number in at the same place in the other.
- + */
- +#define PRODS 0x0002
- +#define MANUS 0x0000
- +/*
- + *====================================================================================
- + */
- +
- +#define VER_MAJOR 1
- +#define VER_MINOR 4
- +
- +#if 0
- +/*
- + * This is as per acorns spec.
- + */
- +#define INIT_DEVCON0 (DEVCON0_RQL | DEVCON0_EXW)
- +#define INIT_DEVCON1 (DEVCON1_BHLD)
- +#define INIT_SBICDMA (CTRL_DMABURST)
- +#define DMAC_READ (MODECON_READ)
- +#define DMAC_WRITE (MODECON_WRITE)
- +#else
- +/*
- + * Hopefully this will improve performance with floppy disk + serial port
- + */
- +#define INIT_DEVCON0 (DEVCON0_RQL | DEVCON0_EXW)
- +#define INIT_DEVCON1 (0)
- +#define INIT_SBICDMA (CTRL_DMAMODE)
- +#define DMAC_READ (MODECON_READ|MODECON_SINGLE)
- +#define DMAC_WRITE (MODECON_WRITE|MODECON_SINGLE)
- +#endif
- +
- +#define POD_SPACE(x) ((x) + 0xd0000)
- +#define SCpdirection SCp.phase
- +#define MASK_ON (MASKREG_M3|MASKREG_M2|MASKREG_M1|MASKREG_M0)
- +#define MASK_OFF (MASKREG_M3|MASKREG_M2|MASKREG_M1)
- +#define RESET_STATUS(x) ({ status_ptr = 0; })
- +#define ADD_STATUS(x) ({ status_array[status_ptr] = (x); status_ptr = (status_ptr + 1) & 63; })
- +
- +static unsigned char status_array[64];
- +static unsigned int status_ptr = 0;
- +
- +static struct Scsi_Host *first_instance = NULL;
- +static Scsi_Host_Template *the_template = NULL;
- +
- +/*
- + * Todo:
- + * Complete abort processing code when command is disconnected [waiting for one to happen]
- + */
- +
- +static struct proc_dir_entry proc_scsi_acornscsi = {
- + PROC_SCSI_EATA, 9, "acornscsi", S_IFDIR | S_IRUGO | S_IXUGO, 2
- +};
- +
- +static inline void sbic_write (struct Scsi_Host *instance, int reg, int value)
- +{
- + outb(reg, instance->io_port + 0x800);
- + outw(value, instance->io_port + 0x801);
- +}
- +
- +static inline int sbic_read (struct Scsi_Host *instance, int reg)
- +{
- + if(reg == ASR)
- + return inw(instance->io_port + 0x800) & 0xff;
- + outb(reg, instance->io_port + 0x800);
- + return inw(instance->io_port + 0x801) & 0xff;
- +}
- +
- +static inline void page_write (struct Scsi_Host *instance, int value)
- +{
- + outb(value, POD_SPACE(instance->io_port) + 0xc00);
- +}
- +
- +static inline int dmac_read (struct Scsi_Host *instance, int reg)
- +{
- + return inb (instance->io_port + 0xc00 + reg);
- +}
- +
- +static inline void dmac_write (struct Scsi_Host *instance, int reg, int value)
- +{
- + outb (value, instance->io_port + 0xc00 + reg);
- +}
- +
- +static inline void dmac_clearintr (struct Scsi_Host *instance)
- +{
- + outb (0, POD_SPACE(instance->io_port) + 0x800);
- +}
- +
- +static inline int read_irqstat (struct Scsi_Host *instance)
- +{
- + return inb (POD_SPACE(instance->io_port) + 0x800);
- +}
- +
- +static inline void sbic_reset (struct Scsi_Host *instance)
- +{
- + sbic_write (instance, OWNID, OWNID_EAF | instance->this_id);
- + sbic_write (instance, CMND, CMND_RESET);
- +}
- +
- +/*=============================================================================================
- + * utility routines
- + */
- +static void print_sbic_status (int asr, int ssr, int cmdphase)
- +{
- + printk("sbic status: %s %s %s %s %s %s ",
- + asr & ASR_INT ? "INT" : "int",
- + asr & ASR_LCI ? "LCI" : "lci",
- + asr & ASR_BSY ? "BSY" : "bsy",
- + asr & ASR_CIP ? "CIP" : "cip",
- + asr & ASR_PE ? "PE" : "pe",
- + asr & ASR_DBR ? "DBR" : "dbr");
- + printk("scsi status: %X:%X phase %02X\n",
- + (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase);
- +}
- +
- +static void acornscsi_printstatus (struct Scsi_Host *instance)
- +{
- + int statptr;
- + printk ("IRQs: ");
- + statptr = status_ptr - 20;
- + if (statptr < 0)
- + statptr += 64;
- + for (; statptr != status_ptr; statptr = (statptr + 2) & 63) {
- + printk ("[%02X:%02X] ", status_array[statptr], status_array[statptr+1]);
- + }
- + printk ("\n");
- +}
- +
- +static inline int acornscsi_cmdtype (int command)
- +{
- + switch (command) {
- + case WRITE_6:
- + case WRITE_10:
- + case WRITE_12:
- + return CMD_WRITE;
- + case READ_6:
- + case READ_10:
- + case READ_12:
- + return CMD_READ;
- + default:
- + return CMD_MISC;
- + }
- +}
- +
- +static int acornscsi_writedirection (int command)
- +{
- + switch (command) {
- + case CHANGE_DEFINITION:
- + case COMPARE:
- + case COPY:
- + case COPY_VERIFY:
- + case LOG_SELECT:
- + case MODE_SELECT:
- + case MODE_SELECT_10:
- + case SEND_DIAGNOSTIC:
- + case WRITE_BUFFER:
- + case FORMAT_UNIT:
- + case REASSIGN_BLOCKS:
- + case RESERVE:
- + case SEARCH_EQUAL:
- + case SEARCH_HIGH:
- + case SEARCH_LOW:
- + case WRITE_6:
- + case WRITE_10:
- + case WRITE_VERIFY:
- + case UPDATE_BLOCK:
- + case WRITE_LONG:
- + case WRITE_SAME:
- + case SEARCH_HIGH_12:
- + case SEARCH_EQUAL_12:
- + case SEARCH_LOW_12:
- + case WRITE_12:
- + case WRITE_VERIFY_12:
- + case SET_WINDOW:
- + case MEDIUM_SCAN:
- + case SEND_VOLUME_TAG:
- + case 0xea:
- + return 1;
- + case TEST_UNIT_READY:
- + default:
- + return 0;
- + }
- +}
- +
- +static inline void acornscsi_setupdmac(struct Scsi_Host *instance, int mode,
- + unsigned long address, int length)
- +{
- + dmac_write (instance, MASKREG, MASK_ON);
- + if (length) {
- + length -= 1;
- + dmac_write (instance, TXCNTLO, length);
- + dmac_write (instance, TXCNTHI, length >> 8);
- + dmac_write (instance, TXADRLO, address);
- + dmac_write (instance, TXADRMD, address >> 8);
- + dmac_write (instance, TXADRHI, address >> 16);
- + dmac_write (instance, MODECON, mode);
- + dmac_write (instance, MASKREG, MASK_OFF);
- + } else {
- + dmac_write (instance, TXCNTLO, 0);
- + dmac_write (instance, TXCNTHI, 0);
- + }
- +}
- +
- +/*
- + * Function : acornscsi_initialisebuffer (Scsi_Cmnd *cmd)
- + *
- + * Purpose : Initialise the buffer pointers
- + *
- + * Params : cmd - command structure
- + *
- + */
- +static inline void acornscsi_initialisebuffer (Scsi_Cmnd *cmd)
- +{
- + if (cmd->use_sg) {
- + cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
- + cmd->SCp.buffers_residual = cmd->use_sg - 1;
- + cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
- + cmd->SCp.this_residual = cmd->SCp.buffer->length;
- + } else {
- + cmd->SCp.buffer = NULL;
- + cmd->SCp.buffers_residual = 0;
- + cmd->SCp.ptr = (char *) cmd->request_buffer;
- + cmd->SCp.this_residual = cmd->request_bufflen;
- + }
- +}
- +
- +/*
- + * Function : acornscsi_nextbuffer (Scsi_Cmnd *cmd)
- + *
- + * Purpose : If the current buffer is empty, move onto the next buffer
- + *
- + * Params : cmd - command structure containing the buffers.
- + *
- + * Returns : zero if no more buffers, else non-zero
- + */
- +static inline int acornscsi_nextbuffer (Scsi_Cmnd *cmd)
- +{
- + if (cmd->SCp.this_residual == 0)
- + if (cmd->SCp.buffers_residual) {
- + cmd->SCp.buffer++;
- + cmd->SCp.buffers_residual--;
- + cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
- + cmd->SCp.this_residual = cmd->SCp.buffer->length;
- + return 1;
- + } else {
- + cmd->SCp.ptr = NULL;
- + cmd->SCp.this_residual = 0;
- + return 0;
- + }
- +}
- +
- +/*
- + * Function : acornscsi_readbuffer (struct Scsi_Host *instance, char *buffer,
- + * unsigned long boardaddr, int len)
- + *
- + * Purpose : transfer a block of data from the scsi card to memory
- + *
- + * Params : instance - host specific data
- + * : buffer - address to put data
- + * : boardaddr - address of data on scsi card
- + * : len - length of data to transfer
- + *
- + */
- +static void acornscsi_readtobh (struct Scsi_Host *instance, struct acornscsi_hostdata *hostdata,
- + struct as_cmnd *ascmd, short residual)
- +{
- + unsigned long buffer_length;
- + unsigned int page, offset;
- + Scsi_Cmnd *SCpnt;
- + extern void __acornscsi_in (int port, char *buffer, int transfer);
- +
- + SCpnt = ascmd->cmd;
- +
- + if (ascmd->buffer) {
- + page = (ascmd->buf1addr >> 12) & 0x3f;
- + offset = ascmd->buf1addr & 4095;
- + buffer_length = ascmd->buf1len - residual;
- + } else {
- + page = (ascmd->buf0addr >> 12) & 0x3f;
- + offset = ascmd->buf0addr & 4095;
- + buffer_length = ascmd->buf0len - residual;
- + }
- +
- + page_write (instance, hostdata->page_reg | page);
- +
- +#if (DEBUG & DEBUG_DMA)
- + printk ("scsi%d : debug : readtobh: (%04lX %04lX)\n ", instance->host_no, (page << 12) | offset,
- + buffer_length);
- +#endif
- +
- + while (buffer_length > 0) {
- + int this_length;
- +
- + if (SCpnt->SCp.this_residual > buffer_length)
- + this_length = buffer_length;
- + else
- + this_length = SCpnt->SCp.this_residual;
- +
- + SCpnt->SCp.this_residual -= this_length;
- + buffer_length -= this_length;
- +
- +#if (DEBUG & DEBUG_DMA)
- + printk ("[%p %04lX %04X] ", SCpnt->SCp.ptr, (page << 12) | offset, this_length);
- +#endif
- +
- + while (this_length > 0) {
- + int this_trans;
- +
- + this_trans = 4096 - offset;
- + if (this_trans > this_length)
- + this_trans = this_length;
- +
- + __acornscsi_in (instance->io_port + (offset >> 1), SCpnt->SCp.ptr, this_trans);
- +
- + SCpnt->SCp.ptr += this_trans;
- + offset += this_trans;
- + this_length -= this_trans;
- +
- + if (offset > 4095) {
- + offset -= 4096;
- + page = (page + 1) & 0x3f;
- + page_write (instance, hostdata->page_reg | page);
- + }
- + }
- +
- + if (acornscsi_nextbuffer (SCpnt) == 0)
- + break;
- + }
- +#if (DEBUG & DEBUG_DMA)
- + printk ("\n");
- +#endif
- +}
- +
- +/*
- + * Function : acornscsi_writebuffer (struct Scsi_Host *instance, char *buffer,
- + * unsigned long boardaddr, int len)
- + *
- + * Purpose : transfer a block of data from memory to the scsi card
- + *
- + * Params : instance - host specific data
- + * : buffer - address to put data
- + * : boardaddr - address of data on scsi card
- + * : len - length of data to transfer
- + *
- + */
- +static inline void acornscsi_writebuffer (struct Scsi_Host *instance, char *buffer,
- + unsigned long boardaddr, int len)
- +{
- + extern void __acornscsi_out (int port, char *buffer, int transfer);
- + struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
- + unsigned int page, offset;
- +
- + page = (boardaddr >> 12) & 0x3f;
- + offset = boardaddr & 4095;
- +
- + page_write (instance, hostdata->page_reg | page);
- +
- + while (len > 0) {
- + int this_trans;
- +
- + this_trans = 4096 - offset;
- + if (len < this_trans)
- + this_trans = len;
- +
- + __acornscsi_out (instance->io_port + (offset >> 1), buffer, this_trans);
- +
- + buffer += this_trans;
- + offset += this_trans;
- + len -= this_trans;
- +
- + if (offset > 4095) {
- + offset -= 4096;
- + page = (page + 1) & 0x3f;
- + page_write (instance, hostdata->page_reg | page);
- + }
- + }
- +}
- +
- +static unsigned long acornscsi_writefrombh (struct Scsi_Host *instance, struct as_cmnd *ascmd,
- + int buffer)
- +{
- + Scsi_Cmnd *SCpnt;
- + unsigned long buffer_address, buffer_offset;
- + SCpnt = ascmd->cmd;
- +
- + if (buffer)
- + buffer_address = ascmd->buf1addr;
- + else
- + buffer_address = ascmd->buf0addr;
- +
- +#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
- + printk ("scsi%d : debug : writefrombh: (%04lX)\n ", instance->host_no, buffer_address);
- +#endif
- +
- + buffer_offset = 0;
- +
- + do {
- + int this_length;
- +
- + if (buffer_offset + SCpnt->SCp.this_residual >= 4096)
- + this_length = 4096 - buffer_offset;
- + else
- + this_length = SCpnt->SCp.this_residual;
- +
- +#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
- + printk ("[%p %04lX %04X] ", SCpnt->SCp.ptr, buffer_address + buffer_offset, this_length);
- +#endif
- +
- + acornscsi_writebuffer (instance, SCpnt->SCp.ptr, buffer_address + buffer_offset, this_length);
- +
- + SCpnt->SCp.this_residual -= this_length;
- + SCpnt->SCp.ptr += this_length;
- + buffer_offset += this_length;
- +
- + if (acornscsi_nextbuffer (SCpnt) == 0)
- + break;
- + } while (buffer_offset < 4096);
- +
- +#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
- + printk ("\n");
- +#endif
- + if (buffer)
- + ascmd->buf1len = buffer_offset;
- + else
- + ascmd->buf0len = buffer_offset;
- +
- + return buffer_offset;
- +}
- +/*
- + * Function : acornscsi_handletransfer (struct Scsi_Host *instance);
- + *
- + * Purpose : transfers a block of data from the on-board buffer
- + * to the scatter list or buffer and updates the dmac
- + *
- + * Params : instance - the device to handle
- + *
- + */
- +static inline int acornscsi_handletransfer (struct Scsi_Host *instance)
- +{
- + struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
- + struct as_cmnd *ascmd = hostdata->connected;
- +
- + if (!ascmd) {
- + printk("scsi%d : warning: dmac interrupt with no connected command\n", instance->host_no);
- + return 0;
- + }
- +
- + dmac_write (instance, MASKREG, MASK_ON);
- + dmac_clearintr (instance);
- +
- +#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
- + printk ("htrans: ");
- +#endif
- + switch (hostdata->phase) {
- + case PHASE_COMMANDSENT:
- + printk ("\n");
- + return 0;
- +
- + case PHASE_DATAIN:
- + if (ascmd->buffer) {
- + ascmd->totallen -= ascmd->buf1len;
- + if (ascmd->totallen > 4096)
- + ascmd->buf0len = 4096;
- + else
- + ascmd->buf0len = ascmd->totallen;
- +
- + acornscsi_setupdmac (instance, DMAC_READ, ascmd->buf0addr, ascmd->buf0len);
- + } else {
- + ascmd->totallen -= ascmd->buf0len;
- + if (ascmd->totallen > 4096)
- + ascmd->buf1len = 4096;
- + else
- + ascmd->buf1len = ascmd->totallen;
- +
- + acornscsi_setupdmac (instance, DMAC_READ, ascmd->buf1addr, ascmd->buf1len);
- + }
- + acornscsi_readtobh (instance, hostdata, ascmd, 0);
- + ascmd->buffer ^= 1;
- + if (ascmd->totallen == 0)
- + return 0;
- + return 1;
- +
- + case PHASE_DATAOUT:
- + if (ascmd->buffer) {
- + ascmd->totallen -= ascmd->buf1len;
- +
- + acornscsi_setupdmac (instance, DMAC_WRITE, ascmd->buf0addr, ascmd->buf0len);
- + ascmd->buffer = 0;
- +
- + acornscsi_writefrombh (instance, ascmd, 1);
- + } else {
- + ascmd->totallen -= ascmd->buf0len;
- +
- + acornscsi_setupdmac (instance, DMAC_WRITE, ascmd->buf1addr, ascmd->buf1len);
- + ascmd->buffer = 1;
- +
- + acornscsi_writefrombh (instance, ascmd, 0);
- + }
- +
- + if (ascmd->totallen == 0)
- + return 0;
- + return 1;
- + }
- + return 0;
- +}
- +
- +static void acornscsi_sendcommand (struct Scsi_Host *instance,
- + struct acornscsi_hostdata *hostdata)
- +{
- + struct as_dev *dev;
- + struct as_cmnd *asprev, *ascmd;
- + int i;
- +
- + cli ();
- + if (hostdata->connected)
- + goto not_busy;
- +
- + if (hostdata->aborting) {
- + if (!(sbic_read (instance, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
- + hostdata->phase = PHASE_CONNECTING_FOR_ABORT;
- + sbic_write (instance, DESTID, hostdata->aborting->cmd->target);
- + sbic_write (instance, CMND, CMND_SELWITHATN);
- +#if (DEBUG & (DEBUG_QUEUES|DEBUG_ABORT|DEBUG_CONNECT))
- + printk ("scsi%d : restarting abortion of id %d\n", instance->host_no,
- + hostdata->aborting->cmd->target);
- +#endif
- + }
- + goto not_busy;
- + }
- +
- + if (hostdata->connecting) {
- + if (!(sbic_read (instance, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
- + hostdata->phase = PHASE_CONNECTING;
- + sbic_write (instance, DESTID, hostdata->connecting->cmd->target);
- + sbic_write (instance, CMND, CMND_SELWITHATN);
- +#if (DEBUG & (DEBUG_QUEUES|DEBUG_CONNECT))
- + printk ("scsi%d : restarting selection of id %d\n", instance->host_no,
- + hostdata->connecting->cmd->target);
- +#endif
- + }
- + goto not_busy;
- + }
- +
- + for (i = 0, dev = hostdata->dev; i < 8; i++, dev++) {
- + for (ascmd = dev->issue_head, asprev = NULL; ascmd; asprev = ascmd, ascmd = ascmd->next) {
- + if (!ascmd->used || !ascmd->cmd || !ascmd->cmd->scsi_done) {
- + printk ("scsi%d : warning: invalid %s found in issue queue\n",
- + instance->host_no, ascmd->used ? ascmd->cmd ? "scsi_done" : "cmd" : "used");
- + if (asprev)
- + asprev = ascmd->next;
- + else
- + dev->issue_head = ascmd->next;
- + if (ascmd == dev->issue_tail)
- + dev->issue_tail = asprev;
- + continue;
- + }
- +
- + if (ascmd->cmd->device->tagged_queue == 0) {
- + if (set_bit (ascmd->cmd->lun, &dev->busy))
- + continue;
- + }
- +#ifdef SCSI2_TAG
- + else {
- + /*
- + * SCSI-II Tagged support. If the command is not a request sense, then
- + * we allocate a tag to it.
- + */
- + if (ascmd->cmd->cmnd[0] != REQUEST_SENSE) {
- + if (ascmd->cmd->device->current_tag == 0) {
- + printk ("scsi%d : Enabling tagged queueing for target %d.\n",
- + ascmd->cmd->host->host_no, ascmd->cmd->target);
- + ascmd->cmd->device->current_tag = 1; /* enable tagging */
- + }
- + ascmd->cmd->tag = ascmd->cmd->device->current_tag++;
- + if (ascmd->cmd->device->current_tag == 0) /* MUST not become zero */
- + ascmd->cmd->device->current_tag += 1;
- + }
- + }
- +#endif
- +
- + hostdata->connecting = ascmd;
- +
- + if (asprev)
- + asprev->next = ascmd->next;
- + else
- + dev->issue_head = ascmd->next;
- +
- + if (ascmd == dev->issue_tail)
- + dev->issue_tail = asprev;
- +
- + hostdata->stats.removes += 1;
- +
- + if (!(sbic_read (instance, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
- + sti ();
- + hostdata->phase = PHASE_CONNECTING;
- + sbic_write (instance, DESTID, hostdata->connecting->cmd->target);
- + sbic_write (instance, CMND, CMND_SELWITHATN);
- +#if (DEBUG & (DEBUG_QUEUES|DEBUG_CONNECT))
- + printk ("scsi%d : debug : starting selection of id %d\n", instance->host_no,
- + hostdata->connecting->cmd->target);
- +#endif
- + }
- +
- + sti ();
- +
- + switch (acornscsi_cmdtype (ascmd->cmd->cmnd[0])) {
- + case CMD_WRITE:
- + hostdata->stats.writes += 1;
- + break;
- + case CMD_READ:
- + hostdata->stats.reads += 1;
- + break;
- + case CMD_MISC:
- + hostdata->stats.miscs += 1;
- + break;
- + }
- + return;
- + }
- + }
- +
- +not_busy:
- + sti ();
- +}
- +
- +static inline void acornscsi_startsendcommand (struct Scsi_Host *instance)
- +{
- + struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
- +
- + if (set_bit (0, (void *)&hostdata->busy) == 0)
- + acornscsi_sendcommand (instance, hostdata);
- + hostdata->busy = 0;
- +}
- +
- +static void acornscsi_done (struct Scsi_Host *instance, struct as_cmnd *ascmd)
- +{
- + struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
- + Scsi_Cmnd *SCpnt;
- +
- + sbic_write (instance, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
- +
- + SCpnt = ascmd->cmd;
- +
- + if (!SCpnt)
- + panic ("scsi%d : fatal : null command in acornscsi_done\n", instance->host_no);
- +#if 0
- + printk ("scsi%d : target %d connected in %d, finished %d, transferred %d\n",
- + instance->host_no, SCpnt->target, ascmd->connect_jiffies - ascmd->queued_jiffies,
- + finished_jiffies - ascmd->queued_jiffies, SCpnt->request_bufflen);
- +#endif
- + ascmd->cmd = NULL;
- + ascmd->next = NULL;
- + ascmd->buf0len = 0;
- + ascmd->buf1len = 0;
- + ascmd->used = 0;
- +
- + hostdata->dev[SCpnt->target].busy &= ~(1 << SCpnt->lun);
- +
- + if (!SCpnt->scsi_done)
- + panic ("scsi%d : fatal : null scsi_done function in acornscsi_done\n", instance->host_no);
- +
- + SCpnt->scsi_done (SCpnt);
- +}
- +
- +static inline void acornscsi_savepointers (struct Scsi_Host *instance,
- + struct acornscsi_hostdata *hostdata)
- +{
- + struct as_cmnd *ascmd = hostdata->connected;
- +
- + dmac_write (instance, MASKREG, MASK_ON);
- +
- + ascmd->saved_sbic_transcnt = hostdata->tmpsbiclen;
- + ascmd->saved_dmac_address = (dmac_read (instance, TXADRHI) << 16) |
- + (dmac_read (instance, TXADRMD) << 8) |
- + dmac_read (instance, TXADRLO);
- + ascmd->saved_dmac_transcnt = (dmac_read (instance, TXCNTHI) << 8) |
- + dmac_read (instance, TXCNTLO);
- + ascmd->saved = 1;
- +#if (DEBUG & DEBUG_DISCON)
- + printk ("scsi%d : debug : discon: sl %06lX da %06lX dl %04lX\n", instance->host_no,
- + ascmd->saved_sbic_transcnt, ascmd->saved_dmac_address,
- + ascmd->saved_dmac_transcnt);
- +#endif
- +}
- +
- +/*
- + * Function : acornscsi_disconnect (struct Scsi_Host *instance)
- + *
- + * Purpose : disconnect the current command & put onto disconnected queue
- + *
- + * Params : instance - host data
- + *
- + * Notes:
- + * We appear to have a race condition between this exiting, sending the next command,
- + * and a device on the bus reselecting us...
- + */
- +static inline void acornscsi_disconnect (struct Scsi_Host *instance)
- +{
- + struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
- + struct as_dev *dev;
- + struct as_cmnd *ascmd;
- + unsigned long flags;
- +
- + dev = &hostdata->dev[hostdata->connected->cmd->target];
- +
- + dmac_write (instance, MASKREG, MASK_ON);
- +
- + save_flags (flags);
- + cli ();
- + ascmd = hostdata->connected;
- + hostdata->connected = NULL;
- +
- + ascmd->next = dev->disconnected;
- + dev->disconnected = ascmd;
- + restore_flags (flags);
- +
- +#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
- + printk("scsi%d : command for target %d lun %d tag %d moved to disconnected queue\n",
- + instance->host_no, ascmd->cmd->target, ascmd->cmd->lun, ascmd->cmd->tag);
- +#endif
- +}
- +
- +/*
- + * Function : acornscsi_reconnect (struct Scsi_Host *instance)
- + *
- + * Purpose : restore a previously saved command state
- + *
- + * Params : instance - host data
- + */
- +static inline int acornscsi_reconnect (struct Scsi_Host *instance)
- +{
- + struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
- + struct as_dev *dev;
- + struct as_cmnd *ascmd, **asprev;
- + int sourceid, sourcelun, sourcetag = 0;
- +
- + sourceid = sbic_read (instance, SOURCEID);
- +
- + if (hostdata->connected) {
- + printk ("scsi%d : warning: reconnecting while connected to target %d\n",
- + instance->host_no, hostdata->connected->cmd->target);
- + hostdata->connected = NULL;
- + }
- +
- + if (!(sourceid & 8)) {
- + printk ("scsi%d : warning: invalid source id after reselection - chip/device fault?\n",
- + instance->host_no);
- + return 0;
- + }
- +
- + sourceid &= 7;
- + sourcelun = sbic_read (instance, DATA); /* should this be target lun or data? */
- + sourcelun &= 7;
- +
- + dev = &hostdata->dev[sourceid];
- +
- + for (asprev = &dev->disconnected; *asprev; asprev = &(*asprev)->next) {
- + ascmd = *asprev;
- + if (ascmd->cmd->target != sourceid) {
- + printk ("scsi%d : warning: command on wrong disconnected queue %d instead of %d\n",
- + instance->host_no, ascmd->cmd->target, sourceid);
- + }
- + if (ascmd->cmd->lun == sourcelun && ascmd->cmd->tag == sourcetag) {
- + *asprev = ascmd->next;
- + ascmd->next = NULL;
- + hostdata->connected = ascmd;
- +#if (DEBUG & DEBUG_QUEUES)
- + printk ("scsi%d : debug : command for target %d lun %d tag %d reconnected\n",
- + instance->host_no, ascmd->cmd->target, ascmd->cmd->lun, ascmd->cmd->tag);
- +#endif
- + return 1;
- + }
- + }
- + printk("scsi%d : warning: target %d lun %d tag %d not in disconnect_queue.\n",
- + instance->host_no, sourceid, sourcelun, sourcetag);
- + return 0;
- +}
- +
- +/*
- + * Function : acornscsi_forcedisconnect (struct Scsi_Host *instance)
- + *
- + * Purpose : force a disconnect from the bus
- + *
- + * Params : instance - hostdata
- + */
- +static inline void acornscsi_forcedisconnect (struct Scsi_Host *instance)
- +{
- +}
- +
- +/*
- + * Function : acornscsi_startabort (struct Scsi_Host *instance)
- + *
- + * Purpose : start aborting a connected command
- + *
- + * Params : instance - hostdata
- + */
- +static inline void acornscsi_startabort (struct Scsi_Host *instance)
- +{
- + acornscsi_printstatus (instance);
- +}
- +
- +/* ==========================================================================================
- + * Interrupt routines.
- + */
- +static int acornscsi_sbicintr (struct Scsi_Host *instance)
- +{
- + struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
- + struct as_cmnd *ascmd;
- + int asr, ssr;
- +
- + asr = sbic_read (instance, ASR);
- + if (!(asr & ASR_INT))
- + return INTR_CONTINUE;
- +
- + ssr = sbic_read (instance, SSR);
- +#if (DEBUG & (DEBUG_DMA|DEBUG_RECON|DEBUG_PHASES|DEBUG_WRITE))
- + print_sbic_status(asr, ssr, sbic_read (instance, CMNDPHASE));
- +#endif
- + ADD_STATUS(ssr);
- + ADD_STATUS(hostdata->phase);
- +
- + ascmd = hostdata->connected;
- +
- + /*
- + * This is just one big switch statement
- + */
- +
- + switch (ssr) {
- +
- +/* ---------------------- Reset -------------------------------------------------------- */
- +
- + case 0x00: /* reset state - not advanced */
- + printk ("scsi%d : reset in standard mode but wanted advanced mode.\n",
- + instance->host_no);
- + case 0x01: /* reset state - advanced */
- + sbic_write (instance, CTRL, INIT_SBICDMA | CTRL_IDI);
- + sbic_write (instance, TIMEOUT, 10);
- + sbic_write (instance, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
- + return INTR_CONTINUE;
- +
- +/* ---------------------- Select ------------------------------------------------------- */
- +
- + case 0x11: /* select command ok */
- + switch (hostdata->phase) {
- + case PHASE_CONNECTING:
- + if (!hostdata->connecting) {
- + printk ("scsi%d : warning: select ok but no connecting command\n",
- + instance->host_no);
- + goto abort_connect;
- + }
- + hostdata->connected = hostdata->connecting;
- + hostdata->connecting = NULL;
- + hostdata->phase = PHASE_CONNECTED;
- + hostdata->connected->buffer = 2;
- + hostdata->msgout = NOP;
- + hostdata->last_message = NOP;
- + hostdata->connected->connect_jiffies = jiffies;
- +#if (DEBUG & (DEBUG_QUEUES|DEBUG_CONNECT))
- + printk ("scsi%d : debug : command connected to target %d\n",
- + instance->host_no, hostdata->connected->cmd->target);
- +#endif
- + return INTR_CONNECTED;
- +
- + case PHASE_CONNECTING_FOR_ABORT:
- + if (!hostdata->aborting) {
- + printk ("scsi%d : warning: select ok but no connecting aborting command\n",
- + instance->host_no);
- + goto abort_connect;
- + }
- + hostdata->connected = hostdata->aborting;
- + hostdata->aborting = NULL;
- + hostdata->phase = PHASE_CONNECTED_FOR_ABORT;
- + hostdata->msgout = ABORT;
- + hostdata->last_message = NOP;
- + hostdata->connected->buffer = 2;
- +#if (DEBUG & (DEBUG_QUEUES|DEBUG_CONNECT))
- + printk ("scsi%d : debug : abort command connected to target %d\n",
- + instance->host_no, hostdata->connected->cmd->target);
- +#endif
- + return INTR_CONNECTED;
- +
- + default:
- + printk ("scsi%d : warning: select connected to target with invalid phase (%X)\n",
- + instance->host_no, hostdata->phase);
- + abort_connect:
- + /* At this point we have lost the command totally (and hence can't return an
- + * error until the upper code causes an abort.
- + */
- + acornscsi_printstatus (instance);
- + acornscsi_forcedisconnect (instance);
- + return INTR_NOTCONNECTED;
- + }
- +
- + case 0x42: /* select timeout */
- + switch (hostdata->phase) {
- + case PHASE_CONNECTING:
- + ascmd = hostdata->connecting;
- + hostdata->connecting = NULL;
- + ascmd->cmd->result = DID_BAD_TARGET << 16;
- + hostdata->phase = PHASE_IDLE;
- + acornscsi_done (instance, ascmd);
- + return INTR_NOTCONNECTED;
- +
- + case PHASE_CONNECTING_FOR_ABORT:
- + ascmd = hostdata->aborting;
- + hostdata->aborting = NULL;
- + ascmd->cmd->result = DID_BAD_TARGET << 16;
- + hostdata->phase = PHASE_IDLE;
- + acornscsi_done (instance, ascmd);
- + return INTR_NOTCONNECTED;
- +
- + default:
- + printk ("scsi%d : warning: select timeout for target %d in wrong phase\n",
- + instance->host_no, hostdata->connecting->cmd->target);
- + acornscsi_printstatus (instance);
- + return INTR_NOTCONNECTED;
- + }
- +
- + case 0x81: /* reselection occurred */
- + /* for this, we need to add extra code to allow us to keep track of whether this
- + * is SCSI 2 tagged or not. I am not sure how this part is supposed to work.
- + *
- + * For the moment, I will assume that it works the same way that we tell the
- + * device, ie. SELECT->MESSAGE(IDENTIFY)->MESSAGE(SIMPLE_TAG)->MESSAGE(TAGID)
- + */
- + if (acornscsi_reconnect (instance)) {
- + sbic_write (instance, CMND, CMND_NEGATEACK);
- + hostdata->phase = PHASE_RECONNECTED;
- + }
- + return INTR_CONNECTED;
- +
- + case 0x41: /* unexpected disconnect aborted command */
- + if (!ascmd) {
- + printk ("scsi%d : warning: unexpected disconnect without connected command\n",
- + instance->host_no);
- + return INTR_NOTCONNECTED;
- + }
- + hostdata->connected = NULL;
- + ascmd->cmd->result = DID_ABORT << 16;
- + acornscsi_done (instance, ascmd);
- + hostdata->phase = PHASE_IDLE;
- + return INTR_NOTCONNECTED;
- +
- + case 0x85: /* disconnection occurred */
- + if (ascmd == NULL) {
- + printk ("scsi%d : warning: disconnect with no connected command\n",
- + instance->host_no);
- + return INTR_NOTCONNECTED;
- + }
- + switch (hostdata->phase) {
- + case PHASE_FINISHED:
- + hostdata->connected = NULL;
- + hostdata->stats.fins += 1;
- + ascmd->cmd->result = ascmd->cmd->SCp.Status | (ascmd->cmd->SCp.Message << 8);
- + hostdata->phase = PHASE_IDLE;
- + acornscsi_done (instance, ascmd);
- + return INTR_NOTCONNECTED;
- +
- + case PHASE_DISCONNECTING:
- + acornscsi_disconnect (instance);
- + hostdata->phase = PHASE_IDLE;
- + return INTR_NOTCONNECTED;
- +
- + default:
- + printk ("scsi%d : warning: invalid phase in disconnect (%X)\n",
- + instance->host_no, hostdata->phase);
- + acornscsi_printstatus (instance);
- + }
- + return INTR_NOTCONNECTED;
- +
- +/* ---------------------- data out ----------------------------------------------------- */
- +
- + case 0x88: /* connect - data out [only after reconnection] */
- + if (hostdata->phase != PHASE_RECONNECTED) {
- + printk ("scsi%d : warning: invalid phase for reconnected data out (%X)\n",
- + instance->host_no, hostdata->phase);
- + return INTR_CONNECTED;
- + }
- +
- +#if (DEBUG & (DEBUG_DISCON|DEBUG_WRITE))
- + printk ("scsi%d : debug : recon: buffer %d ",
- + instance->host_no, ascmd->buffer);
- +#endif
- + if (ascmd->buffer == 0 || ascmd->buffer == 1) {
- + if (!ascmd->saved) {
- + printk ("scsi%d : warning: no saved data for reconnected command\n",
- + instance->host_no);
- + return INTR_CONNECTED;
- + }
- + ascmd->saved = 0;
- +
- + dmac_write (instance, MASKREG, MASK_ON);
- + dmac_write (instance, TXCNTLO, ascmd->saved_dmac_transcnt);
- + dmac_write (instance, TXCNTHI, ascmd->saved_dmac_transcnt >> 8);
- + dmac_write (instance, TXADRLO, ascmd->saved_dmac_address);
- + dmac_write (instance, TXADRMD, ascmd->saved_dmac_address >> 8);
- + dmac_write (instance, TXADRHI, ascmd->saved_dmac_address >> 16);
- + dmac_write (instance, MODECON, DMAC_WRITE);
- + dmac_write (instance, MASKREG, MASK_OFF);
- + sbic_write (instance, TRANSCNTL, ascmd->saved_sbic_transcnt);
- + sbic_write (instance, TRANSCNTM, ascmd->saved_sbic_transcnt >> 8);
- + sbic_write (instance, TRANSCNTH, ascmd->saved_sbic_transcnt >> 16);
- +#if (DEBUG & (DEBUG_DISCON|DEBUG_WRITE))
- + printk ("sl %06lX da %06lX dl %04lX\n", ascmd->saved_sbic_transcnt,
- + ascmd->saved_dmac_address, ascmd->saved_dmac_transcnt);
- +#endif
- + sbic_write (instance, CMND, CMND_XFERINFO);
- + hostdata->phase = PHASE_DATAOUT;
- + return INTR_CONNECTED;
- + }
- + ascmd->saved = 0;
- + goto data_out_common;
- +
- + case 0x18: /* transfer completed - data out */
- + if (hostdata->phase != PHASE_COMMANDSENT) {
- + printk ("scsi%d : warning: invalid phase for data out (%X)\n",
- + instance->host_no, hostdata->phase);
- + acornscsi_startabort (instance);
- + return INTR_CONNECTED;
- + }
- + data_out_common:
- + ascmd->buffer = 0;
- + acornscsi_writefrombh (instance, ascmd, 0);
- + acornscsi_setupdmac (instance, DMAC_WRITE, ascmd->buf0addr, ascmd->buf0len);
- + sbic_write (instance, TRANSCNTL, ascmd->totallen);
- + sbic_write (instance, TRANSCNTM, ascmd->totallen >> 8);
- + sbic_write (instance, TRANSCNTH, ascmd->totallen >> 16);
- + sbic_write (instance, CMND, CMND_XFERINFO);
- + hostdata->phase = PHASE_DATAOUT;
- + acornscsi_writefrombh (instance, ascmd, 1);
- +#if (DEBUG & (DEBUG_DISCON|DEBUG_WRITE))
- + printk ("sl %06lX da %06lX dl %04lX\n", ascmd->totallen, ascmd->buf0addr,
- + ascmd->buf0len);
- +#endif
- + return INTR_CONNECTED;
- +
- +/* ---------------------- data in ------------------------------------------------------ */
- +
- + case 0x89: /* connect - data in [only after reconnection] */
- + if (hostdata->phase != PHASE_RECONNECTED) {
- + printk ("scsi%d : warning: invalid phase for reconnected data in (%X)\n",
- + instance->host_no, hostdata->phase);
- + return INTR_CONNECTED;
- + }
- +
- +#if (DEBUG & DEBUG_DISCON)
- + printk ("scsi%d : debug : recon: buffer %d ",
- + instance->host_no, ascmd->buffer);
- +#endif
- + if (ascmd->buffer == 0 || ascmd->buffer == 1) {
- + if (!ascmd->saved) {
- + printk ("scsi%d : warning: no saved data for reconnected command\n",
- + instance->host_no);
- + return INTR_CONNECTED;
- + }
- + ascmd->saved = 0;
- +
- + dmac_write (instance, MASKREG, MASK_ON);
- + dmac_write (instance, TXCNTLO, ascmd->saved_dmac_transcnt);
- + dmac_write (instance, TXCNTHI, ascmd->saved_dmac_transcnt >> 8);
- + dmac_write (instance, TXADRLO, ascmd->saved_dmac_address);
- + dmac_write (instance, TXADRMD, ascmd->saved_dmac_address >> 8);
- + dmac_write (instance, TXADRHI, ascmd->saved_dmac_address >> 16);
- + dmac_write (instance, MODECON, DMAC_READ);
- + dmac_write (instance, MASKREG, MASK_OFF);
- + sbic_write (instance, TRANSCNTL, ascmd->saved_sbic_transcnt);
- + sbic_write (instance, TRANSCNTM, ascmd->saved_sbic_transcnt >> 8);
- + sbic_write (instance, TRANSCNTH, ascmd->saved_sbic_transcnt >> 16);
- +#if (DEBUG & DEBUG_DISCON)
- + printk ("sl %06lX da %06lX dl %04lX\n", ascmd->saved_sbic_transcnt,
- + ascmd->saved_dmac_address, ascmd->saved_dmac_transcnt);
- +#endif
- + sbic_write (instance, CMND, CMND_XFERINFO);
- + hostdata->phase = PHASE_DATAIN;
- + return INTR_CONNECTED;
- + }
- + ascmd->saved = 0;
- + goto data_in_common;
- +
- + case 0x19: /* transfer completed - data in */
- + if (hostdata->phase != PHASE_COMMANDSENT) {
- + printk ("scsi%d : warning: invalid phase for data in (%X)\n",
- + instance->host_no, hostdata->phase);
- + acornscsi_startabort (instance);
- + return INTR_CONNECTED;
- + }
- + data_in_common:
- + ascmd->buffer = 0;
- +
- + if (ascmd->totallen > 4096)
- + ascmd->buf0len = 4096;
- + else
- + ascmd->buf0len = ascmd->totallen;
- +
- + acornscsi_setupdmac (instance, DMAC_READ, ascmd->buf0addr, ascmd->buf0len);
- +
- + sbic_write (instance, TRANSCNTL, ascmd->totallen);
- + sbic_write (instance, TRANSCNTM, ascmd->totallen >> 8);
- + sbic_write (instance, TRANSCNTH, ascmd->totallen >> 16);
- +#if (DEBUG & DEBUG_DISCON)
- + printk ("sl %06lX da %06lX dl %04X\n", ascmd->totallen, ascmd->buf0addr,
- + ascmd->buf0len);
- +#endif
- + sbic_write (instance, CMND, CMND_XFERINFO);
- + hostdata->phase = PHASE_DATAIN;
- + return INTR_CONNECTED;
- +
- +/* ---------------------- command out -------------------------------------------------- */
- +
- + case 0x1a: /* transfer completed - command info next */
- + case 0x8a: {/* connection - command phase out */
- + Scsi_Cmnd *SCpnt;
- + int i;
- +
- + if (hostdata->phase != PHASE_COMMAND) {
- + printk ("scsi%d : warning: invalid phase for command (%X)\n",
- + instance->host_no, hostdata->phase);
- + acornscsi_startabort (instance);
- + return INTR_CONNECTED;
- + }
- + SCpnt = hostdata->connected->cmd;
- +
- + sbic_write (instance, TRANSCNTL, SCpnt->cmd_len);
- + sbic_write (instance, TRANSCNTM, 0);
- + sbic_write (instance, TRANSCNTH, 0);
- + sbic_write (instance, CMND, CMND_XFERINFO);
- +
- + for (i = 0; i < SCpnt->cmd_len; i++) {
- + int asr;
- + do {
- + asr = sbic_read (instance, ASR) & (ASR_INT | ASR_BSY | ASR_DBR);
- + } while (asr == ASR_BSY);
- + if (!(asr & ASR_DBR)) {
- + printk ("scsi%d : warning: interrupt while sending command\n",
- + instance->host_no);
- + return INTR_CONNECTED;
- + }
- + sbic_write (instance, DATA, SCpnt->cmnd[i]);
- + }
- + hostdata->phase = PHASE_COMMANDSENT;
- +
- + return INTR_CONNECTED;
- + }
- +
- +/* ---------------------- status in ---------------------------------------------------- */
- +
- + case 0x1b: /* transfer completed - status phase in */
- + case 0x8b: /* connection - status phase in [only after reconnection] */
- + switch (hostdata->phase) {
- + case PHASE_RECONNECTED:
- + if (ssr == 0x8b)
- + break;
- + goto complain_status_in;
- +
- + case PHASE_COMMANDSENT:
- + case PHASE_DATAIN:
- + case PHASE_DATAOUT:
- + if (ssr == 0x1b)
- + break;
- +
- + complain_status_in:
- + default:
- + printk ("scsi%d : warning: invalid phase (%02X) in status in (%02X)\n",
- + instance->host_no, hostdata->phase, ssr);
- + acornscsi_printstatus (instance);
- + acornscsi_startabort (instance);
- + return INTR_CONNECTED;
- + }
- + sbic_write (instance, CMND, CMND_XFERINFO|CMND_SBT);
- + while ((sbic_read (instance, ASR) & ASR_DBR) == 0);
- + hostdata->connected->cmd->SCp.Status = sbic_read (instance, DATA);
- + hostdata->phase = PHASE_STATUSIN;
- + return INTR_CONNECTED;
- +
- + case 0x4b: { /* unexpected status info */
- + short residual;
- +
- + dmac_write (instance, MASKREG, MASK_ON);
- + residual = ((dmac_read (instance, TXCNTHI) << 8) | dmac_read (instance, TXCNTLO)) + 1;
- +
- + sbic_write (instance, TRANSCNTH, 0);
- + sbic_write (instance, TRANSCNTM, 0);
- + sbic_write (instance, TRANSCNTL, 0);
- +
- + switch (hostdata->phase) {
- + case PHASE_DATAIN:
- + acornscsi_readtobh (instance, hostdata, ascmd, residual);
- + break;
- +
- + case PHASE_DATAOUT:
- + case PHASE_COMMAND:
- + case PHASE_COMMANDSENT:
- + break;
- +
- + default:
- + printk ("scsi%d : warning: invalid phase in unexpected status in (%X)\n",
- + instance->host_no, hostdata->phase);
- + acornscsi_printstatus (instance);
- + return INTR_CONNECTED;
- + }
- +
- + sbic_write (instance, CMND, CMND_XFERINFO|CMND_SBT);
- + while ((sbic_read (instance, ASR) & ASR_DBR) == 0);
- + hostdata->connected->cmd->SCp.Status = sbic_read (instance, DATA);
- + hostdata->phase = PHASE_STATUSIN;
- +
- + return INTR_CONNECTED;
- + }
- +
- +/* ---------------------- message out -------------------------------------------------- */
- +
- + case 0x8e: /* connection - message out phase [only after reconnection] */
- + switch (hostdata->phase) {
- + case PHASE_CONNECTED:
- +#ifdef SCSI2_TAG
- + if (ascmd->cmd->device->tagged_queue) {
- + int i;
- + sbic_write (instance, TRANSCNTL, 3);
- + sbic_write (instance, TRANSCNTM, 0);
- + sbic_write (instance, TRANSCNTH, 0);
- + sbic_write (instance, CMND, CMND_XFERINFO);
- + if (ascmd->cmd->device->tagged_queue)
- + hostdata->last_message = SIMPLE_QUEUE_TAG;
- + for (i = 0; i < 3; i++) {
- + do {
- + asr = sbic_read (instance, ASR) & (ASR_INT | ASR_BSY | ASR_DBR);
- + } while (asr == ASR_BSY);
- + if (!(asr & ASR_DBR)) {
- + printk ("scsi%d : warning: interrupt while sending tag\n",
- + instance->host_no);
- + return INTR_CONNECTED;
- + }
- + switch (i) {
- + case 0:
- + sbic_write (instance, DATA, IDENTIFY(1, hostdata->connected->cmd->lun));
- + break;
- + case 1:
- + sbic_write (instance, DATA, SIMPLE_QUEUE_TAG);
- + break;
- + case 2:
- + sbic_write (instance, DATA, hostdata->connected->cmd->tag);
- + break;
- + }
- + }
- + } else
- +#endif
- + {
- + sbic_write (instance, CMND, CMND_XFERINFO|CMND_SBT);
- + while ((sbic_read (instance, ASR) & ASR_DBR) == 0);
- + sbic_write (instance, DATA, IDENTIFY(1, hostdata->connected->cmd->lun));
- + }
- + hostdata->phase = PHASE_COMMAND;
- + return INTR_CONNECTED;
- +
- + case PHASE_CONNECTED_FOR_ABORT:
- + sbic_write (instance, CMND, CMND_XFERINFO|CMND_SBT);
- + while ((sbic_read (instance, ASR) & ASR_DBR) == 0);
- + sbic_write (instance, DATA, ABORT);
- + hostdata->last_message = ABORT;
- + return INTR_CONNECTED;
- +
- + default:
- + printk ("scsi%d : warning: invalid phase in message out (%X)\n",
- + instance->host_no, hostdata->phase);
- + acornscsi_printstatus (instance);
- + return INTR_CONNECTED;
- + }
- +
- +/* ---------------------- message in --------------------------------------------------- */
- +
- + case 0x1f: /* transfer completed - message in phase */
- + case 0x4f: /* transfer terminated - message in phase */
- + case 0x8f: /* connection - message in phase [only after reconnection] [???] */
- + hostdata->tmpsbiclen = sbic_read (instance, TRANSCNTL) |
- + (sbic_read (instance, TRANSCNTM) << 8) |
- + (sbic_read (instance, TRANSCNTH) << 16);
- + switch (hostdata->phase) {
- + case PHASE_STATUSIN:
- + case PHASE_DATAIN:
- + case PHASE_DATAOUT:
- + break;
- +
- + case PHASE_COMMANDSENT:
- + if (ssr == 0x1f || ssr == 0x4f)
- + break;
- + goto complain_msg_in;
- +
- + case PHASE_RECONNECTED:
- + if (ssr == 0x8f)
- + break;
- +
- + default:
- + complain_msg_in:
- + /* just complain - accept message in anyway otherwise host will hang until reset */
- + printk ("scsi%d : warning: invalid phase (%02X) in message in (%02X)\n",
- + instance->host_no, hostdata->phase, ssr);
- + acornscsi_printstatus (instance);
- + }
- + sbic_write (instance, CMND, CMND_XFERINFO|CMND_SBT);
- + while ((sbic_read (instance, ASR) & ASR_DBR) == 0);
- + hostdata->connected->cmd->SCp.Message = sbic_read (instance, DATA);
- + return INTR_CONNECTED;
- +
- + case 0x20: {/* message in transfer paused */
- + int message = hostdata->connected->cmd->SCp.Message;
- +#if 0
- +printk ("scsi%d : debug : message received:", instance->host_no);
- +print_msg ((unsigned char *)&hostdata->connected->cmd->SCp.Message);
- +printk ("\n");
- +#endif
- + switch (message) {
- + case COMMAND_COMPLETE:
- + sbic_write (instance, CMND, CMND_NEGATEACK);
- + hostdata->phase = PHASE_FINISHED;
- + return INTR_CONNECTED;
- +
- + case SAVE_POINTERS:
- + sbic_write (instance, CMND, CMND_NEGATEACK);
- + acornscsi_savepointers (instance, hostdata);
- + break;
- +
- + case RESTORE_POINTERS:
- + sbic_write (instance, CMND, CMND_NEGATEACK);
- + break;
- +
- + case DISCONNECT:
- + sbic_write (instance, CMND, CMND_NEGATEACK);
- + /* If the device has not issued a 'save pointers' message, do it. */
- + if (!hostdata->connected->saved)
- + acornscsi_savepointers (instance, hostdata);
- + hostdata->phase = PHASE_DISCONNECTING;
- + return INTR_CONNECTED;
- +
- + case LINKED_CMD_COMPLETE:
- + case LINKED_FLG_CMD_COMPLETE:
- +#ifdef SCSI2_LINK
- + /*
- + * We don't support linked commands yet
- + */
- + if (0) {
- +#if (DEBUG & DEBUG_LINKED)
- + printk ("scsi%d : target %d lun %d tag %d linked command complete\n",
- + instance->host_no, ascmd->connected->target, ascmd->connected->tag);
- +#endif
- + /*
- + * A linked command should only terminate with one of these messages
- + * if there are more linked commands available.
- + */
- + if (!ascmd->cmd->next_link) {
- + printk ("scsi%d : warning: target %d lun %d tag %d linked command complete, but no next_link\n",
- + instance->host_no, ascmd->connected->target, ascmd->connected->tag);
- + hostdata->msgout = ABORT;
- + goto go_msg_out;
- + }
- + sbic_write (instance, CMND, CMND_NEGATEACK);
- +
- + ascmd->cmd->next_link->tag = ascmd->cmd->tag;
- + /* scsi_done */
- + hostdata->connected->cmd = ascmd->cmd->next_link;
- + break;
- + }
- +#endif
- + goto reject;
- +
- + case MESSAGE_REJECT:
- + sbic_write (instance, CMND, CMND_NEGATEACK);
- + switch (hostdata->last_message) {
- +#ifdef SCSI2_TAG
- + case HEAD_OF_QUEUE_TAG:
- + case ORDERED_QUEUE_TAG:
- + case SIMPLE_QUEUE_TAG:
- + ascmd->cmd->device->tagged_queue = 0;
- + set_bit (ascmd->cmd->lun, &hostdata->dev[ascmd->cmd->target].busy);
- + printk ("scsi%d : disabling tagged queueing for target %d\n",
- + instance->host_no, ascmd->cmd->target);
- + break;
- +#endif
- + default:
- + break;
- + }
- +
- + default: /* reject message */
- + reject:
- + hostdata->msgout = MESSAGE_REJECT;
- + sbic_write (instance, CMND, CMND_ASSERTATN);
- + while (sbic_read (instance, ASR) & ASR_BSY);
- + if (!(sbic_read (instance, ASR) & ASR_INT)) {
- + sbic_write (instance, CMND, CMND_NEGATEACK);
- + }
- + break;
- + }
- + }
- + return INTR_CONNECTED;
- +
- + default:
- + printk ("scsi%d : unknown interrupt number %02X\n", instance->host_no, ssr);
- + acornscsi_printstatus (instance);
- + return INTR_CONTINUE;
- + }
- +}
- +
- +static void acornscsi_intr(int irq, struct pt_regs *regs)
- +{
- + struct Scsi_Host *instance;
- + int need_command, need_command_done;
- +
- + do {
- + need_command_done = 0;
- + for (instance = first_instance; instance && (instance->hostt == the_template);
- + instance = instance->next) {
- + if (instance->irq != irq)
- + continue;
- +
- + need_command = 0;
- +
- + if (read_irqstat(instance) & 2)
- + /* setup for next buffer & transfer current buffer */
- + if (acornscsi_handletransfer(instance))
- + continue;
- +
- + if (read_irqstat(instance) & 8)
- + switch (acornscsi_sbicintr (instance)) {
- + case INTR_NOTCONNECTED:
- + need_command = 1;
- + break;
- + case INTR_CONNECTED:
- + need_command_done = 1;
- + break;
- + }
- + if (need_command) {
- + acornscsi_startsendcommand (instance);
- + need_command_done = 1;
- + }
- + }
- + }
- + while(need_command_done);
- +}
- +
- +/*=============================================================================================
- + * Interfaces between interrupt handler and rest of scsi code
- + */
- +
- +/*
- + * Function : acornscsi_queuecmd (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
- + *
- + * Purpose : queues a SCSI command
- + *
- + * Params : cmd - SCSI command
- + * done - function called on completion, with pointer to command descriptor
- + *
- + * Returns : 0
- + *
- + */
- +int acornscsi_queuecmd (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
- +{
- + struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)cmd->host->hostdata;
- + struct as_cmnd *as_cmd;
- + int i;
- +#if (DEBUG & DEBUG_QUEUES)
- + const char *where;
- +#endif
- +
- + if (!done) {
- + /* there should be some way of rejecting errors like this without panicing... */
- + panic ("scsi%d : fatal : queuecommand called with NULL done function [cmd=%p]\n",
- + cmd->host->host_no, cmd);
- + return -EINVAL;
- + }
- +
- +#if (DEBUG & DEBUG_NO_WRITE)
- + if (acornscsi_cmdtype (cmd->cmnd[0]) == CMD_WRITE) {
- + printk ("scsi%d : id %d: WRITE attempted with NO_WRITE flag set\n",
- + cmd->host->host_no, cmd->target);
- + cmd->result = DID_ERROR << 16;
- + done (cmd);
- + return 0;
- + }
- +#endif
- +
- + cmd->host_scribble = NULL;
- + cmd->scsi_done = done;
- + cmd->result = 0;
- + cmd->SCpdirection = acornscsi_writedirection (cmd->cmnd[0]);
- +
- + acornscsi_initialisebuffer (cmd);
- +
- + cmd->tag = 0;
- +
- + for (i = 0, as_cmd = hostdata->queuedcommands; i < CAN_QUEUE; i++, as_cmd ++) {
- + if (set_bit (0, (void *)&as_cmd->used) == 0) {
- + if (as_cmd->cmd) {
- + printk ("scsi%d : warning: free command found wasn't free\n",
- + cmd->host->host_no);
- + continue;
- + }
- + as_cmd->cmd = cmd;
- +
- + if (cmd->cmnd[0] == REQUEST_SENSE) {
- + cli ();
- + as_cmd->next = hostdata->dev[cmd->target].issue_head;
- + hostdata->dev[cmd->target].issue_head = as_cmd;
- + sti ();
- +#if (DEBUG & DEBUG_QUEUES)
- + where = "head";
- +#endif
- + } else {
- + cli ();
- + as_cmd->next = NULL;
- + if (hostdata->dev[cmd->target].issue_head == NULL)
- + hostdata->dev[cmd->target].issue_head = as_cmd;
- + if (hostdata->dev[cmd->target].issue_tail)
- + hostdata->dev[cmd->target].issue_tail->next = as_cmd;
- + hostdata->dev[cmd->target].issue_tail = as_cmd;
- + sti ();
- +#if (DEBUG & DEBUG_QUEUES)
- + where = "tail";
- +#endif
- + }
- + hostdata->stats.queues += 1;
- +#if (DEBUG & DEBUG_QUEUES)
- + printk ("scsi%d : debug : command put on %s of queue for target %d\n",
- + cmd->host->host_no, where, cmd->target);
- +#endif
- + as_cmd->buffer = 2;
- + as_cmd->totallen = cmd->request_bufflen;
- + as_cmd->queued_jiffies = jiffies;
- + acornscsi_startsendcommand (cmd->host);
- + return 0;
- + }
- + }
- +
- + printk ("scsi%d : error : could not find a free slot for command?\n", cmd->host->host_no);
- +
- + cmd->result = DID_ERROR << 16;
- + done (cmd);
- + return -1;
- +}
- +
- +int acornscsi_abort(Scsi_Cmnd * cmd)
- +{
- + struct Scsi_Host *instance = cmd->host;
- + struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *) instance->hostdata;
- + struct as_cmnd *ascmd, *asprev;
- +
- + hostdata->stats.aborts += 1;
- +
- + printk("acornscsi_abort: ");
- + print_sbic_status(sbic_read (instance, ASR), sbic_read (instance, SSR),
- + sbic_read (instance, CMNDPHASE));
- +
- + /*
- + * Check to see if the command hasn't been issued. If so, just remove it from the queue.
- + */
- + for (ascmd = hostdata->dev[cmd->target].issue_head, asprev = NULL; ascmd;
- + asprev = ascmd, ascmd = ascmd->next) {
- +
- + if (ascmd->cmd == cmd) {
- + if (ascmd == hostdata->dev[cmd->target].issue_tail)
- + hostdata->dev[cmd->target].issue_tail = asprev;
- + if (asprev)
- + asprev->next = ascmd->next;
- + else
- + hostdata->dev[cmd->target].issue_head = ascmd->next;
- + ascmd->next = NULL;
- + sti();
- +
- + ascmd->cmd->result = DID_ABORT << 16;
- +#if (DEBUG & DEBUG_ABORT)
- + printk("scsi%d : abort removed command from issue queue\n", instance->host_no);
- +#endif
- + acornscsi_done (instance, ascmd);
- + return SCSI_ABORT_SUCCESS;
- + }
- + }
- +
- + /*
- + * If the command is connected, try to abort it
- + */
- + if (hostdata->connected && hostdata->connected->cmd == cmd) {
- + sti();
- +#if (DEBUG & DEBUG_ABORT)
- + printk("scsi%d : aborting connected command\n", instance->host_no);
- +#endif
- + hostdata->connected->cmd->result = DID_ABORT << 16; /* Hmm... doesn't seem to work.. */
- + sbic_write (instance, CMND, CMND_ASSERTATN); /* DISCONNECT? */
- + return SCSI_ABORT_PENDING;
- + }
- +
- +#if 0 /* we can now schedule an abort of a command while one is executing */
- + /*
- + * If a command is connected, we can't do anything to abort it at the moment.
- + */
- + if (hostdata->connected) {
- + sti();
- +#if (DEBUG & DEBUG_ABORT)
- + printk("scsi%d : abort failed, command connected\n", instance->host_no);
- +#endif
- + return SCSI_ABORT_BUSY;
- + }
- +#endif
- +
- + /*
- + * If the command is disconnected, and there are no connected commands, reconnect
- + * and send abort message
- + */
- + for (ascmd = hostdata->dev[cmd->target].disconnected, asprev = NULL; asprev;
- + asprev = ascmd, ascmd = ascmd->next) {
- + if (ascmd->cmd == cmd) {
- +#if (DEBUG & DEBUG_ABORT)
- + printk("scsi%d : aborting disconnected command\n", instance->host_no);
- +#endif
- + if (asprev)
- + asprev->next = ascmd->next;
- + else
- + hostdata->dev[cmd->target].disconnected = ascmd->next;
- + ascmd->next = NULL;
- +
- + hostdata->aborting = ascmd;
- + sti ();
- +
- + acornscsi_startsendcommand (instance);
- + return SCSI_ABORT_PENDING;
- + }
- + }
- +
- + sti();
- + printk ("scsi%d : warning: SCSI command probably completed successfully before abortion\n",
- + instance->host_no);
- +while (1);
- + return SCSI_ABORT_NOT_RUNNING;
- +}
- +
- +int acornscsi_reset(Scsi_Cmnd * cmd)
- +{
- + struct Scsi_Host *instance = cmd->host;
- + struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
- +
- + hostdata->stats.resets += 1;
- +
- + printk("acornscsi_reset:\n");
- + print_sbic_status(sbic_read (instance, ASR), sbic_read (instance, SSR), sbic_read (instance, CMNDPHASE));
- + hostdata->connected = NULL;
- + sbic_reset(instance);
- +#if 0
- + cmd->result = DID_RESET << 16;
- + acornscsi_done (instance, cmd);
- +#endif
- + return SCSI_RESET_SUCCESS;
- +}
- +
- +/*==============================================================================================
- + * initialisation & miscellaneous support
- + */
- +static struct expansion_card *ecs[4];
- +
- +static void acornscsi_init (struct Scsi_Host *instance)
- +{
- + struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *) instance->hostdata;
- + int i;
- +
- + hostdata->busy = 0;
- + hostdata->phase = 0;
- + hostdata->page_reg = 0x40;
- + hostdata->connecting = NULL;
- + hostdata->aborting = NULL;
- + hostdata->connected = NULL;
- +
- + for (i = 0; i < CAN_QUEUE; i++) {
- + hostdata->queuedcommands[i].used = 0;
- + hostdata->queuedcommands[i].cmd = NULL;
- + hostdata->queuedcommands[i].next = NULL;
- + hostdata->queuedcommands[i].buf0addr = i * 0x2000;
- + hostdata->queuedcommands[i].buf1addr = i * 0x2000 + 0x1000;
- + hostdata->queuedcommands[i].buf0len = 0;
- + hostdata->queuedcommands[i].buf1len = 0;
- + }
- +
- + for (i = 0; i < 8; i++) {
- + hostdata->dev[i].issue_head = NULL;
- + hostdata->dev[i].issue_tail = NULL;
- + hostdata->dev[i].disconnected = NULL;
- + hostdata->dev[i].busy = 0;
- + }
- +
- + hostdata->stats.queues = 0;
- + hostdata->stats.removes= 0;
- + hostdata->stats.fins = 0;
- + hostdata->stats.reads = 0;
- + hostdata->stats.writes = 0;
- + hostdata->stats.aborts = 0;
- + hostdata->stats.resets = 0;
- + hostdata->stats.disconnects = 0;
- +
- + page_write(instance, 0x40);
- +
- + if (!the_template)
- + {
- + the_template = instance->hostt;
- + first_instance = instance;
- + }
- +
- + /* setup sbic - WD33C93A */
- + sbic_reset(instance);
- +
- + /* setup dmac - uPC71071 */
- + dmac_write (instance, INIT, 0);
- + dmac_write (instance, INIT, INIT_8BIT);
- + dmac_write (instance, CHANNEL, CHANNEL_0);
- + dmac_write (instance, DEVCON0, INIT_DEVCON0);
- + dmac_write (instance, DEVCON1, INIT_DEVCON1);
- +}
- +
- +int acornscsi_detect(Scsi_Host_Template * tpnt)
- +{
- + static int acornscsi_prods[] = { PRODS };
- + static int acornscsi_manus[] = { MANUS };
- + int i, count = 0;
- + struct Scsi_Host *instance;
- +
- + tpnt->proc_dir = &proc_scsi_acornscsi;
- +
- + for (i = 0; i < 4; i++)
- + ecs[i] = NULL;
- +
- + while(1) {
- + ecs[count] = ecard_find(0, sizeof(acornscsi_prods), acornscsi_prods, acornscsi_manus);
- + if(!ecs[count])
- + break;
- +
- + instance = scsi_register (tpnt, sizeof(struct acornscsi_hostdata));
- + instance->io_port = ((int)ecs[count]->r_podaddr & ~0x003C0000) >> 2;
- + instance->irq = ecs[count]->irq;
- +
- + if (instance->irq != 0xff) {
- + if (request_irq(instance->irq, acornscsi_intr, SA_INTERRUPT, "acorn scsi"))
- + {
- + printk("scsi%d : IRQ%d not free, interrupts disabled\n",
- + instance->host_no, instance->irq);
- + instance->irq = 0xff;
- + }
- + }
- + if (instance->irq == 0xff) {
- + printk("scsi%d : interrupts not enabled. For better interactive performance,\n",
- + instance->host_no);
- + printk("scsi%d : please find out why I cannot get the interrupt.\n",
- + instance->host_no);
- + }
- +
- + ecard_claim(ecs[count]); /* Must claim here - card produces irq on reset */
- + acornscsi_init(instance);
- +
- + ++count;
- + }
- + return count;
- +}
- +
- +const char *acornscsi_info(struct Scsi_Host *host)
- +{
- + static char string[100], *p;
- +
- + p = string;
- +
- + p += sprintf (string, "%s at port %X irq", host->hostt->name, host->io_port);
- + if (host->irq == 0xff)
- + p += sprintf (p, "s disabled");
- + else
- + p += sprintf (p, " %d", host->irq);
- + sprintf (p, " version %d.%02d", VER_MAJOR, VER_MINOR);
- +
- + return string;
- +}
- +
- +int acornscsi_proc_info(char *buffer, char **start, off_t offset,
- + int length, int host_no, int inout)
- +{
- + int len, pos, begin, first;
- + struct Scsi_Host *host = scsi_hostlist;
- + struct acornscsi_hostdata *hostdata;
- + Scsi_Device *scd;
- +
- + while (host) {
- + if (host->host_no == host_no)
- + break;
- + host = host->next;
- + }
- +
- + hostdata = (struct acornscsi_hostdata *) host->hostdata;
- +
- + if (inout == 1)
- + return -EINVAL;
- +
- + begin = 0;
- + pos = sprintf(buffer,
- + "Acorn SCSI driver version %d.%02d\n", VER_MAJOR, VER_MINOR);
- +
- + len = sprintf(buffer+pos,
- + "Address: %08X IRQ : %d\n"
- + "SBIC : WD33C93 DMAC: uPC71071\n\n"
- + "Statistics:\n", host->io_port, host->irq);
- + pos += len;
- +
- + len = sprintf(buffer+pos,
- + "Queued commands: %ld\n"
- + "Issued commands: %ld\n"
- + "Done commands : %ld\n"
- + "Reads : %ld\n"
- + "Writes : %ld\n"
- + "Others : %ld\n"
- + "Disconnects : %ld\n"
- + "Aborts : %ld\n"
- + "Resets : %ld\n",
- + hostdata->stats.queues,
- + hostdata->stats.removes,
- + hostdata->stats.fins,
- + hostdata->stats.reads,
- + hostdata->stats.writes,
- + hostdata->stats.miscs,
- + hostdata->stats.disconnects,
- + hostdata->stats.aborts,
- + hostdata->stats.resets);
- + pos += len;
- +
- + scd = scsi_devices;
- +
- + first = 1;
- +
- + while (scd) {
- + if (scd->host == host) {
- + if (first) {
- + len = sprintf(buffer+pos,
- + "\nAttached devices:\n");
- + pos += len;
- + first = 0;
- + }
- +
- + proc_print_scsidevice(scd, buffer, &len, pos);
- + pos += len;
- + pos += sprintf (buffer+pos, "Extensions: ");
- + if (scd->tagged_supported)
- + pos += sprintf (buffer+pos, "TAG %sabled [%d] ",
- + scd->tagged_queue ? "en" : "dis", scd->current_tag);
- + pos += sprintf (buffer+pos, "\n");
- +
- + if (pos + begin < offset) {
- + begin += pos;
- + pos = 0;
- + }
- + if (pos + begin > offset + length)
- + break;
- + }
- + scd = scd->next;
- + }
- +
- + if (first) {
- + len = sprintf(buffer+pos,
- + "Attached devices: none\n");
- + pos += len;
- + }
- +
- + *start = buffer + (offset - begin);
- + pos -= offset - begin;
- + if (pos > length)
- + pos = length;
- +
- + return pos;
- +}
- +
- +#include "sd.h"
- +#include "scsi_ioctl.h"
- +
- +int acornscsi_biosparam (Scsi_Disk *disk, kdev_t dev, int info_array[])
- +{
- + info_array[0] = 16;
- + info_array[1] = 63;
- + info_array[2] = 2112;
- +
- + return 0;
- +}
- +#ifdef MODULE
- +
- +Scsi_Host_Template driver_template = ACORNSCSI_3;
- +
- +#define SCSI_CLEANUP \
- + { int i; for (i = 0; i<4; i++) if (ecs[i]) ecard_release(ecs[i]); }
- +
- +#include "scsi_module.c"
- +#endif
- diff -urNwbB linux/arch/arm/drivers/scsi/acornscsi.h linux.arm/arch/arm/drivers/scsi/acornscsi.h
- --- linux/arch/arm/drivers/scsi/acornscsi.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/scsi/acornscsi.h Sun Feb 25 13:45:55 1996
- @@ -0,0 +1,306 @@
- +#ifndef ACORNSCSI_H
- +#define ACORNSCSI_H
- +
- +#ifndef ASM
- +extern int acornscsi_detect (Scsi_Host_Template *);
- +extern const char *acornscsi_info (struct Scsi_Host *);
- +extern int acornscsi_queuecmd (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
- +extern int acornscsi_abort (Scsi_Cmnd *);
- +extern int acornscsi_reset (Scsi_Cmnd *);
- +extern int acornscsi_proc_info (char *, char **, off_t, int, int, int);
- +extern int acornscsi_biosparam (Disk *, kdev_t, int []);
- +
- +#ifndef NULL
- +#define NULL 0
- +#endif
- +
- +#ifndef CMD_PER_LUN
- +#define CMD_PER_LUN 2
- +#endif
- +
- +#ifndef CAN_QUEUE
- +#define CAN_QUEUE 16
- +#endif
- +
- +#ifndef PROC_SCSI_AKA30
- +#include "linux/proc_fs.h"
- +#define PROC_SCSI_AKA30 PROC_SCSI_EATA
- +#endif
- +
- +#define ACORNSCSI_3 \
- + { \
- + NULL, \
- + NULL, \
- + NULL, \
- + acornscsi_proc_info, \
- + "Acorn 16-bit SCSI", \
- + acornscsi_detect, \
- + NULL, \
- + acornscsi_info, \
- + NULL, \
- + acornscsi_queuecmd, \
- + acornscsi_abort, \
- + acornscsi_reset, \
- + NULL, \
- + acornscsi_biosparam, \
- + CAN_QUEUE, \
- + 7, \
- + SG_ALL, \
- + CMD_PER_LUN, \
- + 0, \
- + 0, \
- + DISABLE_CLUSTERING \
- + }
- +
- +#ifndef HOSTS_C
- +
- +/* SBIC registers */
- +#define OWNID 0
- +#define OWNID_FS1 (1<<7)
- +#define OWNID_FS2 (1<<6)
- +#define OWNID_EHP (1<<4)
- +#define OWNID_EAF (1<<3)
- +
- +#define CTRL 1
- +#define CTRL_DMAMODE (1<<7)
- +#define CTRL_DMADBAMODE (1<<6)
- +#define CTRL_DMABURST (1<<5)
- +#define CTRL_DMAPOLLED 0
- +#define CTRL_HHP (1<<4)
- +#define CTRL_EDI (1<<3)
- +#define CTRL_IDI (1<<2)
- +#define CTRL_HA (1<<1)
- +#define CTRL_HSP (1<<0)
- +
- +#define TIMEOUT 2
- +#define TOTSECTS 3
- +#define TOTHEADS 4
- +#define TOTCYLH 5
- +#define TOTCYLL 6
- +#define LOGADDRH 7
- +#define LOGADDRM2 8
- +#define LOGADDRM1 9
- +#define LOGADDRL 10
- +#define SECTORNUM 11
- +#define HEADNUM 12
- +#define CYLH 13
- +#define CYLL 14
- +#define TARGETLUN 15
- +#define TARGETLUN_TLV (1<<7)
- +#define TARGETLUN_DOK (1<<6)
- +
- +#define CMNDPHASE 16
- +#define SYNCHTRANSFER 17
- +#define SYNCHTRANSFER_OF0 0x00
- +#define SYNCHTRANSFER_OF1 0x01
- +#define SYNCHTRANSFER_OF2 0x02
- +#define SYNCHTRANSFER_OF3 0x03
- +#define SYNCHTRANSFER_OF4 0x04
- +#define SYNCHTRANSFER_OF5 0x05
- +#define SYNCHTRANSFER_OF6 0x06
- +#define SYNCHTRANSFER_OF7 0x07
- +#define SYNCHTRANSFER_OF8 0x08
- +#define SYNCHTRANSFER_OF9 0x09
- +#define SYNCHTRANSFER_OF10 0x0A
- +#define SYNCHTRANSFER_OF11 0x0B
- +#define SYNCHTRANSFER_OF12 0x0C
- +#define SYNCHTRANSFER_8DBA 0x00
- +#define SYNCHTRANSFER_2DBA 0x20
- +#define SYNCHTRANSFER_3DBA 0x30
- +#define SYNCHTRANSFER_4DBA 0x40
- +#define SYNCHTRANSFER_5DBA 0x50
- +#define SYNCHTRANSFER_6DBA 0x60
- +#define SYNCHTRANSFER_7DBA 0x70
- +
- +#define TRANSCNTH 18
- +#define TRANSCNTM 19
- +#define TRANSCNTL 20
- +#define DESTID 21
- +#define DESTID_SCC (1<<7)
- +#define DESTID_DPD (1<<6)
- +
- +#define SOURCEID 22
- +#define SOURCEID_ER (1<<7)
- +#define SOURCEID_ES (1<<6)
- +#define SOURCEID_DSP (1<<5)
- +#define SOURCEID_SIV (1<<4)
- +
- +#define SSR 23
- +#define CMND 24
- +#define CMND_RESET 0x00
- +#define CMND_ABORT 0x01
- +#define CMND_ASSERTATN 0x02
- +#define CMND_NEGATEACK 0x03
- +#define CMND_DISCONNECT 0x04
- +#define CMND_RESELECT 0x05
- +#define CMND_SELWITHATN 0x06
- +#define CMND_SELECT 0x07
- +#define CMND_SELECTATNTRANSFER 0x08
- +#define CMND_SELECTTRANSFER 0x09
- +#define CMND_RESELECTRXDATA 0x0A
- +#define CMND_RESELECTTXDATA 0x0B
- +#define CMND_WAITFORSELRECV 0x0C
- +#define CMND_SENDSTATCMD 0x0D
- +#define CMND_SENDDISCONNECT 0x0E
- +#define CMND_SETIDI 0x0F
- +#define CMND_RECEIVECMD 0x10
- +#define CMND_RECEIVEDTA 0x11
- +#define CMND_RECEIVEMSG 0x12
- +#define CMND_RECEIVEUSP 0x13
- +#define CMND_SENDCMD 0x14
- +#define CMND_SENDDATA 0x15
- +#define CMND_SENDMSG 0x16
- +#define CMND_SENDUSP 0x17
- +#define CMND_TRANSLATEADDR 0x18
- +#define CMND_XFERINFO 0x20
- +#define CMND_SBT (1<<7)
- +
- +#define DATA 25
- +#define ASR 26
- +#define ASR_INT (1<<7)
- +#define ASR_LCI (1<<6)
- +#define ASR_BSY (1<<5)
- +#define ASR_CIP (1<<4)
- +#define ASR_PE (1<<1)
- +#define ASR_DBR (1<<0)
- +
- +/* DMAC registers */
- +#define INIT 0x00
- +#define INIT_8BIT (1)
- +
- +#define CHANNEL 0x80
- +#define CHANNEL_0 0x00
- +#define CHANNEL_1 0x01
- +#define CHANNEL_2 0x02
- +#define CHANNEL_3 0x03
- +
- +#define TXCNTLO 0x01
- +#define TXCNTHI 0x81
- +#define TXADRLO 0x02
- +#define TXADRMD 0x82
- +#define TXADRHI 0x03
- +
- +#define DEVCON0 0x04
- +#define DEVCON0_AKL (1<<7)
- +#define DEVCON0_RQL (1<<6)
- +#define DEVCON0_EXW (1<<5)
- +#define DEVCON0_ROT (1<<4)
- +#define DEVCON0_CMP (1<<3)
- +#define DEVCON0_DDMA (1<<2)
- +#define DEVCON0_AHLD (1<<1)
- +#define DEVCON0_MTM (1<<0)
- +
- +#define DEVCON1 0x84
- +#define DEVCON1_WEV (1<<1)
- +#define DEVCON1_BHLD (1<<0)
- +
- +#define MODECON 0x05
- +#define MODECON_WOED 0x01
- +#define MODECON_VERIFY 0x00
- +#define MODECON_READ 0x04
- +#define MODECON_WRITE 0x08
- +#define MODECON_AUTOINIT 0x10
- +#define MODECON_ADDRDIR 0x20
- +#define MODECON_DEMAND 0x00
- +#define MODECON_SINGLE 0x40
- +#define MODECON_BLOCK 0x80
- +#define MODECON_CASCADE 0xC0
- +
- +#define STATUS 0x85
- +#define TEMPLO 0x06
- +#define TEMPHI 0x86
- +#define REQREG 0x07
- +#define MASKREG 0x87
- +#define MASKREG_M0 0x01
- +#define MASKREG_M1 0x02
- +#define MASKREG_M2 0x04
- +#define MASKREG_M3 0x08
- +
- +/* miscellaneous internal variables */
- +
- +#define CMD_READ 0
- +#define CMD_WRITE 1
- +#define CMD_MISC 2
- +
- +#define PHASE_IDLE 0x00
- +#define PHASE_CONNECTING 0x41
- +#define PHASE_CONNECTED 0x42
- +#define PHASE_COMMAND 0x43
- +#define PHASE_COMMANDSENT 0x44
- +
- +#define PHASE_DATAIN 0x46
- +#define PHASE_DATAOUT 0x47
- +
- +#define PHASE_STATUSIN 0x4c
- +#define PHASE_FINISHED 0x4d
- +#define PHASE_DISCONNECTING 0x4f
- +#define PHASE_RECONNECTED 0x50
- +
- +#define PHASE_CONNECTING_FOR_ABORT 0x80
- +#define PHASE_CONNECTED_FOR_ABORT 0x81
- +
- +#define INTR_CONNECTED 0
- +#define INTR_NOTCONNECTED 1
- +#define INTR_CONTINUE 2
- +
- +struct as_stats {
- + unsigned long queues;
- + unsigned long removes;
- + unsigned long fins;
- + unsigned long reads;
- + unsigned long writes;
- + unsigned long miscs;
- + unsigned long aborts;
- + unsigned long resets;
- + unsigned long disconnects;
- +};
- +
- +struct as_cmnd {
- + Scsi_Cmnd *cmd;
- + struct as_cmnd *next; /* next command in queue */
- + int used; /* block in use */
- + unsigned long queued_jiffies;
- + unsigned long connect_jiffies;
- + unsigned long totallen;
- + unsigned long buf0addr;
- + unsigned long buf1addr;
- + unsigned long saved_dmac_address;
- + unsigned long saved_dmac_transcnt;
- + unsigned long saved_sbic_transcnt;
- + unsigned short buf0len;
- + unsigned short buf1len;
- + unsigned char buffer;
- + unsigned char saved;
- +};
- +
- +struct as_dev {
- + struct as_cmnd *issue_head;
- + struct as_cmnd *issue_tail;
- + struct as_cmnd *disconnected; /* commands disconnected */
- + unsigned char busy; /* luns on device that are busy */
- +};
- +
- +struct acornscsi_hostdata {
- + int busy; /* in process of connecting */
- + int page_reg; /* current page register value */
- +
- + /* data for the connected command */
- + int phase; /* phase of command */
- + int last_message; /* last message sent */
- + int msgout; /* message to be sent out */
- + unsigned long tmpsbiclen;
- +
- + struct as_cmnd *connecting; /* command that is connecting */
- + struct as_cmnd *aborting; /* command that is aborting */
- + struct as_cmnd *connected; /* command that is connected */
- +
- + struct as_cmnd queuedcommands[CAN_QUEUE]; /* commands */
- +
- + struct as_dev dev[8];
- + struct as_stats stats;
- +};
- +
- +#endif
- +
- +#endif /* ndef ASM */
- +#endif /* ACORNSCSI_H */
- diff -urNwbB linux/arch/arm/drivers/scsi/cumana_1.c linux.arm/arch/arm/drivers/scsi/cumana_1.c
- --- linux/arch/arm/drivers/scsi/cumana_1.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/scsi/cumana_1.c Sat Feb 24 09:36:55 1996
- @@ -0,0 +1,318 @@
- +#define AUTOSENSE
- +#define PSEUDO_DMA
- +
- +/*
- + * Generic Generic NCR5380 driver
- + *
- + * Copyright 1995, Russell King
- + *
- + * ALPHA RELEASE 1.
- + *
- + * For more information, please consult
- + *
- + * NCR 5380 Family
- + * SCSI Protocol Controller
- + * Databook
- + *
- + * NCR Microelectronics
- + * 1635 Aeroplaza Drive
- + * Colorado Springs, CO 80916
- + * 1+ (719) 578-3400
- + * 1+ (800) 334-5454
- + */
- +
- +
- +/*
- + * Options :
- + *
- + * PARITY - enable parity checking. Not supported.
- + *
- + * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
- + *
- + * USLEEP - enable support for devices that don't disconnect. Untested.
- + */
- +
- +/*
- + * $Log: cumana_NCR5380.c,v $
- + */
- +
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include <linux/signal.h>
- +#include <linux/sched.h>
- +#include "../block/blk.h"
- +#include "scsi.h"
- +#include "hosts.h"
- +#include "cumana_1.h"
- +#include "NCR5380.h"
- +#include "constants.h"
- +
- +#include <asm/ecard.h>
- +
- +static unsigned int cumana_1_prods[] = { 0x00a0 };
- +static unsigned int cumana_1_manus[] = { 0x003a };
- +
- +/*
- + * Function : cumana_NCR5380_setup(char *str, int *ints)
- + *
- + * Purpose : LILO command line initialization of the overrides array,
- + *
- + * Inputs : str - unused, ints - array of integer parameters with ints[0]
- + * equal to the number of ints.
- + *
- + */
- +
- +void cumana_NCR5380_setup(char *str, int *ints) {
- +}
- +
- +/*
- + * Function : int cumana_NCR5380_detect(Scsi_Host_Template * tpnt)
- + *
- + * Purpose : initializes cumana NCR5380 driver based on the
- + * command line / compile time port and irq definitions.
- + *
- + * Inputs : tpnt - template for this SCSI adapter.
- + *
- + * Returns : 1 if a host adapter was found, 0 if not.
- + *
- + */
- +
- +int cumana_NCR5380_detect(Scsi_Host_Template * tpnt) {
- + struct expansion_card *ec;
- + int count = 0;
- + struct Scsi_Host *instance;
- +
- + while(1) {
- + if((ec = ecard_find(0, sizeof(cumana_1_prods), cumana_1_prods, cumana_1_manus)) == NULL)
- + break;
- +
- + instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
- + instance->io_port = (((int)ec->r_podaddr & ~0x003c0000UL) | 0x00242000UL) >> 2;
- +
- + NCR5380_init(instance, 0);
- +
- + instance->irq = ec->irq;
- + ((struct NCR5380_hostdata *)instance->hostdata)->ctrl=0;
- + outb(0x00, instance->io_port - 577);
- +
- + if (instance->irq != IRQ_NONE)
- + if (request_irq(instance->irq, cumana_NCR5380_intr, SA_INTERRUPT, "Cumana SCSI 1 card")) {
- + printk("scsi%d: IRQ%d not free, interrupts disabled\n",
- + instance->host_no, instance->irq);
- + instance->irq = IRQ_NONE;
- + }
- +
- + if (instance->irq == IRQ_NONE) {
- + printk("scsi%d: interrupts not enabled. for better interactive performance,\n", instance->host_no);
- + printk("scsi%d: please jumper the board for a free IRQ.\n", instance->host_no);
- + }
- +
- + printk("scsi%d: at port %X irq", instance->host_no, instance->io_port);
- + if (instance->irq == IRQ_NONE)
- + printk ("s disabled");
- + else
- + printk (" %d", instance->irq);
- + printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
- + CAN_QUEUE, CMD_PER_LUN, CUMANA_NCR5380_PUBLIC_RELEASE);
- + printk("\nscsi%d:", instance->host_no);
- + NCR5380_print_options(instance);
- + printk("\n");
- +
- + ecard_claim(ec);
- + ++count;
- + }
- + return count;
- +}
- +
- +const char * cumana_NCR5380_info (struct Scsi_Host *spnt) {
- + return "";
- +}
- +
- +#ifdef NOT_EFFICIENT
- +#define CTRL(p,v) outb(*ctrl = (v), (p) - 577)
- +#define STAT(p) inb((p)+1)
- +#define IN(p) inb((p))
- +#define OUT(v,p) outb((v), (p))
- +#else
- +#define CTRL(p,v) (p[-2308] = (*ctrl = (v)))
- +#define STAT(p) (p[4])
- +#define IN(p) (*(p))
- +#define IN2(p) ((unsigned short)(*(volatile unsigned long *)(p)))
- +#define OUT(v,p) (*(p) = (v))
- +#define OUT2(v,p) (*((volatile unsigned long *)(p)) = (v))
- +#endif
- +#define L(v) (((v)<<16)|((v) & 0x0000ffff))
- +#define H(v) (((v)>>16)|((v) & 0xffff0000))
- +
- +static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr,
- + int len)
- +{
- + int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
- + int oldctrl = *ctrl;
- + unsigned long *laddr;
- +#ifdef NOT_EFFICIENT
- + int ioaddr = instance->io_port;
- + int dma_io = ioaddr & ~(0x3C0000>>2);
- +#else
- + volatile unsigned char *ioaddr = (unsigned char *)(instance->io_port<<2);
- + volatile unsigned char *dma_io = (unsigned char *)((int)ioaddr & ~0x3C0000);
- +#endif
- +
- + if(!len) return 0;
- +
- + CTRL(ioaddr, 0x02);
- + laddr = (unsigned long *)addr;
- + while(len >= 32)
- + {
- + int status;
- + unsigned long v;
- + status = STAT(ioaddr);
- + if(status & 0x80)
- + goto end;
- + if(!(status & 0x40))
- + continue;
- + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
- + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
- + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
- + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
- + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
- + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
- + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
- + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
- + len -= 32;
- + if(len == 0)
- + break;
- + }
- +
- + addr = (unsigned char *)laddr;
- + CTRL(ioaddr, 0x12);
- + while(len > 0)
- + {
- + int status;
- + status = STAT(ioaddr);
- + if(status & 0x80)
- + goto end;
- + if(status & 0x40)
- + {
- + OUT(*addr++, dma_io);
- + if(--len == 0)
- + break;
- + }
- +
- + status = STAT(ioaddr);
- + if(status & 0x80)
- + goto end;
- + if(status & 0x40)
- + {
- + OUT(*addr++, dma_io);
- + if(--len == 0)
- + break;
- + }
- + }
- +end:
- + CTRL(ioaddr, oldctrl|0x40);
- + return len;
- +}
- +
- +static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr,
- + int len)
- +{
- + int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
- + int oldctrl = *ctrl;
- + unsigned long *laddr;
- +#ifdef NOT_EFFICIENT
- + int ioaddr = instance->io_port;
- + int dma_io = ioaddr & ~(0x3C0000>>2);
- +#else
- + volatile unsigned char *ioaddr = (unsigned char *)(instance->io_port<<2);
- + volatile unsigned char *dma_io = (unsigned char *)((int)ioaddr & ~0x3C0000);
- +#endif
- +
- + if(!len) return 0;
- +
- + CTRL(ioaddr, 0x00);
- + laddr = (unsigned long *)addr;
- + while(len >= 32)
- + {
- + int status;
- + status = STAT(ioaddr);
- + if(status & 0x80)
- + goto end;
- + if(!(status & 0x40))
- + continue;
- + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
- + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
- + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
- + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
- + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
- + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
- + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
- + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
- + len -= 32;
- + if(len == 0)
- + break;
- + }
- +
- + addr = (unsigned char *)laddr;
- + CTRL(ioaddr, 0x10);
- + while(len > 0)
- + {
- + int status;
- + status = STAT(ioaddr);
- + if(status & 0x80)
- + goto end;
- + if(status & 0x40)
- + {
- + *addr++ = IN(dma_io);
- + if(--len == 0)
- + break;
- + }
- +
- + status = STAT(ioaddr);
- + if(status & 0x80)
- + goto end;
- + if(status & 0x40)
- + {
- + *addr++ = IN(dma_io);
- + if(--len == 0)
- + break;
- + }
- + }
- +end:
- + CTRL(ioaddr, oldctrl|0x40);
- + return len;
- +}
- +
- +#undef STAT
- +#undef CTRL
- +#undef IN
- +#undef OUT
- +
- +#define CTRL(p,v) outb(*ctrl = (v), (p) - 577)
- +
- +static char cumana_NCR5380_read(struct Scsi_Host *instance, int reg)
- +{
- + int ioaddr = instance->io_port;
- + int i;
- + int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
- +
- + CTRL(ioaddr, 0);
- + i = inb(ioaddr + 64 + reg);
- + CTRL(ioaddr, 0x40);
- +
- + return i;
- +}
- +
- +static void cumana_NCR5380_write(struct Scsi_Host *instance, int reg, int value)
- +{
- + int ioaddr = instance->io_port;
- + int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
- +
- + CTRL(ioaddr, 0);
- + outb(value, ioaddr + 64 + reg);
- + CTRL(ioaddr, 0x40);
- +}
- +
- +#undef CTRL
- +
- +#include "NCR5380.c"
- diff -urNwbB linux/arch/arm/drivers/scsi/cumana_1.h linux.arm/arch/arm/drivers/scsi/cumana_1.h
- --- linux/arch/arm/drivers/scsi/cumana_1.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/scsi/cumana_1.h Sat Feb 24 09:36:55 1996
- @@ -0,0 +1,88 @@
- +/*
- + * Cumana Generic NCR5380 driver defines
- + *
- + * Copyright 1993, Drew Eckhardt
- + * Visionary Computing
- + * (Unix and Linux consulting and custom programming)
- + * drew@colorado.edu
- + * +1 (303) 440-4894
- + *
- + * ALPHA RELEASE 1.
- + *
- + * For more information, please consult
- + *
- + * NCR 5380 Family
- + * SCSI Protocol Controller
- + * Databook
- + *
- + * NCR Microelectronics
- + * 1635 Aeroplaza Drive
- + * Colorado Springs, CO 80916
- + * 1+ (719) 578-3400
- + * 1+ (800) 334-5454
- + */
- +
- +/*
- + * $Log: cumana_NCR5380.h,v $
- + */
- +
- +#ifndef CUMANA_NCR5380_H
- +#define CUMANA_NCR5380_H
- +
- +#define CUMANA_NCR5380_PUBLIC_RELEASE 1
- +
- +
- +#ifndef ASM
- +int cumana_NCR5380_abort(Scsi_Cmnd *);
- +int cumana_NCR5380_detect(Scsi_Host_Template *);
- +const char *cumana_NCR5380_info(struct Scsi_Host *);
- +int cumana_NCR5380_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
- +int cumana_NCR5380_reset(Scsi_Cmnd *);
- +
- +
- +#ifndef NULL
- +#define NULL 0
- +#endif
- +
- +#ifndef CMD_PER_LUN
- +#define CMD_PER_LUN 2
- +#endif
- +
- +#ifndef CAN_QUEUE
- +#define CAN_QUEUE 16
- +#endif
- +
- +#ifdef HOSTS_C
- +
- +#define CUMANA_NCR5380 {NULL, NULL, "Cumana 16-bit SCSI", \
- + cumana_NCR5380_detect, NULL, cumana_NCR5380_info, NULL, \
- + cumana_NCR5380_queue_command, cumana_NCR5380_abort, \
- + cumana_NCR5380_reset, NULL, \
- + NULL, /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL, \
- + /* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
- +
- +#else
- +#define NCR5380_implementation_fields \
- + int port, ctrl
- +
- +#define NCR5380_local_declare() \
- + struct Scsi_Host *_instance
- +
- +#define NCR5380_setup(instance) \
- + _instance = instance
- +
- +#define NCR5380_read(reg) cumana_NCR5380_read(_instance, reg)
- +#define NCR5380_write(reg, value) cumana_NCR5380_write(_instance, reg, value)
- +
- +#define NCR5380_intr cumana_NCR5380_intr
- +#define NCR5380_queue_command cumana_NCR5380_queue_command
- +#define NCR5380_abort cumana_NCR5380_abort
- +#define NCR5380_reset cumana_NCR5380_reset
- +
- +#define BOARD_NORMAL 0
- +#define BOARD_NCR53C400 1
- +
- +#endif /* else def HOSTS_C */
- +#endif /* ndef ASM */
- +#endif /* CUMANA_NCR5380_H */
- +
- diff -urNwbB linux/arch/arm/drivers/scsi/ecoscsi.c linux.arm/arch/arm/drivers/scsi/ecoscsi.c
- --- linux/arch/arm/drivers/scsi/ecoscsi.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/scsi/ecoscsi.c Sat Feb 24 09:36:55 1996
- @@ -0,0 +1,207 @@
- +#define AUTOSENSE
- +/* #define PSEUDO_DMA */
- +
- +/*
- + * EcoSCSI Generic NCR5380 driver
- + *
- + * Copyright 1995, Russell King
- + *
- + * ALPHA RELEASE 1.
- + *
- + * For more information, please consult
- + *
- + * NCR 5380 Family
- + * SCSI Protocol Controller
- + * Databook
- + *
- + * NCR Microelectronics
- + * 1635 Aeroplaza Drive
- + * Colorado Springs, CO 80916
- + * 1+ (719) 578-3400
- + * 1+ (800) 334-5454
- + */
- +
- +/*
- + * Options :
- + *
- + * PARITY - enable parity checking. Not supported.
- + *
- + * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
- + *
- + * USLEEP - enable support for devices that don't disconnect. Untested.
- + */
- +
- +/*
- + * $Log: ecoscsi_NCR5380.c,v $
- + */
- +
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include <linux/signal.h>
- +#include <linux/sched.h>
- +#include "../block/blk.h"
- +#include "scsi.h"
- +#include "hosts.h"
- +#include "ecoscsi.h"
- +#include "NCR5380.h"
- +#include "constants.h"
- +
- +static char ecoscsi_NCR5380_read(struct Scsi_Host *instance, int reg)
- +{
- + int ioaddr = instance->io_port;
- + outb(reg | 8, ioaddr);
- + return inb(ioaddr + 1);
- +}
- +
- +static void ecoscsi_NCR5380_write(struct Scsi_Host *instance, int reg, int value)
- +{
- + int ioaddr = instance->io_port;
- + outb(reg | 8, ioaddr);
- + outb(value, ioaddr + 1);
- +}
- +
- +/*
- + * Function : ecoscsi_NCR5380_setup(char *str, int *ints)
- + *
- + * Purpose : LILO command line initialization of the overrides array,
- + *
- + * Inputs : str - unused, ints - array of integer parameters with ints[0]
- + * equal to the number of ints.
- + *
- + */
- +
- +void ecoscsi_NCR5380_setup(char *str, int *ints) {
- +}
- +
- +/*
- + * Function : int ecoscsi_NCR5380_detect(Scsi_Host_Template * tpnt)
- + *
- + * Purpose : initializes ecoscsi NCR5380 driver based on the
- + * command line / compile time port and irq definitions.
- + *
- + * Inputs : tpnt - template for this SCSI adapter.
- + *
- + * Returns : 1 if a host adapter was found, 0 if not.
- + *
- + */
- +
- +int ecoscsi_NCR5380_detect(Scsi_Host_Template * tpnt) {
- + struct Scsi_Host *instance;
- +
- + instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
- + instance->io_port = 0x033A0000 >> 2;
- +
- + ecoscsi_NCR5380_write (instance, MODE_REG, 0x20); /* Is it really SCSI? */
- + if (ecoscsi_NCR5380_read (instance, MODE_REG) != 0x20) /* Write to a reg. */
- + {
- + scsi_unregister(instance);
- + return 0; /* and try to read */
- + }
- + ecoscsi_NCR5380_write( instance, MODE_REG, 0x00 ); /* it back. */
- + if (ecoscsi_NCR5380_read (instance, MODE_REG) != 0x00)
- + {
- + scsi_unregister(instance);
- + return 0;
- + }
- +
- + NCR5380_init(instance, 0);
- +
- + instance->irq = IRQ_NONE;
- +
- + if (instance->irq != IRQ_NONE)
- + if (request_irq(instance->irq, ecoscsi_NCR5380_intr, SA_INTERRUPT, "NCR5380")) {
- + printk("scsi%d: IRQ%d not free, interrupts disabled\n",
- + instance->host_no, instance->irq);
- + instance->irq = IRQ_NONE;
- + }
- +
- + if (instance->irq != IRQ_NONE) {
- + printk("scsi%d: eek! Interrupts enabled, but I don't think\n", instance->host_no);
- + printk("scsi%d: that the board had an interrupt!\n", instance->host_no);
- + }
- +
- + printk("scsi%d: at port %X irq", instance->host_no, instance->io_port);
- + if (instance->irq == IRQ_NONE)
- + printk ("s disabled");
- + else
- + printk (" %d", instance->irq);
- + printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
- + CAN_QUEUE, CMD_PER_LUN, ECOSCSI_NCR5380_PUBLIC_RELEASE);
- + printk("\nscsi%d:", instance->host_no);
- + NCR5380_print_options(instance);
- + printk("\n");
- + return 1;
- +}
- +
- +const char * ecoscsi_NCR5380_info (struct Scsi_Host *spnt) {
- + return "";
- +}
- +
- +#if 0
- +#define STAT(p) inw(p + 144)
- +
- +static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr,
- + int len)
- +{
- + int ioaddr = instance->io_port;
- +printk("writing %p len %d\n",addr, len);
- + if(!len) return -1;
- +
- + while(1)
- + {
- + int status;
- + while(((status = STAT(ioaddr)) & 0x100)==0);
- + }
- +}
- +
- +static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr,
- + int len)
- +{
- + int ioaddr = instance->io_port;
- + int ioaddr2= instance->io_port + 0x100;
- + unsigned char *start = addr;
- + int s;
- +printk("reading %p len %d\n",addr, len);
- + outb(inb(ioaddr + 128), ioaddr + 135);
- + while(len > 0)
- + {
- + int status,b,i, timeout;
- + timeout = 0x07FFFFFF;
- + while(((status = STAT(ioaddr)) & 0x100)==0)
- + {
- + timeout--;
- + if(status & 0x200 || !timeout)
- + {
- + printk("status = %p\n",status);
- + outb(0, ioaddr + 135);
- + return 1;
- + }
- + }
- + if(len >= 128)
- + {
- + for(i=0; i<64; i++)
- + {
- + b = inw(ioaddr + 136);
- + *addr++ = b;
- + *addr++ = b>>8;
- + }
- + len -= 128;
- + }
- + else
- + {
- + b = inw(ioaddr + 136);
- + *addr ++ = b;
- + len -= 1;
- + if(len)
- + *addr ++ = b>>8;
- + len -= 1;
- + }
- + }
- + outb(0, ioaddr + 135);
- + printk("first bytes = %02X %02X %02X %20X %02X %02X %02X\n",*start, start[1], start[2], start[3], start[4], start[5], start[6]);
- + return 1;
- +}
- +#endif
- +#undef STAT
- +
- +#include "NCR5380.c"
- diff -urNwbB linux/arch/arm/drivers/scsi/ecoscsi.h linux.arm/arch/arm/drivers/scsi/ecoscsi.h
- --- linux/arch/arm/drivers/scsi/ecoscsi.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/scsi/ecoscsi.h Sat Feb 24 09:36:55 1996
- @@ -0,0 +1,84 @@
- +/*
- + * Cumana Generic NCR5380 driver defines
- + *
- + * Copyright 1995, Russell King
- + *
- + * ALPHA RELEASE 1.
- + *
- + * For more information, please consult
- + *
- + * NCR 5380 Family
- + * SCSI Protocol Controller
- + * Databook
- + *
- + * NCR Microelectronics
- + * 1635 Aeroplaza Drive
- + * Colorado Springs, CO 80916
- + * 1+ (719) 578-3400
- + * 1+ (800) 334-5454
- + */
- +
- +/*
- + * $Log: ecoscsi_NCR5380.h,v $
- + */
- +
- +#ifndef ECOSCSI_NCR5380_H
- +#define ECOSCSI_NCR5380_H
- +
- +#define ECOSCSI_NCR5380_PUBLIC_RELEASE 1
- +
- +
- +#ifndef ASM
- +int ecoscsi_NCR5380_abort(Scsi_Cmnd *);
- +int ecoscsi_NCR5380_detect(Scsi_Host_Template *);
- +const char *ecoscsi_NCR5380_info(struct Scsi_Host *);
- +int ecoscsi_NCR5380_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
- +int ecoscsi_NCR5380_reset(Scsi_Cmnd *);
- +
- +
- +#ifndef NULL
- +#define NULL 0
- +#endif
- +
- +#ifndef CMD_PER_LUN
- +#define CMD_PER_LUN 2
- +#endif
- +
- +#ifndef CAN_QUEUE
- +#define CAN_QUEUE 16
- +#endif
- +
- +#ifdef HOSTS_C
- +
- +#define ECOSCSI_NCR5380 {NULL, NULL, "Serial Port EcoSCSI NCR5380", \
- + ecoscsi_NCR5380_detect, NULL, ecoscsi_NCR5380_info, NULL, \
- + ecoscsi_NCR5380_queue_command, ecoscsi_NCR5380_abort, \
- + ecoscsi_NCR5380_reset, NULL, \
- + NULL, /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL, \
- + /* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
- +
- +#else
- +#define NCR5380_implementation_fields \
- + int port, ctrl
- +
- +#define NCR5380_local_declare() \
- + struct Scsi_Host *_instance
- +
- +#define NCR5380_setup(instance) \
- + _instance = instance
- +
- +#define NCR5380_read(reg) ecoscsi_NCR5380_read(_instance, reg)
- +#define NCR5380_write(reg, value) ecoscsi_NCR5380_write(_instance, reg, value)
- +
- +#define NCR5380_intr ecoscsi_NCR5380_intr
- +#define NCR5380_queue_command ecoscsi_NCR5380_queue_command
- +#define NCR5380_abort ecoscsi_NCR5380_abort
- +#define NCR5380_reset ecoscsi_NCR5380_reset
- +
- +#define BOARD_NORMAL 0
- +#define BOARD_NCR53C400 1
- +
- +#endif /* else def HOSTS_C */
- +#endif /* ndef ASM */
- +#endif /* ECOSCSI_NCR5380_H */
- +
- diff -urNwbB linux/arch/arm/drivers/scsi/hosts.c linux.arm/arch/arm/drivers/scsi/hosts.c
- --- linux/arch/arm/drivers/scsi/hosts.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/scsi/hosts.c Sat Feb 24 09:36:56 1996
- @@ -0,0 +1,339 @@
- +/*
- + * hosts.c Copyright (C) 1992 Drew Eckhardt
- + * Copyright (C) 1993, 1994, 1995 Eric Youngdale
- + *
- + * mid to lowlevel SCSI driver interface
- + * Initial versions: Drew Eckhardt
- + * Subsequent revisions: Eric Youngdale
- + *
- + * <drew@colorado.edu>
- + */
- +
- +
- +/*
- + * This file contains the medium level SCSI
- + * host interface initialization, as well as the scsi_hosts array of SCSI
- + * hosts currently present in the system.
- + */
- +
- +#ifdef MODULE
- +/*
- + * Don't import our own symbols, as this would severely mess up our
- + * symbol tables.
- + */
- +#define _SCSI_SYMS_VER_
- +#include <linux/autoconf.h>
- +#include <linux/module.h>
- +#include <linux/version.h>
- +#else
- +#define MOD_INC_USE_COUNT
- +#define MOD_DEC_USE_COUNT
- +#endif
- +
- +#include <linux/config.h>
- +#include "../block/blk.h"
- +#include <linux/kernel.h>
- +#include <linux/string.h>
- +#include <linux/mm.h>
- +#include <linux/proc_fs.h>
- +
- +#include "scsi.h"
- +
- +#ifndef NULL
- +#define NULL 0L
- +#endif
- +
- +#define HOSTS_C
- +
- +#include "hosts.h"
- +
- +#ifdef CONFIG_SCSI_ACORNSCSI_3
- +#include "acornscsi.h"
- +#endif
- +
- +#ifdef CONFIG_SCSI_CUMANA_1
- +#include "cumana_1.h"
- +#endif
- +
- +#ifdef CONFIG_SCSI_OAK1
- +#include "oak.h"
- +#endif
- +
- +#ifdef CONFIG_SCSI_ECOSCSI
- +#include "ecoscsi.h"
- +#endif
- +
- +#ifdef CONFIG_SCSI_DEBUG
- +#include "scsi_debug.h"
- +#endif
- +
- +/*
- +static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/hosts.c,v 1.3 1993/09/24 12:21:00 drew Exp drew $";
- +*/
- +
- +/*
- + * The scsi host entries should be in the order you wish the
- + * cards to be detected. A driver may appear more than once IFF
- + * it can deal with being detected (and therefore initialized)
- + * with more than one simultaneous host number, can handle being
- + * reentrant, etc.
- + *
- + * They may appear in any order, as each SCSI host is told which host
- + * number it is during detection.
- + */
- +
- +/* This is a placeholder for controllers that are not configured into
- + * the system - we do this to ensure that the controller numbering is
- + * always consistent, no matter how the kernel is configured. */
- +
- +#define NO_CONTROLLER {NULL, NULL, NULL, NULL, NULL, NULL, NULL, \
- + NULL, NULL, 0, 0, 0, 0, 0, 0}
- +
- +/*
- + * When figure is run, we don't want to link to any object code. Since
- + * the macro for each host will contain function pointers, we cannot
- + * use it and instead must use a "blank" that does no such
- + * idiocy.
- + */
- +
- +Scsi_Host_Template * scsi_hosts = NULL;
- +
- +static Scsi_Host_Template builtin_scsi_hosts[] =
- +{
- +#ifdef CONFIG_SCSI_ACORNSCSI_3
- + ACORNSCSI_3,
- +#endif
- +#ifdef CONFIG_SCSI_CUMANA_1
- + CUMANA_NCR5380,
- +#endif
- +#ifdef CONFIG_SCSI_OAK1
- + OAK_NCR5380,
- +#endif
- +#ifdef CONFIG_SCSI_ECOSCSI
- + ECOSCSI_NCR5380,
- +#endif
- +#ifdef CONFIG_SCSI_DEBUG
- + SCSI_DEBUG,
- +#endif
- +};
- +
- +#define MAX_SCSI_HOSTS (sizeof(builtin_scsi_hosts) / sizeof(Scsi_Host_Template))
- +
- +
- +/*
- + * Our semaphores and timeout counters, where size depends on
- + * MAX_SCSI_HOSTS here.
- + */
- +
- +struct Scsi_Host * scsi_hostlist = NULL;
- +struct Scsi_Device_Template * scsi_devicelist = NULL;
- +
- +int max_scsi_hosts = 0;
- +int next_scsi_host = 0;
- +
- +void
- +scsi_unregister(struct Scsi_Host * sh){
- + struct Scsi_Host * shpnt;
- +
- + if(scsi_hostlist == sh)
- + scsi_hostlist = sh->next;
- + else {
- + shpnt = scsi_hostlist;
- + while(shpnt->next != sh) shpnt = shpnt->next;
- + shpnt->next = shpnt->next->next;
- + }
- +
- + /* If we are removing the last host registered, it is safe to reuse
- + * its host number (this avoids "holes" at boot time) (DB)
- + */
- + if (max_scsi_hosts == next_scsi_host && !scsi_loadable_module_flag)
- + max_scsi_hosts--;
- +
- + next_scsi_host--;
- + scsi_init_free((char *) sh, sizeof(struct Scsi_Host) + sh->extra_bytes);
- +}
- +
- +/* We call this when we come across a new host adapter. We only do this
- + * once we are 100% sure that we want to use this host adapter - it is a
- + * pain to reverse this, so we try and avoid it
- + */
- +
- +struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
- + struct Scsi_Host * retval, *shpnt;
- + retval = (struct Scsi_Host *)scsi_init_malloc(sizeof(struct Scsi_Host) + j,
- + (tpnt->unchecked_isa_dma && j ? GFP_DMA : 0) | GFP_ATOMIC);
- + retval->host_busy = 0;
- + retval->block = NULL;
- + retval->wish_block = 0;
- + if(j > 0xffff) panic("Too many extra bytes requested\n");
- + retval->extra_bytes = j;
- + retval->loaded_as_module = scsi_loadable_module_flag;
- + retval->host_no = max_scsi_hosts++; /* never reuse host_no (DB) */
- + next_scsi_host++;
- + retval->host_queue = NULL;
- + retval->host_wait = NULL;
- + retval->last_reset = 0;
- + retval->irq = 0;
- + retval->dma_channel = 0xff;
- +
- + /* These three are default values which can be overridden */
- + retval->max_channel = 0;
- + retval->max_id = 8;
- + retval->max_lun = 8;
- +
- + retval->unique_id = 0;
- + retval->io_port = 0;
- + retval->forbidden_addr = 0;
- + retval->forbidden_size = 0;
- + retval->hostt = tpnt;
- + retval->next = NULL;
- +#ifdef DEBUG
- + printk("Register %x %x: %d\n", (int)retval, (int)retval->hostt, j);
- +#endif
- +
- + /* The next six are the default values which can be overridden
- + * if need be */
- + retval->this_id = tpnt->this_id;
- + retval->can_queue = tpnt->can_queue;
- + retval->sg_tablesize = tpnt->sg_tablesize;
- + retval->cmd_per_lun = tpnt->cmd_per_lun;
- + retval->unchecked_isa_dma = tpnt->unchecked_isa_dma;
- + retval->use_clustering = tpnt->use_clustering;
- + if(!scsi_hostlist)
- + scsi_hostlist = retval;
- + else
- + {
- + shpnt = scsi_hostlist;
- + while(shpnt->next) shpnt = shpnt->next;
- + shpnt->next = retval;
- + }
- +
- + return retval;
- +}
- +
- +int
- +scsi_register_device(struct Scsi_Device_Template * sdpnt)
- +{
- + if(sdpnt->next) panic("Device already registered");
- + sdpnt->next = scsi_devicelist;
- + scsi_devicelist = sdpnt;
- + return 0;
- +}
- +
- +unsigned int scsi_init()
- +{
- + static int called = 0;
- + int i, pcount;
- + Scsi_Host_Template * tpnt;
- + struct Scsi_Host * shpnt;
- + const char * name;
- +
- + if(called) return 0;
- +
- + called = 1;
- + for (tpnt = &builtin_scsi_hosts[0], i = 0; i < MAX_SCSI_HOSTS; ++i, tpnt++)
- + {
- + /*
- + * Initialize our semaphores. -1 is interpreted to mean
- + * "inactive" - where as 0 will indicate a time out condition.
- + */
- +
- + pcount = next_scsi_host;
- + if ((tpnt->detect) &&
- + (tpnt->present =
- + tpnt->detect(tpnt)))
- + {
- + /* The only time this should come up is when people use
- + * some kind of patched driver of some kind or another. */
- + if(pcount == next_scsi_host) {
- + if(tpnt->present > 1)
- + panic("Failure to register low-level scsi driver");
- + /* The low-level driver failed to register a driver. We
- + * can do this now. */
- + scsi_register(tpnt,0);
- + }
- + tpnt->next = scsi_hosts;
- + scsi_hosts = tpnt;
- +
- + /* Add the driver to /proc/scsi */
- +#if CONFIG_PROC_FS
- + build_proc_dir_entries(tpnt);
- +#endif
- + }
- + }
- +
- + for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
- + {
- + if(shpnt->hostt->info)
- + name = shpnt->hostt->info(shpnt);
- + else
- + name = shpnt->hostt->name;
- + printk ("scsi%d : %s\n", /* And print a little message */
- + shpnt->host_no, name);
- + }
- +
- + printk ("scsi : %d host%s.\n", next_scsi_host,
- + (next_scsi_host == 1) ? "" : "s");
- +
- + scsi_make_blocked_list();
- +
- + /* Now attach the high level drivers */
- +#ifdef CONFIG_BLK_DEV_SD
- + scsi_register_device(&sd_template);
- +#endif
- +#ifdef CONFIG_BLK_DEV_SR
- + scsi_register_device(&sr_template);
- +#endif
- +#ifdef CONFIG_CHR_DEV_ST
- + scsi_register_device(&st_template);
- +#endif
- +#ifdef CONFIG_CHR_DEV_SG
- + scsi_register_device(&sg_template);
- +#endif
- +
- +#if 0
- + max_scsi_hosts = next_scsi_host;
- +#endif
- + return 0;
- +}
- +
- +
- +void scsi_mem_init(unsigned long memory_end)
- +{
- + struct Scsi_Host *Host;
- + long High8, Low24;
- + for (Host = scsi_hostlist; Host != NULL; Host = Host->next) {
- + if (Host->forbidden_addr > 0 && Host->forbidden_size > 0) {
- + for (High8 = 1<<24; High8 < memory_end; High8 += 1<<24) {
- + for (Low24 = Host->forbidden_addr;
- + Low24 < Host->forbidden_addr + Host->forbidden_size;
- + Low24 += PAGE_SIZE) {
- + unsigned long ForbiddenAddress = High8 + Low24;
- + if (ForbiddenAddress >= memory_end) goto next_host;
- + mem_map[MAP_NR(ForbiddenAddress)].reserved = 1;
- + }
- + }
- + }
- + next_host:
- + continue;
- + }
- +}
- +
- +/*
- + * Overrides for Emacs so that we follow Linus's tabbing style.
- + * Emacs will notice this stuff at the end of the file and automatically
- + * adjust the settings for this buffer only. This must remain at the end
- + * of the file.
- + * ---------------------------------------------------------------------------
- + * Local variables:
- + * c-indent-level: 4
- + * c-brace-imaginary-offset: 0
- + * c-brace-offset: -4
- + * c-argdecl-indent: 4
- + * c-label-offset: -4
- + * c-continued-statement-offset: 4
- + * c-continued-brace-offset: 0
- + * indent-tabs-mode: nil
- + * tab-width: 8
- + * End:
- + */
- diff -urNwbB linux/arch/arm/drivers/scsi/oak.c linux.arm/arch/arm/drivers/scsi/oak.c
- --- linux/arch/arm/drivers/scsi/oak.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/scsi/oak.c Sat Feb 24 09:36:56 1996
- @@ -0,0 +1,209 @@
- +#define AUTOSENSE
- +/*#define PSEUDO_DMA*/
- +
- +/*
- + * Oak Generic NCR5380 driver
- + *
- + * Copyright 1995, Russell King
- + *
- + * ALPHA RELEASE 1.
- + *
- + * For more information, please consult
- + *
- + * NCR 5380 Family
- + * SCSI Protocol Controller
- + * Databook
- + *
- + * NCR Microelectronics
- + * 1635 Aeroplaza Drive
- + * Colorado Springs, CO 80916
- + * 1+ (719) 578-3400
- + * 1+ (800) 334-5454
- + */
- +
- +/*
- + * Options :
- + *
- + * PARITY - enable parity checking. Not supported.
- + *
- + * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
- + *
- + * USLEEP - enable support for devices that don't disconnect. Untested.
- + */
- +
- +/*
- + * $Log: oak.c,v $
- + */
- +
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/config.h>
- +#endif
- +
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include <linux/signal.h>
- +#include <linux/sched.h>
- +#include "../block/blk.h"
- +#include "scsi.h"
- +#include "hosts.h"
- +#include "oak.h"
- +#include "NCR5380.h"
- +#include "constants.h"
- +
- +#undef START_DMA_INITIATOR_RECEIVE_REG
- +#define START_DMA_INITIATOR_RECEIVE_REG (7 + 128)
- +
- +#include <asm/ecard.h>
- +
- +static int oak_prods[] = { 0x0058 };
- +static int oak_manus[] = { 0x0021 };
- +
- +/*
- + * Function : int oak_NCR5380_detect(Scsi_Host_Template * tpnt)
- + *
- + * Purpose : initializes oak NCR5380 driver based on the
- + * command line / compile time port and irq definitions.
- + *
- + * Inputs : tpnt - template for this SCSI adapter.
- + *
- + * Returns : 1 if a host adapter was found, 0 if not.
- + *
- + */
- +
- +static struct expansion_card *ecs[4];
- +
- +int oak_NCR5380_detect(Scsi_Host_Template * tpnt) {
- + int count = 0, i;
- + struct Scsi_Host *instance;
- +
- + while(1) {
- + if ((ecs[count] = ecard_find(0, sizeof(oak_prods), oak_prods, oak_manus)) == NULL)
- + break;
- +
- + instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
- + instance->io_port = ((int)ecs[count]->r_podaddr & ~0x003c0000)>>2;
- +
- + NCR5380_init(instance, 0);
- +
- + instance->irq = 255;
- +
- + if (instance->irq != IRQ_NONE)
- + if (request_irq(instance->irq, oak_NCR5380_intr, SA_INTERRUPT, "Oak SCSI")) {
- + printk("scsi%d: IRQ%d not free, interrupts disabled\n",
- + instance->host_no, instance->irq);
- + instance->irq = IRQ_NONE;
- + }
- +
- + if (instance->irq != IRQ_NONE) {
- + printk("scsi%d: eek! Interrupts enabled, but I don't think\n", instance->host_no);
- + printk("scsi%d: that the board had an interrupt!\n", instance->host_no);
- + }
- +
- + printk("scsi%d: at port %X irq", instance->host_no, instance->io_port);
- + if (instance->irq == IRQ_NONE)
- + printk ("s disabled");
- + else
- + printk (" %d", instance->irq);
- + printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
- + CAN_QUEUE, CMD_PER_LUN, OAK_NCR5380_PUBLIC_RELEASE);
- + printk("\nscsi%d:", instance->host_no);
- + NCR5380_print_options(instance);
- + printk("\n");
- +
- + ecard_claim(ecs[count]);
- + ++count;
- + }
- + for(i=0; i<count; i++)
- + ecard_release(ecs[i]);
- +#ifdef MODULE
- + if(count == 0)
- + printk("No oak scsi devices found\n");
- +#endif
- + return count;
- +}
- +
- +const char * oak_NCR5380_info (struct Scsi_Host *spnt) {
- + return "";
- +}
- +
- +#define STAT(p) inw(p + 144)
- +extern void inswb(int from, void *to, int len);
- +
- +static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr,
- + int len)
- +{
- + int ioaddr = instance->io_port;
- +printk("writing %p len %d\n",addr, len);
- + if(!len) return -1;
- +
- + while(1)
- + {
- + int status;
- + while(((status = STAT(ioaddr)) & 0x100)==0);
- + }
- +}
- +
- +static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr,
- + int len)
- +{
- + int ioaddr = instance->io_port;
- +printk("reading %p len %d\n", addr, len);
- + while(len > 0)
- + {
- + int status, timeout;
- + unsigned long b;
- +
- + timeout = 0x01FFFFFF;
- +
- + while(((status = STAT(ioaddr)) & 0x100)==0)
- + {
- + timeout--;
- + if(status & 0x200 || !timeout)
- + {
- + printk("status = %08lX\n",status);
- + return 1;
- + }
- + }
- + if(len >= 128)
- + {
- + inswb(ioaddr + 136, addr, 128);
- + addr += 128;
- + len -= 128;
- + }
- + else
- + {
- + b = (unsigned long) inw(ioaddr + 136);
- + *addr ++ = b;
- + len -= 1;
- + if(len)
- + *addr ++ = b>>8;
- + len -= 1;
- + }
- + }
- + return 0;
- +}
- +
- +static char oak_NCR5380_read(struct Scsi_Host *instance, int reg)
- +{
- + int ioaddr = instance->io_port;
- + return inb(ioaddr + reg);
- +}
- +
- +static void oak_NCR5380_write(struct Scsi_Host *instance, int reg, int value)
- +{
- + int ioaddr = instance->io_port;
- + outb(value, ioaddr + reg);
- +}
- +
- +#undef STAT
- +
- +#include "NCR5380.c"
- +
- +#ifdef MODULE
- +
- +Scsi_Host_Template driver_template = OAK_NCR5380;
- +
- +#include "scsi_module.c"
- +#endif
- +
- diff -urNwbB linux/arch/arm/drivers/scsi/oak.h linux.arm/arch/arm/drivers/scsi/oak.h
- --- linux/arch/arm/drivers/scsi/oak.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/scsi/oak.h Sat Feb 24 09:36:56 1996
- @@ -0,0 +1,86 @@
- +/*
- + * Cumana Generic NCR5380 driver defines
- + *
- + * Copyright 1993, Drew Eckhardt
- + * Visionary Computing
- + * (Unix and Linux consulting and custom programming)
- + * drew@colorado.edu
- + * +1 (303) 440-4894
- + *
- + * ALPHA RELEASE 1.
- + *
- + * For more information, please consult
- + *
- + * NCR 5380 Family
- + * SCSI Protocol Controller
- + * Databook
- + *
- + * NCR Microelectronics
- + * 1635 Aeroplaza Drive
- + * Colorado Springs, CO 80916
- + * 1+ (719) 578-3400
- + * 1+ (800) 334-5454
- + */
- +
- +/*
- + * $Log: oak_NCR5380.h,v $
- + */
- +
- +#ifndef OAK_NCR5380_H
- +#define OAK_NCR5380_H
- +
- +#define OAK_NCR5380_PUBLIC_RELEASE 1
- +
- +
- +#ifndef ASM
- +int oak_NCR5380_abort(Scsi_Cmnd *);
- +int oak_NCR5380_detect(Scsi_Host_Template *);
- +const char *oak_NCR5380_info(struct Scsi_Host *);
- +int oak_NCR5380_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
- +int oak_NCR5380_reset(Scsi_Cmnd *);
- +
- +
- +#ifndef NULL
- +#define NULL 0
- +#endif
- +
- +#ifndef CMD_PER_LUN
- +#define CMD_PER_LUN 2
- +#endif
- +
- +#ifndef CAN_QUEUE
- +#define CAN_QUEUE 16
- +#endif
- +
- +#define OAK_NCR5380 {NULL, NULL, "Oak 16-bit SCSI", \
- + oak_NCR5380_detect, NULL, oak_NCR5380_info, NULL, \
- + oak_NCR5380_queue_command, oak_NCR5380_abort, \
- + oak_NCR5380_reset, NULL, \
- + NULL, /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL, \
- + /* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
- +
- +#ifndef HOSTS_C
- +#define NCR5380_implementation_fields \
- + int port, ctrl
- +
- +#define NCR5380_local_declare() \
- + struct Scsi_Host *_instance
- +
- +#define NCR5380_setup(instance) \
- + _instance = instance
- +
- +#define NCR5380_read(reg) oak_NCR5380_read(_instance, reg)
- +#define NCR5380_write(reg, value) oak_NCR5380_write(_instance, reg, value)
- +
- +#define NCR5380_intr oak_NCR5380_intr
- +#define NCR5380_queue_command oak_NCR5380_queue_command
- +#define NCR5380_abort oak_NCR5380_abort
- +#define NCR5380_reset oak_NCR5380_reset
- +
- +#define BOARD_NORMAL 0
- +#define BOARD_NCR53C400 1
- +
- +#endif /* else def HOSTS_C */
- +#endif /* ndef ASM */
- +#endif /* OAK_NCR5380_H */
- +
- diff -urNwbB linux/arch/arm/drivers/scsi/scsi.c linux.arm/arch/arm/drivers/scsi/scsi.c
- --- linux/arch/arm/drivers/scsi/scsi.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/scsi/scsi.c Sat Feb 24 09:36:57 1996
- @@ -0,0 +1,3148 @@
- +/*
- + * scsi.c Copyright (C) 1992 Drew Eckhardt
- + * Copyright (C) 1993, 1994, 1995 Eric Youngdale
- + *
- + * generic mid-level SCSI driver
- + * Initial versions: Drew Eckhardt
- + * Subsequent revisions: Eric Youngdale
- + *
- + * <drew@colorado.edu>
- + *
- + * Bug correction thanks go to :
- + * Rik Faith <faith@cs.unc.edu>
- + * Tommy Thorn <tthorn>
- + * Thomas Wuensche <tw@fgb1.fgb.mw.tu-muenchen.de>
- + *
- + * Modified by Eric Youngdale eric@aib.com to
- + * add scatter-gather, multiple outstanding request, and other
- + * enhancements.
- + *
- + * Native multichannel and wide scsi support added
- + * by Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
- + */
- +#ifdef MODULE
- +/*
- + * Don't import our own symbols, as this would severely mess up our
- + * symbol tables.
- + */
- +#define _SCSI_SYMS_VER_
- +#include <linux/autoconf.h>
- +#include <linux/module.h>
- +#include <linux/version.h>
- +#else
- +#define MOD_INC_USE_COUNT
- +#define MOD_DEC_USE_COUNT
- +#endif
- +
- +#include <asm/system.h>
- +#include <linux/sched.h>
- +#include <linux/timer.h>
- +#include <linux/string.h>
- +#include <linux/malloc.h>
- +#include <asm/irq.h>
- +#include <asm/dma.h>
- +#include <linux/ioport.h>
- +#include <linux/kernel.h>
- +#include<linux/stat.h>
- +
- +#include "../block/blk.h"
- +#include "scsi.h"
- +#include "hosts.h"
- +#include "constants.h"
- +
- +
- +#undef USE_STATIC_SCSI_MEMORY
- +
- +/*
- +static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/scsi.c,v 1.5 1993/09/24 12:45:18 drew Exp drew $";
- +*/
- +
- +
- +/* 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 (panic ("Internal error in file %s, line %d.\n", __FILE__, __LINE__))
- +
- +static void scsi_done (Scsi_Cmnd *SCpnt);
- +static int update_timeout (Scsi_Cmnd *, int);
- +static void print_inquiry(unsigned char *data);
- +static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid);
- +
- +static unsigned char * dma_malloc_freelist = NULL;
- +static int scsi_need_isa_bounce_buffers;
- +static unsigned int dma_sectors = 0;
- +unsigned int dma_free_sectors = 0;
- +unsigned int need_isa_buffer = 0;
- +static unsigned char ** dma_malloc_pages = NULL;
- +
- +static int time_start;
- +static int time_elapsed;
- +static volatile struct Scsi_Host * host_active = NULL;
- +#define SCSI_BLOCK(HOST) ((HOST->block && host_active && HOST != host_active) \
- + || (HOST->can_queue && HOST->host_busy >= HOST->can_queue))
- +
- +#define MAX_SCSI_DEVICE_CODE 10
- +const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
- +{
- + "Direct-Access ",
- + "Sequential-Access",
- + "Printer ",
- + "Processor ",
- + "WORM ",
- + "CD-ROM ",
- + "Scanner ",
- + "Optical Device ",
- + "Medium Changer ",
- + "Communications "
- +};
- +
- +
- +/*
- + * global variables :
- + * scsi_devices an array of these specifying the address for each
- + * (host, id, LUN)
- + */
- +
- +Scsi_Device * scsi_devices = NULL;
- +
- +/* Process ID of SCSI commands */
- +unsigned long scsi_pid = 0;
- +
- +static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0};
- +static void resize_dma_pool(void);
- +
- +/* This variable is merely a hook so that we can debug the kernel with gdb. */
- +Scsi_Cmnd * last_cmnd = NULL;
- +
- +/* This is the pointer to the /proc/scsi code.
- + * It is only initialized to !=0 if the scsi code is present
- + */
- +extern int (* dispatch_scsi_info_ptr)(int ino, char *buffer, char **start,
- + off_t offset, int length, int inout);
- +extern int dispatch_scsi_info(int ino, char *buffer, char **start,
- + off_t offset, int length, int inout);
- +
- +struct proc_dir_entry proc_scsi_scsi = {
- + PROC_SCSI_SCSI, 4, "scsi",
- + S_IFREG | S_IRUGO | S_IWUSR, 2, 0, 0, 0,
- + NULL,
- + NULL, NULL,
- + NULL, NULL, NULL
- +};
- +
- +
- +/*
- + * As the scsi do command functions are intelligent, and may need to
- + * redo a command, we need to keep track of the last command
- + * executed on each one.
- + */
- +
- +#define WAS_RESET 0x01
- +#define WAS_TIMEDOUT 0x02
- +#define WAS_SENSE 0x04
- +#define IS_RESETTING 0x08
- +#define IS_ABORTING 0x10
- +#define ASKED_FOR_SENSE 0x20
- +
- +/*
- + * This is the number of clock ticks we should wait before we time out
- + * and abort the command. This is for where the scsi.c module generates
- + * the command, not where it originates from a higher level, in which
- + * case the timeout is specified there.
- + *
- + * ABORT_TIMEOUT and RESET_TIMEOUT are the timeouts for RESET and ABORT
- + * respectively.
- + */
- +
- +#ifdef DEBUG_TIMEOUT
- +static void scsi_dump_status(void);
- +#endif
- +
- +
- +#ifdef DEBUG
- + #define SCSI_TIMEOUT (5*HZ)
- +#else
- + #define SCSI_TIMEOUT (1*HZ)
- +#endif
- +
- +#ifdef DEBUG
- + #define SENSE_TIMEOUT SCSI_TIMEOUT
- + #define ABORT_TIMEOUT SCSI_TIMEOUT
- + #define RESET_TIMEOUT SCSI_TIMEOUT
- +#else
- + #define SENSE_TIMEOUT (5*HZ/10)
- + #define RESET_TIMEOUT (5*HZ/10)
- + #define ABORT_TIMEOUT (5*HZ/10)
- +#endif
- +
- +#define MIN_RESET_DELAY (1*HZ)
- +
- +/* Do not call reset on error if we just did a reset within 10 sec. */
- +#define MIN_RESET_PERIOD (10*HZ)
- +
- +/* The following devices are known not to tolerate a lun != 0 scan for
- + * one reason or another. Some will respond to all luns, others will
- + * lock up.
- + */
- +
- +#define BLIST_NOLUN 0x01
- +#define BLIST_FORCELUN 0x02
- +#define BLIST_BORKEN 0x04
- +#define BLIST_KEY 0x08
- +#define BLIST_SINGLELUN 0x10
- +
- +struct dev_info{
- + const char * vendor;
- + const char * model;
- + const char * revision; /* Latest revision known to be bad. Not used yet */
- + unsigned flags;
- +};
- +
- +/*
- + * This is what was previously known as the blacklist. The concept
- + * has been expanded so that we can specify other types of things we
- + * need to be aware of.
- + */
- +static struct dev_info device_list[] =
- +{
- +{"CHINON","CD-ROM CDS-431","H42", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
- +{"CHINON","CD-ROM CDS-535","Q14", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
- +{"DENON","DRD-25X","V", BLIST_NOLUN}, /* Locks up if probed for lun != 0 */
- +{"HITACHI","DK312C","CM81", BLIST_NOLUN}, /* Responds to all lun - dtg */
- +{"HITACHI","DK314C","CR21" , BLIST_NOLUN}, /* responds to all lun */
- +{"IMS", "CDD521/10","2.06", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
- +{"MAXTOR","XT-3280","PR02", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
- +{"MAXTOR","XT-4380S","B3C", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
- +{"MAXTOR","MXT-1240S","I1.2", BLIST_NOLUN}, /* Locks up when LUN>0 polled */
- +{"MAXTOR","XT-4170S","B5A", BLIST_NOLUN}, /* Locks-up sometimes when LUN>0 polled. */
- +{"MAXTOR","XT-8760S","B7B", BLIST_NOLUN}, /* guess what? */
- +{"NEC","CD-ROM DRIVE:841","1.0", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
- +{"RODIME","RO3000S","2.33", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
- +{"SEAGATE", "ST157N", "\004|j", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
- + * for aha152x controller, which causes
- + * SCSI code to reset bus.*/
- +{"SEAGATE", "ST296","921", BLIST_NOLUN}, /* Responds to all lun */
- +{"SONY","CD-ROM CDU-541","4.3d", BLIST_NOLUN},
- +{"SONY","CD-ROM CDU-55S","1.0i", BLIST_NOLUN},
- +{"SONY","CD-ROM CDU-561","1.7x", BLIST_NOLUN},
- +{"TANDBERG","TDC 3600","U07", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
- +{"TEAC","CD-ROM","1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
- + * for seagate controller, which causes
- + * SCSI code to reset bus.*/
- +{"TEXEL","CD-ROM","1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
- + * for seagate controller, which causes
- + * SCSI code to reset bus.*/
- +{"QUANTUM","LPS525S","3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */
- +{"QUANTUM","PD1225S","3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */
- +{"MEDIAVIS","CDR-H93MV","1.31", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
- +{"SANKYO", "CP525","6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */
- +{"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */
- +{"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */
- +{"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */
- +
- +/*
- + * Other types of devices that have special flags.
- + */
- +{"SONY","CD-ROM CDU-8001","*", BLIST_BORKEN},
- +{"TEXEL","CD-ROM","1.06", BLIST_BORKEN},
- +{"INSITE","Floptical F*8I","*", BLIST_KEY},
- +{"INSITE","I325VM","*", BLIST_KEY},
- +{"PIONEER","CD-ROMDRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
- +{"PIONEER","CD-ROMDRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
- +/*
- + * Must be at end of list...
- + */
- +{NULL, NULL, NULL}
- +};
- +
- +static int get_device_flags(unsigned char * response_data){
- + int i = 0;
- + unsigned char * pnt;
- + for(i=0; 1; i++){
- + if(device_list[i].vendor == NULL) return 0;
- + pnt = &response_data[8];
- + while(*pnt && *pnt == ' ') pnt++;
- + if(memcmp(device_list[i].vendor, pnt,
- + strlen(device_list[i].vendor))) continue;
- + pnt = &response_data[16];
- + while(*pnt && *pnt == ' ') pnt++;
- + if(memcmp(device_list[i].model, pnt,
- + strlen(device_list[i].model))) continue;
- + return device_list[i].flags;
- + }
- + return 0;
- +}
- +
- +/*
- + * As the actual SCSI command runs in the background, we must set up a
- + * flag that tells scan_scsis() when the result it has is valid.
- + * scan_scsis can set the_result to -1, and watch for it to become the
- + * actual return code for that call. the scan_scsis_done function() is
- + * our user specified completion function that is passed on to the
- + * scsi_do_cmd() function.
- + */
- +
- +volatile int in_scan_scsis = 0;
- +static int the_result;
- +
- +void scsi_make_blocked_list(void) {
- + int block_count = 0, index;
- + unsigned int flags;
- + struct Scsi_Host * sh[128], * shpnt;
- +
- + /*
- + * Create a circular linked list from the scsi hosts which have
- + * the "wish_block" field in the Scsi_Host structure set.
- + * The blocked list should include all the scsi hosts using ISA DMA.
- + * In some systems, using two dma channels simultaneously causes
- + * unpredictable results.
- + * Among the scsi hosts in the blocked list, only one host at a time
- + * is allowed to have active commands queued. The transition from
- + * one active host to the next one is allowed only when host_busy == 0
- + * for the active host (which implies host_busy == 0 for all the hosts
- + * in the list). Moreover for block devices the transition to a new
- + * active host is allowed only when a request is completed, since a
- + * block device request can be divided into multiple scsi commands
- + * (when there are few sg lists or clustering is disabled).
- + *
- + * (DB, 4 Feb 1995)
- + */
- +
- + save_flags(flags);
- + cli();
- + host_active = NULL;
- +
- + for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next) {
- +
- +#if 0
- + /*
- + * Is this is a candidate for the blocked list?
- + * Useful to put into the blocked list all the hosts whose driver
- + * does not know about the host->block feature.
- + */
- + if (shpnt->unchecked_isa_dma) shpnt->wish_block = 1;
- +#endif
- +
- + if (shpnt->wish_block) sh[block_count++] = shpnt;
- + }
- +
- + if (block_count == 1) sh[0]->block = NULL;
- +
- + else if (block_count > 1) {
- +
- + for(index = 0; index < block_count - 1; index++) {
- + sh[index]->block = sh[index + 1];
- + printk("scsi%d : added to blocked host list.\n",
- + sh[index]->host_no);
- + }
- +
- + sh[block_count - 1]->block = sh[0];
- + printk("scsi%d : added to blocked host list.\n",
- + sh[index]->host_no);
- + }
- +
- + restore_flags(flags);
- +}
- +
- +static void scan_scsis_done (Scsi_Cmnd * SCpnt)
- +{
- +
- +#ifdef DEBUG
- + printk ("scan_scsis_done(%p, %06x)\n", SCpnt->host, SCpnt->result);
- +#endif
- + SCpnt->request.rq_status = RQ_SCSI_DONE;
- +
- + if (SCpnt->request.sem != NULL)
- + up(SCpnt->request.sem);
- +}
- +
- +#ifdef CONFIG_SCSI_MULTI_LUN
- +static int max_scsi_luns = 8;
- +#else
- +static int max_scsi_luns = 1;
- +#endif
- +
- +void scsi_luns_setup(char *str, int *ints) {
- + if (ints[0] != 1)
- + printk("scsi_luns_setup : usage max_scsi_luns=n (n should be between 1 and 8)\n");
- + else
- + max_scsi_luns = ints[1];
- +}
- +
- +/*
- + * Detecting SCSI devices :
- + * We scan all present host adapter's busses, from ID 0 to ID (max_id).
- + * We use the INQUIRY command, determine device type, and pass the ID /
- + * lun address of all sequential devices to the tape driver, all random
- + * devices to the disk driver.
- + */
- +static
- +void scan_scsis (struct Scsi_Host * shpnt, unchar hardcoded,
- + unchar hchannel, unchar hid, unchar hlun)
- +{
- + int dev, lun, type, channel;
- + unsigned char scsi_cmd [12];
- + unsigned char scsi_result0 [256];
- + unsigned char * scsi_result;
- + Scsi_Device * SDpnt, *SDtail;
- + struct Scsi_Device_Template * sdtpnt;
- + int bflags;
- + int max_dev_lun = 0;
- + Scsi_Cmnd *SCpnt;
- +
- + ++in_scan_scsis;
- + lun = 0;
- + type = -1;
- + SCpnt = (Scsi_Cmnd *) scsi_init_malloc(sizeof(Scsi_Cmnd), GFP_ATOMIC|GFP_DMA);
- + SDpnt = (Scsi_Device *) scsi_init_malloc(sizeof (Scsi_Device), GFP_ATOMIC);
- + SDtail = scsi_devices;
- +
- + if(scsi_devices) while(SDtail->next) SDtail = SDtail->next;
- +
- + /* Make sure we have something that is valid for DMA purposes */
- + scsi_result = ((!dma_malloc_freelist || !shpnt->unchecked_isa_dma)
- + ? &scsi_result0[0] : scsi_malloc(512));
- +
- + if(scsi_result == NULL) {
- + printk("Unable to obtain scsi_result buffer\n");
- + goto leave;
- + }
- +
- + shpnt->host_queue = SCpnt; /* We need this so that commands can time out */
- +
- + if(hardcoded == 1) {
- + channel = hchannel;
- + dev = hid;
- + lun = hlun;
- + goto crude; /* Anyone remember good ol' BASIC ? :-) */
- + }
- +
- + for (channel = 0; channel <= shpnt->max_channel; channel++)
- + {
- + for (dev = 0; dev < shpnt->max_id; ++dev) {
- + if (shpnt->this_id != dev) {
- +
- + /*
- + * We need the for so our continue, etc. work fine.
- + * We put this in a variable so that we can override
- + * it during the scan if we detect a device *KNOWN*
- + * to have multiple logical units.
- + */
- + max_dev_lun = (max_scsi_luns < shpnt->max_lun ?
- + max_scsi_luns : shpnt->max_lun);
- +
- + for (lun = 0; lun < max_dev_lun; ++lun)
- + {
- + crude:
- + memset(SDpnt, 0, sizeof(Scsi_Device));
- + SDpnt->host = shpnt;
- + SDpnt->id = dev;
- + SDpnt->lun = lun;
- + SDpnt->channel = channel;
- +
- + /* Some low level driver could use device->type (DB) */
- + SDpnt->type = -1;
- + /*
- + * Assume that the device will have handshaking problems,
- + * and then fix this field later if it turns out it doesn't
- + */
- + SDpnt->borken = 1;
- + SDpnt->was_reset = 0;
- + SDpnt->expecting_cc_ua = 0;
- +
- + scsi_cmd[0] = TEST_UNIT_READY;
- + scsi_cmd[1] = lun << 5;
- + scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
- +
- + memset(SCpnt, 0, sizeof(Scsi_Cmnd));
- + SCpnt->host = SDpnt->host;
- + SCpnt->device = SDpnt;
- + SCpnt->target = SDpnt->id;
- + SCpnt->lun = SDpnt->lun;
- + SCpnt->channel = SDpnt->channel;
- +
- + {
- + /*
- + * Do the actual command, and wait for it to finish
- + */
- + struct semaphore sem = MUTEX_LOCKED;
- + SCpnt->request.sem = &sem;
- + SCpnt->request.rq_status = RQ_SCSI_BUSY;
- + scsi_do_cmd (SCpnt, (void *) scsi_cmd,
- + (void *) scsi_result,
- + 256, scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5);
- + down(&sem);
- + }
- +
- +#if defined(DEBUG) || defined(DEBUG_INIT)
- + printk("scsi: scan SCSIS id %d lun %d\n", dev, lun);
- + printk("scsi: return code %08x\n", SCpnt->result);
- +#endif
- +
- + if(SCpnt->result) {
- + if (((driver_byte(SCpnt->result) & DRIVER_SENSE) ||
- + (status_byte(SCpnt->result) & CHECK_CONDITION)) &&
- + ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
- + if (SCpnt->sense_buffer[2] &0xe0)
- + continue; /* No devices here... */
- + if(((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
- + ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION))
- + continue;
- + }
- + else
- + break;
- + }
- +
- +#if defined (DEBUG) || defined(DEBUG_INIT)
- + printk("scsi: performing INQUIRY\n");
- +#endif
- +
- + /*
- + * Build an INQUIRY command block.
- + */
- + scsi_cmd[0] = INQUIRY;
- + scsi_cmd[1] = (lun << 5) & 0xe0;
- + scsi_cmd[2] = 0;
- + scsi_cmd[3] = 0;
- + scsi_cmd[4] = 255;
- + scsi_cmd[5] = 0;
- +
- + SCpnt->cmd_len = 0;
- +
- + {
- + struct semaphore sem = MUTEX_LOCKED;
- + SCpnt->request.sem = &sem;
- + SCpnt->request.rq_status = RQ_SCSI_BUSY;
- + scsi_do_cmd (SCpnt, (void *) scsi_cmd,
- + (void *) scsi_result,
- + 256, scan_scsis_done, SCSI_TIMEOUT, 3);
- + down(&sem);
- + }
- +
- + the_result = SCpnt->result;
- +
- +#if defined(DEBUG) || defined(DEBUG_INIT)
- + if (!the_result)
- + printk("scsi: INQUIRY successful\n");
- + else
- + printk("scsi: INQUIRY failed with code %08x\n", the_result);
- +#endif
- +
- + if(the_result) break;
- +
- + /* skip other luns on this device */
- +
- + if (!the_result)
- + {
- + /* It would seem some TOSHIBA CDROM
- + * gets things wrong
- + */
- + if (!strncmp(scsi_result+8,"TOSHIBA",7) &&
- + !strncmp(scsi_result+16,"CD-ROM",6) &&
- + scsi_result[0] == TYPE_DISK) {
- + scsi_result[0] = TYPE_ROM;
- + scsi_result[1] |= 0x80; /* removable */
- + }
- +
- + if (!strncmp(scsi_result+8,"NEC",3)) {
- + if (!strncmp(scsi_result+16,"CD-ROM DRIVE:84 ",16) ||
- + !strncmp(scsi_result+16,"CD-ROM DRIVE:25",15))
- + SDpnt->manufacturer = SCSI_MAN_NEC_OLDCDR;
- + else
- + SDpnt->manufacturer = SCSI_MAN_NEC;
- + } else if (!strncmp(scsi_result+8,"TOSHIBA",7))
- + SDpnt->manufacturer = SCSI_MAN_TOSHIBA;
- + else if (!strncmp(scsi_result+8,"SONY",4))
- + SDpnt->manufacturer = SCSI_MAN_SONY;
- + else if (!strncmp(scsi_result+8, "PIONEER", 7))
- + SDpnt->manufacturer = SCSI_MAN_PIONEER;
- + else
- + SDpnt->manufacturer = SCSI_MAN_UNKNOWN;
- +
- + memcpy(SDpnt->vendor, scsi_result+8, 8);
- + memcpy(SDpnt->model, scsi_result+16, 16);
- + memcpy(SDpnt->rev, scsi_result+32, 4);
- +
- + SDpnt->removable = (0x80 & scsi_result[1]) >> 7;
- + SDpnt->lockable = SDpnt->removable;
- + SDpnt->changed = 0;
- + SDpnt->access_count = 0;
- + SDpnt->busy = 0;
- + SDpnt->has_cmdblocks = 0;
- + /*
- + * Currently, all sequential devices are assumed to be
- + * tapes, all random devices disk, with the appropriate
- + * read only flags set for ROM / WORM treated as RO.
- + */
- +
- + switch (type = (scsi_result[0] & 0x1f))
- + {
- + case TYPE_TAPE :
- + case TYPE_DISK :
- + case TYPE_MOD :
- + case TYPE_PROCESSOR :