home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-10-27 | 2.2 MB | 83,914 lines |
Text Truncated. Only the first 1MB is shown below. Download the file for the complete contents.
- diff -r -u -N linux.orig/.bash_history linux.arm/.bash_history
- --- linux.orig/.bash_history Thu Jan 1 01:00:00 1970
- +++ linux.arm/.bash_history Fri Oct 27 23:16:06 1995
- @@ -0,0 +1,271 @@
- +dir
- +cd mm
- +ue swap.c
- +cp ~root/.emacsrc ~
- +ue swap.c
- +ue swap.c
- +ue memory.c
- +less ../include/asm/pgtable.h
- +ue /etc/shutdown.allow
- +dir
- +cd mm
- +ue swap.c
- +ue memory.c
- +ue swap.c
- +objdump --disassemble swap.o
- +objdump --disassemble swap.o | less
- +dir
- +ue swap.c
- +make
- +cd ../
- +make
- +cd mm
- +ue swap.c
- +ue Makefile
- +ue swap.c
- +cd ..
- +make
- +shutdown -t3 -rf now ; logout
- +nohup shutdown -r3 -rf now &; logout
- +nohup shutdown -r3 -rf now & ; logout
- +shutdown -r3 -rf now ; logout
- +cd mm
- +objdump --disassemble swap.o | less
- +ue swap.c
- +mcd ..
- +cd ..
- +make
- +ue mm/swap.c
- +make
- +/sbin/bootmap /dev/hda1
- +sync
- +cd /etc
- +mv rc rc.old
- +cd /sbin
- +mv init init.old
- +init.old R
- +sync
- +ue /etc/rc.d/rc.M
- +HOME=/root
- +cd arch/arm/drivers/scsi
- +dir
- +diff NCR5380.c ~lin/drivers/scsi/NCR5380.c
- +diff --help
- +diff NCR5380.c ~lin/drivers/scsi/NCR5380.c -bw
- +rm NCR5380.c
- +ln -s ../../../../drivers/scsi/NCR5380.c .
- +ue ecoscsi.c
- +rm NCR5380.h
- +ln -s ../../../../drivers/scsi/NCR5380.h .
- +rm ecoscsi.o
- +ue ecoscsi.c
- +make
- +make ecoscsi.o
- +dir
- +ue oak.c
- +ue cumana_1.c
- +ue oak.c
- +ue cumana_1.c
- +ue ecoscsi.c
- +ue ecoscsi.c
- +ue oak.c
- +ue cumana_1.c
- +ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +ifconfig eth0 promisc
- +/sbin/ifconfig
- +/sbin/ifconfig eth0 promisc
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +man tcpdump
- +/sbin/ifconfig
- +/sbin/ifconfig
- +/sbin/ifconfig
- +man tcpdump
- +/sbin/ifconfig
- +/sbin/ifconfig
- +fdir
- +dir
- +make -n
- +ue cumana_1.c
- +less ../podules/scsi.c
- +less ../podules/podlist.c
- +%1
- +%2
- +%3
- +%2
- +less ../podules/podlist.c
- +jobs
- +%3
- +jobs
- +ue cumana_1.c
- +less ../podules/scsi.c
- +%2
- +grep scsi_mem_init *.c
- +grep scsi_mem_init ~lin/drivers/scsi/*.c
- +diff hosts.c ~lin/drivers/scsi/hosts.c -bw | less
- +pwd
- +mv hosts.c hosts.c.old
- +cp ~lin/drivers/scsi/hosts.c .
- +ue hosts.c
- +cd ../../../..
- +cd arch/arm/drivers/scsi/
- +ue hosts.c
- +nm hosts.o | grep *UND* | less
- +nm hosts.o
- +ue hosts.c
- +nm hosts.o
- +dir
- +less hosts.c
- +dir
- +cd ../..
- +ue Makefile
- +cd ../../init
- +less init.c
- +less main.c
- +rm main.o
- +cd ..
- +cd /
- +dir
- +dir boot
- +dir
- +dir etc
- +dir
- +dir home
- +dir home/arm
- +dir home/arm/a
- +dir home/arm/1
- +dir home/arm/1/users/
- +dir /root
- +rm -f /root/project
- +rm -rf /root/project
- +df
- +dir /root
- +rm /root/checklinux
- +df
- +dir
- +dir sbin
- +rm /sbin/connect.cua2
- +dir /bin
- +cd /bin
- +rm sh
- +ln -s bash sh
- +dir
- +chgrp bin bash bashbug csh
- +dir
- +cd ..
- +df
- +cd ../ld.so/
- +dir
- +less fixups.c
- +less ld.so.c
- +%1
- +less fixups.h
- +%1
- +jobs
- +%1
- +%2
- +cd arch/arm/drivers/scsi
- +HOME=~root
- +ue acornscsi.c
- +less oak.h
- +less acornscsi.h
- +%1
- +%1
- +ue acornscsi.c
- +less oak.c
- +%1
- +ue acornscsi.c
- +ps -aux
- +%1
- +less acornscsi.h
- +less scsi.h
- +less hosts.c
- +less hosts.h
- +%1
- +less acornscsi.h
- +%1
- +%2
- +less hosts.h
- +ue acornscsi.h
- +%1
- +jobs
- +%2
- +ue acornscsi.c
- +ps -aux
- +kill -9 459
- + kill -TERM 459
- +ps -aux
- +kill -KILL 459
- +ps -aux
- +kill -SEGV 459
- +kill -ILL 459
- +kill -PWR 459
- +kill -l
- +kill -BUS 459
- +kill -IO 459
- +kill -FPE 459
- +kill -IOT 459
- +ps -aux
- +ps -aux
- +cat /proc/devices
- +ue acornscsi.c
- +mount -w -o remount ~lin
- +ue acornscsi.c
- +source ~root/.bashrc
- +cd arch/arm/drivers/block
- +ue blk.h
- +cp ~root/.bashrc ./bashrc
- +mount -w -o remount .
- +cp ~root/.bashrc ./bashrc
- +source ~/.bashrc
- +pwd
- +cp ~root/.bashrc ./.bashrc
- +rm bashrc
- +source ~/.bashrc
- +cd arch/arm/drivers/block/
- +ue mfmhd.c
- +../cdplay
- +cd arch/arm/drivers/block/
- +dir
- +ue mfmhd.c
- diff -r -u -N linux.orig/.bashrc linux.arm/.bashrc
- --- linux.orig/.bashrc Thu Jan 1 01:00:00 1970
- +++ linux.arm/.bashrc Fri Oct 27 23:16:06 1995
- @@ -0,0 +1,4 @@
- +PATH=/sbin:/usr/sbin:$PATH
- +PS1="\u@\h:[\W]:<\!> "
- +alias rm='rm -i'
- +
- diff -r -u -N linux.orig/.config linux.arm/.config
- --- linux.orig/.config Fri Oct 27 23:09:26 1995
- +++ linux.arm/.config Fri Oct 27 23:16:04 1995
- @@ -1,27 +1,26 @@
- #
- # Automatically generated make config: don't edit
- #
- +CONFIG_ARM=1
- +CONFIG_ARMMOUSE=1
-
- #
- # General setup
- #
- -# CONFIG_MATH_EMULATION is not set
- +CONFIG_PAGE_32K=y
- +CONFIG_MATH_EMULATION=y
- CONFIG_BLK_DEV_FD=y
- CONFIG_ST506=y
-
- #
- # Please see drivers/block/README.ide for help/info on IDE drives
- #
- -# CONFIG_BLK_DEV_HD is not set
- -CONFIG_BLK_DEV_IDE=y
- -# CONFIG_BLK_DEV_IDECD is not set
- +CONFIG_BLK_DEV_HD=y
- +# CONFIG_BLK_DEV_IDE is not set
- # CONFIG_BLK_DEV_XD is not set
- CONFIG_NET=y
- # CONFIG_MAX_16M is not set
- -# CONFIG_PCI is not set
- CONFIG_SYSVIPC=y
- -CONFIG_BINFMT_ELF=y
- -CONFIG_M486=y
-
- #
- # Loadable module support
- @@ -32,8 +31,8 @@
- # Networking options
- #
- CONFIG_INET=y
- -# CONFIG_IP_FORWARD is not set
- -# CONFIG_IP_MULTICAST is not set
- +CONFIG_IP_FORWARD=y
- +CONFIG_IP_MULTICAST=y
- # CONFIG_IP_FIREWALL is not set
- # CONFIG_IP_ACCT is not set
-
- @@ -49,44 +48,37 @@
- #
- # SCSI support
- #
- -# CONFIG_SCSI is not set
- +CONFIG_SCSI=y
-
- #
- -# Skipping SCSI configuration options...
- +# SCSI support type (disk, tape, CDrom)
- #
- +CONFIG_BLK_DEV_SD=y
- +# CONFIG_CHR_DEV_ST is not set
- +CONFIG_BLK_DEV_SR=y
- +# CONFIG_CHR_DEV_SG is not set
- +
- +#
- +# SCSI low-level drivers
- +#
- +CONFIG_SCSI_ACORNSCSI_3=y
- +# CONFIG_SCSI_CUMANA_1 is not set
- +# CONFIG_SCSI_ECOSCSI is not set
- +# CONFIG_SCSI_OAK is not set
- +# CONFIG_SCSI_DEBUG is not set
-
- #
- # Network device support
- #
- CONFIG_NETDEVICES=y
- CONFIG_DUMMY=y
- -# CONFIG_SLIP is not set
- +CONFIG_SLIP=y
- +CONFIG_SLIP_COMPRESSED=y
- +# SL_SLIP_LOTS is not set
- +# SL_DUMP is not set
- # CONFIG_PPP is not set
- # CONFIG_PLIP is not set
- -# CONFIG_NET_ALPHA is not set
- -# CONFIG_NET_VENDOR_SMC is not set
- -# CONFIG_LANCE is not set
- -# CONFIG_NET_VENDOR_3COM is not set
- -CONFIG_NET_ISA=y
- -# CONFIG_E2100 is not set
- -# CONFIG_DEPCA is not set
- -# CONFIG_EWRK3 is not set
- -# CONFIG_HPLAN_PLUS is not set
- -# CONFIG_HPLAN is not set
- -CONFIG_NE2000=y
- -# CONFIG_SK_G16 is not set
- -# CONFIG_NET_EISA is not set
- -# CONFIG_NET_POCKET is not set
- -
- -#
- -# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
- -#
- -# CONFIG_CDU31A is not set
- -# CONFIG_MCD is not set
- -CONFIG_SBPCD=y
- -# CONFIG_SBPCD2 is not set
- -# CONFIG_AZTCD is not set
- -# CONFIG_CDU535 is not set
- +CONFIG_ETHER3=y
-
- #
- # Filesystems
- @@ -100,28 +92,19 @@
- CONFIG_PROC_FS=y
- CONFIG_NFS_FS=y
- CONFIG_ISO9660_FS=y
- -# CONFIG_HPFS_FS is not set
- -# CONFIG_SYSV_FS is not set
-
- #
- # character devices
- #
- -# CONFIG_CYCLADES is not set
- CONFIG_PRINTER=y
- -CONFIG_BUSMOUSE=y
- -# CONFIG_PSMOUSE is not set
- -# CONFIG_MS_BUSMOUSE is not set
- -# CONFIG_ATIXL_BUSMOUSE is not set
- -# CONFIG_QIC02_TAPE is not set
- -CONFIG_FTAPE=y
- -NR_FTAPE_BUFFERS=3
-
- #
- # Sound
- #
- -# CONFIG_SOUND is not set
- +CONFIG_SOUND=y
-
- #
- # Kernel hacking
- #
- # CONFIG_PROFILE is not set
- +CONFIG_SCSI_CONSTANTS=y
- diff -r -u -N linux.orig/.config.old linux.arm/.config.old
- --- linux.orig/.config.old Fri Oct 27 23:09:26 1995
- +++ linux.arm/.config.old Fri Oct 27 23:16:05 1995
- @@ -1,27 +1,27 @@
- #
- # Automatically generated make config: don't edit
- #
- +CONFIG_ARM=1
- +CONFIG_ARMMOUSE=1
-
- #
- # General setup
- #
- -# CONFIG_MATH_EMULATION is not set
- +CONFIG_PAGE_32K=y
- +CONFIG_MATH_EMULATION=y
- CONFIG_BLK_DEV_FD=y
- CONFIG_ST506=y
-
- #
- # Please see drivers/block/README.ide for help/info on IDE drives
- #
- -# CONFIG_BLK_DEV_HD is not set
- -CONFIG_BLK_DEV_IDE=y
- -# CONFIG_BLK_DEV_IDECD is not set
- +CONFIG_BLK_DEV_HD=y
- +# CONFIG_BLK_DEV_IDE is not set
- # CONFIG_BLK_DEV_XD is not set
- CONFIG_NET=y
- # CONFIG_MAX_16M is not set
- -# CONFIG_PCI is not set
- CONFIG_SYSVIPC=y
- -CONFIG_BINFMT_ELF=y
- -CONFIG_M486=y
- +# CONFIG_BINFMT_ELF is not set
-
- #
- # Loadable module support
- @@ -32,8 +32,8 @@
- # Networking options
- #
- CONFIG_INET=y
- -# CONFIG_IP_FORWARD is not set
- -# CONFIG_IP_MULTICAST is not set
- +CONFIG_IP_FORWARD=y
- +CONFIG_IP_MULTICAST=y
- # CONFIG_IP_FIREWALL is not set
- # CONFIG_IP_ACCT is not set
-
- @@ -49,43 +49,36 @@
- #
- # SCSI support
- #
- -# CONFIG_SCSI is not set
- +CONFIG_SCSI=y
-
- #
- -# Skipping SCSI configuration options...
- +# SCSI support type (disk, tape, CDrom)
- #
- +CONFIG_BLK_DEV_SD=y
- +# CONFIG_CHR_DEV_ST is not set
- +CONFIG_BLK_DEV_SR=y
- +# CONFIG_CHR_DEV_SG is not set
- +
- +#
- +# SCSI low-level drivers
- +#
- +# CONFIG_SCSI_CUMANA_1 is not set
- +# CONFIG_SCSI_ECOSCSI is not set
- +# CONFIG_SCSI_OAK is not set
- +# CONFIG_SCSI_DEBUG is not set
-
- #
- # Network device support
- #
- CONFIG_NETDEVICES=y
- CONFIG_DUMMY=y
- -# CONFIG_SLIP is not set
- +CONFIG_SLIP=y
- +CONFIG_SLIP_COMPRESSED=y
- +# SL_SLIP_LOTS is not set
- +# SL_DUMP is not set
- # CONFIG_PPP is not set
- # CONFIG_PLIP is not set
- -# CONFIG_NET_ALPHA is not set
- -# CONFIG_NET_VENDOR_SMC is not set
- -# CONFIG_LANCE is not set
- -# CONFIG_NET_VENDOR_3COM is not set
- -CONFIG_NET_ISA=y
- -# CONFIG_E2100 is not set
- -# CONFIG_DEPCA is not set
- -# CONFIG_EWRK3 is not set
- -# CONFIG_HPLAN_PLUS is not set
- -# CONFIG_HPLAN is not set
- -CONFIG_NE2000=y
- -# CONFIG_SK_G16 is not set
- -# CONFIG_NET_EISA is not set
- -# CONFIG_NET_POCKET is not set
- -
- -#
- -# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
- -#
- -# CONFIG_CDU31A is not set
- -# CONFIG_MCD is not set
- -# CONFIG_SBPCD is not set
- -# CONFIG_AZTCD is not set
- -# CONFIG_CDU535 is not set
- +CONFIG_ETHER3=y
-
- #
- # Filesystems
- @@ -99,28 +92,19 @@
- CONFIG_PROC_FS=y
- CONFIG_NFS_FS=y
- CONFIG_ISO9660_FS=y
- -# CONFIG_HPFS_FS is not set
- -# CONFIG_SYSV_FS is not set
-
- #
- # character devices
- #
- -# CONFIG_CYCLADES is not set
- CONFIG_PRINTER=y
- -CONFIG_BUSMOUSE=y
- -# CONFIG_PSMOUSE is not set
- -# CONFIG_MS_BUSMOUSE is not set
- -# CONFIG_ATIXL_BUSMOUSE is not set
- -# CONFIG_QIC02_TAPE is not set
- -CONFIG_FTAPE=y
- -NR_FTAPE_BUFFERS=3
-
- #
- # Sound
- #
- -# CONFIG_SOUND is not set
- +CONFIG_SOUND=y
-
- #
- # Kernel hacking
- #
- # CONFIG_PROFILE is not set
- +CONFIG_SCSI_CONSTANTS=y
- diff -r -u -N linux.orig/.emacsrc linux.arm/.emacsrc
- --- linux.orig/.emacsrc Thu Jan 1 01:00:00 1970
- +++ linux.arm/.emacsrc Fri Oct 27 23:16:06 1995
- @@ -0,0 +1,259 @@
- +; EMACS.RC: Standard MicroEMACS Startup program
- +; for MicroEMACS 3.12 and above
- +; (C)opyright 1987,92 by Daniel M Lawrence
- +; Last Update: 12/28/92
- +
- +set $discmd FALSE
- +write-message "[Setting up....]"
- +
- +; If you screen "SNOWS", comment this line
- + set $flicker "FALSE"
- +
- +; To use an IBM-PC EGA card, uncomment the following line
- +; set $sres "EGA"
- +
- +; If you hate clocks or position counters, comment these
- +set $timeflag TRUE
- +set $posflag TRUE
- +
- +; Set Default Global modes
- +
- +add-global-mode "blue"
- +add-global-mode "WHITE"
- +;bind-to-key meta-prefix ` ;for annoying keyboards with ` at the top left
- +
- +; Toggle function key window display
- +
- +store-procedure toggle-fkeys
- + !if %rcfkeys
- + !goto rcfoff
- + !endif
- +
- +; toggle function key window on
- + save-window
- + 1 next-window
- + !if &sequal $cbufname "emacs.hlp"
- + delete-window
- + !endif
- + !if ¬ &sequal $cbufname "Function Keys"
- + 1 split-current-window
- + 1 select-buffer "Function Keys"
- + add-mode "red"
- + !force 5 resize-window
- + 1 goto-line
- + !endif
- + set %rcfkeys TRUE
- + !force restore-window
- + !if &sequal $cbufname "Function Keys"
- + next-window
- + !endif
- + write-message "[Function key window ON]"
- + !return
- +
- + ;Toggle the function key window off
- +*rcfoff
- + save-window
- + 1 next-window
- + !if &sequal "Function Keys" $cbufname
- + delete-window
- + !endif
- + !force restore-window
- + write-message "[Function key window OFF]"
- + set %rcfkeys FALSE
- +!endm
- +
- +; Bring up Online-help system
- +
- +store-procedure get-help
- + set $discmd FALSE
- + source ehelp.cmd
- + set $discmd TRUE
- +!endm
- +
- +; Load a new page
- +
- +store-procedure get-page-loader
- + !if &seq &find newpage.cmd ""
- + write-message "[Can not find NEWPAGE.CMD]"
- + !return
- + !endif
- + execute-file newpage.cmd
- +!endm
- +
- +;procedure to clean out the current page (which is nothing right now)
- +
- +store-procedure clean
- + ; nothing by default
- +!endm
- +
- +; Set up auto CMODE
- +
- +store-procedure set-default-mode
- + set %rctmp &sin $cfname "."
- + !if &equ %rctmp 0
- + !return
- + !endif
- + set %rctmp &mid $cfname &add %rctmp 1 5
- + !if &or &seq %rctmp "c" &seq %rctmp "h"
- + add-mode "cmode"
- + !endif
- + !if &or &seq %rctmp "cpp" &seq %rctmp "hpp"
- + add-mode "cmode"
- + !endif
- + !if &or &seq %rctmp "mss" &seq %rctmp "txt"
- + add-mode "wrap"
- + !endif
- +!endm
- +set $readhook set-default-mode
- +
- +; This function activates the function key window as
- +; a legitimate place to call up function keys using the mouse
- +
- +store-procedure mouse-clicks
- +
- + ;remember where we started, and do the mouse movement
- + save-window
- + !force mouse-move-down
- +
- + ;If not in the function key window... leave
- + !if ¬ &sequal $cbufname "Function Keys"
- + !return
- + !endif
- +
- + ;First pos is a screen reposition, let it through
- + !if &and &equ $xpos 0 &equ $ypos 0
- + restore-window
- + !return
- + !endif
- +
- + ;Find out what function key were gonna do
- + add-mode magic
- + 2 forward-character
- + set %rctmp $search
- + !force search-reverse "[fF][0-9]"
- + !if &seq $status FALSE
- + delete-mode magic
- + set $search %rctmp
- + !return
- + !endif
- +
- + ;we are on the "f" or "F". Get the function key type and number now
- + set $search %rctmp
- + set %fcase lower
- + !if &equ $curchar 70
- + set %fcase upper
- + !endif
- + 1 forward-character
- + set %fnum &chr $curchar
- + 1 forward-character
- + set %fnum &cat %fnum &chr $curchar
- + set %fnum &add %fnum 0
- + !if &equ %fnum 10
- + set %fnum "0"
- + !endif
- + set %fname &cat "FN" %fnum
- + !if &seq %fcase upper
- + set %fname &cat "S-" %fname
- + !endif
- +
- + ;save the function
- + set %rccmd &bind %fname
- + delete-mode MAGIC
- +
- + ;swallow the up-button
- + set %rctmp >c
- +
- + ;restore the window and exit
- + restore-window
- +
- + ;procedures don't need the square brackets
- + !if &seq &left %rccmd 1 "["
- + set %rccmd &mid %rccmd 2 &sub &len %rccmd 2
- + %rccmd
- + !return
- + !endif
- +
- + ;and then execute it
- + !force execute-named-command %rccmd
- +!endm
- +macro-to-key mouse-clicks MSa
- +
- +; ***** Rebind the Function key group
- +
- +bind-to-key search-forward FN1
- +bind-to-key search-reverse FN2
- +bind-to-key hunt-forward FN3
- +bind-to-key hunt-backward FN4
- +macro-to-key toggle-fkeys FN5
- +macro-to-key get-help FN6
- +bind-to-key next-window FN7
- +macro-to-key get-page-loader FN8
- +bind-to-key save-file FN9
- +bind-to-key exit-emacs FN0
- +
- +bind-to-key execute-macro-10 S-FN1
- +bind-to-key execute-macro-11 S-FN2
- +bind-to-key execute-macro-12 S-FN3
- +bind-to-key execute-macro-13 S-FN4
- +bind-to-key execute-macro-14 S-FN5
- +bind-to-key execute-macro-15 S-FN6
- +bind-to-key execute-macro-16 S-FN7
- +bind-to-key execute-macro-17 S-FN8
- +bind-to-key execute-macro-18 S-FN9
- +bind-to-key execute-macro-19 S-FN0
- +
- +; bring up the function key window
- +
- + 1 split-current-window
- + select-buffer "Function Keys"
- + insert-string "f1 search-> f2 <-search Û MicroEMACS: Text Editor~n"
- + insert-string "f3 hunt-> f4 <-hunt Û ~n"
- + insert-string "f5 fkeys f6 help Û Available function key Pages include:~n"
- + insert-string "f7 nxt wind f8 pg[ ] Û Word Box Emacs Pascal C cObol Lisp~n"
- + insert-string "f9 save f10 exit Û [use the f8 key to load Pages]~n"
- + unmark-buffer
- + delete-window
- + set %rcfkeys TRUE
- +
- + !if &seq $os "UNIX"
- +
- + ;Allow mainframes to simulate function
- + ;keys with ^C<n> and ^C shifted-<n>
- +
- + store-procedure emulate-fkeys
- + !if ¬ $pending
- + write-message "FN-"
- + !endif
- + set %rcchar >key
- + set %rcchar &sindex "1234567890!@#$%^&*()" %rcchar
- + !if &equ %rcchar 0
- + write-message "[Not Bound]"
- + !return
- + !endif
- + clear-message-line
- + set %rctmp "FN"
- + !if &gre %rcchar 10
- + set %rctmp &cat "S-" %rctmp
- + !endif
- + set %rcchar &mid "12345678901234567890" %rcchar 1
- + set %rctmp &bind &cat %rctmp %rcchar
- + !if &seq &lef %rctmp 1 "["
- + set %rctmp &mid %rctmp 2 &sub &len %rctmp 2
- + run %rctmp
- + !return
- + !endif
- + !force execute-named-command %rctmp
- + !endm
- +
- + macro-to-key emulate-fkeys ^C
- +
- + !endif
- +
- + !if &seq $os "MSWIN"
- + source "mewin.cmd"
- + !else
- + toggle-fkeys
- + !endif
- +
- + set $discmd TRUE
- +
- diff -r -u -N linux.orig/.version linux.arm/.version
- --- linux.orig/.version Fri Oct 27 23:09:26 1995
- +++ linux.arm/.version Fri Oct 27 23:16:05 1995
- @@ -1 +1 @@
- -4
- +0
- diff -r -u -N linux.orig/Configure linux.arm/Configure
- --- linux.orig/Configure Fri Oct 27 23:08:06 1995
- +++ linux.arm/Configure Fri Oct 27 23:11:54 1995
- @@ -126,8 +126,10 @@
- fi
- . $CONFIG_IN
-
- -if [ "$CONFIG_SOUND" = "y" ] ; then
- +if [ "$HOSTTYPE" != "arm" ] ; then
- + if [ "$CONFIG_SOUND" = "y" ] ; then
- $MAKE -C drivers/sound config || exit 1
- + fi
- fi
-
- rm -f .config.old
- diff -r -u -N linux.orig/Makefile linux.arm/Makefile
- --- linux.orig/Makefile Fri Oct 27 23:08:06 1995
- +++ linux.arm/Makefile Fri Oct 27 23:09:17 1995
- @@ -2,7 +2,7 @@
- PATCHLEVEL = 2
- SUBLEVEL = 8
-
- -ARCH = i386
- +ARCH = arm
-
- .EXPORT_ALL_VARIABLES:
-
- @@ -86,11 +86,13 @@
-
- ARCHIVES =kernel/kernel.o mm/mm.o fs/fs.o net/net.o ipc/ipc.o
- FILESYSTEMS =fs/filesystems.a
- -DRIVERS =drivers/block/block.a \
- - drivers/char/char.a \
- - drivers/net/net.a
- +DRIVERS =
- LIBS =$(TOPDIR)/lib/lib.a
- -SUBDIRS =kernel drivers mm fs net ipc lib
- +SUBDIRS =kernel mm fs net ipc lib
- +
- +DRIVERS := drivers/block/block.a \
- + drivers/char/char.a \
- + drivers/net/net.a
-
- ifdef CONFIG_SCSI
- DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
- @@ -125,7 +127,8 @@
- $(FILESYSTEMS) \
- $(DRIVERS) \
- $(LIBS) -o vmlinux
- - $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | sort > System.map
- +# $(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
- @@ -198,7 +201,7 @@
- MODV = -DCONFIG_MODVERSIONS
- endif
-
- -modules: include/linux/version.h
- +modules: include/linux/version.h dummy
- @set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i CFLAGS="$(CFLAGS) -DMODULE $(MODV)" modules; done
-
- modules_install:
- diff -r -u -N linux.orig/arch/alpha/Makefile linux.arm/arch/alpha/Makefile
- --- linux.orig/arch/alpha/Makefile Fri Oct 27 23:08:07 1995
- +++ linux.arm/arch/alpha/Makefile Fri Oct 27 23:13:50 1995
- @@ -15,7 +15,7 @@
-
- HEAD := arch/alpha/kernel/head.o
-
- -SUBDIRS := $(SUBDIRS) arch/alpha/kernel arch/alpha/mm arch/alpha/lib
- +SUBDIRS := drivers $(SUBDIRS) arch/alpha/kernel arch/alpha/mm arch/alpha/lib
- ARCHIVES := arch/alpha/kernel/kernel.o arch/alpha/mm/mm.o $(ARCHIVES)
- LIBS := $(TOPDIR)/arch/alpha/lib/lib.a $(LIBS) $(TOPDIR)/arch/alpha/lib/lib.a
-
- diff -r -u -N linux.orig/arch/arm/Makefile linux.arm/arch/arm/Makefile
- --- linux.orig/arch/arm/Makefile Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/Makefile Fri Oct 27 23:14:02 1995
- @@ -0,0 +1,56 @@
- +#
- +# 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)
- +#
- +
- +ZLINKFLAGS =-N -Ttext 0x01800000
- +LINKFLAGS =-Ttext 0x01800000
- +CFLAGS := $(CFLAGS) -fno-omit-frame-pointer
- +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: vmlinux
- + @$(MAKEBOOT) install
- +
- +archclean:
- + @$(MAKEBOOT) clean
- +
- +archdep:
- + @$(MAKEBOOT) dep
- diff -r -u -N linux.orig/arch/arm/boot/Makefile linux.arm/arch/arm/boot/Makefile
- --- linux.orig/arch/arm/boot/Makefile Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/Makefile Fri Oct 27 23:15:23 1995
- @@ -0,0 +1,68 @@
- +#
- +# 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
- +
- +install: $(CONFIGURE) Image
- + sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) Image $(TOPDIR)/System.map "$(INSTALL_PATH)"
- +
- +
- +tools/build: tools/build.c
- + $(HOSTCC) $(CFLAGS) -o $@ $< -I$(TOPDIR)/include -static
- +
- +clean:
- + rm -f Image
- +
- +#zImage: $(CONFIGURE) bootsect setup compressed/vmlinux tools/build
- +# tools/build bootsect setup compressed/vmlinux $(ROOT_DEV) > zImage
- +# sync
- +
- +#compressed/vmlinux: $(TOPDIR)/vmlinux
- +# @$(MAKE) -C compressed vmlinux
- +
- +#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)"
- +
- +#setup: setup.o
- +# $(LD86) -s -o $@ $<
- +
- +#setup.o: setup.s
- +# $(AS86) -o $@ $<
- +
- +#setup.s: setup.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile
- +# $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
- +
- +#bootsect: bootsect.o
- +# $(LD86) -s -o $@ $<
- +
- +#bootsect.o: bootsect.s
- +# $(AS86) -o $@ $<
- +
- +#bootsect.s: bootsect.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile
- +# $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
- +
- +dep:
- +
- +#clean:
- +# rm -f bootsect setup
- +# rm -f zImage tools/build
- +# @$(MAKE) -C compressed clean
- diff -r -u -N linux.orig/arch/arm/boot/install.sh linux.arm/arch/arm/boot/install.sh
- --- linux.orig/arch/arm/boot/install.sh Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/install.sh Fri Oct 27 23:15:25 1995
- @@ -0,0 +1,40 @@
- +#!/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
- +
- +# Default install - same as make zlilo
- +
- +if [ -f $4/vmlinux ]; then
- + rm $4/vmlinux.$1
- +# mv $4/vmlinux $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
- +
- +if [ -x /sbin/bootmap ]; then /sbin/bootmap /dev/hda1; else echo "You have to install it yourself"; fi
- diff -r -u -N linux.orig/arch/arm/boot/tools/build.c linux.arm/arch/arm/boot/tools/build.c
- --- linux.orig/arch/arm/boot/tools/build.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/boot/tools/build.c Fri Oct 27 23:15:25 1995
- @@ -0,0 +1,62 @@
- +#include <stdio.h>
- +#include <stdlib.h>
- +#include <a.out.h>
- +
- +
- +int main(int argc, char **argv)
- +{
- + void *data;
- + struct exec ex;
- + FILE *f, *fo;
- + 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 -r -u -N linux.orig/arch/arm/config.in linux.arm/arch/arm/config.in
- --- linux.orig/arch/arm/config.in Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/config.in Fri Oct 27 23:14:02 1995
- @@ -0,0 +1,199 @@
- +#
- +# For a description of the syntax of this configuration file,
- +# see the Configure script.
- +#
- +
- +echo "#define CONFIG_ARM" >> $CONFIG_H
- +echo "#define CONFIG_ARMMOUSE 1" >> $CONFIG_H
- +echo "CONFIG_ARM=1" >> $CONFIG
- +echo "CONFIG_ARMMOUSE=1" >> $CONFIG
- +
- +comment 'General setup'
- +
- +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
- +bool 'Kernel math emulation' CONFIG_MATH_EMULATION y
- +bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD y
- +bool '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
- +
- +bool '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
- +#bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF n
- +
- +comment 'Loadable module support'
- +bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS n
- +
- +if [ "$CONFIG_NET" = "y" ]; then
- +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
- +comment '(it is safe to leave these untouched)'
- +bool 'PC/TCP compatibility mode' CONFIG_INET_PCTCP n
- +bool 'Reverse ARP' CONFIG_INET_RARP n
- +bool 'Assume subnets are local' CONFIG_INET_SNARL y
- +bool 'Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF 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
- +fi
- +
- +comment 'SCSI support'
- +
- +bool 'SCSI support?' CONFIG_SCSI n
- +
- +if [ "$CONFIG_SCSI" = "n" ]; then
- +
- +comment 'Skipping SCSI configuration options...'
- +
- +else
- +
- +comment 'SCSI support type (disk, tape, CDrom)'
- +
- +bool 'Scsi disk support' CONFIG_BLK_DEV_SD y
- +bool 'Scsi tape support' CONFIG_CHR_DEV_ST n
- +bool 'Scsi CDROM support' CONFIG_BLK_DEV_SR n
- +bool 'Scsi generic support' CONFIG_CHR_DEV_SG n
- +
- +comment 'SCSI low-level drivers'
- +
- +bool 'Acorn SCSI card (aka30) support' CONFIG_SCSI_ACORNSCSI_3 y
- +bool 'Cumana 1 support' CONFIG_SCSI_CUMANA_1 n
- +bool 'EcoScsi support' CONFIG_SCSI_ECOSCSI n
- +bool 'Oak SCSI support' CONFIG_SCSI_OAK n
- +bool 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n
- +fi
- +
- +
- +if [ "$CONFIG_NET" = "y" ]; then
- +
- +comment 'Network device support'
- +
- +bool 'Network device support?' CONFIG_NETDEVICES y
- +if [ "$CONFIG_NETDEVICES" = "n" ]; then
- +
- +comment 'Skipping network driver configuration options...'
- +
- +else
- +bool 'Dummy net driver support' CONFIG_DUMMY y
- +bool 'SLIP (serial line) support' CONFIG_SLIP y
- +if [ "$CONFIG_SLIP" = "y" ]; then
- + bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED y
- + bool ' 16 channels instead of 4' SL_SLIP_LOTS n
- + bool ' SLIP debugging on' SL_DUMP y
- +fi
- +bool 'PPP (point-to-point) support' CONFIG_PPP y
- +bool 'PLIP (parallel port) support' CONFIG_PLIP y
- +bool 'Ether3 (NQ8005) support' CONFIG_ETHER3 n
- +fi
- +fi
- +
- +#comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
- +
- +#bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n
- +#bool 'Mitsumi (not IDE/ATAPI) CDROM driver support' CONFIG_MCD n
- +#bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n
- +#if [ "$CONFIG_SBPCD" = "y" ]; then
- +# bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n
- +# if [ "$CONFIG_SBPCD2" = "y" ]; then
- +# bool 'Matsushita/Panasonic third CDROM controller support' CONFIG_SBPCD3 n
- +# if [ "$CONFIG_SBPCD3" = "y" ]; then
- +# bool 'Matsushita/Panasonic fourth CDROM controller support' CONFIG_SBPCD4 n
- +# fi
- +# fi
- +#fi
- +#bool 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n
- +#bool 'Sony CDU535 CDROM driver support' CONFIG_CDU535 n
- +
- +comment 'Filesystems'
- +
- +bool 'Standard (minix) fs support' CONFIG_MINIX_FS n
- +bool 'Extended fs support' CONFIG_EXT_FS n
- +bool 'Second extended fs support' CONFIG_EXT2_FS y
- +bool 'xiafs filesystem support' CONFIG_XIA_FS n
- +bool 'msdos fs support' CONFIG_MSDOS_FS y
- +if [ "$CONFIG_MSDOS_FS" = "y" ]; then
- +bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n
- +fi
- +bool '/proc filesystem support' CONFIG_PROC_FS y
- +if [ "$CONFIG_INET" = "y" ]; then
- +bool 'NFS filesystem support' CONFIG_NFS_FS y
- +fi
- +if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_CDU31A" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_AZTCD" = "y" -o "$CONFIG_CDU535" = "y" ]; then
- + bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y
- +else
- + bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
- +fi
- +#bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
- +#bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
- +
- +comment 'character devices'
- +
- +#bool 'Cyclades async mux support' CONFIG_CYCLADES n
- +bool 'Parallel printer support' CONFIG_PRINTER y
- +#bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
- +#bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n
- +#if [ "$CONFIG_PSMOUSE" = "y" ]; then
- +#bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y
- +#fi
- +#bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
- +#bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n
- +
- +
- +#bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n
- +#if [ "$CONFIG_QIC02_TAPE" = "y" ]; then
- +#bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF y
- +#if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then
- +
- +#comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!'
- +
- +#else
- +
- +#comment '>>> Setting runtime QIC-02 configuration is done with qic02conf'
- +#comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/'
- +
- +#fi
- +#fi
- +
- +#bool 'QIC-117 tape support' CONFIG_FTAPE n
- +#if [ "$CONFIG_FTAPE" = "y" ]; then
- +#int ' number of ftape buffers' NR_FTAPE_BUFFERS 3
- +#fi
- +
- +comment 'Sound'
- +
- +bool 'Sound support' CONFIG_SOUND n
- +
- +comment 'Kernel hacking'
- +
- +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
- +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 -r -u -N linux.orig/arch/arm/drivers/Makefile linux.arm/arch/arm/drivers/Makefile
- --- linux.orig/arch/arm/drivers/Makefile Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/Makefile Fri Oct 27 23:14:58 1995
- @@ -0,0 +1,48 @@
- +#
- +# 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...
- +
- +.S.s:
- + $(CPP) -traditional $< -o $*.s
- +.c.s:
- + $(CC) $(CFLAGS) -S $<
- +.s.o:
- + $(AS) -c -o $*.o $<
- +.c.o:
- + $(CC) $(CFLAGS) -c $<
- +
- +SUBDIRS = block char net #streams
- +
- +ifdef CONFIG_SCSI
- +SUBDIRS := $(SUBDIRS) scsi
- +endif
- +
- +ifdef CONFIG_SOUND
- +SUBDIRS := $(SUBDIRS) sound
- +endif
- +
- +all: driversubdirs
- +
- +driversubdirs: dummy
- + set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done
- +
- +modules: dummy
- + set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i modules; done
- +
- +dep:
- + set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i dep; done
- +
- +dummy:
- +
- +#
- +# include a dependency file if one exists
- +#
- +ifeq (.depend,$(wildcard .depend))
- +include .depend
- +endif
- +
- diff -r -u -N linux.orig/arch/arm/drivers/block/Makefile linux.arm/arch/arm/drivers/block/Makefile
- --- linux.orig/arch/arm/drivers/block/Makefile Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/Makefile Fri Oct 27 23:14:45 1995
- @@ -0,0 +1,75 @@
- +#
- +# 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.
- +#
- +
- +.c.s:
- + $(CC) $(CFLAGS) -S $<
- +.s.o:
- + $(AS) -c -o $*.o $<
- +.c.o:
- + $(CC) $(CFLAGS) -c $<
- +
- +#
- +# Note : at this point, these files are compiled on all systems.
- +# In the future, some of these should be built conditionally.
- +#
- +
- +all: block.a
- +
- +OBJS := ll_rw_blk.o ramdisk.o genhd.o hdsrch.o
- +SRCS := ll_rw_blk.c ramdisk.c genhd.c hdsrch.c
- +BLOCK_MODULE_OBJS =
- +
- +ifdef CONFIG_BLK_DEV_FD
- +OBJS := $(OBJS) floppy.o
- +SRCS := $(SRCS) floppy.c
- +endif
- +
- +ifdef CONFIG_BLK_DEV_HD
- +OBJS := $(OBJS) hd.o
- +SRCS := $(SRCS) hd.c
- +endif
- +
- +ifdef CONFIG_BLK_DEV_IDE
- +OBJS := ide.o $(OBJS)
- +SRCS := ide.c $(SRCS)
- +endif
- +
- +ifdef CONFIG_BLK_DEV_MFM
- +OBJS := mfmhd.o $(OBJS)
- +SRCS := mfmhd.c $(OBJS)
- +else
- +BLOCK_MODULE_OBJS := mfm-hd.o
- +
- +mfm-hd.o: mfmhd.o
- + ld -r -o mfm-hd.o mfmhd.o `gcc --print-libgcc-file-name`
- +
- +endif
- +
- +block.a: $(OBJS)
- + rm -f block.a
- + $(AR) rcs block.a $(OBJS)
- + sync
- +
- +dep:
- + $(CPP) -M $(SRCS) > .depend
- +
- +modules: $(BLOCK_MODULE_OBJS)
- + echo $(BLOCK_MODULE_OBJS) > ../../../../modules/BLOCK_MODULES
- + (cd ../../../../modules;for i in $(BLOCK_MODULE_OBJS); do ln -sf ../arch/arm/drivers/block/$$i .; done)
- +
- +dummy:
- +
- +#
- +# include a dependency file if one exists
- +#
- +ifeq (.depend,$(wildcard .depend))
- +include .depend
- +endif
- diff -r -u -N linux.orig/arch/arm/drivers/block/blk.h linux.arm/arch/arm/drivers/block/blk.h
- --- linux.orig/arch/arm/drivers/block/blk.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/blk.h Fri Oct 27 23:14:45 1995
- @@ -0,0 +1,295 @@
- +#ifndef _BLK_H
- +#define _BLK_H
- +
- +#include <linux/blkdev.h>
- +#include <linux/locks.h>
- +#include <linux/config.h>
- +
- +/*
- + * NR_REQUEST is the number of entries in the request-queue.
- + * NOTE that writes may use only the low 2/3 of these: reads
- + * take precedence.
- + *
- + * 32 seems to be a reasonable number: enough to get some benefit
- + * from the elevator-mechanism, but not so much as to lock a lot of
- + * buffers when they are in the queue. 64 seems to be too many (easily
- + * long pauses in reading when heavy writing/syncing is going on)
- + */
- +#define NR_REQUEST 64
- +
- +/*
- + * This is used in the elevator algorithm: Note that
- + * reads always go before writes. This is natural: reads
- + * are much more time-critical than writes.
- + */
- +#define IN_ORDER(s1,s2) \
- +((s1)->cmd < (s2)->cmd || ((s1)->cmd == (s2)->cmd && \
- +((s1)->dev < (s2)->dev || (((s1)->dev == (s2)->dev && \
- +(s1)->sector < (s2)->sector)))))
- +
- +/*
- + * These will have to be changed to be aware of different buffer
- + * sizes etc.. It actually needs a major cleanup.
- + */
- +#define SECTOR_MASK (blksize_size[MAJOR_NR] && \
- + blksize_size[MAJOR_NR][MINOR(CURRENT->dev)] ? \
- + ((blksize_size[MAJOR_NR][MINOR(CURRENT->dev)] >> 9) - 1) : \
- + ((BLOCK_SIZE >> 9) - 1))
- +
- +#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
- +
- +extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end);
- +extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end);
- +extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end);
- +#ifdef CONFIG_SBPCD
- +extern unsigned long sbpcd_init(unsigned long, unsigned long);
- +#endif /* CONFIG_SBPCD */
- +extern void set_device_ro(int dev,int flag);
- +
- +extern void rd_load(void);
- +extern long rd_init(long mem_start, int length);
- +extern int ramdisk_size;
- +
- +extern unsigned long xd_init(unsigned long mem_start, unsigned long mem_end);
- +
- +#define RO_IOCTLS(dev,where) \
- + case BLKROSET: if (!suser()) return -EACCES; \
- + set_device_ro((dev),get_fs_long((long *) (where))); return 0; \
- + case BLKROGET: { int __err = verify_area(VERIFY_WRITE, (void *) (where), sizeof(long)); \
- + if (!__err) put_fs_long(0!=is_read_only(dev),(long *) (where)); return __err; }
- +
- +#ifdef MAJOR_NR
- +
- +/*
- + * Add entries as needed. Currently the only block devices
- + * supported are hard-disks and floppies.
- + */
- +
- +#if (MAJOR_NR == MEM_MAJOR)
- +
- +/* ram disk */
- +#define DEVICE_NAME "ramdisk"
- +#define DEVICE_REQUEST do_rd_request
- +#define DEVICE_NR(device) ((device) & 7)
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == FLOPPY_MAJOR)
- +
- +static void floppy_off(unsigned int nr);
- +
- +#define DEVICE_NAME "floppy"
- +#define DEVICE_INTR do_floppy
- +#define DEVICE_REQUEST do_fd_request
- +#define DEVICE_NR(device) ( ((device) & 3) | (((device) & 0x80 ) >> 5 ))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
- +
- +#elif (MAJOR_NR == HD_MAJOR)
- +
- +/* harddisk: timeout is 6 seconds.. */
- +#define DEVICE_NAME "harddisk"
- +#define DEVICE_INTR do_hd
- +#define DEVICE_TIMEOUT HD_TIMER
- +#define TIMEOUT_VALUE 600
- +#define DEVICE_REQUEST do_hd_request
- +#define DEVICE_NR(device) (MINOR(device)>>6)
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == SCSI_DISK_MAJOR)
- +
- +#define DEVICE_NAME "scsidisk"
- +#define DEVICE_INTR do_sd
- +#define TIMEOUT_VALUE 200
- +#define DEVICE_REQUEST do_sd_request
- +#define DEVICE_NR(device) (MINOR(device) >> 4)
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == SCSI_TAPE_MAJOR)
- +
- +#define DEVICE_NAME "scsitape"
- +#define DEVICE_INTR do_st
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == SCSI_CDROM_MAJOR)
- +
- +#define DEVICE_NAME "CD-ROM"
- +#define DEVICE_INTR do_sr
- +#define DEVICE_REQUEST do_sr_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == XT_DISK_MAJOR)
- +
- +#define DEVICE_NAME "xt disk"
- +#define DEVICE_REQUEST do_xd_request
- +#define DEVICE_NR(device) (MINOR(device) >> 6)
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif defined(MFM_DISK_MAJOR) && (MAJOR_NR == MFM_DISK_MAJOR)
- +
- +#define DEVICE_NAME "mfm disk"
- +#define DEVICE_INTR do_mfm
- +#define DEVICE_REQUEST do_mfm_request
- +#define DEVICE_NR(device) (MINOR(device) >> 6)
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == CDU31A_CDROM_MAJOR)
- +
- +#define DEVICE_NAME "CDU31A"
- +#define DEVICE_REQUEST do_cdu31a_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == MITSUMI_CDROM_MAJOR)
- +
- +#define DEVICE_NAME "Mitsumi CD-ROM"
- +/* #define DEVICE_INTR do_mcd */
- +#define DEVICE_REQUEST do_mcd_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
- +
- +#define DEVICE_NAME "Matsushita CD-ROM controller #1"
- +#define DEVICE_REQUEST do_sbpcd_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == MATSUSHITA_CDROM2_MAJOR)
- +
- +#define DEVICE_NAME "Matsushita CD-ROM controller #2"
- +#define DEVICE_REQUEST do_sbpcd2_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == MATSUSHITA_CDROM3_MAJOR)
- +
- +#define DEVICE_NAME "Matsushita CD-ROM controller #3"
- +#define DEVICE_REQUEST do_sbpcd3_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == MATSUSHITA_CDROM4_MAJOR)
- +
- +#define DEVICE_NAME "Matsushita CD-ROM controller #4"
- +#define DEVICE_REQUEST do_sbpcd4_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#endif
- +
- +#if (MAJOR_NR != SCSI_TAPE_MAJOR)
- +
- +#ifndef CURRENT
- +#define CURRENT (blk_dev[MAJOR_NR].current_request)
- +#endif
- +
- +#define CURRENT_DEV DEVICE_NR(CURRENT->dev)
- +
- +#ifdef DEVICE_INTR
- +void (*DEVICE_INTR)(void) = NULL;
- +#endif
- +#ifdef DEVICE_TIMEOUT
- +
- +#define SET_TIMER \
- +((timer_table[DEVICE_TIMEOUT].expires = jiffies + TIMEOUT_VALUE), \
- +(timer_active |= 1<<DEVICE_TIMEOUT))
- +
- +#define CLEAR_TIMER \
- +timer_active &= ~(1<<DEVICE_TIMEOUT)
- +
- +#define SET_INTR(x) \
- +if ((DEVICE_INTR = (x)) != NULL) \
- + SET_TIMER; \
- +else \
- + CLEAR_TIMER;
- +
- +#else
- +
- +#define SET_INTR(x) (DEVICE_INTR = (x))
- +
- +#endif
- +static void (DEVICE_REQUEST)(void);
- +
- +/* end_request() - SCSI devices have their own version */
- +
- +#if ! SCSI_MAJOR(MAJOR_NR)
- +
- +static void end_request(int uptodate)
- +{
- + struct request * req;
- + struct buffer_head * bh;
- +
- + req = CURRENT;
- + req->errors = 0;
- + if (!uptodate) {
- + printk(DEVICE_NAME " I/O error\n");
- + printk("dev %04lX, sector %lu\n",
- + (unsigned long)req->dev, req->sector);
- + req->nr_sectors--;
- + req->nr_sectors &= ~SECTOR_MASK;
- + req->sector += (BLOCK_SIZE / 512);
- + req->sector &= ~SECTOR_MASK;
- + }
- +
- + if ((bh = req->bh) != NULL) {
- + req->bh = bh->b_reqnext;
- + bh->b_reqnext = NULL;
- + bh->b_uptodate = uptodate;
- + if (!uptodate) bh->b_req = 0; /* So no "Weird" errors */
- + unlock_buffer(bh);
- + if ((bh = req->bh) != NULL) {
- + req->current_nr_sectors = bh->b_size >> 9;
- + if (req->nr_sectors < req->current_nr_sectors) {
- + req->nr_sectors = req->current_nr_sectors;
- + printk("end_request: buffer-list destroyed\n");
- + }
- + req->buffer = bh->b_data;
- + return;
- + }
- + }
- + DEVICE_OFF(req->dev);
- + CURRENT = req->next;
- + if (req->sem != NULL)
- + up(req->sem);
- + req->dev = -1;
- + wake_up(&wait_for_request);
- +}
- +#endif
- +
- +#ifdef DEVICE_INTR
- +#define CLEAR_INTR SET_INTR(NULL)
- +#else
- +#define CLEAR_INTR
- +#endif
- +
- +#define INIT_REQUEST \
- + if (!CURRENT) {\
- + CLEAR_INTR; \
- + return; \
- + } \
- + if (MAJOR(CURRENT->dev) != MAJOR_NR) \
- + panic(DEVICE_NAME ": request list destroyed"); \
- + if (CURRENT->bh) { \
- + if (!CURRENT->bh->b_lock) \
- + panic(DEVICE_NAME ": block not locked"); \
- + }
- +
- +#endif
- +
- +#endif
- +#endif
- diff -r -u -N linux.orig/arch/arm/drivers/block/floppy.c linux.arm/arch/arm/drivers/block/floppy.c
- --- linux.orig/arch/arm/drivers/block/floppy.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/floppy.c Fri Oct 27 23:14:43 1995
- @@ -0,0 +1,3227 @@
- +/*
- + * linux/kernel/floppy.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + * Copyright (C) 1993, 1994 Alain Knaff
- + */
- +
- +/* Configuration */
- +/* The following does some extra sanity checks */
- +#define SANITY
- +
- +/* 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. */
- +#define ALLOWED_DRIVE_MASK 0x0F
- +
- +/* Undefine the following if you have to floppy disk controllers:
- + * This works at least for me; if you get two controllers working, with
- + * drives attached to both, please mail me: Alain.Knaff@imag.fr */
- +/* #define HAVE_2_CONTROLLERS */
- +
- +
- +/* Define the following if you don't like that your drives seek audibly
- + * after a disk change (but it may not work correctly for everybody)
- + */
- +#define SILENT_DC_CLEAR
- +
- +
- +/* End of configuration */
- +
- +/*
- + * 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.
- + */
- +int no_floppies;
- +#define FLOPPY_IRQ 12
- +#define FLOPPY_DMA 2
- +#define DEBUGT 2
- +
- +#include <linux/config.h>
- +#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>
- +#include <linux/fd.h>
- +#include <linux/errno.h>
- +#include <linux/malloc.h>
- +#include <linux/string.h>
- +#include <linux/fcntl.h>
- +#include <linux/delay.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"
- +
- +static unsigned int changed_floppies = 0xff, fake_change = 0;
- +static int initialising=1;
- +
- +#ifdef HAVE_2_CONTROLLERS
- +#define N_FDC 2
- +#define N_DRIVE 8
- +#else
- +#define N_FDC 1
- +#define N_DRIVE 4
- +#endif
- +
- +#define TYPE(x) ( ((x)>>2) & 0x1f )
- +#define DRIVE(x) ( ((x)&0x03) | (((x)&0x80 ) >> 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 UDP (&drive_params[drive])
- +#define UDRS (&drive_state[drive])
- +#define UDRWE (&write_errors[drive])
- +#define UFDCS (&fdc_state[FDC(drive)])
- +
- +#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));
- +
- +/* 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.
- + */
- +#define MAX_DISK_SIZE 3984
- +
- +
- +
- +/*
- + * 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.
- + */
- +#if 0
- +#define LAST_DMA_ADDR (0x1000000)
- +#else
- +/* Everything has to go via dma buffer */
- +#define LAST_DMA_ADDR (0x1)
- +#endif
- +#define K_64 (0x10000) /* 64 k */
- +/*
- + * 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])
- +
- +/*
- + * this struct defines the different floppy drive types.
- + */
- +static struct {
- + struct floppy_drive_params params;
- + 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, 100, 300, 0, 2, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0,
- + 0, { 7, 4, 8, 2, 1, 5, 3,10}, 150, 0 }, "unknown" },
- +
- +{{1, 300, 16, 16, 8000, 100, 300, 0, 2, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0,
- + 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 150, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
- +
- +{{2, 500, 16, 16, 6000, 40, 300, 14, 2, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0,
- + 0, { 2, 5, 6,23,10,20,11, 0}, 150, 2 }, "1.2M" }, /*5 1/4 HD AT*/
- +
- +{{3, 250, 16, 16, 3000, 100, 300, 0, 2, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
- + 0, { 4,22,21,30, 3, 0, 0, 0}, 150, 4 }, "720k" }, /*3 1/2 DD*/
- +
- +#if 0
- +{{4, 500, 16, 16, 4000, 40, 300, 10, 2, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
- + 0, { 7, 4,25,22,31,21,29,11}, 150, 7 }, "1.44M" }, /*3 1/2 HD*/
- +#else
- +{{4, 500, 16, 16, 3000, 40, 300, 10, 2, 5, 83, 3*HZ, 20, {3,1,2,0,0}, FTD_MSG,
- + 0, { 7, 4,25,22,31,21,29,11}, 150, 7 }, "1.44M" }, /*3 1/2 HD*/
- +#endif
- +
- +{{5, 1000, 15, 8, 3000, 40, 300, 10, 2, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
- + 0, { 7, 8, 4,25,28,22,31,21}, 150, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
- +
- +{{6, 1000, 15, 8, 3000, 40, 300, 10, 2, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
- + 0, { 7, 8, 4,25,28,22,31,21}, 150, 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 volatile drive_state[N_DRIVE];
- +static struct floppy_write_errors volatile write_errors[N_DRIVE];
- +static struct floppy_raw_cmd raw_cmd;
- +
- +/*
- + * This struct defines the different floppy types.
- + *
- + * The 'stretch' tells if the tracks need to be doubled for some
- + * types (ie 360kB diskette in 1.2MB drive etc). Others should
- + * be self-explanatory.
- + */
- +/*
- + 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
- +#ifdef HAVE_2_CONTROLLERS
- + ,
- + NULL, NULL, NULL, NULL
- +#endif
- +};
- +
- +/*
- + * 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];
- +
- +/*
- + * 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;
- +
- +/* 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.
- + */
- +#if 0
- +extern char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS];
- +#else
- +char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS];
- +#endif
- +#define max_buffer_sectors MAX_BUFFER_SECTORS
- +
- +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;
- +
- +static void floppy_start(void);
- +static void redo_fd_request(void);
- +static void recalibrate_floppy(void);
- +static void seek_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
- +#define PROVEN_ABSENT -4
- +
- +/* 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];
- +int fdc; /* current fdc */
- +
- +static struct floppy_struct * floppy = floppy_type;
- +static unsigned char current_drive = 255;
- +static long current_count_sectors = 0;
- +static char *current_addr = 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.
- + */
- +
- +unsigned char floppy_selects1[]={ 0x10, 0x21, 0x23, 0x33 };
- +unsigned char floppy_selects2[]={ 0 , 0 , 0 , 0 };
- +
- +int fd_sectsizes[256];
- +
- +/*
- + * Debugging
- + * =========
- + */
- +static inline void set_debugt(void)
- +{
- +#ifdef DEBUGT
- + debugtimer = jiffies;
- +#endif
- +}
- +
- +static inline void debugt(char *message)
- +{
- +#ifdef DEBUGT
- + if ( DP->flags & DEBUGT )
- + printk("%s dtime=%lu\n", message, jiffies-debugtimer );
- +#endif
- +}
- +
- +/*
- + * 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 changed_floppies flag,
- + * and the last_checked date.
- + *
- + * last_checked is the date of the last check which showed 'no disk change'
- + * changed_floppies 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.
- + *
- + * 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)
- +{
- + if(jiffies < DP->select_delay + DRS->select_date)
- + udelay(20000);
- +
- + if(inb_p(FD_DIR) & 0x80){
- + UDRS->flags |= FD_VERIFY; /* verify write protection */
- +
- + if(UDRS->maxblock || /* disk change check */
- + !(UDRS->flags & FD_DISK_NEWCHANGE)){/* disk presence check */
- + /* mark it changed or absent */
- + set_bit(drive,&changed_floppies);
- +
- + /* invalidate its geometry */
- + if (UDRS->keep_data >= 0) {
- + if ((DP->flags & FTD_MSG) &&
- + current_type[drive] != NULL)
- + DPRINT("Disk type is undefined after "
- + "disk change\n");
- + current_type[drive] = NULL;
- + floppy_sizes[drive] = MAX_DISK_SIZE;
- + }
- + }
- + UDRS->flags |= FD_DISK_NEWCHANGE;
- + return 1;
- + } else {
- + UDRS->last_checked=jiffies;
- + UDRS->flags &= ~FD_DISK_NEWCHANGE;
- + return 0;
- + }
- +}
- +
- +static 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 locked=0;
- +static int set_dor(int fdc, char mask, char data)
- +{
- + register unsigned char drive, unit, newdor,olddor;
- +
- + locked=1;
- + olddor = FDCS->dor;
- + newdor = (olddor & mask) | data;
- + if ( newdor != olddor ){
- + unit = olddor & 0x3;
- + drive = REVDRIVE(fdc,unit);
- + if ( olddor & ( 0x10 << unit ))
- + disk_change(drive);
- + FDCS->dor = newdor;
- + arm_set_dor(newdor);
- + }
- + locked=0;
- + return olddor;
- +}
- +
- +static void twaddle(void)
- +{
- + cli();
- +#if 0
- + outb_p(FDCS->dor & ~(0x10<<UNIT(current_drive)),FD_DOR);
- + outb_p(FDCS->dor, FD_DOR);
- +#else
- + arm_set_dor(FDCS->dor);
- +#endif
- + sti();
- +}
- +
- +/* 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 &&
- + UDRS->track != PROVEN_ABSENT &&
- + ( 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 ){
- + fdc = FDC(drive);
- + current_drive = drive;
- + }
- + set_dor(fdc,~0,8);
- +#ifdef HAVE_2_CONTROLLERS
- + set_dor(1-fdc, ~8, 0);
- +#endif
- + if ( FDCS->rawcmd == 2 )
- + reset_fdc_info(1);
- + if( inb_p(FD_STATUS) != STATUS_READY )
- + FDCS->reset = 1;
- +}
- +
- +static int usage_count = 0;
- +/* 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);
- + return 0;
- +}
- +
- +#define LOCK_FDC(drive,interruptible) \
- +if(lock_fdc(drive,interruptible)) return -EINTR;
- +
- +/* unlocks the driver */
- +static inline void unlock_fdc(void)
- +{
- + 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;
- + timer_active &= ~(1 << FLOPPY_TIMER);
- + 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));
- +
- + if(locked)
- + floppy_off(nr);
- + else
- + 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 }
- +#ifdef HAVE_2_CONTROLLERS
- + ,
- + { 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 }
- +#endif
- +};
- +
- +/* schedules motor off */
- +static void floppy_off(unsigned int nr)
- +{
- + unsigned long volatile delta;
- + register int fdc=FDC(nr);
- +
- + if( !(FDCS->dor & ( 0x10 << UNIT(nr))))
- + return;
- +
- + del_timer(motor_off_timer+nr);
- +
- + /* make spindle stop in a position which minimizes spinup time
- + * next time */
- + if ( drive_params[nr].rps ){
- + delta = jiffies - drive_state[nr].first_read_date + HZ -
- + drive_params[nr].spindown_offset;
- + delta = (( delta * drive_params[nr].rps) % HZ ) /
- + drive_params[nr].rps;
- + motor_off_timer[nr].expires = drive_params[nr].spindown - delta;
- + }
- + add_timer(motor_off_timer+nr);
- +}
- +
- +/*
- + * 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;
- +
- + saved_drive = current_drive % N_DRIVE;
- + for(i=0; i< N_DRIVE; i++){
- + drive = (saved_drive + i + 1 ) % N_DRIVE;
- + if ( UDRS->fd_ref == 0 )
- + continue; /* skip closed drives */
- + set_fdc(drive);
- + if (!(FDCS->dor & (0x10 << UNIT(drive))) ||
- + ((FDCS->dor & 0x3) != UNIT(drive)))
- + UDRS->select_date = jiffies;
- + 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 );
- + }
- + current_drive = saved_drive;
- +}
- +
- +typedef void (*timeout_fn)(unsigned long);
- +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)
- +{
- + 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 = 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 nr, 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 - jiffies;
- + add_timer(&fd_timer);
- + return 1;
- + }
- + return 0;
- +}
- +
- +static void setup_DMA(void)
- +{
- +#ifdef SANITY
- + if ((!CURRENT ||
- + CURRENT->buffer != current_addr ||
- + raw_cmd.length > 512 * CURRENT->nr_sectors) &&
- + (current_addr < floppy_track_buffer ||
- + current_addr + raw_cmd.length >
- + floppy_track_buffer + 1024 * max_buffer_sectors)){
- + printk("bad address. start=%p lg=%lx tb=%p\n",
- + current_addr, 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 0
- + if ((long) current_addr % 512 ){
- + printk("non aligned address: %p\n", current_addr );
- + cont->done(0);
- + FDCS->reset=1;
- + return;
- + }
- + if ( ( (long)current_addr & ~(64*1024-1) ) !=
- + ((long)(current_addr + raw_cmd.length-1) & ~(64*1024-1))){
- + printk("DMA crossing 64-K boundary %p-%p\n",
- + current_addr, current_addr + 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, (long) current_addr);
- + set_dma_count(FLOPPY_DMA, raw_cmd.length);
- + enable_dma(FLOPPY_DMA);
- + sti();
- +}
- +
- +/* sends a command byte to the fdc */
- +static int output_byte(char byte)
- +{
- + int counter;
- + unsigned char status;
- +
- + if (FDCS->reset)
- + return -1;
- + for(counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
- + status = inb_p(FD_STATUS) &(STATUS_READY|STATUS_DIR|STATUS_DMA);
- + if (status == STATUS_READY){
- + outb_p(byte,FD_DATA);
- + return 0;
- + }
- + if (!(status & STATUS_READY))
- + continue;
- + 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;
- +
- + 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)
- + 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(0x1A); /* 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 void fdc_dtr(void)
- +{
- + /* If data rate not already set to desired value, set it. */
- + if ( raw_cmd.rate == FDCS->dtr)
- + return;
- +
- + /* 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.
- + */
- + udelay(5000);
- + FDCS->dtr = raw_cmd.rate;
- +} /* 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)>>6) {
- + case 1: /* error occured during command execution */
- + bad = 1;
- + if (ST1 & ST1_WP) {
- + DPRINT("Drive is write protected\n");
- + DRS->flags &= ~FD_DISK_WRITABLE;
- + cont->done(0);
- + bad = 2;
- + } else if (ST1 & ST1_ND) {
- + DRS->flags |= 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)
- +{
- +printk("Wrong cylinder!\n");
- + /* wrong cylinder => recal */
- + DRS->track = NEED_2_RECAL;
- +}
- + return bad;
- + case 2: /* invalid command given */
- + DPRINT("Invalid FDC command given!\n");
- + cont->done(0);
- + return 2;
- + case 3:
- + 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(current_drive,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();
- +}
- +
- +#ifdef SILENT_DC_CLEAR
- +static int blind_seek;
- +#endif
- +
- +/*
- + * 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
- +#ifdef SILENT_DC_CLEAR
- + set_dor(fdc, ~0, (0x10 << UNIT(current_drive)));
- +#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
- +#ifdef SILENT_DC_CLEAR
- + && !blind_seek
- +#endif
- + )
- + DRS->flags &= ~FD_DISK_NEWCHANGE; /* effective seek */
- + DRS->track = ST1;
- + DRS->select_date = jiffies;
- + seek_floppy();
- +}
- +
- +static void check_wp(void)
- +{
- + if (DRS->flags & FD_VERIFY) {
- + /* check write protection */
- + output_byte( FD_GETSTATUS );
- + output_byte( UNIT(current_drive) );
- + if ( result() != 1 ){
- + FDCS->reset = 1;
- + return;
- + }
- + DRS->flags &= ~(FD_VERIFY | FD_DISK_WRITABLE | FD_NEED_TWADDLE);
- +
- + if (!( ST3 & 0x40))
- + DRS->flags |= FD_DISK_WRITABLE;
- + }
- +}
- +
- +static void seek_floppy(void)
- +{
- + int track;
- +
- +#ifdef SILENT_DC_CLEAR
- + blind_seek=0;
- +#endif
- + disk_change(current_drive);
- + if ((raw_cmd.flags & FD_RAW_NEED_DISK) &&
- + test_bit(current_drive,&changed_floppies)){
- + /* 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.
- + */
- + cont->done(0);
- + cont->redo();
- + return;
- + }
- + if ( DRS->track <= NEED_1_RECAL ){
- + recalibrate_floppy();
- + return;
- + } else if ((DRS->flags & 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 {
- +#ifdef SILENT_DC_CLEAR
- + set_dor(fdc, ~ (0x10 << UNIT(current_drive)), 0);
- + blind_seek = 1;
- +#endif
- + track = 1;
- + }
- + } else {
- + check_wp();
- + if (raw_cmd.track != DRS->track)
- + track = raw_cmd.track;
- + else {
- + setup_rw_floppy();
- + return;
- + }
- + }
- +
- +#ifndef SILENT_DC_CLEAR
- + if ( !track && DRS->track >= 0 && DRS->track < 80 ){
- + DRS->flags &= ~FD_DISK_NEWCHANGE;
- + /* if we go to track 0 anyways, we can just as well use
- + * recalibrate */
- + recalibrate_floppy();
- + } else
- +#endif
- + {
- + 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 PROVEN_ABSENT:
- +#ifdef DEBUGT
- + debugt("recal interrupt proven absent:");
- +#endif
- + /* fall through */
- + 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
- + */
- + DRS->flags &= ~FD_DISK_NEWCHANGE;
- + /* 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;
- + seek_floppy();
- +}
- +
- +/*
- + * 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;
- + cli();
- + 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;
- + 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 unused, struct pt_regs *regs)
- +{
- + void (*handler)(void) = DEVICE_INTR;
- +
- + CLEAR_INTR;
- + if ( fdc >= N_FDC ){ /* we don't even know which FDC is the culprit */
- + printk("floppy interrupt on bizarre fdc\n");
- + return;
- + }
- + inr = result();
- + if (!handler){
- + unexpected_floppy_interrupt();
- + 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);
- +}
- +
- +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");
- + for(i=0; i<N_FDC; i++){
- + 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... */
- + }
- + 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( timer_active & (1 << FLOPPY_TIMER)){
- + printk("timer_table=%p\n",timer_table[FLOPPY_TIMER].fn);
- + printk("expires=%ld\n",timer_table[FLOPPY_TIMER].expires);
- + 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)
- +{
- + CLEAR_INTR;
- + floppy_tq.routine = (void (*)(void *)) empty;
- + del_timer( &fd_timer);
- + disable_dma(FLOPPY_DMA);
- + /* avoid dma going to a random drive after shutdown */
- + if(!initialising)
- + DPRINT("floppy timeout\n");
- + FDCS->reset = 1;
- + cont->done(0);
- + cont->redo(); /* this will recall reset when needed */
- +}
- +
- +/* start motor, check media-changed condition and write protection */
- +static void start_motor(void)
- +{
- + int mask, data;
- +
- + mask = 0xfc;
- + data = UNIT(current_drive);
- +
- + if ( (FDCS->dor & 0x03) != UNIT(current_drive) ||
- + !(FDCS->dor & ( 0x10 << UNIT(current_drive) ) ))
- + /* notes select time if floppy is not yet selected */
- + DRS->select_date = jiffies;
- +
- + 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);
- + if( raw_cmd.flags & FD_RAW_NO_MOTOR)
- + return;
- +
- + disk_change(current_drive);
- +
- + return;
- +}
- +
- +static void floppy_ready(void)
- +{
- + CHECK_RESET;
- + start_motor();
- +
- + /* wait_for_completion also schedules reset if needed. */
- + if(wait_for_completion(current_drive,
- + DRS->select_date+DP->select_delay,
- + (timeout_fn) floppy_ready))
- + return;
- + fdc_dtr();
- + if ( raw_cmd.flags & FD_RAW_NEED_SEEK ){
- + perpendicular_mode();
- + fdc_specify(); /* must be done here because of hut, hlt ... */
- + seek_floppy();
- + } else
- + setup_rw_floppy();
- +}
- +
- +static void floppy_start(void)
- +{
- + timer_table[FLOPPY_TIMER].expires = jiffies + DP->timeout;
- + timer_active |= 1 << FLOPPY_TIMER;
- + scandrives();
- + 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)
- +{
- + timer_active &= ~(1 << FLOPPY_TIMER);
- + 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)
- + if (current->pid)
- + interruptible_sleep_on(&command_done);
- + else {
- + sti();
- + run_task_queue(&tq_timer);
- + cli();
- + }
- + if(command_status < 2){
- + sti();
- + floppy_shutdown();
- + redo_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)
- +{
- + generic_done(1);
- +}
- +
- +static void generic_failure(void)
- +{
- + generic_done(0);
- +}
- +
- +static void success_and_wakeup(void)
- +{
- + generic_success();
- + do_wakeup();
- +}
- +
- +static void failure_and_wakeup(void)
- +{
- + generic_failure();
- + do_wakeup();
- +}
- +
- +/*
- + * formatting and rw support.
- + * ==========================
- + */
- +
- +static int next_valid_format(void)
- +{
- + int probed_format;
- + while(1){
- + probed_format = DRS->probed_format;
- + 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(int 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(void)
- +{
- + 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.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) + ( format_req.head << 2 );
- + F_SIZECODE = FD_SIZECODE(floppy);
- + F_SECT_PER_TRACK = floppy->sect << 2 >> F_SIZECODE;
- + F_GAP = floppy->fmt_gap;
- + F_FILL = FD_FILL_BYTE;
- +
- + current_addr = 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 + 1;
- +
- + /* 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)
- +{
- + raw_cmd.track = format_req.track << floppy->stretch;
- + buffer_track = -1;
- + setup_format_params();
- + clear_bit(current_drive, &changed_floppies);
- + 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(int device, struct format_descr *tmp_format_req)
- +{
- + int okay;
- +
- + LOCK_FDC(DRIVE(device),1);
- + set_floppy(device);
- + if (!floppy ||
- + tmp_format_req->track >= floppy->track ||
- + tmp_format_req->head >= floppy->head){
- + redo_fd_request();
- + return -EINVAL;
- + }
- + format_req = *tmp_format_req;
- + format_errors = 0;
- + cont = &format_cont;
- + errors = &format_errors;
- + CALL(okay=wait_til_done(redo_format,1));
- + redo_fd_request();
- + return okay;
- +}
- +
- +/*
- + * 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;
- + timer_active &= ~(1 << FLOPPY_TIMER);
- +
- + 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 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[DRIVE(current_drive) + (FDC(current_drive) << 7)] =
- + 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[DRIVE(current_drive) + (FDC(current_drive) << 7)] =
- + floppy->size >> 1;
- + probing = 0;
- + }
- +
- + if ( CT(COMMAND) != FD_READ || current_addr == 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 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 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",
- + (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 ( ((int)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 SANITY
- + if ( !bh){
- + DPRINT("bh=null in copy buffer after copy\n");
- + break;
- + }
- +#endif
- + size = bh->b_size;
- + buffer = bh->b_data;
- + }
- +#ifdef 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;
- +
- + current_drive = DRIVE(CURRENT->dev);
- +
- + 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 ( (DRS->flags & 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 << floppy->stretch;
- + DR_SELECT = UNIT(current_drive) + ( HEAD << 2 );
- + 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 ((long)CURRENT->buffer <= LAST_DMA_ADDR ) {
- + 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 > ( LAST_DMA_ADDR - ((long) CURRENT->buffer))>>9)
- + max_size=(LAST_DMA_ADDR - ((long)CURRENT->buffer))>>9;
- + /* 64 kb boundaries */
- + if ( ((max_size << 9) + ((long) CURRENT->buffer)) / K_64 !=
- + ((long) CURRENT->buffer ) / K_64 )
- + 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 ((indirect - sector_t) * 2 > (direct - sector_t) * 3 &&
- + *errors < DP->max_errors.read_track &&
- + /*!(DRS->flags & FD_NEED_TWADDLE) &&*/
- + ( ( !probing || (DP->read_track &
- + (1 <<DRS->probed_format))))){
- + max_size = CURRENT->nr_sectors;
- + } else {
- + current_addr = CURRENT->buffer;
- + raw_cmd.length = current_count_sectors << 9;
- + 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_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;
- + }
- + current_addr = 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 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 SANITY
- + if ((raw_cmd.length < current_count_sectors << 9) ||
- + (current_addr != 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 ( current_addr != CURRENT->buffer )
- + printk("addr=%d, length=%ld\n",
- + (current_addr - 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 (current_addr != CURRENT->buffer ){
- + if (current_addr < floppy_track_buffer ||
- + current_count_sectors < 0 ||
- + raw_cmd.length < 0 ||
- + current_addr + 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);
- + }
- +#endif
- + return 2;
- +}
- +
- +static struct cont_t rw_cont={
- + rw_interrupt,
- + redo_fd_request,
- + bad_flp_intr,
- + request_done };
- +
- +static void redo_fd_request(void)
- +{
- +#define REPEAT {request_done(0); continue; }
- + int device;
- + int tmp;
- +
- + if (current_drive < N_DRIVE)
- + floppy_off(current_drive);
- +
- + if (CURRENT && CURRENT->dev < 0) return;
- +
- + cont = &rw_cont;
- + while(1){
- + if (!CURRENT) {
- + CLEAR_INTR;
- + unlock_fdc();
- + return;
- + }
- + if (MAJOR(CURRENT->dev) != MAJOR_NR)
- + panic(DEVICE_NAME ": request list destroyed");
- + if (CURRENT->bh && !CURRENT->bh->b_lock)
- + panic(DEVICE_NAME ": block not locked");
- +
- + device = CURRENT->dev;
- + set_fdc( DRIVE(device));
- +
- + timer_table[FLOPPY_TIMER].expires = jiffies + DP->timeout;
- + timer_active |= 1 << FLOPPY_TIMER;
- + raw_cmd.flags=0;
- + start_motor();
- + if(test_bit( DRIVE(device), &fake_change) ||
- + test_bit( DRIVE(device), &changed_floppies)){
- + DPRINT("disk absent or changed during operation\n");
- + REPEAT;
- + }
- + set_floppy(device);
- + 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;
- + }
- +
- + floppy_tq.routine = (void (*)(void *)) floppy_start;
- + queue_task(&floppy_tq, &tq_timer);
- +#ifdef DEBUGT
- + debugt("queue fd request");
- +#endif
- + return;
- + }
- +#undef REPEAT
- +}
- +
- +void do_fd_request(void)
- +{
- + if (fdc_busy)
- + /* fdc busy, this new request will be treated when the
- + current one is done */
- + return;
- + /* fdc_busy cannot be set by an interrupt or a bh */
- + floppy_grab_irq_and_dma();
- + fdc_busy=1;
- + redo_fd_request();
- +}
- +
- +/*
- + * 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 result;
- +
- + result=0;
- + LOCK_FDC(drive,interruptible);
- + switch(arg){
- + case FD_RESET_ALWAYS:
- + FDCS->reset=1;
- + break;
- + case FD_RESET_IF_RAWCMD:
- + if(FDCS->rawcmd == 2 )
- + reset_fdc_info(1);
- + break;
- + }
- + if ( FDCS->reset ){
- + cont = &reset_cont;
- + timer_table[FLOPPY_TIMER].expires = jiffies + 5;
- + timer_active |= 1 << FLOPPY_TIMER;
- + CALL(result=wait_til_done(reset_fdc,interruptible));
- + }
- + if ( UDRS->track == PROVEN_ABSENT )
- + UDRS->track = NEED_2_RECAL;
- + redo_fd_request();
- + return result;
- +}
- +
- +/*
- + * 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;
- +}
- +
- +#define COPYOUT(x) (fd_copyout( (void *)param, &(x), sizeof(x)))
- +#define COPYIN(x) (memcpy_fromfs( &(x), (void *) param, sizeof(x)),0)
- +
- +static 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 struct cont_t raw_cmd_cont={
- + success_and_wakeup,
- + failure_and_wakeup,
- + generic_failure,
- + generic_done };
- +
- +static int raw_cmd_ioctl(int drive, void *param)
- +{
- + int i, count, ret;
- +
- + if ( FDCS->rawcmd <= 1 )
- + FDCS->rawcmd = 1;
- + for ( i= 0; i < N_DRIVE; i++){
- + if ( FDC(i) != fdc)
- + continue;
- + if ( i == drive ){
- + if ( drive_state[i].fd_ref > 1 ){
- + FDCS->rawcmd = 2;
- + break;
- + }
- + } else if ( drive_state[i].fd_ref ){
- + FDCS->rawcmd = 2;
- + break;
- + }
- + }
- +
- + if(FDCS->reset)
- + return -EIO;
- +
- + COPYIN(raw_cmd);
- + raw_cmd.rate &= 0x03;
- + count = raw_cmd.length;
- + if (raw_cmd.flags & (FD_RAW_WRITE | FD_RAW_READ)){
- + if(count > max_buffer_sectors * 1024 )
- + return -ENOMEM;
- + buffer_track = -1;
- + }
- + if ( raw_cmd.flags & FD_RAW_WRITE ){
- + i = verify_area(VERIFY_READ, raw_cmd.data, count );
- + if (i)
- + return i;
- + memcpy_fromfs(floppy_track_buffer, raw_cmd.data, count);
- + }
- +
- + current_addr = floppy_track_buffer;
- + cont = &raw_cmd_cont;
- + CALL(ret=wait_til_done(floppy_start,1));
- + 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(drive);
- +
- + if ( !ret && !FDCS->reset ){
- + 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);
- + } else
- + ret = -EIO;
- +
- + DRS->track = NO_TRACK;
- + if ( ret )
- + return ret;
- +
- + if ( raw_cmd.flags & FD_RAW_READ ){
- + i=fd_copyout( raw_cmd.data, floppy_track_buffer, count);
- + if (i)
- + return i;
- + }
- +
- + return COPYOUT(raw_cmd);
- +}
- +
- +static int invalidate_drive(int rdev)
- +{
- + /* invalidate the buffer track to force a reread */
- + set_bit( DRIVE(rdev), &fake_change);
- + redo_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 IOCTL_ALLOWED (filp && (filp->f_mode & IOCTL_MODE_BIT))
- +
- + struct floppy_struct newparams;
- + struct format_descr tmp_format_req;
- + int i,device,drive,type,cnt;
- + struct floppy_struct *this_floppy;
- + char *name;
- +
- + device = inode->i_rdev;
- + switch (cmd) {
- + RO_IOCTLS(device,param);
- + }
- + type = TYPE(device);
- + drive = DRIVE(device);
- + switch (cmd) {
- + 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_fs_byte(name[cnt],
- + ((char*)param)+cnt);
- + if ( ! *name )
- + break;
- + }
- + return 0;
- + case FDGETMAXERRS:
- + return COPYOUT(UDP->max_errors);
- + case FDGETPRM:
- + if (type)
- + this_floppy = &floppy_type[type];
- + else if ((this_floppy = current_type[drive]) ==
- + NULL)
- + return -ENODEV;
- + return COPYOUT(this_floppy[0]);
- + case FDPOLLDRVSTAT:
- + check_disk_change(device);
- + /* fall through */
- + case FDGETDRVSTAT:
- + return COPYOUT(*UDRS);
- + case FDGETFDCSTAT:
- + return COPYOUT(*UFDCS);
- + case FDGETDRVPRM:
- + return COPYOUT(*UDP);
- + case FDWERRORGET:
- + return COPYOUT(*UDRWE);
- + }
- +
- + if (!IOCTL_ALLOWED)
- + return -EPERM;
- +
- + switch (cmd) {
- + 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 FDRAWCMD:
- + if (type)
- + return -EINVAL;
- + LOCK_FDC(drive,1);
- + set_floppy(device);
- + CALL(i = raw_cmd_ioctl(drive, (void *) param));
- + redo_fd_request();
- + return i;
- + case FDFMTTRK:
- + if (UDRS->fd_ref != 1)
- + return -EBUSY;
- + if (UDRS->track == PROVEN_ABSENT)
- + return -ENXIO;
- + COPYIN(tmp_format_req);
- + return do_format(device, &tmp_format_req);
- + case FDSETMAXERRS:
- + return COPYIN(UDP->max_errors);
- + case FDFMTBEG:
- + return 0;
- + case FDCLRPRM:
- + LOCK_FDC(drive,1);
- + current_type[drive] = NULL;
- + floppy_sizes[drive] = 2;
- + UDRS->keep_data = 0;
- + return invalidate_drive(device);
- + case FDFMTEND:
- + case FDFLUSH:
- + LOCK_FDC(drive,1);
- + return invalidate_drive(device);
- + case FDSETPRM:
- + case FDDEFPRM:
- + COPYIN(newparams);
- + /* sanity checking for parameters.*/
- + if(newparams.sect <= 0 ||
- + newparams.head <= 0 ||
- + newparams.track <= 0 ||
- + newparams.track >
- + UDP->tracks>>newparams.stretch)
- + return -EINVAL;
- + if ( type){
- + if ( !suser() )
- + return -EPERM;
- + LOCK_FDC(-1,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]=
- +#ifdef HAVE_2_CONTROLLERS
- + floppy_sizes[cnt+0x80]=
- +#endif
- + floppy_type[type].size>>1;
- + redo_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*/
- + raw_cmd.flags = 0;
- + start_motor();
- + }
- + 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
- + redo_fd_request();
- + return 0;
- + case FDRESET:
- + return user_reset_fdc( drive, (int)param, 1);
- + case FDMSGON:
- + UDP->flags |= FTD_MSG;
- + return 0;
- + case FDMSGOFF:
- + UDP->flags &= ~FTD_MSG;
- + return 0;
- + case FDSETEMSGTRESH:
- + UDP->max_errors.reporting =
- + (unsigned short) (param & 0x0f);
- + return 0;
- + case FDTWADDLE:
- + LOCK_FDC(drive,1);
- + twaddle();
- + redo_fd_request();
- + }
- + if ( ! suser() )
- + return -EPERM;
- + switch(cmd){
- + case FDSETDRVPRM:
- + return COPYIN(*UDP);
- + default:
- + return -EINVAL;
- + }
- + return 0;
- +#undef IOCTL_ALLOWED
- +}
- +
- +static void set_base_type(int drive, int code)
- +{
- + if(code>0 && code <= NUMBER(default_drive_params)+1) {
- + memcpy((char *)UDP,
- + (char *)(&default_drive_params[code-1].params),
- + sizeof(struct floppy_drive_params));
- + printk("fd%d is %s", drive, default_drive_params[code-1].name);
- + return;
- + }
- + else if (!code)
- + printk("fd%d is not installed", drive);
- + else
- + printk("fd%d is unknown type %d", drive, code);
- +}
- +
- +static void config_types(void)
- +{
- + int drive;
- +
- + for (drive=0; drive<N_DRIVE ; drive++){
- + /* default type for unidentifiable drives */
- + memcpy((char *) UDP, (char *) (&default_drive_params->params),
- + sizeof( struct floppy_drive_params ));
- + }
- + printk("Floppy drive(s): ");
- + for(drive=0; drive<no_floppies; drive++)
- + {
- + set_base_type(drive, 1+4);
- + if(drive < no_floppies-1)
- + printk(", ");
- + }
- + printk("\n");
- +}
- +
- +int floppy_is_wp( int minor)
- +{
- + check_disk_change(minor + (MAJOR_NR << 8));
- + return ! ( drive_state[ DRIVE(minor) ].flags & FD_DISK_WRITABLE );
- +}
- +
- +
- +#define WRAPPER(op) \
- +static int floppy_##op(struct inode * inode, struct file * filp, \
- + char * buf, int count) \
- +{ \
- + check_disk_change(inode->i_rdev); \
- + if ( drive_state[DRIVE(inode->i_rdev)].track == PROVEN_ABSENT ) \
- + return -ENXIO; \
- + if ( test_bit(DRIVE(inode->i_rdev),&changed_floppies)) \
- + return -ENXIO; \
- + return block_##op(inode, filp, buf, count); \
- +}
- +
- +WRAPPER(read)
- +WRAPPER(write)
- +
- +static void floppy_release(struct inode * inode, struct file * filp)
- +{
- + int drive;
- +
- + drive = DRIVE(inode->i_rdev);
- +
- + fsync_dev(inode->i_rdev);
- +
- + 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;
- +
- + 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)) )
- + return -ENXIO;
- +
- + if (TYPE(inode->i_rdev) >= NUMBER(floppy_type))
- + return -ENXIO;
- +
- + if ((filp->f_mode & 3) &&
- + UDRS->track == PROVEN_ABSENT )
- + return -ENXIO;
- +
- + old_dev = UDRS->fd_device;
- + if (UDRS->fd_ref && old_dev != inode->i_rdev)
- + return -EBUSY;
- +
- + 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++;
- +
- + UDRS->fd_device = 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 (UFDCS->rawcmd == 1)
- + UFDCS->rawcmd = 2;
- +
- + if (filp->f_flags & O_NDELAY)
- + return 0;
- +
- + if (filp->f_mode && UDRS->track == PROVEN_ABSENT )
- + RETERR(ENXIO);
- +
- + if (user_reset_fdc(drive, FD_RESET_IF_NEEDED,0))
- + RETERR(EIO);
- +
- + if (filp->f_mode & 3) {
- + UDRS->last_checked = 0;
- + check_disk_change(inode->i_rdev);
- + if (test_bit(drive,&changed_floppies))
- + RETERR(ENXIO);
- + }
- +
- + if (filp->f_mode && UDRS->track == PROVEN_ABSENT )
- + RETERR(ENXIO);
- +
- + if ((filp->f_mode & 2) && !(UDRS->flags & 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(dev_t dev)
- +{
- + int drive = DRIVE( dev );
- +
- + if (MAJOR(dev) != MAJOR_NR) {
- + DPRINT("floppy_changed: not a floppy\n");
- + return 0;
- + }
- +
- + if(test_bit(drive, &changed_floppies))
- + return 1;
- +
- + if(UDRS->last_checked + UDP->checkfreq < jiffies){
- + lock_fdc(drive,0);
- + start_motor();
- + redo_fd_request();
- + }
- +
- + if(test_bit(drive, &changed_floppies))
- + return 1;
- + if(test_bit(drive, &fake_change))
- + return 1;
- + return 0;
- +}
- +
- +static struct cont_t poll_cont={
- + success_and_wakeup,
- + floppy_ready,
- + generic_failure,
- + generic_done };
- +
- +
- +/* revalidate the floppy disk, i.e. trigger format autodetection by reading
- + * the bootblock (block 0). "Autodetection" is also needed to check wether
- + * there is a disk in the drive at all... Thus we also do it for fixed
- + * geometry formats */
- +static int floppy_revalidate(dev_t dev)
- +{
- + struct buffer_head * bh;
- + int drive=DRIVE(dev);
- + int cf;
- +
- + cf = test_bit(drive, &changed_floppies);
- + if(cf || test_bit(drive, &fake_change)){
- + lock_fdc(drive,0);
- + cf = test_bit(drive, &changed_floppies);
- + if(! (cf || test_bit(drive, &fake_change))){
- + redo_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);
- + clear_bit(drive, &changed_floppies);
- + if(cf){
- + UDRS->generation++;
- + if(!current_type[drive] && !TYPE(dev)){
- + /* auto-sensing */
- + if (!(bh = getblk(dev,0,1024))){
- + redo_fd_request();
- + return 1;
- + }
- + if ( bh && ! bh->b_uptodate)
- + ll_rw_block(READ, 1, &bh);
- + redo_fd_request();
- + wait_on_buffer(bh);
- + brelse(bh);
- + return 0;
- + } else {
- + /* no auto-sense, just clear dcl */
- + raw_cmd.flags=FD_RAW_NEED_SEEK|FD_RAW_NEED_DISK;
- + raw_cmd.track=0;
- + raw_cmd.cmd_count=0;
- + cont = &poll_cont;
- + wait_til_done(floppy_ready,0);
- + }
- + }
- + redo_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 */
- +} /* fdc_init */
- +
- +void floppy_init(void)
- +{
- + int i;
- +
- + sti();
- +
- + if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
- + printk("Unable to get major %d for floppy\n",MAJOR_NR);
- + return;
- + }
- +
- + 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;
- + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- + hardsect_size[MAJOR_NR] = fd_sectsizes;
- + timer_table[FLOPPY_TIMER].fn = floppy_shutdown;
- + timer_active &= ~(1 << FLOPPY_TIMER);
- + config_types();
- +
- + for(i = 0; i<256; i++)
- + fd_sectsizes[i] = 1024; /* patch for msdos driver */
- +
- + fdc_state[0].address = 0x3f0;
- +#if N_FDC > 1
- + fdc_state[1].address = 0x370;
- +#endif
- + for (i = 0 ; i < N_FDC ; i++) {
- + fdc = i;
- + FDCS->dtr = -1;
- + FDCS->dor = 0;
- + FDCS->reset = 0;
- + FDCS->version = FDC_NONE;
- + set_dor(fdc, ~0, 0xc );
- + }
- +
- + /* initialise drive state */
- + for (i = 0; i < N_DRIVE ; i++) {
- + current_drive = i;
- + DRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE;
- + DRS->generation = 0;
- + DRS->keep_data = 0;
- + DRS->fd_ref = 0;
- + DRS->fd_device = 0;
- + DRWE->write_errors = 0;
- + DRWE->first_error_sector = 0;
- + DRWE->first_error_generation = 0;
- + DRWE->last_error_sector = 0;
- + DRWE->last_error_generation = 0;
- + DRWE->badness = 0;
- + }
- +
- + floppy_grab_irq_and_dma();
- + for (i = 0 ; i < N_FDC ; i++) {
- + fdc = i;
- + FDCS->rawcmd = 2;
- + if(user_reset_fdc(-1,FD_RESET_IF_NEEDED,0))
- + continue;
- + /* Try to determine the floppy controller type */
- + FDCS->version = get_fdc_version();
- + if (FDCS->version == FDC_NONE)
- + continue;
- +
- + /* 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;
- + current_drive = 0;
- + floppy_release_irq_and_dma();
- + initialising=0;
- +}
- +
- +static int floppy_grab_irq_and_dma(void)
- +{
- + int i;
- + cli();
- + if (usage_count++){
- + sti();
- + return 0;
- + }
- + sti();
- +
- + for(i=0; i< N_FDC; i++){
- + 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, "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;
- + }
- + enable_irq(FLOPPY_IRQ);
- + return 0;
- +}
- +
- +static void floppy_release_irq_and_dma(void)
- +{
- + int i;
- + cli();
- + if (--usage_count){
- + sti();
- + return;
- + }
- + sti();
- + disable_dma(FLOPPY_DMA);
- + free_dma(FLOPPY_DMA);
- + disable_irq(FLOPPY_IRQ);
- + free_irq(FLOPPY_IRQ);
- + /* switch off dma gates */
- + for(i=0; i< N_FDC; i++)
- + set_dor(i, ~8, 0);
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/block/genhd.c linux.arm/arch/arm/drivers/block/genhd.c
- --- linux.orig/arch/arm/drivers/block/genhd.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/genhd.c Fri Oct 27 23:14:43 1995
- @@ -0,0 +1,295 @@
- +/*
- + * 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
- + */
- +
- +#include <linux/config.h>
- +#include <linux/fs.h>
- +#include <linux/genhd.h>
- +#include <linux/kernel.h>
- +
- +#ifndef __ARM__
- +#define __ARM__
- +#endif
- +
- +#ifndef __ARM__
- +struct gendisk *gendisk_head = NULL;
- +#else
- +struct gendisk *gendisk_head;
- +#endif
- +
- +static int current_minor;
- +extern int *blk_size[];
- +extern void rd_load(void);
- +extern int ramdisk_size;
- +
- +#ifndef __ARM__
- +/*
- + * 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, int dev)
- +{
- + struct buffer_head *bh;
- + struct partition *p;
- + unsigned long first_sector, this_sector;
- + int mask = (1 << hd->minor_shift) - 1;
- +
- + first_sector = hd->part[MINOR(dev)].start_sect;
- + this_sector = first_sector;
- +
- + while (1) {
- + if ((current_minor & mask) >= (4 + hd->max_p))
- + 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) {
- + p = (struct partition *) (0x1BE + bh->b_data);
- + /*
- + * Process the first entry, which should be the real
- + * data partition.
- + */
- + if (p->sys_ind == EXTENDED_PARTITION ||
- + !(hd->part[current_minor].nr_sects = p->nr_sects))
- + goto done; /* shouldn't happen */
- + hd->part[current_minor].start_sect = this_sector + p->start_sect;
- + printk(" %s%c%d", hd->major_name,
- + 'a'+(current_minor >> hd->minor_shift),
- + mask & current_minor);
- + current_minor++;
- + p++;
- + /*
- + * Process the second entry, which 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 real
- + * data partition.
- + */
- + if (p->sys_ind != EXTENDED_PARTITION ||
- + !(hd->part[current_minor].nr_sects = p->nr_sects))
- + goto done; /* no more logicals in this partition */
- + hd->part[current_minor].start_sect = first_sector + p->start_sect;
- + this_sector = first_sector + p->start_sect;
- + dev = ((hd->major) << 8) | current_minor;
- + brelse(bh);
- + } else
- + goto done;
- + }
- +done:
- + brelse(bh);
- +}
- +
- +static void check_partition(struct gendisk *hd, unsigned int dev)
- +{
- + static int first_time = 1;
- + int i, minor = current_minor;
- + struct buffer_head *bh;
- + struct partition *p;
- + unsigned long first_sector;
- + int mask = (1 << hd->minor_shift) - 1;
- +
- + if (first_time)
- + printk("Partition check:\n");
- + first_time = 0;
- + first_sector = hd->part[MINOR(dev)].start_sect;
- + if (!(bh = bread(dev,0,1024))) {
- + printk(" unable to read partition table of device %04x\n",dev);
- + return;
- + }
- + printk(" %s%c:", hd->major_name, 'a'+(minor >> hd->minor_shift));
- + current_minor += 4; /* first "extra" minor */
- + if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
- + p = (struct partition *) (0x1BE + bh->b_data);
- + for (i=1 ; i<=4 ; minor++,i++,p++) {
- + if (!(hd->part[minor].nr_sects = p->nr_sects))
- + continue;
- + hd->part[minor].start_sect = first_sector + p->start_sect;
- + printk(" %s%c%d", hd->major_name,'a'+(minor >> hd->minor_shift), i);
- + if ((current_minor & 0x3f) >= 60)
- + continue;
- + if (p->sys_ind == EXTENDED_PARTITION) {
- + printk(" <");
- + extended_partition(hd, (hd->major << 8) | minor);
- + printk(" >");
- + }
- + }
- + /*
- + * check for Disk Manager partition table
- + */
- + if (*(unsigned short *) (bh->b_data+0xfc) == 0x55AA) {
- + p = (struct partition *) (0x1BE + bh->b_data);
- + for (i = 4 ; i < 16 ; i++, current_minor++) {
- + p--;
- + if ((current_minor & mask) >= mask-2)
- + break;
- + if (!(p->start_sect && p->nr_sects))
- + continue;
- + hd->part[current_minor].start_sect = p->start_sect;
- + hd->part[current_minor].nr_sects = p->nr_sects;
- + printk(" %s%c%d", hd->major_name,
- + 'a'+(current_minor >> hd->minor_shift),
- + current_minor & mask);
- + }
- + }
- + } else
- + printk(" bad partition table");
- + printk("\n");
- + brelse(bh);
- +}
- +#else
- +/* 'ARC hard drives' minor allocation:
- + * physical hd = minor >> hd->minor_shift
- + * image file = (minor-1) & 3
- + * extra file = image file + n<<2
- + */
- +
- +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);
- + printk(" %s%c%d", hd->major_name,'a'+(minor >> hd->minor_shift), minor);
- + }
- + printk(" >");
- + }
- +#undef GET_WORD
- +}
- +
- +extern int image_allocate_list(int dev,int minor,struct hd_struct *);
- +extern void image_release_dev(int dev);
- +
- +void check_imagefiles(struct gendisk *hd, int real_dev)
- +{
- + static int first_time = 1;
- + int minor = current_minor;
- + int i,p = 0;
- +
- + if(first_time)
- + printk("Image file check:\n");
- + first_time = 0;
- +
- + current_minor += 4; /* first "extra" minor */
- +
- + for (i=1 ; i<=4 ; minor++,i++) {
- + int len;
- + len = image_allocate_list(real_dev, minor, &hd->part[minor]);
- + if(len && !p)
- + {
- + printk(" %s%c:", hd->major_name, 'a'+(minor >> hd->minor_shift));
- + p = 1;
- + }
- + if(len)
- + {
- + printk(" %s%c%d", hd->major_name,'a'+(minor >> hd->minor_shift), i);
- +/* Now scan it for pc partitions */
- + pc_check_hd(hd, minor);
- + }
- + }
- + image_release_dev(real_dev);
- + printk("\n");
- +}
- +#endif
- +/* 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 start = drive<<dev->minor_shift;
- + int j = start + dev->max_p;
- + int major = dev->major << 8;
- +
- + current_minor = 1+(drive<<dev->minor_shift);
- +#ifndef __ARM__
- + check_partition(dev, major+(drive<<dev->minor_shift));
- +#else
- + check_imagefiles(dev, major+(drive<<dev->minor_shift));
- +#endif
- +
- + for (i=start ; i < j ; i++)
- + dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
- +}
- +
- +static void setup_dev(struct gendisk *dev)
- +{
- + int i;
- + int j = dev->max_nr * dev->max_p;
- + int major = dev->major << 8;
- + int drive;
- +
- +
- + for (i = 0 ; i < j; i++) {
- + dev->part[i].start_sect = 0;
- + dev->part[i].nr_sects = 0;
- + }
- + dev->init();
- + for (drive=0 ; drive<dev->nr_real ; drive++) {
- + current_minor = 1+(drive<<dev->minor_shift);
- +#ifndef __ARM__
- + check_partition(dev, major+(drive<<dev->minor_shift));
- +#else
- + check_imagefiles(dev, major+(drive<<dev->minor_shift));
- +#endif
- + }
- + for (i=0 ; i < j ; i++)
- + dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
- + blk_size[dev->major] = dev->sizes;
- +}
- +
- +void device_setup(void)
- +{
- + struct gendisk *p;
- + int nr=0;
- +
- + for (p = gendisk_head ; p ; p=p->next) {
- + setup_dev(p);
- + nr += p->nr_real;
- + }
- +
- + if (ramdisk_size)
- + rd_load();
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/block/hd.c linux.arm/arch/arm/drivers/block/hd.c
- --- linux.orig/arch/arm/drivers/block/hd.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/hd.c Fri Oct 27 23:14:44 1995
- @@ -0,0 +1,1231 @@
- +/*
- + * linux/arch/arm/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).
- + */
- +
- +/* 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>
- +
- +#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);
- +int issue_request(int dev, unsigned int block, unsigned int nsect, struct request *current);
- +#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->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->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->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->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->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.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.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->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->dev < 0) return;
- + if (DEVICE_INTR)
- + return;
- +repeat:
- + timer_active &= ~(1<<HD_TIMER);
- + sti();
- + INIT_REQUEST;
- + if (reset) {
- + cli();
- + reset_hd();
- + return;
- + }
- + dev = MINOR(CURRENT->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->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->dev,CURRENT->cmd))
- + goto repeat;
- + frag_count = image_file_map(CURRENT->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)
- +{
- + unsigned int dev, block, nsect;
- +
- + dev = MINOR(current->dev);
- + frag_pos++;
- + if(frag_pos >= MAX_FRAGS)
- + {
- + block = current->sector;
- + block += hd[dev].start_sect;
- + nsect = current->nr_sectors;
- + if(!image_file_check(current->dev,current->cmd))
- + {
- + end_request(0);
- + hd_request();
- + return;
- + }
- + frag_count = image_file_map(current->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))
- + {
- + 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)
- +{
- + 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->cmd == READ)?"read":"writ",
- + cyl, head, sec, nsect, (unsigned long) current->buffer,current);
- +#endif
- + frag_sectors = nsect;
- + if (!unmask_intr[dev])
- + cli();
- + if (current->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->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->buffer,256);
- +if(d == 1 && block < 10) while(1);
- +#else
- +if(d == 1)
- + printk("write: buffer = %p (%08lX)\n", current->buffer, *(unsigned long *)current->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(void);
- +
- +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.
- + */
- +int no_hds;
- +static void hd_geninit(void)
- +{
- + 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 */
- +};
- +
- +unsigned long hd_init(unsigned long mem_start, unsigned long mem_end)
- +{
- + if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
- + printk("hd: unable to get major %d for harddisk\n",MAJOR_NR);
- + return mem_start;
- + }
- + 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 mem_start;
- +}
- +
- +#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 -r -u -N linux.orig/arch/arm/drivers/block/hd.diff linux.arm/arch/arm/drivers/block/hd.diff
- --- linux.orig/arch/arm/drivers/block/hd.diff Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/hd.diff Fri Oct 27 23:14:52 1995
- @@ -0,0 +1,2672 @@
- +--- /usr/src/linux/arch/arm/drivers/block/floppy.c Thu Aug 31 22:19:59 1995
- ++++ /usr/src/linux/drivers/block/floppy.c Sun Sep 24 10:23:30 1995
- +@@ -4,32 +4,6 @@
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + * Copyright (C) 1993, 1994 Alain Knaff
- + */
- +-
- +-/* Configuration */
- +-/* The following does some extra sanity checks */
- +-#define SANITY
- +-
- +-/* 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. */
- +-#define ALLOWED_DRIVE_MASK 0x0F
- +-
- +-/* Undefine the following if you have to floppy disk controllers:
- +- * This works at least for me; if you get two controllers working, with
- +- * drives attached to both, please mail me: Alain.Knaff@imag.fr */
- +-/* #define HAVE_2_CONTROLLERS */
- +-
- +-
- +-/* Define the following if you don't like that your drives seek audibly
- +- * after a disk change (but it may not work correctly for everybody)
- +- */
- +-#define SILENT_DC_CLEAR
- +-
- +-
- +-/* End of configuration */
- +-
- + /*
- + * 02.12.91 - Changed to static variables to indicate need for reset
- + * and recalibrate. This makes some things easier (output_byte reset
- +@@ -103,15 +77,39 @@
- + * format bug fixes, but unfortunately some new bugs too...
- + */
- +
- +-/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
- ++/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
- + * errors to allow safe writing by specialized programs.
- + */
- +-int no_floppies;
- +-#define FLOPPY_IRQ 12
- +-#define FLOPPY_DMA 2
- ++
- ++#define CONFIG_FLOPPY_SANITY
- ++#undef CONFIG_FLOPPY_SILENT_DCL_CLEAR
- ++
- ++#define REALLY_SLOW_IO
- ++
- + #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;
- ++
- ++#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 6
- ++#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>
- +@@ -125,6 +123,7 @@
- + #include <linux/string.h>
- + #include <linux/fcntl.h>
- + #include <linux/delay.h>
- ++#include <linux/mc146818rtc.h> /* CMOS defines */
- +
- + #include <asm/dma.h>
- + #include <asm/irq.h>
- +@@ -135,16 +134,24 @@
- + #define MAJOR_NR FLOPPY_MAJOR
- + #include "blk.h"
- +
- +-static unsigned int changed_floppies = 0xff, fake_change = 0;
- ++static unsigned int fake_change = 0;
- + static int initialising=1;
- +
- +-#ifdef HAVE_2_CONTROLLERS
- ++#define FLOPPY0_TYPE ((CMOS_READ(0x10) >> 4) & 15)
- ++#define FLOPPY1_TYPE (CMOS_READ(0x10) & 15)
- ++
- ++/*
- ++ * Again, the CMOS information doesn't work on the alpha..
- ++ */
- ++#ifdef __alpha__
- ++#undef FLOPPY0_TYPE
- ++#undef FLOPPY1_TYPE
- ++#define FLOPPY0_TYPE 6
- ++#define FLOPPY1_TYPE 0
- ++#endif
- ++
- + #define N_FDC 2
- + #define N_DRIVE 8
- +-#else
- +-#define N_FDC 1
- +-#define N_DRIVE 4
- +-#endif
- +
- + #define TYPE(x) ( ((x)>>2) & 0x1f )
- + #define DRIVE(x) ( ((x)&0x03) | (((x)&0x80 ) >> 5))
- +@@ -156,22 +163,28 @@
- + #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 DPRINT(x) printk(DEVICE_NAME "%d: " x,current_drive)
- +
- + #define DPRINT1(x,x1) \
- +-printk(DEVICE_NAME "%d: " x,current_drive,(x1));
- ++printk(DEVICE_NAME "%d: " x,current_drive,(x1))
- +
- + #define DPRINT2(x,x1,x2) \
- +-printk(DEVICE_NAME "%d: " x,current_drive,(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));
- ++printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2),(x3))
- +
- + /* read/write */
- + #define COMMAND raw_cmd.cmd[0]
- +@@ -195,8 +208,9 @@
- + /*
- + * 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 3984
- ++#define MAX_DISK_SIZE 2 /* 3984*/
- +
- +
- +
- +@@ -208,13 +222,9 @@
- + * driver otherwise. It doesn't matter much for performance anyway, as most
- + * floppy accesses go through the track buffer.
- + */
- +-#if 0
- + #define LAST_DMA_ADDR (0x1000000)
- +-#else
- +-/* Everything has to go via dma buffer */
- +-#define LAST_DMA_ADDR (0x1)
- +-#endif
- + #define K_64 (0x10000) /* 64 k */
- ++
- + /*
- + * globals used by 'result()'
- + */
- +@@ -230,6 +240,9 @@
- + #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.
- + */
- +@@ -243,49 +256,44 @@
- + | | 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, 100, 300, 0, 2, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0,
- +- 0, { 7, 4, 8, 2, 1, 5, 3,10}, 150, 0 }, "unknown" },
- +-
- +-{{1, 300, 16, 16, 8000, 100, 300, 0, 2, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0,
- +- 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 150, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
- +-
- +-{{2, 500, 16, 16, 6000, 40, 300, 14, 2, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0,
- +- 0, { 2, 5, 6,23,10,20,11, 0}, 150, 2 }, "1.2M" }, /*5 1/4 HD AT*/
- +-
- +-{{3, 250, 16, 16, 3000, 100, 300, 0, 2, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
- +- 0, { 4,22,21,30, 3, 0, 0, 0}, 150, 4 }, "720k" }, /*3 1/2 DD*/
- +-
- +-#if 0
- +-{{4, 500, 16, 16, 4000, 40, 300, 10, 2, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
- +- 0, { 7, 4,25,22,31,21,29,11}, 150, 7 }, "1.44M" }, /*3 1/2 HD*/
- +-#else
- +-{{4, 500, 16, 16, 3000, 40, 300, 10, 2, 5, 83, 3*HZ, 20, {3,1,2,0,0}, FTD_MSG,
- +- 0, { 7, 4,25,22,31,21,29,11}, 150, 7 }, "1.44M" }, /*3 1/2 HD*/
- +-#endif
- +-
- +-{{5, 1000, 15, 8, 3000, 40, 300, 10, 2, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
- +- 0, { 7, 8, 4,25,28,22,31,21}, 150, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
- +-
- +-{{6, 1000, 15, 8, 3000, 40, 300, 10, 2, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
- +- 0, { 7, 8, 4,25,28,22,31,21}, 150, 8 }, "2.88M" } /*3 1/2 ED*/
- +-/* | ---autodetected formats-- | | |
- +- read_track | | Name printed when booting
- +- | Native format
- ++ | | | | | 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*/
- ++
- ++{{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*/
- ++
- ++{{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 volatile drive_state[N_DRIVE];
- +-static struct floppy_write_errors volatile write_errors[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;
- +
- + /*
- +@@ -295,16 +303,6 @@
- + * types (ie 360kB diskette in 1.2MB drive etc). Others should
- + * be self-explanatory.
- + */
- +-/*
- +- 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 */
- +@@ -348,11 +346,8 @@
- +
- + /* 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
- +-#ifdef HAVE_2_CONTROLLERS
- +- ,
- +- NULL, NULL, NULL, NULL
- +-#endif
- + };
- +
- + /*
- +@@ -362,6 +357,7 @@
- + 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
- +@@ -380,6 +376,9 @@
- + 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 _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;
- +@@ -400,11 +399,7 @@
- + * corrupted/lost. Alignment of these is enforced in boot/head.S.
- + * Note that you must not change the sizes below without updating head.S.
- + */
- +-#if 0
- + extern char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS];
- +-#else
- +-char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS];
- +-#endif
- + #define max_buffer_sectors MAX_BUFFER_SECTORS
- +
- + int *errors;
- +@@ -417,10 +412,10 @@
- + done_f done; /* this is called to say if the operation has succeeded/failed */
- + } *cont;
- +
- ++static void floppy_ready(void);
- + static void floppy_start(void);
- +-static void redo_fd_request(void);
- ++static void process_fd_request(void);
- + static void recalibrate_floppy(void);
- +-static void seek_floppy(void);
- + static void floppy_shutdown(void);
- +
- + static int floppy_grab_irq_and_dma(void);
- +@@ -444,7 +439,10 @@
- + #define NO_TRACK -1
- + #define NEED_1_RECAL -2
- + #define NEED_2_RECAL -3
- +-#define PROVEN_ABSENT -4
- ++
- ++/* */
- ++static int usage_count = 0;
- ++
- +
- + /* buffer related variables */
- + static int buffer_track = -1;
- +@@ -454,10 +452,10 @@
- +
- + /* fdc related variables, should end up in a struct */
- + static struct floppy_fdc_state fdc_state[N_FDC];
- +-int fdc; /* current fdc */
- ++static int fdc; /* current fdc */
- +
- + static struct floppy_struct * floppy = floppy_type;
- +-static unsigned char current_drive = 255;
- ++static unsigned char current_drive = 0;
- + static long current_count_sectors = 0;
- + static char *current_addr = 0;
- + static unsigned char sector_t; /* sector in track */
- +@@ -467,20 +465,6 @@
- + #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.
- +- */
- +-
- +-unsigned char floppy_selects1[]={ 0x10, 0x21, 0x23, 0x33 };
- +-unsigned char floppy_selects2[]={ 0 , 0 , 0 , 0 };
- +-
- +-int fd_sectsizes[256];
- +-
- +-/*
- + * Debugging
- + * =========
- + */
- +@@ -509,14 +493,17 @@
- +
- + /*
- + * disk change.
- +- * This routine is responsible for maintaining the changed_floppies flag,
- ++ * 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'
- +- * changed_floppies is set under two conditions:
- ++ * 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.
- ++ * 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
- +@@ -531,74 +518,102 @@
- +
- + static int disk_change(int drive)
- + {
- +- if(jiffies < DP->select_delay + DRS->select_date)
- +- udelay(20000);
- +-
- +- if(inb_p(FD_DIR) & 0x80){
- +- UDRS->flags |= FD_VERIFY; /* verify write protection */
- ++ 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
- +
- +- if(UDRS->maxblock || /* disk change check */
- +- !(UDRS->flags & FD_DISK_NEWCHANGE)){/* disk presence check */
- +- /* mark it changed or absent */
- +- set_bit(drive,&changed_floppies);
- ++#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 ((DP->flags & FTD_MSG) &&
- ++ if ((UDP->flags & FTD_MSG) &&
- + current_type[drive] != NULL)
- + DPRINT("Disk type is undefined after "
- + "disk change\n");
- + current_type[drive] = NULL;
- +- floppy_sizes[drive] = MAX_DISK_SIZE;
- ++ floppy_sizes[DRIVE(current_drive) + (FDC(current_drive) << 7)] = MAX_DISK_SIZE;
- + }
- + }
- +- UDRS->flags |= FD_DISK_NEWCHANGE;
- ++ /*USETF(FD_DISK_NEWCHANGE);*/
- + return 1;
- + } else {
- + UDRS->last_checked=jiffies;
- +- UDRS->flags &= ~FD_DISK_NEWCHANGE;
- +- return 0;
- ++ UCLEARF(FD_DISK_NEWCHANGE);
- + }
- ++ return 0;
- + }
- +
- +-static void arm_set_dor(int dor)
- ++static inline int is_selected(int dor, int unit)
- + {
- +- if(dor & 0xf0)
- +- outb_p((dor & 0x0c) | floppy_selects1[dor & 3], FD_DOR);
- +- else
- +- outb_p((dor & 0x0c), FD_DOR);
- ++ return ( (dor & (0x10 << unit)) && (dor &3) == unit);
- + }
- +
- +-static int locked=0;
- + static int set_dor(int fdc, char mask, char data)
- + {
- + register unsigned char drive, unit, newdor,olddor;
- +
- +- locked=1;
- ++ if(FDCS->address == -1)
- ++ return -1;
- ++
- + olddor = FDCS->dor;
- + newdor = (olddor & mask) | data;
- + if ( newdor != olddor ){
- + unit = olddor & 0x3;
- +- drive = REVDRIVE(fdc,unit);
- +- if ( olddor & ( 0x10 << unit ))
- ++ 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);
- ++ outb_p(newdor, FD_DOR);
- ++
- ++ unit = newdor & 0x3;
- ++ if(!is_selected(olddor, unit) && is_selected(newdor,unit)){
- ++ drive = REVDRIVE(fdc,unit);
- ++ UDRS->select_date = jiffies;
- ++ }
- + }
- +- locked=0;
- ++ if ( newdor & 0xf0 )
- ++ floppy_grab_irq_and_dma();
- ++ if( olddor & 0xf0 )
- ++ floppy_release_irq_and_dma();
- + return olddor;
- + }
- +
- + static void twaddle(void)
- + {
- +- cli();
- +-#if 0
- ++ if (DP->select_delay)
- ++ return;
- + outb_p(FDCS->dor & ~(0x10<<UNIT(current_drive)),FD_DOR);
- + outb_p(FDCS->dor, FD_DOR);
- +-#else
- +- arm_set_dor(FDCS->dor);
- +-#endif
- +- sti();
- ++ DRS->select_date = jiffies;
- + }
- +
- + /* reset all driver information about the current fdc. This is needed after
- +@@ -613,7 +628,6 @@
- + FDCS->rawcmd = 0;
- + for ( drive = 0; drive < N_DRIVE; drive++)
- + if (FDC(drive) == fdc &&
- +- UDRS->track != PROVEN_ABSENT &&
- + ( mode || UDRS->track != NEED_1_RECAL))
- + UDRS->track = NEED_2_RECAL;
- + }
- +@@ -621,30 +635,28 @@
- + /* selects the fdc and drive, and enables the fdc's input/dma. */
- + static void set_fdc(int drive)
- + {
- +- if ( drive >= 0 ){
- ++ if (drive >= 0 && drive < N_DRIVE){
- + fdc = FDC(drive);
- + current_drive = drive;
- + }
- + set_dor(fdc,~0,8);
- +-#ifdef HAVE_2_CONTROLLERS
- + set_dor(1-fdc, ~8, 0);
- +-#endif
- + if ( FDCS->rawcmd == 2 )
- + reset_fdc_info(1);
- + if( inb_p(FD_STATUS) != STATUS_READY )
- + FDCS->reset = 1;
- + }
- +
- +-static int usage_count = 0;
- + /* 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();
- ++ if (!current->pid)
- ++ run_task_queue(&tq_timer);
- + cli();
- + while (fdc_busy && NO_SIGNAL)
- + interruptible_sleep_on(&fdc_wait);
- +@@ -662,6 +674,10 @@
- + #define LOCK_FDC(drive,interruptible) \
- + if(lock_fdc(drive,interruptible)) return -EINTR;
- +
- ++typedef void (*timeout_fn)(unsigned long);
- ++static struct timer_list fd_timeout ={ NULL, NULL, 0, 0,
- ++ (timeout_fn) floppy_shutdown };
- ++
- + /* unlocks the driver */
- + static inline void unlock_fdc(void)
- + {
- +@@ -672,7 +688,7 @@
- + DPRINT1("device interrupt still active at FDC release: %p!\n",
- + DEVICE_INTR);
- + command_status = FD_COMMAND_NONE;
- +- timer_active &= ~(1 << FLOPPY_TIMER);
- ++ del_timer(&fd_timeout);
- + fdc_busy = 0;
- + floppy_release_irq_and_dma();
- + wake_up(&fdc_wait);
- +@@ -683,47 +699,40 @@
- + {
- + unsigned char mask = ~(0x10 << UNIT(nr));
- +
- +- if(locked)
- +- floppy_off(nr);
- +- else
- +- set_dor( FDC(nr), mask, 0 );
- ++ 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 }
- +-#ifdef HAVE_2_CONTROLLERS
- +- ,
- ++ { 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 }
- +-#endif
- + };
- +
- + /* schedules motor off */
- +-static void floppy_off(unsigned int nr)
- ++static void floppy_off(unsigned int drive)
- + {
- + unsigned long volatile delta;
- +- register int fdc=FDC(nr);
- ++ register int fdc=FDC(drive);
- +
- +- if( !(FDCS->dor & ( 0x10 << UNIT(nr))))
- ++ if( !(FDCS->dor & ( 0x10 << UNIT(drive))))
- + return;
- +
- +- del_timer(motor_off_timer+nr);
- ++ del_timer(motor_off_timer+drive);
- +
- + /* make spindle stop in a position which minimizes spinup time
- + * next time */
- +- if ( drive_params[nr].rps ){
- +- delta = jiffies - drive_state[nr].first_read_date + HZ -
- +- drive_params[nr].spindown_offset;
- +- delta = (( delta * drive_params[nr].rps) % HZ ) /
- +- drive_params[nr].rps;
- +- motor_off_timer[nr].expires = drive_params[nr].spindown - delta;
- ++ 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 = UDP->spindown - delta;
- + }
- +- add_timer(motor_off_timer+nr);
- ++ add_timer(motor_off_timer+drive);
- + }
- +
- + /*
- +@@ -734,36 +743,41 @@
- + static void scandrives(void)
- + {
- + int i, drive, saved_drive;
- ++
- ++ if (DP->select_delay)
- ++ return;
- +
- +- saved_drive = current_drive % N_DRIVE;
- ++ saved_drive = current_drive;
- + for(i=0; i< N_DRIVE; i++){
- + drive = (saved_drive + i + 1 ) % N_DRIVE;
- +- if ( UDRS->fd_ref == 0 )
- ++ if ( UDRS->fd_ref == 0 || UDP->select_delay != 0)
- + continue; /* skip closed drives */
- + set_fdc(drive);
- +- if (!(FDCS->dor & (0x10 << UNIT(drive))) ||
- +- ((FDCS->dor & 0x3) != UNIT(drive)))
- +- UDRS->select_date = jiffies;
- + 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 );
- + }
- +- current_drive = saved_drive;
- ++ set_fdc(saved_drive);
- + }
- +
- +-typedef void (*timeout_fn)(unsigned long);
- + 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 {
- ++ } else {
- + del_timer(&fd_timer);
- + fd_timer.function = (timeout_fn) fd_watchdog;
- + fd_timer.expires = 10;
- +@@ -778,7 +792,7 @@
- + }
- +
- + /* waits for a delay (spinup or select) to pass */
- +-static int wait_for_completion(int nr, int delay, timeout_fn function)
- ++static int wait_for_completion(int delay, timeout_fn function)
- + {
- + if ( FDCS->reset ){
- + reset_fdc(); /* do the reset during sleep to win time
- +@@ -797,9 +811,50 @@
- + 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 SANITY
- ++#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 ((!CURRENT ||
- + CURRENT->buffer != current_addr ||
- + raw_cmd.length > 512 * CURRENT->nr_sectors) &&
- +@@ -817,7 +872,6 @@
- + FDCS->reset=1;
- + return;
- + }
- +-#if 0
- + if ((long) current_addr % 512 ){
- + printk("non aligned address: %p\n", current_addr );
- + cont->done(0);
- +@@ -832,7 +886,7 @@
- + FDCS->reset=1;
- + return;
- + }
- +-#endif
- ++
- + #endif
- + cli();
- + disable_dma(FLOPPY_DMA);
- +@@ -844,6 +898,7 @@
- + set_dma_count(FLOPPY_DMA, raw_cmd.length);
- + enable_dma(FLOPPY_DMA);
- + sti();
- ++ floppy_disable_hlt();
- + }
- +
- + /* sends a command byte to the fdc */
- +@@ -856,13 +911,13 @@
- + return -1;
- + for(counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
- + status = inb_p(FD_STATUS) &(STATUS_READY|STATUS_DIR|STATUS_DMA);
- ++ if (!(status & STATUS_READY))
- ++ continue;
- + if (status == STATUS_READY){
- + outb_p(byte,FD_DATA);
- + return 0;
- +- }
- +- if (!(status & STATUS_READY))
- +- continue;
- +- break;
- ++ } else
- ++ break;
- + }
- + FDCS->reset = 1;
- + if ( !initialising )
- +@@ -930,7 +985,7 @@
- + }
- + } else
- + perp_mode = 0;
- +-
- ++
- + if ( FDCS->perp_mode == perp_mode )
- + return;
- + if (FDCS->version >= FDC_82077_ORIG && FDCS->has_fifo) {
- +@@ -979,7 +1034,7 @@
- + /* TODO: lock this in via LOCK during initialization */
- + output_byte(FD_CONFIGURE);
- + output_byte(0);
- +- output_byte(0x1A); /* FIFO on, polling off, 10 byte threshold */
- ++ 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;
- +@@ -1042,22 +1097,23 @@
- + * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
- + * of the specify command (i.e. using the fdc_specify function).
- + */
- +-static void fdc_dtr(void)
- ++static int fdc_dtr(void)
- + {
- + /* If data rate not already set to desired value, set it. */
- + if ( raw_cmd.rate == FDCS->dtr)
- +- return;
- +-
- ++ 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.
- ++ * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
- + */
- +- udelay(5000);
- + FDCS->dtr = raw_cmd.rate;
- ++ return(wait_for_completion(jiffies+2,
- ++ (timeout_fn) floppy_ready));
- + } /* fdc_dtr */
- +
- + static void tell_sector(void)
- +@@ -1077,8 +1133,7 @@
- + 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;
- +@@ -1087,15 +1142,15 @@
- +
- + /* check IC to find cause of interrupt */
- + switch ((ST0 & ST0_INTR)>>6) {
- +- case 1: /* error occured during command execution */
- ++ case 1: /* error occurred during command execution */
- + bad = 1;
- + if (ST1 & ST1_WP) {
- + DPRINT("Drive is write protected\n");
- +- DRS->flags &= ~FD_DISK_WRITABLE;
- ++ CLEARF(FD_DISK_WRITABLE);
- + cont->done(0);
- + bad = 2;
- + } else if (ST1 & ST1_ND) {
- +- DRS->flags |= FD_NEED_TWADDLE;
- ++ SETF(FD_NEED_TWADDLE);
- + } else if (ST1 & ST1_OR) {
- + if (DP->flags & FTD_MSG )
- + DPRINT("Over/Underrun - retrying\n");
- +@@ -1128,11 +1183,8 @@
- +
- + }
- + if ( ST2 & ST2_WC || ST2 & ST2_BC)
- +-{
- +-printk("Wrong cylinder!\n");
- + /* wrong cylinder => recal */
- + DRS->track = NEED_2_RECAL;
- +-}
- + return bad;
- + case 2: /* invalid command given */
- + DPRINT("Invalid FDC command given!\n");
- +@@ -1162,7 +1214,7 @@
- + flags |= FD_RAW_INTR;
- +
- + if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)){
- +- ready_date = DRS->spinup_date + DP->spinup;
- ++ 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.
- +@@ -1174,14 +1226,14 @@
- + function = (timeout_fn) setup_rw_floppy;
- +
- + /* wait until the floppy is spinning fast enough */
- +- if (wait_for_completion(current_drive,ready_date,function))
- ++ 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);
- +
- +@@ -1204,9 +1256,7 @@
- + fd_watchdog();
- + }
- +
- +-#ifdef SILENT_DC_CLEAR
- + static int blind_seek;
- +-#endif
- +
- + /*
- + * This is the routine called after every seek (or recalibrate) interrupt
- +@@ -1217,9 +1267,6 @@
- + #ifdef DEBUGT
- + debugt("seek interrupt:");
- + #endif
- +-#ifdef SILENT_DC_CLEAR
- +- set_dor(fdc, ~0, (0x10 << UNIT(current_drive)));
- +-#endif
- + if (inr != 2 || (ST0 & 0xF8) != 0x20 ) {
- + DPRINT("seek failed\n");
- + DRS->track = NEED_2_RECAL;
- +@@ -1227,20 +1274,23 @@
- + cont->redo();
- + return;
- + }
- +- if (DRS->track >= 0 && DRS->track != ST1
- +-#ifdef SILENT_DC_CLEAR
- +- && !blind_seek
- ++ 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
- +- )
- +- DRS->flags &= ~FD_DISK_NEWCHANGE; /* effective seek */
- ++ CLEARF(FD_DISK_NEWCHANGE); /* effective seek */
- ++ DRS->select_date = jiffies;
- ++ }
- + DRS->track = ST1;
- +- DRS->select_date = jiffies;
- +- seek_floppy();
- ++ floppy_ready();
- + }
- +
- + static void check_wp(void)
- + {
- +- if (DRS->flags & FD_VERIFY) {
- ++ if (TESTF(FD_VERIFY)) {
- + /* check write protection */
- + output_byte( FD_GETSTATUS );
- + output_byte( UNIT(current_drive) );
- +@@ -1248,10 +1298,18 @@
- + FDCS->reset = 1;
- + return;
- + }
- +- DRS->flags &= ~(FD_VERIFY | FD_DISK_WRITABLE | FD_NEED_TWADDLE);
- +-
- ++ 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))
- +- DRS->flags |= FD_DISK_WRITABLE;
- ++ SETF(FD_DISK_WRITABLE);
- ++ else
- ++ CLEARF(FD_DISK_WRITABLE);
- + }
- + }
- +
- +@@ -1259,16 +1317,22 @@
- + {
- + int track;
- +
- +-#ifdef SILENT_DC_CLEAR
- + blind_seek=0;
- ++
- ++#ifdef DCL_DEBUG
- ++ if (DP->flags & FD_DEBUG){
- ++ DPRINT("calling disk change from seek\n");
- ++ }
- + #endif
- +- disk_change(current_drive);
- +- if ((raw_cmd.flags & FD_RAW_NEED_DISK) &&
- +- test_bit(current_drive,&changed_floppies)){
- ++
- ++ 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;
- +@@ -1276,7 +1340,7 @@
- + if ( DRS->track <= NEED_1_RECAL ){
- + recalibrate_floppy();
- + return;
- +- } else if ((DRS->flags & FD_DISK_NEWCHANGE) &&
- ++ } 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
- +@@ -1284,15 +1348,17 @@
- + if ( raw_cmd.track )
- + track = raw_cmd.track - 1;
- + else {
- +-#ifdef SILENT_DC_CLEAR
- +- set_dor(fdc, ~ (0x10 << UNIT(current_drive)), 0);
- +- blind_seek = 1;
- +-#endif
- ++ 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)
- ++ if (raw_cmd.track != DRS->track &&
- ++ (raw_cmd.flags & FD_RAW_NEED_SEEK))
- + track = raw_cmd.track;
- + else {
- + setup_rw_floppy();
- +@@ -1300,23 +1366,13 @@
- + }
- + }
- +
- +-#ifndef SILENT_DC_CLEAR
- +- if ( !track && DRS->track >= 0 && DRS->track < 80 ){
- +- DRS->flags &= ~FD_DISK_NEWCHANGE;
- +- /* if we go to track 0 anyways, we can just as well use
- +- * recalibrate */
- +- recalibrate_floppy();
- +- } else
- +-#endif
- +- {
- +- SET_INTR(seek_interrupt);
- +- output_byte(FD_SEEK);
- +- output_byte(UNIT(current_drive));
- +- LAST_OUT(track);
- ++ SET_INTR(seek_interrupt);
- ++ output_byte(FD_SEEK);
- ++ output_byte(UNIT(current_drive));
- ++ LAST_OUT(track);
- + #ifdef DEBUGT
- +- debugt("seek command:");
- ++ debugt("seek command:");
- + #endif
- +- }
- + }
- +
- + static void recal_interrupt(void)
- +@@ -1328,18 +1384,13 @@
- + FDCS->reset = 1;
- + else if (ST0 & ST0_ECE) {
- + switch(DRS->track){
- +- case PROVEN_ABSENT:
- +-#ifdef DEBUGT
- +- debugt("recal interrupt proven absent:");
- +-#endif
- +- /* fall through */
- + 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
- ++ * error, as failing immediately might upset
- + * computers possessed by the Devil :-) */
- + cont->error();
- + cont->redo();
- +@@ -1353,7 +1404,14 @@
- + * not to move at recalibration is to be already at
- + * track 0.) Clear the new change flag
- + */
- +- DRS->flags &= ~FD_DISK_NEWCHANGE;
- ++#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
- +@@ -1369,7 +1427,7 @@
- + }
- + } else
- + DRS->track = ST1;
- +- seek_floppy();
- ++ floppy_ready();
- + }
- +
- + /*
- +@@ -1381,34 +1439,41 @@
- + int i;
- + if ( initialising )
- + return;
- +- cli();
- +- DPRINT("unexpected interrupt\n");
- +- if ( inr >= 0 )
- +- for(i=0; i<inr; i++)
- +- printk("%d %x\n", i, reply_buffer[i] );
- ++ 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;
- +- printk("sensei\n");
- +- for(i=0; i<inr; i++)
- +- printk("%d %x\n", i, reply_buffer[i] );
- ++ 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 };
- ++struct tq_struct floppy_tq =
- ++{ 0, 0, (void *) (void *) unexpected_floppy_interrupt, 0 };
- +
- + /* interrupt handler */
- +-static void floppy_interrupt(int unused, struct pt_regs *regs)
- ++static void floppy_interrupt(int irq, struct pt_regs * regs)
- + {
- + void (*handler)(void) = DEVICE_INTR;
- +
- ++ floppy_enable_hlt();
- + CLEAR_INTR;
- +- if ( fdc >= N_FDC ){ /* we don't even know which FDC is the culprit */
- +- printk("floppy interrupt on bizarre fdc\n");
- ++ 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);
- + return;
- + }
- + inr = result();
- +@@ -1422,7 +1487,7 @@
- + inr = result();
- + } while ( (ST0 & 0x83) != UNIT(current_drive) && inr == 2);
- + }
- +- floppy_tq.routine = (void (*)(void *)) handler;
- ++ floppy_tq.routine = (void *)(void *) handler;
- + queue_task_irq(&floppy_tq, &tq_timer);
- + }
- +
- +@@ -1463,9 +1528,9 @@
- + if ( FDCS->version >= FDC_82077 )
- + outb_p(0x80 | ( FDCS->dtr &3), FD_STATUS);
- + else {
- +- arm_set_dor(FDCS->dor & ~0x04);
- ++ outb_p(FDCS->dor & ~0x04, FD_DOR);
- + udelay(FD_RESET_DELAY);
- +- arm_set_dor(FDCS->dor);
- ++ outb(FDCS->dor, FD_DOR);
- + }
- + }
- +
- +@@ -1481,9 +1546,11 @@
- + printk("floppy driver state\n");
- + printk("-------------------\n");
- + for(i=0; i<N_FDC; i++){
- +- 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... */
- ++ 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... */
- ++ }
- + }
- + printk("status=%x\n", inb_p(FD_STATUS));
- + printk("fdc_busy=%d\n", fdc_busy);
- +@@ -1493,9 +1560,9 @@
- + printk("floppy_tq.routine=%p\n", floppy_tq.routine);
- + if(fd_timer.prev)
- + printk("fd_timer.function=%p\n", fd_timer.function);
- +- if( timer_active & (1 << FLOPPY_TIMER)){
- +- printk("timer_table=%p\n",timer_table[FLOPPY_TIMER].fn);
- +- printk("expires=%ld\n",timer_table[FLOPPY_TIMER].expires);
- ++ if(fd_timeout.prev){
- ++ printk("timer_table=%p\n",fd_timeout.function);
- ++ printk("expires=%ld\n",fd_timeout.expires);
- + printk("now=%ld\n",jiffies);
- + }
- + printk("cont=%p\n", cont);
- +@@ -1507,30 +1574,28 @@
- + static void floppy_shutdown(void)
- + {
- + CLEAR_INTR;
- +- floppy_tq.routine = (void (*)(void *)) empty;
- ++ floppy_tq.routine = (void *)(void *) empty;
- + del_timer( &fd_timer);
- ++
- ++ 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;
- + cont->done(0);
- + cont->redo(); /* this will recall reset when needed */
- + }
- ++/*typedef void (*timeout_fn)(unsigned long);*/
- +
- + /* start motor, check media-changed condition and write protection */
- +-static void start_motor(void)
- ++static int start_motor( void (*function)(void) )
- + {
- + int mask, data;
- +
- + mask = 0xfc;
- + data = UNIT(current_drive);
- +-
- +- if ( (FDCS->dor & 0x03) != UNIT(current_drive) ||
- +- !(FDCS->dor & ( 0x10 << UNIT(current_drive) ) ))
- +- /* notes select time if floppy is not yet selected */
- +- DRS->select_date = jiffies;
- +-
- + if (!(raw_cmd.flags & FD_RAW_NO_MOTOR)){
- + if(!(FDCS->dor & ( 0x10 << UNIT(current_drive) ) )){
- + set_debugt();
- +@@ -1547,26 +1612,31 @@
- + /* starts motor and selects floppy */
- + del_timer(motor_off_timer + current_drive);
- + set_dor( fdc, mask, data);
- +- if( raw_cmd.flags & FD_RAW_NO_MOTOR)
- +- return;
- +
- +- disk_change(current_drive);
- +-
- +- return;
- ++ /* 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;
- +- start_motor();
- ++ if(start_motor(floppy_ready)) return;
- ++ if(fdc_dtr()) return;
- +
- +- /* wait_for_completion also schedules reset if needed. */
- +- if(wait_for_completion(current_drive,
- +- DRS->select_date+DP->select_delay,
- +- (timeout_fn) floppy_ready))
- +- return;
- +- fdc_dtr();
- +- if ( raw_cmd.flags & FD_RAW_NEED_SEEK ){
- ++#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();
- +@@ -1576,9 +1646,17 @@
- +
- + static void floppy_start(void)
- + {
- +- timer_table[FLOPPY_TIMER].expires = jiffies + DP->timeout;
- +- timer_active |= 1 << FLOPPY_TIMER;
- ++ del_timer(&fd_timeout);
- ++ fd_timeout.expires = DP->timeout;
- ++ add_timer(&fd_timeout);
- ++
- + scandrives();
- ++#ifdef DCL_DEBUG
- ++ if (DP->flags & FD_DEBUG){
- ++ DPRINT("setting NEWCHANGE in floppy_start\n");
- ++ }
- ++#endif
- ++ SETF(FD_DISK_NEWCHANGE);
- + floppy_ready();
- + }
- +
- +@@ -1598,7 +1676,7 @@
- +
- + static void do_wakeup(void)
- + {
- +- timer_active &= ~(1 << FLOPPY_TIMER);
- ++ del_timer(&fd_timeout);
- + cont = 0;
- + command_status += 2;
- + wake_up(&command_done);
- +@@ -1615,7 +1693,7 @@
- + {
- + int ret;
- +
- +- floppy_tq.routine = (void (*)(void *)) handler;
- ++ floppy_tq.routine = (void *)(void *) handler;
- + queue_task(&floppy_tq, &tq_timer);
- +
- + cli();
- +@@ -1630,7 +1708,7 @@
- + if(command_status < 2){
- + sti();
- + floppy_shutdown();
- +- redo_fd_request();
- ++ process_fd_request();
- + return -EINTR;
- + }
- + sti();
- +@@ -1681,9 +1759,10 @@
- + static int next_valid_format(void)
- + {
- + int probed_format;
- ++
- ++ probed_format = DRS->probed_format;
- + while(1){
- +- probed_format = DRS->probed_format;
- +- if ( probed_format > 8 ||
- ++ if ( probed_format >= 8 ||
- + ! DP->autodetect[probed_format] ){
- + DRS->probed_format = 0;
- + return 1;
- +@@ -1738,6 +1817,7 @@
- + }
- + cont->redo();
- + }
- ++
- + #define CODE2SIZE (ssize = ( ( 1 << SIZECODE ) + 3 ) >> 2)
- + #define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80 ) >>1))
- + #define CT(x) ( (x) | 0x40 )
- +@@ -1804,7 +1884,6 @@
- + raw_cmd.track = format_req.track << floppy->stretch;
- + buffer_track = -1;
- + setup_format_params();
- +- clear_bit(current_drive, &changed_floppies);
- + floppy_start();
- + #ifdef DEBUGT
- + debugt("queue format request");
- +@@ -1819,23 +1898,27 @@
- +
- + static int do_format(int device, struct format_descr *tmp_format_req)
- + {
- +- int okay;
- ++ int ret;
- ++ int drive=DRIVE(device);
- +
- +- LOCK_FDC(DRIVE(device),1);
- ++ 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){
- +- redo_fd_request();
- ++ 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;
- +- CALL(okay=wait_til_done(redo_format,1));
- +- redo_fd_request();
- +- return okay;
- ++ IWAIT(redo_format);
- ++ process_fd_request();
- ++ return ret;
- + }
- +
- + /*
- +@@ -1850,7 +1933,7 @@
- + int block;
- +
- + probing = 0;
- +- timer_active &= ~(1 << FLOPPY_TIMER);
- ++ del_timer(&fd_timeout);
- +
- + if (!CURRENT){
- + DPRINT("request list destroyed in floppy request done\n");
- +@@ -1914,7 +1997,7 @@
- + floppy->sect + ((R_SECTOR-SECTOR) << SIZECODE >> 2) -
- + (sector_t % floppy->sect) % ssize;
- +
- +-#ifdef SANITY
- ++#ifdef CONFIG_FLOPPY_SANITY
- + if ( nr_sectors > current_count_sectors + ssize -
- + (current_count_sectors + sector_t) % ssize +
- + sector_t % ssize){
- +@@ -1987,7 +2070,7 @@
- + size = CURRENT->current_nr_sectors << 9;
- + bh = CURRENT->bh;
- +
- +- if(bh){
- ++ if (bh){
- + bh = bh->b_reqnext;
- + while ( bh && bh->b_data == base + size ){
- + size += bh->b_size;
- +@@ -2034,8 +2117,8 @@
- + current_count_sectors = CURRENT->nr_sectors;
- + }
- + remaining = current_count_sectors << 9;
- +-#ifdef SANITY
- +- if ((remaining >> 9) > CURRENT->nr_sectors &&
- ++#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);
- +@@ -2060,12 +2143,12 @@
- + while ( remaining > 0){
- + if ( size > remaining )
- + size = remaining;
- +-#ifdef SANITY
- ++#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",
- +- (floppy_track_buffer - dma_buffer) >>9);
- ++ (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",
- +@@ -2076,7 +2159,7 @@
- + printk("write\n");
- + break;
- + }
- +- if ( ((int)buffer) % 512 )
- ++ if ( ((unsigned long)buffer) % 512 )
- + DPRINT1("%p buffer not aligned\n", buffer);
- + #endif
- + if ( CT(COMMAND) == FD_READ )
- +@@ -2089,7 +2172,7 @@
- +
- + dma_buffer += size;
- + bh = bh->b_reqnext;
- +-#ifdef SANITY
- ++#ifdef CONFIG_FLOPPY_SANITY
- + if ( !bh){
- + DPRINT("bh=null in copy buffer after copy\n");
- + break;
- +@@ -2098,7 +2181,7 @@
- + size = bh->b_size;
- + buffer = bh->b_data;
- + }
- +-#ifdef SANITY
- ++#ifdef CONFIG_FLOPPY_SANITY
- + if ( remaining ){
- + if ( remaining > 0 )
- + max_sector -= remaining >> 9;
- +@@ -2122,7 +2205,7 @@
- + int aligned_sector_t;
- + int max_sector, max_size, tracksize, ssize;
- +
- +- current_drive = DRIVE(CURRENT->dev);
- ++ set_fdc(DRIVE(CURRENT->dev));
- +
- + raw_cmd.flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
- + FD_RAW_NEED_SEEK;
- +@@ -2145,7 +2228,7 @@
- + return 0;
- + HEAD = sector_t / floppy->sect;
- +
- +- if ( (DRS->flags & FD_NEED_TWADDLE) && sector_t < floppy->sect )
- ++ if ( TESTF( FD_NEED_TWADDLE) && sector_t < floppy->sect )
- + max_sector = floppy->sect;
- +
- + /* 2M disks have phantom sectors on the first track */
- +@@ -2213,22 +2296,19 @@
- + raw_cmd.flags &= ~FD_RAW_WRITE;
- + raw_cmd.flags |= FD_RAW_READ;
- + COMMAND = FM_MODE(floppy,FD_READ);
- +- }
- +- else if ((long)CURRENT->buffer <= LAST_DMA_ADDR ) {
- ++ } else if ((long)CURRENT->buffer <= LAST_DMA_ADDR ) {
- + 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 > ( LAST_DMA_ADDR - ((long) CURRENT->buffer))>>9)
- + max_size=(LAST_DMA_ADDR - ((long)CURRENT->buffer))>>9;
- + /* 64 kb boundaries */
- + if ( ((max_size << 9) + ((long) CURRENT->buffer)) / K_64 !=
- + ((long) CURRENT->buffer ) / K_64 )
- + 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
- +@@ -2237,15 +2317,21 @@
- + * This means we should be able to read a sector even if there
- + * are other bad sectors on this track.
- + */
- +- if ((indirect - sector_t) * 2 > (direct - sector_t) * 3 &&
- +- *errors < DP->max_errors.read_track &&
- +- /*!(DRS->flags & FD_NEED_TWADDLE) &&*/
- +- ( ( !probing || (DP->read_track &
- +- (1 <<DRS->probed_format))))){
- ++ 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 {
- + current_addr = 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;
- + }
- + }
- +@@ -2256,6 +2342,7 @@
- + /* 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 ))&&
- +@@ -2273,7 +2360,7 @@
- + * if we get here, we know that the write
- + * is either aligned or the data already in the buffer
- + * (buffer will be overwritten) */
- +-#ifdef SANITY
- ++#ifdef CONFIG_FLOPPY_SANITY
- + if (sector_t != aligned_sector_t && buffer_track == -1 )
- + DPRINT("internal error offset !=0 on write\n");
- + #endif
- +@@ -2288,8 +2375,7 @@
- + 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 SANITY
- ++#ifdef CONFIG_FLOPPY_SANITY
- + if ((raw_cmd.length < current_count_sectors << 9) ||
- + (current_addr != CURRENT->buffer &&
- + CT(COMMAND) == FD_WRITE &&
- +@@ -2301,7 +2387,7 @@
- + raw_cmd.length, current_count_sectors);
- + if ( current_addr != CURRENT->buffer )
- + printk("addr=%d, length=%ld\n",
- +- (current_addr - floppy_track_buffer ) >> 9,
- ++ (int) ((current_addr - 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);
- +@@ -2342,28 +2428,27 @@
- + 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 struct cont_t rw_cont={
- +- rw_interrupt,
- +- redo_fd_request,
- +- bad_flp_intr,
- +- request_done };
- +-
- + static void redo_fd_request(void)
- + {
- + #define REPEAT {request_done(0); continue; }
- + int device;
- + int tmp;
- ++ int error;
- +
- ++ error = -1;
- + if (current_drive < N_DRIVE)
- + floppy_off(current_drive);
- +
- + if (CURRENT && CURRENT->dev < 0) return;
- +
- +- cont = &rw_cont;
- + while(1){
- + if (!CURRENT) {
- + CLEAR_INTR;
- +@@ -2374,20 +2459,29 @@
- + panic(DEVICE_NAME ": request list destroyed");
- + if (CURRENT->bh && !CURRENT->bh->b_lock)
- + panic(DEVICE_NAME ": block not locked");
- +-
- ++#if 0
- ++ if (!CURRENT->bh->b_count &&
- ++ (CURRENT->errors || error == CURRENT->dev)){
- ++ error=CURRENT->dev;
- ++ DPRINT("skipping read ahead buffer\n");
- ++ REPEAT;
- ++ }
- ++#endif
- ++ error=-1;
- + device = CURRENT->dev;
- + set_fdc( DRIVE(device));
- +
- +- timer_table[FLOPPY_TIMER].expires = jiffies + DP->timeout;
- +- timer_active |= 1 << FLOPPY_TIMER;
- +- raw_cmd.flags=0;
- +- start_motor();
- +- if(test_bit( DRIVE(device), &fake_change) ||
- +- test_bit( DRIVE(device), &changed_floppies)){
- ++ del_timer(&fd_timeout);
- ++ fd_timeout.expires = DP->timeout;
- ++ add_timer(&fd_timeout);
- ++
- ++ set_floppy(device);
- ++ 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;
- + }
- +- set_floppy(device);
- + if (!floppy) { /* Autodetection */
- + if (!probing){
- + DRS->probed_format = 0;
- +@@ -2408,7 +2502,9 @@
- + continue;
- + }
- +
- +- floppy_tq.routine = (void (*)(void *)) floppy_start;
- ++ 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");
- +@@ -2418,7 +2514,22 @@
- + #undef REPEAT
- + }
- +
- +-void do_fd_request(void)
- ++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
- +@@ -2427,7 +2538,30 @@
- + /* fdc_busy cannot be set by an interrupt or a bh */
- + floppy_grab_irq_and_dma();
- + fdc_busy=1;
- +- redo_fd_request();
- ++ process_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.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;
- + }
- +
- + /*
- +@@ -2448,29 +2582,23 @@
- +
- + static int user_reset_fdc(int drive, int arg, int interruptible)
- + {
- +- int result;
- ++ int ret;
- +
- +- result=0;
- ++ ret=0;
- ++ if(arg == FD_RESET_IF_NEEDED && !FDCS->reset)
- ++ return 0;
- + LOCK_FDC(drive,interruptible);
- +- switch(arg){
- +- case FD_RESET_ALWAYS:
- ++ if(arg == FD_RESET_ALWAYS)
- + FDCS->reset=1;
- +- break;
- +- case FD_RESET_IF_RAWCMD:
- +- if(FDCS->rawcmd == 2 )
- +- reset_fdc_info(1);
- +- break;
- +- }
- + if ( FDCS->reset ){
- + cont = &reset_cont;
- +- timer_table[FLOPPY_TIMER].expires = jiffies + 5;
- +- timer_active |= 1 << FLOPPY_TIMER;
- +- CALL(result=wait_til_done(reset_fdc,interruptible));
- +- }
- +- if ( UDRS->track == PROVEN_ABSENT )
- +- UDRS->track = NEED_2_RECAL;
- +- redo_fd_request();
- +- return result;
- ++ del_timer(&fd_timeout);
- ++ fd_timeout.expires = DP->timeout;
- ++ add_timer(&fd_timeout);
- ++ WAIT(reset_fdc);
- ++ }
- ++ process_fd_request();
- ++ return ret;
- + }
- +
- + /*
- +@@ -2493,7 +2621,7 @@
- +
- + static char *drive_name(int type, int drive )
- + {
- +- struct floppy_struct *floppy;
- ++ struct floppy_struct *floppy;
- +
- + if ( type )
- + floppy = floppy_type + type;
- +@@ -2516,21 +2644,21 @@
- + generic_failure,
- + generic_done };
- +
- +-static int raw_cmd_ioctl(int drive, void *param)
- ++static int raw_cmd_ioctl(void *param)
- + {
- +- int i, count, ret;
- ++ int i, drive, count, ret;
- +
- + if ( FDCS->rawcmd <= 1 )
- + FDCS->rawcmd = 1;
- +- for ( i= 0; i < N_DRIVE; i++){
- +- if ( FDC(i) != fdc)
- ++ for ( drive= 0; drive < N_DRIVE; drive++){
- ++ if ( FDC(drive) != fdc)
- + continue;
- +- if ( i == drive ){
- +- if ( drive_state[i].fd_ref > 1 ){
- ++ if ( drive == current_drive ){
- ++ if ( UDRS->fd_ref > 1 ){
- + FDCS->rawcmd = 2;
- + break;
- + }
- +- } else if ( drive_state[i].fd_ref ){
- ++ } else if ( UDRS->fd_ref ){
- + FDCS->rawcmd = 2;
- + break;
- + }
- +@@ -2540,11 +2668,15 @@
- + return -EIO;
- +
- + COPYIN(raw_cmd);
- +- raw_cmd.rate &= 0x03;
- ++ raw_cmd.rate &= 0x03;
- + count = raw_cmd.length;
- + if (raw_cmd.flags & (FD_RAW_WRITE | FD_RAW_READ)){
- + if(count > max_buffer_sectors * 1024 )
- + return -ENOMEM;
- ++ if(count == 0){
- ++ printk("attempt to do a 0 byte dma transfer\n");
- ++ return -EINVAL;
- ++ }
- + buffer_track = -1;
- + }
- + if ( raw_cmd.flags & FD_RAW_WRITE ){
- +@@ -2556,13 +2688,18 @@
- +
- + current_addr = floppy_track_buffer;
- + cont = &raw_cmd_cont;
- +- CALL(ret=wait_til_done(floppy_start,1));
- ++ IWAIT(floppy_start);
- ++#ifdef DCL_DEBUG
- ++ if (DP->flags & FD_DEBUG){
- ++ DPRINT("calling disk change from raw_cmd ioctl\n");
- ++ }
- ++#endif
- + 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(drive);
- ++ motor_off_callback(current_drive);
- +
- + if ( !ret && !FDCS->reset ){
- + raw_cmd.reply_count = inr;
- +@@ -2572,7 +2709,6 @@
- + raw_cmd.length = get_dma_residue(FLOPPY_DMA);
- + } else
- + ret = -EIO;
- +-
- + DRS->track = NO_TRACK;
- + if ( ret )
- + return ret;
- +@@ -2582,7 +2718,7 @@
- + if (i)
- + return i;
- + }
- +-
- ++
- + return COPYOUT(raw_cmd);
- + }
- +
- +@@ -2590,7 +2726,7 @@
- + {
- + /* invalidate the buffer track to force a reread */
- + set_bit( DRIVE(rdev), &fake_change);
- +- redo_fd_request();
- ++ process_fd_request();
- + check_disk_change(rdev);
- + return 0;
- + }
- +@@ -2599,6 +2735,7 @@
- + 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;
- +@@ -2636,7 +2773,9 @@
- + return -ENODEV;
- + return COPYOUT(this_floppy[0]);
- + case FDPOLLDRVSTAT:
- +- check_disk_change(device);
- ++ LOCK_FDC(drive,1);
- ++ CALL(poll_drive(1, FD_RAW_NEED_DISK));
- ++ process_fd_request();
- + /* fall through */
- + case FDGETDRVSTAT:
- + return COPYOUT(*UDRS);
- +@@ -2647,10 +2786,8 @@
- + case FDWERRORGET:
- + return COPYOUT(*UDRWE);
- + }
- +-
- + if (!IOCTL_ALLOWED)
- + return -EPERM;
- +-
- + switch (cmd) {
- + case FDWERRORCLR:
- + UDRWE->write_errors = 0;
- +@@ -2665,14 +2802,12 @@
- + return -EINVAL;
- + LOCK_FDC(drive,1);
- + set_floppy(device);
- +- CALL(i = raw_cmd_ioctl(drive, (void *) param));
- +- redo_fd_request();
- ++ CALL(i = raw_cmd_ioctl((void *) param));
- ++ process_fd_request();
- + return i;
- + case FDFMTTRK:
- + if (UDRS->fd_ref != 1)
- + return -EBUSY;
- +- if (UDRS->track == PROVEN_ABSENT)
- +- return -ENXIO;
- + COPYIN(tmp_format_req);
- + return do_format(device, &tmp_format_req);
- + case FDSETMAXERRS:
- +@@ -2682,7 +2817,7 @@
- + case FDCLRPRM:
- + LOCK_FDC(drive,1);
- + current_type[drive] = NULL;
- +- floppy_sizes[drive] = 2;
- ++ floppy_sizes[drive] = MAX_DISK_SIZE;
- + UDRS->keep_data = 0;
- + return invalidate_drive(device);
- + case FDFMTEND:
- +@@ -2702,7 +2837,7 @@
- + if ( type){
- + if ( !suser() )
- + return -EPERM;
- +- LOCK_FDC(-1,1);
- ++ LOCK_FDC(drive,1);
- + for ( cnt = 0; cnt < N_DRIVE; cnt++){
- + if (TYPE(drive_state[cnt].fd_device) == type &&
- + drive_state[cnt].fd_ref)
- +@@ -2714,11 +2849,9 @@
- + cnt < (type << 2 ) + 4 ;
- + cnt++)
- + floppy_sizes[cnt]=
- +-#ifdef HAVE_2_CONTROLLERS
- + floppy_sizes[cnt+0x80]=
- +-#endif
- + floppy_type[type].size>>1;
- +- redo_fd_request();
- ++ process_fd_request();
- + for ( cnt = 0; cnt < N_DRIVE; cnt++){
- + if (TYPE(drive_state[cnt].fd_device) == type &&
- + drive_state[cnt].fd_ref)
- +@@ -2729,12 +2862,10 @@
- + }
- +
- + LOCK_FDC(drive,1);
- +- if ( cmd != FDDEFPRM ){
- ++ if ( cmd != FDDEFPRM )
- + /* notice a disk change immediately, else
- + * we loose our settings immediately*/
- +- raw_cmd.flags = 0;
- +- start_motor();
- +- }
- ++ CALL(poll_drive(1,0));
- + user_params[drive] = newparams;
- + if (buffer_drive == drive &&
- + buffer_max > user_params[drive].sect)
- +@@ -2755,7 +2886,7 @@
- + DRS->maxtrack )
- + invalidate_drive(device);
- + else
- +- redo_fd_request();
- ++ process_fd_request();
- + return 0;
- + case FDRESET:
- + return user_reset_fdc( drive, (int)param, 1);
- +@@ -2772,7 +2903,7 @@
- + case FDTWADDLE:
- + LOCK_FDC(drive,1);
- + twaddle();
- +- redo_fd_request();
- ++ process_fd_request();
- + }
- + if ( ! suser() )
- + return -EPERM;
- +@@ -2786,70 +2917,89 @@
- + #undef IOCTL_ALLOWED
- + }
- +
- +-static void set_base_type(int drive, int code)
- +-{
- +- if(code>0 && code <= NUMBER(default_drive_params)+1) {
- +- memcpy((char *)UDP,
- +- (char *)(&default_drive_params[code-1].params),
- +- sizeof(struct floppy_drive_params));
- +- printk("fd%d is %s", drive, default_drive_params[code-1].name);
- +- return;
- +- }
- +- else if (!code)
- +- printk("fd%d is not installed", drive);
- +- else
- +- printk("fd%d is unknown type %d", drive, code);
- +-}
- +-
- + static void config_types(void)
- + {
- ++ int first=1;
- + int drive;
- +
- +- for (drive=0; drive<N_DRIVE ; drive++){
- +- /* default type for unidentifiable drives */
- +- memcpy((char *) UDP, (char *) (&default_drive_params->params),
- +- sizeof( struct floppy_drive_params ));
- +- }
- +- printk("Floppy drive(s): ");
- +- for(drive=0; drive<no_floppies; drive++)
- +- {
- +- set_base_type(drive, 1+4);
- +- if(drive < no_floppies-1)
- +- printk(", ");
- ++ /* 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);
- ++ }
- + }
- +- printk("\n");
- ++ if(!first)
- ++ printk("\n");
- + }
- +
- +-int floppy_is_wp( int minor)
- ++static int floppy_read(struct inode * inode, struct file * filp,
- ++ char * buf, int count)
- + {
- +- check_disk_change(minor + (MAJOR_NR << 8));
- +- return ! ( drive_state[ DRIVE(minor) ].flags & FD_DISK_WRITABLE );
- ++ 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,
- ++ char * buf, int count)
- ++{
- ++ int block;
- ++ int ret;
- ++ int drive = DRIVE(inode->i_rdev);
- +
- +-#define WRAPPER(op) \
- +-static int floppy_##op(struct inode * inode, struct file * filp, \
- +- char * buf, int count) \
- +-{ \
- +- check_disk_change(inode->i_rdev); \
- +- if ( drive_state[DRIVE(inode->i_rdev)].track == PROVEN_ABSENT ) \
- +- return -ENXIO; \
- +- if ( test_bit(DRIVE(inode->i_rdev),&changed_floppies)) \
- +- return -ENXIO; \
- +- return block_##op(inode, filp, buf, count); \
- ++ 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;
- + }
- +
- +-WRAPPER(read)
- +-WRAPPER(write)
- +-
- + static void floppy_release(struct inode * inode, struct file * filp)
- + {
- + int drive;
- +-
- ++
- + drive = DRIVE(inode->i_rdev);
- +
- +- fsync_dev(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--) {
- +@@ -2879,20 +3029,22 @@
- + }
- +
- + drive = DRIVE(inode->i_rdev);
- +- if ( drive >= N_DRIVE || !( ALLOWED_DRIVE_MASK & ( 1 << drive)) )
- ++ 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;
- +-
- +- if ((filp->f_mode & 3) &&
- +- UDRS->track == PROVEN_ABSENT )
- +- return -ENXIO;
- +-
- + old_dev = UDRS->fd_device;
- + if (UDRS->fd_ref && old_dev != 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;
- +@@ -2900,7 +3052,7 @@
- + if (floppy_grab_irq_and_dma())
- + return -EBUSY;
- +
- +- if(filp->f_flags & O_EXCL)
- ++ if (filp->f_flags & O_EXCL)
- + UDRS->fd_ref = -1;
- + else
- + UDRS->fd_ref++;
- +@@ -2916,30 +3068,21 @@
- + /* 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 && UDRS->track == PROVEN_ABSENT )
- +- RETERR(ENXIO);
- +-
- +- if (user_reset_fdc(drive, FD_RESET_IF_NEEDED,0))
- +- RETERR(EIO);
- +-
- + if (filp->f_mode & 3) {
- + UDRS->last_checked = 0;
- + check_disk_change(inode->i_rdev);
- +- if (test_bit(drive,&changed_floppies))
- ++ if (UTESTF(FD_DISK_CHANGED))
- + RETERR(ENXIO);
- + }
- +-
- +- if (filp->f_mode && UDRS->track == PROVEN_ABSENT )
- +- RETERR(ENXIO);
- +-
- +- if ((filp->f_mode & 2) && !(UDRS->flags & FD_DISK_WRITABLE))
- ++ if ((filp->f_mode & 2) && !(UTESTF(FD_DISK_WRITABLE)))
- + RETERR(EROFS);
- + return 0;
- + #undef RETERR
- +@@ -2957,45 +3100,38 @@
- + return 0;
- + }
- +
- +- if(test_bit(drive, &changed_floppies))
- ++ if (UTESTF(FD_DISK_CHANGED))
- + return 1;
- +
- + if(UDRS->last_checked + UDP->checkfreq < jiffies){
- + lock_fdc(drive,0);
- +- start_motor();
- +- redo_fd_request();
- ++ poll_drive(0,0);
- ++ process_fd_request();
- + }
- +-
- +- if(test_bit(drive, &changed_floppies))
- +- return 1;
- +- if(test_bit(drive, &fake_change))
- ++
- ++ if(UTESTF(FD_DISK_CHANGED) ||
- ++ test_bit(drive, &fake_change) ||
- ++ (!TYPE(dev) && !current_type[drive]))
- + return 1;
- + return 0;
- + }
- +
- +-static struct cont_t poll_cont={
- +- success_and_wakeup,
- +- floppy_ready,
- +- generic_failure,
- +- generic_done };
- +-
- +-
- + /* revalidate the floppy disk, i.e. trigger format autodetection by reading
- +- * the bootblock (block 0). "Autodetection" is also needed to check wether
- ++ * 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(dev_t dev)
- + {
- ++#define NO_GEOM (!current_type[drive] && !TYPE(dev))
- + struct buffer_head * bh;
- + int drive=DRIVE(dev);
- + int cf;
- +
- +- cf = test_bit(drive, &changed_floppies);
- +- if(cf || test_bit(drive, &fake_change)){
- ++ if(UTESTF(FD_DISK_CHANGED) || test_bit(drive, &fake_change) || NO_GEOM){
- + lock_fdc(drive,0);
- +- cf = test_bit(drive, &changed_floppies);
- +- if(! (cf || test_bit(drive, &fake_change))){
- +- redo_fd_request(); /* already done by another thread */
- ++ cf = UTESTF(FD_DISK_CHANGED);
- ++ if(! (cf || test_bit(drive, &fake_change) || NO_GEOM)){
- ++ process_fd_request(); /*already done by another thread*/
- + return 0;
- + }
- + UDRS->maxblock = 0;
- +@@ -3003,31 +3139,28 @@
- + if ( buffer_drive == drive)
- + buffer_track = -1;
- + clear_bit(drive, &fake_change);
- +- clear_bit(drive, &changed_floppies);
- +- if(cf){
- ++ UCLEARF(FD_DISK_CHANGED);
- ++ if(cf)
- + UDRS->generation++;
- +- if(!current_type[drive] && !TYPE(dev)){
- +- /* auto-sensing */
- +- if (!(bh = getblk(dev,0,1024))){
- +- redo_fd_request();
- +- return 1;
- +- }
- +- if ( bh && ! bh->b_uptodate)
- +- ll_rw_block(READ, 1, &bh);
- +- redo_fd_request();
- +- wait_on_buffer(bh);
- +- brelse(bh);
- +- return 0;
- +- } else {
- +- /* no auto-sense, just clear dcl */
- +- raw_cmd.flags=FD_RAW_NEED_SEEK|FD_RAW_NEED_DISK;
- +- raw_cmd.track=0;
- +- raw_cmd.cmd_count=0;
- +- cont = &poll_cont;
- +- wait_til_done(floppy_ready,0);
- ++ 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;
- + }
- +- }
- +- redo_fd_request();
- ++ 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;
- + }
- +@@ -3094,17 +3227,145 @@
- + }
- + printk("FDC %d is a post-1991 82077\n",fdc);
- + return FDC_82077; /* Revised 82077AA passes all the tests */
- +-} /* fdc_init */
- ++} /* get_fdc_version */
- +
- +-void floppy_init(void)
- ++/* 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 parameter for cmos\n");
- ++ return;
- ++ }
- ++ current_drive = ints[1];
- ++ if (current_drive < 0 || current_drive >= 8 ){
- ++ DPRINT("bad drive for set_cmos\n");
- ++ return;
- ++ }
- ++ 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 {
- ++ 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)
- ++ return;
- ++ 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;
- ++ }
- ++ }
- ++ 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");
- ++ DPRINT("Read linux/drivers/block/README.fd\n");
- ++}
- ++
- ++#ifdef FD_MODULE
- ++static
- ++#endif
- ++int new_floppy_init(void)
- ++{
- ++ int i,drive;
- ++ int have_no_fdc=0;
- ++
- + sti();
- +
- + if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
- + printk("Unable to get major %d for floppy\n",MAJOR_NR);
- +- return;
- ++ return -EBUSY;
- + }
- +
- + for(i=0; i<256; i++)
- +@@ -3114,55 +3375,63 @@
- + 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;
- +- hardsect_size[MAJOR_NR] = fd_sectsizes;
- +- timer_table[FLOPPY_TIMER].fn = floppy_shutdown;
- +- timer_active &= ~(1 << FLOPPY_TIMER);
- ++ del_timer(&fd_timeout);
- + config_types();
- +
- +- for(i = 0; i<256; i++)
- +- fd_sectsizes[i] = 1024; /* patch for msdos driver */
- +-
- +- fdc_state[0].address = 0x3f0;
- ++ fdc_state[0].address = FDC1;
- ++ fdc_state[0].dor = 0;
- + #if N_FDC > 1
- +- fdc_state[1].address = 0x370;
- ++ 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 = 0;
- ++ FDCS->dor = 0x4;
- + FDCS->reset = 0;
- + FDCS->version = FDC_NONE;
- +- set_dor(fdc, ~0, 0xc );
- ++ }
- ++
- ++ if(floppy_grab_irq_and_dma()){
- ++ unregister_blkdev(MAJOR_NR,"fd");
- ++ return -EBUSY;
- + }
- +
- + /* initialise drive state */
- +- for (i = 0; i < N_DRIVE ; i++) {
- +- current_drive = i;
- +- DRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE;
- +- DRS->generation = 0;
- +- DRS->keep_data = 0;
- +- DRS->fd_ref = 0;
- +- DRS->fd_device = 0;
- +- DRWE->write_errors = 0;
- +- DRWE->first_error_sector = 0;
- +- DRWE->first_error_generation = 0;
- +- DRWE->last_error_sector = 0;
- +- DRWE->last_error_generation = 0;
- +- DRWE->badness = 0;
- ++ 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;
- ++ 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;
- + }
- +
- +- floppy_grab_irq_and_dma();
- + for (i = 0 ; i < N_FDC ; i++) {
- + fdc = i;
- ++ if (FDCS->address == -1 )
- ++ continue;
- + FDCS->rawcmd = 2;
- +- if(user_reset_fdc(-1,FD_RESET_IF_NEEDED,0))
- ++ 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)
- ++ if (FDCS->version == FDC_NONE){
- ++ FDCS->address = -1;
- + continue;
- ++ }
- +
- ++ 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.
- +@@ -3174,6 +3443,15 @@
- + current_drive = 0;
- + floppy_release_irq_and_dma();
- + initialising=0;
- ++ if(have_no_fdc)
- ++ unregister_blkdev(MAJOR_NR,"fd");
- ++ return have_no_fdc;
- ++}
- ++
- ++/* stupid compatibility hack... */
- ++void floppy_init(void)
- ++{
- ++ new_floppy_init();
- + }
- +
- + static int floppy_grab_irq_and_dma(void)
- +@@ -3185,13 +3463,17 @@
- + return 0;
- + }
- + sti();
- +-
- ++#ifdef FD_MODULE
- ++ MOD_INC_USE_COUNT;
- ++#endif
- + for(i=0; i< N_FDC; i++){
- +- fdc = i;
- +- reset_fdc_info(1);
- +- arm_set_dor(FDCS->dor);
- ++ if(FDCS->address != -1){
- ++ fdc = i;
- ++ reset_fdc_info(1);
- ++ outb_p(FDCS->dor, FD_DOR);
- ++ }
- + }
- +- set_dor(0, ~0, 8); /* avoid immediate interrupt */
- ++ set_dor(0, ~0, 8); /* avoid immediate interrupt */
- +
- + if (request_irq(FLOPPY_IRQ, floppy_interrupt, SA_INTERRUPT, "floppy")) {
- + DPRINT1("Unable to grab IRQ%d for the floppy driver\n",
- +@@ -3204,24 +3486,49 @@
- + free_irq(FLOPPY_IRQ);
- + return -1;
- + }
- ++ for(fdc = 0; fdc < N_FDC ; fdc++)
- ++ if(FDCS->address != -1)
- ++ outb_p(FDCS->dor, FD_DOR);
- ++ fdc = 0;
- + enable_irq(FLOPPY_IRQ);
- + return 0;
- + }
- +
- + static void floppy_release_irq_and_dma(void)
- + {
- +- int i;
- ++#ifdef CONFIG_FLOPPY_SANITY
- ++ int drive;
- ++#endif
- + 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);
- +- /* switch off dma gates */
- +- for(i=0; i< N_FDC; i++)
- +- set_dor(i, ~8, 0);
- ++
- ++ set_dor(0, ~0, 8);
- ++#if N_FDC > 1
- ++ set_dor(1, ~8, 0);
- ++#endif
- ++ floppy_enable_hlt();
- ++#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\n");
- ++ if (fd_timer.next)
- ++ printk("auxiliary floppy timer still active\n");
- ++ if(floppy_tq.sync)
- ++ printk("task queue still active\n");
- ++#endif
- + }
- ++
- diff -r -u -N linux.orig/arch/arm/drivers/block/hdsrch.c linux.arm/arch/arm/drivers/block/hdsrch.c
- --- linux.orig/arch/arm/drivers/block/hdsrch.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/hdsrch.c Fri Oct 27 23:14:44 1995
- @@ -0,0 +1,654 @@
- +#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>
- +
- +#pragma no_check_stack
- +
- +#define RECSIZE 60
- +
- +#define MAX_BLK_FRAGS 64
- +
- +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;
- +#if 0
- +static long *pages=NULL;
- +#endif
- +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)
- + address-=0x200;
- + }
- + 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;
- +
- +/* p=strchr(path,':'); */
- +/* if(p!=NULL) */
- +/* { */
- +/* drive=p[1]-'0'; */
- +/* if(drive<4 || drive>5) */
- +/* return 0; */
- +/* p=strchr(p,'.'); */
- +/* if(p==NULL) */
- +/* return 0; */
- +/* path=p+1; */
- +/* } */
- + 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;
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/block/ll_rw_blk.c linux.arm/arch/arm/drivers/block/ll_rw_blk.c
- --- linux.orig/arch/arm/drivers/block/ll_rw_blk.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/ll_rw_blk.c Fri Oct 27 23:14:45 1995
- @@ -0,0 +1,568 @@
- +/*
- + * linux/drivers/block/ll_rw_blk.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + * Copyright (C) 1994, Karl Keyte: Added support for disk statistics
- + */
- +
- +/*
- + * This handles all read/write requests to block devices
- + */
- +#include <linux/sched.h>
- +#include <linux/kernel.h>
- +#include <linux/kernel_stat.h>
- +#include <linux/errno.h>
- +#include <linux/string.h>
- +#include <linux/config.h>
- +#include <linux/locks.h>
- +#include <linux/mm.h>
- +
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include "blk.h"
- +
- +/*
- + * The request-struct contains all necessary data
- + * to load a nr of sectors into memory
- + */
- +static struct request all_requests[NR_REQUEST];
- +
- +/*
- + * used to wait on when there are no free requests
- + */
- +struct wait_queue * wait_for_request = NULL;
- +
- +/* This specifies how many sectors to read ahead on the disk. */
- +
- +int read_ahead[MAX_BLKDEV] = {0, };
- +
- +/* blk_dev_struct is:
- + * do_request-address
- + * next-request
- + */
- +struct blk_dev_struct blk_dev[MAX_BLKDEV] = {
- + { NULL, NULL }, /* 0 no_dev */
- + { NULL, NULL }, /* 1 dev mem */
- + { NULL, NULL }, /* 2 dev fd */
- + { NULL, NULL }, /* 3 dev ide0 or hd */
- + { NULL, NULL }, /* 4 dev ttyx */
- + { NULL, NULL }, /* 5 dev tty */
- + { NULL, NULL }, /* 6 dev lp */
- + { NULL, NULL }, /* 7 dev pipes */
- + { NULL, NULL }, /* 8 dev sd */
- + { NULL, NULL }, /* 9 dev st */
- + { NULL, NULL }, /* 10 */
- + { NULL, NULL }, /* 11 */
- + { NULL, NULL }, /* 12 */
- + { NULL, NULL }, /* 13 */
- + { NULL, NULL }, /* 14 */
- + { NULL, NULL }, /* 15 */
- + { NULL, NULL }, /* 16 */
- + { NULL, NULL }, /* 17 */
- + { NULL, NULL }, /* 18 */
- + { NULL, NULL }, /* 19 */
- + { NULL, NULL }, /* 20 */
- + { NULL, NULL }, /* 21 */
- + { NULL, NULL } /* 22 dev ide1 */
- +};
- +
- +/*
- + * blk_size contains the size of all block-devices in units of 1024 byte
- + * sectors:
- + *
- + * blk_size[MAJOR][MINOR]
- + *
- + * if (!blk_size[MAJOR]) then no minor size checking is done.
- + */
- +int * blk_size[MAX_BLKDEV] = { NULL, NULL, };
- +
- +/*
- + * blksize_size contains the size of all block-devices:
- + *
- + * blksize_size[MAJOR][MINOR]
- + *
- + * if (!blksize_size[MAJOR]) then 1024 bytes is assumed.
- + */
- +int * blksize_size[MAX_BLKDEV] = { NULL, NULL, };
- +
- +/*
- + * hardsect_size contains the size of the hardware sector of a device.
- + *
- + * hardsect_size[MAJOR][MINOR]
- + *
- + * if (!hardsect_size[MAJOR])
- + * then 512 bytes is assumed.
- + * else
- + * sector_size is hardsect_size[MAJOR][MINOR]
- + * This is currently set by some scsi device and read by the msdos fs driver
- + * This might be a some uses later.
- + */
- +int * hardsect_size[MAX_BLKDEV] = { NULL, NULL, };
- +
- +/*
- + * look for a free request in the first N entries.
- + * NOTE: interrupts must be disabled on the way in, and will still
- + * be disabled on the way out.
- + */
- +static inline struct request * get_request(int n, int dev)
- +{
- + static struct request *prev_found = NULL, *prev_limit = NULL;
- + register struct request *req, *limit;
- +
- + if (n <= 0)
- + panic("get_request(%d): impossible!\n", n);
- +
- + limit = all_requests + n;
- + if (limit != prev_limit) {
- + prev_limit = limit;
- + prev_found = all_requests;
- + }
- + req = prev_found;
- + for (;;) {
- + req = ((req > all_requests) ? req : limit) - 1;
- + if (req->dev < 0)
- + break;
- + if (req == prev_found)
- + return NULL;
- + }
- + prev_found = req;
- + req->dev = dev;
- + return req;
- +}
- +
- +/*
- + * wait until a free request in the first N entries is available.
- + * NOTE: interrupts must be disabled on the way in, and will still
- + * be disabled on the way out.
- + */
- +static inline struct request * get_request_wait(int n, int dev)
- +{
- + register struct request *req;
- +
- + while ((req = get_request(n, dev)) == NULL)
- + sleep_on(&wait_for_request);
- + return req;
- +}
- +
- +/* RO fail safe mechanism */
- +
- +static long ro_bits[MAX_BLKDEV][8];
- +
- +int is_read_only(int dev)
- +{
- + int minor,major;
- +
- + major = MAJOR(dev);
- + minor = MINOR(dev);
- + if (major < 0 || major >= MAX_BLKDEV) return 0;
- + return ro_bits[major][minor >> 5] & (1 << (minor & 31));
- +}
- +
- +void set_device_ro(int dev,int flag)
- +{
- + int minor,major;
- +
- + major = MAJOR(dev);
- + minor = MINOR(dev);
- + if (major < 0 || major >= MAX_BLKDEV) return;
- + if (flag) ro_bits[major][minor >> 5] |= 1 << (minor & 31);
- + else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
- +}
- +
- +/*
- + * add-request adds a request to the linked list.
- + * It disables interrupts so that it can muck with the
- + * request-lists in peace.
- + */
- +static void add_request(struct blk_dev_struct * dev, struct request * req)
- +{
- + struct request * tmp;
- + short disk_index;
- +
- + switch (MAJOR(req->dev)) {
- + case SCSI_DISK_MAJOR: disk_index = (MINOR(req->dev) & 0x0070) >> 4;
- + if (disk_index < 4)
- + kstat.dk_drive[disk_index]++;
- + break;
- + case HD_MAJOR:
- + case XT_DISK_MAJOR: disk_index = (MINOR(req->dev) & 0x0040) >> 6;
- + kstat.dk_drive[disk_index]++;
- + break;
- + case IDE1_MAJOR: disk_index = ((MINOR(req->dev) & 0x0040) >> 6) + 2;
- + kstat.dk_drive[disk_index]++;
- + default: break;
- + }
- +
- + req->next = NULL;
- + cli();
- + if (req->bh)
- + mark_buffer_clean(req->bh);
- + if (!(tmp = dev->current_request)) {
- + dev->current_request = req;
- + (dev->request_fn)();
- + sti();
- + return;
- + }
- + for ( ; tmp->next ; tmp = tmp->next) {
- + if ((IN_ORDER(tmp,req) ||
- + !IN_ORDER(tmp,tmp->next)) &&
- + IN_ORDER(req,tmp->next))
- + break;
- + }
- + req->next = tmp->next;
- + tmp->next = req;
- +
- +/* for SCSI devices, call request_fn unconditionally */
- + if (scsi_major(MAJOR(req->dev)))
- + (dev->request_fn)();
- +
- + sti();
- +}
- +
- +static void make_request(int major,int rw, struct buffer_head * bh)
- +{
- + unsigned int sector, count;
- + struct request * req;
- + int rw_ahead, max_req;
- +
- +/* WRITEA/READA is special case - it is not really needed, so if the */
- +/* buffer is locked, we just forget about it, else it's a normal read */
- + rw_ahead = (rw == READA || rw == WRITEA);
- + if (rw_ahead) {
- + if (bh->b_lock)
- + return;
- + if (rw == READA)
- + rw = READ;
- + else
- + rw = WRITE;
- + }
- + if (rw!=READ && rw!=WRITE) {
- + printk("Bad block dev command, must be R/W/RA/WA\n");
- + return;
- + }
- + count = bh->b_size >> 9;
- + sector = bh->b_blocknr * count;
- + if (blk_size[major])
- + if (blk_size[major][MINOR(bh->b_dev)] < (sector + count)>>1) {
- + bh->b_dirt = bh->b_uptodate = 0;
- + bh->b_req = 0;
- + return;
- + }
- + /* Uhhuh.. Nasty dead-lock possible here.. */
- + if (bh->b_lock)
- + return;
- + /* Maybe the above fixes it, and maybe it doesn't boot. Life is interesting */
- + lock_buffer(bh);
- + if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {
- + unlock_buffer(bh);
- + return;
- + }
- +
- +/* we don't allow the write-requests to fill up the queue completely:
- + * we want some room for reads: they take precedence. The last third
- + * of the requests are only for reads.
- + */
- + max_req = (rw == READ) ? NR_REQUEST : ((NR_REQUEST*2)/3);
- +
- +/* big loop: look for a free request. */
- +
- +repeat:
- + cli();
- +
- +/* The scsi disk drivers and the IDE driver completely remove the request
- + * from the queue when they start processing an entry. For this reason
- + * it is safe to continue to add links to the top entry for those devices.
- + */
- + if (( major == IDE0_MAJOR /* same as HD_MAJOR */
- + || major == IDE1_MAJOR
- + || major == FLOPPY_MAJOR
- + || major == SCSI_DISK_MAJOR
- + || major == SCSI_CDROM_MAJOR)
- + && (req = blk_dev[major].current_request))
- + {
- +#ifdef CONFIG_BLK_DEV_HD
- + if (major == HD_MAJOR || major == FLOPPY_MAJOR)
- +#else
- + if (major == FLOPPY_MAJOR)
- +#endif CONFIG_BLK_DEV_HD
- + req = req->next;
- + while (req) {
- + if (req->dev == bh->b_dev &&
- + !req->sem &&
- + req->cmd == rw &&
- + req->sector + req->nr_sectors == sector &&
- + req->nr_sectors < 244)
- + {
- + req->bhtail->b_reqnext = bh;
- + req->bhtail = bh;
- + req->nr_sectors += count;
- + mark_buffer_clean(bh);
- + sti();
- + return;
- + }
- +
- + if (req->dev == bh->b_dev &&
- + !req->sem &&
- + req->cmd == rw &&
- + req->sector - count == sector &&
- + req->nr_sectors < 244)
- + {
- + req->nr_sectors += count;
- + bh->b_reqnext = req->bh;
- + req->buffer = bh->b_data;
- + req->current_nr_sectors = count;
- + req->sector = sector;
- + mark_buffer_clean(bh);
- + req->bh = bh;
- + sti();
- + return;
- + }
- +
- + req = req->next;
- + }
- + }
- +
- +/* find an unused request. */
- + req = get_request(max_req, bh->b_dev);
- +
- +/* if no request available: if rw_ahead, forget it; otherwise try again. */
- + if (! req) {
- + if (rw_ahead) {
- + sti();
- + unlock_buffer(bh);
- + return;
- + }
- + sleep_on(&wait_for_request);
- + sti();
- + goto repeat;
- + }
- +
- +/* we found a request. */
- + sti();
- +
- +/* fill up the request-info, and add it to the queue */
- + req->cmd = rw;
- + req->errors = 0;
- + req->sector = sector;
- + req->nr_sectors = count;
- + req->current_nr_sectors = count;
- + req->buffer = bh->b_data;
- + req->sem = NULL;
- + req->bh = bh;
- + req->bhtail = bh;
- + req->next = NULL;
- + add_request(major+blk_dev,req);
- +}
- +
- +void ll_rw_page(int rw, int dev, int page, char * buffer)
- +{
- + struct request * req;
- + unsigned int major = MAJOR(dev);
- + struct semaphore sem = MUTEX_LOCKED;
- +
- + if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
- + printk("Trying to read nonexistent block-device %04x (%d)\n",dev,page*8);
- + return;
- + }
- + if (rw!=READ && rw!=WRITE)
- + panic("Bad block dev command, must be R/W");
- + if (rw == WRITE && is_read_only(dev)) {
- + printk("Can't page to read-only device 0x%X\n",dev);
- + return;
- + }
- + cli();
- + req = get_request_wait(NR_REQUEST, dev);
- + sti();
- +/* fill up the request-info, and add it to the queue */
- + req->cmd = rw;
- + req->errors = 0;
- + req->sector = page << (PAGE_SHIFT-9);
- + req->nr_sectors = 1 << (PAGE_SHIFT-9);
- + req->current_nr_sectors = 1 << (PAGE_SHIFT-9);
- + req->buffer = buffer;
- + req->sem = &sem;
- + req->bh = NULL;
- + req->next = NULL;
- + add_request(major+blk_dev,req);
- + down(&sem);
- +}
- +
- +/* This function can be used to request a number of buffers from a block
- + device. Currently the only restriction is that all buffers must belong to
- + the same device */
- +
- +void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
- +{
- + unsigned int major;
- + struct request plug;
- + int plugged;
- + int correct_size;
- + struct blk_dev_struct * dev;
- + int i;
- +
- + /* Make sure that the first block contains something reasonable */
- + while (!*bh) {
- + bh++;
- + if (--nr <= 0)
- + return;
- + };
- +
- + dev = NULL;
- + if ((major = MAJOR(bh[0]->b_dev)) < MAX_BLKDEV)
- + dev = blk_dev + major;
- + if (!dev || !dev->request_fn) {
- + printk(
- + "ll_rw_block: Trying to read nonexistent block-device %04lX (%ld)\n",
- + (unsigned long) bh[0]->b_dev, bh[0]->b_blocknr);
- + goto sorry;
- + }
- +
- + /* Determine correct block size for this device. */
- + correct_size = BLOCK_SIZE;
- + if (blksize_size[major]) {
- + i = blksize_size[major][MINOR(bh[0]->b_dev)];
- + if (i)
- + correct_size = i;
- + }
- +
- + /* Verify requested block sizes. */
- + for (i = 0; i < nr; i++) {
- + if (bh[i] && bh[i]->b_size != correct_size) {
- + printk(
- + "ll_rw_block: only %d-char blocks implemented (%lu)\n",
- + correct_size, bh[i]->b_size);
- + goto sorry;
- + }
- + }
- +
- + if ((rw == WRITE || rw == WRITEA) && is_read_only(bh[0]->b_dev)) {
- + printk("Can't write to read-only device 0x%X\n",bh[0]->b_dev);
- + goto sorry;
- + }
- +
- + /* If there are no pending requests for this device, then we insert
- + a dummy request for that device. This will prevent the request
- + from starting until we have shoved all of the blocks into the
- + queue, and then we let it rip. */
- +
- + plugged = 0;
- + cli();
- + if (!dev->current_request && nr > 1) {
- + dev->current_request = &plug;
- + plug.dev = -1;
- + plug.next = NULL;
- + plugged = 1;
- + }
- + sti();
- + for (i = 0; i < nr; i++) {
- + if (bh[i]) {
- + bh[i]->b_req = 1;
- + make_request(major, rw, bh[i]);
- + if (rw == READ || rw == READA)
- + kstat.pgpgin++;
- + else
- + kstat.pgpgout++;
- + }
- + }
- + if (plugged) {
- + cli();
- + dev->current_request = plug.next;
- + (dev->request_fn)();
- + sti();
- + }
- + return;
- +
- + sorry:
- + for (i = 0; i < nr; i++) {
- + if (bh[i])
- + bh[i]->b_dirt = bh[i]->b_uptodate = 0;
- + }
- + return;
- +}
- +
- +void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf)
- +{
- + int i;
- + int buffersize;
- + struct request * req;
- + unsigned int major = MAJOR(dev);
- + struct semaphore sem = MUTEX_LOCKED;
- +
- + if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
- + printk("ll_rw_swap_file: trying to swap nonexistent block-device\n");
- + return;
- + }
- +
- + if (rw!=READ && rw!=WRITE) {
- + printk("ll_rw_swap: bad block dev command, must be R/W");
- + return;
- + }
- + if (rw == WRITE && is_read_only(dev)) {
- + printk("Can't swap to read-only device 0x%X\n",dev);
- + return;
- + }
- + printk("ll_rw_swap: cannot swap to a swapfile!\n");
- +
- + return;
- + buffersize = PAGE_SIZE / nb;
- +
- + for (i=0; i<nb; i++, buf += buffersize)
- + {
- + cli();
- + req = get_request_wait(NR_REQUEST, dev);
- + sti();
- + req->cmd = rw;
- + req->errors = 0;
- + req->sector = (b[i] * buffersize) >> 9;
- + req->nr_sectors = buffersize >> 9;
- + req->current_nr_sectors = buffersize >> 9;
- + req->buffer = buf;
- + req->sem = &sem;
- + req->bh = NULL;
- + req->next = NULL;
- + add_request(major+blk_dev,req);
- + down(&sem);
- + }
- +}
- +
- +long blk_dev_init(long mem_start, long mem_end)
- +{
- + struct request * req;
- +
- + req = all_requests + NR_REQUEST;
- + while (--req >= all_requests) {
- + req->dev = -1;
- + req->next = NULL;
- + }
- + memset(ro_bits,0,sizeof(ro_bits));
- +#ifdef CONFIG_BLK_DEV_HD
- + mem_start = hd_init(mem_start,mem_end);
- +#endif
- +#ifdef CONFIG_BLK_DEV_IDE
- + mem_start = ide_init(mem_start,mem_end);
- +#endif
- +#ifdef CONFIG_BLK_DEV_XD
- + mem_start = xd_init(mem_start,mem_end);
- +#endif
- +#ifdef CONFIG_CDU31A
- + mem_start = cdu31a_init(mem_start,mem_end);
- +#endif
- +#ifdef CONFIG_CDU535
- + mem_start = sony535_init(mem_start,mem_end);
- +#endif
- +#ifdef CONFIG_MCD
- + mem_start = mcd_init(mem_start,mem_end);
- +#endif
- +#ifdef CONFIG_AZTCD
- + mem_start = aztcd_init(mem_start,mem_end);
- +#endif
- +#ifdef CONFIG_BLK_DEV_FD
- + floppy_init();
- +#endif
- +#ifdef CONFIG_SBPCD
- + mem_start = sbpcd_init(mem_start, mem_end);
- +#endif CONFIG_SBPCD
- + if (ramdisk_size)
- + mem_start += rd_init(mem_start, ramdisk_size*1024);
- + return mem_start;
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/block/ll_rw_blk.c.old linux.arm/arch/arm/drivers/block/ll_rw_blk.c.old
- --- linux.orig/arch/arm/drivers/block/ll_rw_blk.c.old Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/ll_rw_blk.c.old Fri Oct 27 23:14:50 1995
- @@ -0,0 +1,519 @@
- +/*
- + * linux/drivers/block/ll_rw_blk.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + * Copyright (C) 1994, Karl Keyte: Added support for disk statistics
- + */
- +
- +/*
- + * This handles all read/write requests to block devices
- + */
- +#include <linux/sched.h>
- +#include <linux/kernel.h>
- +#include <linux/kernel_stat.h>
- +#include <linux/errno.h>
- +#include <linux/string.h>
- +#include <linux/config.h>
- +#include <linux/locks.h>
- +
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include <asm/pgtable.h>
- +#include "blk.h"
- +
- +/*
- + * The request-struct contains all necessary data
- + * to load a nr of sectors into memory
- + */
- +static struct request all_requests[NR_REQUEST];
- +
- +/*
- + * used to wait on when there are no free requests
- + */
- +struct wait_queue * wait_for_request = NULL;
- +
- +/* This specifies how many sectors to read ahead on the disk. */
- +
- +int read_ahead[MAX_BLKDEV] = {0, };
- +
- +/* blk_dev_struct is:
- + * do_request-address
- + * next-request
- + */
- +struct blk_dev_struct blk_dev[MAX_BLKDEV] = {
- + { NULL, NULL }, /* no_dev */
- + { NULL, NULL }, /* dev mem */
- + { NULL, NULL }, /* dev fd */
- + { NULL, NULL }, /* dev hd */
- + { NULL, NULL }, /* dev ttyx */
- + { NULL, NULL }, /* dev tty */
- + { NULL, NULL }, /* dev lp */
- + { NULL, NULL }, /* dev pipes */
- + { NULL, NULL }, /* dev sd */
- + { NULL, NULL } /* dev st */
- +};
- +
- +/*
- + * blk_size contains the size of all block-devices in units of 1024 byte
- + * sectors:
- + *
- + * blk_size[MAJOR][MINOR]
- + *
- + * if (!blk_size[MAJOR]) then no minor size checking is done.
- + */
- +int * blk_size[MAX_BLKDEV] = { NULL, NULL, };
- +
- +/*
- + * blksize_size contains the size of all block-devices:
- + *
- + * blksize_size[MAJOR][MINOR]
- + *
- + * if (!blksize_size[MAJOR]) then 1024 bytes is assumed.
- + */
- +int * blksize_size[MAX_BLKDEV] = { NULL, NULL, };
- +
- +/*
- + * look for a free request in the first N entries.
- + * NOTE: interrupts must be disabled on the way in, and will still
- + * be disabled on the way out.
- + */
- +static inline struct request * get_request(int n, int dev)
- +{
- + static struct request *prev_found = NULL, *prev_limit = NULL;
- + register struct request *req, *limit;
- +
- + if (n <= 0)
- + panic("get_request(%d): impossible!\n", n);
- +
- + limit = all_requests + n;
- + if (limit != prev_limit) {
- + prev_limit = limit;
- + prev_found = all_requests;
- + }
- + req = prev_found;
- + for (;;) {
- + req = ((req > all_requests) ? req : limit) - 1;
- + if (req->dev < 0)
- + break;
- + if (req == prev_found)
- + return NULL;
- + }
- + prev_found = req;
- + req->dev = dev;
- + return req;
- +}
- +
- +/*
- + * wait until a free request in the first N entries is available.
- + * NOTE: interrupts must be disabled on the way in, and will still
- + * be disabled on the way out.
- + */
- +static inline struct request * get_request_wait(int n, int dev)
- +{
- + register struct request *req;
- +
- + while ((req = get_request(n, dev)) == NULL)
- + sleep_on(&wait_for_request);
- + return req;
- +}
- +
- +/* RO fail safe mechanism */
- +
- +static long ro_bits[MAX_BLKDEV][8];
- +
- +int is_read_only(int dev)
- +{
- + int minor,major;
- +
- + major = MAJOR(dev);
- + minor = MINOR(dev);
- + if (major < 0 || major >= MAX_BLKDEV) return 0;
- + return ro_bits[major][minor >> 5] & (1 << (minor & 31));
- +}
- +
- +void set_device_ro(int dev,int flag)
- +{
- + int minor,major;
- +
- + major = MAJOR(dev);
- + minor = MINOR(dev);
- + if (major < 0 || major >= MAX_BLKDEV) return;
- + if (flag) ro_bits[major][minor >> 5] |= 1 << (minor & 31);
- + else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
- +}
- +
- +/*
- + * add-request adds a request to the linked list.
- + * It disables interrupts so that it can muck with the
- + * request-lists in peace.
- + */
- +static void add_request(struct blk_dev_struct * dev, struct request * req)
- +{
- + struct request * tmp;
- + short disk_index;
- +
- + switch (MAJOR(req->dev)) {
- + case SCSI_DISK_MAJOR: disk_index = (MINOR(req->dev) & 0x0070) >> 4;
- + if (disk_index < 4)
- + kstat.dk_drive[disk_index]++;
- + break;
- + case HD_MAJOR:
- + case XT_DISK_MAJOR: disk_index = (MINOR(req->dev) & 0x00C0) >> 6;
- + if (disk_index < 4)
- + kstat.dk_drive[disk_index]++;
- + break;
- + default: break;
- + }
- +
- + req->next = NULL;
- + cli();
- + if (req->bh)
- + mark_buffer_clean(req->bh);
- + if (!(tmp = dev->current_request)) {
- + dev->current_request = req;
- + (dev->request_fn)();
- + sti();
- + return;
- + }
- + for ( ; tmp->next ; tmp = tmp->next) {
- + if ((IN_ORDER(tmp,req) ||
- + !IN_ORDER(tmp,tmp->next)) &&
- + IN_ORDER(req,tmp->next))
- + break;
- + }
- + req->next = tmp->next;
- + tmp->next = req;
- +
- +/* for SCSI devices, call request_fn unconditionally */
- + if (scsi_major(MAJOR(req->dev)))
- + (dev->request_fn)();
- +
- + sti();
- +}
- +
- +static void make_request(int major,int rw, struct buffer_head * bh)
- +{
- + unsigned int sector, count;
- + struct request * req;
- + int rw_ahead, max_req;
- +
- +/* WRITEA/READA is special case - it is not really needed, so if the */
- +/* buffer is locked, we just forget about it, else it's a normal read */
- + rw_ahead = (rw == READA || rw == WRITEA);
- + if (rw_ahead) {
- + if (bh->b_lock)
- + return;
- + if (rw == READA)
- + rw = READ;
- + else
- + rw = WRITE;
- + }
- + if (rw!=READ && rw!=WRITE) {
- + printk("Bad block dev command, must be R/W/RA/WA\n");
- + return;
- + }
- + count = bh->b_size >> 9;
- + sector = bh->b_blocknr * count;
- + if (blk_size[major])
- + if (blk_size[major][MINOR(bh->b_dev)] < (sector + count)>>1) {
- + bh->b_dirt = bh->b_uptodate = 0;
- + bh->b_req = 0;
- + return;
- + }
- + lock_buffer(bh);
- + if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {
- + unlock_buffer(bh);
- + return;
- + }
- +
- +/* we don't allow the write-requests to fill up the queue completely:
- + * we want some room for reads: they take precedence. The last third
- + * of the requests are only for reads.
- + */
- + max_req = (rw == READ) ? NR_REQUEST : ((NR_REQUEST*2)/3);
- +
- +/* big loop: look for a free request. */
- +
- +repeat:
- + cli();
- +
- +/* The scsi disk drivers completely remove the request from the queue when
- + * they start processing an entry. For this reason it is safe to continue
- + * to add links to the top entry for scsi devices.
- + */
- + if ((major == HD_MAJOR
- + || major == FLOPPY_MAJOR
- + || major == SCSI_DISK_MAJOR
- + || major == SCSI_CDROM_MAJOR)
- + && (req = blk_dev[major].current_request))
- + {
- + if (major == HD_MAJOR || major == FLOPPY_MAJOR)
- + req = req->next;
- + while (req) {
- + if (req->dev == bh->b_dev &&
- + !req->sem &&
- + req->cmd == rw &&
- + req->sector + req->nr_sectors == sector &&
- + req->nr_sectors < 244)
- + {
- + req->bhtail->b_reqnext = bh;
- + req->bhtail = bh;
- + req->nr_sectors += count;
- + mark_buffer_clean(bh);
- + sti();
- + return;
- + }
- +
- + if (req->dev == bh->b_dev &&
- + !req->sem &&
- + req->cmd == rw &&
- + req->sector - count == sector &&
- + req->nr_sectors < 244)
- + {
- + req->nr_sectors += count;
- + bh->b_reqnext = req->bh;
- + req->buffer = bh->b_data;
- + req->current_nr_sectors = count;
- + req->sector = sector;
- + mark_buffer_clean(bh);
- + req->bh = bh;
- + sti();
- + return;
- + }
- +
- + req = req->next;
- + }
- + }
- +
- +/* find an unused request. */
- + req = get_request(max_req, bh->b_dev);
- +
- +/* if no request available: if rw_ahead, forget it; otherwise try again. */
- + if (! req) {
- + if (rw_ahead) {
- + sti();
- + unlock_buffer(bh);
- + return;
- + }
- + sleep_on(&wait_for_request);
- + sti();
- + goto repeat;
- + }
- +
- +/* we found a request. */
- + sti();
- +
- +/* fill up the request-info, and add it to the queue */
- + req->cmd = rw;
- + req->errors = 0;
- + req->sector = sector;
- + req->nr_sectors = count;
- + req->current_nr_sectors = count;
- + req->buffer = bh->b_data;
- + req->sem = NULL;
- + req->bh = bh;
- + req->bhtail = bh;
- + req->next = NULL;
- + add_request(major+blk_dev,req);
- +}
- +
- +void ll_rw_page(int rw, int dev, int page, char * buffer)
- +{
- + struct request * req;
- + unsigned int major = MAJOR(dev);
- + struct semaphore sem = MUTEX_LOCKED;
- +
- + if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
- + printk("Trying to read nonexistent block-device %04x (%d)\n",dev,page*8);
- + return;
- + }
- + if (rw!=READ && rw!=WRITE)
- + panic("Bad block dev command, must be R/W");
- + if (rw == WRITE && is_read_only(dev)) {
- + printk("Can't page to read-only device 0x%X\n",dev);
- + return;
- + }
- + cli();
- + req = get_request_wait(NR_REQUEST, dev);
- + sti();
- +/* fill up the request-info, and add it to the queue */
- + req->cmd = rw;
- + req->errors = 0;
- + req->sector = page << (PAGE_SHIFT-9);
- + req->nr_sectors = 1 << (PAGE_SHIFT-9);
- + req->current_nr_sectors = 1 << (PAGE_SHIFT-9);
- + req->buffer = buffer;
- + req->sem = &sem;
- + req->bh = NULL;
- + req->next = NULL;
- + add_request(major+blk_dev,req);
- + down(&sem);
- +}
- +
- +/* This function can be used to request a number of buffers from a block
- + device. Currently the only restriction is that all buffers must belong to
- + the same device */
- +
- +void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
- +{
- + unsigned int major;
- + struct request plug;
- + int plugged;
- + int correct_size;
- + struct blk_dev_struct * dev;
- + int i;
- +
- + /* Make sure that the first block contains something reasonable */
- + while (!*bh) {
- + bh++;
- + if (--nr <= 0)
- + return;
- + };
- +
- + dev = NULL;
- + if ((major = MAJOR(bh[0]->b_dev)) < MAX_BLKDEV)
- + dev = blk_dev + major;
- + if (!dev || !dev->request_fn) {
- + printk(
- + "ll_rw_block: Trying to read nonexistent block-device %04lX (%ld)\n",
- + (unsigned long) bh[0]->b_dev, bh[0]->b_blocknr);
- + goto sorry;
- + }
- +
- + /* Determine correct block size for this device. */
- + correct_size = BLOCK_SIZE;
- + if (blksize_size[major]) {
- + i = blksize_size[major][MINOR(bh[0]->b_dev)];
- + if (i)
- + correct_size = i;
- + }
- +
- + /* Verify requested block sizes. */
- + for (i = 0; i < nr; i++) {
- + if (bh[i] && bh[i]->b_size != correct_size) {
- + printk(
- + "ll_rw_block: only %d-char blocks implemented (%lu)\n",
- + correct_size, bh[i]->b_size);
- + goto sorry;
- + }
- + }
- +
- + if ((rw == WRITE || rw == WRITEA) && is_read_only(bh[0]->b_dev)) {
- + printk("Can't write to read-only device 0x%X\n",bh[0]->b_dev);
- + goto sorry;
- + }
- +
- + /* If there are no pending requests for this device, then we insert
- + a dummy request for that device. This will prevent the request
- + from starting until we have shoved all of the blocks into the
- + queue, and then we let it rip. */
- +
- + plugged = 0;
- + cli();
- + if (!dev->current_request && nr > 1) {
- + dev->current_request = &plug;
- + plug.dev = -1;
- + plug.next = NULL;
- + plugged = 1;
- + }
- + sti();
- + for (i = 0; i < nr; i++) {
- + if (bh[i]) {
- + bh[i]->b_req = 1;
- + make_request(major, rw, bh[i]);
- + if (rw == READ || rw == READA)
- + kstat.pgpgin++;
- + else
- + kstat.pgpgout++;
- + }
- + }
- + if (plugged) {
- + cli();
- + dev->current_request = plug.next;
- + (dev->request_fn)();
- + sti();
- + }
- + return;
- +
- + sorry:
- + for (i = 0; i < nr; i++) {
- + if (bh[i])
- + bh[i]->b_dirt = bh[i]->b_uptodate = 0;
- + }
- + return;
- +}
- +
- +void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf)
- +{
- + int i;
- + int buffersize;
- + struct request * req;
- + unsigned int major = MAJOR(dev);
- + struct semaphore sem = MUTEX_LOCKED;
- +
- + if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
- + printk("ll_rw_swap_file: trying to swap nonexistent block-device\n");
- + return;
- + }
- +
- + if (rw!=READ && rw!=WRITE) {
- + printk("ll_rw_swap: bad block dev command, must be R/W");
- + return;
- + }
- + if (rw == WRITE && is_read_only(dev)) {
- + printk("Can't swap to read-only device 0x%X\n",dev);
- + return;
- + }
- + printk("ll_rw_swap: cannot swap to a swapfile!\n");
- + return;
- +
- + buffersize = PAGE_SIZE / nb;
- +
- + for (i=0; i<nb; i++, buf += buffersize)
- + {
- + cli();
- + req = get_request_wait(NR_REQUEST, dev);
- + sti();
- + req->cmd = rw;
- + req->errors = 0;
- + req->sector = (b[i] * buffersize) >> 9;
- + req->nr_sectors = buffersize >> 9;
- + req->current_nr_sectors = buffersize >> 9;
- + req->buffer = buf;
- + req->sem = &sem;
- + req->bh = NULL;
- + req->next = NULL;
- + add_request(major+blk_dev,req);
- + down(&sem);
- + }
- +}
- +
- +long blk_dev_init(long mem_start, long mem_end)
- +{
- + struct request * req;
- +
- + req = all_requests + NR_REQUEST;
- + while (--req >= all_requests) {
- + req->dev = -1;
- + req->next = NULL;
- + }
- + memset(ro_bits,0,sizeof(ro_bits));
- +#ifdef CONFIG_BLK_DEV_HD
- + mem_start = hd_init(mem_start,mem_end);
- +#endif
- +#ifdef CONFIG_BLK_DEV_XD
- + mem_start = xd_init(mem_start,mem_end);
- +#endif
- +#ifdef CONFIG_CDU31A
- + mem_start = cdu31a_init(mem_start,mem_end);
- +#endif
- +#ifdef CONFIG_MCD
- + mem_start = mcd_init(mem_start,mem_end);
- +#endif
- +#ifdef CONFIG_SBPCD
- + mem_start = sbpcd_init(mem_start, mem_end);
- +#endif CONFIG_SBPCD
- + if (ramdisk_size)
- + mem_start += rd_init(mem_start, ramdisk_size*1024);
- + return mem_start;
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/block/mfmhd.c linux.arm/arch/arm/drivers/block/mfmhd.c
- --- linux.orig/arch/arm/drivers/block/mfmhd.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/mfmhd.c Fri Oct 27 23:14:51 1995
- @@ -0,0 +1,770 @@
- +#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 -r -u -N linux.orig/arch/arm/drivers/block/ramdisk.c linux.arm/arch/arm/drivers/block/ramdisk.c
- --- linux.orig/arch/arm/drivers/block/ramdisk.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/block/ramdisk.c Fri Oct 27 23:14:45 1995
- @@ -0,0 +1,234 @@
- +/*
- + * linux/kernel/blk_drv/ramdisk.c
- + *
- + * Written by Theodore Ts'o, 12/2/91
- + *
- + * Modifications by Fred N. van Kempen to allow for bootable root
- + * disks (which are used in LINUX/Pro). Also some cleanups. 03/03/93
- + */
- +
- +
- +#include <linux/sched.h>
- +#include <linux/minix_fs.h>
- +#include <linux/ext2_fs.h>
- +#include <linux/fs.h>
- +#include <linux/kernel.h>
- +#include <linux/string.h>
- +#include <linux/mm.h>
- +
- +#include <asm/system.h>
- +#include <asm/segment.h>
- +
- +#define MAJOR_NR MEM_MAJOR
- +#include "blk.h"
- +
- +#define RAMDISK_MINOR 1
- +
- +extern void wait_for_keypress(void);
- +
- +char *rd_start;
- +int rd_length = 0;
- +static int rd_blocksizes[2] = {0, 0};
- +
- +static void do_rd_request(void)
- +{
- + int len;
- + char *addr;
- +
- +repeat:
- + INIT_REQUEST;
- + addr = rd_start + (CURRENT->sector << 9);
- + len = CURRENT->current_nr_sectors << 9;
- +
- + if ((MINOR(CURRENT->dev) != RAMDISK_MINOR) ||
- + (addr+len > rd_start+rd_length)) {
- + end_request(0);
- + goto repeat;
- + }
- + if (CURRENT-> cmd == WRITE) {
- + (void ) memcpy(addr,
- + CURRENT->buffer,
- + len);
- + } else if (CURRENT->cmd == READ) {
- + (void) memcpy(CURRENT->buffer,
- + addr,
- + len);
- + } else
- + panic("RAMDISK: unknown RAM disk command !\n");
- + end_request(1);
- + goto repeat;
- +}
- +
- +static struct file_operations rd_fops = {
- + NULL, /* lseek - default */
- + block_read, /* read - general block-dev read */
- + block_write, /* write - general block-dev write */
- + NULL, /* readdir - bad */
- + NULL, /* select */
- + NULL, /* ioctl */
- + NULL, /* mmap */
- + NULL, /* no special open code */
- + NULL, /* no special release code */
- + block_fsync /* fsync */
- +};
- +
- +/*
- + * Returns amount of memory which needs to be reserved.
- + */
- +long rd_init(long mem_start, int length)
- +{
- + int i;
- + char *cp;
- +
- + if (register_blkdev(MEM_MAJOR,"rd",&rd_fops)) {
- + printk("RAMDISK: Unable to get major %d.\n", MEM_MAJOR);
- + return 0;
- + }
- + blk_dev[MEM_MAJOR].request_fn = DEVICE_REQUEST;
- + rd_start = (char *) mem_start;
- + rd_length = length;
- + cp = rd_start;
- + for (i=0; i < length; i++)
- + *cp++ = '\0';
- +
- + for(i=0;i<2;i++) rd_blocksizes[i] = 1024;
- + blksize_size[MAJOR_NR] = rd_blocksizes;
- +
- + return(length);
- +}
- +
- +static void do_load(void)
- +{
- + struct buffer_head *bh;
- + struct super_block {
- + union
- + {
- + char minix [sizeof (struct minix_super_block)];
- + char ext2 [sizeof (struct ext2_super_block)];
- + } record;
- + } sb;
- + struct minix_super_block *minixsb =
- + (struct minix_super_block *)&sb;
- + struct ext2_super_block *ext2sb =
- + (struct ext2_super_block *)&sb;
- + int block, tries;
- + int i = 1;
- + int nblocks;
- + char *cp;
- +
- + /*
- + * Check for a super block on the diskette.
- + * The old-style boot/root diskettes had their RAM image
- + * starting at block 512 of the boot diskette. LINUX/Pro
- + * uses the entire diskette as a file system, so in that
- + * case, we have to look at block 0. Be intelligent about
- + * this, and check both... - FvK
- + */
- + for (tries = 0; tries < 1000; tries += 512) {
- + block = tries;
- + bh = breada(ROOT_DEV,block+1,BLOCK_SIZE, 0, PAGE_SIZE);
- + if (!bh) {
- + printk("RAMDISK: I/O error while looking for super block!\n");
- + return;
- + }
- +
- + /* This is silly- why do we require it to be a MINIX FS? */
- + *((struct super_block *) &sb) =
- + *((struct super_block *) bh->b_data);
- + brelse(bh);
- +
- +
- + /* Try Minix */
- + nblocks = -1;
- + if (minixsb->s_magic == MINIX_SUPER_MAGIC ||
- + minixsb->s_magic == MINIX_SUPER_MAGIC2) {
- + printk("RAMDISK: Minix filesystem found at block %d\n",
- + block);
- + nblocks = minixsb->s_nzones << minixsb->s_log_zone_size;
- + }
- +
- + /* Try ext2 */
- + if (nblocks == -1 && (ext2sb->s_magic ==
- + EXT2_PRE_02B_MAGIC ||
- + ext2sb->s_magic == EXT2_SUPER_MAGIC))
- + {
- + printk("RAMDISK: Ext2 filesystem found at block %d\n",
- + block);
- + nblocks = ext2sb->s_blocks_count;
- + }
- +
- + if (nblocks == -1)
- + {
- + printk("RAMDISK: trying old-style RAM image.\n");
- + continue;
- + }
- +
- + if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) {
- + printk("RAMDISK: image too big! (%d/%d blocks)\n",
- + nblocks, rd_length >> BLOCK_SIZE_BITS);
- + return;
- + }
- + printk("RAMDISK: Loading %d blocks into RAM disk", nblocks);
- +
- + /* We found an image file system. Load it into core! */
- + cp = rd_start;
- + while (nblocks) {
- + if (nblocks > 2)
- + bh = breada(ROOT_DEV, block, BLOCK_SIZE, 0, PAGE_SIZE);
- + else
- + bh = bread(ROOT_DEV, block, BLOCK_SIZE);
- + if (!bh) {
- + printk("RAMDISK: I/O error on block %d, aborting!\n",
- + block);
- + return;
- + }
- + (void) memcpy(cp, bh->b_data, BLOCK_SIZE);
- + brelse(bh);
- + if (!(nblocks-- & 15)) printk(".");
- + cp += BLOCK_SIZE;
- + block++;
- + i++;
- + }
- + printk("\ndone\n");
- +
- + /* We loaded the file system image. Prepare for mounting it. */
- + ROOT_DEV = ((MEM_MAJOR << 8) | RAMDISK_MINOR);
- + return;
- + }
- +}
- +
- +/*
- + * If the root device is the RAM disk, try to load it.
- + * In order to do this, the root device is originally set to the
- + * floppy, and we later change it to be RAM disk.
- + */
- +void rd_load(void)
- +{
- + struct inode inode;
- + struct file filp;
- +
- + /* If no RAM disk specified, give up early. */
- + if (!rd_length)
- + return;
- + printk("RAMDISK: %d bytes, starting at 0x%x\n",
- + rd_length, (int) rd_start);
- +
- + /* If we are doing a diskette boot, we might have to pre-load it. */
- + if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR)
- + return;
- +
- + /* for Slackware install disks */
- + printk(KERN_NOTICE "VFS: Insert ramdisk floppy and press ENTER\n");
- + wait_for_keypress();
- +
- + memset(&filp, 0, sizeof(filp));
- + memset(&inode, 0, sizeof(inode));
- + inode.i_rdev = ROOT_DEV;
- + filp.f_mode = 1; /* read only */
- + filp.f_inode = &inode;
- + if(blkdev_open(&inode, &filp) == 0 ){
- + do_load();
- + if(filp.f_op && filp.f_op->release)
- + filp.f_op->release(&inode,&filp);
- + }
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/char/Makefile linux.arm/arch/arm/drivers/char/Makefile
- --- linux.orig/arch/arm/drivers/char/Makefile Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/Makefile Fri Oct 27 23:14:21 1995
- @@ -0,0 +1,81 @@
- +#
- +# 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..
- +#
- +
- +.c.s:
- + $(CC) $(CFLAGS) -S $<
- +.s.o:
- + $(AS) -c -o $*.o $<
- +.c.o:
- + $(CC) $(CFLAGS) -c $<
- +
- +OBJS = tty_io.o n_tty.o console.o keyboard.o serial.o \
- + tty_ioctl.o pty.o vt.o mem.o \
- + defkeymap.o consolemap.o arcmouse.o ptr.o iic.o \
- + vc_screen.o
- +# vesa_blank.o selection.o
- +
- +SRCS = tty_io.c n_tty.c console.c keyboard.c serial.c \
- + tty_ioctl.c pty.c vt.c mem.c \
- + defkeymap.c consolemap.c arcmouse.c ptr.c iic.c \
- + vc_screen.c
- +#vc_screen.c \
- +# defkeymap.c vesa_blank.c selection.c
- +
- +
- +ifdef CONFIG_PRINTER
- +OBJS := $(OBJS) lp.o
- +SRCS := $(SRCS) lp.c
- +else
- +MODULES := $(MODULES) lp.o
- +endif
- +
- +ifdef CONFIG_ARMMOUSE
- +OBJS := $(OBJS) arcmouse.o
- +SRCS := $(SRCS) arcmouse.c
- +M = 1
- +endif
- +
- +ifdef M
- +OBJS := $(OBJS) mouse.o
- +SRCS := $(SRCS) mouse.c
- +endif
- +
- +all: char.a
- +
- +char.a: $(OBJS)
- + $(AR) rcs char.a $(OBJS)
- + sync
- +
- +ifdef MODULES
- +
- +modules: $(MODULES)
- + (cd ../../modules;for i in $(MODULES); do ln -sf ../drivers/char/$$i .; done)
- +
- +else
- +
- +modules:
- +
- +endif
- +
- +dep:
- + $(CPP) -M $(SRCS) > .depend
- +ifdef MODULES
- + $(CPP) -M -DMODULE $(MODULES:.o=.c) >> .depend
- +endif
- +
- +dummy:
- +
- +#
- +# include a dependency file if one exists
- +#
- +ifeq (.depend,$(wildcard .depend))
- +include .depend
- +endif
- diff -r -u -N linux.orig/arch/arm/drivers/char/arcmouse.c linux.arm/arch/arm/drivers/char/arcmouse.c
- --- linux.orig/arch/arm/drivers/char/arcmouse.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/arcmouse.c Fri Oct 27 23:14:22 1995
- @@ -0,0 +1,140 @@
- +#include <linux/config.h>
- +#include <linux/kernel.h>
- +#include <linux/sched.h>
- +#include <linux/errno.h>
- +
- +#include <asm/system.h>
- +#include <asm/segment.h>
- +
- +#pragma no_check_stack
- +
- +char mouse_buttons;
- +int mouse_dxpos;
- +int mouse_dypos;
- +int mouse_present;
- +char mouse_ready;
- +char mouse_active;
- +struct wait_queue *mouse_wait;
- +
- +void arch_mouse_close(struct inode *inode,struct file *file)
- +{
- + mouse_active=0;
- + mouse_ready =0;
- + return;
- +}
- +
- +int arch_mouse_open(struct inode *inode,struct file *file)
- +{
- + unsigned long flags;
- +
- + if(!mouse_present)
- + return -EINVAL;
- +
- + if(mouse_active)
- + return -EBUSY;
- +
- + save_flags(flags);
- + cli();
- +
- + mouse_active =1;
- + mouse_ready =0;
- + mouse_dxpos =0;
- + mouse_dypos =0;
- + mouse_buttons=0;
- +
- + restore_flags(flags);
- +
- + return 0;
- +}
- +
- +int arch_mouse_read(struct inode *inode,struct file *file,char *buffer,int count)
- +{
- + unsigned long flags;
- + int dxpos,dypos,i,buttons;
- +
- + if(count<3)
- + return -EINVAL;
- +
- + 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;
- +}
- +
- +int arch_mouse_write(struct inode *inode,struct file *file,char *buffer,int count)
- +{
- + return -EINVAL;
- +}
- +
- +int arch_mouse_select(struct inode *inode,struct file *file,int sel_type,select_table *wait)
- +{
- + if(sel_type!=SEL_IN)
- + return 0;
- +
- + if(mouse_ready)
- + return 1;
- +
- + select_wait(&mouse_wait,wait);
- +
- + return 0;
- +}
- +
- +struct file_operations arch_mouse_fops=
- +{
- + NULL, /* mouse_seek */
- + arch_mouse_read,
- + arch_mouse_write,
- + NULL, /* mouse_readdir */
- + arch_mouse_select,
- + NULL, /* mouse_ioctl */
- + NULL, /* mouse_mmap */
- + arch_mouse_open,
- + arch_mouse_close
- +};
- +
- +unsigned long arch_mouse_init(unsigned long kmem_start)
- +{
- + 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);
- + return kmem_start;
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/char/console.c linux.arm/arch/arm/drivers/char/console.c
- --- linux.orig/arch/arm/drivers/char/console.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/console.c Fri Oct 27 23:14:30 1995
- @@ -0,0 +1,2663 @@
- +/*
- + * linux/arch/arm/drivers/char/console.c
- + */
- +
- +/*
- + * arcscr.c
- + *
- + * 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)'
- + *
- + * 'long con_init(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 0xd00ff80
- +
- +#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 int conv_uni_to_pc(unsigned long ucs);
- +
- +/* 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 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 char * 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 */
- + 0x100D, /* Red */
- + 0x10D0, /* Green */
- + 0x10DD, /* Yellow */
- + 0x1D00, /* Blue */
- + 0x1D0D, /* Magenta */
- + 0x1DD0, /* Cyan */
- + 0x1DDD, /* 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, 0x1B, 0x63, 0x7B, 0x87, 0x9F, 0xE7, 0xFF,
- + 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(NULL_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(NULL_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);
- + G0_charset = saved_G0;
- + G1_charset = saved_G1;
- + intensity = s_intensity;
- + blink = s_blink;
- + underline = s_underline;
- + reverse = s_reverse;
- + forecol = s_forecol;
- + backcol = s_backcol;
- + need_wrap = 0;
- +}
- +
- +enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
- + EShash, ESsetG0, ESsetG1, ESpercent, ESignore };
- +
- +static void reset_terminal(int currcons,int do_clear)
- +{
- + top = 0;
- + bottom = video_num_lines;
- + vc_state = ESnormal;
- + ques = 0;
- + translate = set_translate(NORM_MAP);
- + G0_charset = NORM_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,
- + 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)
- + 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
- + utf_count = 0;
- + continue;
- + }
- + } else
- + utf_count = 0;
- +
- + tc = conv_uni_to_pc(c);
- + if (tc == -1 || tc == -2)
- + continue;
- + if (tc == -3 || tc == -4) { /* hashtable not valid */
- + /* or symbol not found */
- + tc = (c <= 0xff) ? translate[c] : 040;
- + ok = 0;
- + } else
- + ok = 1;
- + } else { /* no utf */
- + tc = translate[toggle_meta ? (c|0x80) : c];
- + ok = 0;
- + }
- +
- + /* 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!
- + */
- + if (!ok && tc && (c >= 32 || (disp_ctrl && c !=0x1b)
- + || !((CTRL_ACTION >> c) & 1)))
- + ok = 1;
- +
- + if (vc_state == ESnormal && ok) {
- + if (need_wrap) {
- + cr(currcons);
- + lf(currcons);
- + }
- + if(decim)
- + insert_char(currcons);
- + charwrite(currcons, pos, translate[c]);
- + 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 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 = NORM_MAP;
- + else if (c == 'U')
- + G0_charset = NULL_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 = NORM_MAP;
- + else if (c == 'U')
- + G1_charset = NULL_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);
- +}
- +
- +/*
- + * long con_init(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.
- + *
- + */
- +long con_init(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;
- +}
- +
- +/* == 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 -r -u -N linux.orig/arch/arm/drivers/char/consolemap.c linux.arm/arch/arm/drivers/char/consolemap.c
- --- linux.orig/arch/arm/drivers/char/consolemap.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/consolemap.c Fri Oct 27 23:14:30 1995
- @@ -0,0 +1,286 @@
- +/*
- + * consolemap.c
- + *
- + * Mapping from internal code (such as Latin-1 or Unicode or IBM PC code)
- + * to font positions.
- + *
- + * aeb, 950210
- + */
- +
- +#include <linux/kd.h>
- +#include <linux/errno.h>
- +#include <linux/mm.h>
- +#include <linux/malloc.h>
- +#include <asm/segment.h>
- +#include "consolemap.h"
- +
- +static unsigned char * translations[] = {
- +/* 8-bit Latin-1 mapped to the PC character set: '\0' means non-printable */
- +(unsigned char *)
- + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- + "\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0"
- + " !\"#$%&'()*+,-./0123456789:;<=>?"
- + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
- + "`abcdefghijklmnopqrstuvwxyz{|}~\0"
- + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- + "\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376"
- + "\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250"
- + "\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376"
- + "\376\245\376\376\376\376\231\376\350\376\376\376\232\376\376\341"
- + "\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
- + "\376\244\225\242\223\376\224\366\355\227\243\226\201\376\376\230",
- +/* vt100 graphics */
- +(unsigned char *)
- + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- + "\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0"
- + " !\"#$%&'()*+,-./0123456789:;<=>?"
- + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ "
- + "\004\261\007\007\007\007\370\361\007\007\331\277\332\300\305\304"
- + "\304\304\137\137\303\264\301\302\263\363\362\343\330\234\007\0"
- + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- + "\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376"
- + "\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250"
- + "\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376"
- + "\376\245\376\376\376\376\231\376\376\376\376\376\232\376\376\341"
- + "\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
- + "\376\244\225\242\223\376\224\366\376\227\243\226\201\376\376\230",
- +/* IBM graphics: minimal translations (BS, CR, LF, LL, SO, SI and ESC) */
- +(unsigned char *)
- + "\000\001\002\003\004\005\006\007\000\011\000\013\000\000\000\000"
- + "\020\021\022\023\024\025\026\027\030\031\032\000\034\035\036\037"
- + "\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
- + "\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077"
- + "\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117"
- + "\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137"
- + "\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157"
- + "\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177"
- + "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"
- + "\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"
- + "\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"
- + "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"
- + "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
- + "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
- + "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
- + "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377",
- + /* USER: customizable mappings, initialized as the previous one (IBM) */
- +(unsigned char *)
- + "\000\001\002\003\004\005\006\007\010\011\000\013\000\000\016\017"
- + "\020\021\022\023\024\025\026\027\030\031\032\000\034\035\036\037"
- + "\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
- + "\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077"
- + "\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117"
- + "\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137"
- + "\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157"
- + "\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177"
- + "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"
- + "\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"
- + "\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"
- + "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"
- + "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
- + "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
- + "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
- + "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377"
- +};
- +
- +/* the above mappings are not invertible - this is just a best effort */
- +static unsigned char * inv_translate = NULL;
- +static unsigned char inv_norm_transl[E_TABSZ];
- +static unsigned char * inverse_translations[4] = { NULL, NULL, NULL, NULL };
- +
- +static void set_inverse_transl(int i)
- +{
- + int j;
- + unsigned char *p = translations[i];
- + unsigned char *q = inverse_translations[i];
- +
- + if (!q) {
- + /* slightly messy to avoid calling kmalloc too early */
- + q = inverse_translations[i] = ((i == NORM_MAP)
- + ? inv_norm_transl
- + : (unsigned char *) kmalloc(E_TABSZ, GFP_KERNEL));
- + if (!q)
- + return;
- + }
- + for (j=0; j<E_TABSZ; j++)
- + q[j] = 0;
- + for (j=0; j<E_TABSZ; j++)
- + if (q[p[j]] < 32) /* prefer '-' above SHY etc. */
- + q[p[j]] = j;
- +}
- +
- +unsigned char *set_translate(int m)
- +{
- + if (!inverse_translations[m])
- + set_inverse_transl(m);
- + inv_translate = inverse_translations[m];
- + return translations[m];
- +}
- +
- +/*
- + * Inverse translation is impossible for several reasons:
- + * 1. The translation maps are not 1-1
- + * 2. The text may have been written while a different translation map
- + * was active
- + * Still, it is now possible to a certain extent to cut and paste non-ASCII.
- + */
- +unsigned char inverse_translate(unsigned char c) {
- + return ((inv_translate && inv_translate[c]) ? inv_translate[c] : c);
- +}
- +
- +/*
- + * Load customizable translation table
- + * arg points to a 256 byte translation table.
- + */
- +int con_set_trans(char * arg)
- +{
- + int i;
- + unsigned char *p = translations[USER_MAP];
- +
- + i = verify_area(VERIFY_READ, (void *)arg, E_TABSZ);
- + if (i)
- + return i;
- +
- + for (i=0; i<E_TABSZ ; i++)
- + p[i] = get_fs_byte(arg+i);
- + p[012] = p[014] = p[015] = p[033] = 0;
- + set_inverse_transl(USER_MAP);
- + return 0;
- +}
- +
- +int con_get_trans(char * arg)
- +{
- + int i;
- + unsigned char *p = translations[USER_MAP];
- +
- + i = verify_area(VERIFY_WRITE, (void *)arg, E_TABSZ);
- + if (i)
- + return i;
- +
- + for (i=0; i<E_TABSZ ; i++) put_fs_byte(p[i],arg+i);
- + return 0;
- +}
- +
- +/*
- + * Unicode -> current font conversion
- + *
- + * A font has at most 512 chars, usually 256.
- + * But one font position may represent several Unicode chars
- + * (and moreover, hashtables work best when they are not too full),
- + * so pick HASHSIZE somewhat larger than 512.
- + * Since there are likely to be long consecutive stretches
- + * (like U+0000 to U+00FF), HASHSTEP should not be too small.
- + * Searches longer than MAXHASHLEVEL steps are refused, unless
- + * requested explicitly.
- + *
- + * Note: no conversion tables are compiled in, so the user
- + * must supply an explicit mapping herself. See kbd-0.90 (or an
- + * earlier kernel version) for the default Unicode-to-PC mapping.
- + * Usually, the mapping will be loaded simultaneously with the font.
- + */
- +
- +#define HASHSIZE 641
- +#define HASHSTEP 189 /* yields hashlevel = 3 initially */
- +#define MAXHASHLEVEL 6
- +static struct unipair hashtable[HASHSIZE];
- +
- +int hashtable_contents_valid = 0; /* cleared by setfont */
- +
- +static unsigned int hashsize;
- +static unsigned int hashstep;
- +static unsigned int hashlevel;
- +static unsigned int maxhashlevel;
- +
- +void
- +con_clear_unimap(struct unimapinit *ui) {
- + int i;
- +
- + /* read advisory values for hash algorithm */
- + hashsize = ui->advised_hashsize;
- + if (hashsize < 256 || hashsize > HASHSIZE)
- + hashsize = HASHSIZE;
- + hashstep = (ui->advised_hashstep % hashsize);
- + if (hashstep < 64)
- + hashstep = HASHSTEP;
- + maxhashlevel = ui->advised_hashlevel;
- + if (!maxhashlevel)
- + maxhashlevel = MAXHASHLEVEL;
- + if (maxhashlevel > hashsize)
- + maxhashlevel = hashsize;
- +
- + /* initialize */
- + hashlevel = 0;
- + for (i=0; i<hashsize; i++)
- + hashtable[i].unicode = 0xffff;
- + hashtable_contents_valid = 1;
- +}
- +
- +int
- +con_set_unimap(ushort ct, struct unipair *list){
- + int i, lct;
- + ushort u, hu;
- + struct unimapinit hashdefaults = { 0, 0, 0 };
- +
- + if (!hashtable_contents_valid)
- + con_clear_unimap(&hashdefaults);
- + while(ct) {
- + u = get_fs_word(&list->unicode);
- + i = u % hashsize;
- + lct = 1;
- + while ((hu = hashtable[i].unicode) != 0xffff && hu != u) {
- + if (lct++ >= maxhashlevel)
- + return -ENOMEM;
- + i += hashstep;
- + if (i >= hashsize)
- + i -= hashsize;
- + }
- + if (lct > hashlevel)
- + hashlevel = lct;
- + hashtable[i].unicode = u;
- + hashtable[i].fontpos = get_fs_word(&list->fontpos);
- + list++;
- + ct--;
- + }
- + return 0;
- +}
- +
- +int
- +con_get_unimap(ushort ct, ushort *uct, struct unipair *list){
- + int i, ect;
- +
- + ect = 0;
- + if (hashtable_contents_valid)
- + for (i = 0; i<hashsize; i++)
- + if (hashtable[i].unicode != 0xffff) {
- + if (ect++ < ct) {
- + put_fs_word(hashtable[i].unicode, &list->unicode);
- + put_fs_word(hashtable[i].fontpos, &list->fontpos);
- + list++;
- + }
- + }
- + put_fs_word(ect, uct);
- + return ((ect <= ct) ? 0 : -ENOMEM);
- +}
- +
- +int
- +conv_uni_to_pc(unsigned long ucs) {
- + int i, h;
- +
- + if (!hashtable_contents_valid || ucs < 0x20)
- + return -3;
- + if (ucs == 0xffff || ucs == 0xfffe)
- + return -1;
- + if (ucs == 0xfeff || (ucs >= 0x200a && ucs <= 0x200f))
- + return -2;
- +
- + h = ucs % hashsize;
- + for (i = 0; i < hashlevel; i++) {
- + if (hashtable[h].unicode == ucs)
- + return hashtable[h].fontpos;
- + if ((h += hashstep) >= hashsize)
- + h -= hashsize;
- + }
- +
- + return -4; /* not found */
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/char/consolemap.h linux.arm/arch/arm/drivers/char/consolemap.h
- --- linux.orig/arch/arm/drivers/char/consolemap.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/consolemap.h Fri Oct 27 23:14:39 1995
- @@ -0,0 +1,13 @@
- +/*
- + * consolemap.h
- + *
- + * Interface between console.c, selection.c and consolemap.c
- + */
- +#define NORM_MAP 0
- +#define GRAF_MAP 1
- +#define NULL_MAP 2
- +#define USER_MAP 3
- +
- +extern int hashtable_contents_valid;
- +extern unsigned char inverse_translate(unsigned char c);
- +extern unsigned char *set_translate(int m);
- diff -r -u -N linux.orig/arch/arm/drivers/char/defkeymap.c linux.arm/arch/arm/drivers/char/defkeymap.c
- --- linux.orig/arch/arm/drivers/char/defkeymap.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/defkeymap.c Fri Oct 27 23:14:20 1995
- @@ -0,0 +1,331 @@
- +#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 -r -u -N linux.orig/arch/arm/drivers/char/diacr.h linux.arm/arch/arm/drivers/char/diacr.h
- --- linux.orig/arch/arm/drivers/char/diacr.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/diacr.h Fri Oct 27 23:14:20 1995
- @@ -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 -r -u -N linux.orig/arch/arm/drivers/char/iic.c linux.arm/arch/arm/drivers/char/iic.c
- --- linux.orig/arch/arm/drivers/char/iic.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/iic.c Fri Oct 27 23:14:20 1995
- @@ -0,0 +1,102 @@
- +#pragma no_check_stack
- +#include <asm/system.h>
- +
- +#define DELAY udelay(10)
- +
- +/* extern volatile char *const ioc; */
- +#define ioc ((volatile char *)0x03200000)
- +
- +static void iic_start(void)
- +{
- + char out;
- +
- + out=(ioc[0] & 0xFC) | 0xC0;
- +
- + ioc[0]=out|3; DELAY;
- + ioc[0]=out|2; DELAY;
- +}
- +
- +static void iic_stop(void)
- +{
- + char out;
- +
- + out=(ioc[0] & 0xFC) | 0xC0;
- +
- + DELAY; ioc[0]=out|2;
- + DELAY; ioc[0]=out|3;
- +}
- +
- +static int iic_sendbyte(char b)
- +{
- + 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 char iic_recvbyte(void)
- +{
- + char out,in;
- + int i;
- +
- + out=(ioc[0] & 0xFC) | 0xC0;
- +
- + ioc[0]=out;
- + 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(int addr,int loc,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]))
- + goto error;
- + }
- + 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 -r -u -N linux.orig/arch/arm/drivers/char/kbd_kern.h linux.arm/arch/arm/drivers/char/kbd_kern.h
- --- linux.orig/arch/arm/drivers/char/kbd_kern.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/kbd_kern.h Fri Oct 27 23:14:23 1995
- @@ -0,0 +1,127 @@
- +#ifndef _KBD_KERN_H
- +#define _KBD_KERN_H
- +
- +#include <linux/interrupt.h>
- +#define set_leds() mark_bh(KEYBOARD_BH)
- +
- +#include <linux/keyboard.h>
- +
- +extern char *func_table[MAX_NR_FUNC];
- +extern char func_buf[];
- +extern char *funcbufptr;
- +extern int funcbufsize, funcbufleft;
- +
- +/*
- + * kbd->xxx contains the VC-local things (flag settings etc..)
- + *
- + * Note: externally visible are LED_SCR, LED_NUM, LED_CAP defined in kd.h
- + * The code in KDGETLED / KDSETLED depends on the internal and
- + * external order being the same.
- + *
- + * Note: lockstate is used as index in the array key_map.
- + */
- +struct kbd_struct {
- +
- + unsigned char lockstate;
- +/* 8 modifiers - the names do not have any meaning at all;
- + they can be associated to arbitrarily chosen keys */
- +#define VC_SHIFTLOCK KG_SHIFT /* shift lock mode */
- +#define VC_ALTGRLOCK KG_ALTGR /* altgr lock mode */
- +#define VC_CTRLLOCK KG_CTRL /* control lock mode */
- +#define VC_ALTLOCK KG_ALT /* alt lock mode */
- +#define VC_SHIFTLLOCK KG_SHIFTL /* shiftl lock mode */
- +#define VC_SHIFTRLOCK KG_SHIFTR /* shiftr lock mode */
- +#define VC_CTRLLLOCK KG_CTRLL /* ctrll lock mode */
- +#define VC_CTRLRLOCK KG_CTRLR /* ctrlr lock mode */
- +
- + unsigned char ledmode:2; /* one 2-bit value */
- +#define LED_SHOW_FLAGS 0 /* traditional state */
- +#define LED_SHOW_IOCTL 1 /* only change leds upon ioctl */
- +#define LED_SHOW_MEM 2 /* `heartbeat': peek into memory */
- +
- + unsigned char ledflagstate:3; /* flags, not lights */
- + unsigned char default_ledflagstate:3;
- +#define VC_SCROLLOCK 0 /* scroll-lock mode */
- +#define VC_NUMLOCK 1 /* numeric lock mode */
- +#define VC_CAPSLOCK 2 /* capslock mode */
- +
- + unsigned char kbdmode:2; /* one 2-bit value */
- +#define VC_XLATE 0 /* translate keycodes using keymap */
- +#define VC_MEDIUMRAW 1 /* medium raw (keycode) mode */
- +#define VC_RAW 2 /* raw (scancode) mode */
- +#define VC_UNICODE 3 /* Unicode mode */
- +
- + unsigned char modeflags:5;
- +#define VC_APPLIC 0 /* application key mode */
- +#define VC_CKMODE 1 /* cursor key mode */
- +#define VC_REPEAT 2 /* keyboard repeat */
- +#define VC_CRLF 3 /* 0 - enter sends CR, 1 - enter sends CRLF */
- +#define VC_META 4 /* 0 - meta, 1 - meta=prefix with ESC */
- +};
- +
- +extern struct kbd_struct kbd_table[];
- +
- +extern unsigned long kbd_init(unsigned long);
- +
- +extern unsigned char getledstate(void);
- +extern void setledstate(struct kbd_struct *kbd, unsigned int led);
- +#ifndef __ARM__
- +extern inline int vc_kbd_mode(struct kbd_struct * kbd, int flag)
- +{
- + return ((kbd->modeflags >> flag) & 1);
- +}
- +
- +extern inline int vc_kbd_led(struct kbd_struct * kbd, int flag)
- +{
- + return ((kbd->ledflagstate >> flag) & 1);
- +}
- +
- +extern inline void set_vc_kbd_mode(struct kbd_struct * kbd, int flag)
- +{
- + kbd->modeflags |= 1 << flag;
- +}
- +
- +extern inline void set_vc_kbd_led(struct kbd_struct * kbd, int flag)
- +{
- + kbd->ledflagstate |= 1 << flag;
- +}
- +
- +extern inline void clr_vc_kbd_mode(struct kbd_struct * kbd, int flag)
- +{
- + kbd->modeflags &= ~(1 << flag);
- +}
- +
- +extern inline void clr_vc_kbd_led(struct kbd_struct * kbd, int flag)
- +{
- + kbd->ledflagstate &= ~(1 << flag);
- +}
- +
- +extern inline void chg_vc_kbd_lock(struct kbd_struct * kbd, int flag)
- +{
- + kbd->lockstate ^= 1 << flag;
- +}
- +
- +extern inline void chg_vc_kbd_mode(struct kbd_struct * kbd, int flag)
- +{
- + kbd->modeflags ^= 1 << flag;
- +}
- +
- +extern inline void chg_vc_kbd_led(struct kbd_struct * kbd, int flag)
- +{
- + kbd->ledflagstate ^= 1 << flag;
- +}
- +#else
- +#define vc_kbd_mode(kbd,flag) (((kbd)->modeflags >> flag) & 1)
- +#define vc_kbd_led(kbd,flag) (((kbd)->ledflagstate >> flag) & 1)
- +#define set_vc_kbd_mode(kbd,flag) ((kbd)->modeflags |= 1 << flag)
- +#define set_vc_kbd_led(kbd,flag) ((kbd)->ledflagstate |= 1 << flag)
- +#define clr_vc_kbd_mode(kbd,flag) ((kbd)->modeflags &= ~(1 << flag))
- +#define clr_vc_kbd_led(kbd,flag) ((kbd)->ledflagstate &= ~(1 << flag))
- +#define chg_vc_kbd_lock(kbd,flag) ((kbd)->lockstate ^= 1 << flag)
- +#define chg_vc_kbd_mode(kbd,flag) ((kbd)->modeflags ^= 1 << flag)
- +#define chg_vc_kbd_led(kbd,flag) ((kbd)->ledflagstate ^= 1 << flag)
- +#endif
- +
- +#define U(x) ((x) ^ 0xf000)
- +
- +#endif
- diff -r -u -N linux.orig/arch/arm/drivers/char/keyboard.c linux.arm/arch/arm/drivers/char/keyboard.c
- --- linux.orig/arch/arm/drivers/char/keyboard.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/keyboard.c Fri Oct 27 23:14:24 1995
- @@ -0,0 +1,1062 @@
- +#define IRQ_KEYBOARDRX 15
- +#define IRQ_KEYBOARDTX 14
- +
- +#define VERSION 002
- +
- +#include <linux/config.h>
- +#include <linux/sched.h>
- +#include <linux/interrupt.h>
- +#include <linux/tty.h>
- +#include <linux/tty_flip.h>
- +#include <linux/ctype.h>
- +#include <linux/mm.h>
- +#include <linux/timer.h>
- +#include <linux/ptrace.h>
- +
- +#include <asm/irq.h>
- +
- +#include "kbd_kern.h"
- +#include "diacr.h"
- +#include "vt_kern.h"
- +
- +#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
- +
- +/* Mouse stuff */
- +
- +extern struct wait_queue *mouse_wait;
- +extern char mouse_buttons;
- +extern int mouse_dxpos;
- +extern int mouse_dypos;
- +extern char mouse_ready;
- +
- +#define KBD_IRQ_RPT 1
- +#define KBD_IRQ_KEY 2
- +
- +#ifndef KBD_DEFMODE
- +#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
- +#endif
- +
- +#ifndef KBD_DEFLEDS
- +#define KBD_DEFLEDS 0 /* VC_NUMLOCK */
- +#endif
- +
- +#ifndef KBD_DEFLOCK
- +#define KBD_DEFLOCK 0
- +#endif
- +
- +#define KBD_SHIFT 0x11
- +#define KBD_CTRL 0x22
- +#define KBD_ALT 0x44
- +
- +#define KBD_LSHIFT 0x01
- +#define KBD_LCTRL 0x02
- +#define KBD_LALT 0x04
- +#define KBD_RSHIFT 0x10
- +#define KBD_RCTRL 0x20
- +#define KBD_RALT 0x40
- +
- +#define HRST 0xFF
- +#define RAK1 0xFE
- +#define RAK2 0xFD
- +#define SMAK 0x33
- +
- +extern void poke_blanked_console (void);
- +extern void ctrl_alt_del (void);
- +extern void change_console (unsigned int console);
- +static void process_key(int keycode, char up_flag, int repeattimeout);
- +static unsigned char getleds(void);
- +
- +static unsigned char k_down[NR_SHIFT] = {0, };
- +
- +static int want_console = -1;
- +static int last_console = 0; /* last used VC */
- +static int dead_key_next = 0;
- +
- +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 */
- +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;
- +
- +static int kbd_rollover[3] = {-1,-1,-1};
- +
- +extern unsigned char *ioc;
- +static unsigned char kbd_sendvala[4];
- +static unsigned char kbd_sendptri;
- +static unsigned char kbd_sendptro;
- +static unsigned char ledstate = 0xff;
- +static unsigned char kbd_repeatkey=-1;
- +
- +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_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_ignore, do_ignore, do_ignore, do_ignore
- +};
- +
- +typedef void (*void_fnp)(void);
- +typedef void (void_fn)(void);
- +
- +static void_fn enter, show_ptregs, send_intr, lastcons, caps_toggle,
- + num, hold, scroll_forw, scroll_back, boot_it, caps_on, compose,
- + SAK, decr_console, incr_console, show_stack/*, show_floppy*/;
- +
- +static void_fnp spec_fn_table[] = {
- + 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/*, show_floppy*/
- +};
- +
- +/* 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
- +};
- +
- +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;
- +
- +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 */
- +}
- +
- +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
- +}
- +
- +
- +/* ----------------------------------------------------------------------------------------- */
- +
- +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(int keycode, char up_flag)
- +{
- + char process=0;
- + if(keycode >= 0x70 && keycode <= 0x72)
- + {
- + 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;
- + }
- + mouse_ready=1;
- + wake_up_interruptible(&mouse_wait);
- + mark_bh(KEYBOARD_BH);
- + return;
- + }
- +
- + tty = ttytab[fg_console];
- + kbd = kbd_table + fg_console;
- +
- + if(!up_flag)
- + {
- + if(kbd_rollover[0] != -1)
- + {
- + if(kbd_rollover[1] != -1)
- + {
- + if(kbd_rollover[2] != -1)
- + return;
- + kbd_rollover[2] = kbd_rollover[1];
- + }
- + kbd_rollover[1] = kbd_rollover[0];
- + }
- + kbd_rollover[0] = keycode;
- + process=1;
- + }
- + else
- + {
- + if(kbd_rollover[0] == keycode)
- + kbd_rollover[0] = -1;
- + if(kbd_rollover[1] == keycode)
- + kbd_rollover[1] = -1;
- + if(kbd_rollover[2] == keycode)
- + kbd_rollover[2] = -1;
- + process=1;
- + }
- +
- + del_timer(&key_timer);
- + rep = 0;
- +
- + if(kbd->kbdmode == VC_RAW)
- + put_queue(keycode|(up_flag?0x80:0));
- + else
- + {
- + if(process)
- + process_key(keycode, up_flag, HZ*300/1000);
- + mark_bh(KEYBOARD_BH);
- + }
- +}
- +
- +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;
- + 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;
- + static unsigned char kbd_state=0;
- + static unsigned char kbd_keyhigh=0;
- +
- + keyval=ioc[4];
- +
- + switch(kbd_state)
- + {
- + case 0:/* initial reset condition */
- + if(keyval == HRST)
- + {
- + kbd_reset();
- + kbd_sendval(RAK1);
- + kbd_state = 1;
- + }
- + else
- + {
- + kbd_sendval(HRST);
- + kbd_state = 0;
- + }
- + break;
- +
- + case 1:/* Sent RAK1 */
- + if(keyval == RAK1)
- + {
- + kbd_sendval(RAK2);
- + kbd_state = 2;
- + }
- + else
- + {
- + kbd_sendval(HRST);
- + kbd_state = 0;
- + }
- + break;
- +
- + case 2:/* Sent RAK2 */
- + if(keyval == RAK2)
- + {
- + kbd_sendval(SMAK);
- + kbd_state = 3;
- + ledstate = 0xff;
- + mark_bh(KEYBOARD_BH);
- + }
- + else
- + {
- + kbd_sendval(HRST);
- + kbd_state = 0;
- + }
- + 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(0x3F);
- + break;
- +
- + case 0xD0:
- + kbd_keyhigh=keyval;
- + kbd_state = 5;
- + kbd_sendval(0x3F);
- + break;
- +
- + default:
- + kbd_state = 0;
- + kbd_sendval(HRST);
- + }
- + }
- + else
- + {
- + kbd_mousedx=keyval&0x40?keyval|0x80:keyval;
- + kbd_state = 6;
- + kbd_sendval(0x3F);
- + }
- + break;
- +
- + case 4:
- + if((keyval & 0xF0)!=0xC0)
- + {
- + kbd_state = 0;
- + kbd_sendval(HRST);
- + }
- + else
- + {
- + kbd_state = 3;
- + kbd_sendval(SMAK);
- + if(((kbd_keyhigh^keyval)&0xF0)==0)
- + kbd_key((keyval&0x0F)|((kbd_keyhigh<<4)&0xF0),0);
- + }
- + break;
- +
- + case 5:
- + if((keyval & 0xF0)!=0xD0)
- + {
- + kbd_state = 0;
- + kbd_sendval(HRST);
- + }
- + else
- + {
- + kbd_state = 3;
- + kbd_sendval(SMAK);
- + if(((kbd_keyhigh^keyval)&0xF0)==0)
- + kbd_key((keyval&0x0F)|((kbd_keyhigh<<4)&0xF0),1);
- + }
- + break;
- +
- + case 6:
- + if(keyval & 0x80)
- + {
- + kbd_state = 0;
- + kbd_sendval(HRST);
- + }
- + else
- + {
- + kbd_state = 3;
- + kbd_sendval(SMAK);
- + mouse_dypos+=(int)((char)(keyval&0x40?keyval|0x80:keyval));
- + mouse_dxpos+=(int)kbd_mousedx;
- + mouse_ready=1;
- + wake_up_interruptible(&mouse_wait);
- + mark_bh(KEYBOARD_BH);
- + }
- + break;
- + }
- +}
- +
- +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);
- + }
- +}
- +
- +static void process_key(int keycode, char up_flag, int repeattimeout)
- +{
- + del_timer(&key_timer);
- +
- + if(!up_flag)
- + {
- + kbd_repeatkey = keycode;
- + if(vc_kbd_mode(kbd, VC_REPEAT))
- + {
- + key_timer.expires = repeattimeout;
- + add_timer(&key_timer);
- + }
- + }
- + else
- + del_timer(&key_timer);
- +
- + 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;
- + int shift_final = shift_state ^ kbd->lockstate;
- + u_short *key_map = key_maps[shift_final];
- +
- + if(key_map != NULL)
- + {
- + keysym = key_map[keycode];
- + type = KTYP(keysym);
- +
- + if((type & 15) == KT_SHIFT)
- + kbd_rollover[0] = -1;
- +
- + 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);
- + }
- + else
- + {
- + if(!up_flag)
- + to_utf8(keysym);
- + }
- + }
- + else
- + {
- + keysym = U(plain_map[keycode]);
- + type = KTYP(keysym);
- + if(type == KT_SHIFT)
- + (*key_handler[type])(keysym & 0xff, up_flag);
- + }
- + }
- +}
- +
- +static void kbd_bh(void *unused)
- +{
- + unsigned char leds;
- +
- + tty = ttytab[fg_console];
- + kbd = kbd_table + fg_console;
- +
- + if(rep)
- + process_key(kbd_repeatkey, 0, HZ*30/1000);
- + rep = 0;
- +
- + if(want_console >= 0)
- + {
- + if(want_console != fg_console)
- + {
- + last_console = fg_console;
- + change_console(want_console);
- + }
- + want_console = -1;
- + }
- +
- + leds = getleds();
- + if(leds != ledstate)
- + {
- + unsigned long flags;
- + ledstate = leds;
- +#define LED_LKCAPS 1
- +#define LED_LKNUM 2
- +#define LED_LKSCRL 4
- + leds = ((leds & (1<<VC_SCROLLOCK))?LED_LKSCRL:0) | ((leds & (1<<VC_NUMLOCK))?LED_LKNUM:0) |
- + ((leds & (1<<VC_CAPSLOCK))?LED_LKCAPS:0);
- + save_flags(flags); cli(); intr_count -= 1;
- + kbd_sendval(leds);
- + intr_count += 1; restore_flags(flags);
- + }
- + poke_blanked_console();
- +}
- +
- +/* ----------------------------------------------------------------------------------------- */
- +
- +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);
- +}
- +int debug_flag=0;
- +static void show_ptregs(void)
- +{ debug_flag ^= 1;
- + if(!pt_regs)
- + return;
- + printk("\n");
- +}
- +
- +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;
- +
- + if(tty->stopped)
- + start_tty(tty);
- + else
- + stop_tty(tty);
- +}
- +
- +static void num(void)
- +{
- + if(vc_kbd_mode(kbd,VC_APPLIC))
- + {
- + applkey('P', 1);
- + return;
- + }
- + 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 || (tty->termios && I_IGNBRK(tty)))
- + return;
- + tty_insert_flip_char(tty, 0, TTY_BREAK);
- +}
- +
- +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);
- +}
- +
- +static void do_ignore(unsigned char value, char up_flag)
- +{
- +}
- +
- +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;
- +
- + if(diacr)
- + value = handle_diacr(value);
- +
- + if(dead_key_next)
- + {
- + dead_key_next = 0;
- + diacr = value;
- + return;
- + }
- +if (debug_flag) printk("inserting into put_queue\n");
- + 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)
- + {
- + 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<sizeof(func_table) && func_table[value]!=NULL)
- + 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));
- +}
- +
- +void compute_shiftstate(void)
- +{
- +}
- +
- +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;
- + }
- +}
- +
- +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);
- +}
- +
- +/* --------------------------------------------------------------------------------------- *
- + * Led driver *
- + * --------------------------------------------------------------------------------------- */
- +
- +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 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;
- +}
- +
- +/* --------------------------------------------------------------------------------------- *
- + * Keyboard initialisation *
- + * kbd_rx must be allocated first, then kbd_tx *
- + * --------------------------------------------------------------------------------------- */
- +
- +unsigned long kbd_init(unsigned long kmem_start)
- +{
- + 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.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);
- + printk("%s keyboard driver installed (%s) v%d.%02d.\n", "A5000", "English", VERSION/100, VERSION%100);
- + return kmem_start;
- +}
- +
- diff -r -u -N linux.orig/arch/arm/drivers/char/lp.c linux.arm/arch/arm/drivers/char/lp.c
- --- linux.orig/arch/arm/drivers/char/lp.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/lp.c Fri Oct 27 23:14:24 1995
- @@ -0,0 +1,618 @@
- +/*
- + * Copyright (C) 1992 by Jim Weigand and Linus Torvalds
- + * Copyright (C) 1992,1993 by Michael K. Johnson
- + * - Thanks much to Gunter Windau for pointing out to me where the error
- + * checking ought to be.
- + * Copyright (C) 1993 by Nigel Gamble (added interrupt code)
- + * Copyright (C) 1994 by Alan Cox (Modularised it)
- + * LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu
- + */
- +
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/version.h>
- +#else
- +#define MOD_INC_USE_COUNT
- +#define MOD_DEC_USE_COUNT
- +#endif
- +
- +#include <linux/errno.h>
- +#include <linux/kernel.h>
- +#include <linux/major.h>
- +#include <linux/sched.h>
- +#include <linux/lp.h>
- +#include <linux/malloc.h>
- +#include <linux/ioport.h>
- +#include <linux/fcntl.h>
- +
- +#include <asm/io.h>
- +#include <asm/segment.h>
- +#include <asm/system.h>
- +
- +/* the BIOS manuals say there can be up to 4 lpt devices
- + * but I have not seen a board where the 4th address is listed
- + * if you have different hardware change the table below
- + * please let me know if you have different equipment
- + * if you have more than 3 printers, remember to increase LP_NO
- + */
- +struct lp_struct lp_table[] = {
- + { 0x3bc, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
- + { 0x378, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
- + { 0x278, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
- +};
- +#define LP_NO 3
- +
- +/* Test if printer is ready (and optionally has no error conditions) */
- +#define LP_READY(minor, status) \
- + ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : (status & LP_PBUSY))
- +#define LP_CAREFUL_READY(minor, status) \
- + ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : 1)
- +#define _LP_CAREFUL_READY(status) \
- + (status & (LP_PBUSY|LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \
- + (LP_PBUSY|LP_PSELECD|LP_PERRORP)
- +
- +/* Allow old versions of tunelp to continue to work */
- +#define OLD_LPCHAR 0x0001
- +#define OLD_LPTIME 0x0002
- +#define OLD_LPABORT 0x0004
- +#define OLD_LPSETIRQ 0x0005
- +#define OLD_LPGETIRQ 0x0006
- +#define OLD_LPWAIT 0x0008
- +#define OLD_IOCTL_MAX 8
- +
- +/*
- + * All my debugging code assumes that you debug with only one printer at
- + * a time. RWWH
- + */
- +
- +#undef LP_DEBUG
- +
- +static int lp_reset(int minor)
- +{
- + int testvalue;
- + unsigned char command;
- +
- + command = LP_PSELECP | LP_PINITP;
- +
- + /* reset value */
- + outb_p(0, LP_C(minor));
- + for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
- + ;
- + outb_p(command, LP_C(minor));
- + return LP_S(minor);
- +}
- +
- +#ifdef LP_DEBUG
- +static int lp_max_count = 1;
- +#endif
- +
- +static int lp_char_polled(char lpchar, int minor)
- +{
- + int status = 0, wait = 0;
- + unsigned long count = 0;
- +
- + do {
- + status = LP_S(minor);
- + count ++;
- + if(need_resched)
- + schedule();
- + } while(!LP_READY(minor,status) && count < LP_CHAR(minor));
- +
- + if (count == LP_CHAR(minor)) {
- + return 0;
- + /* we timed out, and the character was /not/ printed */
- + }
- +#ifdef LP_DEBUG
- + if (count > lp_max_count) {
- + printk("lp success after %d counts.\n",count);
- + lp_max_count=count;
- + }
- +#endif
- + outb_p(lpchar, LP_B(minor));
- + /* must wait before taking strobe high, and after taking strobe
- + low, according spec. Some printers need it, others don't. */
- + while(wait != LP_WAIT(minor)) wait++;
- + /* control port takes strobe high */
- + outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
- + while(wait) wait--;
- + /* take strobe low */
- + outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
- +
- + return 1;
- +}
- +
- +static int lp_char_interrupt(char lpchar, int minor)
- +{
- + int wait = 0;
- + unsigned char status;
- +
- +
- + if (!((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
- + || !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
- + || !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)) {
- +
- + if (!LP_CAREFUL_READY(minor, status))
- + return 0;
- + outb_p(lpchar, LP_B(minor));
- + /* must wait before taking strobe high, and after taking strobe
- + low, according spec. Some printers need it, others don't. */
- + while(wait != LP_WAIT(minor)) wait++;
- + /* control port takes strobe high */
- + outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
- + while(wait) wait--;
- + /* take strobe low */
- + outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
- + return 1;
- + }
- +
- + return 0;
- +}
- +
- +#ifdef LP_DEBUG
- + unsigned int lp_total_chars = 0;
- + unsigned int lp_last_call = 0;
- +#endif
- +
- +static void lp_interrupt(int irq, struct pt_regs *regs)
- +{
- + struct lp_struct *lp = &lp_table[0];
- + struct lp_struct *lp_end = &lp_table[LP_NO];
- +
- + while (irq != lp->irq) {
- + if (++lp >= lp_end)
- + return;
- + }
- +
- + wake_up(&lp->lp_wait_q);
- +}
- +
- +static int lp_write_interrupt(struct inode * inode, struct file * file, char * buf, int count)
- +{
- + unsigned int minor = MINOR(inode->i_rdev);
- + unsigned long copy_size;
- + unsigned long total_bytes_written = 0;
- + unsigned long bytes_written;
- + struct lp_struct *lp = &lp_table[minor];
- + unsigned char status;
- +
- + do {
- + bytes_written = 0;
- + copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
- + memcpy_fromfs(lp->lp_buffer, buf, copy_size);
- +
- + while (copy_size) {
- + if (lp_char_interrupt(lp->lp_buffer[bytes_written], minor)) {
- + --copy_size;
- + ++bytes_written;
- + } else {
- + int rc = total_bytes_written + bytes_written;
- + status = LP_S(minor);
- + if ((status & LP_POUTPA)) {
- + printk(KERN_INFO "lp%d out of paper\n", minor);
- + if (LP_F(minor) & LP_ABORT)
- + return rc?rc:-ENOSPC;
- + } else if (!(status & LP_PSELECD)) {
- + printk(KERN_INFO "lp%d off-line\n", minor);
- + if (LP_F(minor) & LP_ABORT)
- + return rc?rc:-EIO;
- + } else if (!(status & LP_PERRORP)) {
- + printk(KERN_ERR "lp%d printer error\n", minor);
- + if (LP_F(minor) & LP_ABORT)
- + return rc?rc:-EIO;
- + }
- + cli();
- + outb_p((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor)));
- + status = LP_S(minor);
- + if ((!(status & LP_PACK) || (status & LP_PBUSY))
- + && LP_CAREFUL_READY(minor, status)) {
- + outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
- + sti();
- + continue;
- + }
- + current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
- + interruptible_sleep_on(&lp->lp_wait_q);
- + outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
- + sti();
- + if (current->signal & ~current->blocked) {
- + if (total_bytes_written + bytes_written)
- + return total_bytes_written + bytes_written;
- + else
- + return -EINTR;
- + }
- + }
- + }
- +
- + total_bytes_written += bytes_written;
- + buf += bytes_written;
- + count -= bytes_written;
- +
- + } while (count > 0);
- +
- + return total_bytes_written;
- +}
- +
- +static int lp_write_polled(struct inode * inode, struct file * file,
- + char * buf, int count)
- +{
- + int retval;
- + unsigned int minor = MINOR(inode->i_rdev);
- + char c, *temp = buf;
- +
- +#ifdef LP_DEBUG
- + if (jiffies-lp_last_call > LP_TIME(minor)) {
- + lp_total_chars = 0;
- + lp_max_count = 1;
- + }
- + lp_last_call = jiffies;
- +#endif
- +
- + temp = buf;
- + while (count > 0) {
- + c = get_fs_byte(temp);
- + retval = lp_char_polled(c, minor);
- + /* only update counting vars if character was printed */
- + if (retval) { count--; temp++;
- +#ifdef LP_DEBUG
- + lp_total_chars++;
- +#endif
- + }
- + if (!retval) { /* if printer timed out */
- + int status = LP_S(minor);
- +
- + if (status & LP_POUTPA) {
- + printk(KERN_INFO "lp%d out of paper\n", minor);
- + if(LP_F(minor) & LP_ABORT)
- + return temp-buf?temp-buf:-ENOSPC;
- + current->state = TASK_INTERRUPTIBLE;
- + current->timeout = jiffies + LP_TIMEOUT_POLLED;
- + schedule();
- + } else
- + if (!(status & LP_PSELECD)) {
- + printk(KERN_INFO "lp%d off-line\n", minor);
- + if(LP_F(minor) & LP_ABORT)
- + return temp-buf?temp-buf:-EIO;
- + current->state = TASK_INTERRUPTIBLE;
- + current->timeout = jiffies + LP_TIMEOUT_POLLED;
- + schedule();
- + } else
- + /* not offline or out of paper. on fire? */
- + if (!(status & LP_PERRORP)) {
- + printk(KERN_ERR "lp%d reported invalid error status (on fire, eh?)\n", minor);
- + if(LP_F(minor) & LP_ABORT)
- + return temp-buf?temp-buf:-EIO;
- + current->state = TASK_INTERRUPTIBLE;
- + current->timeout = jiffies + LP_TIMEOUT_POLLED;
- + schedule();
- + }
- +
- + /* check for signals before going to sleep */
- + if (current->signal & ~current->blocked) {
- + if (temp != buf)
- + return temp-buf;
- + else
- + return -EINTR;
- + }
- +#ifdef LP_DEBUG
- + printk("lp sleeping at %d characters for %d jiffies\n",
- + lp_total_chars, LP_TIME(minor));
- + lp_total_chars=0;
- +#endif
- + current->state = TASK_INTERRUPTIBLE;
- + current->timeout = jiffies + LP_TIME(minor);
- + schedule();
- + }
- + }
- + return temp-buf;
- +}
- +
- +static int lp_write(struct inode * inode, struct file * file, char * buf, int count)
- +{
- + if (LP_IRQ(MINOR(inode->i_rdev)))
- + return lp_write_interrupt(inode, file, buf, count);
- + else
- + return lp_write_polled(inode, file, buf, count);
- +}
- +
- +static int lp_lseek(struct inode * inode, struct file * file,
- + off_t offset, int origin)
- +{
- + return -ESPIPE;
- +}
- +
- +static int lp_open(struct inode * inode, struct file * file)
- +{
- + unsigned int minor = MINOR(inode->i_rdev);
- + int ret;
- + unsigned int irq;
- +
- + if (minor >= LP_NO)
- + return -ENODEV;
- + if ((LP_F(minor) & LP_EXIST) == 0)
- + return -ENODEV;
- + if (LP_F(minor) & LP_BUSY)
- + return -EBUSY;
- +
- + MOD_INC_USE_COUNT;
- +
- + /* If ABORTOPEN is set and the printer is offline or out of paper,
- + we may still want to open it to perform ioctl()s. Therefore we
- + have commandeered O_NONBLOCK, even though it is being used in
- + a non-standard manner. This is strictly a Linux hack, and
- + should most likely only ever be used by the tunelp application. */
- + if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
- + int status = LP_S(minor);
- + if (status & LP_POUTPA) {
- + printk(KERN_INFO "lp%d out of paper\n", minor);
- + MOD_DEC_USE_COUNT;
- + return -ENOSPC;
- + } else if (!(status & LP_PSELECD)) {
- + printk(KERN_INFO "lp%d off-line\n", minor);
- + MOD_DEC_USE_COUNT;
- + return -EIO;
- + } else if (!(status & LP_PERRORP)) {
- + printk(KERN_ERR "lp%d printer error\n", minor);
- + MOD_DEC_USE_COUNT;
- + return -EIO;
- + }
- + }
- +
- + if ((irq = LP_IRQ(minor))) {
- + lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
- + if (!lp_table[minor].lp_buffer) {
- + MOD_DEC_USE_COUNT;
- + return -ENOMEM;
- + }
- +
- + ret = request_irq(irq, lp_interrupt, SA_INTERRUPT, "printer");
- + if (ret) {
- + kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
- + lp_table[minor].lp_buffer = NULL;
- + printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret);
- + MOD_DEC_USE_COUNT;
- + return ret;
- + }
- + }
- +
- + LP_F(minor) |= LP_BUSY;
- + return 0;
- +}
- +
- +static void lp_release(struct inode * inode, struct file * file)
- +{
- + unsigned int minor = MINOR(inode->i_rdev);
- + unsigned int irq;
- +
- + if ((irq = LP_IRQ(minor))) {
- + free_irq(irq);
- + kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
- + lp_table[minor].lp_buffer = NULL;
- + }
- +
- + LP_F(minor) &= ~LP_BUSY;
- + MOD_DEC_USE_COUNT;
- +}
- +
- +
- +static int lp_ioctl(struct inode *inode, struct file *file,
- + unsigned int cmd, unsigned long arg)
- +{
- + unsigned int minor = MINOR(inode->i_rdev);
- + int retval = 0;
- +
- +#ifdef LP_DEBUG
- + printk("lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
- +#endif
- + if (minor >= LP_NO)
- + return -ENODEV;
- + if ((LP_F(minor) & LP_EXIST) == 0)
- + return -ENODEV;
- + if (cmd <= OLD_IOCTL_MAX)
- + printk(KERN_NOTICE "lp%d: warning: obsolete ioctl %#x (perhaps you need a new tunelp)\n",
- + minor, cmd);
- + switch ( cmd ) {
- + case OLD_LPTIME:
- + case LPTIME:
- + LP_TIME(minor) = arg;
- + break;
- + case OLD_LPCHAR:
- + case LPCHAR:
- + LP_CHAR(minor) = arg;
- + break;
- + case OLD_LPABORT:
- + case LPABORT:
- + if (arg)
- + LP_F(minor) |= LP_ABORT;
- + else
- + LP_F(minor) &= ~LP_ABORT;
- + break;
- + case LPABORTOPEN:
- + if (arg)
- + LP_F(minor) |= LP_ABORTOPEN;
- + else
- + LP_F(minor) &= ~LP_ABORTOPEN;
- + break;
- + case LPCAREFUL:
- + if (arg)
- + LP_F(minor) |= LP_CAREFUL;
- + else
- + LP_F(minor) &= ~LP_CAREFUL;
- + break;
- + case OLD_LPWAIT:
- + case LPWAIT:
- + LP_WAIT(minor) = arg;
- + break;
- + case OLD_LPSETIRQ:
- + case LPSETIRQ: {
- + int oldirq;
- + int newirq = arg;
- + struct lp_struct *lp = &lp_table[minor];
- +
- + if (!suser())
- + return -EPERM;
- +
- + oldirq = LP_IRQ(minor);
- +
- + /* Allocate buffer now if we are going to need it */
- + if (!oldirq && newirq) {
- + lp->lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
- + if (!lp->lp_buffer)
- + return -ENOMEM;
- + }
- +
- + if (oldirq) {
- + free_irq(oldirq);
- + }
- + if (newirq) {
- + /* Install new irq */
- + if ((retval = request_irq(newirq, lp_interrupt, SA_INTERRUPT, "printer"))) {
- + if (oldirq) {
- + /* restore old irq */
- + request_irq(oldirq, lp_interrupt, SA_INTERRUPT, "printer");
- + } else {
- + /* We don't need the buffer */
- + kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
- + lp->lp_buffer = NULL;
- + }
- + return retval;
- + }
- + }
- + if (oldirq && !newirq) {
- + /* We don't need the buffer */
- + kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
- + lp->lp_buffer = NULL;
- + }
- + LP_IRQ(minor) = newirq;
- + lp_reset(minor);
- + break;
- + }
- + case OLD_LPGETIRQ:
- + retval = LP_IRQ(minor);
- + break;
- + case LPGETIRQ:
- + retval = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(int));
- + if (retval)
- + return retval;
- + memcpy_tofs((int *) arg, &LP_IRQ(minor), sizeof(int));
- + break;
- + case LPGETSTATUS:
- + retval = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(int));
- + if (retval)
- + return retval;
- + else {
- + int status = LP_S(minor);
- + memcpy_tofs((int *) arg, &status, sizeof(int));
- + }
- + break;
- + case LPRESET:
- + lp_reset(minor);
- + break;
- + default:
- + retval = -EINVAL;
- + }
- + return retval;
- +}
- +
- +
- +static struct file_operations lp_fops = {
- + lp_lseek,
- + NULL, /* lp_read */
- + lp_write,
- + NULL, /* lp_readdir */
- + NULL, /* lp_select */
- + lp_ioctl,
- + NULL, /* lp_mmap */
- + lp_open,
- + lp_release
- +};
- +
- +#ifndef MODULE
- +
- +long lp_init(long kmem_start)
- +{
- + int offset = 0;
- + unsigned int testvalue = 0;
- + int count = 0;
- +
- + if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
- + printk("unable to get major %d for line printer\n", LP_MAJOR);
- + return kmem_start;
- + }
- + /* take on all known port values */
- + for (offset = 0; offset < LP_NO; offset++) {
- + if (check_region(LP_B(offset), 3))
- + continue;
- + /* write to port & read back to check */
- + outb_p( LP_DUMMY, LP_B(offset));
- + for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
- + ;
- + testvalue = inb_p(LP_B(offset));
- + if (testvalue == LP_DUMMY) {
- + LP_F(offset) |= LP_EXIST;
- + lp_reset(offset);
- + printk("lp%d at 0x%04x, ", offset,LP_B(offset));
- + request_region(LP_B(offset), 3, "lp");
- + if (LP_IRQ(offset))
- + printk("using IRQ%d\n", LP_IRQ(offset));
- + else
- + printk("using polling driver\n");
- + count++;
- + }
- + }
- + if (count == 0)
- + printk("lp_init: no lp devices found\n");
- + return kmem_start;
- +}
- +
- +#else
- +
- +char kernel_version[]= UTS_RELEASE;
- +
- +int init_module(void)
- +{
- + int offset = 0;
- + unsigned int testvalue = 0;
- + int count = 0;
- +
- + if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
- + printk("unable to get major %d for line printer\n", LP_MAJOR);
- + return -EIO;
- + }
- + /* take on all known port values */
- + for (offset = 0; offset < LP_NO; offset++) {
- + /* write to port & read back to check */
- + outb_p( LP_DUMMY, LP_B(offset));
- + for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
- + ;
- + testvalue = inb_p(LP_B(offset));
- + if (testvalue == LP_DUMMY) {
- + LP_F(offset) |= LP_EXIST;
- + lp_reset(offset);
- + printk("lp%d at 0x%04x, ", offset,LP_B(offset));
- + request_region(LP_B(offset),3,"lp");
- + if (LP_IRQ(offset))
- + printk("using IRQ%d\n", LP_IRQ(offset));
- + else
- + printk("using polling driver\n");
- + count++;
- + }
- + }
- + if (count == 0)
- + printk("lp_init: no lp devices found\n");
- + return 0;
- +}
- +
- +void cleanup_module(void)
- +{
- + int offset;
- + if(MOD_IN_USE)
- + printk("lp: busy - remove delayed\n");
- + else
- + unregister_chrdev(LP_MAJOR,"lp");
- + for (offset = 0; offset < LP_NO; offset++)
- + if(LP_F(offset) && LP_EXIST)
- + release_region(LP_B(offset),3);
- +}
- +
- +#endif
- diff -r -u -N linux.orig/arch/arm/drivers/char/lp.c.orig linux.arm/arch/arm/drivers/char/lp.c.orig
- --- linux.orig/arch/arm/drivers/char/lp.c.orig Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/lp.c.orig Fri Oct 27 23:14:38 1995
- @@ -0,0 +1,526 @@
- +/*
- + * Copyright (C) 1992 by Jim Weigand and Linus Torvalds
- + * Copyright (C) 1992,1993 by Michael K. Johnson
- + * - Thanks much to Gunter Windau for pointing out to me where the error
- + * checking ought to be.
- + * Copyright (C) 1993 by Nigel Gamble (added interrupt code)
- + * Copyright (C) 1994 by Alan Cox (Modularised it)
- + */
- +
- +#include <linux/config.h>
- +#include <linux/errno.h>
- +#include <linux/kernel.h>
- +#include <linux/major.h>
- +#include <linux/sched.h>
- +#include <linux/lp.h>
- +#include <linux/malloc.h>
- +
- +#include <asm/io.h>
- +#include <asm/segment.h>
- +#include <asm/system.h>
- +
- +/* the BIOS manuals say there can be up to 4 lpt devices
- + * but I have not seen a board where the 4th address is listed
- + * if you have different hardware change the table below
- + * please let me know if you have different equipment
- + * if you have more than 3 printers, remember to increase LP_NO
- + */
- +struct lp_struct lp_table[] = {
- + { 0x3bc, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
- + { 0x378, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
- + { 0x278, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
- +};
- +#define LP_NO 3
- +
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include "../../tools/version.h"
- +#endif
- +
- +/*
- + * All my debugging code assumes that you debug with only one printer at
- + * a time. RWWH
- + */
- +
- +#undef LP_DEBUG
- +
- +static int lp_reset(int minor)
- +{
- + int testvalue;
- + unsigned char command;
- +
- + command = LP_PSELECP | LP_PINITP;
- +
- + /* reset value */
- + outb_p(0, LP_C(minor));
- + for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
- + ;
- + outb_p(command, LP_C(minor));
- + return LP_S(minor);
- +}
- +
- +#ifdef LP_DEBUG
- +static int lp_max_count = 1;
- +#endif
- +
- +static int lp_char_polled(char lpchar, int minor)
- +{
- + int status = 0, wait = 0;
- + unsigned long count = 0;
- +
- + do {
- + status = LP_S(minor);
- + count ++;
- + if(need_resched)
- + schedule();
- + } while(!(status & LP_PBUSY) && count < LP_CHAR(minor));
- +
- + if (count == LP_CHAR(minor)) {
- + return 0;
- + /* we timed out, and the character was /not/ printed */
- + }
- +#ifdef LP_DEBUG
- + if (count > lp_max_count) {
- + printk("lp success after %d counts.\n",count);
- + lp_max_count=count;
- + }
- +#endif
- + outb_p(lpchar, LP_B(minor));
- + /* must wait before taking strobe high, and after taking strobe
- + low, according spec. Some printers need it, others don't. */
- + while(wait != LP_WAIT(minor)) wait++;
- + /* control port takes strobe high */
- + outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
- + while(wait) wait--;
- + /* take strobe low */
- + outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
- +
- + return 1;
- +}
- +
- +static int lp_char_interrupt(char lpchar, int minor)
- +{
- + int wait = 0;
- + unsigned char status;
- +
- +
- + if (!((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
- + || !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
- + || !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)) {
- +
- + outb_p(lpchar, LP_B(minor));
- + /* must wait before taking strobe high, and after taking strobe
- + low, according spec. Some printers need it, others don't. */
- + while(wait != LP_WAIT(minor)) wait++;
- + /* control port takes strobe high */
- + outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
- + while(wait) wait--;
- + /* take strobe low */
- + outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
- + return 1;
- + }
- +
- + return 0;
- +}
- +
- +#ifdef LP_DEBUG
- + unsigned int lp_total_chars = 0;
- + unsigned int lp_last_call = 0;
- +#endif
- +
- +static void lp_interrupt(int irq)
- +{
- + struct lp_struct *lp = &lp_table[0];
- + struct lp_struct *lp_end = &lp_table[LP_NO];
- +
- + while (irq != lp->irq) {
- + if (++lp >= lp_end)
- + return;
- + }
- +
- + wake_up(&lp->lp_wait_q);
- +}
- +
- +static int lp_write_interrupt(struct inode * inode, struct file * file, char * buf, int count)
- +{
- + unsigned int minor = MINOR(inode->i_rdev);
- + unsigned long copy_size;
- + unsigned long total_bytes_written = 0;
- + unsigned long bytes_written;
- + struct lp_struct *lp = &lp_table[minor];
- + unsigned char status;
- +
- + do {
- + bytes_written = 0;
- + copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
- + memcpy_fromfs(lp->lp_buffer, buf, copy_size);
- +
- + while (copy_size) {
- + if (lp_char_interrupt(lp->lp_buffer[bytes_written], minor)) {
- + --copy_size;
- + ++bytes_written;
- + } else {
- + if (!((status = LP_S(minor)) & LP_PERRORP)) {
- + int rc = total_bytes_written + bytes_written;
- +
- + if ((status & LP_POUTPA)) {
- + printk("lp%d out of paper\n", minor);
- + if (!rc)
- + rc = -ENOSPC;
- + } else if (!(status & LP_PSELECD)) {
- + printk("lp%d off-line\n", minor);
- + if (!rc)
- + rc = -EIO;
- + } else {
- + printk("lp%d printer error\n", minor);
- + if (!rc)
- + rc = -EIO;
- + }
- + if(LP_F(minor) & LP_ABORT)
- + return rc;
- + }
- + cli();
- + outb_p((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor)));
- + status = LP_S(minor);
- + if (!(status & LP_PACK) || (status & LP_PBUSY)) {
- + outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
- + sti();
- + continue;
- + }
- + current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
- + interruptible_sleep_on(&lp->lp_wait_q);
- + outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
- + sti();
- + if (current->signal & ~current->blocked) {
- + if (total_bytes_written + bytes_written)
- + return total_bytes_written + bytes_written;
- + else
- + return -EINTR;
- + }
- + }
- + }
- +
- + total_bytes_written += bytes_written;
- + buf += bytes_written;
- + count -= bytes_written;
- +
- + } while (count > 0);
- +
- + return total_bytes_written;
- +}
- +
- +static int lp_write_polled(struct inode * inode, struct file * file,
- + char * buf, int count)
- +{
- + int retval;
- + unsigned int minor = MINOR(inode->i_rdev);
- + char c, *temp = buf;
- +
- +#ifdef LP_DEBUG
- + if (jiffies-lp_last_call > LP_TIME(minor)) {
- + lp_total_chars = 0;
- + lp_max_count = 1;
- + }
- + lp_last_call = jiffies;
- +#endif
- +
- + temp = buf;
- + while (count > 0) {
- + c = get_fs_byte(temp);
- + retval = lp_char_polled(c, minor);
- + /* only update counting vars if character was printed */
- + if (retval) { count--; temp++;
- +#ifdef LP_DEBUG
- + lp_total_chars++;
- +#endif
- + }
- + if (!retval) { /* if printer timed out */
- + int status = LP_S(minor);
- +
- + if (status & LP_POUTPA) {
- + printk("lp%d out of paper\n", minor);
- + if(LP_F(minor) & LP_ABORT)
- + return temp-buf?temp-buf:-ENOSPC;
- + current->state = TASK_INTERRUPTIBLE;
- + current->timeout = jiffies + LP_TIMEOUT_POLLED;
- + schedule();
- + } else
- + if (!(status & LP_PSELECD)) {
- + printk("lp%d off-line\n", minor);
- + if(LP_F(minor) & LP_ABORT)
- + return temp-buf?temp-buf:-EIO;
- + current->state = TASK_INTERRUPTIBLE;
- + current->timeout = jiffies + LP_TIMEOUT_POLLED;
- + schedule();
- + } else
- + /* not offline or out of paper. on fire? */
- + if (!(status & LP_PERRORP)) {
- + printk("lp%d reported invalid error status (on fire, eh?)\n", minor);
- + if(LP_F(minor) & LP_ABORT)
- + return temp-buf?temp-buf:-EFAULT;
- + current->state = TASK_INTERRUPTIBLE;
- + current->timeout = jiffies + LP_TIMEOUT_POLLED;
- + schedule();
- + }
- +
- + /* check for signals before going to sleep */
- + if (current->signal & ~current->blocked) {
- + if (temp != buf)
- + return temp-buf;
- + else
- + return -EINTR;
- + }
- +#ifdef LP_DEBUG
- + printk("lp sleeping at %d characters for %d jiffies\n",
- + lp_total_chars, LP_TIME(minor));
- + lp_total_chars=0;
- +#endif
- + current->state = TASK_INTERRUPTIBLE;
- + current->timeout = jiffies + LP_TIME(minor);
- + schedule();
- + }
- + }
- + return temp-buf;
- +}
- +
- +static int lp_write(struct inode * inode, struct file * file, char * buf, int count)
- +{
- + if (LP_IRQ(MINOR(inode->i_rdev)))
- + return lp_write_interrupt(inode, file, buf, count);
- + else
- + return lp_write_polled(inode, file, buf, count);
- +}
- +
- +static int lp_lseek(struct inode * inode, struct file * file,
- + off_t offset, int origin)
- +{
- + return -ESPIPE;
- +}
- +
- +static int lp_open(struct inode * inode, struct file * file)
- +{
- + unsigned int minor = MINOR(inode->i_rdev);
- + int ret;
- + unsigned int irq;
- +
- + if (minor >= LP_NO)
- + return -ENODEV;
- + if ((LP_F(minor) & LP_EXIST) == 0)
- + return -ENODEV;
- + if (LP_F(minor) & LP_BUSY)
- + return -EBUSY;
- +
- + if ((irq = LP_IRQ(minor))) {
- + lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
- + if (!lp_table[minor].lp_buffer)
- + return -ENOMEM;
- +
- + ret = request_irq(irq, lp_interrupt, SA_INTERRUPT, "printer");
- + if (ret) {
- + kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
- + lp_table[minor].lp_buffer = NULL;
- + printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret);
- + return ret;
- + }
- + }
- +
- + LP_F(minor) |= LP_BUSY;
- +#ifdef MODULE
- + MOD_INC_USE_COUNT;
- +#endif
- + return 0;
- +}
- +
- +static void lp_release(struct inode * inode, struct file * file)
- +{
- + unsigned int minor = MINOR(inode->i_rdev);
- + unsigned int irq;
- +
- + if ((irq = LP_IRQ(minor))) {
- + free_irq(irq);
- + kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
- + lp_table[minor].lp_buffer = NULL;
- + }
- +
- + LP_F(minor) &= ~LP_BUSY;
- +#ifdef MODULE
- + MOD_DEC_USE_COUNT;
- +#endif
- +}
- +
- +
- +static int lp_ioctl(struct inode *inode, struct file *file,
- + unsigned int cmd, unsigned long arg)
- +{
- + unsigned int minor = MINOR(inode->i_rdev);
- + int retval = 0;
- +
- +#ifdef LP_DEBUG
- + printk("lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
- +#endif
- + if (minor >= LP_NO)
- + return -ENODEV;
- + if ((LP_F(minor) & LP_EXIST) == 0)
- + return -ENODEV;
- + switch ( cmd ) {
- + case LPTIME:
- + LP_TIME(minor) = arg;
- + break;
- + case LPCHAR:
- + LP_CHAR(minor) = arg;
- + break;
- + case LPABORT:
- + if (arg)
- + LP_F(minor) |= LP_ABORT;
- + else
- + LP_F(minor) &= ~LP_ABORT;
- + break;
- + case LPWAIT:
- + LP_WAIT(minor) = arg;
- + break;
- + case LPSETIRQ: {
- + int oldirq;
- + int newirq = arg;
- + struct lp_struct *lp = &lp_table[minor];
- +
- + if (!suser())
- + return -EPERM;
- +
- + oldirq = LP_IRQ(minor);
- +
- + /* Allocate buffer now if we are going to need it */
- + if (!oldirq && newirq) {
- + lp->lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
- + if (!lp->lp_buffer)
- + return -ENOMEM;
- + }
- +
- + if (oldirq) {
- + free_irq(oldirq);
- + }
- + if (newirq) {
- + /* Install new irq */
- + if ((retval = request_irq(newirq, lp_interrupt, SA_INTERRUPT, "printer"))) {
- + if (oldirq) {
- + /* restore old irq */
- + request_irq(oldirq, lp_interrupt, SA_INTERRUPT, "printer");
- + } else {
- + /* We don't need the buffer */
- + kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
- + lp->lp_buffer = NULL;
- + }
- + return retval;
- + }
- + }
- + if (oldirq && !newirq) {
- + /* We don't need the buffer */
- + kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
- + lp->lp_buffer = NULL;
- + }
- + LP_IRQ(minor) = newirq;
- + lp_reset(minor);
- + break;
- + }
- + case LPGETIRQ:
- + retval = LP_IRQ(minor);
- + break;
- + default:
- + retval = -EINVAL;
- + }
- + return retval;
- +}
- +
- +
- +static struct file_operations lp_fops = {
- + lp_lseek,
- + NULL, /* lp_read */
- + lp_write,
- + NULL, /* lp_readdir */
- + NULL, /* lp_select */
- + lp_ioctl,
- + NULL, /* lp_mmap */
- + lp_open,
- + lp_release
- +};
- +
- +#ifndef MODULE
- +
- +long lp_init(long kmem_start)
- +{
- + int offset = 0;
- + unsigned int testvalue = 0;
- + int count = 0;
- +
- + if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
- + printk("unable to get major %d for line printer\n", LP_MAJOR);
- + return kmem_start;
- + }
- + /* take on all known port values */
- + for (offset = 0; offset < LP_NO; offset++) {
- + /* write to port & read back to check */
- + outb_p( LP_DUMMY, LP_B(offset));
- + for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
- + ;
- + testvalue = inb_p(LP_B(offset));
- + if (testvalue == LP_DUMMY) {
- + LP_F(offset) |= LP_EXIST;
- + lp_reset(offset);
- + printk("lp_init: lp%d exists, ", offset);
- + if (LP_IRQ(offset))
- + printk("using IRQ%d\n", LP_IRQ(offset));
- + else
- + printk("using polling driver\n");
- + count++;
- + }
- + }
- + if (count == 0)
- + printk("lp_init: no lp devices found\n");
- + return kmem_start;
- +}
- +
- +#else
- +
- +char kernel_version[]= UTS_RELEASE;
- +
- +int init_module(void)
- +{
- + int offset = 0;
- + unsigned int testvalue = 0;
- + int count = 0;
- +
- + if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
- + printk("unable to get major %d for line printer\n", LP_MAJOR);
- + return -EIO;
- + }
- + /* take on all known port values */
- + for (offset = 0; offset < LP_NO; offset++) {
- + /* write to port & read back to check */
- + outb_p( LP_DUMMY, LP_B(offset));
- + for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
- + ;
- + testvalue = inb_p(LP_B(offset));
- + if (testvalue == LP_DUMMY) {
- + LP_F(offset) |= LP_EXIST;
- + lp_reset(offset);
- + printk("lp_init: lp%d exists, ", offset);
- + if (LP_IRQ(offset))
- + printk("using IRQ%d\n", LP_IRQ(offset));
- + else
- + printk("using polling driver\n");
- + count++;
- + }
- + }
- + if (count == 0)
- + printk("lp_init: no lp devices found\n");
- + return 0;
- +}
- +
- +void cleanup_module(void)
- +{
- + if(MOD_IN_USE)
- + printk("lp: busy - remove delayed\n");
- + else
- + unregister_chrdev(LP_MAJOR,"lp");
- +}
- +
- +#endif
- diff -r -u -N linux.orig/arch/arm/drivers/char/mem.c linux.arm/arch/arm/drivers/char/mem.c
- --- linux.orig/arch/arm/drivers/char/mem.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/mem.c Fri Oct 27 23:14:24 1995
- @@ -0,0 +1,388 @@
- +/*
- + * linux/drivers/char/mem.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + */
- +
- +#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 <asm/segment.h>
- +#include <asm/io.h>
- +
- +#ifdef CONFIG_SOUND
- +extern long soundcard_init(long mem_start);
- +#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,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;
- + int read;
- +
- + if (count < 0)
- + return -EINVAL;
- + if (p >= high_memory)
- + return 0;
- + if (count > high_memory - p)
- + count = high_memory - p;
- + read = 0;
- + while (p < PAGE_SIZE && count > 0) {
- + put_fs_byte(0,buf);
- + buf++;
- + p++;
- + count--;
- + read++;
- + }
- + memcpy_tofs(buf,(void *) p,count);
- + read += count;
- + file->f_pos += read;
- + return read;
- +}
- +
- +static int write_mem(struct inode * inode, struct file * file,char * buf, int count)
- +{
- + unsigned long p = file->f_pos;
- + int written;
- +
- + if (count < 0)
- + return -EINVAL;
- + if (p >= high_memory)
- + return 0;
- + if (count > high_memory - p)
- + count = high_memory - p;
- + written = 0;
- + while (p < PAGE_SIZE && count > 0) {
- + /* Hmm. Do something? */
- + buf++;
- + p++;
- + count--;
- + written++;
- + }
- + memcpy_fromfs((void *) p,buf,count);
- + written += count;
- + file->f_pos += written;
- + 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;
- +#ifndef __arm__
- + if (x86 > 3 && vma->vm_offset >= high_memory)
- + vma->vm_page_prot |= PAGE_PCD;
- +#endif
- + 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_fs_byte(inb(i),tmp);
- + i++;
- + tmp++;
- + }
- + file->f_pos = i;
- + return tmp-buf;
- +}
- +
- +static int write_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) {
- + outb(get_fs_byte(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,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_fs_byte(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,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 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 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;
- + 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 */
- +};
- +
- +#ifdef CONFIG_FTAPE
- +char* ftape_big_buffer;
- +#endif
- +
- +long chr_dev_init(long mem_start, long mem_end)
- +{
- + if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
- + printk("unable to get major %d for memory devs\n", MEM_MAJOR);
- + mem_start = tty_init(mem_start);
- +#ifdef CONFIG_PRINTER
- + mem_start = lp_init(mem_start);
- +#endif
- +#ifdef CONFIG_ARMMOUSE
- + mem_start = mouse_init(mem_start);
- +#endif
- +/*#ifdef CONFIG_SOUND
- + mem_start = soundcard_init(mem_start);
- +#endif*/
- +#ifdef CONFIG_QIC02_TAPE
- + mem_start = qic02_tape_init(mem_start);
- +#endif
- +/*
- + * Rude way to allocate kernel memory buffer for tape device
- + */
- +#ifdef CONFIG_FTAPE
- + /* allocate NR_FTAPE_BUFFERS 32Kb buffers at aligned address */
- + ftape_big_buffer= (char*) ((mem_start + 0x7fff) & ~0x7fff);
- + printk( "ftape: allocated %d buffers aligned at: %p\n",
- + NR_FTAPE_BUFFERS, ftape_big_buffer);
- + mem_start = (long) ftape_big_buffer + NR_FTAPE_BUFFERS * 0x8000;
- +#endif
- + return mem_start;
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/char/mouse.c linux.arm/arch/arm/drivers/char/mouse.c
- --- linux.orig/arch/arm/drivers/char/mouse.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/mouse.c Fri Oct 27 23:14:24 1995
- @@ -0,0 +1,68 @@
- +/*
- + * linux/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.
- + */
- +
- +#include <linux/fs.h>
- +#include <linux/errno.h>
- +#include <linux/mouse.h>
- +#include <linux/config.h>
- +#include <linux/kernel.h>
- +#include <linux/major.h>
- +
- +/*
- + * note that you can remove any or all of the drivers by undefining
- + * the minor values in <linux/mouse.h>
- + */
- +extern struct file_operations arch_mouse_fops;
- +extern unsigned long arch_mouse_init(unsigned long);
- +
- +static int mouse_open(struct inode * inode, struct file * file)
- +{
- + int minor = MINOR(inode->i_rdev);
- +
- + switch (minor) {
- +#ifdef CONFIG_ARMMOUSE
- + case 4:
- + file->f_op = &arch_mouse_fops;
- + break;
- +#endif
- + default:
- + 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 */
- +};
- +
- +unsigned long mouse_init(unsigned long kmem_start)
- +{
- +#ifdef CONFIG_ARMMOUSE
- + kmem_start = arch_mouse_init(kmem_start);
- +#endif
- + if (register_chrdev(MOUSE_MAJOR, "mouse", &mouse_fops))
- + printk("unable to get major %d for mouse devices\n",
- + MOUSE_MAJOR);
- + return kmem_start;
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/char/n_tty.c linux.arm/arch/arm/drivers/char/n_tty.c
- --- linux.orig/arch/arm/drivers/char/n_tty.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/n_tty.c Fri Oct 27 23:14:25 1995
- @@ -0,0 +1,1021 @@
- +/*
- + * 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.
- + */
- +
- +#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);
- +}
- +extern int debug_flag;
- +static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
- +{if (debug_flag) printk("received from queue: n_tty_receive_char");
- + 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 (debug_flag) printk("trace 1\n");
- + /*
- + * 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 (debug_flag) printk("trace 2\n");
- + 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 (debug_flag) printk("trace 3\n");
- + 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, unsigned char *cp,
- + char *fp, int count)
- +{
- + unsigned char *p;
- + char *f, flags = 0;
- + int i;
- +if (debug_flag) printk("n_tty_receive_buf\n");
- + if (!tty->read_buf)
- + return;
- +if (debug_flag) printk("trace a\n");
- + if (tty->real_raw) {
- +if (debug_flag) printk("trace b\n");
- + 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 {
- +if (debug_flag) printk("trace c\n");
- + 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 (debug_flag) printk("trace d\n");
- + if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
- +if (debug_flag) printk("trace e: %p %p\n", tty->fasync, tty->read_wait);
- + 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->sigaction[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/32);
- +
- + 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((unsigned char *) 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_fs_byte(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_fs_byte(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_fs_byte(c, b++);
- + if (--nr)
- + continue;
- + break;
- + }
- + if (--tty->canon_data < 0) {
- + tty->canon_data = 0;
- + }
- + if (c != __DISABLED_CHAR) {
- + put_fs_byte(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,
- + unsigned char * buf, unsigned int nr)
- +{
- + struct wait_queue wait = { current, NULL };
- + int c;
- + 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_fs_byte(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 -r -u -N linux.orig/arch/arm/drivers/char/n_tty.c.orig linux.arm/arch/arm/drivers/char/n_tty.c.orig
- --- linux.orig/arch/arm/drivers/char/n_tty.c.orig Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/n_tty.c.orig Fri Oct 27 23:14:38 1995
- @@ -0,0 +1,1007 @@
- +/*
- + * 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.
- + */
- +
- +#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) && L_IEXTEN(tty)) {
- + start_tty(tty);
- + return;
- + }
- +
- + if (I_ISTRIP(tty))
- + c &= 0x7f;
- + if (I_IUCLC(tty) && L_IEXTEN(tty))
- + c=tolower(c);
- +
- + /*
- + * 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, unsigned char *cp,
- + char *fp, int count)
- +{
- + 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, let as many
- + * characters through as possible, so that the excess
- + * characters can be "beeped".
- + */
- + if (L_ICANON(tty))
- + return N_TTY_BUF_SIZE;
- +
- + if (left > 0)
- + return left;
- + return 0;
- +}
- +
- +int is_ignored(int sig)
- +{
- + return ((current->blocked & (1<<(sig-1))) ||
- + (current->sigaction[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/32);
- +
- + 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)
- +{
- + tty_wait_until_sent(tty, 0);
- + 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;
- + 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)
- +{
- + int c;
- + unsigned char *b = buf;
- + int minimum, time;
- + int retval = 0;
- + int size;
- + struct wait_queue wait = { current, NULL };
- +
- +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;
- +printk("read_chan: suspending: tpgrp = %p, ttypgrp = %p\n",current->pgrp, tty->pgrp);
- + 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_fs_byte(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_fs_byte(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_fs_byte(c, b++);
- + if (--nr)
- + continue;
- + break;
- + }
- + if (--tty->canon_data < 0) {
- + tty->canon_data = 0;
- + }
- + if (c != __DISABLED_CHAR) {
- + put_fs_byte(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,
- + unsigned char * buf, unsigned int nr)
- +{
- + int c;
- + unsigned char *b = buf;
- + int retval = 0;
- + struct wait_queue wait = { current, NULL };
- +
- + /* 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_fs_byte(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 -r -u -N linux.orig/arch/arm/drivers/char/ptr.c linux.arm/arch/arm/drivers/char/ptr.c
- --- linux.orig/arch/arm/drivers/char/ptr.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/ptr.c Fri Oct 27 23:14:20 1995
- @@ -0,0 +1,210 @@
- +/*
- + * ptr.c
- + *
- + * Archimedes pointer driver
- + *
- + * By R.M.King
- + *
- + * This pointer driver provides a total of 5 possible pointers.
- + *
- + */
- +#include <linux/errno.h>
- +#include <linux/mm.h>
- +
- +#include <asm/segment.h>
- +
- +extern void vidc_write(int reg, int val);
- +extern void memc_write(int reg, int val);
- +
- +/* Have to go into a separate file */
- +#define N_PTRS 5
- +
- +#define ARC_MOUSE_ON 0x7A01
- +#define ARC_MOUSE_OFF 0x7A02
- +#define ARC_MOUSE_SETPOS 0x7A03
- +#define ARC_MOUSE_SHAPE 0x7A04
- +#define ARC_MOUSE_SELECT 0x7A05
- +
- +#define MAX_X 799
- +#define MAX_Y 599
- +
- +struct pointer_define
- +{
- + unsigned char no;
- + unsigned char x; /* X size */
- + unsigned char y; /* Y size */
- + unsigned char x_hot; /* Hot spot X offset */
- + unsigned char y_hot; /* Hot spot Y offset */
- + char ndata[1];
- +};
- +
- +static unsigned char current_ptr=0;
- +static unsigned long ptr_y=0;
- +static unsigned long ptr_x=0;
- +static int ptr_toupdate=0;
- +static int ptr_displayed=0;
- +
- +static struct ptr_data
- +{
- + unsigned char x_size;
- + unsigned char y_size;
- + unsigned char x_hot;
- + unsigned char y_hot;
- +} data[N_PTRS];
- +
- +void ptr_update(void)
- +{
- + long y;
- + y = ptr_y - (int)data[current_ptr].y_hot;
- + if(ptr_toupdate & 3)
- + {
- + long x,ry,ey;
- + x = 195 + ptr_x - (int)data[current_ptr].x_hot;
- + ry = 23 + (y < 0 ? 0 : y);
- + ey = 23 + y + (int)data[current_ptr].y_size;
- +
- + if(ey < 7)
- + ey=7;
- +
- + vidc_write(0x98, (x << 13));
- + vidc_write(0xB8, (ry << 14));
- + vidc_write(0xBC, (ey << 14));
- + }
- + if(ptr_toupdate & 3)
- + {
- + if(y < 0)
- + memc_write(3, (0x40100+0x300*current_ptr+8*(-y))>>2);
- + else
- + memc_write(3, (0x40100+0x300*current_ptr)>>2);
- + }
- +
- + if(ptr_toupdate & 4)
- + {
- + vidc_write(0x98, 0);
- + vidc_write(0xB8, 0);
- + vidc_write(0xBC, 0x4000);
- + }
- + ptr_toupdate = 0;
- +}
- +
- +int ptr_ioctl(struct tty_struct *tty, struct file * file,
- + unsigned int cmd, unsigned long arg)
- +{
- + int i;
- + /* Check to make sure that this tty is local */
- +
- + switch(cmd)
- + {
- + case ARC_MOUSE_ON:
- + ptr_displayed = 1;
- + ptr_toupdate |= 3;
- + return 0;
- +
- + case ARC_MOUSE_OFF:
- + ptr_displayed = 0;
- + ptr_toupdate |= 4;
- + return 0;
- +
- + case ARC_MOUSE_SETPOS:
- + i = verify_area(VERIFY_READ, (void *)arg, 8);
- + if(i)
- + return i;
- + ptr_x = get_fs_long((unsigned long *)arg);
- + ptr_y = get_fs_long(((unsigned long *)arg)+1);
- + if(ptr_x > MAX_X)
- + ptr_x = MAX_X;
- + if(ptr_y > MAX_Y)
- + ptr_y = MAX_Y;
- +
- + ptr_toupdate |= ptr_displayed?3:0;
- + return 0;
- +
- + case ARC_MOUSE_SHAPE:
- + {
- + struct pointer_define ptrdef;
- + int total_bytes,ptrbuf,y;
- + unsigned char ptr;
- +
- + i = verify_area(VERIFY_READ, (void *)arg,
- + sizeof(struct pointer_define)-1);
- + if (i)
- + return i;
- +
- + memcpy_fromfs((void *)&ptrdef, (void *)arg,
- + sizeof(struct pointer_define)-1);
- +
- + total_bytes = ptrdef.x * ptrdef.y / 4;
- + ptr = ptrdef.no;
- +
- + if(total_bytes > 0x300 || ptr > 4)
- + return -EINVAL;
- + i = verify_area(VERIFY_READ,
- + (void *)(arg + sizeof(struct pointer_define)-1), total_bytes);
- + if (i)
- + return i;
- +
- + /* Hmm. Or shall I switch it off, update & switch on? */
- + if(ptr == current_ptr && ptr_displayed)
- + return -EBUSY;
- +
- + data[ptr].x_size = ptrdef.x;
- + data[ptr].y_size = ptrdef.y;
- + data[ptr].x_hot = ptrdef.x_hot;
- + data[ptr].y_hot = ptrdef.y_hot;
- +
- + ptrbuf = 0x100 + 0x300 * ptr;
- + memset((void *)ptrbuf, 0, 0x300);
- +
- + for(y=0; y < ptrdef.y; y++)
- + {
- + memcpy_fromfs((void *)(ptrbuf + 8 * y),
- + (void *)(arg + 5 + y*ptrdef.x/4),
- + ptrdef.x / 4);
- + }
- + return 0;
- + }
- + case ARC_MOUSE_SELECT:
- + if(arg > 4)
- + return -EINVAL;
- +
- + current_ptr = arg;
- + ptr_toupdate |= ptr_displayed?3:0;
- + return 0;
- + }
- + return -EINVAL;
- +}
- +
- +void ptr_concontrol(int on_off, unsigned int pos)
- +{
- + if(on_off)
- + {
- + if(!ptr_displayed)
- + {
- + int i;
- + current_ptr = 0;
- + ptr_displayed = 1;
- + data[0].x_size = 4;
- + data[0].y_size = 8;
- + data[0].x_hot = 0;
- + data[0].y_hot = 0;
- + for(i = 0x100; i < 0x300; i++)
- + *(unsigned char *)i=0x55 << (i & 1);
- + }
- +
- + ptr_x = pos % (MAX_X+1);
- + ptr_y = pos / (MAX_X+1);
- +
- + if(ptr_y > MAX_Y)
- + ptr_y = MAX_Y;
- +
- + ptr_toupdate |= 3;
- + }
- + else
- + {
- + if(ptr_displayed)
- + {
- + ptr_displayed = 0;
- + ptr_toupdate |= 4;
- + }
- + }
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/char/pty.c linux.arm/arch/arm/drivers/char/pty.c
- --- linux.orig/arch/arm/drivers/char/pty.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/pty.c Fri Oct 27 23:14:25 1995
- @@ -0,0 +1,262 @@
- +/*
- + * linux/drivers/char/pty.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + */
- +
- +/*
- + * 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 <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,
- + 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;
- +}
- +
- +long pty_init(long kmem_start)
- +{
- + 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 kmem_start;
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/char/serial.c linux.arm/arch/arm/drivers/char/serial.c
- --- linux.orig/arch/arm/drivers/char/serial.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/serial.c Fri Oct 27 23:14:26 1995
- @@ -0,0 +1,2691 @@
- +/*
- + * 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:
- + *
- + * long rs_init(long);
- + * int rs_open(struct tty_struct * tty, struct file * filp)
- + *
- + * Slight modifications for ARM
- + */
- +
- +#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/major.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
- +#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,
- + dev_t device, const char *routine)
- +{
- +#ifdef SERIAL_PARANOIA_CHECK
- + static const char *badmagic =
- + "Warning: bad magic number for serial struct (%d, %d) in %s\n";
- + static const char *badinfo =
- + "Warning: null async_struct for (%d, %d) in %s\n";
- +
- + if (!info) {
- + printk(badinfo, MAJOR(device), MINOR(device), routine);
- + return 1;
- + }
- + if (info->magic != SERIAL_MAGIC) {
- + printk(badmagic, MAJOR(device), MINOR(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);
- + 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*HZ) {
- + 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 * HZ;
- + 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 = 6000; /* 60 seconds === a long time :-) */
- +
- + info = IRQ_ports[irq];
- + if (!info) {
- + IRQ_timeout[irq] = 6000;
- + 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;
- + }
- +#ifdef __alpha__
- + 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;
- + 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 */
- + cval = cflag & (CSIZE | CSTOPB);
- + cval >>= 4;
- + 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,
- + 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;
- + info->closing_wait = new_serial.closing_wait;
- +
- + 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_fs_long(result,(unsigned long *) 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_fs_long(result,(unsigned long *) 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_fs_long((unsigned long *) 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+10;
- + while (timeout >= jiffies)
- + ;
- +
- + rs_triggered = 0; /* Reset after letting things settle */
- +
- + timeout = jiffies+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;
- +
- + multi->port1 = new_multi.port1;
- + multi->mask1 = new_multi.mask1;
- + multi->match1 = new_multi.match1;
- +
- + multi->port2 = new_multi.port2;
- + multi->mask2 = new_multi.mask2;
- + multi->match2 = new_multi.match2;
- +
- + multi->port3 = new_multi.port3;
- + multi->mask3 = new_multi.mask3;
- + multi->match3 = new_multi.match3;
- +
- + multi->port4 = new_multi.port4;
- + multi->mask4 = new_multi.mask4;
- + multi->match4 = new_multi.match4;
- +
- + 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;
- + 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.
- + */
- +#ifdef __alpha__
- + /*
- + * 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!
- + */
- +long rs_init(long kmem_start)
- +{
- + 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 = 50;
- + info->closing_wait = 3000;
- + 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 kmem_start;
- +}
- +
- +/*
- + * 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 -r -u -N linux.orig/arch/arm/drivers/char/serial.c.orig linux.arm/arch/arm/drivers/char/serial.c.orig
- --- linux.orig/arch/arm/drivers/char/serial.c.orig Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/serial.c.orig Fri Oct 27 23:14:39 1995
- @@ -0,0 +1,2478 @@
- +/*
- + * linux/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:
- + *
- + * long rs_init(long);
- + * int rs_open(struct tty_struct * tty, struct file * filp)
- + */
- +
- +#include <linux/config.h>
- +#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/major.h>
- +#include <linux/string.h>
- +#include <linux/fcntl.h>
- +#include <linux/ptrace.h>
- +#include <linux/major.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.
- + */
- +int ser_check=0;
- +#define SERIAL_PARANOIA_CHECK
- +#define CONFIG_SERIAL_NOPAUSE_IO
- +#define SERIAL_DO_RESTART
- +#define CONFIG_SERIAL_NEW_ISR
- +
- +#undef SERIAL_DEBUG_INTR
- +#undef SERIAL_DEBUG_OPEN
- +#undef SERIAL_DEBUG_FLOW
- +
- +#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 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 )
- +
- +/* Standard COM flags (except for COM4, because of the 8514 problem) */
- +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
- +#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF
- +
- +#define FOURPORT_FLAGS ASYNC_FOURPORT
- +#define ACCENT_FLAGS 0
- +#define BOCA_FLAGS 0
- +#define HUB6_FLAGS 0
- +
- +/*
- + * The following define the access methods for the HUB6 card. All
- + * access is through two ports for all 24 possible chips. The card is
- + * selected through the high 2 bits, the port on that card with the
- + * "middle" 3 bits, and the register on that port with the bottom
- + * 3 bits.
- + *
- + * While the access port and interrupt is configurable, the default
- + * port locations are 0x302 for the port control register, and 0x303
- + * for the data read/write register. Normally, the interrupt is at irq3
- + * but can be anything from 3 to 7 inclusive. Note that using 3 will
- + * require disabling com2.
- + */
- +
- +#define C_P(card,port) (((card)<<6|(port)<<3) + 1)
- +
- +struct async_struct rs_table[] = {
- + /* UART CLK PORT IRQ FLAGS */
- +#ifdef __arm__
- + { 0, BASE_BAUD, 0x3F8,10, STD_COM_FLAGS },
- + { 0, BASE_BAUD, 0x2F8,10, STD_COM_FLAGS }
- +#define PODULE
- +#ifdef PODULE
- + ,{ 0, BASE_BAUD, 0, 0, STD_COM_FLAGS },
- + { 0, BASE_BAUD, 0, 0, STD_COM_FLAGS },
- + { 0, BASE_BAUD, 0, 0, STD_COM_FLAGS },
- + { 0, BASE_BAUD, 0, 0, STD_COM_FLAGS },
- + { 0, BASE_BAUD, 0, 0, STD_COM_FLAGS },
- + { 0, BASE_BAUD, 0, 0, STD_COM_FLAGS },
- + { 0, BASE_BAUD, 0, 0, STD_COM_FLAGS },
- + { 0, BASE_BAUD, 0, 0, STD_COM_FLAGS }
- +#endif
- +#else
- + { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */
- + { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */
- + { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */
- + { 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */
- +
- + { 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, /* ttyS4 */
- + { 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS }, /* ttyS5 */
- + { 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS }, /* ttyS6 */
- + { 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS }, /* ttyS7 */
- +
- + { 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS }, /* ttyS8 */
- + { 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS }, /* ttyS9 */
- + { 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS }, /* ttyS10 */
- + { 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS }, /* ttyS11 */
- +
- + { 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS }, /* ttyS12 */
- + { 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS }, /* ttyS13 */
- + { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS14 (spare; user configurable) */
- + { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS15 (spare; user configurable) */
- +
- + { 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS }, /* ttyS16 */
- + { 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS }, /* ttyS17 */
- + { 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS }, /* ttyS18 */
- + { 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS }, /* ttyS19 */
- + { 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS }, /* ttyS20 */
- + { 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS }, /* ttyS21 */
- + { 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS }, /* ttyS22 */
- + { 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS }, /* ttyS23 */
- + { 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS }, /* ttyS24 */
- + { 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS }, /* ttyS25 */
- + { 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS }, /* ttyS26 */
- + { 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS }, /* ttyS27 */
- + { 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS }, /* ttyS28 */
- + { 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS }, /* ttyS29 */
- + { 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS }, /* ttyS30 */
- + { 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS }, /* ttyS31 */
- +
- +/* You can have up to four HUB6's in the system, but I've only
- + * included two cards here for a total of twelve ports.
- + */
- + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) }, /* ttyS32 */
- + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) }, /* ttyS33 */
- + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) }, /* ttyS34 */
- + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) }, /* ttyS35 */
- + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) }, /* ttyS36 */
- + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) }, /* ttyS37 */
- + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) }, /* ttyS32 */
- + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) }, /* ttyS33 */
- + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) }, /* ttyS34 */
- + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) }, /* ttyS35 */
- + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS36 */
- + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS37 */
- +#endif
- +};
- +
- +#define NR_PORTS (sizeof(rs_table)/sizeof(struct async_struct))
- +#ifdef PODULE
- +int dser_sport = 2; /* This is the start of the serial port NULL entries */
- +#endif
- +
- +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,
- + dev_t device, const char *routine)
- +{
- +#ifdef SERIAL_PARANOIA_CHECK
- + static const char *badmagic =
- + "Warning: bad magic number for serial struct (%d, %d) in %s\n";
- + static const char *badinfo =
- + "Warning: null async_struct for (%d, %d) in %s\n";
- +
- + if (!info) {
- + printk(badinfo, MAJOR(device), MINOR(device), routine);
- + return 1;
- + }
- + if (info->magic != SERIAL_MAGIC) {
- + printk(badmagic, MAJOR(device), MINOR(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)
- +{
- + 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;
- +
- + do {
- + ch = serial_inp(info, UART_RX);
- + if (*status & (info->ignore_status_mask & ~UART_LSR_OE))/* RMK - stupid to ignore errors! */
- + continue;
- + if (tty->flip.count >= TTY_FLIPBUF_SIZE)
- + break;
- + tty->flip.count++;
- + if (*status & info->read_status_mask) {
- + if (*status & (UART_LSR_BI)) {
- + *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;
- + } else
- + *tty->flip.flag_buf_ptr++ = 0;
- + *tty->flip.char_buf_ptr++ = ch;
- + } while ((*status = serial_inp(info, UART_LSR)) & UART_LSR_DR);
- + 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;
- +#ifdef CONFIG_SERIAL_NEW_ISR
- + serial_out(info, UART_IER, info->IER);
- +#endif
- + 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;
- +#ifdef CONFIG_SERIAL_NEW_ISR
- + serial_out(info, UART_IER, info->IER);
- +#endif
- + }
- +}
- +
- +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...\n", 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...\n");
- +#endif
- + rs_sched_event(info, RS_EVENT_HANGUP);
- + }
- + }
- + 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;
- +#ifdef CONFIG_SERIAL_NEW_ISR
- + serial_out(info, UART_IER, info->IER);
- +#endif
- + 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;
- +#ifdef CONFIG_SERIAL_NEW_ISR
- + serial_out(info, UART_IER, info->IER);
- +#endif
- + }
- + }
- + }
- +}
- +
- +#ifdef CONFIG_SERIAL_NEW_ISR
- +/*
- + * This is the serial driver's generic interrupt routine
- + */
- +static void rs_interrupt(int irq)
- +{
- + int status;
- + struct async_struct * info;
- + int pass_counter = 0;
- + struct async_struct *end_mark = 0;
- +
- +#ifdef SERIAL_DEBUG_INTR
- + printk("rs_interrupt(%d)...", irq);
- +#endif
- +
- + info = IRQ_ports[irq];
- + if (!info)
- + return;
- +
- + 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);
- + 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++ > 64) {
- +#if 0
- + printk("rs loop break\n");
- +#endif
- + break; /* Prevent infinite loops */
- + }
- + continue;
- + }
- + } while (end_mark != info);
- +}
- +
- +/*
- + * This is the serial driver's interrupt routine for a single port
- + */
- +static void rs_interrupt_single(int irq)
- +{
- + int status;
- + int pass_counter = 0;
- + struct async_struct * info;
- +
- +#ifdef SERIAL_DEBUG_INTR
- + printk("rs_interrupt_single(%d)...", irq);
- +#endif
- +
- + info = IRQ_ports[irq];
- + if (!info || !info->tty)
- + return;
- +
- + do {
- + status = serial_inp(info, UART_LSR);
- + 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++ > 64) {
- +#if 0
- + printk("rs_single loop break.\n");
- +#endif
- + break;
- + }
- + } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
- + info->last_active = jiffies;
- +}
- +
- +#else /* CONFIG_SERIAL_NEW_ISR */
- +
- +/*
- + * This is the serial driver's generic interrupt routine
- + */
- +static void rs_interrupt(int irq)
- +{
- + int status;
- + struct async_struct * info;
- + int done = 1, pass_counter = 0;
- +
- +
- +#ifdef SERIAL_DEBUG_INTR
- + printk("rs_interrupt(%d)...", irq);
- +#endif
- +
- + info = IRQ_ports[irq];
- + if (!info)
- + return;
- +
- + while (1) {
- + if (!info->tty)
- + goto next;
- +
- + serial_outp(info, UART_IER, 0);
- + status = serial_inp(info, UART_LSR);
- + if (status & UART_LSR_DR) {
- + receive_chars(info, &status);
- + done = 0;
- + }
- + check_modem_status(info);
- + if (status & UART_LSR_THRE)
- + transmit_chars(info, &done);
- +
- + next:
- + info = info->next_port;
- + if (!info) {
- + info = IRQ_ports[irq];
- + if (done)
- + break;
- + done = 1;
- + if (pass_counter++ > 64) {
- +#if 0
- + printk("rs loop break\n");
- +#endif
- + break; /* Prevent infinite loops */
- + }
- + }
- + }
- +
- + /*
- + * Reset the IER registers; info is already set up from the
- + * above while loop.
- + */
- + do
- + serial_outp(info, UART_IER, info->IER);
- + while ((info = info->next_port) != NULL);
- +}
- +
- +/*
- + * This is the serial driver's interrupt routine for a single port
- + */
- +static void rs_interrupt_single(int irq)
- +{
- + int status;
- + struct async_struct * info;
- +
- +
- +#ifdef SERIAL_DEBUG_INTR
- + printk("rs_interrupt_single(%d)...", irq);
- +#endif
- +
- + info = IRQ_ports[irq];
- + if (!info || !info->tty)
- + return;
- +
- + serial_outp(info, UART_IER, 0);
- + status = serial_inp(info, UART_LSR);
- + if (status & UART_LSR_DR)
- + receive_chars(info, &status);
- + check_modem_status(info);
- + if (status & UART_LSR_THRE)
- + transmit_chars(info, 0);
- +
- + /*
- + * Reset the IER register
- + */
- + serial_outp(info, UART_IER, info->IER);
- +}
- +
- +#endif /* CONFIG_SERIAL_NEW_ISR */
- +
- +/*
- + * -------------------------------------------------------------------
- + * 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_HANGUP, &info->event)) {
- + tty_hangup(tty);
- + wake_up_interruptible(&info->open_wait);
- + info->flags &= ~(ASYNC_NORMAL_ACTIVE|
- + ASYNC_CALLOUT_ACTIVE);
- + }
- + 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 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) >= 60*HZ) {
- + 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);
- + rs_interrupt(i);
- + } else
- + rs_interrupt_single(i);
- + sti();
- + }
- + }
- + last_strobe = jiffies;
- + timer_table[RS_TIMER].expires = jiffies + 60 * HZ;
- + timer_active |= 1 << RS_TIMER;
- +
- + if (IRQ_ports[0]) {
- + cli();
- + rs_interrupt(0);
- + 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 = 6000; /* 60 seconds === a long time :-) */
- +
- + info = IRQ_ports[irq];
- + if (!info) {
- + IRQ_timeout[irq] = 6000;
- + 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);
- +
- + 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 *) kmalloc(SERIAL_XMIT_SIZE,GFP_KERNEL);
- + if (!info->xmit_buf)
- + return -ENOMEM;
- + }
- +
- + save_flags(flags); cli();
- +
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("starting up ttys%d (irq %d)...\n", info->line, info->irq);
- +#endif
- +
- + /*
- + * Clear the FIFO buffers and disable them
- + * (they will be reenabled in change_speed())
- + */
- + 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);
- + 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 (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;
- + 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)....\n", 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) {
- + kfree_s(info->xmit_buf, SERIAL_XMIT_SIZE);
- + 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 == 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 */
- + cval = cflag & (CSIZE | CSTOPB);
- + cval >>= 4;
- + 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
- + 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;
- + 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;
- + if (I_IGNBRK(info->tty)) {
- + info->ignore_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;
- + }
- +
- + 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,
- + 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.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 > 31) || (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;
- +
- + 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;
- + }
- +
- +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;
- +}
- +
- +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_fs_long(result,(unsigned long *) value);
- + return 0;
- +}
- +
- +static int set_modem_info(struct async_struct * info, unsigned int cmd,
- + unsigned int *value)
- +{
- + unsigned int arg = get_fs_long((unsigned long *) 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+10;
- + while (timeout >= jiffies)
- + ;
- +
- + rs_triggered = 0; /* Reset after letting things settle */
- +
- + timeout = jiffies+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 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;
- +
- + 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 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;
- +
- + 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 (!(old_termios->c_cflag & CLOCAL) &&
- + (tty->termios->c_cflag & CLOCAL))
- + wake_up_interruptible(&info->open_wait);
- +}
- +
- +/*
- + * ------------------------------------------------------------
- + * 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;
- + if (info->flags & ASYNC_INITIALIZED) {
- + tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
- + /*
- + * 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);
- + 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;
- +
- + 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)
- +{
- + int retval;
- + int do_clocal = 0;
- + struct wait_queue wait = { current, NULL };
- +
- + /*
- + * 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, then make the check up front
- + * and then exit.
- + */
- + if (filp->f_flags & O_NONBLOCK) {
- + if (info->flags & ASYNC_CALLOUT_ACTIVE)
- + return -EBUSY;
- + info->flags |= ASYNC_NORMAL_ACTIVE;
- + return 0;
- + }
- +
- + if (info->normal_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 *) kmalloc(SERIAL_XMIT_SIZE,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...\n", 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.00 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;
- + 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);
- +
- + 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:
- + info->type = PORT_16550A;
- + info->xmit_fifo_size = 16;
- + 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;
- + }
- +
- + /*
- + * Reset the UART.
- + */
- + serial_outp(info, UART_MCR, 0x00);
- + 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!
- + */
- +long rs_init(long kmem_start)
- +{
- + 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;
- + }
- +
- + 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 = 50;
- + info->x_char = 0;
- + info->event = 0;
- + info->count = 0;
- + info->blocked_open = 0;
- + info->tqueue.routine = do_softint;
- + info->tqueue.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;
- + default:
- + printk("\n");
- + break;
- + }
- + }
- + return kmem_start;
- +}
- +
- +/*
- + * 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 -r -u -N linux.orig/arch/arm/drivers/char/tty_diff linux.arm/arch/arm/drivers/char/tty_diff
- --- linux.orig/arch/arm/drivers/char/tty_diff Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/tty_diff Fri Oct 27 23:14:40 1995
- @@ -0,0 +1,605 @@
- +--- tty_io.c Fri Jul 14 00:07:27 1995
- ++++ ../../../../drivers/char/tty_io.c Sun Sep 24 11:09:57 1995
- +@@ -52,6 +52,7 @@
- + #include <linux/mm.h>
- + #include <linux/string.h>
- + #include <linux/malloc.h>
- ++#include <linux/config.h>
- +
- + #include <asm/segment.h>
- + #include <asm/system.h>
- +@@ -59,18 +60,27 @@
- +
- + #include "kbd_kern.h"
- + #include "vt_kern.h"
- ++/* This to come */
- ++/*#include "selection.h"*/
- +
- + #define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
- + #define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
- +
- + #undef TTY_DEBUG_HANGUP
- +
- ++/* These to go */
- + extern int set_selection(const int arg, struct tty_struct *tty);
- + extern int paste_selection(struct tty_struct *tty);
- + extern int sel_loadlut(const int arg);
- + extern int mouse_reporting(void);
- + extern int shift_state;
- + extern int do_screendump(int arg);
- ++#define TTY_PARANOIA_CHECK
- ++#define CHECK_TTY_COUNT
- ++
- ++extern void do_blank_screen(int nopowersave);
- ++extern void do_unblank_screen(void);
- ++/*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 */
- +@@ -78,10 +80,12 @@
- +
- + /*
- + * 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;
- +
- +@@ -123,8 +127,6 @@
- + return(_tty_name(tty, buf));
- + }
- +
- +-#define TTY_PARANOIA_CHECK
- +-
- + inline int tty_paranoia_check(struct tty_struct *tty, dev_t device,
- + const char *routine)
- + {
- +@@ -146,18 +148,45 @@
- + 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 (%d, %d) tty->count(%d) != #fd's(%d) in %s\n",
- ++ MAJOR(tty->device), MINOR(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;
- + }
- +
- +@@ -175,6 +204,8 @@
- + 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);
- +@@ -313,6 +344,7 @@
- +
- + 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;
- +@@ -352,18 +384,22 @@
- + -i);
- + }
- + }
- +-
- +- if (tty->session > 0) {
- +- kill_sl(tty->session,SIGHUP,1);
- +- kill_sl(tty->session,SIGCONT,1);
- +- }
- +- tty->flags = 0;
- +- tty->session = 0;
- +- tty->pgrp = -1;
- ++
- + 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)
- +@@ -406,13 +442,19 @@
- + struct tty_struct *tty = current->tty;
- + struct task_struct *p;
- +
- +- if (!tty)
- ++ 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;
- +
- +@@ -466,6 +508,7 @@
- + return;
- + if (!vc_cons_allocated(new_console))
- + return;
- ++ last_console = fg_console;
- +
- + /*
- + * If we're switching, we could be going from KD_GRAPHICS to
- +@@ -511,11 +554,9 @@
- + if (old_vc_mode != vt_cons[new_console]->vc_mode)
- + {
- + if (vt_cons[new_console]->vc_mode == KD_TEXT)
- +- unblank_screen();
- +- else {
- +- timer_active &= ~(1<<BLANK_TIMER);
- +- blank_screen();
- +- }
- ++ do_unblank_screen();
- ++ else
- ++ do_blank_screen(1);
- + }
- +
- + /*
- +@@ -715,7 +756,7 @@
- + 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;
- ++ struct tty_driver *driver;
- + int retval;
- + int idx;
- +
- +@@ -733,6 +774,7 @@
- + 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 &&
- +@@ -772,9 +813,10 @@
- +
- + if (!*o_tty_loc && !o_tty) {
- + dev_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,
- +@@ -800,7 +841,7 @@
- + memset(o_ltp, 0, sizeof(struct termios));
- + goto repeat;
- + }
- +-
- ++
- + }
- + /* Now we have allocated all the structures: update all the pointers.. */
- + if (!*tp_loc) {
- +@@ -826,8 +867,15 @@
- + }
- + }
- + tty = NULL;
- +- } else
- ++ } 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;
- +@@ -887,12 +935,13 @@
- + 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;
- +@@ -955,7 +1004,7 @@
- + }
- + }
- + #endif
- +-
- ++
- + if (tty->driver.close)
- + tty->driver.close(tty, filp);
- + if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
- +@@ -974,6 +1023,9 @@
- + if (tty->count)
- + return;
- +
- ++ /*
- ++ * We're committed; at this point, we must not block!
- ++ */
- + if (o_tty) {
- + if (o_tty->count)
- + return;
- +@@ -981,10 +1033,11 @@
- + 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
- +@@ -1007,7 +1060,12 @@
- + (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;
- +@@ -1036,11 +1094,12 @@
- + 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));
- + }
- + }
- +
- +@@ -1077,11 +1136,12 @@
- + noctty = 1;
- + }
- + minor = MINOR(device);
- +-
- ++
- + retval = init_dev(device, &tty);
- +- filp->private_data = 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;
- +@@ -1118,6 +1178,7 @@
- + !current->tty &&
- + tty->session == 0) {
- + current->tty = tty;
- ++ current->tty_old_pgrp = 0;
- + tty->session = current->session;
- + tty->pgrp = current->pgrp;
- + }
- +@@ -1190,13 +1251,14 @@
- + if (!tty->fasync && !tty->read_wait)
- + tty->minimum_to_wake = N_TTY_BUF_SIZE;
- + }
- +- return 0;
- ++ return 0;
- + }
- +
- ++#if 0
- + /*
- + * XXX does anyone use this anymore?!?
- + */
- +-static int do_get_ps_info(int arg)
- ++static int do_get_ps_info(unsigned long arg)
- + {
- + struct tstruct {
- + int flag;
- +@@ -1207,7 +1269,7 @@
- + 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;
- +@@ -1220,10 +1282,11 @@
- + put_fs_byte(*c++, d++);
- + put_fs_long(1, (unsigned long *)(ts->present+n));
- + }
- +- else
- ++ else
- + put_fs_long(0, (unsigned long *)(ts->present+n));
- +- return(0);
- ++ return(0);
- + }
- ++#endif
- +
- + static int tty_ioctl(struct inode * inode, struct file * file,
- + unsigned int cmd, unsigned long arg)
- +@@ -1235,7 +1298,7 @@
- + 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;
- +@@ -1268,7 +1331,7 @@
- + retval = verify_area(VERIFY_READ, (void *) arg,
- + sizeof (struct winsize));
- + if (retval)
- +- return retval;
- ++ return retval;
- + memcpy_fromfs(&tmp_ws, (struct winsize *) arg,
- + sizeof (struct winsize));
- + if (memcmp(&tmp_ws, &tty->winsize,
- +@@ -1344,6 +1407,7 @@
- + return -EPERM;
- + }
- + current->tty = tty;
- ++ current->tty_old_pgrp = 0;
- + tty->session = current->session;
- + tty->pgrp = current->pgrp;
- + return 0;
- +@@ -1389,39 +1453,61 @@
- + arg = get_fs_long((unsigned long *) 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 (get_fs_byte((char *)arg))
- ++ switch (retval = get_fs_byte((char *)arg))
- + {
- + case 0:
- +- return do_screendump(arg);
- ++ 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:
- +- unblank_screen();
- ++ 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_fs_byte(0, arg);
- +-/* put_fs_byte(shift_state,arg); */
- ++ /*
- ++ * 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_fs_byte(shift_state,arg);
- + return 0;
- + case 7:
- + put_fs_byte(mouse_reporting(),arg);
- + return 0;
- +- default:
- ++/* 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,
- +@@ -1445,7 +1531,7 @@
- + * 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
- +@@ -1461,7 +1547,7 @@
- + int session;
- + int i;
- + struct file *filp;
- +-
- ++
- + if (!tty)
- + return;
- + session = tty->session;
- +@@ -1520,7 +1606,7 @@
- + count = tty->flip.count;
- + tty->flip.count = 0;
- + sti();
- +-
- ++
- + #if 0
- + if (count > tty->max_flip_cnt)
- + tty->max_flip_cnt = count;
- +@@ -1569,10 +1655,10 @@
- +
- + if (!driver->put_char)
- + driver->put_char = tty_default_put_char;
- +-
- ++
- + driver->prev = 0;
- + driver->next = tty_drivers;
- +- tty_drivers->prev = driver;
- ++ if (tty_drivers) tty_drivers->prev = driver;
- + tty_drivers = driver;
- + return error;
- + }
- +@@ -1585,31 +1671,32 @@
- + int retval;
- + struct tty_driver *p;
- + int found = 0;
- +- int major_inuse = 0;
- +-
- +- if (driver->refcount)
- ++ 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)
- +- major_inuse++;
- ++ othername = p->name;
- + }
- +
- +- if (!major_inuse) {
- ++ 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 = driver->next->prev;
- ++ driver->next->prev = driver->prev;
- +
- + return 0;
- + }
- +@@ -1628,20 +1715,19 @@
- + (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
- +
- + /*
- +- * Set up the standard termios. Individual tty drivers may
- ++ * 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
- ++ * set up the console device so that later boot sequences can
- + * inform about problems etc..
- + */
- + return con_init(kmem_start);
- +@@ -1662,6 +1748,10 @@
- +
- + kmem_start = kbd_init(kmem_start);
- + kmem_start = rs_init(kmem_start);
- ++#ifdef CONFIG_CYCLADES
- ++ kmem_start = cy_init(kmem_start);
- ++#endif
- + kmem_start = pty_init(kmem_start);
- ++ kmem_start = vcs_init(kmem_start);
- + return kmem_start;
- + }
- diff -r -u -N linux.orig/arch/arm/drivers/char/tty_io.c linux.arm/arch/arm/drivers/char/tty_io.c
- --- linux.orig/arch/arm/drivers/char/tty_io.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/tty_io.c Fri Oct 27 23:14:27 1995
- @@ -0,0 +1,1767 @@
- +/*
- + * linux/drivers/char/tty_io.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + */
- +
- +/*
- + * '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
- + */
- +
- +#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 <linux/config.h>
- +
- +#include <asm/segment.h>
- +#include <asm/system.h>
- +#include <asm/bitops.h>
- +
- +#include "kbd_kern.h"
- +#include "vt_kern.h"
- +/* This to come */
- +/*#include "selection.h"*/
- +
- +#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
- +#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
- +
- +#undef TTY_DEBUG_HANGUP
- +
- +/* These to go */
- +extern int set_selection(const int arg, struct tty_struct *tty);
- +extern int paste_selection(struct tty_struct *tty);
- +extern int sel_loadlut(const int arg);
- +extern int mouse_reporting(void);
- +extern int shift_state;
- +extern int do_screendump(int arg);
- +#define TTY_PARANOIA_CHECK
- +#define CHECK_TTY_COUNT
- +
- +extern void do_blank_screen(int nopowersave);
- +extern void do_unblank_screen(void);
- +/*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 *, 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);
- +
- +#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, dev_t device,
- + const char *routine)
- +{
- +#ifdef TTY_PARANOIA_CHECK
- + static const char *badmagic =
- + "Warning: bad magic number for tty struct (%d, %d) in %s\n";
- + static const char *badtty =
- + "Warning: null TTY for (%d, %d) in %s\n";
- +
- + if (!tty) {
- + printk(badtty, MAJOR(device), MINOR(device), routine);
- + return 1;
- + }
- + if (tty->magic != TTY_MAGIC) {
- + printk(badmagic, MAJOR(device), MINOR(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 (%d, %d) tty->count(%d) != #fd's(%d) in %s\n",
- + MAJOR(tty->device), MINOR(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(dev_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, 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;
- +}
- +
- +/*
- + * 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);
- + }
- +
- + /*
- + * 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, 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,(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(dev_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) {
- + dev_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 (%d, %d)\n",
- + MAJOR(tty->device), MINOR(tty->device));
- + return;
- + }
- + if (tty != tty->driver.table[idx]) {
- + printk("release_dev: driver.table[%d] not tty for (%d, %d)\n",
- + idx, MAJOR(tty->device), MINOR(tty->device));
- + return;
- + }
- + if (tp != tty->driver.termios[idx]) {
- + printk("release_dev: driver.termios[%d] not termios for (%d, %d)\n",
- + idx, MAJOR(tty->device), MINOR(tty->device));
- + return;
- + }
- + if (ltp != tty->driver.termios_locked[idx]) {
- + printk("release_dev: driver.termios_locked[%d] not termios_locked for (%d, %d)\n",
- + idx, MAJOR(tty->device), MINOR(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 (%d, %d)\n",
- + idx, MAJOR(tty->device), MINOR(tty->device));
- + return;
- + }
- + if (o_tp != tty->driver.other->termios[idx]) {
- + printk("release_dev: other->termios[%d] not o_termios for (%d, %d)\n",
- + idx, MAJOR(tty->device), MINOR(tty->device));
- + return;
- + }
- + if (o_ltp != tty->driver.other->termios_locked[idx]) {
- + printk("release_dev: other->termios_locked[%d] not o_termios_locked for (%d, %d)\n",
- + idx, MAJOR(tty->device), MINOR(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;
- + dev_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;
- +}
- +
- +static int tty_fasync(struct inode * inode, struct file * filp, int on)
- +{
- + struct tty_struct * tty;
- + struct fasync_struct *fa, *prev;
- +
- + tty = (struct tty_struct *)filp->private_data;
- + if (tty_paranoia_check(tty, inode->i_rdev, "tty_fasync"))
- + return 0;
- +
- + for (fa = tty->fasync, prev = 0; fa; prev= fa, fa = 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;
- + fa->fa_next = tty->fasync;
- + tty->fasync = fa;
- + 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 (!fa)
- + return 0;
- + if (prev)
- + prev->fa_next = fa->fa_next;
- + else
- + tty->fasync = fa->fa_next;
- + kfree_s(fa, sizeof(struct fasync_struct));
- + 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_fs_byte(*c++, d++);
- + put_fs_long(1, (unsigned long *)(ts->present+n));
- + }
- + else
- + put_fs_long(0, (unsigned long *)(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_fs_byte((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(long));
- + if (retval)
- + return retval;
- + arg = get_fs_long((unsigned long *) 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_fs_long(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_fs_long((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 (unsigned long));
- + if (retval)
- + return retval;
- + put_fs_long(tty->ldisc.num, (unsigned long *) arg);
- + return 0;
- + case TIOCSETD:
- + retval = tty_check_change(tty);
- + if (retval)
- + return retval;
- + arg = get_fs_long((unsigned long *) 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_fs_byte((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_fs_byte(shift_state,arg);
- + return 0;
- + case 7:
- + put_fs_byte(mouse_reporting(),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;
- + 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..
- + */
- +long tty_init(long kmem_start)
- +{
- + 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);
- +
- + kmem_start = kbd_init(kmem_start);
- + kmem_start = rs_init(kmem_start);
- +#ifdef CONFIG_CYCLADES
- + kmem_start = cy_init(kmem_start);
- +#endif
- + kmem_start = pty_init(kmem_start);
- + kmem_start = vcs_init(kmem_start);
- + return kmem_start;
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/char/tty_io.c.orig linux.arm/arch/arm/drivers/char/tty_io.c.orig
- --- linux.orig/arch/arm/drivers/char/tty_io.c.orig Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/tty_io.c.orig Fri Oct 27 23:14:41 1995
- @@ -0,0 +1,1667 @@
- +/*
- + * linux/drivers/char/tty_io.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + */
- +
- +/*
- + * '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
- + */
- +
- +#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 "kbd_kern.h"
- +#include "vt_kern.h"
- +
- +#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
- +#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
- +
- +#undef TTY_DEBUG_HANGUP
- +
- +extern int set_selection(const int arg, struct tty_struct *tty);
- +extern int paste_selection(struct tty_struct *tty);
- +extern int sel_loadlut(const int arg);
- +extern int mouse_reporting(void);
- +extern int shift_state;
- +extern int do_screendump(int 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,
- + * redirect is the pseudo-tty that console output
- + * is redirected to if asked by TIOCCONS.
- + */
- +int fg_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 *, 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);
- +
- +#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));
- +}
- +
- +#define TTY_PARANOIA_CHECK
- +
- +inline int tty_paranoia_check(struct tty_struct *tty, dev_t device,
- + const char *routine)
- +{
- +#ifdef TTY_PARANOIA_CHECK
- + static const char *badmagic =
- + "Warning: bad magic number for tty struct (%d, %d) in %s\n";
- + static const char *badtty =
- + "Warning: null TTY for (%d, %d) in %s\n";
- +
- + if (!tty) {
- + printk(badtty, MAJOR(device), MINOR(device), routine);
- + return 1;
- + }
- + if (tty->magic != TTY_MAGIC) {
- + printk(badmagic, MAJOR(device), MINOR(device), routine);
- + return 1;
- + }
- +#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;
- +
- + /* 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(dev_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, 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;
- + 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);
- + }
- + }
- +
- + if (tty->session > 0) {
- + kill_sl(tty->session,SIGHUP,1);
- + kill_sl(tty->session,SIGCONT,1);
- + }
- + tty->flags = 0;
- + tty->session = 0;
- + tty->pgrp = -1;
- + for_each_task(p) {
- + if (p->tty == tty)
- + p->tty = NULL;
- + }
- + 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)
- + return;
- +
- + if (tty->pgrp > 0) {
- + kill_pg(tty->pgrp, SIGHUP, priv);
- + kill_pg(tty->pgrp, SIGCONT, priv);
- + }
- + 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;
- +}
- +
- +/*
- + * 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;
- +
- + /*
- + * 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)
- + unblank_screen();
- + else {
- + timer_active &= ~(1<<BLANK_TIMER);
- + blank_screen();
- + }
- + }
- +
- + /*
- + * 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, 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,(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(dev_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) {
- + dev_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
- + (*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;
- +
- + 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 (%d, %d)\n",
- + MAJOR(tty->device), MINOR(tty->device));
- + return;
- + }
- + if (tty != tty->driver.table[idx]) {
- + printk("release_dev: driver.table[%d] not tty for (%d, %d)\n",
- + idx, MAJOR(tty->device), MINOR(tty->device));
- + return;
- + }
- + if (tp != tty->driver.termios[idx]) {
- + printk("release_dev: driver.termios[%d] not termios for (%d, %d)\n",
- + idx, MAJOR(tty->device), MINOR(tty->device));
- + return;
- + }
- + if (ltp != tty->driver.termios_locked[idx]) {
- + printk("release_dev: driver.termios_locked[%d] not termios_locked for (%d, %d)\n",
- + idx, MAJOR(tty->device), MINOR(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 (%d, %d)\n",
- + idx, MAJOR(tty->device), MINOR(tty->device));
- + return;
- + }
- + if (o_tp != tty->driver.other->termios[idx]) {
- + printk("release_dev: other->termios[%d] not o_termios for (%d, %d)\n",
- + idx, MAJOR(tty->device), MINOR(tty->device));
- + return;
- + }
- + if (o_ltp != tty->driver.other->termios_locked[idx]) {
- + printk("release_dev: other->termios_locked[%d] not o_termios_locked for (%d, %d)\n",
- + idx, MAJOR(tty->device), MINOR(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;
- +
- + 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
- +
- + /*
- + * 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;
- +
- + 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));
- + 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;
- + dev_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);
- + filp->private_data = tty;
- + if (retval)
- + return retval;
- + 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;
- + 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;
- +}
- +
- +static int tty_fasync(struct inode * inode, struct file * filp, int on)
- +{
- + struct tty_struct * tty;
- + struct fasync_struct *fa, *prev;
- +
- + tty = (struct tty_struct *)filp->private_data;
- + if (tty_paranoia_check(tty, inode->i_rdev, "tty_fasync"))
- + return 0;
- +
- + for (fa = tty->fasync, prev = 0; fa; prev= fa, fa = 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;
- + fa->fa_next = tty->fasync;
- + tty->fasync = fa;
- + 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 (!fa)
- + return 0;
- + if (prev)
- + prev->fa_next = fa->fa_next;
- + else
- + tty->fasync = fa->fa_next;
- + kfree_s(fa, sizeof(struct fasync_struct));
- + if (!tty->fasync && !tty->read_wait)
- + tty->minimum_to_wake = N_TTY_BUF_SIZE;
- + }
- + return 0;
- +}
- +
- +/*
- + * XXX does anyone use this anymore?!?
- + */
- +static int do_get_ps_info(int 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_fs_byte(*c++, d++);
- + put_fs_long(1, (unsigned long *)(ts->present+n));
- + }
- + else
- + put_fs_long(0, (unsigned long *)(ts->present+n));
- + return(0);
- +}
- +
- +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_fs_byte((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(long));
- + if (retval)
- + return retval;
- + arg = get_fs_long((unsigned long *) 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;
- + 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_fs_long(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_fs_long((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 (unsigned long));
- + if (retval)
- + return retval;
- + put_fs_long(tty->ldisc.num, (unsigned long *) arg);
- + return 0;
- + case TIOCSETD:
- + retval = tty_check_change(tty);
- + if (retval)
- + return retval;
- + arg = get_fs_long((unsigned long *) arg);
- + return tty_set_ldisc(tty, arg);
- + case TIOCLINUX:
- + retval = verify_area(VERIFY_READ, (void *) arg, 1);
- + if (retval)
- + return retval;
- + switch (get_fs_byte((char *)arg))
- + {
- + case 0:
- + return do_screendump(arg);
- + case 1:
- + printk("Deprecated TIOCLINUX (1) ioctl\n");
- + return do_get_ps_info(arg);
- + case 2:
- + return set_selection(arg, tty);
- + case 3:
- + return paste_selection(tty);
- + case 4:
- + 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_fs_byte(0, arg);
- +/* put_fs_byte(shift_state,arg); */
- + return 0;
- + case 7:
- + put_fs_byte(mouse_reporting(),arg);
- + return 0;
- + default:
- + return -EINVAL;
- + }
- + 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;
- + 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;
- + int major_inuse = 0;
- +
- + if (driver->refcount)
- + return -EBUSY;
- +
- + for (p = tty_drivers; p; p = p->next) {
- + if (p == driver)
- + found++;
- + else if (p->major == driver->major)
- + major_inuse++;
- + }
- +
- + if (!major_inuse) {
- + retval = unregister_chrdev(driver->major, driver->name);
- + if (retval)
- + return retval;
- + }
- +
- + if (driver->prev)
- + driver->prev->next = driver->next;
- + else
- + tty_drivers = driver->next;
- +
- + if (driver->next)
- + driver->next = driver->next->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..
- + */
- +long tty_init(long kmem_start)
- +{
- + 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);
- +
- + kmem_start = kbd_init(kmem_start);
- + kmem_start = rs_init(kmem_start);
- + kmem_start = pty_init(kmem_start);
- + return kmem_start;
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/char/tty_ioctl.c linux.arm/arch/arm/drivers/char/tty_ioctl.c
- --- linux.orig/arch/arm/drivers/char/tty_ioctl.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/tty_ioctl.c Fri Oct 27 23:14:27 1995
- @@ -0,0 +1,374 @@
- +/*
- + * linux/drivers/char/tty_ioctl.c
- + *
- + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
- + *
- + * 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 -r -u -N linux.orig/arch/arm/drivers/char/vc_screen.c linux.arm/arch/arm/drivers/char/vc_screen.c
- --- linux.orig/arch/arm/drivers/char/vc_screen.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/vc_screen.c Fri Oct 27 23:14:41 1995
- @@ -0,0 +1,226 @@
- +/*
- + * linux/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
- + */
- +
- +#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, char *buf, int count)
- +{
- + unsigned long p = file->f_pos;
- + unsigned int cons = MINOR(inode->i_rdev);
- + int viewed, attr, size, written;
- + char *buf0;
- + unsigned long *org, d;
- +
- + 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 */
- +};
- +
- +long vcs_init(long kmem_start)
- +{
- + if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))
- + printk("unable to get major %d for vcs device", VCS_MAJOR);
- + return kmem_start;
- +}
- diff -r -u -N linux.orig/arch/arm/drivers/char/vt.c linux.arm/arch/arm/drivers/char/vt.c
- --- linux.orig/arch/arm/drivers/char/vt.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/vt.c Fri Oct 27 23:14:21 1995
- @@ -0,0 +1,920 @@
- +/*
- + * linux/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 and EGA/VGA font from console.c
- + */
- +extern int con_set_trans(char * table);
- +extern int con_get_trans(char * 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 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.
- + */
- +void
- +kd_mksound2(unsigned int count, unsigned int vol, unsigned int ticks)
- +{
- + int freq;
- +
- + if(count)
- + freq = 1193180/count;
- + else
- + freq = 0;
- + mk_beep(freq, vol, ticks);
- +}
- +
- +void
- +kd_mksound(unsigned int count, unsigned int ticks)
- +{
- + kd_mksound2(count, 64, 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] >> 16) & 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_fs_byte(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_fs_long(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_fs_long(ucval, (unsigned long *) 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_fs_long(ucval, (unsigned long *) 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_fs_long((int *) &a->scancode);
- + kc = getkeycode(sc);
- + if (kc < 0)
- + return kc;
- + put_fs_long(kc, (int *) &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_fs_long((int *) &a->scancode);
- + kc = get_fs_long((int *) &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_fs_byte((char *) &a->kb_index)) >= NR_KEYS)
- + return -EINVAL;
- + if ((s = get_fs_byte((char *) &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_fs_word(val, (short *) &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_fs_byte((char *) &a->kb_index)) >= NR_KEYS)
- + return -EINVAL;
- + if ((s = get_fs_byte((char *) &a->kb_table)) >= MAX_NR_KEYMAPS)
- + return -EINVAL;
- + v = get_fs_word(&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_fs_byte(&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_fs_byte(*p, q++);
- + put_fs_byte(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_fs_byte(&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_fs_byte(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_fs_byte(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_fs_long(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_fs_long(&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_fs_byte(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_fs_byte(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_fs_byte(&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_fs_byte(&vtmode->waitv);
- + vt_cons[console]->vt_mode.relsig = get_fs_word(&vtmode->relsig);
- + vt_cons[console]->vt_mode.acqsig = get_fs_word(&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_fs_byte(vt_cons[console]->vt_mode.mode, &vtmode->mode);
- + put_fs_byte(vt_cons[console]->vt_mode.waitv, &vtmode->waitv);
- + put_fs_word(vt_cons[console]->vt_mode.relsig, &vtmode->relsig);
- + put_fs_word(vt_cons[console]->vt_mode.acqsig, &vtmode->acqsig);
- + put_fs_word(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_fs_word(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_fs_word(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_fs_long(i < MAX_NR_CONSOLES ? (i+1) : -1,
- + (unsigned long *)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_fs_word(&vtsizes->v_rows);
- + cc = get_fs_word(&vtsizes->v_cols);
- + return vc_resize(ll, cc);
- + }
- +
- + case VT_GETPALETTE:
- + {
- + unsigned long pix, num, *val;
- +
- + 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;
- +
- + 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:
- + 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((char *)arg);
- +
- + case GIO_SCRNMAP:
- + return con_get_trans((char *)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 -r -u -N linux.orig/arch/arm/drivers/char/vt_kern.h linux.arm/arch/arm/drivers/char/vt_kern.h
- --- linux.orig/arch/arm/drivers/char/vt_kern.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/char/vt_kern.h Fri Oct 27 23:14:27 1995
- @@ -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 -r -u -N linux.orig/arch/arm/drivers/net/Makefile linux.arm/arch/arm/drivers/net/Makefile
- --- linux.orig/arch/arm/drivers/net/Makefile Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/Makefile Fri Oct 27 23:15:04 1995
- @@ -0,0 +1,105 @@
- +# File: drivers/net/Makefile
- +#
- +# Makefile for the Linux network (ethercard) device drivers.
- +#
- +
- +# This will go away in some future future: hidden configuration files
- +# are difficult for users to deal with.
- +include CONFIG
- +
- +# Build MODULES by appending to this string for every driver below
- +#MODULES :=
- +
- +NETDRV_OBJS := Space.o auto_irq.o net_init.o loopback.o
- +CFLAGS :=$(CFLAGS) -I../../../../net/inet
- +CPP :=$(CPP) -I../../../../net/inet
- +
- +.c.o:
- + $(CC) $(CFLAGS) -c $<
- +
- +
- +# The point of the makefile...
- +all: net.a
- +
- +Space.o: Space.c ../../../../include/linux/autoconf.h
- + $(CC) $(CFLAGS) $(OPTS) -c $<
- +
- +net_init.o: ../../../../include/linux/autoconf.h
- +
- +ifdef CONFIG_ETHER3
- +NETDRV_OBJS := $(NETDRV_OBJS) nq8005.o
- +else
- +MODULES := $(MODULES) nq8005.o
- +endif
- +
- +ifdef CONFIG_PLIP
- +NETDRV_OBJS := $(NETDRV_OBJS) plip.o
- +else
- +MODULES := $(MODULES) plip.o
- +endif
- +plip.o: plip.c CONFIG
- + $(CC) $(CPPFLAGS) $(CFLAGS) $(PLIP_OPTS) -c $<
- +
- +ifdef CONFIG_PPP
- +NETDRV_OBJS := $(NETDRV_OBJS) ppp.o
- +CONFIG_SLHC = CONFIG_SLHC
- +else
- +MODULES := $(MODULES) ppp.o
- +endif
- +
- +ifdef CONFIG_SLIP
- +NETDRV_OBJS := $(NETDRV_OBJS) slip.o
- +CONFIG_SLHC = CONFIG_SLHC
- +else
- +MODULES := $(MODULES) slip.o
- +endif
- +slip.o: slip.c CONFIG
- + $(CC) $(CPPFLAGS) $(CFLAGS) -c $<
- +
- +ifdef CONFIG_DUMMY
- +NETDRV_OBJS := $(NETDRV_OBJS) dummy.o
- +else
- +MODULES := $(MODULES) dummy.o
- +endif
- +dummy.o: dummy.c CONFIG
- + $(CC) $(CPPFLAGS) $(CFLAGS) -c $<
- +
- +ifdef CONFIG_SLHC
- +NETDRV_OBJS := $(NETDRV_OBJS) slhc.o
- +else
- +MODULES := slhc.o $(MODULES)
- +endif
- +
- +net.a: $(NETDRV_OBJS)
- + rm -f net.a
- + $(AR) rcs net.a $(NETDRV_OBJS)
- +
- +clean:
- + rm -f core *.o *.a *.s
- +
- +dep:
- + $(CPP) -M $(NETDRV_OBJS:.o=.c) > .depend
- +ifdef MODULES
- + $(CPP) -M -DMODULE $(MODULES:.o=.c) >> .depend
- +endif
- +
- +tar:
- +
- +ifdef MODULES
- +
- +modules: $(MODULES)
- + echo $(MODULES) > ../../../../modules/NET_MODULES
- + cd ../../../../modules; \
- + for i in $(MODULES); do ln -sf ../arch/arm/drivers/net/$$i .; done
- +
- +else
- +
- +modules:
- +
- +endif
- +
- +# include a dependency file if one exists
- +
- +ifeq (.depend,$(wildcard .depend))
- +include .depend
- +endif
- diff -r -u -N linux.orig/arch/arm/drivers/net/Space.c linux.arm/arch/arm/drivers/net/Space.c
- --- linux.orig/arch/arm/drivers/net/Space.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/Space.c Fri Oct 27 23:15:03 1995
- @@ -0,0 +1,202 @@
- +/*
- + * 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>
- +
- +#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 ether3_probe(struct device *dev);
- +
- +static int
- +ethif_probe(struct device *dev)
- +{
- + short base_addr = dev->base_addr;
- +
- + if (base_addr < 0 || base_addr == 1)
- + return 1; /* ENXIO */
- +
- + if (1
- +#ifdef CONFIG_ETHER3
- + && ether3_probe(dev)
- +#endif
- + && 1 ) {
- + return 1; /* -ENODEV or -EAGAIN would be more accurate. */
- + }
- + return 0;
- +}
- +
- +/* 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. */
- +
- +static struct device eth3_dev = {
- + "eth3", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, NEXT_DEV, ethif_probe };
- +static struct device eth2_dev = {
- + "eth2", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, ð3_dev, ethif_probe };
- +static struct device eth1_dev = {
- + "eth1", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, ð2_dev, ethif_probe };
- +
- +static struct device eth0_dev = {
- + "eth0", 0, 0, 0, 0, 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", 0, 0, 0, 0, 0x278, 2, 0, 0, 0, NEXT_DEV, plip_init, };
- + static struct device plip1_dev = {
- + "plip1", 0, 0, 0, 0, 0x378, 7, 0, 0, 0, &plip2_dev, plip_init, };
- + static struct device plip0_dev = {
- + "plip0", 0, 0, 0, 0, 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)
- + extern int slip_init(struct device *);
- +
- +#ifdef SL_SLIP_LOTS
- +
- + static struct device slip15_dev={"sl15",0,0,0,0,15,0,0,0,0,NEXT_DEV,slip_init};
- + static struct device slip14_dev={"sl14",0,0,0,0,14,0,0,0,0,&slip15_dev,slip_init};
- + static struct device slip13_dev={"sl13",0,0,0,0,13,0,0,0,0,&slip14_dev,slip_init};
- + static struct device slip12_dev={"sl12",0,0,0,0,12,0,0,0,0,&slip13_dev,slip_init};
- + static struct device slip11_dev={"sl11",0,0,0,0,11,0,0,0,0,&slip12_dev,slip_init};
- + static struct device slip10_dev={"sl10",0,0,0,0,10,0,0,0,0,&slip11_dev,slip_init};
- + static struct device slip9_dev={"sl9",0,0,0,0,9,0,0,0,0,&slip10_dev,slip_init};
- + static struct device slip8_dev={"sl8",0,0,0,0,8,0,0,0,0,&slip9_dev,slip_init};
- + static struct device slip7_dev={"sl7",0,0,0,0,7,0,0,0,0,&slip8_dev,slip_init};
- + static struct device slip6_dev={"sl6",0,0,0,0,6,0,0,0,0,&slip7_dev,slip_init};
- + static struct device slip5_dev={"sl5",0,0,0,0,5,0,0,0,0,&slip6_dev,slip_init};
- + static struct device slip4_dev={"sl4",0,0,0,0,4,0,0,0,0,&slip5_dev,slip_init};
- +# undef NEXT_DEV
- +# define NEXT_DEV (&slip4_dev)
- +#endif /* SL_SLIP_LOTS */
- +
- + static struct device slip3_dev = {
- + "sl3", /* Internal SLIP driver, channel 3 */
- + 0x0, /* recv memory end */
- + 0x0, /* recv memory start */
- + 0x0, /* memory end */
- + 0x0, /* memory start */
- + 0x3, /* base I/O address */
- + 0, /* IRQ */
- + 0, 0, 0, /* flags */
- + NEXT_DEV, /* next device */
- + slip_init /* slip_init should set up the rest */
- + };
- + static struct device slip2_dev = {
- + "sl2", /* Internal SLIP driver, channel 2 */
- + 0x0, /* recv memory end */
- + 0x0, /* recv memory start */
- + 0x0, /* memory end */
- + 0x0, /* memory start */
- + 0x2, /* base I/O address */
- + 0, /* IRQ */
- + 0, 0, 0, /* flags */
- + &slip3_dev, /* next device */
- + slip_init /* slip_init should set up the rest */
- + };
- + static struct device slip1_dev = {
- + "sl1", /* Internal SLIP driver, channel 1 */
- + 0x0, /* recv memory end */
- + 0x0, /* recv memory start */
- + 0x0, /* memory end */
- + 0x0, /* memory start */
- + 0x1, /* base I/O address */
- + 0, /* IRQ */
- + 0, 0, 0, /* flags */
- + &slip2_dev, /* next device */
- + slip_init /* slip_init should set up the rest */
- + };
- + static struct device slip0_dev = {
- + "sl0", /* Internal SLIP driver, channel 0 */
- + 0x0, /* recv memory end */
- + 0x0, /* recv memory start */
- + 0x0, /* memory end */
- + 0x0, /* memory start */
- + 0x0, /* base I/O address */
- + 0, /* IRQ */
- + 0, 0, 0, /* flags */
- + &slip1_dev, /* next device */
- + slip_init /* slip_init should set up the rest */
- + };
- +# undef NEXT_DEV
- +# define NEXT_DEV (&slip0_dev)
- +#endif /* SLIP */
- +
- +#if defined(CONFIG_PPP)
- +extern int ppp_init(struct device *);
- +static struct device ppp3_dev = {
- + "ppp3", 0x0, 0x0, 0x0, 0x0, 3, 0, 0, 0, 0, NEXT_DEV, ppp_init, };
- +static struct device ppp2_dev = {
- + "ppp2", 0x0, 0x0, 0x0, 0x0, 2, 0, 0, 0, 0, &ppp3_dev, ppp_init, };
- +static struct device ppp1_dev = {
- + "ppp1", 0x0, 0x0, 0x0, 0x0, 1, 0, 0, 0, 0, &ppp2_dev, ppp_init, };
- +static struct device ppp0_dev = {
- + "ppp0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, &ppp1_dev, ppp_init, };
- +#undef NEXT_DEV
- +#define NEXT_DEV (&ppp0_dev)
- +#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
- +
- +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 -r -u -N linux.orig/arch/arm/drivers/net/auto_irq.c linux.arm/arch/arm/drivers/net/auto_irq.c
- --- linux.orig/arch/arm/drivers/net/auto_irq.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/auto_irq.c Fri Oct 27 23:14:59 1995
- @@ -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 */};
- +
- +int irqs_busy = 0x2147; /* The set of fixed IRQs (keyboard, timer, etc) */
- +int irqs_used = 0x0001; /* The set of fixed IRQs sometimes enabled. */
- +int irqs_reserved = 0x0000; /* An advisory "reserved" table. */
- +int irqs_shared = 0x0000; /* IRQ lines "shared" among conforming cards.*/
- +
- +static volatile int irq_number; /* The latest irq number we actually found. */
- +static volatile int irq_bitmap; /* The irqs we actually found. */
- +static int irq_handled; /* The irq lines we have a handler on. */
- +
- +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 -r -u -N linux.orig/arch/arm/drivers/net/dummy.c linux.arm/arch/arm/drivers/net/dummy.c
- --- linux.orig/arch/arm/drivers/net/dummy.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/dummy.c Fri Oct 27 23:14:59 1995
- @@ -0,0 +1,175 @@
- +/* dummy.c: a dummy net driver
- +
- + The purpose of this driver is to provide a device to point a
- + route through, but not to actually transmit packets.
- +
- + Why? If you have a machine whose only connection is an occasional
- + PPP/SLIP/PLIP link, you can only connect to your own hostname
- + when the link is up. Otherwise you have to use localhost.
- + This isn't very consistent.
- +
- + One solution is to set up a dummy link using PPP/SLIP/PLIP,
- + but this seems (to me) too much overhead for too little gain.
- + This driver provides a small alternative. Thus you can do
- +
- + [when not running slip]
- + ifconfig dummy slip.addr.ess.here up
- + [to go to slip]
- + ifconfig dummy down
- + dip whatever
- +
- + This was written by looking at Donald Becker's skeleton driver
- + and the loopback driver. I then threw away anything that didn't
- + apply! Thanks to Alan Cox for the key clue on what to do with
- + misguided packets.
- +
- + Nick Holloway, 27th May 1994
- + [I tweaked this explanation a little but thats all]
- + Alan Cox, 30th May 1994
- +*/
- +
- +/* To have statistics (just packets sent) define this */
- +#undef DUMMY_STATS
- +
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/version.h>
- +#endif
- +
- +#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>
- +
- +static int dummy_xmit(struct sk_buff *skb, struct device *dev);
- +#ifdef DUMMY_STATS
- +static struct enet_statistics *dummy_get_stats(struct device *dev);
- +#endif
- +
- +#ifdef MODULE
- +static int dummy_open(struct device *dev)
- +{
- + MOD_INC_USE_COUNT;
- + return 0;
- +}
- +
- +static int dummy_close(struct device *dev)
- +{
- + MOD_DEC_USE_COUNT;
- + return 0;
- +}
- +
- +#endif
- +
- +
- +int dummy_init(struct device *dev)
- +{
- +/* I commented this out as bootup is noisy enough anyway and this driver
- + seems pretty reliable 8) 8) 8) */
- +/* printk ( KERN_INFO "Dummy net driver (94/05/27 v1.0)\n" ); */
- +
- + /* Initialize the device structure. */
- + dev->hard_start_xmit = dummy_xmit;
- +
- +#if DUMMY_STATS
- + dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
- + memset(dev->priv, 0, sizeof(struct enet_statistics));
- + dev->get_stats = dummy_get_stats;
- +#endif
- +#ifdef MODULE
- + dev->open = &dummy_open;
- + dev->stop = &dummy_close;
- +#endif
- +
- + /* Fill in the fields of the device structure with ethernet-generic values. */
- + ether_setup(dev);
- + dev->flags |= IFF_NOARP;
- +
- + return 0;
- +}
- +
- +static int
- +dummy_xmit(struct sk_buff *skb, struct device *dev)
- +{
- +#if DUMMY_STATS
- + struct enet_statistics *stats;
- +#endif
- +
- + if (skb == NULL || dev == NULL)
- + return 0;
- +
- + dev_kfree_skb(skb, FREE_WRITE);
- +
- +#if DUMMY_STATS
- + stats = (struct enet_statistics *)dev->priv;
- + stats->tx_packets++;
- +#endif
- +
- + return 0;
- +}
- +
- +#if DUMMY_STATS
- +static struct enet_statistics *
- +dummy_get_stats(struct device *dev)
- +{
- + struct enet_statistics *stats = (struct enet_statistics*) dev->priv;
- + return stats;
- +}
- +#endif
- +
- +#ifdef MODULE
- +char kernel_version[] = UTS_RELEASE;
- +
- +static int dummy_probe(struct device *dev)
- +{
- + dummy_init(dev);
- + return 0;
- +}
- +
- +static struct device dev_dummy = {
- + "dummy0\0 ",
- + 0, 0, 0, 0,
- + 0x0, 0,
- + 0, 0, 0, NULL, dummy_probe };
- +
- +int init_module(void)
- +{
- + /* Find a name for this unit */
- + int ct= 1;
- +
- + while(dev_get(dev_dummy.name)!=NULL && ct<100)
- + {
- + sprintf(dev_dummy.name,"dummy%d",ct);
- + ct++;
- + }
- +
- + if (register_netdev(&dev_dummy) != 0)
- + return -EIO;
- + return 0;
- +}
- +
- +void cleanup_module(void)
- +{
- + if (MOD_IN_USE)
- + printk("dummy: device busy, remove delayed\n");
- + else
- + {
- + unregister_netdev(&dev_dummy);
- + }
- +}
- +#endif /* MODULE */
- diff -r -u -N linux.orig/arch/arm/drivers/net/ether3.c linux.arm/arch/arm/drivers/net/ether3.c
- --- linux.orig/arch/arm/drivers/net/ether3.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/ether3.c Fri Oct 27 23:15:07 1995
- @@ -0,0 +1,860 @@
- +/*
- + * linux/drivers/net/ether3.c
- + *
- + * SEEQ nq8005 ethernet driver
- + *
- + */
- +
- +#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>
- +
- +#include "ether3.h"
- +
- +#define FUNC_PROLOGUE \
- + struct arc_net *an = (struct arc_net *)dev->priv
- +
- +static unsigned int net_debug = NET_DEBUG;
- +
- +#define tx_done(dev) 0
- +/* ------------------------------------------------------------------------------- */
- +static char *version = "ether3 ethernet driver (c) 1995 R.M.King V1.00\n";
- +
- +#define BUS_16 16
- +#define BUS_8 8
- +
- +extern int inswb(int reg, void *buffer, int len);
- +extern int outswb(int reg, void *buffer, int len);
- +
- +unsigned char def_eth_addr[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
- +
- +static int ether3_prods[] = { 0x00A4 };
- +static int ether3_manus[] = { 0x0011 };
- +
- +/* --------------------------------------------------------------------------- */
- +
- +static void check_dev(struct device *dev, char *s)
- +{
- + struct device *dev2;
- +
- + dev2 = dev->next;
- +
- + while (dev2 && dev2 != dev)
- + dev2 = dev2->next;
- +
- + if (dev2 == dev)
- + printk("eek! - %s\n", s);
- +}
- +
- +/*
- + * set one of the receive address registers in the chip
- + */
- +
- +static void
- +ether3_setrxaddr(struct device *dev, int address, unsigned char *addr)
- +{
- + FUNC_PROLOGUE;
- + unsigned long flags;
- + int stat, i;
- +
- + save_flags(flags);
- + cli();
- +
- + stat = inw(REG_STATUS);
- + if(stat & STAT_RXON)
- + outw(an->regs.command | CMD_RXOFF, REG_COMMAND);
- +
- + outw(an->regs.config1 | address, REG_CONFIG1);
- + for(i=0; i<6; i++)
- + outb(addr[i], REG_BUFWIN);
- +
- + if(stat & STAT_RXON)
- + outw(an->regs.command | CMD_RXON, REG_COMMAND);
- +
- + restore_flags(flags);
- +}
- +
- +/*
- + * 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(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- + outw(an->regs.command | CMD_FIFOWRITE, REG_COMMAND);
- + while((inw(REG_STATUS) & STAT_FIFOEMPTY) == 0)
- + if(!timeout--)
- + {
- + printk("settxlim_16 broken\n");
- + an->broken = 1;
- + return;
- + }
- + outw(an->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(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- + outw(an->regs.command | CMD_FIFOWRITE, REG_COMMAND);
- + while((inw(REG_STATUS) & STAT_FIFOEMPTY) == 0)
- + if(!timeout--)
- + {
- + printk("setrxlim_16 broken\n");
- + an->broken = 1;
- + return;
- + }
- + outw(start, REG_DMAADDR);
- + outw(an->regs.command | CMD_FIFOREAD, REG_COMMAND);
- + }
- + if(length != 0)
- + inswb(REG_BUFWIN, data, length);
- +}
- +
- +/* --------------------------------------------------------------------------- */
- +
- +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_1(struct device *dev)
- +{
- + outb(0x80, REG_CONFIG2);
- + outb(0, REG_COMMAND);
- +
- + outb(1, REG_CONFIG1);
- + if(inb(REG_CONFIG1+1)==1 && inb(REG_CONFIG1)==0)
- + return BUS_8;
- + if(inw(REG_CONFIG1)==0x101)
- + return BUS_16;
- + return 0;
- +}
- +
- +static int
- +ether3_init_2(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + unsigned long a=0;
- + int i;
- +
- + ether3_setrxaddr(dev, CFG1_BUFSELSTAT0, dev->dev_addr);
- +
- + an->regs.config1 = CFG1_RECVSPECBROAD|CFG1_RECVCOMPSTAT0|CFG1_DMABURST8;
- + an->regs.config2 = CFG2_CTRLO|CFG2_RECVCRC|CFG2_ERRENCRC;
- +
- + outw(an->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
- + outw((TX_END>>8) - 1, REG_BUFWIN);
- + outw(an->regs.recvptr , REG_RECVPTR);
- + outw(an->regs.transmitptr , REG_TRANSMITPTR);
- + outw(an->regs.recvptr >> 8, REG_RECVEND);
- + outw(an->regs.config2 , REG_CONFIG2);
- + outw(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- + ether3_writebuffer(dev, &a, an->regs.transmitptr, 4);
- + outw(an->regs.command , REG_COMMAND);
- +
- + i = ether3_ramtest(dev, 0x5A);
- + if(i)
- + return i;
- + return ether3_ramtest(dev, 0x1E);
- +}
- +
- +/* This is the real probe routine. */
- +
- +static int
- +ether3_probe1(struct device *dev)
- +{
- + static unsigned version_printed = 0;
- + struct arc_net *an;
- + int i;
- +
- + if(!dev->priv)
- + dev->priv = kmalloc(sizeof(struct arc_net), GFP_KERNEL);
- +
- + if(!dev->priv)
- + return 1;
- +
- + an = (struct arc_net *) dev->priv;
- + memset(an, 0, sizeof(struct arc_net));
- + an->bus_type = BUS_16;
- + an->broken = 0;
- +
- + if((an->bus_type=ether3_init_1(dev))==0)
- + {
- + kfree(dev->priv);
- + return 1;
- + }
- +
- + if (net_debug && version_printed++ == 0)
- + printk(version);
- +
- + printk("%s: ether3 found (bus width %d), ", dev->name, an->bus_type);
- +
- + /* Retrive and print the ethernet address. */
- + for (i = 0; i < 6; i++)
- + printk(i==0?" %2.2x":i==5?":%2.2x\n":":%2.2x", dev->dev_addr[i]);
- +
- + if(ether3_init_2(dev))
- + {
- + kfree(dev->priv);
- + return 1;
- + }
- +
- + 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);
- + return -EAGAIN;
- + }
- +
- + irq2dev_map[dev->irq] = dev;
- +#endif
- + return 0;
- +}
- +
- +/* --------------------------------------------------------------------------- */
- +
- +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);
- +}
- +int
- +ether3_probe(struct device *dev)
- +{
- +#ifndef MODULE
- + struct expansion_card *ec;
- + unsigned char *eth_addr;
- + int base_addr;
- +
- + 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);
- +#endif
- + if(ether3_probe1(dev) == 0)
- + return 0;
- + return ENODEV;
- +}
- +
- +/* ------------------------------------------------------------------------ */
- +
- +static void
- +ether3_init_for_open(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + unsigned long a=0;
- +
- + an->regs.command = 0;
- + outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
- + while(inw(REG_STATUS) & (STAT_RXON|STAT_TXON));
- +
- + ether3_setrxaddr(dev, CFG1_BUFSELSTAT0, dev->dev_addr);
- + an->regs.transmitptr = 0; /* address that transmitter has processed to */
- + an->txinsert = 0; /* address of next available packet for tx */
- + an->txed = 0; /* transmitted length index */
- + an->txto = 0; /* to transmit length index */
- + an->tx_ends[0] = 0;
- + an->use = 0;
- +
- + an->regs.config2 |= CFG2_CTRLO;
- +
- + outw(an->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
- + outw((TX_END>>8) - 1, REG_BUFWIN);
- +
- + an->regs.recvptr = RX_START;
- + outw(an->regs.recvptr , REG_RECVPTR);
- + outw(an->regs.recvptr>>8, REG_RECVEND);
- +
- + outw(0, REG_TRANSMITPTR);
- + outw(an->regs.config2 , REG_CONFIG2);
- + outw(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- +
- + ether3_writebuffer(dev, &a, an->regs.transmitptr, 4);
- +
- +
- + an->regs.command = CMD_ENINTRX|CMD_ENINTTX;
- + outw(an->regs.command | CMD_RXON, REG_COMMAND);
- +}
- +
- +/* 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")) {
- + kfree(dev->priv);
- + return -EAGAIN;
- + }
- +
- + irq2dev_map[dev->irq] = dev;
- +#endif
- +
- + MOD_INC_USE_COUNT;
- +
- + dev->tbusy = 0;
- + dev->interrupt = 0;
- + dev->start = 1;
- + return 0;
- +}
- +
- +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, %s?\n", dev->name,
- + tx_done(dev) ? "IRQ conflict" : "network cable problem");
- + /* Try to restart the adaptor. */
- + dev->tbusy = 0;
- + an->use = 0;
- + an->regs.config2 |= CFG2_CTRLO;
- + outw(an->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 {
- + short length = ETH_ZLEN < (skb->len-2) ? (skb->len-2) : ETH_ZLEN;
- + unsigned char *buf = skb->data;
- + unsigned long flags;
- + unsigned long thisinfo, nextinfo=0;
- + int thisdata, thisstart, nextpacket;
- +
- + if(an->broken)
- + {
- + dev_kfree_skb(skb, FREE_WRITE);
- + an->stats.tx_dropped ++;
- + return 1;
- + }
- +
- + thisstart = an->txinsert;
- + thisdata = thisstart + 4;
- + if(thisdata >= TX_END)
- + thisdata -= TX_END;
- + nextpacket = thisdata + length;
- + if(nextpacket >= TX_END)
- + nextpacket -= TX_END;
- + an->txinsert = nextpacket;
- + an->tx_ends[an->txto++] = nextpacket;
- + if(an->txto >= MAX_TXED)
- + an->txto = 0;
- +
- + thisinfo = 0x00E80000 | ((nextpacket >> 8) & 0xff) | ((nextpacket << 8) & 0xff00);
- +
- + save_flags(flags);
- + cli();
- + an->use++;
- + an->regs.config2 &= ~CFG2_CTRLO;
- + outw(an->regs.config2 , REG_CONFIG2);
- +
- + ether3_writebuffer(dev, buf + 2, thisdata, length&1?(length+1):length);
- + ether3_writebuffer(dev, &nextinfo, -1, 4);
- + ether3_writebuffer(dev, &thisinfo, thisstart, 4);
- + restore_flags(flags);
- +
- + if((inw(REG_STATUS) & STAT_TXON)==0) {
- + outw(thisstart, REG_TRANSMITPTR);
- + outw(an->regs.command | CMD_TXON, REG_COMMAND);
- + }
- + dev->trans_start = jiffies;
- + }
- + dev_kfree_skb (skb, FREE_WRITE);
- +
- + /* You might need to clean up and record Tx statistics here. */
- +
- + return 0;
- +}
- +
- +/* The typical workload of the driver:
- + Handle the network interface interrupts. */
- +static void
- +ether3_interrupt(int irq, struct pt_regs *regs)
- +{
- + struct device *dev = (struct device *)(irq2dev_map[irq]);
- + struct arc_net *an = (struct arc_net *)dev->priv;
- + int status, boguscount = 0, 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;
- + }
- + dev->interrupt = 1;
- +
- + do {
- + status = inw(REG_STATUS);
- + if (status & STAT_INTRX) {
- + /* Got a packet(s). */
- + ether3_rx(dev);
- + done = 1;
- + }
- + if (status & STAT_INTTX) {
- + ether3_tx(dev);
- + done = 1;
- + }
- + outw((status & (STAT_INTRX|STAT_INTTX|STAT_INTBUFWIN))
- + | an->regs.command, REG_COMMAND);
- + } while (++boguscount < 20 && !done) ;
- +
- + dev->interrupt = 0;
- +
- +#if NET_DEBUG > 1
- + if(net_debug & DEBUG_INT)
- + printk("ether3_interrupt: done\n", irq);
- +#endif
- +}
- +
- +/* We have a good packet(s), get it/them out of the buffers. */
- +static void
- +ether3_rx(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + struct sk_buff *skb=NULL;
- + unsigned int end;
- + unsigned int length;
- +
- + an->use++;
- + if(an->use == 1)
- + {
- + an->regs.config2 &= ~CFG2_CTRLO;
- + outw(an->regs.config2 , REG_CONFIG2);
- + }
- + do {
- + unsigned char buffer[20];
- + ether3_readbuffer(dev, buffer, an->regs.recvptr, 20);
- +
- + if(buffer[0] == 0 || (buffer[3] & 0x80)==0)
- + goto rx_fin;
- +#if NET_DEBUG > 1
- + if(net_debug & DEBUG_RX) {
- + {
- + int i;
- + printk("ether3_rx:", an->regs.recvptr);
- + for(i=3; i<18; i++)
- + printk("%02X ",(unsigned int)buffer[i]);
- + printk("\n");
- + }
- +#endif
- +
- + end = (buffer[0]<<8) | buffer[1];
- +
- + if((buffer[2] & 0xC0) != 0x40)
- + /* no data follows */
- + goto done;
- +
- + if(buffer[3] & 15) {
- + an->stats.rx_errors++;
- + if(buffer[3] & 1) an->stats.rx_fifo_errors ++;
- + if(buffer[3] & 2) an->stats.rx_crc_errors ++;
- + if(buffer[3] & 4) an->stats.rx_fifo_errors ++;
- + if(buffer[3] & 8) an->stats.rx_length_errors ++;
- + goto done;
- + }
- +
- + if(end > an->regs.recvptr)
- + length = end - an->regs.recvptr;
- + else
- + length = (end - RX_START) + (RX_END - an->regs.recvptr);
- +
- + skb = alloc_skb(length + 4, GFP_ATOMIC);
- + if(skb == NULL) {
- + printk("%s: memory squeeze, dropping packet.\n", dev->name);
- + an->stats.rx_dropped++;
- + an->regs.recvptr = end;
- + outw(an->regs.recvptr >> 8, REG_RECVEND);
- + goto rx_fin;
- + }
- +
- + skb->len = length - 4;
- + skb->dev = dev;
- + memcpy(skb->data + 2, buffer + 4, 20-4);
- + ether3_readbuffer(dev, skb->data+18, -1, length-20);
- + done:
- + an->regs.recvptr = end;
- + outw(an->regs.recvptr >> 8, REG_RECVEND);
- + if(skb) {
- + netif_rx(skb);
- + skb=NULL;
- + an->stats.rx_packets++;
- + }
- + }
- + while(inw(REG_STATUS) & STAT_RXON);
- +
- + outw(an->regs.recvptr, REG_RECVPTR);
- + outw(an->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. */
- +rx_fin:
- + if(an->use)
- + {
- + an->use--;
- + if(an->use == 0)
- + {
- + an->regs.config2 |= CFG2_CTRLO;
- + outw(an->regs.config2 , REG_CONFIG2);
- + }
- + }
- +}
- +
- +/* ----------------------------------------------------------------------------- */
- +
- +static void
- +ether3_tx(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + unsigned char buffer[4];
- +
- + if(an->regs.transmitptr + 4 > TX_END) {
- + int len = TX_END - an->regs.transmitptr;
- + ether3_readbuffer(dev, buffer, an->regs.transmitptr, len);
- + ether3_readbuffer(dev, buffer+len, 0, 4 - len);
- + }
- + else
- + ether3_readbuffer(dev, buffer, an->regs.transmitptr, 4);
- +
- + if(!(buffer[3] & 0x80))
- + return;
- +
- + if(buffer[3] & 7) {
- + if(buffer[3] & 5) an->stats.tx_errors ++;
- + if(buffer[3] & 4) an->stats.collisions += 16;
- + if(buffer[3] & 2) an->stats.collisions += (buffer[3] & 0x78)>>3;
- + if(buffer[3] & 1) an->stats.tx_fifo_errors ++;
- + }
- + if(!(buffer[3] & 5))
- + an->stats.tx_packets++;
- +
- + an->regs.transmitptr = an->tx_ends[an->txed++];
- + if(an->txed >= MAX_TXED)
- + an->txed = 0;
- + if(an->regs.transmitptr >= TX_END)
- + an->regs.transmitptr -= TX_END;
- +/* if(an->regs.transmitptr != ((buffer[0]<<8)|buffer[1])) */
- +/* printk("%p:%p\n",an->regs.transmitptr, (buffer[2]<<24)|(buffer[3]<<16)|(buffer[0]<<8)|buffer[1]); */
- + dev->tbusy = 0;
- + mark_bh(NET_BH); /* Inform upper layers. */
- + if(an->use)
- + {
- + an->use--;
- + if(an->use == 0)
- + {
- + an->regs.config2 |= CFG2_CTRLO;
- + outw(an->regs.config2 , REG_CONFIG2);
- + }
- + }
- +}
- +
- +/* The inverse routine to net_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);
- + an->regs.command = 0;
- + while(inw(REG_STATUS) & (STAT_RXON|STAT_TXON));
- + ether3_init_1(dev);
- +
- + restore_flags(flags);
- +
- + /* Flush the Tx */
- +#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 &an->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;
- +
- + if(num_addrs == -1)
- + an->regs.config1 |= CFG1_RECVPROMISC;
- + else
- + an->regs.config1 = (an->regs.config1 & ~CFG1_RECVPROMISC)
- + | CFG1_RECVSPECBROAD;
- +
- + outw(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- +}
- +
- +#ifdef MODULE
- +char kernel_version[] = UTS_RELEASE;
- +static struct device ether3 = {
- + " ",
- + 0, 0, 0, 0,
- + 0, 0,
- + 0, 0, 0, NULL, ether3_probe
- +};
- +
- +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_probe;
- + 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("%s: device busy, remove delayed\n", ether3.name);
- + } 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 -r -u -N linux.orig/arch/arm/drivers/net/ether3.h linux.arm/arch/arm/drivers/net/ether3.h
- --- linux.orig/arch/arm/drivers/net/ether3.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/ether3.h Fri Oct 27 23:15:07 1995
- @@ -0,0 +1,153 @@
- +/*
- + * 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 1
- +#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)
- +
- +
- +#define TX_END 0x6000
- +#define RX_START 0x6000
- +#define RX_LEN 0xA000
- +#define RX_END 0x10000
- +#define MAX_TXED 360
- +
- +struct arc_net
- +{
- + int bus_type;
- + struct
- + {
- + int command;
- + int config1;
- + int config2;
- + int transmitptr;
- + int recvptr;
- + } regs;
- + int txinsert;
- + struct enet_statistics stats;
- + int txed;
- + int txto;
- + int tx_ends[MAX_TXED];
- + int broken;
- + int use;
- +};
- +
- +extern struct device *init_etherdev(struct device *dev, int sizeof_private,
- + unsigned long *mem_startp);
- +
- +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);
- +static void ether3_tx(struct device *dev);
- +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 -r -u -N linux.orig/arch/arm/drivers/net/loopback.c linux.arm/arch/arm/drivers/net/loopback.c
- --- linux.orig/arch/arm/drivers/net/loopback.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/loopback.c Fri Oct 27 23:14:59 1995
- @@ -0,0 +1,140 @@
- +/*
- + * 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.
- + *
- + * Pseudo-driver for the loopback interface.
- + *
- + * Version: @(#)loopback.c 1.0.4b 08/16/93
- + *
- + * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
- + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- + * Donald Becker, <becker@cesdis.gsfc.nasa.gov>
- + *
- + * Alan Cox : Fixed oddments for NET3.014
- + *
- + * 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/kernel.h>
- +#include <linux/sched.h>
- +#include <linux/interrupt.h>
- +#include <linux/fs.h>
- +#include <linux/types.h>
- +#include <linux/string.h>
- +#include <linux/socket.h>
- +#include <linux/errno.h>
- +#include <linux/fcntl.h>
- +#include <linux/in.h>
- +#include <linux/if_ether.h> /* For the statistics structure. */
- +
- +#include <asm/system.h>
- +#include <asm/segment.h>
- +#include <asm/io.h>
- +
- +#include <linux/inet.h>
- +#include <linux/netdevice.h>
- +#include <linux/etherdevice.h>
- +#include <linux/skbuff.h>
- +
- +
- +static int
- +loopback_xmit(struct sk_buff *skb, struct device *dev)
- +{
- + struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
- + int done;
- +
- + if (skb == NULL || dev == NULL) return(0);
- +
- + cli();
- + if (dev->tbusy != 0) {
- + sti();
- + stats->tx_errors++;
- + return(1);
- + }
- + dev->tbusy = 1;
- + sti();
- +
- + /* FIXME: Optimise so buffers with skb->free=1 are not copied but
- + instead are lobbed from tx queue to rx queue */
- +
- + done = dev_rint(skb->data, skb->len, 0, dev);
- + dev_kfree_skb(skb, FREE_WRITE);
- +
- + while (done != 1) {
- + done = dev_rint(NULL, 0, 0, dev);
- + }
- + stats->tx_packets++;
- +
- + dev->tbusy = 0;
- +
- + if (!intr_count && (bh_active & bh_mask)) {
- + start_bh_atomic();
- + do_bottom_half();
- + end_bh_atomic();
- + }
- +
- + return(0);
- +}
- +
- +static struct enet_statistics *
- +get_stats(struct device *dev)
- +{
- + return (struct enet_statistics *)dev->priv;
- +}
- +
- +static int loopback_open(struct device *dev)
- +{
- + dev->flags|=IFF_LOOPBACK;
- + return 0;
- +}
- +
- +/* Initialize the rest of the LOOPBACK device. */
- +int
- +loopback_init(struct device *dev)
- +{
- + int i;
- +
- + dev->mtu = 2000; /* MTU */
- + dev->tbusy = 0;
- + dev->hard_start_xmit = loopback_xmit;
- + dev->open = NULL;
- +#if 1
- + dev->hard_header = eth_header;
- + dev->hard_header_len = ETH_HLEN; /* 14 */
- + dev->addr_len = ETH_ALEN; /* 6 */
- + dev->type = ARPHRD_ETHER; /* 0x0001 */
- + dev->type_trans = eth_type_trans;
- + dev->rebuild_header = eth_rebuild_header;
- + dev->open = loopback_open;
- +#else
- + dev->hard_header_length = 0;
- + dev->addr_len = 0;
- + dev->type = 0; /* loopback_type (0) */
- + dev->hard_header = NULL;
- + dev->type_trans = NULL;
- + dev->rebuild_header = NULL;
- +#endif
- +
- + /* New-style flags. */
- + dev->flags = IFF_LOOPBACK|IFF_BROADCAST;
- + dev->family = AF_INET;
- +#ifdef CONFIG_INET
- + dev->pa_addr = in_aton("127.0.0.1");
- + dev->pa_brdaddr = in_aton("127.255.255.255");
- + dev->pa_mask = in_aton("255.0.0.0");
- + dev->pa_alen = sizeof(unsigned long);
- +#endif
- + dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
- + memset(dev->priv, 0, sizeof(struct enet_statistics));
- + dev->get_stats = get_stats;
- +
- + /* Fill in the generic fields of the device structure. */
- + for (i = 0; i < DEV_NUMBUFFS; i++)
- + skb_queue_head_init(&dev->buffs[i]);
- +
- + return(0);
- +};
- diff -r -u -N linux.orig/arch/arm/drivers/net/net_init.c linux.arm/arch/arm/drivers/net/net_init.c
- --- linux.orig/arch/arm/drivers/net/net_init.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/net_init.c Fri Oct 27 23:14:59 1995
- @@ -0,0 +1,320 @@
- +/* netdrv_init.c: Initialization for network devices. */
- +/*
- + Written 1993,1994,1995 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 file contains the initialization for the "pl14+" style ethernet
- + drivers. It should eventually replace most of drivers/net/Space.c.
- + It's primary advantage is that it's able to allocate low-memory buffers.
- + A secondary advantage is that the dangerous NE*000 netcards can reserve
- + their I/O port region before the SCSI probes start.
- +
- + Modifications/additions by Bjorn Ekwall <bj0rn@blox.se>:
- + ethdev_index[MAX_ETH_CARDS]
- + register_netdev() / unregister_netdev()
- +*/
- +
- +#include <linux/config.h>
- +#include <linux/kernel.h>
- +#include <linux/sched.h>
- +#include <linux/types.h>
- +#include <linux/fs.h>
- +#include <linux/malloc.h>
- +#include <linux/if_ether.h>
- +#include <linux/string.h>
- +#include <linux/netdevice.h>
- +#include <linux/etherdevice.h>
- +
- +/* The network devices currently exist only in the socket namespace, so these
- + entries are unused. The only ones that make sense are
- + open start the ethercard
- + close stop the ethercard
- + ioctl To get statistics, perhaps set the interface port (AUI, BNC, etc.)
- + One can also imagine getting raw packets using
- + read & write
- + but this is probably better handled by a raw packet socket.
- +
- + Given that almost all of these functions are handled in the current
- + socket-based scheme, putting ethercard devices in /dev/ seems pointless.
- +
- + [Removed all support for /dev network devices. When someone adds
- + streams then by magic we get them, but otherwise they are un-needed
- + and a space waste]
- +*/
- +
- +/* The list of used and available "eth" slots (for "eth0", "eth1", etc.) */
- +#define MAX_ETH_CARDS 16 /* same as the number if irq's in irq2dev[] */
- +static struct device *ethdev_index[MAX_ETH_CARDS];
- +
- +unsigned long lance_init(unsigned long mem_start, unsigned long mem_end);
- +unsigned long pi_init(unsigned long mem_start, unsigned long mem_end);
- +unsigned long apricot_init(unsigned long mem_start, unsigned long mem_end);
- +unsigned long dec21040_init(unsigned long mem_start, unsigned long mem_end);
- +
- +/*
- + net_dev_init() is our network device initialization routine.
- + It's called from init/main.c with the start and end of free memory,
- + and returns the new start of free memory.
- + */
- +
- +unsigned long net_dev_init (unsigned long mem_start, unsigned long mem_end)
- +{
- +
- + /* Network device initialization for devices that must allocate
- + low-memory or contiguous DMA buffers.
- + */
- +#if defined(CONFIG_LANCE)
- + mem_start = lance_init(mem_start, mem_end);
- +#endif
- +#if defined(CONFIG_PI)
- + mem_start = pi_init(mem_start, mem_end);
- +#endif
- +#if defined(CONFIG_DEC_ELCP)
- + mem_start = dec21040_init(mem_start, mem_end);
- +#endif
- + return mem_start;
- +}
- +
- +/* Fill in the fields of the device structure with ethernet-generic values.
- +
- + If no device structure is passed, a new one is constructed, complete with
- + a SIZEOF_PRIVATE private data area.
- +
- + If an empty string area is passed as dev->name, or a new structure is made,
- + a new name string is constructed. The passed string area should be 8 bytes
- + long.
- + */
- +
- +struct device *
- +init_etherdev(struct device *dev, int sizeof_priv, unsigned long *mem_startp)
- +{
- + int new_device = 0;
- + int i;
- +
- + /* Use an existing correctly named device in Space.c:dev_base. */
- + if (dev == NULL) {
- + int alloc_size = sizeof(struct device) + sizeof("eth%d ")
- + + sizeof_priv + 3;
- + struct device *cur_dev;
- + char pname[8]; /* Putative name for the device. */
- +
- + for (i = 0; i < MAX_ETH_CARDS; ++i)
- + if (ethdev_index[i] == NULL) {
- + sprintf(pname, "eth%d", i);
- + for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next)
- + if (strcmp(pname, cur_dev->name) == 0) {
- + dev = cur_dev;
- + dev->init = NULL;
- + sizeof_priv = (sizeof_priv + 3) & ~3;
- + if (mem_startp && *mem_startp ) {
- + dev->priv = (void*) *mem_startp;
- + *mem_startp += sizeof_priv;
- + } else
- + dev->priv = kmalloc(sizeof_priv, GFP_KERNEL);
- + memset(dev->priv, 0, sizeof_priv);
- + goto found;
- + }
- + }
- +
- + alloc_size &= ~3; /* Round to dword boundary. */
- +
- + if (mem_startp && *mem_startp ) {
- + dev = (struct device *)*mem_startp;
- + *mem_startp += alloc_size;
- + } else
- + dev = (struct device *)kmalloc(alloc_size, GFP_KERNEL);
- + memset(dev, 0, alloc_size);
- + if (sizeof_priv)
- + dev->priv = (void *) (dev + 1);
- + dev->name = sizeof_priv + (char *)(dev + 1);
- + new_device = 1;
- + }
- +
- + found: /* From the double loop above. */
- +
- + if (dev->name &&
- + ((dev->name[0] == '\0') || (dev->name[0] == ' '))) {
- + for (i = 0; i < MAX_ETH_CARDS; ++i)
- + if (ethdev_index[i] == NULL) {
- + sprintf(dev->name, "eth%d", i);
- + ethdev_index[i] = dev;
- + break;
- + }
- + }
- +
- + ether_setup(dev); /* Hmmm, should this be called here? */
- +
- + if (new_device) {
- + /* Append the device to the device queue. */
- + struct device **old_devp = &dev_base;
- + while ((*old_devp)->next)
- + old_devp = & (*old_devp)->next;
- + (*old_devp)->next = dev;
- + dev->next = 0;
- + }
- + return dev;
- +}
- +
- +void ether_setup(struct device *dev)
- +{
- + int i;
- + /* Fill in the fields of the device structure with ethernet-generic values.
- + This should be in a common file instead of per-driver. */
- + for (i = 0; i < DEV_NUMBUFFS; i++)
- + skb_queue_head_init(&dev->buffs[i]);
- +
- + /* register boot-defined "eth" devices */
- + if (dev->name && (strncmp(dev->name, "eth", 3) == 0)) {
- + i = simple_strtoul(dev->name + 3, NULL, 0);
- + if (ethdev_index[i] == NULL) {
- + ethdev_index[i] = dev;
- + }
- + else if (dev != ethdev_index[i]) {
- + /* Really shouldn't happen! */
- + printk("ether_setup: Ouch! Someone else took %s\n",
- + dev->name);
- + }
- + }
- +
- + dev->hard_header = eth_header;
- + dev->rebuild_header = eth_rebuild_header;
- + dev->type_trans = eth_type_trans;
- +
- + dev->type = ARPHRD_ETHER;
- +#ifndef __arm__
- + dev->hard_header_len = ETH_HLEN;
- +#else
- + dev->hard_header_len = (ETH_HLEN | 3) + 1;
- +#endif
- + dev->mtu = 1500; /* eth_mtu */
- + dev->addr_len = ETH_ALEN;
- + for (i = 0; i < ETH_ALEN; i++) {
- + dev->broadcast[i]=0xff;
- + }
- +
- + /* New-style flags. */
- + dev->flags = IFF_BROADCAST|IFF_MULTICAST;
- + dev->family = AF_INET;
- + dev->pa_addr = 0;
- + dev->pa_brdaddr = 0;
- + dev->pa_mask = 0;
- + dev->pa_alen = sizeof(unsigned long);
- +}
- +
- +int ether_config(struct device *dev, struct ifmap *map)
- +{
- + if (map->mem_start != (u_long)(-1))
- + dev->mem_start = map->mem_start;
- + if (map->mem_end != (u_long)(-1))
- + dev->mem_end = map->mem_end;
- + if (map->base_addr != (u_short)(-1))
- + dev->base_addr = map->base_addr;
- + if (map->irq != (u_char)(-1))
- + dev->irq = map->irq;
- + if (map->dma != (u_char)(-1))
- + dev->dma = map->dma;
- + if (map->port != (u_char)(-1))
- + dev->if_port = map->port;
- + return 0;
- +}
- +
- +int register_netdev(struct device *dev)
- +{
- + struct device *d = dev_base;
- + unsigned long flags;
- + int i=MAX_ETH_CARDS;
- +
- + save_flags(flags);
- + cli();
- +
- + if (dev && dev->init) {
- + if (dev->name &&
- + ((dev->name[0] == '\0') || (dev->name[0] == ' '))) {
- + for (i = 0; i < MAX_ETH_CARDS; ++i)
- + if (ethdev_index[i] == NULL) {
- + sprintf(dev->name, "eth%d", i);
- + printk("loading device '%s'...\n", dev->name);
- + ethdev_index[i] = dev;
- + break;
- + }
- + }
- +
- + if (dev->init(dev) != 0) {
- + if (i < MAX_ETH_CARDS) ethdev_index[i] = NULL;
- + restore_flags(flags);
- + return -EIO;
- + }
- +
- + /* Add device to end of chain */
- + if (dev_base) {
- + while (d->next)
- + d = d->next;
- + d->next = dev;
- + }
- + else
- + dev_base = dev;
- + dev->next = NULL;
- + }
- + restore_flags(flags);
- + return 0;
- +}
- +
- +void unregister_netdev(struct device *dev)
- +{
- + struct device *d = dev_base;
- + unsigned long flags;
- + int i;
- +
- + save_flags(flags);
- + cli();
- +
- + printk("unregister_netdev: device ");
- +
- + if (dev == NULL) {
- + printk("was NULL\n");
- + restore_flags(flags);
- + return;
- + }
- + /* else */
- + if (dev->start)
- + printk("'%s' busy\n", dev->name);
- + else {
- + if (dev_base == dev)
- + dev_base = dev->next;
- + else {
- + while (d && (d->next != dev))
- + d = d->next;
- +
- + if (d && (d->next == dev)) {
- + d->next = dev->next;
- + printk("'%s' unlinked\n", dev->name);
- + }
- + else {
- + printk("'%s' not found\n", dev->name);
- + restore_flags(flags);
- + return;
- + }
- + }
- + for (i = 0; i < MAX_ETH_CARDS; ++i) {
- + if (ethdev_index[i] == dev) {
- + ethdev_index[i] = NULL;
- + break;
- + }
- + }
- + }
- + restore_flags(flags);
- +}
- +
- +
- +
- +/*
- + * Local variables:
- + * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c net_init.c"
- + * version-control: t
- + * kept-new-versions: 5
- + * tab-width: 4
- + * End:
- + */
- diff -r -u -N linux.orig/arch/arm/drivers/net/nq8005.c linux.arm/arch/arm/drivers/net/nq8005.c
- --- linux.orig/arch/arm/drivers/net/nq8005.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/nq8005.c Fri Oct 27 23:15:00 1995
- @@ -0,0 +1,860 @@
- +/*
- + * linux/drivers/net/ether3.c
- + *
- + * SEEQ nq8005 ethernet driver
- + *
- + */
- +
- +#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>
- +
- +#include "ether3.h"
- +
- +#define FUNC_PROLOGUE \
- + struct arc_net *an = (struct arc_net *)dev->priv
- +
- +static unsigned int net_debug = NET_DEBUG;
- +
- +#define tx_done(dev) 0
- +/* ------------------------------------------------------------------------------- */
- +static char *version = "ether3 ethernet driver (c) 1995 R.M.King V1.00\n";
- +
- +#define BUS_16 16
- +#define BUS_8 8
- +
- +extern int inswb(int reg, void *buffer, int len);
- +extern int outswb(int reg, void *buffer, int len);
- +
- +unsigned char def_eth_addr[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
- +
- +static int ether3_prods[] = { 0x00A4 };
- +static int ether3_manus[] = { 0x0011 };
- +
- +/* --------------------------------------------------------------------------- */
- +
- +static void check_dev(struct device *dev, char *s)
- +{
- + struct device *dev2;
- +
- + dev2 = dev->next;
- +
- + while (dev2 && dev2 != dev)
- + dev2 = dev2->next;
- +
- + if (dev2 == dev)
- + printk("eek! - %s\n", s);
- +}
- +
- +/*
- + * set one of the receive address registers in the chip
- + */
- +
- +static void
- +ether3_setrxaddr(struct device *dev, int address, unsigned char *addr)
- +{
- + FUNC_PROLOGUE;
- + unsigned long flags;
- + int stat, i;
- +
- + save_flags(flags);
- + cli();
- +
- + stat = inw(REG_STATUS);
- + if(stat & STAT_RXON)
- + outw(an->regs.command | CMD_RXOFF, REG_COMMAND);
- +
- + outw(an->regs.config1 | address, REG_CONFIG1);
- + for(i=0; i<6; i++)
- + outb(addr[i], REG_BUFWIN);
- +
- + if(stat & STAT_RXON)
- + outw(an->regs.command | CMD_RXON, REG_COMMAND);
- +
- + restore_flags(flags);
- +}
- +
- +/*
- + * 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(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- + outw(an->regs.command | CMD_FIFOWRITE, REG_COMMAND);
- + while((inw(REG_STATUS) & STAT_FIFOEMPTY) == 0)
- + if(!timeout--)
- + {
- + printk("settxlim_16 broken\n");
- + an->broken = 1;
- + return;
- + }
- + outw(an->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(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- + outw(an->regs.command | CMD_FIFOWRITE, REG_COMMAND);
- + while((inw(REG_STATUS) & STAT_FIFOEMPTY) == 0)
- + if(!timeout--)
- + {
- + printk("setrxlim_16 broken\n");
- + an->broken = 1;
- + return;
- + }
- + outw(start, REG_DMAADDR);
- + outw(an->regs.command | CMD_FIFOREAD, REG_COMMAND);
- + }
- + if(length != 0)
- + inswb(REG_BUFWIN, data, length);
- +}
- +
- +/* --------------------------------------------------------------------------- */
- +
- +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_1(struct device *dev)
- +{
- + outb(0x80, REG_CONFIG2);
- + outb(0, REG_COMMAND);
- +
- + outb(1, REG_CONFIG1);
- + if(inb(REG_CONFIG1+1)==1 && inb(REG_CONFIG1)==0)
- + return BUS_8;
- + if(inw(REG_CONFIG1)==0x101)
- + return BUS_16;
- + return 0;
- +}
- +
- +static int
- +ether3_init_2(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + unsigned long a=0;
- + int i;
- +
- + ether3_setrxaddr(dev, CFG1_BUFSELSTAT0, dev->dev_addr);
- +
- + an->regs.config1 = CFG1_RECVSPECBROAD|CFG1_RECVCOMPSTAT0|CFG1_DMABURST8;
- + an->regs.config2 = CFG2_CTRLO|CFG2_RECVCRC|CFG2_ERRENCRC;
- +
- + outw(an->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
- + outw((TX_END>>8) - 1, REG_BUFWIN);
- + outw(an->regs.recvptr , REG_RECVPTR);
- + outw(an->regs.transmitptr , REG_TRANSMITPTR);
- + outw(an->regs.recvptr >> 8, REG_RECVEND);
- + outw(an->regs.config2 , REG_CONFIG2);
- + outw(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- + ether3_writebuffer(dev, &a, an->regs.transmitptr, 4);
- + outw(an->regs.command , REG_COMMAND);
- +
- + i = ether3_ramtest(dev, 0x5A);
- + if(i)
- + return i;
- + return ether3_ramtest(dev, 0x1E);
- +}
- +
- +/* This is the real probe routine. */
- +
- +static int
- +ether3_probe1(struct device *dev)
- +{
- + static unsigned version_printed = 0;
- + struct arc_net *an;
- + int i;
- +
- + if(!dev->priv)
- + dev->priv = kmalloc(sizeof(struct arc_net), GFP_KERNEL);
- +
- + if(!dev->priv)
- + return 1;
- +
- + an = (struct arc_net *) dev->priv;
- + memset(an, 0, sizeof(struct arc_net));
- + an->bus_type = BUS_16;
- + an->broken = 0;
- +
- + if((an->bus_type=ether3_init_1(dev))==0)
- + {
- + kfree(dev->priv);
- + return 1;
- + }
- +
- + if (net_debug && version_printed++ == 0)
- + printk(version);
- +
- + printk("%s: ether3 found (bus width %d), ", dev->name, an->bus_type);
- +
- + /* Retrive and print the ethernet address. */
- + for (i = 0; i < 6; i++)
- + printk(i==0?" %2.2x":i==5?":%2.2x\n":":%2.2x", dev->dev_addr[i]);
- +
- + if(ether3_init_2(dev))
- + {
- + kfree(dev->priv);
- + return 1;
- + }
- +
- + 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);
- + return -EAGAIN;
- + }
- +
- + irq2dev_map[dev->irq] = dev;
- +#endif
- + return 0;
- +}
- +
- +/* --------------------------------------------------------------------------- */
- +
- +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);
- +}
- +int
- +ether3_probe(struct device *dev)
- +{
- +#ifndef MODULE
- + struct expansion_card *ec;
- + unsigned char *eth_addr;
- + int base_addr;
- +
- + 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);
- +#endif
- + if(ether3_probe1(dev) == 0)
- + return 0;
- + return ENODEV;
- +}
- +
- +/* ------------------------------------------------------------------------ */
- +
- +static void
- +ether3_init_for_open(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + unsigned long a=0;
- +
- + an->regs.command = 0;
- + outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
- + while(inw(REG_STATUS) & (STAT_RXON|STAT_TXON));
- +
- + ether3_setrxaddr(dev, CFG1_BUFSELSTAT0, dev->dev_addr);
- + an->regs.transmitptr = 0; /* address that transmitter has processed to */
- + an->txinsert = 0; /* address of next available packet for tx */
- + an->txed = 0; /* transmitted length index */
- + an->txto = 0; /* to transmit length index */
- + an->tx_ends[0] = 0;
- + an->use = 0;
- +
- + an->regs.config2 |= CFG2_CTRLO;
- +
- + outw(an->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
- + outw((TX_END>>8) - 1, REG_BUFWIN);
- +
- + an->regs.recvptr = RX_START;
- + outw(an->regs.recvptr , REG_RECVPTR);
- + outw(an->regs.recvptr>>8, REG_RECVEND);
- +
- + outw(0, REG_TRANSMITPTR);
- + outw(an->regs.config2 , REG_CONFIG2);
- + outw(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- +
- + ether3_writebuffer(dev, &a, an->regs.transmitptr, 4);
- +
- +
- + an->regs.command = CMD_ENINTRX|CMD_ENINTTX;
- + outw(an->regs.command | CMD_RXON, REG_COMMAND);
- +}
- +
- +/* 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")) {
- + kfree(dev->priv);
- + return -EAGAIN;
- + }
- +
- + irq2dev_map[dev->irq] = dev;
- +#endif
- +
- + MOD_INC_USE_COUNT;
- +
- + dev->tbusy = 0;
- + dev->interrupt = 0;
- + dev->start = 1;
- + return 0;
- +}
- +
- +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, %s?\n", dev->name,
- + tx_done(dev) ? "IRQ conflict" : "network cable problem");
- + /* Try to restart the adaptor. */
- + dev->tbusy = 0;
- + an->use = 0;
- + an->regs.config2 |= CFG2_CTRLO;
- + outw(an->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 {
- + short length = ETH_ZLEN < (skb->len-2) ? (skb->len-2) : ETH_ZLEN;
- + unsigned char *buf = skb->data;
- + unsigned long flags;
- + unsigned long thisinfo, nextinfo=0;
- + int thisdata, thisstart, nextpacket;
- +
- + if(an->broken)
- + {
- + dev_kfree_skb(skb, FREE_WRITE);
- + an->stats.tx_dropped ++;
- + return 1;
- + }
- +
- + thisstart = an->txinsert;
- + thisdata = thisstart + 4;
- + if(thisdata >= TX_END)
- + thisdata -= TX_END;
- + nextpacket = thisdata + length;
- + if(nextpacket >= TX_END)
- + nextpacket -= TX_END;
- + an->txinsert = nextpacket;
- + an->tx_ends[an->txto++] = nextpacket;
- + if(an->txto >= MAX_TXED)
- + an->txto = 0;
- +
- + thisinfo = 0x00E80000 | ((nextpacket >> 8) & 0xff) | ((nextpacket << 8) & 0xff00);
- +
- + save_flags(flags);
- + cli();
- + an->use++;
- + an->regs.config2 &= ~CFG2_CTRLO;
- + outw(an->regs.config2 , REG_CONFIG2);
- +
- + ether3_writebuffer(dev, buf + 2, thisdata, length&1?(length+1):length);
- + ether3_writebuffer(dev, &nextinfo, -1, 4);
- + ether3_writebuffer(dev, &thisinfo, thisstart, 4);
- + restore_flags(flags);
- +
- + if((inw(REG_STATUS) & STAT_TXON)==0) {
- + outw(thisstart, REG_TRANSMITPTR);
- + outw(an->regs.command | CMD_TXON, REG_COMMAND);
- + }
- + dev->trans_start = jiffies;
- + }
- + dev_kfree_skb (skb, FREE_WRITE);
- +
- + /* You might need to clean up and record Tx statistics here. */
- +
- + return 0;
- +}
- +
- +/* The typical workload of the driver:
- + Handle the network interface interrupts. */
- +static void
- +ether3_interrupt(int irq, struct pt_regs *regs)
- +{
- + struct device *dev = (struct device *)(irq2dev_map[irq]);
- + struct arc_net *an = (struct arc_net *)dev->priv;
- + int status, boguscount = 0, 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;
- + }
- + dev->interrupt = 1;
- +
- + do {
- + status = inw(REG_STATUS);
- + if (status & STAT_INTRX) {
- + /* Got a packet(s). */
- + ether3_rx(dev);
- + done = 1;
- + }
- + if (status & STAT_INTTX) {
- + ether3_tx(dev);
- + done = 1;
- + }
- + outw((status & (STAT_INTRX|STAT_INTTX|STAT_INTBUFWIN))
- + | an->regs.command, REG_COMMAND);
- + } while (++boguscount < 20 && !done) ;
- +
- + dev->interrupt = 0;
- +
- +#if NET_DEBUG > 1
- + if(net_debug & DEBUG_INT)
- + printk("ether3_interrupt: done\n", irq);
- +#endif
- +}
- +
- +/* We have a good packet(s), get it/them out of the buffers. */
- +static void
- +ether3_rx(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + struct sk_buff *skb=NULL;
- + unsigned int end;
- + unsigned int length;
- +
- + an->use++;
- + if(an->use == 1)
- + {
- + an->regs.config2 &= ~CFG2_CTRLO;
- + outw(an->regs.config2 , REG_CONFIG2);
- + }
- + do {
- + unsigned char buffer[20];
- + ether3_readbuffer(dev, buffer, an->regs.recvptr, 20);
- +
- + if(buffer[0] == 0 || (buffer[3] & 0x80)==0)
- + goto rx_fin;
- +#if NET_DEBUG > 1
- + if(net_debug & DEBUG_RX) {
- + {
- + int i;
- + printk("ether3_rx:", an->regs.recvptr);
- + for(i=3; i<18; i++)
- + printk("%02X ",(unsigned int)buffer[i]);
- + printk("\n");
- + }
- +#endif
- +
- + end = (buffer[0]<<8) | buffer[1];
- +
- + if((buffer[2] & 0xC0) != 0x40)
- + /* no data follows */
- + goto done;
- +
- + if(buffer[3] & 15) {
- + an->stats.rx_errors++;
- + if(buffer[3] & 1) an->stats.rx_fifo_errors ++;
- + if(buffer[3] & 2) an->stats.rx_crc_errors ++;
- + if(buffer[3] & 4) an->stats.rx_fifo_errors ++;
- + if(buffer[3] & 8) an->stats.rx_length_errors ++;
- + goto done;
- + }
- +
- + if(end > an->regs.recvptr)
- + length = end - an->regs.recvptr;
- + else
- + length = (end - RX_START) + (RX_END - an->regs.recvptr);
- +
- + skb = alloc_skb(length + 4, GFP_ATOMIC);
- + if(skb == NULL) {
- + printk("%s: memory squeeze, dropping packet.\n", dev->name);
- + an->stats.rx_dropped++;
- + an->regs.recvptr = end;
- + outw(an->regs.recvptr >> 8, REG_RECVEND);
- + goto rx_fin;
- + }
- +
- + skb->len = length - 4;
- + skb->dev = dev;
- + memcpy(skb->data + 2, buffer + 4, 20-4);
- + ether3_readbuffer(dev, skb->data+18, -1, length-20);
- + done:
- + an->regs.recvptr = end;
- + outw(an->regs.recvptr >> 8, REG_RECVEND);
- + if(skb) {
- + netif_rx(skb);
- + skb=NULL;
- + an->stats.rx_packets++;
- + }
- + }
- + while(inw(REG_STATUS) & STAT_RXON);
- +
- + outw(an->regs.recvptr, REG_RECVPTR);
- + outw(an->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. */
- +rx_fin:
- + if(an->use)
- + {
- + an->use--;
- + if(an->use == 0)
- + {
- + an->regs.config2 |= CFG2_CTRLO;
- + outw(an->regs.config2 , REG_CONFIG2);
- + }
- + }
- +}
- +
- +/* ----------------------------------------------------------------------------- */
- +
- +static void
- +ether3_tx(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + unsigned char buffer[4];
- +
- + if(an->regs.transmitptr + 4 > TX_END) {
- + int len = TX_END - an->regs.transmitptr;
- + ether3_readbuffer(dev, buffer, an->regs.transmitptr, len);
- + ether3_readbuffer(dev, buffer+len, 0, 4 - len);
- + }
- + else
- + ether3_readbuffer(dev, buffer, an->regs.transmitptr, 4);
- +
- + if(!(buffer[3] & 0x80))
- + return;
- +
- + if(buffer[3] & 7) {
- + if(buffer[3] & 5) an->stats.tx_errors ++;
- + if(buffer[3] & 4) an->stats.collisions += 16;
- + if(buffer[3] & 2) an->stats.collisions += (buffer[3] & 0x78)>>3;
- + if(buffer[3] & 1) an->stats.tx_fifo_errors ++;
- + }
- + if(!(buffer[3] & 5))
- + an->stats.tx_packets++;
- +
- + an->regs.transmitptr = an->tx_ends[an->txed++];
- + if(an->txed >= MAX_TXED)
- + an->txed = 0;
- + if(an->regs.transmitptr >= TX_END)
- + an->regs.transmitptr -= TX_END;
- +/* if(an->regs.transmitptr != ((buffer[0]<<8)|buffer[1])) */
- +/* printk("%p:%p\n",an->regs.transmitptr, (buffer[2]<<24)|(buffer[3]<<16)|(buffer[0]<<8)|buffer[1]); */
- + dev->tbusy = 0;
- + mark_bh(NET_BH); /* Inform upper layers. */
- + if(an->use)
- + {
- + an->use--;
- + if(an->use == 0)
- + {
- + an->regs.config2 |= CFG2_CTRLO;
- + outw(an->regs.config2 , REG_CONFIG2);
- + }
- + }
- +}
- +
- +/* The inverse routine to net_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);
- + an->regs.command = 0;
- + while(inw(REG_STATUS) & (STAT_RXON|STAT_TXON));
- + ether3_init_1(dev);
- +
- + restore_flags(flags);
- +
- + /* Flush the Tx */
- +#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 &an->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;
- +
- + if(num_addrs == -1)
- + an->regs.config1 |= CFG1_RECVPROMISC;
- + else
- + an->regs.config1 = (an->regs.config1 & ~CFG1_RECVPROMISC)
- + | CFG1_RECVSPECBROAD;
- +
- + outw(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- +}
- +
- +#ifdef MODULE
- +char kernel_version[] = UTS_RELEASE;
- +static struct device ether3 = {
- + " ",
- + 0, 0, 0, 0,
- + 0, 0,
- + 0, 0, 0, NULL, ether3_probe
- +};
- +
- +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_probe;
- + 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("%s: device busy, remove delayed\n", ether3.name);
- + } 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 -r -u -N linux.orig/arch/arm/drivers/net/plip.c linux.arm/arch/arm/drivers/net/plip.c
- --- linux.orig/arch/arm/drivers/net/plip.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/plip.c Fri Oct 27 23:15:01 1995
- @@ -0,0 +1,1104 @@
- +/* $Id: plip.c,v 1.12 1995/02/11 10:26:05 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.
- + *
- + * 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.
- + */
- +
- +static char *version = "NET3 PLIP version 2.0 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
- +*/
- +
- +#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 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;
- + int (*orig_rebuild_header)(void *eth, struct device *dev,
- + unsigned long raddr, struct sk_buff *skb);
- +};
- +
- +/* Entry point of PLIP driver.
- + Probe the hardware, and register/initialize the driver. */
- +int
- +plip_init(struct device *dev)
- +{
- + struct net_local *nl;
- +
- + /* Check region before the probe */
- + if (check_region(PAR_DATA(dev), 3) < 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) {
- + printk("using assigned IRQ %d.\n", dev->irq);
- + } else {
- + int irq = 0;
- +#ifdef MODULE
- + /* dev->irq==0 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 > 0) {
- + 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), 3, 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)
- + return EAGAIN;
- + 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;
- +
- + /* 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
- + && 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;
- + outb(0x00, 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;
- + return OK;
- +
- + case PLIP_NB_2:
- + }
- +}
- +
- +/* 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;
- + outb(0x01, 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));
- + 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 || rcv->length.h < 8) {
- + printk("%s: bogus packet size %d.\n", dev->name, rcv->length.h);
- + return ERROR;
- + }
- + /* Malloc up new buffer. */
- + rcv->skb = alloc_skb(rcv->length.h, GFP_ATOMIC);
- + if (rcv->skb == NULL) {
- + printk("%s: Memory squeeze.\n", dev->name);
- + return ERROR;
- + }
- + rcv->skb->len = 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. */
- + 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. */
- + outb (0x00, 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));
- + enable_irq(dev->irq);
- + return OK;
- + } else {
- + nl->connection = PLIP_CN_NONE;
- + sti();
- + outb(PAR_INTR_ON, PAR_CONTROL(dev));
- + 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;
- + }
- +}
- +
- +/* 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:
- + /* Trigger remote rx interrupt. */
- + outb(0x08, 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) {
- + outb(0x00, data_addr);
- + return TIMEOUT;
- + }
- + }
- +
- + 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 */
- + outb (0x00, 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));
- + 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));
- + if ((status & 0xf8) == 0x80) {
- + 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));
- + enable_irq(dev->irq);
- + mark_bh(NET_BH);
- + } else {
- + nl->is_deferred = 1;
- + queue_task(&nl->deferred, &tq_timer);
- + }
- +
- + 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;
- +
- + if (dev == NULL) {
- + printk ("plip_interrupt: irq %d for unknown device.\n", irq);
- + return;
- + }
- +
- + if (dev->interrupt)
- + return;
- +
- + c0 = inb(PAR_STATUS(dev));
- + if ((c0 & 0xf8) != 0xc0) {
- + if (net_debug > 1)
- + printk("%s: spurious interrupt\n", dev->name);
- + return;
- + }
- + 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) {
- + 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 == 0) {
- + 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. */
- + outb (0x00, PAR_DATA(dev));
- +
- + /* Enable rx interrupt. */
- + outb(PAR_INTR_ON, PAR_CONTROL(dev));
- +
- + /* 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;
- +
- + /* 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();
- + outb(0x00, 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;
- +
- +static struct device dev_plip0 =
- +{
- + "plip0" /*"plip"*/,
- + 0, 0, 0, 0, /* memory */
- + 0x3BC, 5, /* base, irq */
- + 0, 0, 0, NULL, plip_init
- +};
- +
- +static struct device dev_plip1 =
- +{
- + "plip1" /*"plip"*/,
- + 0, 0, 0, 0, /* memory */
- + 0x378, 7, /* base, irq */
- + 0, 0, 0, NULL, plip_init
- +};
- +
- +static struct device dev_plip2 =
- +{
- + "plip2" /*"plip"*/,
- + 0, 0, 0, 0, /* memory */
- + 0x278, 2, /* base, irq */
- + 0, 0, 0, NULL, plip_init
- +};
- +
- +int
- +init_module(void)
- +{
- + int devices=0;
- +
- + if (register_netdev(&dev_plip0) != 0)
- + devices++;
- + if (register_netdev(&dev_plip1) != 0)
- + devices++;
- + if (register_netdev(&dev_plip2) != 0)
- + devices++;
- + if (devices == 0)
- + return -EIO;
- + return 0;
- +}
- +
- +void
- +cleanup_module(void)
- +{
- + if (dev_plip0.priv) {
- + unregister_netdev(&dev_plip0);
- + release_region(PAR_DATA(&dev_plip0), 3);
- + kfree_s(dev_plip0.priv, sizeof(struct net_local));
- + dev_plip0.priv = NULL;
- + }
- + if (dev_plip1.priv) {
- + unregister_netdev(&dev_plip1);
- + release_region(PAR_DATA(&dev_plip1), 3);
- + kfree_s(dev_plip1.priv, sizeof(struct net_local));
- + dev_plip1.priv = NULL;
- + }
- + if (dev_plip2.priv) {
- + unregister_netdev(&dev_plip2);
- + release_region(PAR_DATA(&dev_plip2), 3);
- + kfree_s(dev_plip2.priv, sizeof(struct net_local));
- + dev_plip2.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 -r -u -N linux.orig/arch/arm/drivers/net/ppp.c linux.arm/arch/arm/drivers/net/ppp.c
- --- linux.orig/arch/arm/drivers/net/ppp.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/ppp.c Fri Oct 27 23:15:02 1995
- @@ -0,0 +1,2126 @@
- +/*
- + PPP for Linux
- +*/
- +
- +/*
- + 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.):
- +
- + NET02D - Define if using Net-2-Debugged in kernels earlier
- + than v1.1.4.
- +
- + NEW_TTY_DRIVERS - Define if using new Ted Ts'o's alpha TTY drivers
- + from tsx-11.mit.edu. From Ted Ts'o.
- +
- + 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 not defined then the leading
- + flag is always sent.
- +*/
- +
- +/* #define NET02D -* */
- +#define NEW_TTY_DRIVERS /* */
- +#define OPTIMIZE_FLAG_TIME ((HZ * 3)/2) /* */
- +#define CHECK_CHARACTERS
- +
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/version.h>
- +#endif
- +
- +#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>
- +
- +#ifdef NET02D /* v1.1.4 net code and earlier */
- +#include <dev.h>
- +#include <skbuff.h>
- +#include <inet.h>
- +#define skb_queue_head_init(buf) *(buf) = NULL
- +#else /* v1.1.5 and later */
- +#include <linux/netdevice.h>
- +#include <linux/skbuff.h>
- +#include <linux/inet.h>
- +#endif
- +
- +#include <linux/ppp.h>
- +
- +#include <linux/ip.h>
- +#include <linux/tcp.h>
- +
- +#include "slhc.h"
- +
- +#include <linux/if_arp.h>
- +#ifndef ARPHRD_PPP
- +#define ARPHRD_PPP 0
- +#endif
- +
- +#define PRINTK(p) printk p ;
- +#define ASSERT(p) if (!p) PRINTK ((KERN_CRIT "assertion failed: " # p))
- +#define PRINTKN(n,p) {if (ppp_debug >= n) PRINTK (p)}
- +#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) (unsigned char) (c)) < 0x20) && \
- + ppp->recv_async_map & (1 << (c)))
- +
- +#define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f)))
- +
- +int ppp_debug = 2;
- +int ppp_debug_netpackets = 0;
- +
- +/* Define this string only once for all macro invocations */
- +static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n";
- +
- +int ppp_init(struct device *);
- +static void ppp_init_ctrl_blk(struct ppp *);
- +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 void ppp_kick_tty(struct ppp *);
- +
- +#ifdef NEW_TTY_DRIVERS
- +#define ppp_find(tty) ((struct ppp *) tty->disc_data)
- +#else
- +static void ppp_output_done(void *);
- +static void ppp_unesc(struct ppp *ppp, unsigned char *c, int n);
- +static struct ppp *ppp_find(struct tty_struct *);
- +#endif
- +
- +static void ppp_doframe(struct ppp *);
- +static int ppp_do_ip(struct ppp *, unsigned short, unsigned char *, int);
- +static int ppp_us_queue(struct ppp *, unsigned short, unsigned char *, int);
- +static int ppp_xmit(struct sk_buff *, struct device *);
- +static unsigned short ppp_type_trans(struct sk_buff *, struct device *);
- +
- +#ifdef NET02D
- +static int ppp_header(unsigned char *buff, struct device *dev,
- + unsigned short type, unsigned long daddr,
- + unsigned long saddr, unsigned len);
- +static int ppp_rebuild_header(void *buff, struct device *dev);
- +static void ppp_add_arp(unsigned long addr, struct sk_buff *skb,
- + struct device *dev);
- +#else
- +static int ppp_header(unsigned char *, struct device *, unsigned short,
- + void *, void *, unsigned, struct sk_buff *);
- +static int ppp_rebuild_header(void *, struct device *, unsigned long,
- + struct sk_buff *);
- +#endif
- +
- +static struct enet_statistics *ppp_get_stats (struct device *);
- +static struct ppp *ppp_alloc(void);
- +static int ppp_lock(struct ppp *);
- +static void ppp_unlock(struct ppp *);
- +static void ppp_add_fcs(struct ppp *);
- +static int ppp_check_fcs(struct ppp *);
- +static void ppp_print_buffer(const char *,char *,int,int);
- +
- +static int ppp_read(struct tty_struct *, struct file *, unsigned char *,
- + unsigned int);
- +static int ppp_write(struct tty_struct *, struct file *, unsigned char *,
- + unsigned int);
- +static int ppp_ioctl(struct tty_struct *, struct file *, unsigned int,
- + unsigned long);
- +static int ppp_select(struct tty_struct *tty, struct inode * inode,
- + struct file * filp, int sel_type, select_table * wait);
- +static int ppp_open(struct tty_struct *);
- +static void ppp_close(struct tty_struct *);
- +
- +#ifdef NEW_TTY_DRIVERS
- +static int ppp_receive_room(struct tty_struct *tty);
- +static void ppp_receive_buf(struct tty_struct *tty, unsigned char *cp,
- + char *fp, int count);
- +static void ppp_write_wakeup(struct tty_struct *tty);
- +#else
- +static void ppp_tty_input_ready(struct tty_struct *);
- +#endif
- +
- +/* FCS table from RFC1331 */
- +
- +static unsigned short fcstab[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
- + };
- +
- +struct tty_ldisc ppp_ldisc;
- +
- +static struct ppp ppp_ctrl[PPP_NRUNIT];
- +
- +/*************************************************************
- + * INITIALIZATION
- + *************************************************************/
- +
- +static int first_time = 1;
- +
- +/* called at boot time for each ppp device */
- +
- +int
- +ppp_init(struct device *dev)
- +{
- + struct ppp *ppp;
- + int i;
- +
- + ppp = &ppp_ctrl[dev->base_addr];
- +
- + if (first_time) {
- + first_time = 0;
- +
- + printk (KERN_INFO "PPP: version %s (%d channels)"
- +#ifdef NET02D
- + " NET02D"
- +#endif
- +#ifdef NEW_TTY_DRIVERS
- + " NEW_TTY_DRIVERS"
- +#endif
- +#ifdef OPTIMIZE_FLAG_TIME
- + " OPTIMIZE_FLAGS"
- +#endif
- + "\n", PPP_VERSION, PPP_NRUNIT);
- +
- + printk (KERN_INFO
- + "TCP compression code copyright 1989 Regents of the "
- + "University of California\n");
- +
- + (void) memset(&ppp_ldisc, 0, sizeof(ppp_ldisc));
- + ppp_ldisc.open = ppp_open;
- + ppp_ldisc.close = ppp_close;
- + ppp_ldisc.read = ppp_read;
- + ppp_ldisc.write = ppp_write;
- + ppp_ldisc.ioctl = ppp_ioctl;
- + ppp_ldisc.select = ppp_select;
- +
- +#ifdef NEW_TTY_DRIVERS
- + ppp_ldisc.magic = TTY_LDISC_MAGIC;
- + ppp_ldisc.receive_room = ppp_receive_room;
- + ppp_ldisc.receive_buf = ppp_receive_buf;
- + ppp_ldisc.write_wakeup = ppp_write_wakeup;
- +#else
- + ppp_ldisc.handler = ppp_tty_input_ready;
- +#endif
- +
- + if ((i = tty_register_ldisc(N_PPP, &ppp_ldisc)) == 0)
- + printk(KERN_INFO "PPP line discipline registered.\n");
- + else
- + printk(KERN_ERR "error registering line discipline: %d\n", i);
- + }
- +
- + /* initialize PPP control block */
- + ppp_init_ctrl_blk (ppp);
- + ppp->inuse = 0;
- + ppp->line = dev->base_addr;
- + ppp->tty = NULL;
- + ppp->dev = dev;
- +
- + /* clear statistics */
- + memset (&ppp->stats, '\0', sizeof (struct ppp_stats));
- +
- + /* device INFO */
- + dev->mtu = PPP_MTU;
- + dev->hard_start_xmit = ppp_xmit;
- + dev->open = ppp_dev_open;
- + dev->stop = ppp_dev_close;
- + dev->get_stats = ppp_get_stats;
- + dev->hard_header = ppp_header;
- + dev->type_trans = ppp_type_trans;
- + dev->rebuild_header = ppp_rebuild_header;
- + dev->hard_header_len = 0;
- + dev->addr_len = 0;
- + dev->type = ARPHRD_PPP;
- +
- +#ifdef NET02D
- + dev->add_arp = ppp_add_arp;
- + dev->queue_xmit = dev_queue_xmit;
- +#else
- + dev->do_ioctl = ppp_dev_ioctl;
- +#endif
- +
- + for (i = 0; i < DEV_NUMBUFFS; i++)
- + skb_queue_head_init(&dev->buffs[i]); /* = NULL if NET02D */
- +
- + /* 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 = sizeof(unsigned long);
- +
- + return 0;
- +}
- +
- +static void
- +ppp_init_ctrl_blk(struct ppp *ppp)
- +{
- + ppp->magic = PPP_MAGIC;
- + ppp->sending = 0;
- + ppp->toss = 0xFE;
- + ppp->escape = 0;
- +
- + ppp->flags = 0;
- + ppp->mtu = PPP_MTU;
- + ppp->mru = PPP_MRU;
- + ppp->fcs = 0;
- +
- + 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->slcomp = NULL;
- + ppp->rbuff = NULL;
- + ppp->xbuff = NULL;
- + ppp->cbuff = NULL;
- +
- + ppp->rhead = NULL;
- + ppp->rend = NULL;
- + ppp->rcount = 0;
- + ppp->xhead = NULL;
- + ppp->xtail = NULL;
- +
- + ppp->us_rbuff = NULL;
- + ppp->us_rbuff_end = NULL;
- + ppp->us_rbuff_head = NULL;
- + ppp->us_rbuff_tail = NULL;
- + ppp->read_wait = NULL;
- + ppp->write_wait = NULL;
- + ppp->us_rbuff_lock = 0;
- + ppp->inp_sig = 0;
- + ppp->inp_sig_pid = 0;
- +
- +#ifdef OPTIMIZE_FLAG_TIME /* ensure flag will always be sent first time */
- + ppp->last_xmit = jiffies - OPTIMIZE_FLAG_TIME;
- +#else
- + ppp->last_xmit = 0;
- +#endif
- +
- + /* clear statistics */
- + memset (&ppp->stats, '\0', sizeof (struct ppp_stats));
- +
- + /* Reset the demand dial information */
- + ppp->ddinfo.ip_sjiffies =
- + ppp->ddinfo.ip_rjiffies =
- + ppp->ddinfo.nip_sjiffies =
- + ppp->ddinfo.nip_rjiffies = jiffies;
- +}
- +
- +/*
- + * 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 void
- +ppp_changedmtu (struct ppp *ppp, int new_mtu, int new_mru)
- +{
- + struct device *dev;
- + unsigned char *new_rbuff, *new_xbuff, *new_cbuff;
- + unsigned char *old_rbuff, *old_xbuff, *old_cbuff;
- + int mtu, mru;
- +/*
- + * Allocate the buffer from the kernel for the data
- + */
- + dev = ppp->dev;
- + mru = new_mru;
- + mtu = new_mtu;
- +
- + /* RFC 1331, section 7.2 says the minimum value is 1500 bytes */
- + if (mru < PPP_MRU)
- + mru = PPP_MRU;
- +
- + mtu = (mtu * 2) + 20;
- + mru = (mru * 2) + 20;
- +
- + PRINTKN (2,(KERN_INFO "ppp: channel %s mtu = %d, mru = %d\n",
- + dev->name, new_mtu, new_mru));
- +
- + new_xbuff = (unsigned char *) kmalloc(mtu + 4, GFP_ATOMIC);
- + new_rbuff = (unsigned char *) kmalloc(mru + 4, GFP_ATOMIC);
- + new_cbuff = (unsigned char *) kmalloc(mru + 4, GFP_ATOMIC);
- +/*
- + * If the buffers failed to allocate then complain.
- + */
- + if (new_xbuff == NULL || new_rbuff == NULL || new_cbuff == NULL)
- + {
- + PRINTKN (2,(KERN_ERR "ppp: failed to allocate new buffers\n"));
- +/*
- + * Release new buffer pointers if the updates were not performed
- + */
- + if (new_rbuff != NULL)
- + kfree (new_rbuff);
- +
- + if (new_xbuff != NULL)
- + kfree (new_xbuff);
- +
- + if (new_cbuff != NULL)
- + kfree (new_cbuff);
- + }
- +/*
- + * Update the pointers to the new buffer structures.
- + */
- + else
- + {
- + cli();
- + old_xbuff = ppp->xbuff;
- + old_rbuff = ppp->rbuff;
- + old_cbuff = ppp->cbuff;
- +
- + ppp->xbuff = new_xbuff;
- + ppp->rbuff = new_rbuff;
- + ppp->cbuff = new_cbuff;
- +
- + dev->mem_start = (unsigned long) new_xbuff;
- + dev->mem_end = (unsigned long) (dev->mem_start + mtu);
- +
- + dev->rmem_start = (unsigned long) new_rbuff;
- + ppp->rend = (unsigned char *)
- + dev->rmem_end = (unsigned long) (dev->rmem_start + mru);
- +
- + ppp->rhead = new_rbuff;
- +/*
- + * Update the parameters for the new buffer sizes
- + */
- + ppp->toss = 0xFE;
- + ppp->escape = 0;
- + ppp->sending = 0;
- + ppp->rcount = 0;
- +
- + ppp->mru = new_mru;
- +
- + ppp->mtu =
- + dev->mtu = new_mtu;
- +
- + sti();
- +/*
- + * Release old buffer pointers
- + */
- + if (old_rbuff != NULL)
- + kfree (old_rbuff);
- +
- + if (old_xbuff != NULL)
- + kfree (old_xbuff);
- +
- + if (old_cbuff != NULL)
- + kfree (old_cbuff);
- + }
- +}
- +
- +/* called when we abandon the PPP line discipline */
- +
- +static void
- +ppp_release(struct ppp *ppp)
- +{
- +#ifdef NEW_TTY_DRIVERS
- + if (ppp->tty != NULL && ppp->tty->disc_data == ppp)
- + ppp->tty->disc_data = NULL; /* Break the tty->ppp link */
- +#endif
- +
- + if (ppp->dev) {
- + dev_close (ppp->dev);
- + ppp->dev->flags = 0;
- + }
- +
- + kfree (ppp->xbuff);
- + kfree (ppp->cbuff);
- + kfree (ppp->rbuff);
- + kfree (ppp->us_rbuff);
- +
- + ppp->xbuff =
- + ppp->cbuff =
- + ppp->rbuff =
- + ppp->us_rbuff = NULL;
- +
- + if (ppp->slcomp) {
- + slhc_free(ppp->slcomp);
- + ppp->slcomp = NULL;
- + }
- +
- + ppp->inuse = 0;
- + ppp->tty = NULL;
- +}
- +
- +static void
- +ppp_close(struct tty_struct *tty)
- +{
- + struct ppp *ppp = ppp_find(tty);
- +
- + if (ppp == NULL || ppp->magic != PPP_MAGIC) {
- + PRINTKN (1,(KERN_WARNING "ppp: trying to close unopened tty!\n"));
- + } else {
- + CHECK_PPP_VOID();
- + ppp_release (ppp);
- +
- + PRINTKN (2,(KERN_INFO "ppp: channel %s closing.\n", ppp->dev->name));
- + }
- +}
- +
- +/* called when PPP line discipline is selected on a tty */
- +static int
- +ppp_open(struct tty_struct *tty)
- +{
- + struct ppp *ppp = ppp_find(tty);
- +
- + if (ppp) {
- + PRINTKN (1,(KERN_ERR "ppp_open: gack! tty already associated to %s!\n",
- + ppp->magic == PPP_MAGIC ? ppp->dev->name : "unknown"));
- + return -EEXIST;
- + }
- +
- + ppp = ppp_alloc();
- + if (ppp == NULL) {
- + PRINTKN (1,(KERN_ERR "ppp_open: couldn't allocate ppp channel\n"));
- + return -ENFILE;
- + }
- +
- + /* make sure the channel is actually open */
- + ppp_init_ctrl_blk (ppp);
- +
- + ppp->tty = tty;
- +
- +#ifdef NEW_TTY_DRIVERS
- + tty->disc_data = ppp;
- + if (tty->driver.flush_buffer)
- + tty->driver.flush_buffer(tty);
- + if (tty->ldisc.flush_buffer)
- + tty->ldisc.flush_buffer(tty);
- +#else
- + tty_read_flush (tty);
- + tty_write_flush (tty);
- +#endif
- +
- + if ((ppp->slcomp = slhc_init(16, 16)) == NULL) {
- + PRINTKN (1,(KERN_ERR "ppp: no space for compression buffers!\n"));
- + ppp_release (ppp);
- + return -ENOMEM;
- + }
- +
- + /* Define the buffers for operation */
- + ppp_changedmtu (ppp, ppp->dev->mtu, ppp->mru);
- + if (ppp->rbuff == NULL) {
- + ppp_release (ppp);
- + return -ENOMEM;
- + }
- +
- + /* Allocate a user-level receive buffer */
- + ppp->us_rbuff = (unsigned char *) kmalloc (RBUFSIZE, GFP_KERNEL);
- + if (ppp->us_rbuff == NULL) {
- + PRINTKN (1,(KERN_ERR "ppp: no space for user receive buffer\n"));
- + ppp_release (ppp);
- + return -ENOMEM;
- + }
- +
- + ppp->us_rbuff_head =
- + ppp->us_rbuff_tail = ppp->us_rbuff;
- + ppp->us_rbuff_end = ppp->us_rbuff + RBUFSIZE;
- +
- + PRINTKN (2,(KERN_INFO "ppp: channel %s open\n", ppp->dev->name));
- +
- +#ifdef MODULE
- + MOD_INC_USE_COUNT;
- +#endif
- +
- + return (ppp->line);
- +}
- +
- +/* called when ppp interface goes "up". here this just means we start
- + passing IP packets */
- +static int
- +ppp_dev_open(struct device *dev)
- +{
- + struct ppp *ppp = &ppp_ctrl[dev->base_addr];
- +
- + /* reset POINTOPOINT every time, since dev_close zaps it! */
- + dev->flags |= IFF_POINTOPOINT;
- +
- + if (ppp->tty == NULL) {
- + PRINTKN (1,(KERN_ERR "ppp: %s not connected to a TTY! can't go open!\n",
- + dev->name));
- + return -ENXIO;
- + }
- +
- + PRINTKN (2,(KERN_INFO "ppp: channel %s going up for IP packets!\n",
- + dev->name));
- +
- + CHECK_PPP(-ENXIO);
- + return 0;
- +}
- +
- +static int
- +ppp_dev_close(struct device *dev)
- +{
- + struct ppp *ppp = &ppp_ctrl[dev->base_addr];
- +
- + if (ppp->tty == NULL) {
- + PRINTKN (1,(KERN_ERR "ppp: %s not connected to a TTY! can't go down!\n",
- + dev->name));
- + return -ENXIO;
- + }
- +
- + PRINTKN (2,(KERN_INFO "ppp: channel %s going down for IP packets!\n",
- + dev->name));
- + CHECK_PPP(-ENXIO);
- +#ifdef MODULE
- + MOD_DEC_USE_COUNT;
- +#endif
- + return 0;
- +}
- +
- +#ifndef NET02D
- +static int ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
- +{
- + struct ppp *ppp = &ppp_ctrl[dev->base_addr];
- + int error;
- +
- + struct stats
- + {
- + struct ppp_stats ppp_stats;
- + struct slcompress slhc;
- + } *result;
- +
- + error = verify_area (VERIFY_READ,
- + ifr->ifr_ifru.ifru_data,
- + sizeof (struct stats));
- +
- + if (error == 0) {
- + result = (struct stats *) ifr->ifr_ifru.ifru_data;
- +
- + memcpy_tofs (&result->ppp_stats, &ppp->stats, sizeof (struct ppp_stats));
- + if (ppp->slcomp)
- + memcpy_tofs (&result->slhc, ppp->slcomp, sizeof (struct slcompress));
- + }
- +
- + return error;
- +}
- +#endif
- +
- +/*************************************************************
- + * TTY OUTPUT
- + * The following function delivers a fully-formed PPP
- + * frame in ppp->xbuff to the TTY for output.
- + *************************************************************/
- +
- +#ifdef NEW_TTY_DRIVERS
- +static inline void
- +#else
- +static void
- +#endif
- +ppp_output_done (void *ppp)
- +{
- + /* unlock the transmitter queue */
- + ppp_unlock ((struct ppp *) ppp);
- +
- + /* If the device is still up then enable the transmitter of the
- + next frame. */
- + if (((struct ppp *) ppp)->dev->flags & IFF_UP)
- +#ifndef NET02D
- + mark_bh (NET_BH);
- +#else
- + dev_tint (((struct ppp *) ppp)->dev);
- +#endif
- +
- + /* enable any blocked process pending transmission */
- + wake_up_interruptible (&((struct ppp *) ppp)->write_wait);
- +}
- +
- +#ifndef NEW_TTY_DRIVERS
- +static void
- +ppp_kick_tty (struct ppp *ppp)
- +{
- + register int count = ppp->xhead - ppp->xbuff;
- + register int answer;
- +
- + ppp->stats.sbytes += count;
- +
- + answer = tty_write_data (ppp->tty,
- + ppp->xbuff,
- + count,
- + ppp_output_done,
- + (void *) ppp);
- +
- + if (answer == 0)
- + ppp_output_done (ppp); /* Should not happen */
- + else
- + if (answer < 0) {
- + ppp->stats.serrors++;
- + ppp_output_done (ppp); /* unlock the transmitter */
- + }
- +}
- +
- +#else
- +
- +static void
- +ppp_kick_tty (struct ppp *ppp)
- +{
- + register int count, actual;
- +
- + count = ppp->xhead - ppp->xbuff;
- +
- + actual = ppp->tty->driver.write(ppp->tty, 0, ppp->xbuff, count);
- + ppp->stats.sbytes += actual;
- + if (actual == count) {
- + ppp_output_done(ppp);
- + } else {
- + ppp->xtail = ppp->xbuff + actual;
- + ppp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
- + }
- +}
- +
- +static void ppp_write_wakeup(struct tty_struct *tty)
- +{
- + register int count, actual;
- + struct ppp *ppp = ppp_find(tty);
- +
- + if (!ppp || ppp->magic != PPP_MAGIC) {
- + PRINTKN (1,
- + (KERN_ERR "PPP: write_wakeup called but couldn't "
- + "find PPP struct.\n"));
- + return;
- + }
- +
- + if (!ppp->xtail)
- + return;
- +
- + cli();
- + if (ppp->flags & SC_XMIT_BUSY) {
- + sti();
- + return;
- + }
- + ppp->flags |= SC_XMIT_BUSY;
- + sti();
- +
- + count = ppp->xhead - ppp->xtail;
- +
- + actual = tty->driver.write(tty, 0, ppp->xtail, count);
- + ppp->stats.sbytes += actual;
- + if (actual == count) {
- + ppp->xtail = 0;
- + tty->flags &= ~TTY_DO_WRITE_WAKEUP;
- +
- + ppp_output_done(ppp);
- + } else {
- + ppp->xtail += actual;
- + }
- + ppp->flags &= ~SC_XMIT_BUSY;
- +}
- +#endif
- +
- +/*************************************************************
- + * 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.
- + *************************************************************/
- +
- +/* stuff a single character into the receive buffer */
- +
- +static inline void
- +ppp_enqueue(struct ppp *ppp, unsigned char c)
- +{
- + unsigned long flags;
- +
- + save_flags(flags);
- + cli();
- + if (ppp->rhead < ppp->rend) {
- + *ppp->rhead = c;
- + ppp->rhead++;
- + ppp->rcount++;
- + } else
- + ppp->stats.roverrun++;
- + restore_flags(flags);
- +}
- +
- +#ifdef CHECK_CHARACTERS
- +static unsigned paritytab[8] = {
- + 0x96696996, 0x69969669, 0x69969669, 0x96696996,
- + 0x69969669, 0x96696996, 0x96696996, 0x69969669
- +};
- +#endif
- +
- +#ifndef NEW_TTY_DRIVERS
- +static void
- +ppp_dump_inqueue(struct tty_struct *tty)
- +{
- + int head = tty->read_q.head,
- + tail = tty->read_q.tail,
- + i, count;
- + char buffer[8];
- +
- + PRINTK ((KERN_DEBUG "INQUEUE: head %d tail %d imode %x:\n", head, tail,
- + (unsigned int) tty->termios->c_iflag))
- +
- + i = tail;
- + count = 0;
- +
- + while (i != head) {
- + buffer [count] = tty->read_q.buf[i];
- + if (++count == 8) {
- + ppp_print_buffer (NULL, buffer, 8, KERNEL_DS);
- + count = 0;
- + }
- + i = (i + 1) & (TTY_BUF_SIZE - 1);
- + }
- + ppp_print_buffer (NULL, buffer, count, KERNEL_DS);
- +}
- +
- +/* called by lower levels of TTY driver when data becomes available.
- + all incoming data comes through this function. */
- +
- +void ppp_tty_input_ready(struct tty_struct *tty)
- +{
- + struct ppp *ppp = ppp_find(tty);
- + int n, error;
- + unsigned char buff[128];
- +
- +/* PRINTK( (KERN_DEBUG "PPP: handler called.\n") ) */
- + if (!ppp || ppp->magic != PPP_MAGIC) {
- + PRINTKN (1,
- + (KERN_ERR "PPP: handler called but couldn't find PPP struct.\n"));
- + return;
- + }
- +
- + CHECK_PPP_VOID();
- +
- + /* ZZZ */
- + if (ppp_debug >= 5)
- + ppp_dump_inqueue(ppp->tty);
- +
- + do {
- + n = tty_read_raw_data(tty, buff, 128);
- + if ( n == 0 ) /* nothing there */
- + break;
- +
- + if (ppp_debug >= 5)
- + ppp_print_buffer ("receive buffer", buff, n > 0 ? n : -n, KERNEL_DS);
- +
- + if ( n < 0 ) {
- + /* Last character is error flag.
- + Process the previous characters, then set toss flag. */
- + n = (-n) - 1;
- + error = buff[n];
- + } else error = 0;
- + ppp->stats.rbytes += n;
- + ppp_unesc(ppp,buff,n);
- + if (error)
- + ppp->toss = error;
- + } while (1);
- +}
- +
- +/* recover frame by undoing PPP escape mechanism;
- + copies N chars of input data from C into PPP->rbuff
- + calls ppp_doframe to dispose of any frames it finds
- +*/
- +
- +static void
- +ppp_unesc(struct ppp *ppp, unsigned char *c, int n)
- +{
- + int i;
- +
- + for (i = 0; i < n; i++, c++) {
- + PRINTKN (6,(KERN_DEBUG "(%x)", (unsigned int) *c));
- +
- +#ifdef CHECK_CHARACTERS
- + if (*c & 0x80)
- + sc->sc_flags |= SC_RCV_B7_1;
- + else
- + sc->sc_flags |= SC_RCV_B7_0;
- +
- + if (paritytab[*c >> 5] & (1 << (*c & 0x1F)))
- + sc->sc_flags |= SC_RCV_ODDP;
- + else
- + sc->sc_flags |= SC_RCV_EVNP;
- +#endif
- +
- + switch (*c) {
- + case PPP_ESC: /* PPP_ESC: invert 0x20 in next character */
- + ppp->escape = PPP_TRANS;
- + break;
- +
- + case PPP_FLAG: /* PPP_FLAG: end of frame */
- + if (ppp->escape) /* PPP_ESC just before PPP_FLAG is illegal */
- + ppp->toss = 0xFF;
- +
- + if ((ppp->toss & 0x80) == 0)
- + ppp_doframe(ppp); /* pass frame on to next layers */
- +
- + ppp->rcount = 0;
- + ppp->rhead = ppp->rbuff;
- + ppp->escape = 0;
- + ppp->toss = 0;
- + break;
- +
- + default: /* regular character */
- + if (!in_rmap (ppp, *c)) {
- + if (ppp->toss == 0)
- + ppp_enqueue (ppp, *c ^ ppp->escape);
- + ppp->escape = 0;
- + }
- + break;
- + }
- + }
- +}
- +
- +#else
- +static int ppp_receive_room(struct tty_struct *tty)
- +{
- + return 65536; /* We can handle an infinite amount of data. :-) */
- +}
- +
- +
- +static void ppp_receive_buf(struct tty_struct *tty, unsigned char *cp,
- + char *fp, int count)
- +{
- + register struct ppp *ppp = ppp_find (tty);
- + unsigned char c;
- +
- +/* PRINTK( ("PPP: handler called.\n") ); */
- +
- + if (!ppp || ppp->magic != PPP_MAGIC) {
- + PRINTKN (1,("PPP: handler called but couldn't find "
- + "PPP struct.\n"));
- + return;
- + }
- +
- + CHECK_PPP_VOID();
- +
- + if (ppp_debug >= 5) {
- + ppp_print_buffer ("receive buffer", cp, count, KERNEL_DS);
- + }
- +
- + ppp->stats.rbytes += count;
- +
- + while (count-- > 0) {
- + c = *cp++;
- +
- + if (fp) {
- + if (*fp && ppp->toss == 0)
- + ppp->toss = *fp;
- + fp++;
- + }
- +
- +#ifdef CHECK_CHARACTERS
- + if (c & 0x80)
- + ppp->flags |= SC_RCV_B7_1;
- + else
- + ppp->flags |= SC_RCV_B7_0;
- +
- + if (paritytab[c >> 5] & (1 << (c & 0x1F)))
- + ppp->flags |= SC_RCV_ODDP;
- + else
- + ppp->flags |= SC_RCV_EVNP;
- +#endif
- +
- + switch (c) {
- + case PPP_ESC: /* PPP_ESC: invert 0x20 in next character */
- + ppp->escape = PPP_TRANS;
- + break;
- +
- + case PPP_FLAG: /* PPP_FLAG: end of frame */
- + if (ppp->escape) /* PPP_ESC just before PPP_FLAG is "cancel"*/
- + ppp->toss = 0xFF;
- +
- + if ((ppp->toss & 0x80) == 0)
- + ppp_doframe(ppp); /* pass frame on to next layers */
- +
- + ppp->rcount = 0;
- + ppp->rhead = ppp->rbuff;
- + ppp->escape = 0;
- + ppp->toss = 0;
- + break;
- +
- + default: /* regular character */
- + if (!in_rmap (ppp, c)) {
- + if (ppp->toss == 0)
- + ppp_enqueue (ppp, c ^ ppp->escape);
- + ppp->escape = 0;
- + }
- + }
- + }
- +}
- +#endif
- +
- +/* on entry, a received frame is in ppp->rbuff
- + check it and dispose as appropriate */
- +static void
- +ppp_doframe(struct ppp *ppp)
- +{
- + u_char *c = ppp->rbuff;
- + u_short proto;
- + int count = ppp->rcount;
- +
- + /* forget it if we've already noticed an error */
- + if (ppp->toss) {
- + PRINTKN (1, (KERN_WARNING "ppp_toss: tossing frame, reason = %d\n",
- + ppp->toss));
- + slhc_toss (ppp->slcomp);
- + ppp->stats.rerrors++;
- + return;
- + }
- +
- + /* do this before printing buffer to avoid generating copious output */
- + if (count == 0)
- + return;
- +
- + if (ppp_debug >= 3)
- + ppp_print_buffer ("receive frame", c, count, KERNEL_DS);
- +
- + if (count < 4) {
- + PRINTKN (1,(KERN_WARNING "ppp: got runt ppp frame, %d chars\n", count));
- + slhc_toss (ppp->slcomp);
- + ppp->stats.runts++;
- + return;
- + }
- +
- + /* check PPP error detection field */
- + if (!ppp_check_fcs(ppp)) {
- + PRINTKN (1,(KERN_WARNING "ppp: frame with bad fcs\n"));
- + slhc_toss (ppp->slcomp);
- + ppp->stats.rerrors++;
- + return;
- + }
- +
- + count -= 2; /* ignore last two characters */
- +
- + /* now we have a good frame */
- + /* figure out the protocol field */
- + if ((c[0] == PPP_ADDRESS) && (c[1] == PPP_CONTROL)) {
- + c = c + 2; /* ADDR/CTRL not compressed, so skip */
- + count -= 2;
- + }
- +
- + proto = (u_short) *c++; /* PROTO compressed */
- + if (proto & 1) {
- + count--;
- + } else {
- + proto = (proto << 8) | (u_short) *c++; /* PROTO uncompressed */
- + count -= 2;
- + }
- +
- + /* Send the frame to the network if the ppp device is up */
- + if ((ppp->dev->flags & IFF_UP) && ppp_do_ip(ppp, proto, c, count)) {
- + ppp->ddinfo.ip_rjiffies = jiffies;
- + return;
- + }
- +
- + /* If we got here, it has to go to a user process doing a read,
- + so queue it.
- +
- + User process expects to get whole frame (for some reason), so
- + use count+2 so as to include FCS field. */
- +
- + if (ppp_us_queue (ppp, proto, c, count+2)) {
- + ppp->ddinfo.nip_rjiffies = jiffies;
- + ppp->stats.rothers++;
- + return;
- + }
- +
- + /* couldn't cope. */
- + PRINTKN (1,(KERN_WARNING
- + "ppp: dropping packet on the floor: nobody could take it.\n"));
- + slhc_toss (ppp->slcomp);
- + ppp->stats.tossed++;
- +}
- +
- +/* Examine packet at C, attempt to pass up to net layer.
- + PROTO is the protocol field from the PPP frame.
- + Return 1 if could handle it, 0 otherwise. */
- +
- +static int
- +ppp_do_ip (struct ppp *ppp, unsigned short proto, unsigned char *c,
- + int count)
- +{
- + int flags, done;
- +
- + PRINTKN (4,(KERN_DEBUG "ppp_do_ip: proto %x len %d first byte %x\n",
- + (int) proto, count, c[0]));
- +
- + if (ppp_debug_netpackets) {
- + PRINTK (("KERN_DEBUG %s <-- proto %x len %d\n", ppp->dev->name,
- + (int) proto, count));
- + }
- +
- + if (proto == PROTO_IP) {
- + ppp->stats.runcomp++;
- + goto sendit;
- + }
- +
- + if ((proto == PROTO_VJCOMP) && !(ppp->flags & SC_REJ_COMP_TCP)) {
- + /* get space for uncompressing the header */
- + done = 0;
- + save_flags (flags);
- + cli();
- + if ((ppp->rhead + 80) < ppp->rend) {
- + ppp->rhead += 80;
- + ppp->rcount += 80;
- + done = 1;
- + }
- + restore_flags(flags);
- +
- + if (! done) {
- + PRINTKN (1,(KERN_NOTICE
- + "ppp: no space to decompress VJ compressed TCP header.\n"));
- + ppp->stats.roverrun++;
- + slhc_toss (ppp->slcomp);
- + return 1;
- + }
- +
- + count = slhc_uncompress(ppp->slcomp, c, count);
- + if (count <= 0) {
- + ppp->stats.rerrors++;
- + PRINTKN (1,(KERN_NOTICE "ppp: error in VJ decompression\n"));
- + slhc_toss (ppp->slcomp);
- + return 1;
- + }
- + ppp->stats.rcomp++;
- + goto sendit;
- + }
- +
- + if ((proto == PROTO_VJUNCOMP) && !(ppp->flags & SC_REJ_COMP_TCP)) {
- + if (slhc_remember(ppp->slcomp, c, count) <= 0) {
- + ppp->stats.rerrors++;
- + PRINTKN (1,(KERN_NOTICE "ppp: error in VJ memorizing\n"));
- + slhc_toss (ppp->slcomp);
- + return 1;
- + }
- + ppp->stats.runcomp++;
- + goto sendit;
- + }
- +
- + /* not ours */
- + return 0;
- +
- + sendit:
- + if (ppp_debug_netpackets) {
- + struct iphdr *iph = (struct iphdr *) c;
- + PRINTK ((KERN_INFO "%s <-- src %lx dst %lx len %d\n", ppp->dev->name,
- + iph->saddr, iph->daddr, count))
- + }
- +
- + /* receive the frame through the network software */
- + (void) dev_rint (c, count, 0, ppp->dev);
- + return 1;
- +}
- +
- +/* stuff packet at BUF, length LEN, into the us_rbuff buffer
- + prepend PROTO information */
- +
- +#define PUTC(c,label) *ppp->us_rbuff_head++ = c; \
- + if (ppp->us_rbuff_head == ppp->us_rbuff_end) \
- + ppp->us_rbuff_head = ppp->us_rbuff; \
- + if (ppp->us_rbuff_head == ppp->us_rbuff_tail) \
- + goto label;
- +#define GETC(c) c = *ppp->us_rbuff_tail++; \
- + if (ppp->us_rbuff_tail == ppp->us_rbuff_end) \
- + ppp->us_rbuff_tail = ppp->us_rbuff;
- +
- +static int
- +ppp_us_queue(struct ppp *ppp, unsigned short proto,
- + unsigned char *buf, int len)
- +{
- + int totlen;
- + unsigned char *saved_head;
- +
- + totlen = len+2; /* including protocol */
- +
- + if (set_bit(1, &ppp->us_rbuff_lock)) {
- + PRINTKN (1, (KERN_NOTICE "ppp_us_queue: can't get lock\n"));
- + return 0;
- + }
- + saved_head = ppp->us_rbuff_head;
- +
- + PUTC((totlen & 0xff00) >> 8, failure);
- + PUTC(totlen & 0x00ff, failure);
- + PUTC((proto & 0xff00) >> 8, failure);
- + PUTC(proto & 0x00ff, failure);
- +
- + while (len-- > 0) {
- + PUTC(*buf++, failure);
- + }
- +
- + PRINTKN (3, (KERN_INFO "ppp: successfully queued %d bytes\n", totlen));
- + clear_bit(1, &ppp->us_rbuff_lock);
- + wake_up_interruptible (&ppp->read_wait);
- +
- +#ifdef NEW_TTY_DRIVERS
- + kill_fasync(ppp->tty->fasync, SIGIO);
- +#endif
- +
- + if (ppp->inp_sig && ppp->inp_sig_pid)
- + if (kill_proc (ppp->inp_sig_pid, ppp->inp_sig, 1) != 0) {
- + /* process is gone */
- + PRINTKN (2,(KERN_NOTICE
- + "ppp: process that requested notification is gone\n"));
- + ppp->inp_sig = 0;
- + ppp->inp_sig_pid = 0;
- + }
- + return 1;
- +
- + failure:
- + ppp->us_rbuff_head = saved_head;
- + clear_bit(1, &ppp->us_rbuff_lock);
- +
- + PRINTKN (1, (KERN_NOTICE "ppp_us_queue: ran out of buffer space.\n"));
- +
- + return 0;
- +}
- +
- +/*************************************************************
- + * 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_read(struct tty_struct *tty, struct file *file, unsigned char *buf, unsigned int nr)
- +{
- + struct ppp *ppp = ppp_find(tty);
- + unsigned char c;
- + int len, i;
- +
- + if (!ppp || ppp->magic != PPP_MAGIC) {
- + PRINTKN (1,(KERN_ERR "ppp_read: cannot find ppp channel\n"));
- + return -EIO;
- + }
- +
- + CHECK_PPP(-ENXIO);
- +
- + PRINTKN (4,(KERN_DEBUG "ppp_read: called %x num %u\n",
- + (unsigned int) buf,
- + nr));
- +
- + do {
- + /* try to acquire read lock */
- + if (set_bit(0, &ppp->us_rbuff_lock) == 0) {
- + /* got lock */
- + if (ppp->us_rbuff_head == ppp->us_rbuff_tail) {
- + /* no data */
- + PRINTKN (4,(KERN_DEBUG "ppp_read: no data\n"));
- + clear_bit(0, &ppp->us_rbuff_lock);
- + if (ppp->inp_sig) {
- + PRINTKN (4,(KERN_DEBUG "ppp_read: EWOULDBLOCK\n"));
- + return -EWOULDBLOCK;
- + } else goto wait;
- + }
- +
- + /* reset the time of the last read operation */
- + ppp->ddinfo.nip_rjiffies = jiffies;
- +
- + GETC (c); len = c << 8; GETC (c); len += c;
- +
- + PRINTKN (4,(KERN_DEBUG "ppp_read: len = %d\n", len));
- +
- + if (len + 2 > nr) {
- + /* frame too big; can't copy it, but do update us_rbuff_head */
- + PRINTKN (1,(KERN_DEBUG
- + "ppp: read of %u bytes too small for %d frame\n",
- + nr, len+2));
- + ppp->us_rbuff_head += len;
- + if (ppp->us_rbuff_head > ppp->us_rbuff_end)
- + ppp->us_rbuff_head += - (ppp->us_rbuff_end - ppp->us_rbuff);
- + clear_bit(0, &ppp->us_rbuff_lock);
- + wake_up_interruptible (&ppp->read_wait);
- + ppp->stats.rgiants++;
- + return -EOVERFLOW; /* ZZZ; HACK! */
- + } else {
- + /* have the space: copy the packet, faking the first two bytes */
- + put_fs_byte (PPP_ADDRESS, buf++);
- + put_fs_byte (PPP_CONTROL, buf++);
- + i = len;
- + while (i-- > 0) {
- + GETC (c);
- + put_fs_byte (c, buf++);
- + }
- + }
- +
- + clear_bit(0, &ppp->us_rbuff_lock);
- + PRINTKN (3,(KERN_DEBUG "ppp_read: passing %d bytes up\n", len + 2));
- + ppp->stats.rothers++;
- + return len + 2;
- + }
- +
- + /* need to wait */
- + wait:
- + current->timeout = 0;
- + PRINTKN (3,(KERN_DEBUG "ppp_read: sleeping\n"));
- + interruptible_sleep_on (&ppp->read_wait);
- + if (current->signal & ~current->blocked)
- + return -EINTR;
- + } while (1);
- +}
- +
- +/* stuff a character into the transmit buffer, using PPP's way of escaping
- + special characters.
- + also, update ppp->fcs to take account of new character */
- +static inline void
- +ppp_stuff_char(struct ppp *ppp, unsigned char c)
- +{
- + int curpt = ppp->xhead - ppp->xbuff;
- + if ((curpt < 0) || (curpt > 3000)) {
- + PRINTK ((KERN_DEBUG "ppp_stuff_char: %x %x %d\n",
- + (unsigned int) ppp->xbuff, (unsigned int) ppp->xhead, curpt))
- + }
- + if (in_xmap (ppp, c)) {
- + *ppp->xhead++ = PPP_ESC;
- + *ppp->xhead++ = c ^ PPP_TRANS;
- + } else
- + *ppp->xhead++ = c;
- + ppp->fcs = (ppp->fcs >> 8) ^ fcstab[(ppp->fcs ^ c) & 0xff];
- +}
- +
- +/* write a frame with NR chars from BUF to TTY
- + we have to put the FCS field on ourselves
- +*/
- +
- +static int
- +ppp_write(struct tty_struct *tty, struct file *file, unsigned char *buf, unsigned int nr)
- +{
- + struct ppp *ppp = ppp_find(tty);
- + int i;
- +
- + if (!ppp || ppp->magic != PPP_MAGIC) {
- + PRINTKN (1,(KERN_ERR "ppp_write: cannot find ppp unit\n"));
- + return -EIO;
- + }
- +
- + CHECK_PPP(-ENXIO);
- +
- + if (ppp->mtu != ppp->dev->mtu) /* Someone has been ifconfigging */
- + ppp_changedmtu (ppp, ppp->dev->mtu, ppp->mru);
- +
- + if (nr > ppp->mtu) {
- + PRINTKN (1,(KERN_WARNING
- + "ppp_write: truncating user packet from %u to mtu %d\n",
- + nr, ppp->mtu));
- + nr = ppp->mtu;
- + }
- +
- + if (ppp_debug >= 3)
- + ppp_print_buffer ("write frame", buf, nr, USER_DS);
- +
- + /* lock this PPP unit so we will be the only writer;
- + sleep if necessary */
- + while ((ppp->sending == 1) || !ppp_lock(ppp)) {
- + current->timeout = 0;
- + PRINTKN (3,(KERN_DEBUG "ppp_write: sleeping\n"));
- + interruptible_sleep_on(&ppp->write_wait);
- + if (current->signal & ~current->blocked)
- + return -EINTR;
- + }
- +
- + /* OK, locked. Stuff the given bytes into the buffer. */
- +
- + PRINTKN(4,(KERN_DEBUG "ppp_write: acquired write lock\n"));
- + ppp->xhead = ppp->xbuff;
- +
- +#ifdef OPTIMIZE_FLAG_TIME
- + if (jiffies - ppp->last_xmit > OPTIMIZE_FLAG_TIME)
- + *ppp->xhead++ = PPP_FLAG;
- + ppp->last_xmit = jiffies;
- +#else
- + *ppp->xhead++ = PPP_FLAG;
- +#endif
- +
- + ppp->fcs = PPP_FCS_INIT;
- + i = nr;
- + while (i-- > 0)
- + ppp_stuff_char(ppp,get_fs_byte(buf++));
- +
- + ppp_add_fcs(ppp); /* concatenate FCS at end */
- +
- + *ppp->xhead++ = PPP_FLAG;
- +
- + /* reset the time of the last write operation */
- + ppp->ddinfo.nip_sjiffies = jiffies;
- +
- + if (ppp_debug >= 6)
- + ppp_print_buffer ("xmit buffer", ppp->xbuff, ppp->xhead - ppp->xbuff, KERNEL_DS);
- + else {
- + PRINTKN (4,(KERN_DEBUG
- + "ppp_write: writing %d chars\n", ppp->xhead - ppp->xbuff));
- + }
- +
- + /* packet is ready-to-go */
- + ++ppp->stats.sothers;
- + ppp_kick_tty(ppp);
- +
- + return((int)nr);
- +}
- +
- +static int
- +ppp_ioctl(struct tty_struct *tty, struct file *file, unsigned int i,
- + unsigned long l)
- +{
- + struct ppp *ppp = ppp_find(tty);
- + register int temp_i = 0;
- + int error;
- +
- + if (!ppp || ppp->magic != PPP_MAGIC) {
- + PRINTK ((KERN_ERR "ppp_ioctl: can't find PPP block from tty!\n"))
- + return -EBADF;
- + }
- +
- + CHECK_PPP(-ENXIO);
- +
- + /* This must be root user */
- + if (!suser())
- + return -EPERM;
- +
- + switch (i) {
- + case PPPIOCSMRU:
- + error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
- + if (error == 0) {
- + temp_i = (int) get_fs_long (l);
- + PRINTKN (3,(KERN_INFO "ppp_ioctl: set mru to %d\n", temp_i));
- + if (ppp->mru != temp_i)
- + ppp_changedmtu (ppp, ppp->dev->mtu, temp_i);
- + }
- + break;
- +
- + case PPPIOCGFLAGS:
- + error = verify_area (VERIFY_WRITE, (void *) l, 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_fs_long ((long) temp_i, l);
- + PRINTKN (3,(KERN_DEBUG "ppp_ioctl: get flags: addr %lx flags %x\n",
- + l,
- + temp_i));
- + }
- + break;
- +
- + case PPPIOCSFLAGS:
- + error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
- + if (error == 0) {
- + temp_i = (int) get_fs_long (l);
- + ppp->flags ^= ((ppp->flags ^ temp_i) & SC_MASK);
- + PRINTKN (3,(KERN_INFO "ppp_ioctl: set flags to %x\n", temp_i));
- + }
- + break;
- +
- + case PPPIOCGASYNCMAP:
- + error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
- + if (error == 0) {
- + put_fs_long (ppp->xmit_async_map[0], l);
- + PRINTKN (3,(KERN_INFO "ppp_ioctl: get asyncmap: addr %lx asyncmap %lx\n",
- + l, ppp->xmit_async_map[0]));
- + }
- + break;
- +
- + case PPPIOCSASYNCMAP:
- + error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
- + if (error == 0) {
- + ppp->xmit_async_map[0] = get_fs_long (l);
- + bset (ppp->xmit_async_map, PPP_FLAG);
- + bset (ppp->xmit_async_map, PPP_ESC);
- + PRINTKN (3,(KERN_INFO "ppp_ioctl: set xmit asyncmap %lx\n",
- + ppp->xmit_async_map[0]));
- + }
- + break;
- +
- + case PPPIOCRASYNCMAP:
- + error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
- + if (error == 0) {
- + ppp->recv_async_map = get_fs_long (l);
- + PRINTKN (3,(KERN_INFO "ppp_ioctl: set recv asyncmap %lx\n",
- + ppp->recv_async_map));
- + }
- + break;
- +
- + case PPPIOCGUNIT:
- + error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
- + if (error == 0) {
- + put_fs_long (ppp->dev->base_addr, l);
- + PRINTKN (3,(KERN_INFO "ppp_ioctl: get unit: %ld", ppp->dev->base_addr));
- + }
- + break;
- +
- + case PPPIOCSINPSIG:
- + error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
- + if (error == 0) {
- + ppp->inp_sig = (int) get_fs_long (l);
- + ppp->inp_sig_pid = current->pid;
- + PRINTKN (3,(KERN_INFO "ppp_ioctl: set input signal %d\n", ppp->inp_sig));
- + }
- + break;
- +
- + case PPPIOCSDEBUG:
- + error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
- + if (error == 0) {
- + ppp_debug = (int) get_fs_long (l);
- + ppp_debug_netpackets = (ppp_debug & 0xff00) >> 8;
- + ppp_debug &= 0xff;
- + PRINTKN (1, (KERN_INFO "ppp_ioctl: set debug level %d, netpacket %d\n",
- + ppp_debug, ppp_debug_netpackets));
- + }
- + break;
- +
- + case PPPIOCGDEBUG:
- + error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
- + if (error == 0) {
- + put_fs_long ((long) (ppp_debug | (ppp_debug_netpackets << 8)), l);
- + PRINTKN (3,(KERN_INFO "ppp_ioctl: get debug level %d\n",
- + ppp_debug | (ppp_debug_netpackets << 8)));
- + }
- + break;
- +
- + case PPPIOCGSTAT:
- + error = verify_area (VERIFY_WRITE, (void *) l, sizeof (struct ppp_stats));
- + if (error == 0) {
- + memcpy_tofs ((void *) l, &ppp->stats, sizeof (struct ppp_stats));
- + PRINTKN (3,(KERN_INFO "ppp_ioctl: read statistics\n"));
- + }
- + break;
- +
- + case PPPIOCGTIME:
- + error = verify_area (VERIFY_WRITE, (void *) l, sizeof (struct ppp_ddinfo));
- + if (error == 0) {
- + struct ppp_ddinfo cur_ddinfo;
- + unsigned long cur_jiffies = jiffies;
- +
- + /* change absolute times to relative times. */
- + cur_ddinfo.ip_sjiffies = cur_jiffies - ppp->ddinfo.ip_sjiffies;
- + cur_ddinfo.ip_rjiffies = cur_jiffies - ppp->ddinfo.ip_rjiffies;
- + cur_ddinfo.nip_sjiffies = cur_jiffies - ppp->ddinfo.nip_sjiffies;
- + cur_ddinfo.nip_rjiffies = cur_jiffies - ppp->ddinfo.nip_rjiffies;
- +
- + memcpy_tofs ((void *) l, &cur_ddinfo, sizeof (struct ppp_ddinfo));
- + PRINTKN (3,(KERN_INFO "ppp_ioctl: read demand dial info\n"));
- + }
- + break;
- +
- + case PPPIOCGXASYNCMAP:
- + error = verify_area (VERIFY_WRITE,
- + (void *) l,
- + sizeof (ppp->xmit_async_map));
- + if (error == 0) {
- + memcpy_tofs ((void *) l,
- + ppp->xmit_async_map,
- + sizeof (ppp->xmit_async_map));
- + PRINTKN (3,(KERN_INFO "ppp_ioctl: get xasyncmap: addr %lx\n", l));
- + }
- + break;
- +
- + case PPPIOCSXASYNCMAP:
- + error = verify_area (VERIFY_READ, (void *) l,
- + sizeof (ppp->xmit_async_map));
- + if (error == 0) {
- + unsigned long temp_tbl [8];
- +
- + memcpy_fromfs (temp_tbl, (void *) l, sizeof (ppp->xmit_async_map));
- + temp_tbl[1] = 0x00000000; /* must not escape 0x20 - 0x3f */
- + temp_tbl[2] &= ~0x40000000; /* must not escape 0x5e */
- + temp_tbl[3] |= 0x60000000; /* must escape 0x7d and 0x7e */
- +
- + 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));
- + PRINTKN (3,(KERN_INFO "ppp_ioctl: set xasyncmap\n"));
- + }
- + }
- + break;
- +
- + case PPPIOCSMAXCID:
- + error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
- + if (error == 0) {
- + temp_i = (int) get_fs_long (l) + 1;
- + PRINTKN (3,(KERN_INFO "ppp_ioctl: set maxcid to %d\n", temp_i));
- + if (ppp->slcomp != NULL)
- + slhc_free (ppp->slcomp);
- +
- + ppp->slcomp = slhc_init (temp_i, temp_i);
- +
- + if (ppp->slcomp == NULL) {
- + PRINTKN (1,(KERN_ERR "ppp: no space for compression buffers!\n"));
- + ppp_release (ppp);
- + error = -ENOMEM;
- + }
- + }
- + break;
- +
- +#ifdef NEW_TTY_DRIVERS
- + /* Allow stty to read, but not set, the serial port */
- + case TCGETS:
- + case TCGETA:
- + error = n_tty_ioctl(tty, file, i, l);
- + break;
- +#endif
- +
- +/*
- + * All other ioctl() events will come here.
- + */
- +
- + default:
- + PRINTKN (1,(KERN_ERR "ppp_ioctl: invalid ioctl: %x, addr %lx\n",
- + i,
- + l));
- +#ifdef NEW_TTY_DRIVERS
- + error = -ENOIOCTLCMD;
- +#else
- + error = -EINVAL;
- +#endif
- + break;
- + }
- + return error;
- +}
- +
- +static int
- +ppp_select (struct tty_struct *tty, struct inode * inode,
- + struct file * filp, int sel_type, select_table * wait)
- +{
- + struct ppp *ppp = ppp_find (tty);
- +
- + if (!ppp || ppp->magic != PPP_MAGIC) {
- + PRINTK ((KERN_ERR "ppp_select: can't find PPP block from tty!\n"))
- + return -EBADF;
- + }
- +
- + /* If the PPP protocol is no longer active, return false */
- + CHECK_PPP (0);
- +
- + /* Process the request based upon the type desired */
- + switch (sel_type) {
- + case SEL_IN:
- + if (set_bit(0, &ppp->us_rbuff_lock) == 0) {
- + /* Test for the presence of data in the queue */
- + if (ppp->us_rbuff_head != ppp->us_rbuff_tail) {
- + clear_bit (0, &ppp->us_rbuff_lock);
- + return 1;
- + }
- + clear_bit (0, &ppp->us_rbuff_lock);
- + } /* fall through */
- +
- + case SEL_EX:
- + /* Is there a pending error condition? */
- + if (tty->packet && tty->link->ctrl_status)
- + return 1;
- +
- + /* closed? */
- + if (tty->flags & (1 << TTY_SLAVE_CLOSED))
- + return 1;
- +
- + /* If the tty is disconnected, then this is an exception too */
- + if (tty_hung_up_p(filp))
- + return 1;
- +
- + select_wait (&ppp->read_wait, wait);
- + break;
- +
- + case SEL_OUT:
- + if (ppp_lock (ppp)) {
- + if (ppp->sending == 0) {
- + ppp_unlock (ppp);
- + return 1;
- + }
- + ppp_unlock (ppp);
- + }
- + select_wait (&ppp->write_wait, wait);
- + break;
- + }
- + return 0;
- +}
- +
- +/*************************************************************
- + * 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...).
- + *************************************************************/
- +
- +int
- +ppp_xmit(struct sk_buff *skb, struct device *dev)
- +{
- + struct tty_struct *tty;
- + struct ppp *ppp;
- + unsigned char *p;
- + unsigned short proto;
- + int len;
- +
- + /* just a little sanity check. */
- + if (skb == NULL) {
- + PRINTKN(3,(KERN_WARNING "ppp_xmit: null packet!\n"));
- + return 0;
- + }
- +
- + /* Get pointers to the various components */
- + ppp = &ppp_ctrl[dev->base_addr];
- + tty = ppp->tty;
- + p = (unsigned char *) (skb + 1);
- + len = skb->len;
- + proto = PROTO_IP;
- +
- + PRINTKN(4,(KERN_DEBUG "ppp_xmit [%s]: skb %lX busy %d\n", dev->name,
- + (unsigned long int) skb, ppp->sending));
- +
- + /* avoid race conditions when the link fails */
- + if (!ppp->inuse) {
- + dev_kfree_skb(skb, FREE_WRITE);
- + dev_close (dev);
- + return 0;
- + }
- +
- + if (tty == NULL) {
- + PRINTKN(1,(KERN_ERR "ppp_xmit: %s not connected to a TTY!\n", dev->name));
- + goto done;
- + }
- +
- + if (!(dev->flags & IFF_UP)) {
- + PRINTKN(1,(KERN_WARNING
- + "ppp_xmit: packet sent on interface %s, which is down for IP\n",
- + dev->name));
- + goto done;
- + }
- +
- + /* get length from IP header as per Alan Cox bugfix for slip.c */
- + if (len < sizeof(struct iphdr)) {
- + PRINTKN(0,(KERN_ERR "ppp_xmit: given runt packet, ignoring\n"));
- + goto done;
- + }
- + len = ntohs( ((struct iphdr *)(skb->data)) -> tot_len );
- +
- + /* If doing demand dial then divert the first frame to pppd. */
- + if (ppp->flags & SC_IP_DOWN) {
- + if (ppp->flags & SC_IP_FLUSH == 0) {
- + if (ppp_us_queue (ppp, proto, p, len))
- + ppp->flags |= SC_IP_FLUSH;
- + }
- + goto done;
- + }
- +
- + /* Attempt to acquire send lock */
- + if (ppp->sending || !ppp_lock(ppp)) {
- + PRINTKN(3,(KERN_WARNING "ppp_xmit: busy\n"));
- + ppp->stats.sbusy++;
- + return 1;
- + }
- +
- + ppp->xhead = ppp->xbuff;
- +
- + /* try to compress, if VJ compression mode is on */
- + if (ppp->flags & SC_COMP_TCP) {
- + len = slhc_compress(ppp->slcomp, p, len, ppp->cbuff, &p,
- + !(ppp->flags & SC_NO_TCP_CCID));
- + if (p[0] & SL_TYPE_COMPRESSED_TCP)
- + proto = PROTO_VJCOMP;
- + else {
- + if (p[0] >= SL_TYPE_UNCOMPRESSED_TCP) {
- + proto = PROTO_VJUNCOMP;
- + p[0] = (p[0] & 0x0f) | 0x40;
- + }
- + }
- + }
- +
- + /* increment appropriate counter */
- + if (proto == PROTO_VJCOMP)
- + ++ppp->stats.scomp;
- + else
- + ++ppp->stats.suncomp;
- +
- + if (ppp_debug_netpackets) {
- + struct iphdr *iph = (struct iphdr *) (skb + 1);
- + PRINTK ((KERN_DEBUG "%s ==> proto %x len %d src %x dst %x proto %d\n",
- + dev->name, (int) proto, (int) len, (int) iph->saddr,
- + (int) iph->daddr, (int) iph->protocol))
- + }
- +
- + /* start of frame: FLAG ALL_STATIONS CONTROL <protohi> <protolo> */
- +#ifdef OPTIMIZE_FLAG_TIME
- + if (jiffies - ppp->last_xmit > OPTIMIZE_FLAG_TIME)
- + *ppp->xhead++ = PPP_FLAG;
- + ppp->last_xmit = jiffies;
- +#else
- + *ppp->xhead++ = PPP_FLAG;
- +#endif
- +
- + ppp->fcs = PPP_FCS_INIT;
- + if (!(ppp->flags & SC_COMP_AC)) {
- + ppp_stuff_char(ppp, PPP_ADDRESS);
- + ppp_stuff_char(ppp, PPP_CONTROL);
- + }
- +
- + if (!(ppp->flags & SC_COMP_PROT) || (proto & 0xff00))
- + ppp_stuff_char(ppp, proto>>8);
- + ppp_stuff_char(ppp, proto&0xff);
- +
- + /* data part */
- + while (len-- > 0)
- + ppp_stuff_char(ppp, *p++);
- +
- + /* fcs and flag */
- + ppp_add_fcs(ppp);
- + *ppp->xhead++ = PPP_FLAG;
- +
- + /* update the time for demand dial function */
- + ppp->ddinfo.ip_sjiffies = jiffies;
- +
- + /* send it! */
- + if (ppp_debug >= 6)
- + ppp_print_buffer ("xmit buffer", ppp->xbuff, ppp->xhead - ppp->xbuff, KERNEL_DS);
- + else {
- + PRINTKN (4,(KERN_DEBUG
- + "ppp_write: writing %d chars\n", ppp->xhead - ppp->xbuff));
- + }
- +
- + ppp_kick_tty(ppp);
- +
- + done:
- + dev_kfree_skb(skb, FREE_WRITE);
- + return 0;
- +}
- +
- +static unsigned short
- +ppp_type_trans (struct sk_buff *skb, struct device *dev)
- +{
- + return(htons(ETH_P_IP));
- +}
- +
- +#ifdef NET02D
- +static int
- +ppp_header(unsigned char *buff, struct device *dev, unsigned short type,
- + unsigned long daddr, unsigned long saddr, unsigned len)
- +{
- + return(0);
- +}
- +
- +static int
- +ppp_rebuild_header(void *buff, struct device *dev)
- +{
- + return(0);
- +}
- +
- +static void
- +ppp_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev)
- +{
- +}
- +
- +#else
- +
- +static int
- +ppp_header(unsigned char *buff, struct device *dev, unsigned short type,
- + void *daddr, void *saddr, unsigned len, struct sk_buff *skb)
- +{
- + return(0);
- +}
- +
- +static int
- +ppp_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
- + struct sk_buff *skb)
- +{
- + return(0);
- +}
- +#endif
- +
- +static struct enet_statistics *
- +ppp_get_stats (struct device *dev)
- +{
- + struct ppp *ppp = &ppp_ctrl[dev->base_addr];
- + static struct enet_statistics ppp_stats;
- +
- + ppp_stats.rx_packets = ppp->stats.rcomp + ppp->stats.runcomp;
- + ppp_stats.rx_errors = ppp->stats.rerrors;
- + ppp_stats.rx_dropped = ppp->stats.tossed;
- + ppp_stats.rx_fifo_errors = 0;
- + ppp_stats.rx_length_errors = ppp->stats.runts;
- + ppp_stats.rx_over_errors = ppp->stats.roverrun;
- + ppp_stats.rx_crc_errors = 0;
- + ppp_stats.rx_frame_errors = 0;
- + ppp_stats.tx_packets = ppp->stats.scomp + ppp->stats.suncomp;
- + ppp_stats.tx_errors = ppp->stats.serrors;
- + ppp_stats.tx_dropped = 0;
- + ppp_stats.tx_fifo_errors = 0;
- + ppp_stats.collisions = ppp->stats.sbusy;
- + ppp_stats.tx_carrier_errors = 0;
- + ppp_stats.tx_aborted_errors = 0;
- + ppp_stats.tx_window_errors = 0;
- + ppp_stats.tx_heartbeat_errors = 0;
- +
- + PRINTKN (3, (KERN_INFO "ppp_get_stats called"));
- + return &ppp_stats;
- +}
- +
- +/*************************************************************
- + * UTILITIES
- + * Miscellany called by various functions above.
- + *************************************************************/
- +
- +#ifndef NEW_TTY_DRIVERS
- +/* find a PPP channel given a TTY */
- +struct ppp *
- +ppp_find(struct tty_struct *tty)
- +{
- + int i;
- + for (i = 0; i < PPP_NRUNIT; i++)
- + if (ppp_ctrl[i].inuse && (ppp_ctrl[i].tty == tty)) return &ppp_ctrl[i];
- +
- + return NULL;
- +}
- +#endif
- +
- +/* allocate a PPP channel */
- +static struct ppp *
- +ppp_alloc(void)
- +{
- + int i;
- + for (i = 0; i < PPP_NRUNIT; i++)
- + if (!set_bit(0, &ppp_ctrl[i].inuse)) return &ppp_ctrl[i];
- +
- + return NULL;
- +}
- +
- +/* marks a PPP interface 'busy'. user processes will wait, if
- + they try to write, and the network code will refrain from sending
- + return nonzero if succeeded in acquiring lock
- +*/
- +
- +static int
- +ppp_lock(struct ppp *ppp)
- +{
- + int flags, locked;
- + save_flags(flags);
- + cli();
- + locked = ppp->sending;
- + ppp->sending = 1;
- + if (ppp->dev->flags & IFF_UP)
- + ppp->dev->tbusy = 1;
- + restore_flags(flags);
- + return locked == 0;
- +}
- +
- +static void
- +ppp_unlock(struct ppp *ppp)
- +{
- + int flags;
- + save_flags(flags);
- + cli();
- + ppp->sending = 0;
- + if (ppp->dev->flags & IFF_UP)
- + ppp->dev->tbusy = 0;
- + restore_flags(flags);
- +}
- +
- +/* FCS support functions */
- +
- +static void
- +ppp_add_fcs(struct ppp *ppp)
- +{
- + unsigned short fcs = ppp->fcs;
- +
- + fcs ^= 0xffff;
- + ppp_stuff_char(ppp, fcs & 0x00ff);
- + ppp_stuff_char(ppp, (fcs & 0xff00) >> 8);
- + ASSERT (ppp->fcs == PPP_FCS_GOOD);
- + PRINTKN (4,(KERN_DEBUG "ppp_add_fcs: fcs is %lx\n",
- + (long) (unsigned long) fcs));
- +}
- +
- +static int
- +ppp_check_fcs(struct ppp *ppp)
- +{
- + unsigned short fcs = PPP_FCS_INIT, msgfcs;
- + unsigned char *c = ppp->rbuff;
- + int i;
- +
- + for (i = 0; i < ppp->rcount - 2; i++, c++)
- + fcs = (fcs >> 8) ^ fcstab[(fcs ^ *c) & 0xff];
- +
- + fcs ^= 0xffff;
- + msgfcs = (c[1] << 8) + c[0];
- + PRINTKN (4,(KERN_INFO "ppp_check_fcs: got %lx want %lx\n",
- + (unsigned long) msgfcs, (unsigned long) fcs));
- + return fcs == msgfcs;
- +}
- +
- +static char hex[] = "0123456789ABCDEF";
- +
- +static inline void ppp_print_hex (register char *out, char *in, int count)
- +{
- + register unsigned char next_ch;
- +
- + while (count-- > 0) {
- + next_ch = (unsigned char) get_fs_byte (in);
- +
- + *out++ = hex[(next_ch >> 4) & 0x0F];
- + *out++ = hex[next_ch & 0x0F];
- + ++out;
- + ++in;
- + }
- +}
- +
- +static inline void ppp_print_char (register char *out, char *in, int count)
- +{
- + register unsigned char next_ch;
- +
- + while (count-- > 0) {
- + next_ch = (unsigned char) get_fs_byte (in);
- +
- + if (next_ch < 0x20 || next_ch > 0x7e)
- + *out++ = '.';
- + else {
- + *out++ = next_ch;
- + if (next_ch == '%') /* printk/syslogd has a bug !! */
- + *out++ = '%';
- + }
- + ++in;
- + }
- + *out = '\0';
- +}
- +
- +static void ppp_print_buffer(const char *name, char *buf, int count, int seg)
- +{
- + char line [44];
- + int old_fs = get_fs();
- +
- + set_fs (seg);
- +
- + if (name != NULL)
- + PRINTK ((KERN_DEBUG "ppp: %s, count = %d\n", name, count));
- +
- + while (count > 8) {
- + memset (line, ' ', sizeof (line));
- + 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, ' ', sizeof (line));
- + ppp_print_hex (line, buf, count);
- + ppp_print_char (&line[8 * 3], buf, count);
- + PRINTK ((KERN_DEBUG "%s\n", line));
- + }
- +
- + set_fs (old_fs);
- +}
- +
- +#ifdef MODULE
- +char kernel_version[] = UTS_RELEASE;
- +
- +static struct device dev_ppp[PPP_NRUNIT] = {
- + {
- + "ppp0", /* ppp */
- + 0, 0, 0, 0, /* memory */
- + 0, 0, /* base, irq */
- + 0, 0, 0, NULL, ppp_init,
- + }
- + , { "ppp1" , 0, 0, 0, 0, 1, 0, 0, 0, 0, NULL, ppp_init }
- + , { "ppp2" , 0, 0, 0, 0, 2, 0, 0, 0, 0, NULL, ppp_init }
- + , { "ppp3" , 0, 0, 0, 0, 3, 0, 0, 0, 0, NULL, ppp_init }
- +
- +#ifdef PPP_PPP_LOTS
- + , { "ppp4" , 0, 0, 0, 0, 4, 0, 0, 0, 0, NULL, ppp_init }
- + , { "ppp5" , 0, 0, 0, 0, 5, 0, 0, 0, 0, NULL, ppp_init }
- + , { "ppp6" , 0, 0, 0, 0, 6, 0, 0, 0, 0, NULL, ppp_init }
- + , { "ppp7" , 0, 0, 0, 0, 7, 0, 0, 0, 0, NULL, ppp_init }
- + , { "ppp8" , 0, 0, 0, 0, 8, 0, 0, 0, 0, NULL, ppp_init }
- + , { "ppp9" , 0, 0, 0, 0, 9, 0, 0, 0, 0, NULL, ppp_init }
- + , { "ppp10" , 0, 0, 0, 0, 10, 0, 0, 0, 0, NULL, ppp_init }
- + , { "ppp11" , 0, 0, 0, 0, 11, 0, 0, 0, 0, NULL, ppp_init }
- + , { "ppp12" , 0, 0, 0, 0, 12, 0, 0, 0, 0, NULL, ppp_init }
- + , { "ppp13" , 0, 0, 0, 0, 13, 0, 0, 0, 0, NULL, ppp_init }
- + , { "ppp14" , 0, 0, 0, 0, 14, 0, 0, 0, 0, NULL, ppp_init }
- + , { "ppp15" , 0, 0, 0, 0, 15, 0, 0, 0, 0, NULL, ppp_init }
- +#endif
- +};
- +
- +int
- +init_module(void)
- +{
- + int err;
- + int i;
- +
- + for (i = 0; i < PPP_NRUNIT; i++) {
- + if ((err = register_netdev(&dev_ppp[i]))) {
- + if (err == -EEXIST) {
- + printk("PPP: devices already present. Module not loaded.\n");
- + }
- + return err;
- + }
- + }
- + return 0;
- +}
- +
- +void
- +cleanup_module(void)
- +{
- + int i;
- +
- + if (MOD_IN_USE) {
- + printk("PPP: device busy, remove delayed\n");
- + return;
- + }
- + for (i = 0; i < PPP_NRUNIT; i++) {
- + unregister_netdev(&dev_ppp[i]);
- + }
- + if ((i = tty_register_ldisc(N_PPP, NULL))) {
- + printk("PPP: can't unregister line discipline (err = %d)\n", i);
- + }
- +}
- +
- +#endif
- diff -r -u -N linux.orig/arch/arm/drivers/net/slhc.c linux.arm/arch/arm/drivers/net/slhc.c
- --- linux.orig/arch/arm/drivers/net/slhc.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/slhc.c Fri Oct 27 23:15:05 1995
- @@ -0,0 +1,747 @@
- +/*
- + * Routines to compress and uncompress tcp packets (for transmission
- + * over low speed serial lines).
- + *
- + * Copyright (c) 1989 Regents of the University of California.
- + * All rights reserved.
- + *
- + * Redistribution and use in source and binary forms are permitted
- + * provided that the above copyright notice and this paragraph are
- + * duplicated in all such forms and that any documentation,
- + * advertising materials, and other materials related to such
- + * distribution and use acknowledge that the software was developed
- + * by the University of California, Berkeley. The name of the
- + * University may not be used to endorse or promote products derived
- + * from this software without specific prior written permission.
- + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- + *
- + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
- + * - Initial distribution.
- + *
- + *
- + * modified for KA9Q Internet Software Package by
- + * Katie Stevens (dkstevens@ucdavis.edu)
- + * University of California, Davis
- + * Computing Services
- + * - 01-31-90 initial adaptation (from 1.19)
- + * PPP.05 02-15-90 [ks]
- + * PPP.08 05-02-90 [ks] use PPP protocol field to signal compression
- + * PPP.15 09-90 [ks] improve mbuf handling
- + * PPP.16 11-02 [karn] substantially rewritten to use NOS facilities
- + *
- + * - Feb 1991 Bill_Simpson@um.cc.umich.edu
- + * variable number of conversation slots
- + * allow zero or one slots
- + * separate routines
- + * status display
- + * - Jul 1994 Dmitry Gorodchanin
- + * Fixes for memory leaks.
- + * - Oct 1994 Dmitry Gorodchanin
- + * Modularization.
- + * - Jan 1995 Bjorn Ekwall
- + * Use ip_fast_csum from ip.h
- + *
- + *
- + * This module is a difficult issue. It's clearly inet code but it's also clearly
- + * driver code belonging close to PPP and SLIP
- + */
- +
- +#include <linux/config.h>
- +#ifdef CONFIG_INET
- +/* Entire module is for IP only */
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/version.h>
- +#endif
- +
- +#include <linux/types.h>
- +#include <linux/sched.h>
- +#include <linux/mm.h>
- +#include <linux/string.h>
- +#include <linux/socket.h>
- +#include <linux/sockios.h>
- +#include <linux/termios.h>
- +#include <linux/in.h>
- +#include <linux/fcntl.h>
- +#include <linux/inet.h>
- +#include <linux/netdevice.h>
- +#include "ip.h"
- +#include "protocol.h"
- +#include "icmp.h"
- +#include "tcp.h"
- +#include <linux/skbuff.h>
- +#include "sock.h"
- +#include <linux/errno.h>
- +#include <linux/timer.h>
- +#include <asm/system.h>
- +#include <asm/segment.h>
- +#include <linux/mm.h>
- +#include "slhc.h"
- +
- +int last_retran;
- +
- +static unsigned char *encode(unsigned char *cp, unsigned short n);
- +static long decode(unsigned char **cpp);
- +static unsigned char * put16(unsigned char *cp, unsigned short x);
- +static unsigned short pull16(unsigned char **cpp);
- +
- +/* Initialize compression data structure
- + * slots must be in range 0 to 255 (zero meaning no compression)
- + */
- +struct slcompress *
- +slhc_init(int rslots, int tslots)
- +{
- + register short i;
- + register struct cstate *ts;
- + struct slcompress *comp;
- +
- + comp = (struct slcompress *)kmalloc(sizeof(struct slcompress),
- + GFP_KERNEL);
- + if (! comp)
- + return NULL;
- +
- + memset(comp, 0, sizeof(struct slcompress));
- +
- + if ( rslots > 0 && rslots < 256 ) {
- + comp->rstate =
- + (struct cstate *)kmalloc(rslots * sizeof(struct cstate),
- + GFP_KERNEL);
- + if (! comp->rstate)
- + {
- + kfree((unsigned char *)comp);
- + return NULL;
- + }
- + memset(comp->rstate, 0, rslots * sizeof(struct cstate));
- + comp->rslot_limit = rslots - 1;
- + }
- +
- + if ( tslots > 0 && tslots < 256 ) {
- + comp->tstate =
- + (struct cstate *)kmalloc(tslots * sizeof(struct cstate),
- + GFP_KERNEL);
- + if (! comp->tstate)
- + {
- + kfree((unsigned char *)comp->rstate);
- + kfree((unsigned char *)comp);
- + return NULL;
- + }
- + memset(comp->tstate, 0, rslots * sizeof(struct cstate));
- + comp->tslot_limit = tslots - 1;
- + }
- +
- + comp->xmit_oldest = 0;
- + comp->xmit_current = 255;
- + comp->recv_current = 255;
- + /*
- + * don't accept any packets with implicit index until we get
- + * one with an explicit index. Otherwise the uncompress code
- + * will try to use connection 255, which is almost certainly
- + * out of range
- + */
- + comp->flags |= SLF_TOSS;
- +
- + if ( tslots > 0 ) {
- + ts = comp->tstate;
- + for(i = comp->tslot_limit; i > 0; --i){
- + ts[i].cs_this = i;
- + ts[i].next = &(ts[i - 1]);
- + }
- + ts[0].next = &(ts[comp->tslot_limit]);
- + ts[0].cs_this = 0;
- + }
- +#ifdef MODULE
- + MOD_INC_USE_COUNT;
- +#endif
- + return comp;
- +}
- +
- +
- +/* Free a compression data structure */
- +void
- +slhc_free(struct slcompress *comp)
- +{
- + if ( comp == NULLSLCOMPR )
- + return;
- +
- + if ( comp->rstate != NULLSLSTATE )
- + kfree( comp->rstate );
- +
- + if ( comp->tstate != NULLSLSTATE )
- + kfree( comp->tstate );
- +
- +#ifdef MODULE
- + MOD_DEC_USE_COUNT;
- +#endif
- + kfree( comp );
- +}
- +
- +
- +/* Put a short in host order into a char array in network order */
- +static inline unsigned char *
- +put16(unsigned char *cp, unsigned short x)
- +{
- + *cp++ = x >> 8;
- + *cp++ = x;
- +
- + return cp;
- +}
- +
- +
- +/* Encode a number */
- +unsigned char *
- +encode(unsigned char *cp, unsigned short n)
- +{
- + if(n >= 256 || n == 0){
- + *cp++ = 0;
- + cp = put16(cp,n);
- + } else {
- + *cp++ = n;
- + }
- + return cp;
- +}
- +
- +/* Pull a 16-bit integer in host order from buffer in network byte order */
- +static unsigned short
- +pull16(unsigned char **cpp)
- +{
- + short rval;
- +
- + rval = *(*cpp)++;
- + rval <<= 8;
- + rval |= *(*cpp)++;
- + return rval;
- +}
- +
- +/* Decode a number */
- +long
- +decode(unsigned char **cpp)
- +{
- + register int x;
- +
- + x = *(*cpp)++;
- + if(x == 0){
- + return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */
- + } else {
- + return x & 0xff; /* -1 if PULLCHAR returned error */
- + }
- +}
- +
- +/*
- + * icp and isize are the original packet.
- + * ocp is a place to put a copy if necessary.
- + * cpp is initially a pointer to icp. If the copy is used,
- + * change it to ocp.
- + */
- +
- +int
- +slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
- + unsigned char *ocp, unsigned char **cpp, int compress_cid)
- +{
- + register struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
- + register struct cstate *lcs = ocs;
- + register struct cstate *cs = lcs->next;
- + register unsigned long deltaS, deltaA;
- + register short changes = 0;
- + int hlen;
- + unsigned char new_seq[16];
- + register unsigned char *cp = new_seq;
- + struct iphdr *ip;
- + struct tcphdr *th, *oth;
- +
- + ip = (struct iphdr *) icp;
- +
- + /* Bail if this packet isn't TCP, or is an IP fragment */
- + if(ip->protocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x1fff) ||
- + (ip->frag_off & 32)){
- + /* Send as regular IP */
- + if(ip->protocol != IPPROTO_TCP)
- + comp->sls_o_nontcp++;
- + else
- + comp->sls_o_tcp++;
- + return isize;
- + }
- + /* Extract TCP header */
- +
- + th = (struct tcphdr *)(((unsigned char *)ip) + ip->ihl*4);
- + hlen = ip->ihl*4 + th->doff*4;
- +
- + /* Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or
- + * some other control bit is set).
- + */
- + if(th->syn || th->fin || th->rst ||
- + ! (th->ack)){
- + /* TCP connection stuff; send as regular IP */
- + comp->sls_o_tcp++;
- + return isize;
- + }
- + /*
- + * Packet is compressible -- we're going to send either a
- + * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way,
- + * we need to locate (or create) the connection state.
- + *
- + * States are kept in a circularly linked list with
- + * xmit_oldest pointing to the end of the list. The
- + * list is kept in lru order by moving a state to the
- + * head of the list whenever it is referenced. Since
- + * the list is short and, empirically, the connection
- + * we want is almost always near the front, we locate
- + * states via linear search. If we don't find a state
- + * for the datagram, the oldest state is (re-)used.
- + */
- + for ( ; ; ) {
- + if( ip->saddr == cs->cs_ip.saddr
- + && ip->daddr == cs->cs_ip.daddr
- + && th->source == cs->cs_tcp.source
- + && th->dest == cs->cs_tcp.dest)
- + goto found;
- +
- + /* if current equal oldest, at end of list */
- + if ( cs == ocs )
- + break;
- + lcs = cs;
- + cs = cs->next;
- + comp->sls_o_searches++;
- + };
- + /*
- + * Didn't find it -- re-use oldest cstate. Send an
- + * uncompressed packet that tells the other side what
- + * connection number we're using for this conversation.
- + *
- + * Note that since the state list is circular, the oldest
- + * state points to the newest and we only need to set
- + * xmit_oldest to update the lru linkage.
- + */
- + comp->sls_o_misses++;
- + comp->xmit_oldest = lcs->cs_this;
- + goto uncompressed;
- +
- +found:
- + /*
- + * Found it -- move to the front on the connection list.
- + */
- + if(lcs == ocs) {
- + /* found at most recently used */
- + } else if (cs == ocs) {
- + /* found at least recently used */
- + comp->xmit_oldest = lcs->cs_this;
- + } else {
- + /* more than 2 elements */
- + lcs->next = cs->next;
- + cs->next = ocs->next;
- + ocs->next = cs;
- + }
- +
- + /*
- + * Make sure that only what we expect to change changed.
- + * Check the following:
- + * IP protocol version, header length & type of service.
- + * The "Don't fragment" bit.
- + * The time-to-live field.
- + * The TCP header length.
- + * IP options, if any.
- + * TCP options, if any.
- + * If any of these things are different between the previous &
- + * current datagram, we send the current datagram `uncompressed'.
- + */
- + oth = &cs->cs_tcp;
- +
- + if(last_retran
- + || ip->version != cs->cs_ip.version || ip->ihl != cs->cs_ip.ihl
- + || ip->tos != cs->cs_ip.tos
- + || (ip->frag_off & 64) != (cs->cs_ip.frag_off & 64)
- + || ip->ttl != cs->cs_ip.ttl
- + || th->doff != cs->cs_tcp.doff
- + || (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)
- + || (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4 != 0))){
- + goto uncompressed;
- + }
- +
- + /*
- + * Figure out which of the changing fields changed. The
- + * receiver expects changes in the order: urgent, window,
- + * ack, seq (the order minimizes the number of temporaries
- + * needed in this section of code).
- + */
- + if(th->urg){
- + deltaS = ntohs(th->urg_ptr);
- + cp = encode(cp,deltaS);
- + changes |= NEW_U;
- + } else if(th->urg_ptr != oth->urg_ptr){
- + /* argh! URG not set but urp changed -- a sensible
- + * implementation should never do this but RFC793
- + * doesn't prohibit the change so we have to deal
- + * with it. */
- + goto uncompressed;
- + }
- + if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){
- + cp = encode(cp,deltaS);
- + changes |= NEW_W;
- + }
- + if((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L){
- + if(deltaA > 0x0000ffff)
- + goto uncompressed;
- + cp = encode(cp,deltaA);
- + changes |= NEW_A;
- + }
- + if((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L){
- + if(deltaS > 0x0000ffff)
- + goto uncompressed;
- + cp = encode(cp,deltaS);
- + changes |= NEW_S;
- + }
- +
- + switch(changes){
- + case 0: /* Nothing changed. If this packet contains data and the
- + * last one didn't, this is probably a data packet following
- + * an ack (normal on an interactive connection) and we send
- + * it compressed. Otherwise it's probably a retransmit,
- + * retransmitted ack or window probe. Send it uncompressed
- + * in case the other side missed the compressed version.
- + */
- + if(ip->tot_len != cs->cs_ip.tot_len &&
- + ntohs(cs->cs_ip.tot_len) == hlen)
- + break;
- + goto uncompressed;
- + break;
- + case SPECIAL_I:
- + case SPECIAL_D:
- + /* actual changes match one of our special case encodings --
- + * send packet uncompressed.
- + */
- + goto uncompressed;
- + case NEW_S|NEW_A:
- + if(deltaS == deltaA &&
- + deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
- + /* special case for echoed terminal traffic */
- + changes = SPECIAL_I;
- + cp = new_seq;
- + }
- + break;
- + case NEW_S:
- + if(deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
- + /* special case for data xfer */
- + changes = SPECIAL_D;
- + cp = new_seq;
- + }
- + break;
- + }
- + deltaS = ntohs(ip->id) - ntohs(cs->cs_ip.id);
- + if(deltaS != 1){
- + cp = encode(cp,deltaS);
- + changes |= NEW_I;
- + }
- + if(th->psh)
- + changes |= TCP_PUSH_BIT;
- + /* Grab the cksum before we overwrite it below. Then update our
- + * state with this packet's header.
- + */
- + deltaA = ntohs(th->check);
- + memcpy(&cs->cs_ip,ip,20);
- + memcpy(&cs->cs_tcp,th,20);
- + /* We want to use the original packet as our compressed packet.
- + * (cp - new_seq) is the number of bytes we need for compressed
- + * sequence numbers. In addition we need one byte for the change
- + * mask, one for the connection id and two for the tcp checksum.
- + * So, (cp - new_seq) + 4 bytes of header are needed.
- + */
- + deltaS = cp - new_seq;
- + if(compress_cid == 0 || comp->xmit_current != cs->cs_this){
- + cp = ocp;
- + *cpp = ocp;
- + *cp++ = changes | NEW_C;
- + *cp++ = cs->cs_this;
- + comp->xmit_current = cs->cs_this;
- + } else {
- + cp = ocp;
- + *cpp = ocp;
- + *cp++ = changes;
- + }
- + cp = put16(cp,(short)deltaA); /* Write TCP checksum */
- +/* deltaS is now the size of the change section of the compressed header */
- + memcpy(cp,new_seq,deltaS); /* Write list of deltas */
- + memcpy(cp+deltaS,icp+hlen,isize-hlen);
- + comp->sls_o_compressed++;
- + ocp[0] |= SL_TYPE_COMPRESSED_TCP;
- + return isize - hlen + deltaS + (cp - ocp);
- +
- + /* Update connection state cs & send uncompressed packet (i.e.,
- + * a regular ip/tcp packet but with the 'conversation id' we hope
- + * to use on future compressed packets in the protocol field).
- + */
- +uncompressed:
- + memcpy(&cs->cs_ip,ip,20);
- + memcpy(&cs->cs_tcp,th,20);
- + if (ip->ihl > 5)
- + memcpy(cs->cs_ipopt, ip+1, ((ip->ihl) - 5) * 4);
- + if (th->doff > 5)
- + memcpy(cs->cs_tcpopt, th+1, ((th->doff) - 5) * 4);
- + comp->xmit_current = cs->cs_this;
- + comp->sls_o_uncompressed++;
- + memcpy(ocp, icp, isize);
- + *cpp = ocp;
- + ocp[9] = cs->cs_this;
- + ocp[0] |= SL_TYPE_UNCOMPRESSED_TCP;
- + return isize;
- +}
- +
- +
- +int
- +slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
- +{
- + register int changes;
- + long x;
- + register struct tcphdr *thp;
- + register struct iphdr *ip;
- + register struct cstate *cs;
- + int len, hdrlen;
- + unsigned char *cp = icp;
- +
- + /* We've got a compressed packet; read the change byte */
- + comp->sls_i_compressed++;
- + if(isize < 3){
- + comp->sls_i_error++;
- + return 0;
- + }
- + changes = *cp++;
- + if(changes & NEW_C){
- + /* Make sure the state index is in range, then grab the state.
- + * If we have a good state index, clear the 'discard' flag.
- + */
- + x = *cp++; /* Read conn index */
- + if(x < 0 || x > comp->rslot_limit)
- + goto bad;
- +
- + comp->flags &=~ SLF_TOSS;
- + comp->recv_current = x;
- + } else {
- + /* this packet has an implicit state index. If we've
- + * had a line error since the last time we got an
- + * explicit state index, we have to toss the packet. */
- + if(comp->flags & SLF_TOSS){
- + comp->sls_i_tossed++;
- + return 0;
- + }
- + }
- + cs = &comp->rstate[comp->recv_current];
- + thp = &cs->cs_tcp;
- + ip = &cs->cs_ip;
- +
- + if((x = pull16(&cp)) == -1) { /* Read the TCP checksum */
- + goto bad;
- + }
- + thp->check = htons(x);
- +
- + thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
- +/*
- + * we can use the same number for the length of the saved header and
- + * the current one, because the packet wouldn't have been sent
- + * as compressed unless the options were the same as the previous one
- + */
- +
- + hdrlen = ip->ihl * 4 + thp->doff * 4;
- +
- + switch(changes & SPECIALS_MASK){
- + case SPECIAL_I: /* Echoed terminal traffic */
- + {
- + register short i;
- + i = ntohs(ip->tot_len) - hdrlen;
- + thp->ack_seq = htonl( ntohl(thp->ack_seq) + i);
- + thp->seq = htonl( ntohl(thp->seq) + i);
- + }
- + break;
- +
- + case SPECIAL_D: /* Unidirectional data */
- + thp->seq = htonl( ntohl(thp->seq) +
- + ntohs(ip->tot_len) - hdrlen);
- + break;
- +
- + default:
- + if(changes & NEW_U){
- + thp->urg = 1;
- + if((x = decode(&cp)) == -1) {
- + goto bad;
- + }
- + thp->urg_ptr = htons(x);
- + } else
- + thp->urg = 0;
- + if(changes & NEW_W){
- + if((x = decode(&cp)) == -1) {
- + goto bad;
- + }
- + thp->window = htons( ntohs(thp->window) + x);
- + }
- + if(changes & NEW_A){
- + if((x = decode(&cp)) == -1) {
- + goto bad;
- + }
- + thp->ack_seq = htonl( ntohl(thp->ack_seq) + x);
- + }
- + if(changes & NEW_S){
- + if((x = decode(&cp)) == -1) {
- + goto bad;
- + }
- + thp->seq = htonl( ntohl(thp->seq) + x);
- + }
- + break;
- + }
- + if(changes & NEW_I){
- + if((x = decode(&cp)) == -1) {
- + goto bad;
- + }
- + ip->id = htons (ntohs (ip->id) + x);
- + } else
- + ip->id = htons (ntohs (ip->id) + 1);
- +
- + /*
- + * At this point, cp points to the first byte of data in the
- + * packet. Put the reconstructed TCP and IP headers back on the
- + * packet. Recalculate IP checksum (but not TCP checksum).
- + */
- +
- + len = isize - (cp - icp);
- + if (len < 0)
- + goto bad;
- + len += hdrlen;
- + ip->tot_len = htons(len);
- + ip->check = 0;
- +
- + memmove(icp + hdrlen, cp, len - hdrlen);
- +
- + cp = icp;
- + memcpy(cp, ip, 20);
- + cp += 20;
- +
- + if (ip->ihl > 5) {
- + memcpy(cp, cs->cs_ipopt, ((ip->ihl) - 5) * 4);
- + cp += ((ip->ihl) - 5) * 4;
- + }
- +
- + ((struct iphdr *)icp)->check = ip_fast_csum(icp, ((struct iphdr*)icp)->ihl);
- +
- + memcpy(cp, thp, 20);
- + cp += 20;
- +
- + if (thp->doff > 5) {
- + memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4);
- + cp += ((thp->doff) - 5) * 4;
- + }
- +
- + return len;
- +bad:
- + comp->sls_i_error++;
- + return slhc_toss( comp );
- +}
- +
- +
- +int
- +slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
- +{
- + register struct cstate *cs;
- + short ip_len;
- + struct iphdr *ip;
- + struct tcphdr *thp;
- +
- + unsigned char index;
- +
- + if(isize < 20) {
- + /* The packet is shorter than a legal IP header */
- + comp->sls_i_runt++;
- + return slhc_toss( comp );
- + }
- + /* Sneak a peek at the IP header's IHL field to find its length */
- + ip_len = (icp[0] & 0xf) << 2;
- + if(ip_len < 20){
- + /* The IP header length field is too small */
- + comp->sls_i_runt++;
- + return slhc_toss( comp );
- + }
- + index = icp[9];
- + icp[9] = IPPROTO_TCP;
- + ip = (struct iphdr *) icp;
- +
- + if (ip_fast_csum(icp, ip->ihl)) {
- + /* Bad IP header checksum; discard */
- + comp->sls_i_badcheck++;
- + return slhc_toss( comp );
- + }
- + thp = (struct tcphdr *)(((unsigned char *)ip) + ip->ihl*4);
- + if(index > comp->rslot_limit) {
- + comp->sls_i_error++;
- + return slhc_toss(comp);
- + }
- +
- + /* Update local state */
- + cs = &comp->rstate[comp->recv_current = index];
- + comp->flags &=~ SLF_TOSS;
- + memcpy(&cs->cs_ip,ip,20);
- + memcpy(&cs->cs_tcp,thp,20);
- + if (ip->ihl > 5)
- + memcpy(cs->cs_ipopt, ip+1, ((ip->ihl) - 5) * 4);
- + if (thp->doff > 5)
- + memcpy(cs->cs_tcpopt, thp+1, ((thp->doff) - 5) * 4);
- + cs->cs_hsize = ip->ihl*2 + thp->doff*2;
- + /* Put headers back on packet
- + * Neither header checksum is recalculated
- + */
- + comp->sls_i_uncompressed++;
- + return isize;
- +}
- +
- +
- +int
- +slhc_toss(struct slcompress *comp)
- +{
- + if ( comp == NULLSLCOMPR )
- + return 0;
- +
- + comp->flags |= SLF_TOSS;
- + return 0;
- +}
- +
- +
- +void slhc_i_status(struct slcompress *comp)
- +{
- + if (comp != NULLSLCOMPR) {
- + printk("\t%ld Cmp, %ld Uncmp, %ld Bad, %ld Tossed\n",
- + comp->sls_i_compressed,
- + comp->sls_i_uncompressed,
- + comp->sls_i_error,
- + comp->sls_i_tossed);
- + }
- +}
- +
- +
- +void slhc_o_status(struct slcompress *comp)
- +{
- + if (comp != NULLSLCOMPR) {
- + printk("\t%ld Cmp, %ld Uncmp, %ld AsIs, %ld NotTCP\n",
- + comp->sls_o_compressed,
- + comp->sls_o_uncompressed,
- + comp->sls_o_tcp,
- + comp->sls_o_nontcp);
- + printk("\t%10ld Searches, %10ld Misses\n",
- + comp->sls_o_searches,
- + comp->sls_o_misses);
- + }
- +}
- +
- +#ifdef MODULE
- +char kernel_version[] = UTS_RELEASE;
- +
- +int init_module(void)
- +{
- + printk("CSLIP: code copyright 1989 Regents of the University of California\n");
- + return 0;
- +}
- +
- +void cleanup_module(void)
- +{
- + if (MOD_IN_USE) {
- + printk("CSLIP: module in use, remove delayed");
- + }
- + return;
- +}
- +#endif /* MODULE */
- +#endif /* CONFIG_INET */
- diff -r -u -N linux.orig/arch/arm/drivers/net/slhc.h linux.arm/arch/arm/drivers/net/slhc.h
- --- linux.orig/arch/arm/drivers/net/slhc.h Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/slhc.h Fri Oct 27 23:15:03 1995
- @@ -0,0 +1,187 @@
- +#ifndef _SLHC_H
- +#define _SLHC_H
- +/*
- + * Definitions for tcp compression routines.
- + *
- + * $Header: slcompress.h,v 1.10 89/12/31 08:53:02 van Exp $
- + *
- + * Copyright (c) 1989 Regents of the University of California.
- + * All rights reserved.
- + *
- + * Redistribution and use in source and binary forms are permitted
- + * provided that the above copyright notice and this paragraph are
- + * duplicated in all such forms and that any documentation,
- + * advertising materials, and other materials related to such
- + * distribution and use acknowledge that the software was developed
- + * by the University of California, Berkeley. The name of the
- + * University may not be used to endorse or promote products derived
- + * from this software without specific prior written permission.
- + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- + *
- + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
- + * - Initial distribution.
- + *
- + *
- + * modified for KA9Q Internet Software Package by
- + * Katie Stevens (dkstevens@ucdavis.edu)
- + * University of California, Davis
- + * Computing Services
- + * - 01-31-90 initial adaptation
- + *
- + * - Feb 1991 Bill_Simpson@um.cc.umich.edu
- + * variable number of conversation slots
- + * allow zero or one slots
- + * separate routines
- + * status display
- + */
- +
- +/*
- + * Compressed packet format:
- + *
- + * The first octet contains the packet type (top 3 bits), TCP
- + * 'push' bit, and flags that indicate which of the 4 TCP sequence
- + * numbers have changed (bottom 5 bits). The next octet is a
- + * conversation number that associates a saved IP/TCP header with
- + * the compressed packet. The next two octets are the TCP checksum
- + * from the original datagram. The next 0 to 15 octets are
- + * sequence number changes, one change per bit set in the header
- + * (there may be no changes and there are two special cases where
- + * the receiver implicitly knows what changed -- see below).
- + *
- + * There are 5 numbers which can change (they are always inserted
- + * in the following order): TCP urgent pointer, window,
- + * acknowledgment, sequence number and IP ID. (The urgent pointer
- + * is different from the others in that its value is sent, not the
- + * change in value.) Since typical use of SLIP links is biased
- + * toward small packets (see comments on MTU/MSS below), changes
- + * use a variable length coding with one octet for numbers in the
- + * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
- + * range 256 - 65535 or 0. (If the change in sequence number or
- + * ack is more than 65535, an uncompressed packet is sent.)
- + */
- +
- +/*
- + * Packet types (must not conflict with IP protocol version)
- + *
- + * The top nibble of the first octet is the packet type. There are
- + * three possible types: IP (not proto TCP or tcp with one of the
- + * control flags set); uncompressed TCP (a normal IP/TCP packet but
- + * with the 8-bit protocol field replaced by an 8-bit connection id --
- + * this type of packet syncs the sender & receiver); and compressed
- + * TCP (described above).
- + *
- + * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
- + * is logically part of the 4-bit "changes" field that follows. Top
- + * three bits are actual packet type. For backward compatibility
- + * and in the interest of conserving bits, numbers are chosen so the
- + * IP protocol version number (4) which normally appears in this nibble
- + * means "IP packet".
- + */
- +
- +/* SLIP compression masks for len/vers byte */
- +#define SL_TYPE_IP 0x40
- +#define SL_TYPE_UNCOMPRESSED_TCP 0x70
- +#define SL_TYPE_COMPRESSED_TCP 0x80
- +#define SL_TYPE_ERROR 0x00
- +
- +/* Bits in first octet of compressed packet */
- +#define NEW_C 0x40 /* flag bits for what changed in a packet */
- +#define NEW_I 0x20
- +#define NEW_S 0x08
- +#define NEW_A 0x04
- +#define NEW_W 0x02
- +#define NEW_U 0x01
- +
- +/* reserved, special-case values of above */
- +#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
- +#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
- +#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
- +
- +#define TCP_PUSH_BIT 0x10
- +
- +/*
- + * data type and sizes conversion assumptions:
- + *
- + * VJ code KA9Q style generic
- + * u_char byte_t unsigned char 8 bits
- + * u_short int16 unsigned short 16 bits
- + * u_int int16 unsigned short 16 bits
- + * u_long unsigned long unsigned long 32 bits
- + * int int32 long 32 bits
- + */
- +
- +typedef unsigned char byte_t;
- +typedef unsigned long int32;
- +
- +/*
- + * "state" data for each active tcp conversation on the wire. This is
- + * basically a copy of the entire IP/TCP header from the last packet
- + * we saw from the conversation together with a small identifier
- + * the transmit & receive ends of the line use to locate saved header.
- + */
- +struct cstate {
- + byte_t cs_this; /* connection id number (xmit) */
- + struct cstate *next; /* next in ring (xmit) */
- + struct iphdr cs_ip; /* ip/tcp hdr from most recent packet */
- + struct tcphdr cs_tcp;
- + unsigned char cs_ipopt[64];
- + unsigned char cs_tcpopt[64];
- + int cs_hsize;
- +};
- +#define NULLSLSTATE (struct cstate *)0
- +
- +/*
- + * all the state data for one serial line (we need one of these per line).
- + */
- +struct slcompress {
- + struct cstate *tstate; /* transmit connection states (array)*/
- + struct cstate *rstate; /* receive connection states (array)*/
- +
- + byte_t tslot_limit; /* highest transmit slot id (0-l)*/
- + byte_t rslot_limit; /* highest receive slot id (0-l)*/
- +
- + byte_t xmit_oldest; /* oldest xmit in ring */
- + byte_t xmit_current; /* most recent xmit id */
- + byte_t recv_current; /* most recent rcvd id */
- +
- + byte_t flags;
- +#define SLF_TOSS 0x01 /* tossing rcvd frames until id received */
- +
- + int32 sls_o_nontcp; /* outbound non-TCP packets */
- + int32 sls_o_tcp; /* outbound TCP packets */
- + int32 sls_o_uncompressed; /* outbound uncompressed packets */
- + int32 sls_o_compressed; /* outbound compressed packets */
- + int32 sls_o_searches; /* searches for connection state */
- + int32 sls_o_misses; /* times couldn't find conn. state */
- +
- + int32 sls_i_uncompressed; /* inbound uncompressed packets */
- + int32 sls_i_compressed; /* inbound compressed packets */
- + int32 sls_i_error; /* inbound error packets */
- + int32 sls_i_tossed; /* inbound packets tossed because of error */
- +
- + int32 sls_i_runt;
- + int32 sls_i_badcheck;
- +};
- +#define NULLSLCOMPR (struct slcompress *)0
- +
- +#define __ARGS(x) x
- +
- +/* In slhc.c: */
- +struct slcompress *slhc_init __ARGS((int rslots, int tslots));
- +void slhc_free __ARGS((struct slcompress *comp));
- +
- +int slhc_compress __ARGS((struct slcompress *comp, unsigned char *icp,
- + int isize, unsigned char *ocp, unsigned char **cpp,
- + int compress_cid));
- +int slhc_uncompress __ARGS((struct slcompress *comp, unsigned char *icp,
- + int isize));
- +int slhc_remember __ARGS((struct slcompress *comp, unsigned char *icp,
- + int isize));
- +int slhc_toss __ARGS((struct slcompress *comp));
- +
- +void slhc_i_status __ARGS((struct slcompress *comp));
- +void slhc_o_status __ARGS((struct slcompress *comp));
- +
- +#endif /* _SLHC_H */
- diff -r -u -N linux.orig/arch/arm/drivers/net/slip.c linux.arm/arch/arm/drivers/net/slip.c
- --- linux.orig/arch/arm/drivers/net/slip.c Thu Jan 1 01:00:00 1970
- +++ linux.arm/arch/arm/drivers/net/slip.c Fri Oct 27 23:15:03 1995
- @@ -0,0 +1,1212 @@
- +/*
- + * slip.c This module implements the SLIP protocol for kernel-based
- + * devices like TTY. It interfaces between a raw TTY, and the
- + * kernel's INET protocol layers (via DDI).
- + *
- + * Version: @(#)slip.c 0.8.3 12/24/94
- + *
- + * Authors: Laurence Culhane, <loz@holmes.demon.co.uk>
- + * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
- + *
- + * Fixes:
- + * Alan Cox : Sanity checks and avoid tx overruns.
- + * Has a new sl->mtu field.
- + * Alan Cox : Found cause of overrun. ifconfig sl0 mtu upwards.
- + * Driver now spots this and grows/shrinks its buffers(hack!).
- + * Memory leak if you run out of memory setting up a slip driver fixed.
- + * Matt Dillon : Printable slip (borrowed from NET2E)
- + * Pauline Middelink : Slip driver fixes.
- + * Alan Cox : Honours the old SL_COMPRESSED flag
- + * Alan Cox : KISS AX.25 and AXUI IP support
- + * Michael Riepe : Automatic CSLIP recognition added
- + * Charles Hedrick : CSLIP header length problem fix.
- + * Alan Cox : Corrected non-IP cases of the above.
- + * Alan Cox : Now uses hardware type as per FvK.
- + * Alan Cox : Default to 192.168.0.0 (RFC 1597)
- + * A.N.Kuznetsov : dev_tint() recursion fix.
- + * Dmitry Gorodchanin : SLIP memory leaks
- + * Dmitry Gorodchanin : Code cleanup. Reduce tty driver
- + * buffering from 4096 to 256 bytes.
- + * Improving SLIP response time.
- + * CONFIG_SLIP_MODE_SLIP6.
- + * ifconfig sl? up & down now works correctly.
- + * Modularization.
- + * Alan Cox : Oops - fix AX.25 buffer lengths
- + * Dmitry Gorodchanin : Even more cleanups. Preserve CSLIP
- + * statistics. Include CSLIP code only
- + * if it really needed.
- + * Alan Cox : Free slhc buffers in the right place.
- + *
- + *
- + *
- + * FIXME: This driver still makes some IP'ish assumptions. It should build cleanly KISS TNC only without
- + * CONFIG_INET defined.
- + * I hope now it is fixed ;)
- + */
- +
- +#define SL_CHECK_TRANSMIT
- +#include <linux/config.h>
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/version.h>
- +#endif
- +
- +/* Undef this, if you don't need 6bit encapsulation code in the driver */
- +#define CONFIG_SLIP_MODE_SLIP6
- +
- +#include <asm/system.h>
- +#include <asm/segment.h>
- +#include <asm/bitops.h>
- +#include <linux/string.h>
- +#include <linux/mm.h>
- +#include <linux/interrupt.h>
- +#include <linux/in.h>
- +#include <linux/tty.h>
- +#include <linux/errno.h>
- +#include <linux/netdevice.h>
- +#ifdef CONFIG_AX25
- +#include "ax25.h"
- +#endif
- +#include <linux/etherdevice.h>
- +#include <linux/skbuff.h>
- +#include <linux/if_arp.h>
- +#include "slip.h"
- +#ifdef CONFIG_INET
- +#include <linux/ip.h>
- +#include <linux/tcp.h>
- +#include "slhc.h"
- +#endif
- +
- +#ifdef MODULE
- +#define SLIP_VERSION "0.8.3-NET3.019-NEWTTY-MODULAR"
- +#else
- +#define SLIP_VERSION "0.8.3-NET3.019-NEWTTY"
- +#endif
- +
- +
- +static struct slip sl_ctrl[SL_NRUNIT];
- +static struct tty_ldisc sl_ldisc;
- +static int already = 0;
- +
- +static int slip_esc(unsigned char *p, unsigned char *d, int len);
- +static void slip_unesc(struct slip *sl, unsigned char c);
- +#ifdef CONFIG_SLIP_MODE_SLIP6
- +static int slip_esc6(unsigned char *p, unsigned char *d, int len);
- +static void slip_unesc6(struct slip *sl, unsigned char c);
- +#endif
- +
- +
- +/* Find a free SLIP channel, and link in this `tty' line. */
- +static inline struct slip *
- +sl_alloc(void)
- +{
- + struct slip *sl;
- + int i;
- +
- + for (i = 0; i < SL_NRUNIT; i++) {
- + sl = &sl_ctrl[i];
- + if (!set_bit(SLF_INUSE, &sl->flags)) {
- + return sl;
- + }
- + }
- + return NULL;
- +}
- +
- +
- +/* Free a SLIP channel. */
- +static inline void
- +sl_free(struct slip *sl)
- +{
- + /* Free all SLIP frame buffers. */
- + if (sl->rbuff) {
- + kfree(sl->rbuff);
- + }
- + sl->rbuff = NULL;
- + if (sl->xbuff) {
- + kfree(sl->xbuff);
- + }
- + sl->xbuff = NULL;
- +#ifdef SL_INCLUDE_CSLIP
- + /* Save CSLIP statistics */
- + if (sl->slcomp) {
- + sl->rx_compressed += sl->slcomp->sls_i_compressed;
- + sl->rx_dropped += sl->slcomp->sls_i_tossed;
- + sl->tx_compressed += sl->slcomp->sls_o_compressed;
- + sl->tx_misses += sl->slcomp->sls_o_misses;
- + }
- + if (sl->cbuff) {
- + kfree(sl->cbuff);
- + }
- + sl->cbuff = NULL;
- + if(sl->slcomp)
- + slhc_free(sl->slcomp);
- + sl->slcomp = NULL;
- +#endif
- +
- + if (!clear_bit(SLF_INUSE, &sl->flags)) {
- + printk("%s: sl_free for already free unit.\n", sl->dev->name);
- + }
- +}
- +
- +/* 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 void sl_changedmtu(struct slip *sl)
- +{
- + struct device *dev = sl->dev;
- + unsigned char *xbuff, *rbuff, *oxbuff, *orbuff;
- +#ifdef SL_INCLUDE_CSLIP
- + unsigned char *cbuff, *ocbuff;
- +#endif
- + int len;
- + unsigned long flags;
- +
- + len = dev->mtu * 2;
- +/*
- + * allow for arrival of larger UDP packets, even if we say not to
- + * also fixes a bug in which SunOS sends 512-byte packets even with
- + * an MSS of 128
- + */
- + if (len < 576 * 2) {
- + len = 576 * 2;
- + }
- +
- + xbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
- + rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
- +#ifdef SL_INCLUDE_CSLIP
- + cbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
- +#endif
- +
- +#ifdef SL_INCLUDE_CSLIP
- + if (xbuff == NULL || rbuff == NULL || cbuff == NULL) {
- +#else
- + if (xbuff == NULL || rbuff == NULL) {
- +#endif
- + printk("%s: unable to grow slip buffers, MTU change cancelled.\n",
- + sl->dev->name);
- + dev->mtu = sl->mtu;
- + if (xbuff != NULL) {
- + kfree(xbuff);
- + }
- + if (rbuff != NULL) {
- + kfree(rbuff);
- + }
- +#ifdef SL_INCLUDE_CSLIP
- + if (cbuff != NULL) {
- + kfree(cbuff);
- + }
- +#endif
- + return;
- + }
- +
- + save_flags(flags); cli();
- +
- + oxbuff = sl->xbuff;
- + sl->xbuff = xbuff;
- + orbuff = sl->rbuff;
- + sl->rbuff = rbuff;
- +#ifdef SL_INCLUDE_CSLIP
- + ocbuff = sl->cbuff;
- + sl->cbuff = cbuff;
- +#endif
- + if (sl->xleft) {
- + if (sl->xleft <= len) {
- + memcpy(sl->xbuff, sl->xhead, sl->xleft);
- + } else {
- + sl->xleft = 0;
- + sl->tx_dropped++;
- + }
- + }
- + sl->xhead = sl->xbuff;
- +
- + if (sl->rcount) {
- + if (sl->rcount <= len) {
- + memcpy(sl->rbuff, orbuff, sl->rcount);
- + } else {
- + sl->rcount = 0;
- + sl->rx_over_errors++;
- + set_bit(SLF_ERROR, &sl->flags);
- + }
- + }
- +#ifdef CONFIG_AX25
- + sl->mtu = dev->mtu + 73;
- +#else
- + sl->mtu = dev->mtu;
- +#endif
- + sl->buffsize = len;
- +
- + restore_flags(flags);
- +
- + if (oxbuff != NULL) {
- + kfree(oxbuff);
- + }
- + if (orbuff != NULL) {
- + kfree(orbuff);
- + }
- +#ifdef SL_INCLUDE_CSLIP
- + if (ocbuff != NULL) {
- + kfree(ocbuff);
- + }
- +#endif
- +}
- +
- +
- +/* Set the "sending" flag. This must be atomic, hence the ASM. */
- +static inline void
- +sl_lock(struct slip *sl)
- +{
- + if (set_bit(0, (void *) &sl->dev->tbusy)) {
- + printk("%s: trying to lock already locked device!\n", sl->dev->name);
- + }
- +}
- +
- +
- +/* Clear the "sending" flag. This must be atomic, hence the ASM. */
- +static inline void
- +sl_unlock(struct slip *sl)
- +{
- + if (!clear_bit(0, (void *)&sl->dev->tbusy)) {
- + printk("%s: trying to unlock already unlocked device!\n", sl->dev->name);
- + }
- +}
- +
- +/* Send one completely decapsulated IP datagram to the IP layer. */
- +static void
- +sl_bump(struct slip *sl)
- +{
- + struct sk_buff *skb;
- + int count;
- +
- + count = sl->rcount;
- +#ifdef SL_INCLUDE_CSLIP
- + if (sl->mode & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) {
- + unsigned char c;
- + if ((c = sl->rbuff[0]) & SL_TYPE_COMPRESSED_TCP) {
- + /* ignore compressed packets when CSLIP is off */
- + if (!(sl->mode & SL_MODE_CSLIP)) {
- + printk("%s: compressed packet ignored\n", sl->dev->name);
- + return;
- + }
- + /* make sure we've reserved enough space for uncompress to use */
- + if (count + 80 > sl->buffsize) {
- + sl->rx_over_errors++;
- + return;
- + }
- + count = slhc_uncompress(sl->slcomp, sl->rbuff, count);
- + if (count <= 0) {
- + return;
- + }
- + } else if (c >= SL_TYPE_UNCOMPRESSED_TCP) {
- + if (!(sl->mode & SL_MODE_CSLIP)) {
- + /* turn on header compression */
- + sl->mode |= SL_MODE_CSLIP;
- + sl->mode &= ~SL_MODE_ADAPTIVE;
- + printk("%s: header compression turned on\n", sl->dev->name);
- + }
- + sl->rbuff[0] &= 0x4f;
- + if (slhc_remember(sl->slcomp, sl->rbuff, count) <= 0) {
- + return;
- + }
- + }
- + }
- +#endif /* SL_INCLUDE_CSLIP */
- +
- + skb = alloc_skb(count, GFP_ATOMIC);
- + if (skb == NULL) {
- + printk("%s: memory squeeze, dropping packet.\n", sl->dev->name);
- + sl->rx_dropped++;
- + return;
- + }
- + skb->len = count;
- + skb->dev = sl->dev;
- + memcpy(skb->data, sl->rbuff, count);
- + netif_rx(skb);
- + sl->rx_packets++;
- +}
- +
- +/* Encapsulate one IP datagram and stuff into a TTY queue. */
- +static void
- +sl_encaps(struct slip *sl, unsigned char *icp, int len)
- +{
- + unsigned char *p;
- + int actual, count;
- +
- +
- +#ifdef CONFIG_AX25
- + if (sl->mtu != sl->dev->mtu + 73) { /* Someone has been ifconfigging */
- +#else
- + if (sl->mtu != sl->dev->mtu) { /* Someone has been ifconfigging */
- +#endif
- + sl_changedmtu(sl);
- + }
- +
- + if (len > sl->mtu) { /* Sigh, shouldn't occur BUT ... */
- + len = sl->mtu;
- + printk ("%s: truncating oversized transmit packet!\n", sl->dev->name);
- + sl->tx_dropped++;
- + sl_unlock(sl);
- + return;
- + }
- +
- + p = icp;
- +#ifdef SL_INCLUDE_CSLIP
- + if (sl->mode & SL_MODE_CSLIP) {
- + len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1);
- + }
- +#endif
- +#ifdef CONFIG_SLIP_MODE_SLIP6
- + if(sl->mode & SL_MODE_SLIP6)
- + count = slip_esc6(p, (unsigned char *) sl->xbuff, len);
- + else
- +#endif
- + count = slip_esc(p, (unsigned char *) sl->xbuff, len);
- +
- + /* Order of next two lines is *very* important.
- + * When we are sending a little amount of data,
- + * the transfer may be completed inside driver.write()
- + * routine, because it's running with interrupts enabled.
- + * In this case we *never* got WRITE_WAKEUP event,
- + * if we did not request it before write operation.
- + * 14 Oct 1994 Dmitry Gorodchanin.
- + */
- + sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
- + actual = sl->tty->driver.write(sl->tty, 0, sl->xbuff, count);
- +#ifdef SL_CHECK_TRANSMIT
- + sl->dev->trans_start = jiffies;
- +#endif
- + sl->xleft = count - actual;
- + sl->xhead = sl->xbuff + actual;
- +}
- +
- +/*
- + * Called by the driver when there's room for more data. If we have
- + * more packets to send, we send them here.
- + */
- +static void slip_write_wakeup(struct tty_struct *tty)
- +{
- + int actual;
- + struct slip *sl = (struct slip *) tty->disc_data;
- +
- + /* First make sure we're connected. */
- + if (!sl || sl->magic != SLIP_MAGIC || !sl->dev->start) {
- + return;
- + }
- +
- + if (sl->xleft <= 0) {
- + /* Now serial buffer is almost free & we can start
- + * transmission of another packet */
- + sl->tx_packets++;
- + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
- + sl_unlock(sl);
- + mark_bh(NET_BH);
- + return;
- + }
- +
- + actual = tty->driver.write(tty, 0, sl->xhead, sl->xleft);
- + sl->xleft -= actual;
- + sl->xhead += actual;
- +}
- +
- +/* Encapsulate an IP datagram and kick it into a TTY queue. */
- +static int
- +sl_xmit(struct sk_buff *skb, struct device *dev)
- +{
- + struct slip *sl = &sl_ctrl[dev->base_addr];
- +
- + if (!dev->start) {
- + printk("%s: xmit call when iface is down\n", dev->name);
- + return 1;
- + }
- + /*
- + * If we are busy already- too bad. We ought to be able
- + * to queue things at this point, to allow for a little
- + * frame buffer. Oh well...
- + * -----------------------------------------------------
- + * I hate queues in SLIP driver. May be it's efficient,
- + * but for me latency is more important. ;)
- + * So, no queues !
- + * 14 Oct 1994 Dmitry Gorodchanin.
- + */
- + if (dev->tbusy) {
- + /* May be we must check transmitter timeout here ?
- + * 14 Oct 1994 Dmitry Gorodchanin.
- + */
- +#ifdef SL_CHECK_TRANSMIT
- + if (jiffies - dev->trans_start < 20 * HZ) {
- + /* 20 sec timeout not reached */
- + return 1;
- + }
- + printk("%s: transmit timed out, %s?\n", dev->name,
- + (sl->tty->driver.chars_in_buffer(sl->tty) || sl->xleft) ?
- + "bad line quality" : "driver error");
- + sl->xleft = 0;
- + sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
- + sl_unlock(sl);
- +#else
- + return 1;
- +#endif
- + }
- +
- + /* We were not busy, so we are now... :-) */
- + if (skb != NULL) {
- + sl_lock(sl);
- + sl_encaps(sl, skb->data, skb->len);
- + dev_kfree_skb(skb, FREE_WRITE);
- + }
- + return 0;
- +}
- +
- +
- +/* Return the frame type ID. This is normally IP but maybe be AX.25. */
- +static unsigned short
- +sl_type_trans (struct sk_buff *skb, struct device *dev)
- +{
- +#ifdef CONFIG_AX25
- + struct slip *sl = &sl_ctrl[dev->base_addr];
- +
- + if (sl->mode & SL_MODE_AX25) {
- + return htons(ETH_P_AX25);
- + }
- +#endif
- + return htons(ETH_P_IP);
- +}
- +
- +
- +/* Fill in the MAC-level header. Not used by SLIP. */
- +static int
- +sl_header(unsigned char *buff, struct device *dev, unsigned short type,
- + void *daddr, void *saddr, unsigned len, struct sk_buff *skb)
- +{
- +#ifdef CONFIG_AX25
- +#ifdef CONFIG_INET
- + struct slip *sl = &sl_ctrl[dev->base_addr];
- +
- + if ((sl->mode & SL_MODE_AX25) && type != htons(ETH_P_AX25)) {
- + return ax25_encapsulate(buff, dev, type, daddr, saddr, len, skb);
- + }
- +#endif
- +#endif
- + return 0;
- +}
- +
- +
- +/* Rebuild the MAC-level header. Not used by SLIP. */
- +static int
- +sl_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
- + struct sk_buff *skb)
- +{
- +#ifdef CONFIG_AX25
- +#ifdef CONFIG_INET
- + struct slip *sl = &sl_ctrl[dev->base_addr];
- +
- + if (sl->mode & SL_MODE_AX25) {
- + return ax25_rebuild_header(buff, dev, raddr, skb);
- + }
- +#endif
- +#endif
- + return 0;
- +}
- +
- +
- +/* Open the low-level part of the SLIP channel. Easy! */
- +static int
- +sl_open(struct device *dev)
- +{
- + struct slip *sl = &sl_ctrl[dev->base_addr];
- + unsigned long len;
- +
- + if (sl->tty == NULL) {
- + return -ENODEV;
- + }
- +
- + /*
- + * Allocate the SLIP frame buffers:
- + *
- + * rbuff Receive buffer.
- + * xbuff Transmit buffer.
- + * cbuff Temporary compression buffer.
- + */
- + len = dev->mtu * 2;
- + /*
- + * allow for arrival of larger UDP packets, even if we say not to
- + * also fixes a bug in which SunOS sends 512-byte packets even with
- + * an MSS of 128
- + */
- + if (len < 576 * 2) {
- + len = 576 * 2;
- + }
- + sl->rbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
- + if (sl->rbuff == NULL) {
- + goto norbuff;
- + }
- + sl->xbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
- + if (sl->xbuff == NULL) {
- + goto noxbuff;
- + }
- +#ifdef SL_INCLUDE_CSLIP
- + sl->cbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
- + if (sl->cbuff == NULL) {
- + goto nocbuff;
- + }
- + sl->slcomp = slhc_init(16, 16);
- + if (sl->slcomp == NULL) {
- + goto noslcomp;
- + }
- +#endif
- +
- +#ifdef CONFIG_AX25
- + sl->mtu = dev->mtu + 73;
- +#else
- + sl->mtu = dev->mtu;
- +#endif
- + sl->buffsize = len;
- + sl->rcount = 0;
- + sl->xleft = 0;
- +#ifdef CONFIG_SLIP_MODE_SLIP6
- + sl->xdata = 0;
- + sl->xbits = 0;
- +#endif
- + sl->flags &= (1 << SLF_INUSE); /* Clear ESCAPE & ERROR flags */
- +
- + /* Needed because address '0' is special */
- + if (dev->pa_addr == 0) {
- + dev->pa_addr=ntohl(0xC0A80001);
- + }
- + dev->tbusy = 0;
- +/* dev->flags |= IFF_UP; */
- + dev->start = 1;
- +
- + return 0;
- +
- + /* Cleanup */
- +#ifdef SL_INCLUDE_CSLIP
- +noslcomp:
- + kfree(sl->cbuff);
- +nocbuff:
- +#endif
- + kfree(sl->xbuff);
- +noxbuff:
- + kfree(sl->rbuff);
- +norbuff:
- + return -ENOMEM;
- +}
- +
- +
- +/* Close the low-level part of the SLIP channel. Easy! */
- +static int
- +sl_close(struct device *dev)
- +{
- + struct slip *sl = &sl_ctrl[dev->base_addr];
- +
- + if (sl->tty == NULL) {
- + return -EBUSY;
- + }
- + sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
- + dev->tbusy = 1;
- + dev->start = 0;
- +
- +/* dev->flags &= ~IFF_UP; */
- +
- + return 0;
- +}
- +
- +static int
- +slip_receive_room(struct tty_struct *tty)
- +{
- + return 65536; /* We can handle an infinite amount of data. :-) */
- +}
- +
- +/*
- + * Handle the 'receiver data ready' interrupt.
- + * This function is called by the 'tty_io' module in the kernel when
- + * a block of SLIP data has been received, which can now be decapsulated
- + * and sent on to some IP layer for further processing.
- + */
- +static void
- +slip_receive_buf(struct tty_struct *tty, unsigned char *cp, char *fp, int count)
- +{
- + struct slip *sl = (struct slip *) tty->disc_data;
- +
- + if (!sl || sl->magic != SLIP_MAGIC || !sl->dev->start)
- + return;
- +
- + /*
- + * Argh! mtu change time! - costs us the packet part received
- + * at the change
- + */
- +#ifdef CONFIG_AX25
- + if (sl->mtu != sl->dev->mtu + 73) {
- +#else
- + if (sl->mtu != sl->dev->mtu) {
- +#endif
- + sl_changedmtu(sl);
- + }
- +
- + /* Read the characters out of the buffer */
- + while (count--) {
- + if (fp && *fp++) {
- + if (!set_bit(SLF_ERROR, &sl->flags)) {
- + sl->rx_errors++;
- + }
- + cp++;
- + continue;
- + }
- +#ifdef CONFIG_SLIP_MODE_SLIP6
- + if (sl->mode & SL_MODE_SLIP6)
- + slip_unesc6(sl, *cp++);
- + else
- +#endif
- + slip_unesc(sl, *cp++);
- + }
- +}
- +
- +/*
- + * Open the high-level part of the SLIP channel.
- + * This function is called by the TTY module when the
- + * SLIP line discipline is called for. Because we are
- + * sure the tty line exists, we only have to link it to
- + * a free SLIP channel...
- + */
- +static int
- +slip_open(struct tty_struct *tty)
- +{
- + struct slip *sl = (struct slip *) tty->disc_data;
- + int err;
- +
- + /* First make sure we're not already connected. */
- + if (sl && sl->magic == SLIP_MAGIC) {
- + return -EEXIST;
- + }
- +
- + /* OK. Find a free SLIP channel to use. */
- + if ((sl = sl_alloc()) == NULL) {
- + return -ENFILE;
- + }
- +
- + sl->tty = tty;
- + tty->disc_data = sl;
- + if (tty->driver.flush_buffer) {
- + tty->driver.flush_buffer(tty);
- + }
- + if (tty->ldisc.flush_buffer) {
- + tty->ldisc.flush_buffer(tty);
- + }
- +
- + /* Restore default settings */
- + sl->mode = SL_MODE_DEFAULT;
- + sl->dev->type = ARPHRD_SLIP + sl->mode;
- +#ifdef CONFIG_AX25
- + if (sl->dev->type == 260) { /* KISS */
- + sl->dev->type = ARPHRD_AX25;
- + }
- +#endif
- + /* Perform the low-level SLIP initialization. */
- + if ((err = sl_open(sl->dev))) {
- + return err;
- + }
- +
- +#ifdef MODULE
- + MOD_INC_USE_COUNT;
- +#endif
- +
- + /* Done. We have linked the TTY line to a channel. */
- + return sl->dev->base_addr;
- +}
- +
- +
- +/*
- + * Close down a SLIP channel.
- + * This means flushing out any pending queues, and then restoring the
- + * TTY line discipline to what it was before it got hooked to SLIP
- + * (which usually is TTY again).
- + */
- +static void
- +slip_close(struct tty_struct *tty)
- +{
- + struct slip *sl = (struct slip *) tty->disc_data;
- +
- + /* First make sure we're connected. */
- + if (!sl || sl->magic != SLIP_MAGIC) {
- + return;
- + }
- +
- + (void) dev_close(sl->dev);
- +
- + tty->disc_data = 0;
- + sl->tty = NULL;
- + sl_free(sl);
- +#ifdef MODULE
- + MOD_DEC_USE_COUNT;
- +#endif
- +}
- +
- +
- +static struct enet_statistics *
- +sl_get_stats(struct device *dev)
- +{
- + static struct enet_statistics stats;
- + struct slip *sl = &sl_ctrl[dev->base_addr];
- +#ifdef SL_INCLUDE_CSLIP
- + struct slcompress *comp;
- +#endif
- +
- + memset(&stats, 0, sizeof(struct enet_statistics));
- +
- + stats.rx_packets = sl->rx_packets;
- + stats.tx_packets = sl->tx_packets;
- + stats.rx_dropped = sl->rx_dropped;
- + stats.tx_dropped = sl->tx_dropped;
- + stats.tx_errors = sl->tx_errors;
- + stats.rx_errors = sl->rx_errors;
- + stats.rx_over_errors = sl->rx_over_errors;
- +#ifdef SL_INCLUDE_CSLIP
- + stats.rx_fifo_errors = sl->rx_compressed;
- + stats.tx_fifo_errors = sl->tx_compressed;
- + stats.collisions = sl->tx_misses;
- + comp = sl->slcomp;
- + if (comp) {
- + stats.rx_fifo_errors += comp->sls_i_compressed;
- + stats.rx_dropped += comp->sls_i_tossed;
- + stats.tx_fifo_errors += comp->sls_o_compressed;
- + stats.collisions += comp->sls_o_misses;
- + }
- +#endif /* CONFIG_INET */
- + return (&stats);
- +}
- +
- +
- + /************************************************************************
- + * STANDARD SLIP ENCAPSULATION *
- + ************************************************************************/
- +
- +int
- +slip_esc(unsigned char *s, unsigned char *d, int len)
- +{
- + unsigned char *ptr = d;
- + unsigned char